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.
Den
@stylobot/coreoch@stylobot/nodenpm-paket kommer snart att publiceras. De här fragmenten beskriver ytan som de ska utsätta
StyloBot-utsläppsserien
- Behavior, Inte Identitet: varför StyloBot modellerar klienter beteendemässigt
- Behavior-Är medveten ASP.NET UI: servern
- Att hitta och lösa gränslös tillväxt i långtids-, -- och / eller körande- -.-nätverk: den tillförlitlighetsdisciplin som gör motorn tråkig i produktionen
- Behavior-Akvis typscript-UI: den här artikeln
- Sidecar-arkitekturen: hur detektoren kopplas till icke--.NET-skivor
- Att lära sig bli snabbare: adaptiv inlärningssystem, ,, fyra, M SK2, övningsminnet, ,, och bedömningsキャッシュen
- Att testa det som inte stannar kvar: kontrolldisciplinen : en BDF-fielddriver regression M SK2 belastning , och kalibrering
- StyloExtract - en lokal lärande HTML omvandlare till Markdown: 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: när fingeravtrycket har setts tillräckligt noggrant, , hoppar bedömande kasken över det fulla spårningsröret och svarar i mikrosekunder
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 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 denis-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.
Innan något annat kommer det här:
// @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.
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.
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
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 ModusDetektorn 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.
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.
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' });
Lägg till en typdeklaration om du vill ha skrivna tillgångar på Fastify requests:
// types.d.ts
import type { StyloBotResult } from '@stylobot/node';
declare module 'fastify' {
interface FastifyRequest { stylobot: StyloBotResult; }
}
Samma exemplar i butiken från ASP.NET artikel, 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
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:
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):
{{! 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
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
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:
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:
{{#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
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.
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>
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.
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
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 är inte bara ett annat fientligt etikett.
sbVerdictInjector kör efter det upptäckande mellanprogramet och sätter bedömelsen på 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 är tillgänglig i varje form res.locals.sbVerdictScript är en inline <script> tag som sätter window.__sb för webbläsarens komponenter.
Definiera RISK_ORDER en gång vid modulspektrum ( använder Nunjucks och EJS hjälparbetarna härunder samma karta ):
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:
{{#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}}
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 har inga block-helfer, men lokalfunktioner täcker de flesta fall rent
// 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>
<% } %>
Lägg till: {{{sbVerdictScript}}} till ditt layout-kopf. Det renderar som
<script>window.__sb = {"isBot":false,"botProbability":0.12,"riskBand":"Low","recommendedAction":"Allow",...}</script>
Sedan ladda elementpaketet:
<head>
{{{sbVerdictScript}}}
<script type="module" src="/js/sb-elements.js"></script>
</head>
<sb-gate>: visa eller gömma innehållet efter riskband<!-- 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<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:
<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
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
| 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, hur detektormotoren kopplas till icke-
Om du kom hit från .NET sida Behavior-Är medveten ASP.NET UI 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. Livmotorn , brädan, , och kommersiella kontrollen stylobot.net.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.