StyloBot Release Series: Behavior-Aware TypeScript UI (Italiano (Italian))

StyloBot Release Series: Behavior-Aware TypeScript UI

Monday, 01 June 2026

//

21 minute read

La rilevazione del bot non dovrebbe fermarsi a permettere/block. Questo post mostra come il risultato della classificazione di StyloBotM SK2 diventi la logica dell'applicazione nel TypeScript : middleware del server per Express e FastifyMSC4 helpers dei template per le barre di comandosMST5 NunjucksMSL6 e EJSMLS7 e componenti web del browserMSSK8 così che l'interfaccia utente possa modellare l'esperienza invece di infilare la frizione dopo il fattoMSS9

StyloBot

L'informazione figura nella parte dispositiva. @stylobot/core e @stylobot/node I pacchetti npm verranno pubblicati tra poco. I frammenti sotto descrivono la superficie che esponeranno.

StyloBot Release Series

  1. Comportamento, Non identità: perché StyloBot modella i clienti comportamentalmente
  2. Comportamento-Aware ASP.NET UI: il server- la superficie restituita per le applicazioni .NET
  3. Trovare e riparare la crescita senza limiti nei servizi NET Long-Running .: la disciplina di affidabilità che mantiene il motore noioso nella produzione.
  4. Comportamento-Conoscere l'interfaccia grafica del tipoScript: questo articolo
  5. L'architettura della Sidecar: come il motore di rilevazione si collega a stack non -.NET
  6. Imparare a diventare più veloce: il sistema di apprendimento adattativo, quattroM SK2 memoria a livello di livello+, e la cassa del verdetto
  7. Provare a fare ciò che non si ferma.: la disciplina di verificazioneM SK1 un file BDF guida la regressione , carico, e calibrazione
  8. StyloExtract - un convertitore locale di HTML per Markdown: l'HTML→Layere Markdown che si combina con il rilevatore , il walkeder bug lucidVIEW catturatoM SK3 e la catena di cibo per cani che lo rende onesto

Il motore di rilevamento del StyloBot' è scritto in ASP.NET CoreM SK2 un sistema ad alta performance - con eccellenti primitivi asincentiMSC4 sotto-- millisecondi di latenza del percorso MNK6 e 20 anni di pedigree produttivo dietro di essoMRK8 Il TypeScript SDK è la superficie che porta il motoreMST9 la fuoriuscita in NodeMst10js applicazioni e browser senza bisogno di sapere nulla su ciò che è successo sotto l'aspetto SST11 (La latenza di percorso \MST13\ viene dal sistema di apprendimento adattabile coperto Imparare a diventare più veloce: una volta che l'impronta digitale è stata vista con abbastanza sicurezza, il cache del verdetto supera la catena di rilevamento completa e risponde in microsecondi .)

Introduzione

La maggior parte della rilevazione dei robot disponibile nell'ecosistema di TypeScript ti dà una delle due cose: un'impronta digitale del browser che ti dice se il cliente Sembra automatizzato, o un regex sull'User-String Agent che cattura solo i bot che si annuncianoM SK2

Nessuno dei due è utile nella stratagemma dell'applicazione.

Si trattava di un sistema che si basava su una serie di fattori, come il tempo, l'energia e la capacità. funziona nel browser e non ha una visuale dei titoli di richiesta, impronte digitali TLSM SK1 comportamento della sessione, o reputazione IP . Un Chromium senza testa e un Googlebot sembrano identici a luiMSC4 Pacchetti di compatibilità Pure UA Mスク5is-bot, isbot) cattura solo quello che si opta per essere catturatoM SK1 Network-Producti di strata (Cloudflare Bot Management+, DataDome+) blocca al limite ma non può personalizzarlo nella strata dell'applicazione+M SK6 Avete imparato che una richiesta è stata bloccata=, mai che una domanda che ha raggiunto la vostra app abbia avuto una probabilità di bot e una fascia di rischio medio=M SK9 così la vostra pagina di pagamento avrebbe potuto mostrare un passo di frizione e le vostre analisi l'avrebbero potuto escludere+MSC10 tutto senza un codice d'errore*.

Quello che StyloBot dà alla vostra applicazione TypeScript è un verdetto: un risultato digitato con un punteggio di probabilità continua del bot, una classifica M SK2AiBot, Scraper, GoodBot, MaliciousBot, ...), una fascia di rischioM SK2 un'azione consigliata, e un punteggio per la minacciaMSC4 Quello che fate con quel giudizio dipende da voi.

Questo articolo passa attraverso un esempio di storefront e mostra come quel giudizio diventi il comportamento dell'applicazione a ogni strato: middleware del server, helpers dei templateM SK2 e componenti del browserMSC3

Il verdetto

Prima di ogni altra cosa, questo è ciò che arriva:

// @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 punto di fine del checkout può gonfiare. Medium Rischio invece di bloccarlo. Un API può escludere AiBot traffic from rate-limit quotas without counting it against real users Elevated Rischio senza toccare l'esperienza per Low Visitori a rischio. Niente di questo è possibile se il risultato della rilevazione è un boolean alla strata di rete.

Bande di rischio in ordine: VeryLow, Low, Elevated, Medium, High, VeryHigh, Verified.

L'installazione

npm install @stylobot/core @stylobot/node

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

@stylobot/core Ha zero dipendenze del tempo di esecuzione. contiene le definizioni di tipo, il cliente RESTM SK2 il parsatore di header , e MSC4 opzionealmenteMST5 il client gRPCMSV6 Funziona in NodeMSL7 DenoMsl8 e Bun senza modificazioniMSR9

@stylobot/node fornisce Express middleware, un plugin Fastify, utilità per l'aiuto di schemiM SK2 e l'injectore del verdetto . dipende da @stylobot/core e niente altro al runtime.

La superficie del server.

Come il verdetto raggiunge la vostra middleware

Il middleware ha tre modi. Tutti i tre producono lo stesso. req.stylobot forma. Quella che usate dipende da dove funziona la vostra detezione dell'infrastruttura

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 detezione è andata in arrivo alla porta ; il middleware legge i titoli inseriti senza chiamare la rete МSK2 Questo è il modello di produzione quando una porta Caddy o YARP si trova davanti a noi

grpc Mode: il middleware chiama la sidecar direttamente su HTTP/2. La latenza più bassaM SK2 non c'è JSON overhead @grpc/grpc-js. L'interfaccia del gRPC e i dettagli di deployment della sidecar sono in articolo sull'architettura della carrozza laterale.

api ModeLe chiamate : POST /api/v1/detect su HTTP/1.1. Ritorna anche reasons (perM SK1contributioni dei rilevatori) e signals (lo stato completo della lavagna) che gli altri modi omitanoM SK2

Il middleware continua a fallire quando si apre. Se l'autostrana è inaccessibile o non funziona. req.stylobot è stabilito ad un verdetto vuoto permisivo e la richiesta continua.

Costruirlo.

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

Add a type declaration if you want typed access on Fastify requests:

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

La vitrina.

Lo stesso campione di magazzino di Article ASP.NET, la pagina del prodottoM SK1 il pagamento, l'inscrittaMST3 il newsletterM ST4 ma in TypeScriptMst5 Il risultato della rilevazione è lo stessoMSST6 quello che cambia è come la superficie lo esponeMSS7

La pagina 1: La pagina del prodotto

I tiratori di prezzi iniziano qui. Vogliono i nomi dei prodotti, i prezziM SK2 e i livelli di quotaMSC3 Vogliate il catalogo indicebile da motori di ricerca ed utile per gli umani senza dare i segnali commerciali a una raccolta sistematica .

Scenario: un scafo di prezzo colpisce /products/:id. Non vede un codice di sconto, nessun aggiungimentoM SK2a- bottone di carto , e nessun segnale d'acquisto che valga la pena di agire suMSC5

L'operatore del percorso:

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

La template (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 modelli in uso qui. Differenzazione Crawler: SearchEngine e VerifiedBot ottenere metadati strutturati senza segnali commerciali, che è quello di cui hanno veramente bisogno. Targeting discountM SK2 un codice di lealtà mostrato solo a basso livelloMSC3 il traffico a rischio è meno probabile di finire su un forum per i voucher . Frizione progressivaMST5 Challenge è un avvertimento, non è un blocco. Le sessioni sospette possono ancora comprarsi

La pagina 2: Checkout

Automatizzazione della frode, testatori di carte, voucher bruteM SK2 forscheriMSC3 e retreazioni scritte tutte convergono al checkout . L'obiettivo è la difesa in profondità senza CAPTCHAMST5la prima esperienza per tuttiMst6

Scenario: un voucher-test bot colpisce /checkout. Vede un mortoM SK1 messaggio finale, invia una vasca da miele - modulo completatoMSC4 e riceve una conferma silenziosa senza incoraggiamento a riprovare.

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

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

La template:

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

La discardazione del vaso di miele è la tecnica chiave qui. Il feedback sull'errore insegna agli attacchi a ripetere; una falsa conferma silenziosa spreca il loro tempo , e la pagina di conferma sembra identica se l'ordine era reale o discardatoM SK3 Il checkout Express come beneficio di fiducia MSC4 non è un predefinitoMST5 significa che solo le sessioni che lo hanno guadagnato ottengono il percorso più breveM ST6 Nella produzioneMst7 il fattore del vaso <div> un wrapper in una maniglia condivisa ({{> honeypot}}) invece di ripetere il blocco dello stile inline su ogni forma protetta.

Page 3: Login

L'ingresso è affidabile-territorio di affichageM SK1 Il commercio-l'affetto è diverso dal checkout : un falso positivo al checkout può perdere una venditaMSC4 un affidabile mancanteMST5 l'attacco di affari perde un accountMst6 L'applicazione può essere più rigorosaMSSK7

Scenario: credenzale- battito dello script /login. Vede un messaggio di dissuasione , riempie il vaso con il miele, e viene spinto verso /login/denied prima di qualsiasi authenticazione funziona.

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>

Il bot vede il contenitore prima di presentarlo. Se lo presenta comunqueM SK1 il vaso d'almonde lo cattura. Se in qualche modo evita il vaso di almonde , verdict.isBot è il server finale-controllo laterale prima che una logica di autenticazione funzioni. Tre stratiM SK2 tutti piccoli .

La pagina 4: Newsletter

I crawler artificiali sono un problema diverso. Non stanno cercando di brutalizzare- forzare le account o i prezzi delle raccolteM SK2 stanno ingaggiando contenuti per la formazione dei modelli . Sono spesso onesti riguardo all'identitàMSC4 il che significa che la risposta giusta è commerciale piuttosto che avversariaMST5

scenario: GPTBot colpisce /newsletter. Vede un messaggio di licenzione dei dati invece che un'affidazione di sottoscrizione. La sua ricevuta è discesa silenziosamenteM SK2

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 Non è solo un'altra etichetta ostile.

Aiutitori per i moduli

Inserire il verdetto in schemi.

sbVerdictInjector va avanti dopo il middleware di rilevazione e mette il verdetto. 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 è disponibile in ogni modello. res.locals.sbVerdictScript è un'inline. <script> tag che stabilisce window.__sb per i componenti del browser.

Le barre di mano

Definire RISK_ORDER una volta nella scala di moduli ( i Nunjucks e gli assistenti EJS sotto usano la stessa mappa):

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

Nelle schemi:

{{#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 non ha degli aiuti a blocchi, ma le funzioni locali coprono la maggior parte dei casi in modo pulito:

// 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 superficie del browser

Introduzione del verdetto nel browser

Aggiungere {{{sbVerdictScript}}} alla vostra testa di layout. Rende come:

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

Poi caricate il pacchetto di elementi:

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

<sb-gate>: mostrare o nascondere il contenuto secondo la fascia di rischio

<!-- 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>: scegliere il primo caso corrispondente

<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> Prende il primo. <sb-case> I quali max-risk Si adatta e nasconde il resto. Il finale <sb-case> Non c'è. max-risk è il taglio-toccoM SK1 Entrambi <sb-gate> e <sb-adapt> Ascoltate. sb:verdict evento e ri-evalutare se il verdetto cambia dopo la prima pitturaM SK1

<sb-widget>: server-fragmenti liquidi consegnati

<sb-gate> nasconde il cliente del contenuto-lato. Se avete bisogno di un bot per ricevere un diverso marchoM SK2 non un marcho nascosto , dovete rendere il frammento sul server con il contesto di rilevazione disponibile alla templateMSC4

<sb-widget> legge il foglio Liquido da un'inline. <template> element, batchi tutte le richieste di widget sulla pagina in una singola ronda -trip, e si sostituisce con il HTML riferitoM SK3

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

Variabili del template: isBot, botProbability, confidence, botType, botName, riskBand, recommendedAction, threatScore, threatBand. In più qualsiasi vars Si passa dal server-side quando si chiama. RenderWidget direttamente.

Tutti. <sb-widget> Elementi che si collegano nello stesso ticolo di microtask sono battuti in una sola richiesta. Una pagina con cinque widget fa una volta rotonda

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

Il grafico sintetico.

Componente
styloBotMiddleware Express Attaches req.stylobot attraverso i titoli / gRPC / REST
styloBotPlugin Fastify Lo stesso, come plugin Fastify con decorateRequest
sbVerdictInjector Express Populazioni res.locals.sbVerdict e sbVerdictScript
Aiuti alla barra di comandos Template del server sbGate, sbIsBot, sbBotType, sbMinRisk Aiutatori di blocco
Espansione di Nunjucks Immagini del server {% sbgate "Low" %}...{% endsbgate %}
Locali EJS Servetori templates sbBelowRisk(), sbAboveRisk(), sbIsBot(), sbBotIs()
<sb-gate> Browser mostrare/ nascondere il contenuto secondo la banda di rischioM SK3 ascolta per sb:verdict
<sb-adapt> Browser Prendete il primo caso corrispondente da una lista SSK2
<sb-widget> Browser Server di attivazione- Fragmento liquido consegnatoM SK3 battuto per taglio SSK4
StyloBotGrpcClient Node / Bun

Il prossimo nella serie di release: L'architettura della Sidecar, come il motore di rilevazione si collega a non--. stacks NETM SK2 Go SDK, la plugin Caddy , e l'interfaccia gRPC che lo collega insiemeMSC5

Se siete arrivati qui dal lato .NET, Comportamento-Aware ASP.NET UI copre gli stessi schemi frontali con tag helper e filtri di azione; il ritocco di affidabilità che mantiene la memoria del motore- stabile sotto un traffico sostenuto è in Trovare e riparare la crescita senza limiti nei servizi NET Long-Running .. Motore viventeM SK1 Armatura dashboard, e controlli commerciali a 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.