# StyloBot-julkaisusarja: Behaviour-Aware TypeScript UI

*Botin havaitseminen ei saa lopettaa sallimalla/block. Tämä raportti osoittaa, miten StyloBotin luokittelutulosta tulee TypeScriptin soveltamisloogikaaM SK3 Express- ja Fastify-server middlewareMSC4 handlebars-temppujen avustajatMST5 Nunjucks*, ja EJSMSP7 sekä browser-verkkokomponentitMSL8 niin että UI:llänne voidaan muokata kokemusta sen sijaan, että se jarruttaisi sitä jälkikäteen.*

[<img src="/articleimages/stylobot-logo.svg" alt="StyloBot" width="120" />](https://www.stylobot.net)

> **Euroopan unionin `@stylobot/core` ja `@stylobot/node` npm-paketit julkaistaan pian.** jäljempänä esitetyt osiot kuvailevat pinta-alaa, jota ne esittävät.

> **StyloBot -sarja**
> 
> 1. [**Käytänne, Ei identiteettiä**](/blog/stylobot-fingerprint): miksi StyloBot mallistaa asiakkaita käytännöllisesti
> 2. [**Behaviour-Aware ASP.NET UI**](/blog/behaviour-aware-ux): serveri-renderoitu pinta .NET- sovelluksille
> 3. [**Pitkällä aikavälillä tapahtuvan -käynnissä olevan .NET-palvelujen rajaton kasvun löytäminen ja korjaaminen**](/blog/stylobot-release-reliability):, luotettavuuden kurinalaisuus, joka pitää moottorin tuskallisena tuotannossa
> 4. **Behaviour-Aware TypeScript UI**: tämä artikkeli
> 5. [**Sidecar-arkkitehtuuri**](/blog/sidecar-architecture): miten havaitsemismoottori liitetään muihin kuin -.NET-palkkiin
> 6. [**Oppiminen nopeuttamaan**](/blog/stylobot-release-learning): mukautuva oppimisjärjestelmä, , neljä,- tasotasoinen muisti ja , tuomiovarasto
> 7. [**Todistuksen testaaminen, joka ei jää rauhaan**](/blog/stylobot-release-nondeterministic-testing): tarkastusta koskevat säännökset : yksi BDF-tietokanta hallinnoi regressiaa M SK2 lastausta, , ja kalibrointia
> 8. [**StyloExtract - paikallisen oppimisen HTML:n Markdown-muuttaja**](/blog/stylobot-release-styloextract): HTMLM SK1Markdown-laaja, joka on päällekkäinen tunnistajan kanssa , walkinger bug lucidVIEW caught, ja dogfood loop, joka teki siitä rehellisen

StyloBotin havaitsemismoottori on kirjoitettu ASP:ssa, ., NET Core ja :, korkean suorituskyvyn kehys, jossa on erinomainen asynkki primitivi. [Oppiminen nopeuttamaan](/blog/stylobot-release-learning): kun sormenjälki on nähty riittävän luottamuksellisestiM SK1 tuomiovaraus välttää koko detektaattorin kaasuputken ja vastaa mikrosekkunnissa

<!--category-- TypeScript, Node.js, StyloBot, Bot Detection, Security -->
<datetime class="hidden">2026-06-01T10:30</datetime>

# Esitys

Suurin osa TypeScriptin ekosysteemissä käytettävistä bot-tutkimuksista antaa teille yhden kahdesta asiasta: : -lähettilään sormenjäljen, joka kertoo, onko *yhteyshenkilö* näyttää automaattiselta, tai käyttäjän reeksiltä-Agent string, joka kiinnittää vain itsensä ilmoittavia bottejaM SK2

Niistä kumpikaan ei ole käyttökelpoista soveltamislaajassa.

[Botd](https://github.com/fingerprintjs/BotD) runs in the browser and has no view of request headers, TLS fingerprints , session behaviourM SK2 or IP reputation. A headless Chromium and a Googlebot look identical to itMST4 Pure UA matching packages MSC5`is-bot`, `isbot`) pyydä vain sitä, mikä haluaa tulla pyydettyäM SK1 Verkojärjestelmä-laajatuotteet (Cloudflare Bot ManagementMSC4 DataDome*) estää pyynnön etäisyydessä mutta ei voi mukauttaa sitä soveltamisalaanMST6 Kerro pyynnöstänne, että se on suljettu–, ette ole koskaan todennut, että pyyntöllä, joka on saavuttanut sovelluksenne, olisi ollut bot-mahdollisuus ja keskinkertainen riskivyöhyke–MST9 joten tilaussivullanne olisi voitu osoittaa törmäysvaihetta ja analyysit olisivat voineet sulkea sen pois –MST10 kaikki virhekoodista riippumatta

What StyloBot gives your TypeScript application is a verdict: typed result with a continuous bot probability score`AiBot`, `Scraper`, `GoodBot`, `MaliciousBot`,...), Riskiryhmä, Suositettu toiminta, ja uhkaluokitus. Teidän tehtävänne tämän tuomion kanssa riippuu teistä.

Tässä artiklassa tarkastellaan esikuvaa kaupungista ja esitetään, miten tuomiosta tulee käytännöllinen käytäntö jokaisella tasolla.

[TOC]

# Tuomio

Ennen kaikkea, tämä on se, mikä saapuu

```ts
// @stylobot/core
interface Verdict {
  isBot: boolean;
  botProbability: number;        // 0.0–1.0 - not binary, a continuous score
  confidence: number;            // certainty in that score
  botType: BotType | null;       // 'AiBot' | 'Scraper' | 'GoodBot' | 'MaliciousBot' | ...
  botName: string | null;        // 'GPTBot', 'Googlebot', 'curl/8.9.1', ...
  riskBand: RiskBand;            // 'VeryLow' | 'Low' | 'Elevated' | 'Medium' | 'High' | 'VeryHigh'
  recommendedAction: RecommendedAction;  // 'Allow' | 'Throttle' | 'Challenge' | 'Block'
  threatScore: number;           // elevated when CVE probes or credential stuffing detected
  threatBand: ThreatBand;        // 'None' | 'Low' | 'Elevated' | 'High' | 'Critical'
}
```

Tarkastuspiste voi polkea `Medium` risk instead of blocking it. API voi sulkea `AiBot` liikkeeseenlasketut kiintiöt lukuun ottamatta todellisia käyttäjiä `Elevated` riskiä ilman, että puututaan kokemuksiin `Low` riskivierailijat. Mikään näistä ei ole mahdollista, jos havaitsemisen tulos on boolean verkkotasolla

Riskivyöhykkeet järjestyksessä: `VeryLow`, `Low`, `Elevated`, `Medium`, `High`, `VeryHigh`, `Verified`.

# Asetus

```bash
npm install @stylobot/core @stylobot/node

# only needed for grpc mode
npm install @grpc/grpc-js @grpc/proto-loader
```

`@stylobot/core` runningtime-riippuvuus on nolla. Se sisältää tyyppimäärittelyt , REST-clienti, header parserM SK3 ja MSC4vaihtoehtoisestiMST5 gRPC-clientinMSV6 Se toimii nodessaMSL7 Denossa MSL8 ja Bunissa ilman muutoksiaMSR9

`@stylobot/node` provides Express middleware, a Fastify plugin, template helper utilities , and the verdict injectorM SK3 se riippuu `@stylobot/core` eikä mitään muuta runtimessa.

# Serverin pinta

## Miten tuomio saavuttaa välineet

Keskiteollisuudella on kolme menetelmää. Kaikki kolme tuottavat samaa `req.stylobot` muoto. Mitkä olette käyttämässä riippuu siitä, missä infrastruktuurin havaitsemisessa toimitaan

```mermaid
graph LR
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px
    classDef async fill:none,stroke:#a855f7,stroke-width:2px

    subgraph H ["headers mode - zero added latency"]
        GW[Gateway / Caddy / YARP]:::input -->|"X-StyloBot-* headers"| A[Express / Fastify]:::good
    end

    subgraph G ["grpc mode - HTTP/2, no JSON overhead"]
        B[Express / Fastify]:::good -->|"gRPC Detect RPC"| SC[StyloBot Sidecar :5090]:::async
    end

    subgraph R ["api mode - REST, full signal set"]
        C[Express / Fastify]:::good -->|"POST /api/v1/detect"| SC2[StyloBot Sidecar :5091]:::async
    end
```

**`headers` menettely**: havaitseminen suoritettiin etuvirtaan ottamalla vartiointiasemaan; välineet lukevat syötettyjä otsikoita ilman verkkovierailua . Tämä on tuotantomalli, kun Caddyn tai YARP:n ottama vartioitusasema on edessä

**`grpc` menettely**: välineet kutsuvat sivuautoa suoraan HTTP:n kautta `@grpc/grpc-js`. gRPC-yhteys ja sivuauton käyttöönotto yksityiskohdat ovat [Luettelo sivuautoarkkitehtuurista](/blog/sidecar-architecture).

**`api` menettely**:-puhelut `POST /api/v1/detect` yli HTTP/1.1. myös palaa `reasons` ( `signals` (muiden muodojen jättämä täydellinen taulukon tila

Keskinäinen ohjelmisto on aina epäonnistunut avata. Jos sivuautoa ei ole saavutettavissa tai se on ajautunut ulosM SK1 `req.stylobot` asetetaan sallivaan tyhjään tuomioon ja pyyntö jatkuu.

## Siirretään se

**Ekspresti**

```ts
import express from 'express';
import { styloBotMiddleware } from '@stylobot/node';

const app = express();
app.use(styloBotMiddleware({ mode: 'headers' }));
// or:
app.use(styloBotMiddleware({ mode: 'grpc', endpoint: 'localhost:5090', timeout: 100 }));
```

**Nopeuttaa:**

```ts
import Fastify from 'fastify';
import { styloBotPlugin } from '@stylobot/node';

const fastify = Fastify();
await fastify.register(styloBotPlugin, { mode: 'headers' });
```

Lisätä tyyppijulistus, jos haluatte kirjatun pääsyn nopeuttamispyyntöihin:

```ts
// types.d.ts
import type { StyloBotResult } from '@stylobot/node';
declare module 'fastify' {
  interface FastifyRequest { stylobot: StyloBotResult; }
}
```

# Kaupan etu

Samat näytteet varastolta [ASP.NET artikla](/blog/behaviour-aware-ux), tuotesivutM SK1 tilauscheckout, liittymä , lehtitiedoteMSC4 mutta TypeScript-muodossaMST5 havaitsemistulos on samaMSV6 mitä muuttuu on se, miten pinta paljastaa sen

## Page 1: Tuotteen sivu

Hintojen hävittäjät alkavat täällä. He haluavat tuotenimiäM SK1 hintoja , ja varastojen tasoa. Te haluatte luettelon, jonka hakukoneet voivat indeksoida ja joka on hyödyllinen ihmisille välittämättä kaupallisia signaaleja järjestelmälliselle sadonkorjuukselleMSC4

*Scenario: hintojen purkaminen koittuu `/products/:id`. Se ei näe mitään alennuskoodiaM SK1 ei lisätä -to-kart-puppua, eikä mitään ostosignaalia, jota olisi syytä käyttääMSC5*

Tieliikenteen hallintajärjestelmä:

```ts
app.get('/products/:id', (req, res) => {
  const { verdict } = req.stylobot;
  const product = getProduct(req.params.id);

  res.render('product', {
    product,
    sbVerdict: verdict,
    showDiscount:   verdict.riskBand === 'VeryLow' || verdict.riskBand === 'Low',
    showBuyButton:  !verdict.isBot,
    showCartWarning: verdict.recommendedAction === 'Challenge',
    isSearchBot:    verdict.botType === 'SearchEngine' || verdict.botType === 'VerifiedBot',
  });
});
```

malli (Handlebars):

```handlebars
{{! verified crawlers get structured metadata instead of commercial UI }}
{{#if isSearchBot}}
  <meta name="description" content="{{product.name}} -{{product.category}}." />
  <p>{{product.description}}</p>
{{else}}

  {{! discount only for low-risk human visitors }}
  {{#if showDiscount}}
    <div class="alert alert-success">
      Member price: use code <strong>LOYAL10</strong> for 10% off.
    </div>
  {{/if}}

  {{! add-to-cart only for non-bot traffic }}
  {{#if showBuyButton}}
    <form method="post" action="/cart/add">
      <input type="hidden" name="productId" value="{{product.id}}" />
      {{#if showCartWarning}}
        <p class="text-warning">Additional verification may be required at checkout.</p>
      {{/if}}
      <button type="submit" class="btn btn-primary">Add to Cart</button>
    </form>
  {{else}}
    <p class="text-muted">Purchase available to human visitors.</p>
  {{/if}}

{{/if}}
```

Käytettävissä on kolme mallia. Crawlerin eriyttäminenM SK1 `SearchEngine` ja `VerifiedBot` saada rakenteellisia metatietoja ilman kaupallisia signaaleja, mikä on se, mitä he itse asiassa tarvitsevat `Challenge` on varoitus, ei ryhmää. epäilyttävät istunnot voivat edelleen ostaaM SK2

## Page 2: Tarkastus

Petosten automatisointi, korttitesteritM SK1 voucher brute -forcers, ja kirjoitetut reterit kaikki lähentyvät tarkastuksen yhteydessäMSC4 Tavoitteena on puolustaa perusteellisesti ilman CAPTCHA-korttia

*Scénario: osuuskorttiM SK1kokeet bot-iskut `/checkout`. Se näkee kuolleenM SK1 päättyvän viestin , toimittaa hunajapotin- täytetyn lomakkeenMSC4 ja saa hiljaisen vahvistuksen ilman toistamiseen tähtäävää kannustimia*

```mermaid
flowchart TD
    classDef input fill:none,stroke:#3b82f6,stroke-width:2px
    classDef good fill:none,stroke:#22c55e,stroke-width:2px
    classDef problem fill:none,stroke:#ef4444,stroke-width:2px

    A([Visitor reaches /checkout]):::input --> B{bot?}
    B -->|yes| C[Dead-end message - no form]:::problem
    B -->|no| D{risk band}
    D -->|Low| E[Express checkout CTA]:::good
    D -->|Elevated| F[Standard checkout CTA]:::good
    D -->|High+| G[Phone number - no online path]:::problem
    E & F --> H[POST /checkout/order]
    H --> I{honeypot triggered?}
    I -->|yes| J[Silent fake confirmation]:::problem
    I -->|no| K[Process real order]:::good
```

Reitti:

```ts
app.get('/checkout', (req, res) => {
  const { verdict } = req.stylobot;
  res.render('checkout', {
    sbVerdict: verdict,
    showForm:     !verdict.isBot,
    showExpress:  verdict.riskBand === 'VeryLow' || verdict.riskBand === 'Low',
    showStandard: ['VeryLow','Low','Elevated'].includes(verdict.riskBand),
    showPhone:    ['High','VeryHigh'].includes(verdict.riskBand),
  });
});

app.post('/checkout/order', (req, res) => {
  // honeypot fields are invisible to humans; bots fill everything
  if (req.body.hp_name || req.body.hp_email) {
    return res.redirect('/checkout/confirmed');  // silent fake - no retry incentive
  }
  return processRealOrder(req, res);
});
```

malli:

```handlebars
{{#if showForm}}
  <form method="post" action="/checkout/order">

    {{! invisible honeypot trap - humans leave blank, bots fill everything }}
    <div style="position:absolute;left:-9999px;opacity:0" aria-hidden="true">
      <input type="text" name="hp_name" tabindex="-1" autocomplete="off" />
      <input type="email" name="hp_email" tabindex="-1" autocomplete="off" />
    </div>

    {{#if showExpress}}
      <button type="submit" name="express" value="true" class="btn btn-success btn-lg">
        Express Checkout
      </button>
    {{/if}}

    {{#if showStandard}}
      <button type="submit" class="btn btn-primary">Proceed to Payment</button>
    {{/if}}

    {{#if showPhone}}
      <p>Please call us to complete your order: <strong>0800 123 456</strong></p>
    {{/if}}

  </form>
{{else}}
  <p class="text-muted">Checkout is available to human visitors only.</p>
{{/if}}
```

Honeypotin poisheittäminen on avainasemassa tässä asiassa. virheen palaute opettaa hyökkääjiä toistamaan; hiljainen väärennetty vahvistus tuhlaa heidän ajansa , ja vahvistusta koskeva sivu näyttää samankaltaiselta, oliko tilaus todellinen vai poisheitettuM SK3 Express checkout trust-etuksi | ( ei ole standardi |) tarkoittaa, että vain tilaisuudet, jotka ovat saaneet sen, saavat lyhyemmän tien `<div>` wrapper osaksi yhteistä käsipide-aluetta (`{{> honeypot}}`) sen sijaan, että jokaisessa suojatussa muodossa toistetaan sisäinen malliblokti

## Page 3: Login

Liittyminen on luottamuksellista-varustamisaluettaM SK1kauppaa koskevat sopimukset ovat erilaisia kuin lähtöä varten tehdyt sopimuksentekotoimet : virheellinen myönteinen sopimus saattaisi menettää myynnin; puuttunut luottamuslauseke-varmistamistoimissa hyökkäys menettää tilinpäätöksen. täytäntöönpano voi olla tiukempaa

*Scenario: todistusaineistoM SK1kirjanpitäjät `/login`. Se näkee pelotteellisen viestin `/login/denied` ennen mahdollisten todentamistoimien toteuttamista.*

```ts
app.get('/login', (req, res) => {
  const { verdict } = req.stylobot;
  res.render('login', {
    sbVerdict: verdict,
    showWarning: verdict.isBot,
    showRisk:    ['High','VeryHigh'].includes(verdict.riskBand),
  });
});

app.post('/login', async (req, res) => {
  if (req.body.hp_user || req.body.hp_pass) {
    return res.redirect('/login/denied');
  }
  if (req.stylobot.verdict.isBot) {
    return res.redirect('/login/denied');
  }
  return authenticate(req, res);
});
```

```handlebars
{{#if showRisk}}
  <div class="alert alert-danger">
    High-risk signals detected. Login attempts are logged.
  </div>
{{/if}}

{{#if showWarning}}
  <div class="alert alert-warning">
    Automated login attempts are detected and blocked.
  </div>
{{/if}}

<form method="post" action="/login">
  <div style="position:absolute;left:-9999px;opacity:0" aria-hidden="true">
    <input type="text" name="hp_user" tabindex="-1" autocomplete="off" />
    <input type="password" name="hp_pass" tabindex="-1" autocomplete="off" />
  </div>
  <div class="form-group">
    <label for="email">Email</label>
    <input type="email" id="email" name="email" autocomplete="email" />
  </div>
  <div class="form-group">
    <label for="password">Password</label>
    <input type="password" id="password" name="password" autocomplete="current-password" />
  </div>
  <button type="submit" class="btn btn-primary">Sign In</button>
</form>
```

Bot näkee pelotteen ennen kuin esittää. Jos se esittää joka tapauksessaMSC1 hunajapotti kiinnittää senM SK2 Jos hän jollakin tavalla välttää hunajanpottin, `verdict.isBot` on lopullinen serveri- sivutarkastelu ennen mahdollisen todentamislogiikan käyttöönottoa . Kolme kerrosta

## Page 4: Newsletter

Älykkäitä crawlerejä on eri asia. Ne eivät pyri brutoimaan- pakottamaan tileitä tai korjaamaan hintojaM SK2 ne syövät sisältöä mallikoulutusta varten . Ne ovat usein rehellisiä identiteetin suhteenMSC4 mikä tarkoittaa, että oikea vastaus on kaupallinen eikä vastakkainasetteluinen

*Scenario: GPTBot-iskut `/newsletter`. Tietojen lisensointia koskeva viesti nähdään tilaajahinnan sijasta*

```ts
app.get('/newsletter', (req, res) => {
  const { verdict } = req.stylobot;
  res.render('newsletter', {
    sbVerdict:     verdict,
    isHuman:       !verdict.isBot,
    isAiBot:       verdict.botType === 'AiBot',
    isOtherBot:    verdict.isBot && verdict.botType !== 'AiBot',
  });
});

app.post('/newsletter/subscribe', (req, res) => {
  if (req.body.hp_email2 || req.stylobot.verdict.isBot) {
    return res.redirect('/newsletter/thanks');  // silent discard
  }
  mailingList.subscribe(req.body.email);
  return res.redirect('/newsletter/thanks');
});
```

```handlebars
{{#if isHuman}}
  <p>Get exclusive deals delivered to your inbox. Unsubscribe any time.</p>
{{/if}}

{{#if isAiBot}}
  <div class="alert alert-info">
    This subscription endpoint is for human readers.
    For data licensing enquiries, <a href="/contact">contact us directly</a>.
  </div>
{{/if}}

{{#if isOtherBot}}
  <div class="alert alert-warning">
    Automated subscription attempts are discarded.
  </div>
{{/if}}

<form method="post" action="/newsletter/subscribe">
  <div style="position:absolute;left:-9999px;opacity:0" aria-hidden="true">
    <input type="email" name="hp_email2" tabindex="-1" autocomplete="off" />
  </div>
  <div class="form-group">
    <input type="email" name="email" placeholder="your@email.com" autocomplete="email" />
  </div>
  <button type="submit" class="btn btn-success">Subscribe</button>
</form>
```

`AiBot` ei ole vain toinen vihamielinen merkintä, se on luokittelu, joka mahdollistaa liiketoiminnan vastauksen.Lisensointiviesti on hyödyllisempää kuin ‐403:, sillä siinä kerrotaan operaattorille, mistä he ovat tekemisissä ja annetaan heille mahdollisuus ryhtyä toimiin.

# mallin avustajat

## Tuomion sisällyttäminen malliin

`sbVerdictInjector` suoritetaan havaitsemisen jälkeen middleware ja panee tuomion `res.locals`:

```ts
import { sbVerdictInjector } from '@stylobot/node';

app.use(styloBotMiddleware({ mode: 'headers' }));
app.use(sbVerdictInjector({ mode: 'gateway' }));
// or for direct sidecar calls (REST port, not the gRPC port):
app.use(sbVerdictInjector({ mode: 'sidecar', endpoint: 'http://localhost:5091' }));
```

`res.locals.sbVerdict` on saatavilla jokaisessa mallissa. `res.locals.sbVerdictScript` on sisäinen `<script>` merkintä, joka asettaa `window.__sb` hakukonekomponenttien osalta.

## Apuvälit

määritellä `RISK_ORDER` Nunjucksin ja EJS:n avustajat käyttävät samaa kartaa ):

```ts
import type { Verdict, RiskBand } from '@stylobot/core';

const RISK_ORDER: Record<string, number> = {
  Unknown: 0, VeryLow: 1, Low: 2, Elevated: 3, Medium: 4, High: 5, VeryHigh: 6, Verified: 7,
};

// register on your Handlebars engine instance
hbs.engine({
  helpers: {
    // {{#sbGate sbVerdict "Low"}}...{{else}}...{{/sbGate}}
    sbGate(verdict: Verdict | null, maxRisk: string, options: any) {
      if (!verdict) return options.fn(this);
      return RISK_ORDER[verdict.riskBand] <= RISK_ORDER[maxRisk]
        ? options.fn(this)
        : options.inverse(this);
    },

    // {{#sbIsBot sbVerdict}}...{{else}}...{{/sbIsBot}}
    sbIsBot(verdict: Verdict | null, options: any) {
      return verdict?.isBot ? options.fn(this) : options.inverse(this);
    },

    // {{#sbBotType sbVerdict "AiBot"}}...{{/sbBotType}}
    sbBotType(verdict: Verdict | null, type: string, options: any) {
      return verdict?.botType === type ? options.fn(this) : options.inverse(this);
    },

    // {{#sbMinRisk sbVerdict "High"}}...{{/sbMinRisk}}
    sbMinRisk(verdict: Verdict | null, minRisk: string, options: any) {
      if (!verdict) return options.inverse(this);
      return RISK_ORDER[verdict.riskBand] >= RISK_ORDER[minRisk]
        ? options.fn(this)
        : options.inverse(this);
    },
  },
});
```

Esimerkeissä:

```handlebars
{{#sbGate sbVerdict "Low"}}
  <a href="/checkout/express" class="btn btn-success">Express Checkout</a>
{{else}}
  <a href="/checkout" class="btn btn-secondary">Checkout</a>
{{/sbGate}}

{{#sbBotType sbVerdict "AiBot"}}
  <div class="alert alert-info">AI crawler detected. Data licensing info above.</div>
{{/sbBotType}}

{{#sbMinRisk sbVerdict "High"}}
  <div class="alert alert-danger">High-risk session. Some features restricted.</div>
{{/sbMinRisk}}
```

## Nunjukkit

```ts
import nunjucks from 'nunjucks';

// RISK_ORDER - same map defined in the Handlebars section above

class SbGateExtension {
  tags = ['sbgate'];
  parse(parser: any, nodes: any, lexer: any) {
    const tok = parser.nextToken();
    const args = parser.parseSignature(null, true);
    parser.advanceAfterBlockEnd(tok.value);
    const body = parser.parseUntilBlocks('else', 'endsbgate');
    let elseBody = null;
    if (parser.skipSymbol('else')) {
      parser.skip(lexer.TOKEN_BLOCK_END);
      elseBody = parser.parseUntilBlocks('endsbgate');
    }
    parser.advanceAfterBlockEnd();
    return new nodes.CallExtension(this, 'run', args, [body, elseBody]);
  }
  run(context: any, maxRisk: string, body: any, elseBody: any) {
    const v = context.ctx.sbVerdict;
    const fits = !v || RISK_ORDER[v.riskBand] <= RISK_ORDER[maxRisk];
    return fits ? body() : (elseBody?.() ?? '');
  }
}
env.addExtension('SbGateExtension', new SbGateExtension());
```

```nunjucks
{% sbgate "Low" %}
  <a href="/checkout/express">Express checkout</a>
{% else %}
  <a href="/checkout">Checkout</a>
{% endsbgate %}
```

## EJS-järjestelmä

EJS:ssä ei ole lohkohelporeita,, mutta paikalliset toiminnot kattavat suurimman osan tapauksista puhtaastiM SK1

```ts
// RISK_ORDER - same map defined in the Handlebars section above

app.use((req, res, next) => {
  const v = res.locals.sbVerdict ?? null;
  res.locals.sbBelowRisk = (max: string) =>
    !v || RISK_ORDER[v.riskBand] <= RISK_ORDER[max];
  res.locals.sbAboveRisk = (min: string) =>
    v && RISK_ORDER[v.riskBand] >= RISK_ORDER[min];
  res.locals.sbIsBot  = () => v?.isBot ?? false;
  res.locals.sbBotIs  = (type: string) => v?.botType === type;
  next();
});
```

```ejs
<% if (sbBelowRisk('Low')) { %>
  <a href="/checkout/express" class="btn btn-success">Express Checkout</a>
<% } else { %>
  <a href="/checkout" class="btn btn-secondary">Checkout</a>
<% } %>

<% if (sbBotIs('AiBot')) { %>
  <div class="alert alert-info">AI crawler. Data licensing enquiries via /contact.</div>
<% } %>
```

# Lähtäjän pinta

## Tuomioistuimen tuomion sisällyttäminen браузерiin

Lisäys `{{{sbVerdictScript}}}` layout-päällystönne. Se on

```html
<script>window.__sb = {"isBot":false,"botProbability":0.12,"riskBand":"Low","recommendedAction":"Allow",...}</script>
```

Sitten laaditaan elementtipaketti:

```handlebars
<head>
  {{{sbVerdictScript}}}
  <script type="module" src="/js/sb-elements.js"></script>
</head>
```

## `<sb-gate>`: näyttää tai peittää sisältöä riskivyöhykkeen mukaan

```html
<!-- only shown when riskBand <= 'low' -->
<sb-gate max-risk="low">
  <a href="/checkout/express" class="btn btn-success">Express Checkout</a>
</sb-gate>

<!-- only shown when riskBand >= 'elevated' -->
<sb-gate min-risk="elevated">
  <div class="alert alert-warning">Unusual activity detected on your session.</div>
</sb-gate>
```

## `<sb-adapt>`: valitse ensimmäinen sopiva tapaus

```html
<sb-adapt>
  <sb-case max-risk="low">
    Standard checkout - enter your card details below.
  </sb-case>
  <sb-case max-risk="medium">
    Additional verification is required before we can process your order.
  </sb-case>
  <sb-case>
    Automated checkout access is not permitted.
  </sb-case>
</sb-adapt>
```

`<sb-adapt>` valitsee ensimmäisen `<sb-case>` jonka `max-risk` fits and hides the rest. Lopullinen `<sb-case>` ei `max-risk` -kaikki `<sb-gate>` ja `<sb-adapt>` kuunnelkaa `sb:verdict` event ja re-arvioida, muuttuuko tuomio ensimmäisen maalien jälkeen

## `<sb-widget>`: palvelijaM SK1 luovutetut nesteydet

`<sb-gate>` hides content client-side. Jos tarvitsette robotin, joka saa erilaista markuptiaM SK2 ei peiteltyä markupia , tarvitsette fragmentin, jonka on esitettävä serverissä mallissa käytettävissä olevan havaitsemista koskevan kontekstin kanssa

`<sb-widget>` lukee nestemuodon mallia suoralta sivulta `<template>` element, liittää kaikki widget-pyynnöt sivuun yhdeksi kierrosta-tripM SK2 ja korvaa itsensä palautetulla HTML:lläMSC3

```html
<sb-widget data-sb-widget="checkout-cta">
  <template>
    {% if recommendedAction == 'Block' %}
      <p class="text-danger">Automated checkout is blocked.</p>
    {% elsif botType == 'AiBot' %}
      <p>AI crawlers cannot complete purchases. <a href="/contact">Data licensing enquiries</a>.</p>
    {% elsif isBot %}
      <p class="text-muted">Checkout is available to human visitors.</p>
    {% else %}
      <a href="/checkout/express" class="btn btn-success">Express Checkout</a>
    {% endif %}
  </template>
</sb-widget>
```

Esimerkkivaihtoehtot: `isBot`, `botProbability`, `confidence`, `botType`, `botName`, `riskBand`, `recommendedAction`, `threatScore`, `threatBand`. Lisätään kaikki `vars` puhelua tehdessitte siirretään serverin-puolelle `RenderWidget` suoraan.

Kaikki `<sb-widget>` osatekijät, jotka liittyvät samaan mikrotaskin tikkiin, kootaan yhteen pyyntöön. Puolet, joissa on viisi widgetiä, muodostavat yhden rundan

```mermaid
sequenceDiagram
    participant B as Browser
    participant C as Coordinator
    participant S as /_stylobot/partials/render
    participant L as Fluid / Liquid

    B->>C: sb-widget#checkout-cta registers
    B->>C: sb-widget#promo-banner registers
    B->>C: sb-widget#nav-links registers
    Note over C: queueMicrotask - one batch
    C->>S: POST { widgets: { checkout-cta: "...", promo-banner: "...", nav-links: "..." } }
    S->>L: render each template with verdict context
    L-->>S: rendered HTML fragments
    S-->>C: <div data-sb-widget="checkout-cta">...</div> ...
    C-->>B: each sb-widget replaced in DOM
```

# Lyhyesti sanottuna

| Komponenti SSK1 Layer | Mitä se tekee S|
|---|---|---|
| `styloBotMiddleware` | Ekspresti | Liitteet `req.stylobot` header-osoitteiden välityksellä / gRPC
| `styloBotPlugin` | Nopeuttaa | Sama, kuin nopeuttaa plugin `decorateRequest` |
| `sbVerdictInjector` | Express | Väestö `res.locals.sbVerdict` ja `sbVerdictScript` |
| Apuvälineet toimenpidebareihin | Server-templatet | `sbGate`, `sbIsBot`, `sbBotType`, `sbMinRisk` auktorit |
| Nunjucks-laajentaminen | Server-temppuja `{% sbgate "Low" %}...{% endsbgate %}` |
| EJS:n paikalliset mallit `sbBelowRisk()`, `sbAboveRisk()`, `sbIsBot()`, `sbBotIs()` |
| `<sb-gate>` | Lähtölähettiläs `sb:verdict` |
| `<sb-adapt>` | Lähtäjä | Ottakaa ensimmäinen sopiva tapaus luettelosta.
| `<sb-widget>` | Browser | Fetch server-rendered Liquid fragmentM SK3 batched per tick SSK4
| `StyloBotGrpcClient` | Node / Bun SSK2 Deno | Suora gRPC-klientti muiden kuin

Seuraava versio sarjasta: [**Sidecar-arkkitehtuuri**](/blog/sidecar-architecture), millä tavoin havaitsemismoottori liitetään muihin kuin -.NET-palkkiin, , Go SDK:hen,, Caddy- pluginiin ja siihen liitettyyn gRPC-yhteyteen

Jos olette saapunut tänne .NET-puolelta [Behaviour-Aware ASP.NET UI](/blog/behaviour-aware-ux) kattaa samat storefront-muutokset merkintähelpoitteilla ja toimintafiltreillä; moottoreiden luottamuksen parantaminen, joka säilyttää moottorin muistin stable under sustainable traffic is in [Pitkällä aikavälillä tapahtuvan -käynnissä olevan .NET-palvelujen rajaton kasvun löytäminen ja korjaaminen](/blog/stylobot-release-reliability). Elävä moottoriM SK1 Dashboard , ja kaupalliset valvontalaitteet [stylobot.net](https://www.stylobot.net).