StyloBot Release Series: Behaviour-Aware TypeScript UI (Français (French))

StyloBot Release Series: Behaviour-Aware TypeScript UI

Monday, 01 June 2026

//

21 minute read

La détection des bottes ne doit pas se terminer par permettre/block. Ce post montre comment le résultat de la classification du StyloBot ' devient une logique d’application dans TypeScriptM SK3 middleware pour les serveurs Express et FastifyMSC4 template helpers for HandlebarsMST5 NunjucksMSL6 and EJSMSP7 and browser web components MST8 so your UI can shape the experience instead of bolting friction on after the factMSV9

StyloBot

L'article 5 de la Loi sur l'immigration et le statut des réfugiés @stylobot/core et @stylobot/node Les paquets npm seront publiés prochainement. Les fragments ci-dessous décrivent la surface qu’ils exposeront.

StyloBot Release Series

  1. Comportement, Non identité: pourquoi StyloBot modele les clients de façon comportementale
  2. Behaviour-Aware ASP.NET UI: le serveur- la surface rendue pour les applications .NET
  3. Trouver et rétablir une croissance sans limites dans les services longs-Running .NET: la discipline de fiabilité qui maintient le moteur borne dans la production
  4. Behaviour-Aware TypeScript UI: cet article
  5. L'architecture de la Sidecar: comment le moteur de détection se connecte aux piles NET non -.
  6. Apprentissage pour devenir plus rapide: le système d'apprentissage adaptatif , quatre- mémoire de niveauM SK3 et cache des verdicts
  7. Étudier ce qui ne va pas se tenir: la discipline de vérification : un fichier BDF conduit à la régressionM SK2 charge, et à l'étalonnage
  8. StyloExtract - un convertisseur HTML d'apprentissage local à Markdown: la couche de marquage HTML→ qui se joigne au détecteur , le bug lucidVIEW du fauteuil roulant qui a capté l’image

StyloBot's moteur de détection est écrit dans ASPM SK1NET Core: un cadre de performance élevée - avec d'excellents primitives asynciques M SK4 sous-- millisecondes chaudes - latence des cheminements MSC7 et vingt ans de pedigree de production derrière elle MST8 Le TypeScript SDK est la surface qui permet à l'engineMNK9 de transférer sa sortie en NodeMST10js applications et navigateurs sans qu'il soit nécessaire de savoir quoi s'est passé au-dessous de cette dernière MSS11 MSS12La latence du cheminement chaudeMSSS13 provient du système d'apprentissage adaptatif couvert par Apprentissage pour devenir plus rapide: une fois qu’une empreinte digitale a été vue avec suffisamment de confiance , le cache du verdict dépasse la conduite complète du détecteur et répond en microsecondes

Introduction

La plupart des détecteurs de robots disponibles dans l’écosystème TypeScript vous donnent une des deux choses suivantes : : une empreinte digitale du navigateur qui vous indique si le client apparaît automatisé, ou un regex au-dessus de l'utilisateur-Streinte d'agent qui ne capte que les bots qui s'annoncent eux-mêmes

Aucun des deux n'est utile à la couche d'application.

États-Unis fonctionne dans le navigateur et n’a pas de vue des titres de demande, empreintes digitales TLSM SK1 comportement de session , ou réputation IP. Un Chromium sans tête et un Googlebot semblent identiques à celui-ciis-bot, isbot) saisir uniquement ce qui opte pour être captéM SK1 Réseau - produits de la couche d’application M SK3 Gestion des bots Cloudflare , DataDome MSC5 bloque à l’extrémité mais ne peut pas personnaliser au niveau de l’application. Vous apprenez qu’une demande a été bloquée

Ce que StyloBot donne à votre application TypeScript, c’est un verdict: un résultat type avec une cote de probabilité continue du botM SK1 une classification (AiBot, Scraper, GoodBot, MaliciousBot, ...), une bande de risqueM SK2 une action recommandée, et un score de menaceMSC4 Ce que vous faites avec ce verdict est à votre discrétion

Cet article passe par un échantillon de storefront et montre comment ce verdict devient le comportement d’une application à chaque couche.

Le verdict

Avant tout autre, c’est ce qui arrive.

// @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'
}

Un point final de contrôle peut tourner Medium risque au lieu de le bloquer. Une API peut exclure AiBot -limiter les quotas sans le compter par rapport aux utilisateurs réels . Un formulaire d’inscription peut ajouter la preuve Elevated risque sans toucher l'expérience pour Low n’est pas possible si le résultat de détection est un boolean à la couche réseau.

Bandes de risque en ordre: VeryLow, Low, Elevated, Medium, High, VeryHigh, Verified.

Installation

npm install @stylobot/core @stylobot/node

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

@stylobot/core a des dépendances de temps d'exécution zéro. Il contient les définitions de type , le client REST M SK2 le parteur de titres , et ( optionnellement MSC5 le clients gRPC MST6 Il fonctionne dans Node MSL7 Deno MLSC8 et Bun sans modification MSSC9

@stylobot/node fournit Express middleware, un plugin Fastify, des utilités d'aide à la template , et l'injecteur de verdictM SK3 Il dépend @stylobot/core et rien d’autre à l’heure de démarrage.

La surface du serveur

Comment le verdict atteint votre logiciel de médiation

Le middleware a trois modes. Tous les trois produisent la même req.stylobot forme. Le type d’appareil que vous utilisez dépend de l’endroit où se déroule la détection de votre infrastructure.

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 mode: la détection a été effectuée en amont à l’entrée ; le middleware lit les titres injectés sans appel de réseau

grpc mode: le middleware appele la sidecar directement par HTTP /2. La latence la plus faible @grpc/grpc-js. Les détails de l’interface gRPC et du déploiement des véhicules secondaires figurent dans le Architecture Sidecar article.

api mode: appels POST /api/v1/detect sur HTTP/1.1. Retourne également reasons (parM SK1contributions des détecteurs ) et signals (l'état complet du tableaux de bord ) que les autres modes omettent

Le middleware faille toujours à ouvrir. Si l'automobile n'est pas accessible ou s'éloigne de la route req.stylobot est fixé à un verdict vide permissif et la demande se poursuit.

Établissement d'un réseau

Express:

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:

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

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

Ajouter une déclaration de type si vous voulez un accès type sur les demandes Fastify:

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

L'emplacement du magasin

Le même échantillon d’entrepôt de la Article ASP.NET, page de produit , sortie d'échantillonnage, loginM SK3 bulletin d'informationMSC4 mais dans TypeScriptMNK5 le résultat de détection est le mêmeMMK6 ce qui change c'est la façon dont la surface l'exprimeMRK7

Page 1: La page du produit

Les acheteurs de prix commencent ici. Ils veulent les noms des produitsM SK1 les prix, et les niveaux de stocks . Vous voulez que le catalogue soit indexable par les moteurs de recherche et utile aux humains sans remettre les signaux commerciaux à une récolte systématiqueMSC4

Scénario: un affaiblissement des prix frappe /products/:id. Il n'y a pas de code d'actualisation , il n'existe pas d'addition - à M SK3 bouton de cartouche MSC4 et aucun signal d'achat valable à l'action sur.

Le gestionnaire de la route:

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

Le modèle (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}}

Trois modèles utilisés ici. Différenciation Crawler: SearchEngine et VerifiedBot Obtenir des métadonnées structurées sans signaux commerciaux, ce qu’ils ont réellement besoin de ce type de donnéesM SK1 ciblage à rabais: code de loyauté montré seulement à faible valeurMSC3 le trafic à risque est moins susceptible d’aboutir à un forum sur les bons de commandes Challenge est un avertissement, non un blocM SK1 Les séances suspectes peuvent encore acheter.

Page 2: Dépôt

Automatisation de la fraude, testers de cartes, brute de bonsM SK2 forcers , et retraits scriptés tous convergent à l’étape du débarquementMSC4 L’objectif est une défense en profondeur sans CAPTCHA

Scénario: un bon d'achatM SK1 coupures de bots testés /checkout. Il voit un message mort - message d’achèvement , soumet une poêle de miel MSC3 formulaire rempli M SK4 et reçoit une confirmation silencieuse sans incitation à essayer de nouveau

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

La route:

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

Le modèle:

{{#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}}

L’élimination de la pomme d’herbe est la technique clé à cet égard. La rétroaction des erreurs instructe les attaquants à iterer ; une confirmation fausse silencieuse enfouie leur temps , et la page de confirmation apparaît identique si l’ordonnance était réelle ou discardée M SK3 le versement express comme un avantage de confiance <div> emballer dans une barre de manœuvre partagée partielle ({{> honeypot}}) plutôt que de répéter le bloc de style en ligne sur chaque formulaire protégé .

Page 3: Login

L’adhésion est crédible-territoire d’approvisionnementM SK1Le commerce-la sortie diffère de l’expéditionMSC3un faux positif à la sortie peut perdre une vente

Scénario: un certificat de crédibilité - attaques d’un script /login. Il voit un message de dissuasion , remplit le récipient d’herbe M SK2 et est renversé /login/denied avant que l’authentification ne se déroule.

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);
});
{{#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>

Le robot voit le dissuasif avant de le soumettre. S’il le soumette en tout état de cause , la poêle d’herbe l’a capturéeM SK2 s’il évite quelque peu la poôle d'herbe, verdict.isBot est le serveur final- vérification des côtés avant que toute logique d'authentification ne soit exécutée

Page 4: Bulletin

Les crawleurs de l’AI sont un autre problème.

Scénario: GPTBot frappe /newsletter. Il voit un message de licence de données au lieu d’un pitch d’abonnementM SK1 Sa soumission est discardée silencieusement .

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');
});
{{#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="[email protected]" autocomplete="email" />
  </div>
  <button type="submit" class="btn btn-success">Subscribe</button>
</form>

AiBot n’est pas seulement un autre étiquette hostile, il s’agit d’une classification qui permet une réponse commerciale.

Modèles d'aide

Injection du verdict dans les modèles

sbVerdictInjector fonctionne après le middleware de détection et met le verdict sur res.locals:

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 est disponible dans chaque modèle. res.locals.sbVerdictScript est une ligne en ligne <script> de l'étiquette qui fixe window.__sb pour les composants du navigateur.

Les barres de manutention

Définir RISK_ORDER une fois à l’échelle du module ( les Nunjucks et les Aideurs EJS ci-dessous utilisent la même carte ):

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

Dans les modèles:

{{#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

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());
{% sbgate "Low" %}
  <a href="/checkout/express">Express checkout</a>
{% else %}
  <a href="/checkout">Checkout</a>
{% endsbgate %}

EJS

EJS n’a pas d’aide-bloc, mais les fonctions locales couvrent la plupart des cas de façon propre:

// 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();
});
<% 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>
<% } %>

La surface du navigateur

Injection du verdict dans le navigateur

Ajouter {{{sbVerdictScript}}} à votre tête de planification. Il se traduit comme

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

Puis chargez le paquet d'éléments:

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

<sb-gate>: montrer ou dissimuler le contenu par bande de risque

<!-- 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>: choisissez le premier cas correspondant

<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> Choisissez le premier <sb-case> dont max-risk fit et cache le reste. Le dernier <sb-case> avec aucun max-risk est la prise-tousM SK1Bois <sb-gate> et <sb-adapt> écouter le sb:verdict événement et re-évaluer si le verdict change après la première peinture

<sb-widget>: serveur-fragments liquides remis

<sb-gate> hides content client-side. Si vous avez besoin d'un bot pour recevoir un marquage différent , non camouflage cachéM SK3 vous aurez besoin du fragment qui sera rendu sur le serveur avec le contexte de détection disponible dans la templateMSC4

<sb-widget> lire un modèle liquide d'une ligne en ligne <template> element, batches toutes les demandes de widget sur la page en une seule rondeM SK1trip, et remplace elle-même par le HTML retourné

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

Modèles de modèle: isBot, botProbability, confidence, botType, botName, riskBand, recommendedAction, threatScore, threatBand. Plus tout vars vous passez de côté du serveur- lors d'un appel RenderWidget direct.

Tous <sb-widget> les éléments qui se connectent dans le même tick de microtask sont battus en une seule demande. Une page avec cinq widgets fait une ronde

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

Tableau de synthèse

Component Layer S Ce qu'il fait M
styloBotMiddleware Express Annexes req.stylobot via les titres / gRPC / REST
styloBotPlugin Favoriser Le même , que le plugin Fastify avec decorateRequest
sbVerdictInjector Express Populations res.locals.sbVerdict et sbVerdictScript
Outils d'aide aux barres de manœuvre Modèles de serveur sbGate, sbIsBot, sbBotType, sbMinRisk aides à bloquer
Modèles de serveur {% sbgate "Low" %}...{% endsbgate %}
Locals EJS Modèles de serveur sbBelowRisk(), sbAboveRisk(), sbIsBot(), sbBotIs()
<sb-gate> Browser Afficher/ dissimuler le contenu par bande de risqueM SK3 écouter pour sb:verdict
<sb-adapt> Explorateur Choisissez le premier cas correspondant d’une liste
<sb-widget> Browser Serveur d'acheminement- fragment liquide remisM SK3 battu par tick
StyloBotGrpcClient Node / Bun / Deno S Client gRPC direct pour non--utilisation de logiciels M

Suivant dans la série de sortie: L'architecture de la Sidecar, la façon dont le moteur de détection se connecte à des piles NET autres que -. , le Go SDK, , le Caddy plugin et l’interface gRPC qui les relie ensemble

Si vous êtes arrivé ici à partir du côté .NET Behaviour-Aware ASP.NET UI couvre les mêmes modèles de storefront avec des aides-étiquettes et des filtres d’action; le réaménagement de la fiabilité qui maintient la mémoire du moteur - stable sous un trafic soutenu est en Trouver et rétablir une croissance sans limites dans les services longs-Running .NET. moteur en direct , tableaux de bord M SK2 et contrôles commerciaux à stylobot.net.

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.