# StyloBot-utsläppsserien

*Bot-detektorn borde inte stanna vid att tillåta/block. Denna artikel visar hur StyloBots klassificeringsresultat blir till applikationslogik i TypeScript : server middleware för Express och Fastify MSC4 template helpers för Handlebars M SK5 Nunjucks , och EJS MST7 och webbkomponenter för webbfarkoster| , så att din gränssnitt kan forma upplevelsen istället för att knuffa på friktionen efter själva faktat.*

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

> **Den `@stylobot/core` och `@stylobot/node` npm-paket kommer snart att publiceras.** De här fragmenten beskriver ytan som de ska utsätta

> **StyloBot-utsläppsserien**
> 
> 1. [**Behavior, Inte Identitet**](/blog/stylobot-fingerprint): varför StyloBot modellerar klienter beteendemässigt
> 2. [**Behavior-Är medveten ASP.NET UI**](/blog/behaviour-aware-ux): servern
> 3. [**Att hitta och lösa gränslös tillväxt i långtids-, -- och / eller körande- -.-nätverk**](/blog/stylobot-release-reliability): den tillförlitlighetsdisciplin som gör motorn tråkig i produktionen
> 4. **Behavior-Akvis typscript-UI**: den här artikeln
> 5. [**Sidecar-arkitekturen**](/blog/sidecar-architecture): hur detektoren kopplas till icke--.NET-skivor
> 6. [**Att lära sig bli snabbare**](/blog/stylobot-release-learning): adaptiv inlärningssystem, ,, fyra, M SK2, övningsminnet, ,, och bedömningsキャッシュen
> 7. [**Att testa det som inte stannar kvar**](/blog/stylobot-release-nondeterministic-testing): kontrolldisciplinen : en BDF-fielddriver regression M SK2 belastning , och kalibrering
> 8. [**StyloExtract - en lokal lärande HTML omvandlare till Markdown**](/blog/stylobot-release-styloextract): HTML, →, Markdown-lagret som paras med detektorn, ,, walkersbug lucidVIEW som fångade, M SK3, och den hundföda-loopan som gjorde det ärligt

StyloBots detektormotor är skriven i ASP, ., NET Core och :, ett högpresterande ramverk med utmärkta asyncka primitiver. ,, sub, -, millisekunderlig värme och latens på banan - och tjugo år av produktionskapande bakgrund. [Att lära sig bli snabbare](/blog/stylobot-release-learning): när fingeravtrycket har setts tillräckligt noggrant, , hoppar bedömande kasken över det fulla spårningsröret och svarar i mikrosekunder

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

# Inleiding

De flesta botdetektorer som finns i TypeScripts ekosystem ger dig en av två saker - : - ett fingeravtryck från en webblader som talar om *klient* ser automatiserad ut, eller en regex över Benutzern -En agentsträng som fångar bara de robotar som annonserar sig själva

Inget av dem är användbart på applikationslayern.

[botd](https://github.com/fingerprintjs/BotD) drivs i webbläsaren och har ingen bild av begäranschefer, fingeravtryck från TLS , inspelningsbeteende, eller IP-värdeM SK3 En huvudlös Chromium och en Googlebot ser likadana ut som den`is-bot`, `isbot`) fånga bara det som väljer att bli fångad, . nätverk, - lagerprodukter, | ( | Cloudflare Bot Management, , | DataDome, M SK5 | Block på gränsen men inte personaliserad på applikationslayret, ♫ . | Du får reda på att en begäran blockerades, ♪ , | aldrig att en begäran som nådde din app hade en ♪0.87 | bot sannolikhet och en medelriskgrupp, \ , | så din kontosida kunde ha visat en friktionstillgång, och din analys skulle kunna ha uteslutit den, ♪

Vad StyloBot ger din TypeScript-app är en bedömelse: ett tryckt resultat med en kontinuerlig bot sannolikhetsscore , en klassificering (`AiBot`, `Scraper`, `GoodBot`, `MaliciousBot`,, ...),, en riskgrupp, ,, en tillämpt åtgärd, M SK3, och ett hotsvärde. ., vad du gör med det Urteilet är upp till dig.

Den här artikeln går genom ett exemplar av butiksfront och visar hur det Urteilet blir till applikationsbeteende på varje lager.

[TOC]

# Bedömelsen

Innan något annat kommer det här:

```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'
}
```

En kassa slutpunkt kan kväva `Medium` risk istället för att blockera det. En API kan `AiBot` trafik från hastigheten-begränsa kvoten utan att räkna det mot verkliga användaren. Ett loggande Formular kan lägga till bevis `Elevated` risk utan att röra vid upplevelsen för `Low` risk-biträdare. Inget av detta är möjligt om det detekterade resultatet är en booleer på nätverkslayret

Riskband i ordning: `VeryLow`, `Low`, `Elevated`, `Medium`, `High`, `VeryHigh`, `Verified`.

# Installation

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

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

`@stylobot/core` har noll drivtids beroenden. Den innehåller typdefinitionerna , den REST-clienten , den header-parsern M SK3 och ( optionellt Mska5 gRPC-clientet Msaka6 Den fungerar i Node M Ska7 Deno M ska8 och Bun utan modifiering

`@stylobot/node` erbjuder Express middleware, en Fastify- plugin , template helper utilities, och den bedömande injicernM SK3 det beror på `@stylobot/core` och inget annat vid starttid.

# Serverytan

## Hur dömelsen når din mellanverk

Mittvaran har tre olika trybar . Alla tre producerar samma `req.stylobot` form. Vilken som du använder beror på var i din infrastruktur detektor körs

```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` Modus**Detektorn kördes uppströms vid portaalet.;.Mittvaran läser injicerade opskrifte utan nätverkssändning... Detta är produktionsmönstret när en Caddy eller YARP portaal sitter framför.

**`grpc` Modus**: mellanvaran ropar på bibilen direkt via HTTP /2. minst latens `@grpc/grpc-js`. Detailen om gRPC-gränssnittet och [Sidecar-arkitekturen artikel](/blog/sidecar-architecture).

**`api` Modus**: samtal `POST /api/v1/detect` över HTTP/1.1. Även återvänder `reasons` ( `signals` ( den fulla svarta brädans tillståndet ) som andra former utelämnar

Mittvaran misslyckas alltid att öppna. Om bibilen inte är tillgänglig eller går ut `req.stylobot` ställs till ett tillåtande tomt slutsats och begäran fortsätter.

## Att koppla upp det

**Express:**

```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 }));
```

**Fastify:**

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

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

Lägg till en typdeklaration om du vill ha skrivna tillgångar på Fastify requests:

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

# Storefronten

Samma exemplar i butiken från [ASP.NET artikel](/blog/behaviour-aware-ux), produktsida , utgångslistan, inloggningM SK3 newslettern M SK4 men i TypeScript . Det detekterade resultatet är samma МSK6 vad som ändrar är hur ytan exponerar det

## Page 1: Artikelsidan

Kursskrapper börjar här. De vill ha namn på produkterna , priser M SK2 och lagernivåer . Ni vill ha katalogen indexerad av sökmotorer och användbar för människor utan att ge kommersiella signaler till systematiskt skörd

*Scenario: en priskrapper träffar `/products/:id`. Den ser inga rabattkoder , ingen tillsättning - till M SK3 kartknappen MSC4 och inget köpsignal värd att agera på*

Routenoperatören:

```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',
  });
});
```

Vorlagen (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}}
```

Tre mönster som används här. Crawler-differentiering : `SearchEngine` och `VerifiedBot` få strukturerade metadata utan kommersiella signaler , vilket är vad de faktiskt behöver. Försämpningstarkulationen : en lojalitetskod som bara visar sig vara lågt M SK3 risktrafiken är mindre benägen att hamna på ett kupongforum `Challenge` är en varning, inte en block

## sida 2: Kasse

Automatisering av bedrägerier, korttestare , kupong brute- kraftverkareM SK3 och skriptade retryr förbereder sig alla vid affärskontrollenMSC4 målet är att försvara djupt utan att behöva någon CAPTCHA

*Scenario: en kupong - testbot träffar `/checkout`. Den ser ett dödt - slut meddelande , skickar ett honungsbehåller, - fyllt form, M SK4 och får en tyst bekräftelse utan att uppmuntra till att pröva igen*

```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
```

Routen:

```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);
});
```

Vorlagen:

```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}}
```

Honigkaka slängning är den viktigaste tekniken här...Fehler återkoppling lär attacker att itera.;. en tyst falsk bekräftelse slösar bort sin tid. ,. Bekräftelsesidan ser identisk ut, oavsett om beställningen var verklig eller slängd. `<div>` wrapper in i ett gemensamt Handlebars del (`{{> honeypot}}`) istället för att upprepa inline-stilblocket på varje skyddad form

## Page 3: logga in

Att logga in är krävbart- när det handlar om mat, . Världen är annorlunda från att lämna pengarna på affären.: ett falskt positivt till affärerna kan förlora en affär.; ett misslyckat krävande.

*Scenariot : en credential- med hjälp av skripta träffar `/login`. Den ser en avskräckande meddelande , täcker honungskålen `/login/denied` innan någon autentisering körs.*

```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>
```

Boten ser att det är avskräckande innan den lägger upp den.. Om den gör det i alla fall, så fångar honungsburken den. `verdict.isBot` är den slutgiltiga servern.

## Page 4: Newsletter

AI-kralare är ett annat problem.., de försöker inte brutera.-, de tvingar in konton eller gröda pris. ;, de smäller in innehåll för modellutbildningar, ., de är ofta ärliga om identitet.M SK4, vilket betyder att rätt lösning är kommersiellt snarare än motsägelsefullt.

*Scenario: GPTBot träffar `/newsletter`. Den ser en datalicensenskaps meddelande istället för en subskryptionspitch*

```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` är inte bara ett annat fientligt etikett.

# Template hjälparbetare

## Injictering av bedömningen i templater

`sbVerdictInjector` kör efter det upptäckande mellanprogramet och sätter bedömelsen på `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` är tillgänglig i varje form `res.locals.sbVerdictScript` är en inline `<script>` tag som sätter `window.__sb` för webbläsarens komponenter.

## Handlebar

Definiera `RISK_ORDER` en gång vid modulspektrum ( använder Nunjucks och EJS hjälparbetarna härunder samma karta ):

```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);
    },
  },
});
```

I maller:

```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}}
```

## Nunjucks

```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

EJS har inga block-helfer, men lokalfunktioner täcker de flesta fall rent

```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>
<% } %>
```

# Browsergränssnittet

## Injicerar bedömningen i webbläsaren

Lägg till: `{{{sbVerdictScript}}}` till ditt layout-kopf. Det renderar som

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

Sedan ladda elementpaketet:

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

## `<sb-gate>`: visa eller gömma innehållet efter riskband

```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>`: välja den första matchande fallen

```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>` tar den första `<sb-case>` vars `max-risk` passar och gömmer resten. Den slutliga `<sb-case>` utan `max-risk` är avfångst `<sb-gate>` och `<sb-adapt>` lyssna på `sb:verdict` händelse och re-värdera om bedömningen ändras efter den första målningen

## `<sb-widget>`: server - skickade flytande fragment

`<sb-gate>` gömmer innehållsclient- sida. Om du behöver en robot för att få en annan markup , inte gömd markup~ , så behöver du fragmentet som skall användas på servern med det detekterande sammanhanget som är tillgängligt i szablonen

`<sb-widget>` läser en vätska-template från en inline `<template>` element, byter alla widget-behov på sidan till en enda cirkel -tripM SK2 och ersätter sig själv med den återvända HTML:

```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>
```

Vorlagens variabler: `isBot`, `botProbability`, `confidence`, `botType`, `botName`, `riskBand`, `recommendedAction`, `threatScore`, `threatBand`. Plus någon `vars` du passerar från servern-sidan när du ska ringa `RenderWidget` direkt.

Alla `<sb-widget>` element som kopplas ihop i samma mikrotask tick är sammanslagna till en fråga. En sida med fem widgetar gör en runda

```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
```

# Summaristabellen

| Komponent ♫ | ♫ Ytan ♫| ♫ Vad gör den ♫
|---|---|---|
| `styloBotMiddleware` | Express | Anhängningar `req.stylobot` via opskrifte / gRPC / REST |
| `styloBotPlugin` | Fastifiera | Samma , som en Fastifier plugin med `decorateRequest` |
| `sbVerdictInjector` | Express | Befolkningar `res.locals.sbVerdict` och `sbVerdictScript` |
| Handlebars hjälper | Server-templates ♫ | ♫ `sbGate`, `sbIsBot`, `sbBotType`, `sbMinRisk` block hjälpmedel |
| Nunjucks-extension | Server-templates | `{% sbgate "Low" %}...{% endsbgate %}` |
| EJS lokaler | Server-templates | `sbBelowRisk()`, `sbAboveRisk()`, `sbIsBot()`, `sbBotIs()` |
| `<sb-gate>` | Browser | Anzeiger M SK2 gömmer innehållet efter riskband `sb:verdict` |
| `<sb-adapt>` | Browser | Välj den första matchande fallen från en lista
| `<sb-widget>` | Browser | | | Fetch server |- | Renderat flytande fragment | МSK3 | Batched per tick
| `StyloBotGrpcClient` | Nod / Bun / Deno M| Direkt gRPC-client för icke-

Nästa i releaseserien: [**Sidecar-arkitekturen**](/blog/sidecar-architecture), hur detektormotoren kopplas till icke-

Om du kom hit från .NET sida [Behavior-Är medveten ASP.NET UI](/blog/behaviour-aware-ux) täcker samma hyllningsmönster med taghelpare och handlingsfilter.;. Den tillförlitlighet som håller maskinens minne, -. Stabilt under långvarig trafik. [Att hitta och lösa gränslös tillväxt i långtids-, -- och / eller körande- -.-nätverk](/blog/stylobot-release-reliability). Livmotorn , brädan, , och kommersiella kontrollen [stylobot.net](https://www.stylobot.net).