# StyloBot-Release-Serien-:-Behaviour--Aware TypeScript UI

*Bot-Erkennung sollte nicht bei allow ./ .block ?. . Dieser Beitrag zeigt, wie StyloBot' .s Klassifikation Ergebnis wird Anwendung Logik in TypeScript . : . Server Middleware für Express und Fastify . , . Schablone Helfer für Lenker .*

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

> **Das `@stylobot/core` und `@stylobot/node` npm-Pakete werden in Kürze veröffentlicht** Die Schnipsel unten beschreiben die Oberfläche, die sie aussetzen werden

> **StyloBot Release Series**
> 
> 1. [**Verhalten nicht Identität**](/blog/stylobot-fingerprint)Warum StyloBot die Kunden verhaltensmäßig modelliert
> 2. [**Verhalten Bewusst WERDEN**](/blog/behaviour-aware-ux)Die Server-Oberfläche von -, die für Anwendungen von . oder NET bestimmt ist
> 3. [**Ungebundenes Wachstum in langen --Diensten finden und beheben**](/blog/stylobot-release-reliability)Die Zuverlässigkeitsdisziplin, die den Motor in der Produktion langweilig hält
> 4. **Verhalten--Bewusstsein TypScript UI**Dieser Artikel
> 5. [**Die Sidecar-Architektur**](/blog/sidecar-architecture)Wie der Detektions-Engine mit Nicht--.-NET-Stacks verbunden wird
> 6. [**Schneller lernen**](/blog/stylobot-release-learning)Das adaptive Lernsystem (MSK) und der Urteilscache
> 7. [**Testen der Sache, die Won-' sitt Still**](/blog/stylobot-release-nondeterministic-testing)Eine BDF-Datei treibt Regression an.
> 8. [**StyloExtrahieren Sie einen lokalen HTML zu Markdown Konverter**](/blog/stylobot-release-styloextract)Die HTML-Markdown-Schicht, die sich mit dem Detektor verbindet, hat den Walker Bug lucidVIEW gefangen und die Hundefutterschleife, die es ehrlich machte.

StyloBot-'s Detektionsmaschine ist geschrieben in ASP...NET Core...:... ein hochleistungsfähiges Rahmenwerk mit hervorragenden async Primitives...MSC4. sub.MSC5.millisekunden heiß-.path Latency, und zwanzig Jahre Produktionsstamm dahinter Das TypeScript SDK ist die Oberfläche, die den Motor bringt hot messK9s Ausgabe in Node messk10.js Anwendungen und Browser ohne entweder etwas darüber zu wissen, was unter dem passiert ist, .. (. Die heißen -.path-Latency kommt aus dem adaptiven Lernsystem abgedeckt in [Schneller lernen](/blog/stylobot-release-learning)Sobald ein Fingerabdruck mit genügend Vertrauen gesehen wurde, überspringt der Urteilscache die vollständige Detektor-Pipeline und antwortet in Mikrosekunden.

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

# Einleitung

Die meisten Bot-Erkennung im TypeScript Ökosystem gibt Ihnen eines von zwei Dingen .: , ein Browser-Fingerabdruck, der Ihnen sagt, ob die *Kunde* sieht automatisiert aus oder ein Regex über dem User-String, der nur die Bots fängt, die sich selbst ankündigen

Auch bei der Applikationsschicht nicht nützlich

[Abbott](https://github.com/fingerprintjs/BotD) läuft im Browser und hat keine Ansicht von Anfrage-Headern., , TLS Fingerabdrucke ,, , Sitzungsverhalten oder IP Reputation , . , Ein kopfloses Chrom und ein Googlebot aussehen identisch mit ihm ,. , Pure UA Matching Pakete , ( ,`is-bot`, `isbot`)-Schnappen Sie nur, was sich in zu werden gefangen entscheidet ..-Netzwerk .--Schicht-Produkte ?(-Cloudflare Bot Management - ,-DatenDome-)-Block am Rand, aber nicht an der Applikationsschicht personalisieren können . .-Sie lernen eine Anfrage wurde blockiert ,-Schneid nie, dass eine Anfrage, die Ihre App erreicht hatte eine Wahrscheinlichkeit und ein Medium Risikoband . ,-, so dass Ihre Checkout-Seite könnte gezeigt haben, einen Reibungsschritt und Ihre Analysen könnten es ausgeschlossen haben .,-Alle ohne Fehlercode .

Was StyloBot Ihrer TypeScript-Anwendung gibt, ist ein Urteil ein typisiertes Ergebnis mit einer kontinuierlichen Bot Wahrscheinlichkeitsbewertung.`AiBot`, `Scraper`, `GoodBot`, `MaliciousBot`Was Sie mit diesem Urteil tun, liegt bei Ihnen.

Dieser Artikel geht durch ein Beispiel storefront und zeigt, wie dieses Urteil wird Anwendungsverhalten an jeder Schicht .: ? Server-Middleware ?, ? Vorlage Helfer ? , ? und Browser-Komponenten ?

[TOC]

# Das Urteil

Vor etwas anderem ist dies, was ankommt.

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

Ein Checkout-Endpunkt kann drosseln `Medium` Risiko statt zu blockieren Eine API kann ausschließen `AiBot` Verkehr von rate---Limit-Quoten, ohne es gegen reale Benutzer zu zählen, ein Anmeldeformular kann proof-MSC2-of---Arbeit für `Elevated` Risiko, ohne die Erfahrung für `Low` Risiko-Besucher-.-Nichts davon ist möglich, wenn das Erkennungsergebnis ein Boolean an der Netzwerkschicht ist..-

Risikobänder in der Reihenfolge `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` hat Null Runtime Abhängigkeiten...... Es enthält die Typdefinitionen...... den REST Client... den Header Parser... und... den gRPC Client... es funktioniert in Node... und Bun ohne Modifikation....

`@stylobot/node` bietet Express-Middleware, ein Fastify-Plugin, einen Vorlagenhilfedienst und den Urteilsinjektor an. `@stylobot/core` und nichts anderes zur Laufzeit

# Die Serveroberfläche

## Wie das Urteil Ihre Middleware erreicht

Die Middleware hat drei Modi: Alle drei produzieren die gleichen `req.stylobot` Welche Sie verwenden, hängt davon ab, wo in Ihrer Infrastruktur-Erkennung läuft.

```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**Die Middleware liest injizierte Header ohne Netzwerkanruf. Das ist das Produktionsmuster, wenn ein Caddy- oder YARP-Gateway vorn sitzt.

**`grpc` Modus**Die Middleware ruft das Sidecar direkt über HTTP an. `@grpc/grpc-js`Die gRPC-Schnittstelle und die Sidecar-Deployment Details sind in der [Sidecar Architektur Artikel](/blog/sidecar-architecture).

**`api` Modus**Anrufe `POST /api/v1/detect` auch über HTTP-/1.1.-Retouren `reasons` Beiträge von (, Per--, Detektor und `signals` Die volle Tafel Zustand, dass die anderen Modi weglassen

Die Middleware immer fehlschlägt offen, wenn das Sidecar unerreichbar ist oder mal aus,, `req.stylobot` wird zu einem freisprechenden leeren Urteil gesetzt und die Anfrage geht fort

## Verkabelung

**Ausdrucken**

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

**Befestigen Sie die :**

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

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

Fügen Sie eine Typ-Deklaration hinzu, wenn Sie den eingegebenen Zugriff auf Fastify Requests wünschen.

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

# Das Schaufenster

Die gleiche Probe storefront von der [ASP-.-NET-Artikel](/blog/behaviour-aware-ux)Produktseite , checkout-, login-Newsletter ,, aber in TypeScript. Das Detektionsergebnis ist das gleiche ;, was sich ändert, wie die Oberfläche es auslegt.

## Seite Die Produktseite

Preiskratzer beginnen hier.Sie wollen Produktnamen, Preise und Lagerniveaus. Sie möchten den Katalog indexierbar durch Suchmaschinen und nützlich für Menschen, ohne die kommerziellen Signale an systematische Ernte zu übergeben.

*Szenario: Ein Preiskratzer trifft `/products/:id`Es sieht keinen Rabattcode und kein Kaufsignal, das es wert ist auf zu handeln.*

Der Routenbetreuer

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

Die Schablone (Handgriffe ):

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

Drei Muster im Gebrauch hier . . . . Crawler Differenzierung . `SearchEngine` und `VerifiedBot` erhalten Sie strukturierte Metadaten ohne kommerzielle Signale, die sie tatsächlich benötigen, um . . Discount-Targeting .: . ein Treuecode nur zu niedrig angezeigt ?- Risiko Verkehr ist weniger wahrscheinlich, auf einem Gutschein Forum enden . `Challenge` ist eine Warnung, die nicht ein Block sein kann.

## Seite Auschecken

Betrugsautomatisierung,-Kartentester,-Gutscheinbrute--Streitkräfte,- und scripted Retries alle konvergieren bei Checkout.-Das Ziel ist die Verteidigung in der Tiefe ohne eine CAPTCHA---erste Erfahrung für jedermann.

*Szenario: ein Gutschein für Bot-Hits `/checkout`Es sieht eine tote message messaging messing send , die ein honigpot , - , ausgefülltes Formular , , , und erhält eine stille Bestätigung ohne erneuten Anreiz ,. ,*

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

Die Route

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

Die Vorlage

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

Honeypot-Rückwurf ist hier die Schlüsseltechnik..-Fehler-Feedback lehrt Angreifer zu iterieren, ;-eine stille gefälschte Bestätigung verschwendet ihre Zeit und die Bestätigungsseite sieht identisch aus, ob der Auftrag real war oder verworfen wurde.MSC3-Express Checkout als Vertrauensvorteil. (-kein Default-)-Mittel nur Sitzungen, die es verdient haben, den kürzeren Pfad erhalten zu haben. `<div>` Wrapper in einen geteilten Lenker teilweise`{{> honeypot}}`Statt den Inline-Stilblock auf jeder geschützten Form zu wiederholen

## Seite Anmeldung

Login is credential---Territorium - .-Der Handel ist anders als Checkout-:-ein falsches positives an der Kasse kann einen Verkauf verlieren , ;-ein verpasstes Credential---Füllen Angriff verliert ein Konto , .-Die Durchsetzung kann strenger sein ,.-

*Szenario: Ein Credential-Skript trifft `/login`Es sieht eine abschreckende Nachricht, die den Honigtopf füllt, und wird zu `/login/denied` bevor eine Authentifizierung ausgeführt wird.*

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

Der Bot sieht die Abschreckung vor dem Einreichen, wenn er ohnehin eingibt, der Honeypot fängt ihn, wenn es irgendwie das Honigtopf vermeidet. `verdict.isBot` ist die letzte Server-Prüfung, bevor jede Authentifizierungslogik ausgeführt wird, drei Schichten, alle kleinen

## Seite Newsletter

KI-Crawler sind ein anderes Problem. Sie versuchen nicht, Brute--force Konten oder Erntepreise zu ernten.

*Szenario: GPTBot-Treffer `/newsletter`Es sieht eine Datenlizenz-Nachricht statt eines Abonnements pech... Seine Einreichung wird stillschweigend verworfen.*

```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` ist nicht nur ein anderes feindliches Label, es ist eine Klassifizierung, die eine geschäftliche Antwort ermöglicht... Eine Lizenz-Nachricht ist nützlicher als ein 403:. Es sagt einem Bediener, womit sie es zu tun haben und gibt ihnen eine Aktion zu ergreifen."..

# Vorlagenhelfer

## Injizieren des Urteils in Vorlagen

`sbVerdictInjector` läuft nach der Erkennung Middleware und setzt das Urteil auf `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` ist in jeder Vorlage erhältlich. `res.locals.sbVerdictScript` ist ein Inline `<script>` Tag, das setzt `window.__sb` für die Browser-Komponenten

## Lenker

Definieren `RISK_ORDER` einmal im Modulbereich verwenden die Helfer Nunjucks und EJS unten dieselbe Karte

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

In Vorlagen

```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 hat nicht Block-Hilfskräfte, sondern lokale Funktionen decken die meisten Fälle sauber ab.

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

# Die Browseroberfläche

## Injizieren des Urteils in den Browser

Hinzufügen `{{{sbVerdictScript}}}` zu deinem Layout-Kopf, es rendert als,:

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

Dann laden Sie die Elemente Bundle-:

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

## `<sb-gate>`Inhalt durch Risikoband anzeigen oder ausblenden

```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>`Wählen Sie den ersten passenden Fall

```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>` wählt die erste `<sb-case>` deren `max-risk` passt und versteckt den Rest `<sb-case>` ohne `max-risk` ist der Catch---all-. `<sb-gate>` und `<sb-adapt>` Hören Sie auf die `sb:verdict` Ereignis und neu bewerten, wenn sich das Urteil nach der ersten Farbe ändert.

## `<sb-widget>`:-Server , --rendered Flüssige Fragmente

`<sb-gate>` blendet Content Client aus--Seite. Wenn Sie einen Bot benötigen, um verschiedene Markups zu empfangen, müssen Sie das Fragment auf dem Server mit dem Erkennungskontext der Vorlage darstellen.

`<sb-widget>` liest eine flüssige Vorlage aus einer Inline `<template>` Element: , chargt alle Widget-Anfragen auf der Seite in eine einzelne Runde und ersetzt sich durch die zurückgegebene HTML-Version.

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

Vorlagenvariablen `isBot`, `botProbability`, `confidence`, `botType`, `botName`, `riskBand`, `recommendedAction`, `threatScore`, `threatBand`Zusätzlich zu jeder `vars` wenn Sie vom Server übergeben werden. `RenderWidget` direkt - .-

Insgesamt `<sb-widget>` Elemente, die in der gleichen Microtask-Tick verbinden werden in eine Anfrage gechargt.Eine Seite mit fünf Widgets macht eine Runde.

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

# Übersichtstabelle

Komponente |, Schicht |, was es tut
|---|---|---|
| `styloBotMiddleware` Anhängungen für Express-Anhänge `req.stylobot` über Kopfzeilen
| `styloBotPlugin` Verfeinern Sie das Plugin "Befestigen" mit `decorateRequest` |
| `sbVerdictInjector` Express-Bevölkerung `res.locals.sbVerdict` und `sbVerdictScript` |
Benutzer-Helfer für die Handgriffe Servervorlagen `sbGate`, `sbIsBot`, `sbBotType`, `sbMinRisk` Block-Hilfskräfte
Server-Vorlagen für die Erweiterung um Nunjucks `{% sbgate "Low" %}...{% endsbgate %}` |
EJS-Einheimische Servervorlagen `sbBelowRisk()`, `sbAboveRisk()`, `sbIsBot()`, `sbBotIs()` |
| `<sb-gate>` Browser Anzeige des / Ausblenden-Inhalts durch Risikoband lauscht für `sb:verdict` |
| `<sb-adapt>` Browser Wählen Sie den ersten passenden Fall aus einer Liste
| `<sb-widget>` Browser für |, Fetch-Server, -, gerendert Flüssiges Fragment, ,, gechargt pro Zecke, MSC4,
| `StyloBotGrpcClient` Direkter gRPC-Client für Nicht---Middleware-Anwendung

Der Nächste in der Release-Serie [**Die Sidecar-Architektur**](/blog/sidecar-architecture)Wie die Detektions-Engine mit Nicht--.-NET-Stacks verbunden wird, ist das Go SDK®,. Das Caddy-Plugin und die gRPC-Schnittstelle, die es miteinander verbinden.

Wenn Sie hier von der Seite des .-NET angekommen sind [Verhalten Bewusst WERDEN](/blog/behaviour-aware-ux) deckt die gleichen storefront Muster mit Tag-Helper und Aktionsfiltern, die Zuverlässigkeit rework, die den Motor Speicher hält .- ist unter anhaltendem Verkehr in [Ungebundenes Wachstum in langen --Diensten finden und beheben](/blog/stylobot-release-reliability)Live-Engine, Dashboard und kommerzielle Steuerungen am [Stylobot-.-net](https://www.stylobot.net).