@osqd/bothandlerjs 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +157 -1
- package/README.md +1 -1
- package/dist/challenge/index.d.ts +18 -0
- package/dist/challenge/interaction.d.ts +215 -0
- package/dist/challenge/page.d.ts +18 -0
- package/dist/cli.cjs +653 -51
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +653 -51
- package/dist/cli.js.map +1 -1
- package/dist/core.d.ts +15 -0
- package/dist/corpus/index.cjs +20 -0
- package/dist/corpus/index.cjs.map +1 -1
- package/dist/corpus/index.js +20 -0
- package/dist/corpus/index.js.map +1 -1
- package/dist/dashboard/client/actions.d.ts +17 -0
- package/dist/dashboard/client/actor.d.ts +5 -0
- package/dist/dashboard/client/api.d.ts +15 -0
- package/dist/dashboard/client/app.d.ts +37 -0
- package/dist/dashboard/client/bars.d.ts +4 -0
- package/dist/dashboard/client/boot.d.ts +5 -0
- package/dist/dashboard/client/charts.d.ts +25 -0
- package/dist/dashboard/client/css.d.ts +11 -0
- package/dist/dashboard/client/dom.d.ts +55 -0
- package/dist/dashboard/client/draft.d.ts +46 -0
- package/dist/dashboard/client/feed.d.ts +6 -0
- package/dist/dashboard/client/format.d.ts +19 -0
- package/dist/dashboard/client/guard.d.ts +3 -0
- package/dist/dashboard/client/index.d.ts +1 -0
- package/dist/dashboard/client/outcome.d.ts +33 -0
- package/dist/dashboard/client/panels.d.ts +40 -0
- package/dist/dashboard/client/policy.d.ts +25 -0
- package/dist/dashboard/client/query.d.ts +42 -0
- package/dist/dashboard/client/ranges.d.ts +2 -0
- package/dist/dashboard/client/registry.d.ts +4 -0
- package/dist/dashboard/client/replay.d.ts +14 -0
- package/dist/dashboard/client/result.d.ts +10 -0
- package/dist/dashboard/client/store.d.ts +68 -0
- package/dist/dashboard/client/stream.d.ts +14 -0
- package/dist/dashboard/client/tester.d.ts +1 -0
- package/dist/dashboard/client/types.d.ts +69 -0
- package/dist/dashboard/client.generated.d.ts +1 -1
- package/dist/dashboard/page.d.ts +25 -0
- package/dist/dashboard/sections.d.ts +92 -0
- package/dist/dashboard/types.d.ts +2 -82
- package/dist/element/config.d.ts +135 -0
- package/dist/element/index.cjs +4844 -0
- package/dist/element/index.cjs.map +1 -0
- package/dist/element/index.d.ts +137 -0
- package/dist/element/index.js +4825 -0
- package/dist/element/index.js.map +1 -0
- package/dist/index.cjs +644 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +635 -51
- package/dist/index.js.map +1 -1
- package/dist/metrics.d.ts +27 -0
- package/dist/policy/index.d.ts +1 -1
- package/dist/policy/presets.d.ts +47 -0
- package/docs/challenge/index.md +1 -0
- package/docs/challenge/interaction.md +325 -0
- package/docs/course/11-the-challenge.md +36 -1
- package/docs/course/13-operating-it.md +20 -2
- package/docs/index.md +2 -0
- package/docs/operations/dashboard.md +2 -0
- package/docs/operations/embedding.md +348 -0
- package/docs/operations/index.md +1 -0
- package/docs/policy/presets.md +53 -0
- package/docs/start/choosing-a-policy.md +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/dashboard/client/css.ts","../../src/dashboard/client/dom.ts","../../src/dashboard/client/boot.ts","../../src/dashboard/client/app.ts","../../src/dashboard/client/outcome.ts","../../src/dashboard/client/query.ts","../../src/dashboard/client/store.ts","../../src/dashboard/client/api.ts","../../src/dashboard/client/format.ts","../../src/dashboard/client/replay.ts","../../src/dashboard/client/actions.ts","../../src/dashboard/client/bars.ts","../../src/dashboard/client/actor.ts","../../src/dashboard/client/result.ts","../../src/dashboard/client/guard.ts","../../src/dashboard/client/ranges.ts","../../src/dashboard/client/draft.ts","../../src/dashboard/client/policy.ts","../../src/dashboard/client/feed.ts","../../src/dashboard/client/stream.ts","../../src/dashboard/client/panels.ts","../../src/dashboard/client/charts.ts","../../src/dashboard/client/registry.ts","../../src/dashboard/client/tester.ts","../../src/dashboard/client/index.ts","../../src/element/index.ts","../../src/element/config.ts","../../src/dashboard/page.ts"],"sourcesContent":["/**\n * Selector escaping, on its own so both halves can have it.\n *\n * `dom.ts` is the browser client's entry into the document and carries module state that\n * has to be initialised in order; the embeddable element imports it lazily for exactly\n * that reason. But the element also assembles selectors from developer-supplied panel ids\n * *synchronously*, before any of that has happened — so the escape lives here, in a leaf\n * with nothing behind it, rather than being duplicated or dragging the client graph in\n * early.\n */\nexport function cssEscape(value: string): string {\n return typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\" ? CSS.escape(value) : String(value).replace(/[^\\w-]/g, \"\\\\$&\");\n}\n","/**\n * The page's only way of putting something in the document.\n *\n * `textContent`, always. Every string that reaches this file — a User-Agent, a path, a\n * detector's summary, a rule id somebody typed into the editor — was written by\n * somebody else, and one `innerHTML` anywhere below turns a bot's User-Agent into\n * script running in an operator's browser. A test asserts the served page contains\n * none of the four ways to do that, and `scripts/build-client.mjs` refuses to bundle\n * one.\n */\nimport { cssEscape } from \"./css.js\";\n\nexport function el<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string | null, value?: string | number | null): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className !== undefined && className !== null && className !== \"\") node.className = className;\n if (value !== undefined && value !== null) node.textContent = String(value);\n return node;\n}\n\nexport function svgEl(name: string, attributes: Record<string, string | number> = {}): SVGElement {\n const node = document.createElementNS(\"http://www.w3.org/2000/svg\", name);\n for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, String(value));\n return node;\n}\n\nexport function svgText(attributes: Record<string, string | number>, text: string | number): SVGElement {\n const node = svgEl(\"text\", attributes);\n node.textContent = String(text);\n return node;\n}\n\nexport function clear(node: Node): void {\n while (node.firstChild) node.removeChild(node.firstChild);\n}\n\n/**\n * Ids are base64url today, which needs no escaping — but a selector assembled from a\n * value is only safe until somebody widens the alphabet, and `CSS.escape` costs\n * nothing. It lives in its own module because the embeddable element needs it without\n * loading this one; re-exported here so the rest of the client reads unchanged.\n */\nexport { cssEscape };\n\n/**\n * Where this dashboard lives.\n *\n * The standalone page owns the whole document; the embeddable element owns a shadow root\n * inside somebody else's. Everything below looks things up through here rather than\n * through `document`, so the same client code drives both — a shadow root is not\n * reachable from `document.getElementById`, and a document is not reachable from a\n * detached fragment.\n */\nlet root: Document | ShadowRoot | HTMLElement = typeof document === \"undefined\" ? (undefined as never) : document;\n\n/**\n * The element the theme tokens hang off.\n *\n * On the page that is the document element. Inside a shadow tree it is the host, because\n * custom properties inherit *into* a shadow root from the host and `:root` matches\n * nothing in there.\n */\nlet themeHost: HTMLElement = typeof document === \"undefined\" ? (undefined as never) : document.documentElement;\n\n/**\n * True when this dashboard is rendered inside somebody else's page.\n *\n * Three things the standalone page may do and an embedded one may not: write its tab and\n * filter into `location.hash` — which is the host's URL, and whose back button would then\n * walk through somebody's tab changes; listen for keys on the window, which hijacks the\n * host's own shortcuts; and rely on fragment navigation, which does not cross a shadow\n * boundary at all.\n */\nlet embedded = false;\n\n/** Points the client at a shadow root. Call it before the rest of the client loads. */\nexport function setRoot(node: Document | ShadowRoot | HTMLElement, host?: HTMLElement): void {\n root = node as Document | ShadowRoot;\n embedded = node !== (globalThis as unknown as { document?: Document }).document;\n if (host !== undefined) themeHost = host;\n}\n\n/** Updates the element the theme hangs off, without disturbing the rest. */\nexport function setThemeHost(host: HTMLElement): void {\n themeHost = host;\n}\n\nexport function isEmbedded(): boolean {\n return embedded;\n}\n\n/**\n * What global-ish events are listened on.\n *\n * The document when this owns the page. The dashboard's own subtree when it does not, so\n * a digit pressed on the host's page is the host's business and not a tab change here.\n */\nexport function eventTarget(): EventTarget {\n return embedded ? (root as unknown as EventTarget) : (globalThis as unknown as EventTarget);\n}\n\n/** The node every lookup runs against. */\nexport function rootNode(): Document | ShadowRoot {\n return root as Document | ShadowRoot;\n}\n\n/** The element carrying `data-theme` and the custom properties. */\nexport function themeElement(): HTMLElement {\n return themeHost;\n}\n\nexport function $(id: string): HTMLElement {\n // `querySelector` rather than `getElementById`, because the latter is a method of\n // Document and of DocumentFragment but the two are reached differently, and one call\n // that works against both is worth more than the microseconds.\n const node = root.querySelector<HTMLElement>(`#${cssEscape(id)}`);\n // Every id this is called with is one the page's own markup declares, so a miss is a\n // typo in a rename rather than a condition to handle. Failing loudly here beats a\n // null dereference three frames later.\n if (node === null || node === undefined) throw new Error(`dashboard: no element #${id}`);\n return node;\n}\n\nexport function byId<T extends HTMLElement>(id: string): T {\n return $(id) as T;\n}\n\n/** A CSS custom property's value, for the charts — which draw in the theme's own colours. */\nexport function css(name: string): string {\n return getComputedStyle(themeHost).getPropertyValue(name).trim();\n}\n\nlet sequence = 0;\n\n/**\n * Ties a `<label>` to the control it names.\n *\n * A label sitting next to an input is a label to a reader who can see the layout and\n * nothing at all to a screen reader: it announces \"edit, blank\" and moves on. Both form\n * builders on this page had that defect, and both were caught by the automated\n * accessibility pass rather than by anybody looking — which is the argument for having\n * one.\n *\n * Only for the controls a label can point at. A group of chips or a segmented control is\n * several buttons, and those carry their own names.\n */\nexport function label(text: string, control: HTMLElement | HTMLElement[]): HTMLLabelElement {\n const node = document.createElement(\"label\");\n node.textContent = text;\n const single = Array.isArray(control) ? (control.length === 1 ? control[0] : undefined) : control;\n if (single !== undefined && /^(input|select|textarea)$/i.test(single.tagName)) {\n if (single.id === \"\") single.id = `field-${++sequence}`;\n node.htmlFor = single.id;\n } else {\n // Nothing to point at, so the group gets the name instead and the text becomes\n // decoration rather than a promise the page does not keep.\n const group = Array.isArray(control) ? control : [control];\n for (const node_ of group) if (!node_.hasAttribute(\"aria-label\")) node_.setAttribute(\"aria-label\", text);\n }\n return node;\n}\n","import type { Boot } from \"./types.js\";\n\n/**\n * What the server told the page about itself.\n *\n * Stamped into the one nonced `<script>` as a JSON string and parsed here, rather than\n * fetched: the title, the mount path and — the part that matters — which sections and\n * controls this listener has. A page that had to ask for those would spend its first\n * frame drawing tabs it is about to remove.\n */\nconst raw = (globalThis as unknown as { __BOOTSTRAP__?: Boot }).__BOOTSTRAP__;\n\nexport const BOOT: Boot = raw ?? {\n base: \"\",\n title: \"bothandlerjs\",\n allowReset: false,\n allowEdit: false,\n allowGuardEdit: false,\n allowActing: false,\n peers: [],\n sections: { feed: true, evidence: true, actors: true, registry: true, tester: true, statistics: true, audit: true, notices: true, changes: true, policy: true, guard: true, robots: true, ranges: true },\n links: [],\n};\n\n/** Every request the page makes is relative to the mount path. */\nexport const API = BOOT.base;\n\nexport const SECTIONS = BOOT.sections;\n","import { el, rootNode } from \"./dom.js\";\nimport type { TabName } from \"./types.js\";\n\n/**\n * The handful of things every module needs to be able to do to the page as a whole.\n *\n * A mutable record filled in by `index.ts` at start-up, rather than each module\n * importing the module that owns `draw`. The feed opens the actor panel, the actor\n * panel drafts a rule into the policy editor, the policy editor switches to its own\n * tab: written as direct imports that is a cycle, and a cycle in a bundle is a\n * half-initialised module waiting to be discovered at run time. One indirection costs\n * a property lookup and makes the shape obvious.\n */\nexport interface App {\n /** Redraws the active view on the next frame. Coalesced; safe to call per event. */\n draw: () => void;\n /** Redraws immediately, for the cases where a click has to be reflected before the next frame. */\n drawNow: () => void;\n showTab: (tab: TabName, options?: { focus?: boolean; replace?: boolean; push?: boolean }) => void;\n /** Puts the feed's filter state into the URL, so a view can be sent to somebody. */\n syncUrl: () => void;\n}\n\nexport const app: App = {\n draw: () => {},\n drawNow: () => {},\n showTab: () => {},\n syncUrl: () => {},\n};\n\n/**\n * A transient message in the corner.\n *\n * Lives here rather than in the policy editor because the guard form, the feed's\n * exporter and the stream all raise them now, and three copies of a six-line function\n * is how they end up looking different from each other.\n */\nexport function toast(kind: \"ok\" | \"bad\" | \"warn\", title: string, detail = \"\"): void {\n const node = el(\"div\", `toast ${kind}`);\n node.appendChild(el(\"b\", null, title));\n if (detail !== \"\") node.appendChild(el(\"span\", null, detail));\n const host = rootNode().querySelector<HTMLElement>(\"#toasts\");\n if (host === null) return;\n host.appendChild(node);\n setTimeout(() => node.remove(), 6000);\n}\n\n/** Hands the viewer a file. Used by the settings export and the feed export. */\nexport function download(text: string, filename: string, type: string): void {\n const blob = new Blob([text], { type });\n const url = URL.createObjectURL(blob);\n const anchor = el(\"a\");\n anchor.href = url;\n anchor.download = filename;\n anchor.click();\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n/** Today, as a filename fragment. Every export the page produces is stamped with it. */\nexport function today(): string {\n return new Date().toISOString().slice(0, 10);\n}\n","import type { DashboardEntry } from \"./types.js\";\n\n/**\n * One request has one of four outcomes, and every part of the page agrees on which.\n *\n * Served, mitigated (something happened that a legitimate client can still get past),\n * denied, or pending — assessed, with no decision recorded, which is what `assess()`\n * on its own produces and what a row looks like for the moment between the two events.\n *\n * Pure, and exported on its own, because the table, the left edges, the timeline, the\n * actor mix and the statistics panels all classify the same way. Two of them disagreeing\n * about what a denial is would be a bug nobody could see.\n */\nexport type Outcome = \"allow\" | \"mitigate\" | \"deny\" | \"pending\";\n\nconst DENY = new Set([\"block\", \"drop\", \"redirect\"]);\nconst MITIGATE = new Set([\"challenge\", \"rate-limit\", \"delay\"]);\n\nexport function outcome(entry: Pick<DashboardEntry, \"action\">): Outcome {\n return actionKind(entry.action);\n}\n\n/** The same classification for a bare action name, which is what the rule editor has. */\nexport function actionKind(action: string | undefined): Outcome {\n if (action === undefined) return \"pending\";\n if (DENY.has(action)) return \"deny\";\n if (MITIGATE.has(action)) return \"mitigate\";\n return \"allow\";\n}\n\n/** The verdict badge: its class, and the word on it. */\nexport function verdictBadge(entry: Pick<DashboardEntry, \"verdict\" | \"bypass\">): [className: string, label: string] {\n if (entry.verdict === \"verified-bot\" || entry.verdict === \"confirmed-bot\") return [\"b-proven\", entry.verdict];\n if (entry.verdict === \"suspected-bot\") return [\"b-suspected\", \"suspected-bot\"];\n if (entry.verdict === \"human\") return [\"b-human\", \"human\"];\n return [\"b-unknown\", entry.bypass !== undefined ? `skipped · ${entry.bypass}` : \"unknown\"];\n}\n\n/**\n * How many assessments proved a *bot*.\n *\n * Not `metrics.proven`, which is what the tile used to show. That counter is honestly\n * named for what it holds — assessments resting on at least one piece of proven evidence —\n * and evidence has a direction: a client holding an operator or interaction clearance\n * produces *certain human* evidence, so it lands there too. A dashboard watching nothing\n * but cleared humans therefore read \"Proven bots: 100% of traffic\", and the same requests\n * were counted again under Unremarkable.\n *\n * The two proven-bot verdicts are the exact answer. Only the proven path can reach them —\n * the probabilistic path produces `suspected-bot`, `human` or `unknown` and nothing else —\n * so this is a rename of the truth rather than an approximation of it.\n */\nexport function provenBots(verdicts: Record<string, number>): number {\n return (verdicts[\"confirmed-bot\"] ?? 0) + (verdicts[\"verified-bot\"] ?? 0);\n}\n","import { outcome } from \"./outcome.js\";\nimport type { DashboardEntry } from \"./types.js\";\n\n/**\n * The feed's search.\n *\n * It used to be one `indexOf` over every field of a request joined into a string,\n * which answers \"does this word appear anywhere\" and nothing else. That is the wrong\n * question in the two cases somebody actually reaches for the box: an address that\n * also appears inside a User-Agent, and a path that is a prefix of ten others.\n *\n * So terms can name a field, and can be negated:\n *\n * ```text\n * actor:203.0.113.4 -path:/health that address, except its health checks\n * rule:no-scrapers action:tag the rule that fired, and what it settled on\n * score:>70 -certain probabilistic traffic close to the line\n * \"GET /api/v2/orders\" a phrase, spaces and all\n * ```\n *\n * Every term must match — this is an `AND`, because that is what narrowing means and\n * a filter box that quietly `OR`s is a filter box that lies. Anything that does not\n * parse as a field term is matched against the whole request, so a plain word still\n * behaves exactly as it did.\n *\n * Kept free of the DOM so it can be unit-tested, which is the point: this is the piece\n * of the page most likely to be wrong in a way nobody notices.\n */\nexport interface Term {\n /** The field named before the colon, already resolved to a canonical name. */\n field: string | undefined;\n value: string;\n negated: boolean;\n /** For numeric fields: how `value` should be compared. */\n compare?: \"<\" | \">\" | \"=\" | undefined;\n}\n\n/** Field names, plus the shorthands people type instead. */\nconst FIELDS: Record<string, string> = {\n path: \"path\",\n url: \"path\",\n actor: \"actor\",\n ip: \"actor\",\n ua: \"userAgent\",\n useragent: \"userAgent\",\n agent: \"userAgent\",\n verdict: \"verdict\",\n action: \"action\",\n rule: \"rule\",\n detector: \"detector\",\n identity: \"identity\",\n method: \"method\",\n class: \"botClass\",\n botclass: \"botClass\",\n id: \"requestId\",\n request: \"requestId\",\n bypass: \"bypass\",\n score: \"score\",\n outcome: \"outcome\",\n certain: \"certain\",\n};\n\nconst NUMERIC = new Set([\"score\"]);\n\n/** Splits on whitespace, keeping double-quoted runs together. */\nfunction tokenize(input: string): string[] {\n const tokens: string[] = [];\n let current = \"\";\n let quoted = false;\n for (const character of input) {\n if (character === '\"') {\n quoted = !quoted;\n continue;\n }\n if (!quoted && /\\s/.test(character)) {\n if (current !== \"\") tokens.push(current);\n current = \"\";\n continue;\n }\n current += character;\n }\n if (current !== \"\") tokens.push(current);\n return tokens;\n}\n\nexport function parseQuery(input: string): Term[] {\n const terms: Term[] = [];\n for (const token of tokenize(input.trim())) {\n const negated = token.startsWith(\"-\") || token.startsWith(\"!\");\n const body = negated ? token.slice(1) : token;\n if (body === \"\") continue;\n\n const colon = body.indexOf(\":\");\n const name = colon === -1 ? \"\" : body.slice(0, colon).toLowerCase();\n const field = FIELDS[name];\n // A colon with an unknown name in front of it is not a field term — a path can\n // contain one, and so can a User-Agent. It falls through to a free term.\n if (colon === -1 || field === undefined) {\n terms.push({ field: undefined, value: body.toLowerCase(), negated });\n continue;\n }\n\n let value = body.slice(colon + 1).toLowerCase();\n let compare: Term[\"compare\"];\n if (NUMERIC.has(field)) {\n compare = value.startsWith(\">\") ? \">\" : value.startsWith(\"<\") ? \"<\" : \"=\";\n if (compare !== \"=\") value = value.slice(1);\n }\n if (value === \"\") continue;\n terms.push({ field, value, negated, compare });\n }\n return terms;\n}\n\n/** Everything about a request, as one lower-case string. What a free term is matched against. */\nexport function searchableText(entry: DashboardEntry): string {\n const parts = [\n entry.method,\n entry.path,\n entry.actor,\n entry.userAgent,\n entry.verdict,\n entry.botClass,\n entry.identity ?? \"\",\n entry.action ?? \"\",\n entry.rule ?? \"\",\n entry.requestId,\n ];\n for (const item of entry.evidence) parts.push(item.detector, item.summary);\n return parts.join(\" \").toLowerCase();\n}\n\nfunction fieldValue(entry: DashboardEntry, field: string): string {\n switch (field) {\n case \"path\":\n return entry.path;\n case \"actor\":\n return entry.actor;\n case \"userAgent\":\n return entry.userAgent;\n case \"verdict\":\n return entry.verdict;\n case \"action\":\n return entry.action ?? \"\";\n case \"rule\":\n return entry.rule ?? \"\";\n case \"identity\":\n return entry.identity ?? \"\";\n case \"method\":\n return entry.method;\n case \"botClass\":\n return entry.botClass;\n case \"requestId\":\n return entry.requestId;\n case \"bypass\":\n return entry.bypass ?? \"\";\n case \"outcome\":\n return outcome(entry);\n case \"certain\":\n return String(entry.certain);\n case \"detector\":\n return entry.evidence.map((item) => item.detector).join(\" \");\n default:\n return \"\";\n }\n}\n\nfunction matchesTerm(term: Term, entry: DashboardEntry, haystack: string): boolean {\n if (term.field === undefined) return haystack.includes(term.value);\n if (term.field === \"score\") {\n const wanted = Number(term.value);\n if (Number.isNaN(wanted)) return false;\n if (term.compare === \">\") return entry.score > wanted;\n if (term.compare === \"<\") return entry.score < wanted;\n return entry.score === wanted;\n }\n return fieldValue(entry, term.field).toLowerCase().includes(term.value);\n}\n\n/** True when every term is satisfied. An empty query matches everything. */\nexport function matchesQuery(terms: readonly Term[], entry: DashboardEntry, haystack: string): boolean {\n for (const term of terms) {\n if (matchesTerm(term, entry, haystack) === term.negated) return false;\n }\n return true;\n}\n\n/** The named filter buttons, which are a second, independent narrowing. */\nexport type FilterName = \"all\" | \"proven\" | \"suspected\" | \"human\" | \"guard\" | \"deny\" | \"mitigate\" | \"allow\";\n\nexport function matchesFilter(filter: FilterName, entry: DashboardEntry): boolean {\n switch (filter) {\n case \"proven\":\n return entry.certain;\n case \"suspected\":\n return entry.verdict === \"suspected-bot\";\n case \"human\":\n return entry.verdict === \"human\";\n case \"guard\":\n return entry.downgradedFrom !== undefined;\n case \"deny\":\n return outcome(entry) === \"deny\";\n case \"mitigate\":\n return outcome(entry) === \"mitigate\";\n case \"allow\":\n return outcome(entry) === \"allow\";\n default:\n return true;\n }\n}\n","import { matchesFilter, matchesQuery, parseQuery, searchableText } from \"./query.js\";\nimport { outcome } from \"./outcome.js\";\nimport type { EditorRule } from \"./draft.js\";\nimport type { FilterName, Term } from \"./query.js\";\nimport type { ActorRow, DashboardEntry, Policy, Row, Snapshot, TabName } from \"./types.js\";\n\n/** Requests the page keeps. The server's ring is smaller; this is the ceiling, not the target. */\nconst MAX_ROWS = 1000;\n\n/** Rows built into the table at once. Beyond this a feed is scrolled past, not read. */\nexport const FEED_LIMIT = 300;\n\nexport interface State {\n rows: Row[];\n byId: Map<string, Row>;\n snapshot: Snapshot | undefined;\n policy: Policy | undefined;\n /** The Actors screen's list, fetched rather than streamed. See `registry.ts`. */\n actors: ActorRow[];\n actorsTracked: number;\n paused: boolean;\n filter: FilterName;\n search: string;\n terms: Term[];\n tab: TabName;\n open: Set<string>;\n actor: string | undefined;\n rangeMs: number;\n /** Which population the score distribution is drawn from. See `charts.drawScores`. */\n scoreScope: \"run\" | \"window\";\n editorRules: EditorRule[];\n editorDirty: boolean;\n editorMode: \"gui\" | \"json\";\n guardDirty: boolean;\n /** Rows that arrived while paused, so the button can say what resuming will show. */\n bufferedWhilePaused: number;\n /** Frames the server dropped because *this* connection was too slow to take them. */\n laggedDrops: number;\n}\n\nexport const state: State = {\n rows: [],\n byId: new Map(),\n snapshot: undefined,\n policy: undefined,\n actors: [],\n actorsTracked: 0,\n paused: false,\n filter: \"all\",\n search: \"\",\n terms: [],\n tab: \"live\",\n open: new Set(),\n actor: undefined,\n rangeMs: 300_000,\n scoreScope: \"run\",\n editorRules: [],\n editorDirty: false,\n editorMode: \"gui\",\n guardDirty: false,\n bufferedWhilePaused: 0,\n laggedDrops: 0,\n};\n\n/**\n * Files one entry into the ring.\n *\n * The same request arrives twice — once when it is assessed and again when the\n * decision lands, and again after a reconnect that replays what was missed — so this\n * is an upsert keyed by request id, and `rev` is what tells the renderer that a row on\n * screen is now stale. Rebuilding a row nothing changed about is what used to wipe a\n * text selection every time a request came in.\n */\nexport function ingest(entry: DashboardEntry): void {\n const existing = state.byId.get(entry.requestId);\n if (existing !== undefined) {\n existing.entry = entry;\n existing.rev++;\n existing.text = undefined;\n return;\n }\n const row: Row = { entry, rev: 0 };\n state.byId.set(entry.requestId, row);\n state.rows.push(row);\n if (state.rows.length > MAX_ROWS) {\n for (const dropped of state.rows.splice(0, state.rows.length - MAX_ROWS)) {\n state.byId.delete(dropped.entry.requestId);\n state.open.delete(dropped.entry.requestId);\n }\n }\n}\n\nexport function clearFeed(): void {\n state.rows = [];\n state.byId = new Map();\n state.open.clear();\n state.actor = undefined;\n state.bufferedWhilePaused = 0;\n // The lag count goes with them. It is half of the \"N not streamed\" badge — the server's\n // own `skipped` is the other half, and `FeedRing.clear()` resets that — so leaving this\n // one behind made the badge disagree with itself: a number that survived the feed it\n // described, under a tooltip promising the entries were \"still in the window, the\n // preview and the export\" when the window had just been replaced. Both ways a feed is\n // cleared lead here, and the second is the one that matters: a viewer whose stream was\n // dropped for lagging reconnects with a stale cursor and is sent a fresh backlog, so the\n // gap this counted is exactly what has just been filled in.\n state.laggedDrops = 0;\n}\n\nexport function setSearch(value: string): void {\n state.search = value;\n state.terms = parseQuery(value);\n}\n\nfunction textOf(row: Row): string {\n if (row.text === undefined) row.text = searchableText(row.entry);\n return row.text;\n}\n\nexport function matches(row: Row): boolean {\n return matchesFilter(state.filter, row.entry) && matchesQuery(state.terms, row.entry, textOf(row));\n}\n\n/** The rows the feed would draw, newest first. Also what \"export what I am looking at\" means. */\nexport function visibleRows(limit = FEED_LIMIT): Row[] {\n const shown: Row[] = [];\n for (let i = state.rows.length - 1; i >= 0 && shown.length < limit; i--) {\n const row = state.rows[i];\n if (row !== undefined && matches(row)) shown.push(row);\n }\n return shown;\n}\n\nexport function matchingCount(): number {\n let count = 0;\n for (const row of state.rows) if (matches(row)) count++;\n return count;\n}\n\nexport function oldestAt(): number | undefined {\n return state.rows[0]?.entry.at;\n}\n\nexport interface Aggregates {\n detectors: Map<string, number>;\n actors: Map<string, number>;\n identities: Map<string, number>;\n paths: Map<string, number>;\n deniedPaths: Map<string, number>;\n guardStops: Map<string, number>;\n ruleHits: Map<string, number>;\n bypassed: Map<string, number>;\n}\n\nfunction bump(counter: Map<string, number>, key: string): void {\n counter.set(key, (counter.get(key) ?? 0) + 1);\n}\n\n/**\n * Everything the window-scoped panels count, in one pass over the ring.\n *\n * One pass rather than eight, and — more to the point — it is now called only by the\n * screen that draws them. It used to run on every frame of the live feed to fill in\n * panels on a tab nobody was looking at.\n */\nexport function aggregate(rows: readonly Row[]): Aggregates {\n const totals: Aggregates = {\n detectors: new Map(),\n actors: new Map(),\n identities: new Map(),\n paths: new Map(),\n deniedPaths: new Map(),\n guardStops: new Map(),\n ruleHits: new Map(),\n bypassed: new Map(),\n };\n\n for (const { entry } of rows) {\n for (const item of entry.evidence) bump(totals.detectors, item.detector);\n bump(totals.actors, entry.actor);\n\n if (entry.bypass !== undefined) {\n bump(totals.bypassed, `${entry.path} (${entry.bypass})`);\n continue;\n }\n bump(totals.paths, entry.path);\n if (entry.identity !== undefined && entry.identity !== \"\") {\n // Claimed and confirmed are different facts about the same name, and a panel\n // that merged them would report a forged Googlebot as Googlebot.\n bump(totals.identities, `${entry.identity} · ${entry.verdict === \"verified-bot\" ? \"verified\" : \"claimed\"}`);\n }\n if (outcome(entry) === \"deny\") bump(totals.deniedPaths, entry.path);\n if (entry.downgradedFrom !== undefined && entry.rule !== undefined) bump(totals.guardStops, `${entry.rule} → ${entry.downgradedFrom}`);\n if (entry.rule !== undefined) bump(totals.ruleHits, entry.rule);\n }\n\n return totals;\n}\n","import { API } from \"./boot.js\";\n\n/**\n * The page's two ways of talking to its server.\n *\n * Every write is JSON with a JSON content type, and that is not a style choice: the\n * server refuses a body it did not get as `application/json` precisely because an HTML\n * form cannot send one, which is what stops somebody else's page from posting here\n * with an operator's credentials attached.\n */\nexport async function getJson<T>(path: string): Promise<T> {\n const response = await fetch(API + path);\n if (!response.ok) throw new Error(await errorFrom(response));\n return (await response.json()) as T;\n}\n\nexport interface PostResult<T> {\n ok: boolean;\n data: T;\n error?: string;\n}\n\nexport async function postJson<T>(path: string, body: unknown): Promise<PostResult<T>> {\n const response = await fetch(API + path, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n const data = (await response.json().catch(() => ({}))) as T & { error?: string };\n return response.ok ? { ok: true, data } : { ok: false, data, error: String(data.error ?? \"The server refused this.\") };\n}\n\nasync function errorFrom(response: Response): Promise<string> {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n return body.error ?? `${response.status} ${response.statusText}`;\n}\n","/** Number, duration and window formatting. Pure, so the awkward cases can be tested. */\n\nconst numbers = new Intl.NumberFormat();\n\nexport function n(value: number | undefined): string {\n return numbers.format(value ?? 0);\n}\n\nexport function pct(part: number, whole: number): string {\n return whole > 0 ? `${Math.round((part / whole) * 100)}%` : \"—\";\n}\n\nexport function ms(value: number): string {\n return value >= 10 ? `${value.toFixed(1)}ms` : `${value.toFixed(2)}ms`;\n}\n\nexport function uptime(milliseconds: number): string {\n const seconds = Math.max(0, Math.round(milliseconds / 1000));\n if (seconds < 90) return `${seconds}s`;\n const minutes = Math.round(seconds / 60);\n if (minutes < 90) return `${minutes}m`;\n return `${Math.round(minutes / 60)}h`;\n}\n\nexport function rangeLabel(milliseconds: number): string {\n const seconds = Math.round(milliseconds / 1000);\n if (seconds < 90) return `${seconds}s`;\n const minutes = Math.round(seconds / 60);\n return minutes < 90 ? `${minutes} min` : `${Math.round(minutes / 60)}h`;\n}\n\n/** Wall-clock time for a feed row. Local, seconds included, because a feed moves in seconds. */\nexport function clockTime(at: number): string {\n return new Date(at).toLocaleTimeString();\n}\n\n/**\n * What \"this window\" actually holds, said out loud.\n *\n * Half the panels on the Statistics screen count the retained ring and half count\n * since the process started, and until now both wore the same quiet grey subtitle.\n * They are different populations — on a busy server the ring can be ninety seconds of\n * a three-week run — so comparing a panel from one against a panel from the other is a\n * mistake the page was inviting. Every \"this window\" label now says how much window\n * there is.\n */\nexport function windowLabel(count: number, oldestAt: number | undefined, now: number): string {\n if (count === 0) return \"this window · empty\";\n const span = oldestAt === undefined ? 0 : Math.max(0, now - oldestAt);\n return `last ${n(count)} requests · ${rangeLabel(span)}`;\n}\n","import type { DashboardEntry } from \"./types.js\";\n\n/**\n * A request as a line `bothandlerjs replay` can read.\n *\n * The point of the button this backs: a verdict you disagree with on screen becomes a\n * fixture you can re-run offline, and then a corpus case that stops it coming back.\n * Values the dashboard redacted stay redacted — the shape is what replays, and a\n * session cookie is not part of the shape.\n */\nexport function replayLine(entry: DashboardEntry): string {\n const headers: Record<string, string> = {};\n for (const [name, value] of entry.headers ?? []) headers[name] = value;\n const query = Object.keys(entry.query)\n .map((name) => `${encodeURIComponent(name)}=${encodeURIComponent(entry.query[name] ?? \"\")}`)\n .join(\"&\");\n return JSON.stringify({\n method: entry.method,\n url: entry.path + (query === \"\" ? \"\" : `?${query}`),\n headers,\n ip: entry.actor,\n timestamp: new Date(entry.at).toISOString(),\n protocol: entry.protocol ?? \"https\",\n httpVersion: entry.httpVersion ?? \"1.1\",\n });\n}\n\n/** Every shown request as replay JSONL, oldest first — the whole filtered window at once. */\nexport function replayFile(entries: readonly DashboardEntry[]): string {\n return entries.map(replayLine).join(\"\\n\");\n}\n\n/** The same request as a traffic-corpus case, ready to paste into a fixture file. */\nexport function corpusCase(entry: DashboardEntry): string {\n const headers = (entry.headers ?? []).map((pair) => ` [${JSON.stringify(pair[0])}, ${JSON.stringify(pair[1])}]`).join(\",\\n\");\n return [\n \"bot({\",\n ` id: ${JSON.stringify(`case-${entry.requestId.toLowerCase().replace(/[^a-z0-9]+/g, \"-\")}`)},`,\n ` title: ${JSON.stringify(`${entry.method} ${entry.path} from ${entry.userAgent.slice(0, 60)}`)},`,\n ' audience: \"unwanted-bot\", // human | benign-bot | declared-bot | unwanted-bot | hostile | infrastructure',\n ' category: \"observed\",',\n ` provenance: \"Captured from the live dashboard on ${new Date(entry.at).toISOString().slice(0, 10)}\",`,\n \" requests: [\",\n \" {\",\n \" headers: [\",\n headers,\n \" ],\",\n ` protocol: ${JSON.stringify(entry.protocol ?? \"https\")},`,\n ` httpVersion: ${JSON.stringify(entry.httpVersion ?? \"1.1\")},`,\n ` path: ${JSON.stringify(entry.path)},`,\n \" },\",\n \" ],\",\n ` expect: { verdict: ${JSON.stringify(entry.verdict)}, certain: ${String(entry.certain)} },`,\n \"}),\",\n ].join(\"\\n\");\n}\n","import { el } from \"./dom.js\";\nimport { BOOT } from \"./boot.js\";\nimport { postJson } from \"./api.js\";\nimport { toast } from \"./app.js\";\n\n/**\n * The three things you can do to one client, rather than to a class of request.\n *\n * Shared by the actor drill-down and the Actors screen, which offer the same three and\n * would otherwise offer them slightly differently.\n *\n * Allowlisting is the consequential one and it is the one that asks twice. **An\n * allowlisted address is not judged leniently; it is not judged at all** — detection\n * does not run, no evidence is produced, no rule sees it — so the button states the\n * address it is about to exempt and waits for a second click. That is deliberately not\n * a dialog: a confirmation you can dismiss without reading is a click with extra steps,\n * whereas a button that changes into the sentence it is about to enact has to be read\n * to be pressed.\n */\nexport type AfterAction = () => void;\n\n/**\n * How many confirmations are half-pressed right now.\n *\n * The Actors screen repaints on every counters frame — every two seconds — and a repaint\n * builds new buttons. An operator who clicked \"Allowlist\", read the address it offered\n * back and reached for the second click could therefore have the button quietly reset\n * under their cursor, so that the second click armed it again instead of doing anything.\n * Which is the worst outcome available for a confirmation: it teaches people that the\n * first click does nothing and the way through is to click twice, quickly.\n *\n * So the list holds still while somebody is deciding. See `drawActors`.\n */\nlet armed = 0;\n\nexport function isConfirming(): boolean {\n return armed > 0;\n}\n\nexport function actorActions(key: string, after: AfterAction): HTMLElement[] {\n if (!BOOT.allowActing) return [];\n\n const forget = el(\"button\", null, \"Forget\");\n forget.title = \"Discard this actor's history — the cure for a false positive that has stuck\";\n forget.addEventListener(\"click\", () => {\n void act({ key, action: \"forget\" }, `Forgot ${key}`, \"Its next request is assessed as a first request.\", after);\n });\n\n const clear = el(\"button\", null, \"Clear as human\");\n clear.title = \"Grant this actor human clearance for an hour, as though it had solved a challenge\";\n clear.addEventListener(\"click\", () => {\n void act({ key, action: \"clear\", forMs: 60 * 60_000 }, `Cleared ${key}`, \"Held as human for an hour, then reassessed.\", after);\n });\n\n return [confirmingButton(\"Allowlist\", `Allowlist ${key} — it stops being assessed at all`, () => allowlist(key, after)), forget, clear];\n}\n\n/**\n * A button that has to be pressed twice, and says what it will do in between.\n *\n * Reverts after a few seconds, so a half-pressed destructive action does not sit there\n * waiting for an accidental second click.\n */\nfunction confirmingButton(label: string, confirmation: string, run: () => void): HTMLButtonElement {\n const button = el(\"button\", \"danger\", label);\n let pending = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const disarm = (): void => {\n if (!pending) return;\n pending = false;\n armed--;\n button.textContent = label;\n button.className = \"danger\";\n };\n\n button.addEventListener(\"click\", () => {\n if (pending) {\n if (timer !== undefined) clearTimeout(timer);\n disarm();\n run();\n return;\n }\n pending = true;\n armed++;\n button.textContent = confirmation;\n button.className = \"danger primary\";\n timer = setTimeout(disarm, 5000);\n });\n return button;\n}\n\nasync function allowlist(key: string, after: AfterAction): Promise<void> {\n const result = await postJson<{ entries?: string[]; error?: string }>(\"/api/ranges\", { name: \"allowlist\", add: [key] });\n if (!result.ok) {\n toast(\"bad\", \"Not allowlisted\", result.error ?? \"\");\n return;\n }\n toast(\"warn\", `Allowlisted ${key}`, \"Requests from it are no longer assessed at all.\");\n after();\n}\n\nasync function act(body: { key: string; action: string; forMs?: number }, title: string, detail: string, after: AfterAction): Promise<void> {\n const result = await postJson<{ error?: string }>(\"/api/actor\", body);\n if (!result.ok) {\n toast(\"bad\", \"Refused\", result.error ?? \"\");\n return;\n }\n toast(\"ok\", title, detail);\n after();\n}\n","import { clear, el } from \"./dom.js\";\nimport { n } from \"./format.js\";\n\n/** A horizontal bar list, sorted by value, capped at what a panel can show without scrolling. */\nexport function drawBars(target: HTMLElement, rows: Iterable<[string, number]>, emptyText: string): void {\n clear(target);\n const filtered = [...rows].filter(([, value]) => value > 0);\n if (filtered.length === 0) {\n target.appendChild(el(\"div\", \"note\", emptyText));\n return;\n }\n filtered.sort((a, b) => b[1] - a[1]);\n const max = filtered[0]?.[1] ?? 1;\n for (const [label, value] of filtered.slice(0, 14)) {\n const bar = el(\"div\", \"bar\");\n const track = el(\"div\", \"track\");\n const fill = el(\"div\", \"fill\");\n fill.style.width = `${Math.max(2, Math.round((value / max) * 100))}%`;\n track.appendChild(fill);\n track.appendChild(el(\"div\", \"lbl\", label));\n bar.appendChild(track);\n bar.appendChild(el(\"div\", \"v tnum\", n(value)));\n target.appendChild(bar);\n }\n}\n\n/** A record of counters as bar rows. */\nexport function pairs(record: Record<string, number> | undefined): Array<[string, number]> {\n return Object.entries(record ?? {});\n}\n","import { $, clear, el } from \"./dom.js\";\nimport { actorActions } from \"./actions.js\";\nimport { SECTIONS } from \"./boot.js\";\nimport { app } from \"./app.js\";\nimport { clockTime, n } from \"./format.js\";\nimport { drawBars } from \"./bars.js\";\nimport { state } from \"./store.js\";\n\n/** Wires the panel's own Close button. Called once, and only when the panel exists. */\nexport function initActor(): void {\n if (!SECTIONS.actors) return;\n $(\"actor-close\").addEventListener(\"click\", closeActor);\n}\n\n/** Opens the drill-down above the feed and scrolls it into view. */\nexport function openActor(key: string): void {\n state.actor = key;\n app.drawNow();\n $(\"actor-panel\").scrollIntoView({ block: \"nearest\" });\n}\n\nfunction closeActor(): void {\n state.actor = undefined;\n app.drawNow();\n}\n\nexport function drawActor(): void {\n // The whole panel is removed when the actors section is off, so nothing below here\n // has anything to draw into.\n if (!SECTIONS.actors) return;\n const panel = $(\"actor-panel\");\n if (state.actor === undefined) {\n panel.hidden = true;\n return;\n }\n panel.hidden = false;\n $(\"actor-key\").textContent = state.actor;\n\n const mine = state.rows.filter((row) => row.entry.actor === state.actor).map((row) => row.entry);\n const latest = mine[mine.length - 1];\n const stats = latest?.actorStats;\n\n const gaps: number[] = [];\n for (let i = 1; i < mine.length; i++) gaps.push((mine[i] as { at: number }).at - (mine[i - 1] as { at: number }).at);\n const meanGap = gaps.length > 0 ? gaps.reduce((a, b) => a + b, 0) / gaps.length : 0;\n\n const box = $(\"actor-stats\");\n clear(box);\n const rows: Array<[string, string]> = [\n [\"In this window\", `${n(mine.length)} requests`],\n [\"Engine sees\", stats !== undefined ? `${n(stats.requests)} requests, ${n(stats.distinctPaths)} distinct paths` : \"—\"],\n [\"Prior confirmations\", stats !== undefined ? n(stats.priorConfirmations) : \"—\"],\n [\"Holds clearance\", stats !== undefined ? (stats.cleared ? \"yes\" : \"no\") : \"—\"],\n [\"First seen\", stats !== undefined ? clockTime(stats.firstSeen) : \"—\"],\n [\"Mean gap\", gaps.length > 0 ? `${Math.round(meanGap)}ms over ${n(gaps.length)} gaps` : \"one request only\"],\n ];\n for (const [key, value] of rows) {\n box.appendChild(el(\"dt\", null, key));\n box.appendChild(el(\"dd\", null, value));\n }\n\n const verdicts = new Map<string, number>();\n const actions = new Map<string, number>();\n for (const entry of mine) {\n verdicts.set(entry.verdict, (verdicts.get(entry.verdict) ?? 0) + 1);\n if (entry.action !== undefined) actions.set(entry.action, (actions.get(entry.action) ?? 0) + 1);\n }\n drawBars($(\"actor-mix\"), [...verdicts, ...actions], \"Nothing yet.\");\n\n // The same three operations the Actors screen offers, on the actor already in front\n // of you. Absent entirely without `controls.editRanges`.\n const bar = $(\"actor-actions\");\n clear(bar);\n const buttons = actorActions(state.actor, () => app.drawNow());\n bar.hidden = buttons.length === 0;\n for (const button of buttons) bar.appendChild(button);\n}\n","import { $, clear, el } from \"./dom.js\";\nimport { n } from \"./format.js\";\nimport type { Preview } from \"./types.js\";\n\n/** The result box under the editor. One box, whichever of the two forms wrote to it. */\nexport function showResult(kind: \"ok\" | \"bad\" | \"warn\", build: (box: HTMLElement) => void): void {\n const box = $(\"policy-result\");\n box.hidden = false;\n box.className = `result ${kind}`;\n clear(box);\n build(box);\n}\n\n/**\n * What a candidate would have done to the traffic still in the window.\n *\n * `newDenials` is called out on its own because it is the one direction of change that\n * costs somebody their access, and it is easy to miss in a table of counts.\n */\nexport function renderPreview(preview: Preview, notes: readonly string[] = []): void {\n showResult(preview.newDenials > 0 ? \"warn\" : \"ok\", (box) => {\n const head = el(\"div\");\n head.appendChild(el(\"b\", null, `${n(preview.changed)} of ${n(preview.evaluated)} requests would be treated differently.`));\n box.appendChild(head);\n // Whatever led to this preview, kept beside its result. A draft explains what it\n // matched on and why it went last, and that explanation is worth more next to the\n // numbers than it was in the two seconds before they arrived.\n for (const note of notes) box.appendChild(el(\"div\", \"ev-meta\", note));\n if (preview.newDenials > 0) {\n box.appendChild(el(\"div\", null, `${n(preview.newDenials)} request(s) that are served today would be denied. Read the samples before applying this.`));\n }\n for (const warning of preview.warnings) box.appendChild(el(\"div\", \"ev-meta\", warning));\n\n const dead = preview.ruleHits.filter((row) => row.hits === 0 && row.rule !== \"default\");\n if (dead.length > 0) box.appendChild(el(\"div\", \"ev-meta\", `Never matched in this window: ${dead.map((row) => row.rule).join(\", \")}`));\n\n if (preview.samples.length > 0) {\n const table = el(\"table\", \"diff\");\n const head2 = el(\"tr\");\n for (const label of [\"Request\", \"Now\", \"Would be\"]) head2.appendChild(el(\"th\", null, label));\n table.appendChild(head2);\n for (const sample of preview.samples) {\n const row = el(\"tr\");\n const what = el(\"td\");\n what.appendChild(el(\"div\", \"mono\", sample.path));\n what.appendChild(el(\"div\", \"ev-meta\", `${sample.verdict} · ${sample.userAgent.slice(0, 48)}`));\n row.appendChild(what);\n const from = el(\"td\", \"from\");\n from.appendChild(el(\"div\", null, sample.from));\n from.appendChild(el(\"div\", \"ev-meta\", sample.fromRule));\n row.appendChild(from);\n const kind = sample.to === \"block\" || sample.to === \"drop\" || sample.to === \"redirect\" ? \"deny\" : sample.to === \"allow\" ? \"allow\" : \"\";\n const to = el(\"td\", `to ${kind}`);\n to.appendChild(el(\"div\", null, sample.to));\n to.appendChild(el(\"div\", \"ev-meta\", sample.toRule));\n row.appendChild(to);\n table.appendChild(row);\n }\n box.appendChild(table);\n } else if (preview.evaluated === 0) {\n box.appendChild(el(\"div\", \"ev-meta\", \"No traffic in the window to preview against — send some requests first.\"));\n }\n });\n}\n","import { $, clear, el, label } from \"./dom.js\";\nimport { SECTIONS } from \"./boot.js\";\nimport { app, toast } from \"./app.js\";\nimport { postJson } from \"./api.js\";\nimport { renderPreview, showResult } from \"./result.js\";\nimport { state } from \"./store.js\";\nimport type { Preview } from \"./types.js\";\n\n/** The guard as the form holds it. The same five fields the server accepts back. */\ninterface GuardDraft {\n falsePositivePolicy: string;\n fallbackAction: string;\n defaultAction: string;\n terminalScoreThreshold: number;\n suspectThreshold: number;\n}\n\nlet draft: GuardDraft | undefined;\n\n/**\n * What each mode actually does, in the place somebody is about to change it.\n *\n * The words `strict`, `balanced` and `aggressive` sound like a slider from cautious to\n * effective. They are not: they are three different answers to \"may a guess close the\n * door\", and the third one is \"yes\". Anybody moving this setting should read what it\n * means without going to find the documentation, so the documentation comes to them.\n */\nconst MODE_NOTES: Record<string, string> = {\n strict: \"A terminal action survives only on proven evidence. Nothing is ever denied on a guess. This is the default, and it is the claim this library makes about itself.\",\n balanced:\n \"A terminal action also survives on a probabilistic verdict that clears the score threshold with at least two independent strong signals. Real people do trip two signals — a hardened browser behind a corporate proxy is the usual pair — so this setting will eventually deny somebody who should have been served.\",\n aggressive: \"The guard is off. Every rule does exactly what it says, on proof or on suspicion alike, and the people it turns away first are the ones with the most unusual and most legitimate setups.\",\n};\n\nexport function drawGuard(): void {\n // Removed with its section, which can be off while the Policy tab as a whole is on.\n if (!SECTIONS.guard) return;\n const document_ = state.policy;\n const snapshot = state.snapshot;\n if (snapshot === undefined) return;\n\n const editable = document_?.guardEditable === true;\n const panel = $(\"stat-policy\");\n clear(panel);\n\n if (!editable || document_ === undefined) {\n const rows: Array<[string, string]> = [\n [\"False-positive policy\", snapshot.policy.falsePositivePolicy],\n [\"Fallback when the guard stops a rule\", snapshot.policy.fallbackAction],\n [\"Terminal score threshold\", String(snapshot.policy.terminalScoreThreshold)],\n [\"Suspect threshold\", String(snapshot.policy.suspectThreshold)],\n [\"Action when no rule matches\", snapshot.policy.defaultAction],\n [\"Challenge configured\", snapshot.policy.challengeEnabled ? \"yes\" : \"no\"],\n [\"Range sets\", snapshot.ranges.length === 0 ? \"none\" : snapshot.ranges.map((range) => `${range.name} (${range.size})`).join(\", \")],\n ];\n for (const [key, value] of rows) {\n const line = el(\"div\", \"stat-row\");\n line.appendChild(el(\"span\", \"k\", key));\n line.appendChild(el(\"span\", \"v\", value));\n panel.appendChild(line);\n }\n $(\"guard-mode\").textContent = \"not editable here\";\n $(\"guard-note\").textContent =\n \"These are fixed at construction on this dashboard. It can change which rules exist; it cannot change how far a rule is allowed to go, because relaxing that is the one edit that can start denying people. Enable it deliberately with controls: { editGuard: true }.\";\n return;\n }\n\n if (draft === undefined) draft = { ...document_.guard };\n const vocabulary = document_.vocabulary;\n $(\"guard-mode\").textContent = state.guardDirty ? \"unsaved changes\" : \"editable\";\n $(\"guard-note\").textContent =\n \"Changing these changes what every rule is allowed to do, on the next request. Preview it against the traffic in the window first — that is the only view you get of who it would start turning away.\";\n\n panel.appendChild(\n guardField(\n // \"Mode\" rather than \"Guard\": the panel is already called that, and a field\n // repeating its own panel's name reads as a heading rather than as a control.\n \"Mode\",\n segmented(\n vocabulary.falsePositivePolicies.map((mode) => [mode, mode] as [string, string]),\n draft.falsePositivePolicy,\n (value) => {\n setDraft({ falsePositivePolicy: value });\n drawGuard();\n },\n ),\n ),\n );\n panel.appendChild(el(\"div\", \"note guard-explains\", MODE_NOTES[draft.falsePositivePolicy] ?? \"\"));\n\n panel.appendChild(\n guardField(\n \"Fallback\",\n select(vocabulary.fallbackActions, draft.fallbackAction, (value) => setDraft({ fallbackAction: value })),\n \"what a stopped rule becomes — the terminal actions are absent because a terminal fallback would deny the request the guard just protected\",\n ),\n );\n panel.appendChild(\n guardField(\n \"Default action\",\n select(vocabulary.actions, draft.defaultAction, (value) => setDraft({ defaultAction: value })),\n \"when no rule matches\",\n ),\n );\n panel.appendChild(\n guardField(\n \"Terminal score\",\n number(draft.terminalScoreThreshold, (value) => setDraft({ terminalScoreThreshold: value })),\n \"balanced mode only: the score a probabilistic verdict must clear\",\n ),\n );\n panel.appendChild(\n guardField(\n \"Suspect at\",\n number(draft.suspectThreshold, (value) => setDraft({ suspectThreshold: value })),\n \"the score at which a request becomes suspected-bot\",\n ),\n );\n\n const bar = el(\"div\", \"bar-actions\");\n const preview = el(\"button\", null, \"Preview\");\n preview.addEventListener(\"click\", () => {\n void previewGuard();\n });\n const apply = el(\"button\", \"primary\", \"Apply\");\n apply.addEventListener(\"click\", () => {\n void applyGuard();\n });\n const revert = el(\"button\", null, \"Revert\");\n revert.addEventListener(\"click\", () => {\n draft = { ...document_.guard };\n state.guardDirty = false;\n drawGuard();\n });\n bar.appendChild(preview);\n bar.appendChild(apply);\n bar.appendChild(revert);\n panel.appendChild(bar);\n}\n\nfunction setDraft(change: Partial<GuardDraft>): void {\n if (draft === undefined) return;\n draft = { ...draft, ...change };\n state.guardDirty = true;\n $(\"guard-mode\").textContent = \"unsaved changes\";\n}\n\n/** Forgets an in-progress edit, so a reload of the policy document is reflected. */\nexport function resetGuardDraft(): void {\n draft = undefined;\n state.guardDirty = false;\n}\n\n/**\n * The rules a guard preview is run against.\n *\n * The *running* rules, not whatever is sitting unsaved in the editor, so the answer is\n * about the guard alone. The server splices its predicate rules back in by position, so\n * this is the policy in force.\n */\nfunction liveRules(): unknown[] {\n return (state.policy?.rules ?? []).filter((row) => row.editable && row.rule !== undefined).map((row) => row.rule);\n}\n\nasync function previewGuard(): Promise<void> {\n if (draft === undefined) return;\n const result = await postJson<Preview & { error?: string }>(\"/api/policy/preview\", { rules: liveRules(), guard: draft });\n if (!result.ok) {\n showResult(\"bad\", (box) => box.appendChild(el(\"div\", null, result.error ?? \"\")));\n toast(\"bad\", \"Refused\", result.error ?? \"\");\n return;\n }\n renderPreview(result.data, state.editorDirty ? [\"Previewed against the rules currently in force, not the unsaved edits in the editor.\"] : []);\n app.showTab(\"policy\");\n}\n\nasync function applyGuard(): Promise<void> {\n if (draft === undefined) return;\n const result = await postJson<{ guard: GuardDraft; error?: string }>(\"/api/guard\", draft);\n if (!result.ok) {\n showResult(\"bad\", (box) => box.appendChild(el(\"div\", null, result.error ?? \"\")));\n toast(\"bad\", \"Guard unchanged\", result.error ?? \"\");\n return;\n }\n state.guardDirty = false;\n draft = { ...result.data.guard };\n if (state.policy !== undefined) state.policy.guard = { ...result.data.guard };\n toast(\"ok\", \"Guard changed\", `${result.data.guard.falsePositivePolicy}, falling back to ${result.data.guard.fallbackAction}.`);\n showResult(\"warn\", (box) => {\n box.appendChild(el(\"div\", null, \"The guard changed. It applies from the next request, and it is in the notices panel and in your logs.\"));\n if (result.data.guard.falsePositivePolicy !== \"strict\") {\n box.appendChild(\n el(\n \"div\",\n \"ev-meta\",\n \"Requests can now be denied without proof. The guard-stop count is the series to watch: every stop that no longer happens is a request that used to be recoverable and is not any more.\",\n ),\n );\n }\n });\n app.draw();\n}\n\nfunction guardField(name: string, control: HTMLElement, hint?: string): HTMLElement {\n const row = el(\"div\", \"field\");\n row.appendChild(label(name, control));\n const right = el(\"div\", \"field-row\");\n right.appendChild(control);\n if (hint !== undefined) right.appendChild(el(\"span\", \"hint\", hint));\n row.appendChild(right);\n return row;\n}\n\nfunction segmented(options: Array<[string, string]>, value: string, onChange: (value: string) => void): HTMLElement {\n const box = el(\"div\", \"seg\");\n for (const [label, option] of options) {\n const button = el(\"button\", null, label);\n button.type = \"button\";\n button.setAttribute(\"aria-pressed\", String(option === value));\n button.addEventListener(\"click\", () => onChange(option));\n box.appendChild(button);\n }\n return box;\n}\n\nfunction select(options: readonly string[], value: string, onChange: (value: string) => void): HTMLElement {\n const node = el(\"select\");\n for (const option of options) {\n const item = el(\"option\", null, option);\n item.value = option;\n if (option === value) item.selected = true;\n node.appendChild(item);\n }\n node.addEventListener(\"change\", () => onChange(node.value));\n return node;\n}\n\nfunction number(value: number, onChange: (value: number) => void): HTMLElement {\n const input = el(\"input\");\n input.type = \"number\";\n input.min = \"1\";\n input.max = \"100\";\n input.value = String(value);\n input.addEventListener(\"input\", () => {\n if (input.value !== \"\") onChange(Number(input.value));\n });\n return input;\n}\n","import { $, clear, el } from \"./dom.js\";\nimport { BOOT, SECTIONS } from \"./boot.js\";\nimport { getJson, postJson } from \"./api.js\";\nimport { n } from \"./format.js\";\nimport { toast } from \"./app.js\";\n\n/**\n * The range sets, and what is in them.\n *\n * The Guard panel used to report these as `allowlist (3)` — a count, which tells you\n * that something is exempt without telling you what. These are the lists that decide\n * which traffic is never assessed and which crawler ranges are believed, so \"what is\n * actually in it\" is the whole question.\n *\n * Editing is behind `controls.editRanges`, and the allowlist carries a warning wherever\n * it appears: an address on it is not judged leniently, it is not judged at all.\n */\ninterface RangeSet {\n name: string;\n size: number;\n entries: string[];\n}\n\ninterface RangesBody {\n ranges: RangeSet[];\n editable: boolean;\n}\n\nlet sets: RangeSet[] = [];\n\nexport async function loadRanges(): Promise<void> {\n if (!SECTIONS.ranges) return;\n try {\n const body = await getJson<RangesBody>(\"/api/ranges\");\n sets = body.ranges;\n drawRanges();\n } catch {\n /* the panel simply stays as it was */\n }\n}\n\nexport function drawRanges(): void {\n if (!SECTIONS.ranges) return;\n const body = $(\"ranges-body\");\n clear(body);\n\n $(\"ranges-mode\").textContent = BOOT.allowActing ? \"editable\" : \"read-only\";\n $(\"ranges-note\").textContent = BOOT.allowActing\n ? \"An address on the allowlist is not judged leniently — it is not judged at all. Detection does not run on it, no evidence is produced, and no rule sees it.\"\n : \"These come from the code that constructed the handler. Enable controls.editRanges to add an address from here.\";\n\n if (sets.length === 0) {\n body.appendChild(el(\"div\", \"note\", \"No range sets are configured. Add an address to the allowlist and one appears.\"));\n }\n\n for (const set of sets) {\n const block = el(\"div\", \"rangeset\");\n const heading = el(\"h3\");\n heading.appendChild(document.createTextNode(set.name));\n heading.appendChild(el(\"span\", null, `${n(set.size)} entr${set.size === 1 ? \"y\" : \"ies\"}`));\n block.appendChild(heading);\n\n const list = el(\"div\", \"cidrs\");\n for (const entry of set.entries) {\n const chip = el(\"span\", BOOT.allowActing ? \"cidr\" : \"cidr readonly\");\n chip.appendChild(document.createTextNode(entry));\n if (BOOT.allowActing) {\n const remove = el(\"button\", null, \"×\");\n remove.title = `Remove ${entry} from ${set.name}`;\n remove.setAttribute(\"aria-label\", `Remove ${entry} from ${set.name}`);\n remove.addEventListener(\"click\", () => void update(set.name, { remove: [entry] }));\n chip.appendChild(remove);\n }\n list.appendChild(chip);\n }\n if (set.entries.length === 0) list.appendChild(el(\"span\", \"hint\", \"empty\"));\n block.appendChild(list);\n body.appendChild(block);\n }\n\n if (!BOOT.allowActing) return;\n\n const form = el(\"div\", \"rangeset\");\n const row = el(\"div\", \"field-row\");\n const name = el(\"input\", \"mono-input\");\n name.type = \"text\";\n name.value = \"allowlist\";\n name.setAttribute(\"aria-label\", \"Range set\");\n name.style.maxWidth = \"150px\";\n const value = el(\"input\", \"mono-input\");\n value.type = \"text\";\n value.placeholder = \"203.0.113.0/24\";\n value.setAttribute(\"aria-label\", \"Address or CIDR to add\");\n const add = el(\"button\", null, \"Add\");\n const submit = (): void => {\n const entry = value.value.trim();\n if (entry === \"\") return;\n value.value = \"\";\n void update(name.value.trim(), { add: [entry] });\n };\n add.addEventListener(\"click\", submit);\n value.addEventListener(\"keydown\", (event) => {\n if (event.key === \"Enter\") submit();\n });\n row.appendChild(name);\n row.appendChild(value);\n row.appendChild(add);\n form.appendChild(row);\n body.appendChild(form);\n}\n\nasync function update(name: string, change: { add?: string[]; remove?: string[] }): Promise<void> {\n const result = await postJson<{ entries?: string[]; error?: string }>(\"/api/ranges\", { name, ...change });\n if (!result.ok) {\n toast(\"bad\", \"Ranges unchanged\", result.error ?? \"\");\n return;\n }\n toast(name === \"allowlist\" && change.add !== undefined ? \"warn\" : \"ok\", `“${name}” updated`, `${n(result.data.entries?.length ?? 0)} entr${result.data.entries?.length === 1 ? \"y\" : \"ies\"} now.`);\n await loadRanges();\n}\n","import type { DashboardEntry } from \"./types.js\";\n\n/** A rule as the editor holds it: plain JSON, plus the card's open/closed state. */\nexport interface EditorRule {\n id: string;\n match: Record<string, unknown>;\n action: string;\n params?: Record<string, unknown>;\n reason?: string;\n /** Editor-only: whether this card is expanded. Stripped before anything is submitted. */\n _open?: boolean;\n}\n\nexport interface Draft {\n rule: EditorRule;\n /** One line saying what the draft matched on, and why that and not something else. */\n because: string;\n}\n\n/**\n * Turns a request you are looking at into a rule you can preview.\n *\n * This is the half of the loop the dashboard was missing. The row detail could already\n * turn a request into a replay line and a corpus case — the offline loop, where a\n * verdict you disagree with becomes a test. The online loop ran the other way and had\n * no help at all: you saw an actor worth acting on, then went and hand-wrote a rule in\n * a different tab, guessing at which field would catch it.\n *\n * Three decisions in here, and all three are about not being clever on somebody's\n * behalf:\n *\n * **The action is always `tag`.** Never `block`, never `challenge`, whatever the\n * request looks like. A drafted rule is a starting point that has not been reviewed by\n * anyone, and the dashboard picking a terminal action for a request that annoyed you is\n * exactly the reflex this library exists to interrupt. The operator picks the action;\n * the guard still checks it afterwards either way.\n *\n * **It matches on the strongest thing the request actually proves**, in a fixed order:\n * a claimed-and-checked identity, else the detectors whose evidence was proven, else\n * the verdict with a score floor. Each of those is a fact about the client. A path\n * would narrow it further and is deliberately left out — the request happens to have\n * one, which is not the same as the rule being about it.\n *\n * **It goes last.** Appending is the only position that cannot change what any\n * existing rule does, because first match wins. A rule that never fires shows up as\n * \"never matched\" in the preview, which is a better way to learn it is shadowed than\n * discovering it silently took over from something else.\n */\nexport function draftRule(entry: DashboardEntry, existingIds: readonly string[] = []): Draft {\n const match: Record<string, unknown> = {};\n let because: string;\n let stem: string;\n\n const proven = entry.evidence.filter((item) => item.certainty === \"certain\" && item.direction !== \"human\");\n const detectors = [...new Set((proven.length > 0 ? proven : entry.evidence.filter((item) => item.direction !== \"human\")).map((item) => item.detector))];\n\n if (entry.identity !== undefined && entry.identity !== \"\") {\n match[\"identity\"] = [entry.identity];\n if (entry.certain) match[\"certain\"] = true;\n stem = entry.identity;\n because = entry.certain\n ? `Matched on the identity “${entry.identity}”, and on proof — so a client merely claiming that name does not match.`\n : `Matched on the claimed identity “${entry.identity}”. Nothing has verified it, so this matches anything that says so.`;\n } else if (proven.length > 0) {\n match[\"detector\"] = detectors;\n match[\"certain\"] = true;\n stem = detectors[0] ?? \"proven\";\n because = `Matched on proof from ${detectors.join(\", \")}. Only requests that carry the same proof match.`;\n } else if (detectors.length > 0) {\n match[\"verdict\"] = [entry.verdict];\n match[\"detector\"] = detectors;\n match[\"minScore\"] = Math.max(0, Math.floor(entry.score / 10) * 10);\n stem = detectors[0] ?? entry.verdict;\n because = `Matched on ${entry.verdict} at score ${String(match[\"minScore\"])} or more, from ${detectors.join(\", \")}. Every one of those is probabilistic, so the guard will not let this rule deny anybody.`;\n } else {\n match[\"verdict\"] = [entry.verdict];\n stem = entry.verdict;\n because = `Nothing fired on this request, so there is nothing sharper to match on than the verdict itself. Narrow it before you use it.`;\n }\n\n return {\n rule: {\n id: uniqueId(`from-${slug(stem)}`, existingIds),\n match,\n action: \"tag\",\n reason: \"Drafted from a request on the dashboard.\",\n _open: true,\n },\n because,\n };\n}\n\nfunction slug(value: string): string {\n const cleaned = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return cleaned === \"\" ? \"request\" : cleaned.slice(0, 40);\n}\n\n/** Rule ids name every decision and every log line, so two rules sharing one is a real problem. */\nfunction uniqueId(wanted: string, taken: readonly string[]): string {\n if (!taken.includes(wanted)) return wanted;\n for (let suffix = 2; suffix < 1000; suffix++) {\n const candidate = `${wanted}-${suffix}`;\n if (!taken.includes(candidate)) return candidate;\n }\n return `${wanted}-${Date.now()}`;\n}\n","import { $, byId, clear, el, label } from \"./dom.js\";\nimport { API, SECTIONS } from \"./boot.js\";\nimport { app, download, today, toast } from \"./app.js\";\nimport { getJson, postJson } from \"./api.js\";\nimport { actionKind } from \"./outcome.js\";\nimport { drawGuard, resetGuardDraft } from \"./guard.js\";\nimport { drawRanges } from \"./ranges.js\";\nimport { n } from \"./format.js\";\nimport { renderPreview, showResult } from \"./result.js\";\nimport { state } from \"./store.js\";\nimport { draftRule } from \"./draft.js\";\nimport type { EditorRule } from \"./draft.js\";\nimport type { DashboardEntry, Policy, Preview } from \"./types.js\";\n\n/**\n * The rule editor works on a model — an array of plain rule objects — and both views\n * render from it. The GUI mutates the model in place on each keystroke, which is what\n * keeps focus and cursor position while somebody types; only structural changes (add,\n * remove, move, a different action) re-render a card.\n */\nexport async function loadPolicy(): Promise<void> {\n if (!SECTIONS.policy) return;\n try {\n const document_ = await getJson<Policy>(\"/api/policy\");\n state.policy = document_;\n if (!state.editorDirty) {\n setEditorRules(document_.rules.filter((row) => row.editable).map((row) => row.rule as unknown as EditorRule));\n }\n if (!state.guardDirty) resetGuardDraft();\n drawPolicyTab();\n } catch {\n /* the page still works without it */\n }\n}\n\nfunction setEditorRules(rules: readonly EditorRule[]): void {\n state.editorRules = JSON.parse(JSON.stringify(rules ?? [])) as EditorRule[];\n renderEditor();\n}\n\nfunction markDirty(): void {\n state.editorDirty = true;\n $(\"policy-dirty\").hidden = false;\n}\n\nfunction editorRules(): { rules?: EditorRule[]; error?: string } {\n if (state.editorMode === \"json\") {\n try {\n const parsed: unknown = JSON.parse(byId<HTMLTextAreaElement>(\"policy-json\").value);\n if (!Array.isArray(parsed)) return { error: \"The JSON must be an array of rules.\" };\n return { rules: parsed as EditorRule[] };\n } catch (error) {\n return { error: `The editor does not contain valid JSON: ${String(error)}` };\n }\n }\n return { rules: state.editorRules };\n}\n\n/** Strips the empty fields a form inevitably leaves behind, so the JSON stays readable. */\nfunction cleanRule(rule: EditorRule): Record<string, unknown> {\n const match: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rule.match ?? {})) {\n if (value === undefined || value === null || value === \"\") continue;\n if (Array.isArray(value) && value.length === 0) continue;\n match[key] = value;\n }\n const params: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(rule.params ?? {})) {\n if (value === undefined || value === null || value === \"\") continue;\n if (key === \"limit\") {\n const limit = value as { max?: unknown; windowMs?: unknown };\n if (limit.max === undefined || limit.windowMs === undefined) continue;\n }\n params[key] = value;\n }\n const out: Record<string, unknown> = { id: rule.id, match, action: rule.action };\n if (Object.keys(params).length > 0) out[\"params\"] = params;\n if (rule.reason !== undefined && rule.reason !== \"\") out[\"reason\"] = rule.reason;\n return out;\n}\n\nfunction cleanRules(rules: readonly EditorRule[]): Array<Record<string, unknown>> {\n return rules.map(cleanRule);\n}\n\n// ---- field widgets ---------------------------------------------------------\n\nfunction field(name: string, control: HTMLElement | HTMLElement[], hint?: string): HTMLElement {\n const row = el(\"div\", \"field\");\n row.appendChild(label(name, control));\n const right = el(\"div\", \"field-row\");\n for (const node of Array.isArray(control) ? control : [control]) right.appendChild(node);\n if (hint !== undefined) right.appendChild(el(\"span\", \"hint\", hint));\n row.appendChild(right);\n return row;\n}\n\nfunction chipSelect(options: readonly string[], selected: unknown, onChange: (value: string[] | undefined) => void): HTMLElement {\n const box = el(\"div\", \"chips-select\");\n const chosen: string[] = Array.isArray(selected) ? [...(selected as string[])] : selected === undefined ? [] : [String(selected)];\n for (const option of options) {\n const button = el(\"button\", null, option);\n button.type = \"button\";\n button.setAttribute(\"aria-pressed\", String(chosen.includes(option)));\n button.addEventListener(\"click\", () => {\n const at = chosen.indexOf(option);\n if (at === -1) chosen.push(option);\n else chosen.splice(at, 1);\n button.setAttribute(\"aria-pressed\", String(at === -1));\n onChange(chosen.length === 0 ? undefined : [...chosen]);\n });\n box.appendChild(button);\n }\n return box;\n}\n\nfunction segmented<T>(options: Array<[string, T]>, value: T, onChange: (value: T) => void): HTMLElement {\n const box = el(\"div\", \"seg\");\n for (const [label, option] of options) {\n const button = el(\"button\", null, label);\n button.type = \"button\";\n button.setAttribute(\"aria-pressed\", String(option === value));\n button.addEventListener(\"click\", () => {\n for (const other of Array.from(box.children)) other.setAttribute(\"aria-pressed\", \"false\");\n button.setAttribute(\"aria-pressed\", \"true\");\n onChange(option);\n });\n box.appendChild(button);\n }\n return box;\n}\n\nfunction textInput(value: unknown, placeholder: string, onChange: (value: string | undefined) => void, mono = false): HTMLInputElement {\n const input = el(\"input\", mono ? \"mono-input\" : null);\n input.type = \"text\";\n input.value = value === undefined || value === null ? \"\" : String(value);\n input.placeholder = placeholder;\n input.addEventListener(\"input\", () => onChange(input.value.trim() === \"\" ? undefined : input.value));\n return input;\n}\n\n/** A comma-separated list, because a tag widget is a lot of keyboard surface for one path. */\nfunction listInput(value: unknown, placeholder: string, onChange: (value: string | string[] | undefined) => void): HTMLInputElement {\n const current = value === undefined ? \"\" : Array.isArray(value) ? (value as string[]).join(\", \") : String(value);\n const input = textInput(current, placeholder, () => {}, true);\n input.addEventListener(\"input\", () => {\n const parts = input.value\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n onChange(parts.length === 0 ? undefined : parts.length === 1 ? parts[0] : parts);\n });\n return input;\n}\n\nfunction numberInput(value: unknown, placeholder: string, onChange: (value: number | undefined) => void): HTMLInputElement {\n const input = el(\"input\");\n input.type = \"number\";\n input.value = value === undefined || value === null ? \"\" : String(value);\n input.placeholder = placeholder;\n input.addEventListener(\"input\", () => onChange(input.value === \"\" ? undefined : Number(input.value)));\n return input;\n}\n\n// ---- one rule --------------------------------------------------------------\n\n/** What a collapsed card says about itself: the match, in the order a reader scans it. */\nfunction matchSummary(rule: EditorRule): HTMLElement {\n const box = el(\"div\", \"rule-summary\");\n const match = rule.match ?? {};\n const parts: Array<[string, string]> = [];\n for (const key of [\"verdict\", \"botClass\", \"category\", \"identity\", \"detector\", \"method\", \"path\"]) {\n const value = match[key];\n if (value === undefined) continue;\n parts.push([key, Array.isArray(value) ? (value as string[]).join(\", \") : String(value)]);\n }\n if (match[\"certain\"] !== undefined) parts.push([\"certain\", String(match[\"certain\"])]);\n if (match[\"minScore\"] !== undefined || match[\"maxScore\"] !== undefined) {\n parts.push([\"score\", `${String(match[\"minScore\"] ?? 0)}–${String(match[\"maxScore\"] ?? 99)}`]);\n }\n if (match[\"minPriorConfirmations\"] !== undefined) parts.push([\"prior\", String(match[\"minPriorConfirmations\"])]);\n if (match[\"minUnsolvedChallenges\"] !== undefined) parts.push([\"unsolved\", String(match[\"minUnsolvedChallenges\"])]);\n\n if (parts.length === 0) {\n box.appendChild(el(\"span\", \"none\", \"matches everything\"));\n return box;\n }\n for (const [key, value] of parts.slice(0, 4)) box.appendChild(el(\"span\", \"t\", `${key}: ${value}`));\n if (parts.length > 4) box.appendChild(el(\"span\", \"k\", `+${parts.length - 4} more`));\n return box;\n}\n\nfunction ruleCard(rule: EditorRule, index: number): HTMLElement {\n const vocabulary = state.policy?.vocabulary;\n const open = rule._open === true;\n const card = el(\"div\", `rule${open ? \"\" : \" collapsed\"}`);\n\n const head = el(\"div\", \"rule-head\");\n\n // Collapsed by default: eight rules with every field on screen is a wall, and what a\n // reader wants first is the order and what each one matches.\n const chevron = el(\"button\", \"chev\", open ? \"▾\" : \"▸\");\n chevron.title = open ? \"Collapse\" : \"Expand\";\n chevron.setAttribute(\"aria-expanded\", String(open));\n chevron.addEventListener(\"click\", () => {\n rule._open = !open;\n renderEditor();\n });\n head.appendChild(chevron);\n head.appendChild(el(\"span\", \"ord\", index + 1));\n\n const id = textInput(rule.id, \"rule-id\", (value) => {\n rule.id = value ?? \"\";\n markDirty();\n }, true);\n id.setAttribute(\"aria-label\", \"Rule id\");\n head.appendChild(id);\n\n if (open) {\n const action = el(\"select\");\n action.setAttribute(\"aria-label\", \"Action\");\n for (const name of vocabulary?.actions ?? []) {\n const option = el(\"option\", null, name);\n option.value = name;\n if (name === rule.action) option.selected = true;\n action.appendChild(option);\n }\n action.addEventListener(\"change\", () => {\n rule.action = action.value;\n rule.params = {};\n markDirty();\n renderEditor();\n });\n head.appendChild(action);\n } else {\n head.appendChild(matchSummary(rule));\n head.appendChild(el(\"span\", `act-pill ${actionKind(rule.action)}`, rule.action));\n }\n\n const up = el(\"button\", \"icon\", \"↑\");\n up.title = \"Move earlier — the first matching rule wins\";\n up.addEventListener(\"click\", () => moveRule(index, -1));\n const down = el(\"button\", \"icon\", \"↓\");\n down.title = \"Move later\";\n down.addEventListener(\"click\", () => moveRule(index, 1));\n const remove = el(\"button\", \"icon danger\", \"Remove\");\n remove.addEventListener(\"click\", () => {\n state.editorRules.splice(index, 1);\n markDirty();\n renderEditor();\n });\n head.appendChild(up);\n head.appendChild(down);\n head.appendChild(remove);\n card.appendChild(head);\n\n if (!open) return card;\n\n const body = el(\"div\", \"rule-body\");\n rule.match = rule.match ?? {};\n const match = rule.match;\n\n body.appendChild(field(\"Verdict\", chipSelect(vocabulary?.verdicts ?? [], match[\"verdict\"], (value) => { match[\"verdict\"] = value; markDirty(); })));\n body.appendChild(field(\"Bot class\", chipSelect(vocabulary?.botClasses ?? [], match[\"botClass\"], (value) => { match[\"botClass\"] = value; markDirty(); })));\n body.appendChild(field(\"Category\", chipSelect(vocabulary?.categories ?? [], match[\"category\"], (value) => { match[\"category\"] = value; markDirty(); })));\n body.appendChild(field(\"Detector\", chipSelect(vocabulary?.detectors ?? [], match[\"detector\"], (value) => { match[\"detector\"] = value; markDirty(); })));\n body.appendChild(field(\"Method\", chipSelect(vocabulary?.methods ?? [], match[\"method\"], (value) => { match[\"method\"] = value; markDirty(); })));\n\n body.appendChild(\n field(\n \"Evidence\",\n segmented<boolean | undefined>(\n [\n [\"any\", undefined],\n [\"proven\", true],\n [\"unproven\", false],\n ],\n match[\"certain\"] as boolean | undefined,\n (value) => {\n match[\"certain\"] = value;\n markDirty();\n },\n ),\n \"proven means at least one piece of certain evidence — including a proven human\",\n ),\n );\n\n body.appendChild(\n field(\"Score\", [\n numberInput(match[\"minScore\"], \"min\", (value) => { match[\"minScore\"] = value; markDirty(); }),\n el(\"span\", \"hint\", \"to\"),\n numberInput(match[\"maxScore\"], \"max\", (value) => { match[\"maxScore\"] = value; markDirty(); }),\n ]),\n );\n\n body.appendChild(field(\"Identity\", listInput(match[\"identity\"], \"googlebot, gptbot\", (value) => { match[\"identity\"] = value; markDirty(); })));\n body.appendChild(field(\"Path\", listInput(match[\"path\"], \"/api/, /search\", (value) => { match[\"path\"] = value; markDirty(); }), \"a string matches as a prefix\"));\n body.appendChild(\n field(\n \"Prior bots\",\n numberInput(match[\"minPriorConfirmations\"], \"0\", (value) => { match[\"minPriorConfirmations\"] = value; markDirty(); }),\n \"times this actor was already proven a bot\",\n ),\n );\n body.appendChild(\n field(\n \"Unsolved\",\n numberInput(match[\"minUnsolvedChallenges\"], \"0\", (value) => { match[\"minUnsolvedChallenges\"] = value; markDirty(); }),\n \"challenges issued to this actor that were never answered — solving one clears the count\",\n ),\n );\n\n rule.params = rule.params ?? {};\n const params = rule.params;\n for (const node of paramFields(rule.action, params)) body.appendChild(node);\n\n body.appendChild(field(\"Reason\", textInput(rule.reason, \"shown in the decision and in your logs\", (value) => { rule.reason = value ?? \"\"; markDirty(); })));\n\n card.appendChild(body);\n return card;\n}\n\nfunction paramFields(action: string, params: Record<string, unknown>): HTMLElement[] {\n switch (action) {\n case \"block\":\n return [\n field(\"Status\", numberInput(params[\"status\"], \"403\", (value) => { params[\"status\"] = value; markDirty(); })),\n field(\"Body\", textInput(params[\"body\"], \"Automated traffic is not served here.\", (value) => { params[\"body\"] = value; markDirty(); })),\n ];\n case \"redirect\":\n return [field(\"Location\", textInput(params[\"location\"], \"/too-fast\", (value) => { params[\"location\"] = value; markDirty(); }, true))];\n case \"delay\":\n return [field(\"Delay\", numberInput(params[\"delayMs\"], \"250\", (value) => { params[\"delayMs\"] = value; markDirty(); }), \"milliseconds\")];\n case \"rate-limit\": {\n const limit = (params[\"limit\"] ?? {}) as Record<string, unknown>;\n params[\"limit\"] = limit;\n return [\n field(\"Limit\", [\n numberInput(limit[\"max\"], \"60\", (value) => { limit[\"max\"] = value; markDirty(); }),\n el(\"span\", \"hint\", \"requests per\"),\n numberInput(limit[\"windowMs\"], \"60000\", (value) => { limit[\"windowMs\"] = value; markDirty(); }),\n el(\"span\", \"hint\", \"ms\"),\n ]),\n ];\n }\n case \"custom\":\n return [field(\"Handler\", textInput(params[\"handler\"], \"handler-id\", (value) => { params[\"handler\"] = value; markDirty(); }, true), \"id of a handler you registered\")];\n default:\n return [];\n }\n}\n\nfunction lockedCard(row: { id: string; index: number }): HTMLElement {\n const card = el(\"div\", \"rule locked\");\n const head = el(\"div\", \"rule-head\");\n head.appendChild(el(\"span\", \"ord\", `#${row.index + 1}`));\n head.appendChild(el(\"span\", \"mono\", row.id));\n head.appendChild(el(\"span\", \"grow\"));\n head.appendChild(el(\"span\", \"pill\", \"predicate — locked\"));\n card.appendChild(head);\n card.appendChild(\n el(\"div\", \"rule-body\", \"This rule matches with a function, which cannot be represented here or sent over HTTP. It stays exactly as it is, at this position, whatever else you change.\"),\n );\n return card;\n}\n\nfunction moveRule(index: number, delta: number): void {\n const target = index + delta;\n if (target < 0 || target >= state.editorRules.length) return;\n const moved = state.editorRules.splice(index, 1)[0];\n if (moved === undefined) return;\n state.editorRules.splice(target, 0, moved);\n markDirty();\n renderEditor();\n}\n\nfunction renderEditor(): void {\n if (!SECTIONS.policy) return;\n const list = $(\"rulelist\");\n clear(list);\n const locked = (state.policy?.rules ?? []).filter((row) => !row.editable);\n\n if (state.editorRules.length === 0 && locked.length === 0) {\n list.appendChild(el(\"div\", \"note\", \"No rules. Every request takes the default action — add one, or import a set.\"));\n }\n\n // Locked rules are drawn at the index they hold, because that is where the server\n // will splice them back in and therefore what decides whether they run first.\n const rendered: HTMLElement[] = state.editorRules.map((rule, index) => ruleCard(rule, index));\n for (const row of locked) rendered.splice(Math.min(row.index, rendered.length), 0, lockedCard(row));\n for (const node of rendered) list.appendChild(node);\n\n if (state.editorMode === \"json\") byId<HTMLTextAreaElement>(\"policy-json\").value = JSON.stringify(cleanRules(state.editorRules), null, 2);\n}\n\nexport function drawPolicyTab(): void {\n const document_ = state.policy;\n if (document_ === undefined) return;\n\n const editable = document_.editable;\n $(\"policy-apply\").hidden = !editable;\n byId<HTMLTextAreaElement>(\"policy-json\").readOnly = !editable;\n $(\"policy-mode\").textContent = editable ? \"editable\" : \"read-only\";\n $(\"rule-add\").hidden = !editable;\n $(\"policy-import\").hidden = !editable;\n\n const preserved = document_.rules.filter((row) => !row.editable);\n let note = editable ? \"First match wins — order matters.\" : \"Read-only. Enable controls.editPolicy to change these.\";\n if (preserved.length > 0) note += ` ${preserved.length} rule(s) use a predicate function and are locked.`;\n $(\"policy-note\").textContent = note;\n\n renderPresetButtons();\n drawGuard();\n drawRanges();\n\n if (SECTIONS.robots) {\n $(\"robots-preview\").textContent = document_.robots === \"\" ? \"(this policy declines no crawler by name)\" : document_.robots;\n const notes = $(\"robots-notes\");\n clear(notes);\n for (const note_ of document_.robotsNotes) notes.appendChild(el(\"div\", \"ev-meta\", `${note_.rule}: ${note_.reason}`));\n }\n\n const rules = $(\"stat-rules\");\n clear(rules);\n const installed = state.snapshot?.rules ?? [];\n if (installed.length === 0) rules.appendChild(el(\"div\", \"note\", \"No rules configured — every request takes the default action.\"));\n else installed.forEach((rule, index) => rules.appendChild(el(\"span\", \"chip\", `${index + 1}. ${rule}`)));\n}\n\n/**\n * Starts a rule from a request the operator was looking at.\n *\n * Appended, expanded, and previewed straight away — the three things somebody would\n * otherwise do by hand between noticing an actor and having a rule they can judge.\n * Nothing is applied: this fills the editor, exactly like an import.\n *\n * It loads the policy document first, and that `await` is load-bearing rather than\n * tidy. Drafting is reachable from the Live tab, which somebody can use for an hour\n * without ever opening the Policy one — and appending to an editor that has not been\n * filled yet leaves it holding the draft *and nothing else*, while marking it dirty so\n * the load that follows will not correct it. Apply that and the running policy becomes\n * one drafted rule.\n */\nexport async function draftIntoEditor(entry: DashboardEntry): Promise<void> {\n if (state.policy === undefined) await loadPolicy();\n const drafted = draftRule(entry, state.editorRules.map((rule) => rule.id));\n state.editorRules.push(drafted.rule);\n markDirty();\n if (state.editorMode === \"json\") byId<HTMLTextAreaElement>(\"policy-json\").value = JSON.stringify(cleanRules(state.editorRules), null, 2);\n renderEditor();\n app.showTab(\"policy\");\n\n const notes = [\n `Drafted “${drafted.rule.id}”, tagging only. Nothing is applied — read it, choose the action, then apply.`,\n drafted.because,\n \"It was added last, which is the only position that cannot change what an existing rule does. Move it up with ↑ if it needs to win.\",\n ];\n // Said immediately, because the preview is a round trip and an empty box in the\n // meantime reads as nothing having happened.\n showResult(\"warn\", (box) => {\n for (const note of notes) box.appendChild(el(\"div\", note === notes[0] ? null : \"ev-meta\", note));\n });\n toast(\"ok\", \"Rule drafted\", \"In the editor, tagging only, not applied.\");\n await runPreview(notes);\n}\n\n// ---- mode switch, add, expand ----------------------------------------------\n\nfunction switchToGui(): void {\n if (state.editorMode === \"gui\") return;\n const parsed = editorRules();\n if (parsed.error !== undefined) {\n toast(\"bad\", \"That JSON will not parse\", parsed.error);\n return;\n }\n state.editorRules = parsed.rules ?? [];\n state.editorMode = \"gui\";\n $(\"mode-gui\").setAttribute(\"aria-pressed\", \"true\");\n $(\"mode-json\").setAttribute(\"aria-pressed\", \"false\");\n $(\"editor-gui\").hidden = false;\n $(\"editor-json\").hidden = true;\n renderEditor();\n}\n\nfunction switchToJson(): void {\n state.editorMode = \"json\";\n $(\"mode-gui\").setAttribute(\"aria-pressed\", \"false\");\n $(\"mode-json\").setAttribute(\"aria-pressed\", \"true\");\n $(\"editor-gui\").hidden = true;\n $(\"editor-json\").hidden = false;\n byId<HTMLTextAreaElement>(\"policy-json\").value = JSON.stringify(cleanRules(state.editorRules), null, 2);\n}\n\n// ---- import and export ------------------------------------------------------\n\nasync function exportSettings(): Promise<void> {\n // The whole settings document, not just the rules: a file that records the detectors,\n // ranges and guard alongside them is the one you want when comparing two deployments\n // six months from now. Only the rules half is importable, and the file says so.\n try {\n const response = await fetch(`${API}/api/settings`);\n const text = await response.text();\n if (!response.ok) throw new Error(text);\n download(text, `bothandler-settings-${today()}.json`, \"application/json\");\n toast(\"ok\", \"Settings exported\", \"Rules, plus a record of the configuration around them.\");\n } catch (error) {\n toast(\"bad\", \"Export failed\", String(error));\n }\n}\n\nfunction readSettingsFile(file: File): void {\n const reader = new FileReader();\n reader.onload = (): void => {\n try {\n applyImported(JSON.parse(String(reader.result)) as Record<string, unknown>, file.name);\n } catch (error) {\n toast(\"bad\", \"That file is not JSON\", String(error));\n }\n };\n reader.onerror = (): void => toast(\"bad\", \"Could not read that file\", \"\");\n reader.readAsText(file);\n}\n\n/**\n * Loads a file into the editor — and only into the editor.\n *\n * Nothing is applied: an import that took effect on drop would be a policy change made\n * by a mis-drag. What it does is fill the editor and say what it ignored, so the next\n * step is the same Preview and Apply as any other edit.\n */\nfunction applyImported(document_: Record<string, unknown> | unknown[], name: string): void {\n const rules = Array.isArray(document_)\n ? (document_ as EditorRule[])\n : Array.isArray((document_ as { rules?: unknown }).rules)\n ? ((document_ as { rules: EditorRule[] }).rules)\n : undefined;\n if (rules === undefined) {\n toast(\"bad\", \"Nothing to import\", \"Expected an array of rules, or a settings file with a rules array.\");\n return;\n }\n setEditorRules(rules);\n markDirty();\n switchToGui();\n\n const ignored: string[] = [];\n const readOnly = (document_ as { readOnly?: { lockedRules?: unknown[] } }).readOnly;\n if (readOnly !== undefined) {\n ignored.push(\"the guard, the detectors, the ranges and the audit — those come from the code that built the handler, not from a file\");\n if (Array.isArray(readOnly.lockedRules) && readOnly.lockedRules.length > 0) {\n ignored.push(`${readOnly.lockedRules.length} predicate rule(s), which stay as they are`);\n }\n }\n showResult(\"warn\", (box) => {\n box.appendChild(el(\"div\", null, `Loaded ${rules.length} rule(s) from ${name}. Nothing has been applied yet — preview it first.`));\n for (const line of ignored) box.appendChild(el(\"div\", \"ev-meta\", `Ignored: ${line}`));\n });\n toast(\"ok\", `Imported ${rules.length} rule(s)`, \"Review, preview, then apply.\");\n}\n\n// ---- preview and apply ------------------------------------------------------\n\nfunction collectForSubmit(): Array<Record<string, unknown>> | undefined {\n const parsed = editorRules();\n if (parsed.error !== undefined) {\n showResult(\"bad\", (box) => box.appendChild(el(\"div\", null, parsed.error ?? \"\")));\n toast(\"bad\", \"That JSON will not parse\", parsed.error);\n return undefined;\n }\n const cleaned = cleanRules(parsed.rules ?? []);\n const blank = cleaned.filter((rule) => rule[\"id\"] === undefined || rule[\"id\"] === \"\").length;\n if (blank > 0) {\n toast(\"bad\", \"Every rule needs an id\", \"It is what every decision and log line names.\");\n return undefined;\n }\n return cleaned;\n}\n\nasync function runPreview(notes: readonly string[] = []): Promise<void> {\n const rules = collectForSubmit();\n if (rules === undefined) return;\n const result = await postJson<Preview & { error?: string }>(\"/api/policy/preview\", { rules });\n if (!result.ok) {\n showResult(\"bad\", (box) => box.appendChild(el(\"div\", null, result.error ?? \"\")));\n toast(\"bad\", \"Refused\", result.error ?? \"\");\n return;\n }\n renderPreview(result.data, notes);\n}\n\nasync function applyPolicy(): Promise<void> {\n const rules = collectForSubmit();\n if (rules === undefined) return;\n const result = await postJson<{ rules: number; warnings?: string[]; error?: string }>(\"/api/policy/apply\", { rules });\n if (!result.ok) {\n showResult(\"bad\", (box) => box.appendChild(el(\"div\", null, result.error ?? \"\")));\n toast(\"bad\", \"Refused\", result.error ?? \"\");\n return;\n }\n state.editorDirty = false;\n $(\"policy-dirty\").hidden = true;\n await loadPolicy();\n toast(\"ok\", \"Applied\", `${n(result.data.rules)} rule(s) now in force.`);\n showResult(\"ok\", (box) => {\n box.appendChild(el(\"div\", null, `Applied. ${n(result.data.rules)} rule(s) are now in force.`));\n for (const warning of result.data.warnings ?? []) box.appendChild(el(\"div\", \"ev-meta\", warning));\n });\n}\n\n/** Built from the registry the server sends, so a new preset appears here on its own. */\nfunction renderPresetButtons(): void {\n const box = $(\"preset-buttons\");\n const presets = state.policy?.vocabulary.presets ?? [];\n if (box.childElementCount === presets.length) return;\n clear(box);\n for (const preset of presets) {\n const button = el(\"button\", null, preset);\n button.addEventListener(\"click\", () => {\n void (async () => {\n const result = await postJson<Preview & { error?: string }>(\"/api/policy/preview\", { preset });\n if (!result.ok) {\n toast(\"bad\", \"Refused\", result.error ?? \"\");\n return;\n }\n renderPreview(result.data);\n })();\n });\n box.appendChild(button);\n }\n}\n\nexport function initPolicy(): void {\n if (!SECTIONS.policy) return;\n $(\"mode-gui\").addEventListener(\"click\", switchToGui);\n $(\"mode-json\").addEventListener(\"click\", switchToJson);\n\n $(\"rule-add\").addEventListener(\"click\", () => {\n state.editorRules.push({ id: `new-rule-${state.editorRules.length + 1}`, match: {}, action: \"tag\", params: {}, _open: true });\n markDirty();\n renderEditor();\n });\n\n $(\"rule-expand\").addEventListener(\"click\", () => {\n const anyClosed = state.editorRules.some((rule) => rule._open !== true);\n for (const rule of state.editorRules) rule._open = anyClosed;\n $(\"rule-expand\").textContent = anyClosed ? \"Collapse all\" : \"Expand all\";\n renderEditor();\n });\n\n byId<HTMLTextAreaElement>(\"policy-json\").addEventListener(\"input\", markDirty);\n $(\"policy-preview\").addEventListener(\"click\", () => void runPreview());\n $(\"policy-apply\").addEventListener(\"click\", () => void applyPolicy());\n $(\"policy-revert\").addEventListener(\"click\", () => {\n state.editorDirty = false;\n $(\"policy-dirty\").hidden = true;\n $(\"policy-result\").hidden = true;\n resetGuardDraft();\n void loadPolicy();\n });\n\n $(\"policy-export\").addEventListener(\"click\", () => void exportSettings());\n $(\"policy-import\").addEventListener(\"click\", () => byId<HTMLInputElement>(\"policy-file\").click());\n byId<HTMLInputElement>(\"policy-file\").addEventListener(\"change\", () => {\n const input = byId<HTMLInputElement>(\"policy-file\");\n const file = input.files?.[0];\n if (file !== undefined) readSettingsFile(file);\n input.value = \"\";\n });\n\n const panel = $(\"editor-panel\");\n for (const name of [\"dragenter\", \"dragover\"]) {\n panel.addEventListener(name, (event) => {\n if (state.policy?.editable !== true) return;\n event.preventDefault();\n panel.classList.add(\"drop\");\n });\n }\n for (const name of [\"dragleave\", \"drop\"]) {\n panel.addEventListener(name, (event) => {\n panel.classList.remove(\"drop\");\n if (name !== \"drop\") return;\n event.preventDefault();\n const file = (event as DragEvent).dataTransfer?.files?.[0];\n if (file !== undefined) readSettingsFile(file);\n });\n }\n}\n","import { $, byId, clear, cssEscape, el, rootNode } from \"./dom.js\";\nimport { FEED_LIMIT, matchingCount, setSearch, state, visibleRows } from \"./store.js\";\nimport { SECTIONS } from \"./boot.js\";\nimport { app, download, today, toast } from \"./app.js\";\nimport { clockTime, n } from \"./format.js\";\nimport { corpusCase, replayFile, replayLine } from \"./replay.js\";\nimport { openActor } from \"./actor.js\";\nimport { outcome, verdictBadge } from \"./outcome.js\";\nimport { draftIntoEditor } from \"./policy.js\";\nimport type { FilterName } from \"./query.js\";\nimport type { DashboardEntry } from \"./types.js\";\n\nconst FILTERS: Array<[FilterName, string]> = [\n [\"all\", \"All\"],\n [\"proven\", \"Proven\"],\n [\"suspected\", \"Suspected\"],\n [\"human\", \"Human\"],\n [\"guard\", \"Guard stops\"],\n [\"deny\", \"Denied\"],\n [\"mitigate\", \"Mitigated\"],\n [\"allow\", \"Served\"],\n];\n\n/** A row currently in the table, and what it was built from. */\ninterface Rendered {\n row: HTMLTableRowElement;\n detail: HTMLTableRowElement | undefined;\n rev: number;\n open: boolean;\n}\n\nconst rendered = new Map<string, Rendered>();\n\nexport function initFeed(): void {\n const filters = $(\"filters\");\n for (const [name, label] of FILTERS) {\n const button = el(\"button\", null, label);\n button.setAttribute(\"aria-pressed\", String(name === state.filter));\n button.dataset[\"filter\"] = name;\n button.addEventListener(\"click\", () => {\n state.filter = name;\n for (const other of Array.from(filters.children)) other.setAttribute(\"aria-pressed\", \"false\");\n button.setAttribute(\"aria-pressed\", \"true\");\n app.syncUrl();\n app.drawNow();\n });\n filters.appendChild(button);\n }\n\n const search = byId<HTMLInputElement>(\"search\");\n search.addEventListener(\"input\", () => {\n setSearch(search.value.trim());\n app.syncUrl();\n app.drawNow();\n });\n\n // Downloading the window is the bulk half of what the per-row buttons do one request\n // at a time. It exports what is on screen rather than everything held, because the\n // filter is how you said which requests you meant.\n const exportShown = byId<HTMLButtonElement>(\"feed-export\");\n exportShown.hidden = !SECTIONS.evidence;\n exportShown.addEventListener(\"click\", () => {\n const rows = visibleRows(Number.POSITIVE_INFINITY);\n if (rows.length === 0) {\n toast(\"warn\", \"Nothing to export\", \"No request in the window matches this filter.\");\n return;\n }\n // Oldest first, so the file replays in the order the traffic happened.\n const entries = rows.map((row) => row.entry).reverse();\n download(`${replayFile(entries)}\\n`, `bothandler-feed-${today()}.jsonl`, \"application/x-ndjson\");\n toast(\"ok\", `Exported ${n(entries.length)} request(s)`, \"Replay them with `bothandlerjs replay`.\");\n });\n}\n\n/** Applies filter state that arrived in the URL rather than from a click. */\nexport function reflectFilterButtons(): void {\n for (const button of Array.from($(\"filters\").children)) {\n button.setAttribute(\"aria-pressed\", String(button instanceof HTMLElement && button.dataset[\"filter\"] === state.filter));\n }\n const search = byId<HTMLInputElement>(\"search\");\n if (search.value !== state.search) search.value = state.search;\n}\n\nexport function drawFeed(): void {\n const body = byId<HTMLTableSectionElement>(\"rows\");\n const shown = visibleRows();\n\n // Keyed reconciliation against what is already in the table.\n //\n // The old renderer emptied the tbody and rebuilt three hundred rows on every frame\n // that carried a new request. Two things were wrong with that, and the invisible one\n // is not the expensive one: rebuilding a node destroys any text selection inside it,\n // so a live feed could not be *read* — drag across a User-Agent to copy it and the\n // next request wiped the selection. Rows are now rebuilt only when the entry behind\n // them changed or they were opened, which on a busy feed is one row in three hundred.\n let index = 0;\n const place = (node: Node): void => {\n const current = body.childNodes[index] ?? null;\n // insertBefore moves a node that is already in the document, so this handles a\n // reorder as well as an insert.\n if (current !== node) body.insertBefore(node, current);\n index++;\n };\n\n for (const row of shown) {\n const id = row.entry.requestId;\n const open = state.open.has(id);\n let cached = rendered.get(id);\n if (cached === undefined || cached.rev !== row.rev || cached.open !== open) {\n cached = {\n row: buildRow(row.entry, open),\n detail: open ? buildDetail(row.entry) : undefined,\n rev: row.rev,\n open,\n };\n rendered.set(id, cached);\n }\n place(cached.row);\n if (cached.detail !== undefined) place(cached.detail);\n }\n\n while (body.childNodes.length > index) body.removeChild(body.childNodes[index] as Node);\n\n // The cache outlives the table — a row scrolled past the filter is likely to come\n // back — but not indefinitely. Anything not on screen goes once it has grown past\n // twice the visible set.\n if (rendered.size > shown.length * 2 + 100) {\n const live = new Set(shown.map((row) => row.entry.requestId));\n for (const id of Array.from(rendered.keys())) if (!live.has(id)) rendered.delete(id);\n }\n\n const total = state.rows.length;\n const matching = matchingCount();\n $(\"empty\").hidden = total > 0;\n $(\"feed-count\").textContent =\n matching === total ? `${n(total)} in this window` : `${n(matching)} of ${n(total)}${matching > FEED_LIMIT ? ` · showing ${n(FEED_LIMIT)}` : \"\"}`;\n // `#feed-window` carries the `win` class, so `updateWindowLabels` fills it — one\n // owner for a label that appears on eight panels.\n\n // A thinned feed must never look like a quiet one. The ring still has these — they\n // are in the preview, in the export and in whatever a reconnect replays — but they\n // were kept off the stream to stop a busy origin handing every open browser a\n // megabyte a second.\n const skipped = (state.snapshot?.skipped ?? 0) + state.laggedDrops;\n const note = $(\"feed-skipped\");\n note.hidden = skipped === 0;\n note.textContent = `${n(skipped)} not streamed`;\n note.title =\n state.laggedDrops > 0\n ? `${n(state.laggedDrops)} were skipped because this connection could not keep up, and the rest by the rate cap. All of them are still in the window, the preview and the export.`\n : \"Entries the rate cap kept off this stream. They are still in the window, the preview and the export — raise maxEventsPerSecond to see them live.\";\n}\n\n/** Drops every cached node. Used when the feed is cleared under the page's feet. */\nexport function resetFeedCache(): void {\n rendered.clear();\n}\n\nfunction buildRow(entry: DashboardEntry, open: boolean): HTMLTableRowElement {\n const out = outcome(entry);\n const tr = el(\"tr\", `row a-${entry.downgradedFrom !== undefined ? \"guard\" : out}${open ? \" open\" : \"\"}`);\n\n // When it happened, which the feed never used to say. Ordering implies it while the\n // stream is live and stops implying it the moment you type into the filter box.\n tr.appendChild(el(\"td\", \"num mono tnum when\", clockTime(entry.at)));\n\n const request = el(\"td\", \"edge req\");\n // The row's one control. It carries the name, the state and the keys; the row itself\n // keeps a click handler for the mouse, which needs no role to be useful.\n const toggle = el(\"button\", \"row-toggle\", `${entry.method} ${entry.path}`);\n toggle.type = \"button\";\n toggle.setAttribute(\"aria-expanded\", String(open));\n toggle.setAttribute(\"aria-label\", `${entry.method} ${entry.path}, ${entry.verdict}. Evidence.`);\n toggle.dataset[\"request\"] = entry.requestId;\n request.appendChild(toggle);\n const ua = el(\"span\", \"ua\");\n if (SECTIONS.actors) {\n const actorLink = el(\"a\", null, entry.actor);\n actorLink.href = \"#actor\";\n actorLink.title = \"Show everything from this actor\";\n actorLink.addEventListener(\"click\", (event) => {\n event.preventDefault();\n event.stopPropagation();\n openActor(entry.actor);\n });\n ua.appendChild(actorLink);\n ua.appendChild(document.createTextNode(` · ${entry.userAgent}`));\n } else {\n ua.appendChild(document.createTextNode(`${entry.actor} · ${entry.userAgent}`));\n }\n ua.title = `${entry.actor} · ${entry.userAgent}`;\n request.appendChild(ua);\n tr.appendChild(request);\n\n const verdictCell = el(\"td\");\n const [badgeClass, badgeLabel] = verdictBadge(entry);\n verdictCell.appendChild(el(\"span\", `badge ${badgeClass}`, badgeLabel));\n if (entry.identity !== undefined) verdictCell.appendChild(el(\"span\", \"sub\", entry.identity));\n tr.appendChild(verdictCell);\n\n tr.appendChild(el(\"td\", \"num mono tnum\", entry.certain ? \"proven\" : String(entry.score)));\n\n const actionCell = el(\"td\");\n if (entry.action !== undefined) {\n const kind = out === \"deny\" ? \"act-deny\" : out === \"mitigate\" ? \"act-mitigate\" : out === \"allow\" ? \"act-allow\" : \"act-tag\";\n actionCell.appendChild(el(\"span\", `act ${kind}`, entry.action));\n if (entry.rule !== undefined) {\n const ruleLabel = el(\"span\", \"sub\", entry.rule);\n ruleLabel.title = entry.rule;\n actionCell.appendChild(ruleLabel);\n }\n if (entry.downgradedFrom !== undefined) actionCell.appendChild(el(\"span\", \"guard\", `guard stopped ${entry.downgradedFrom}`));\n } else {\n actionCell.appendChild(el(\"span\", \"sub\", \"assessed only\"));\n }\n tr.appendChild(actionCell);\n\n tr.appendChild(el(\"td\", \"num mono tnum\", entry.durationMs.toFixed(2)));\n\n tr.dataset[\"request\"] = entry.requestId;\n const flip = (): void => {\n if (state.open.has(entry.requestId)) state.open.delete(entry.requestId);\n else state.open.add(entry.requestId);\n app.drawNow();\n // The row was rebuilt, so focus has to be put back or a keyboard user is returned\n // to the top of the document every time they open one.\n rootNode().querySelector<HTMLElement>(`button.row-toggle[data-request=\"${cssEscape(entry.requestId)}\"]`)?.focus();\n };\n toggle.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n flip();\n });\n // Clicking anywhere in the row is the mouse affordance people expect from a feed, and\n // it needs no role to work — a click is not an accessibility contract.\n tr.addEventListener(\"click\", flip);\n return tr;\n}\n\nfunction buildDetail(entry: DashboardEntry): HTMLTableRowElement {\n const tr = el(\"tr\", \"detail\");\n const cell = el(\"td\");\n cell.colSpan = 6;\n\n if (!SECTIONS.evidence) {\n cell.appendChild(\n el(\n \"div\",\n \"ev-meta\",\n \"The evidence section is switched off on this dashboard, so the reasons behind this verdict are not sent to it. What fired, and why, is on a dashboard that has `sections: { evidence: true }`.\",\n ),\n );\n } else if (entry.evidence.length === 0) {\n cell.appendChild(\n el(\n \"div\",\n \"ev-meta\",\n entry.bypass !== undefined\n ? `Detection was skipped for this request: ${entry.bypass}.`\n : \"No detector produced any evidence. This is what ordinary traffic looks like.\",\n ),\n );\n } else {\n const list = el(\"div\", \"ev\");\n for (const item of entry.evidence) {\n const row = el(\"div\", `ev-item${item.direction === \"human\" ? \" human\" : \"\"}`);\n row.appendChild(el(\"div\", `tier t-${item.certainty}`, item.certainty));\n const body = el(\"div\");\n body.appendChild(el(\"div\", null, item.summary));\n let meta = `${item.detector} · points to ${item.direction}`;\n if (item.family !== undefined) meta += ` · family “${item.family}”, counted once with its siblings`;\n body.appendChild(el(\"div\", \"ev-meta\", meta));\n if (item.deterministicBasis !== undefined) body.appendChild(el(\"div\", \"basis\", item.deterministicBasis));\n row.appendChild(body);\n list.appendChild(row);\n }\n cell.appendChild(list);\n }\n\n for (const failure of entry.failures) {\n cell.appendChild(el(\"div\", \"ev-meta\", `Detector ${failure.detector} ${failure.reason}: ${failure.message}`));\n }\n if (entry.downgradeReason !== undefined) cell.appendChild(el(\"div\", \"basis\", `Guard: ${entry.downgradeReason}`));\n\n const queryNames = Object.keys(entry.query);\n if (queryNames.length > 0) {\n const queryTable = el(\"table\", \"hdr\");\n for (const name of queryNames) {\n const value = entry.query[name] ?? \"\";\n const row = el(\"tr\");\n row.appendChild(el(\"td\", \"n\", `?${name}`));\n row.appendChild(el(\"td\", `v${value === \"[redacted]\" ? \" red\" : \"\"}`, value));\n queryTable.appendChild(row);\n }\n cell.appendChild(queryTable);\n }\n\n // The header set in wire order: what half the detectors are actually reading, and the\n // first thing worth looking at when a verdict seems wrong.\n if (entry.headers !== undefined && entry.headers.length > 0) {\n const table = el(\"table\", \"hdr\");\n for (const [name, value] of entry.headers) {\n const row = el(\"tr\");\n row.appendChild(el(\"td\", \"n\", `${name}:`));\n row.appendChild(el(\"td\", `v${value === \"[redacted]\" ? \" red\" : \"\"}`, value));\n table.appendChild(row);\n }\n cell.appendChild(table);\n }\n\n const tools = el(\"div\", \"tools\");\n if (SECTIONS.evidence) {\n tools.appendChild(copyButton(\"Copy replay line\", () => replayLine(entry)));\n tools.appendChild(downloadButton(\"Download replay line\", () => replayLine(entry), `request-${entry.requestId}.jsonl`));\n tools.appendChild(copyButton(\"Copy corpus case\", () => corpusCase(entry)));\n }\n if (SECTIONS.policy) {\n const draft = el(\"button\", null, \"Draft a rule\");\n draft.title = \"Start a rule from this request, in the policy editor\";\n draft.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n void draftIntoEditor(entry);\n });\n tools.appendChild(draft);\n }\n if (SECTIONS.actors) {\n const actorButton = el(\"button\", null, \"Show this actor\");\n actorButton.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n openActor(entry.actor);\n });\n tools.appendChild(actorButton);\n }\n cell.appendChild(tools);\n\n const foot = el(\"div\", \"detail-foot\");\n foot.appendChild(el(\"span\", null, clockTime(entry.at)));\n foot.appendChild(el(\"span\", null, `actor ${entry.actor}`));\n if (entry.rule !== undefined) foot.appendChild(el(\"span\", null, `rule “${entry.rule}”`));\n foot.appendChild(el(\"span\", null, `assessed in ${entry.durationMs.toFixed(3)}ms`));\n foot.appendChild(el(\"span\", \"mono\", entry.requestId));\n cell.appendChild(foot);\n\n tr.appendChild(cell);\n return tr;\n}\n\nfunction copyButton(label: string, produce: () => string): HTMLButtonElement {\n const button = el(\"button\", null, label);\n button.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n const text = produce();\n const done = (): void => {\n button.textContent = \"Copied\";\n setTimeout(() => {\n button.textContent = label;\n }, 1200);\n };\n // navigator.clipboard needs a secure context, which a dashboard on a plain HTTP LAN\n // address is not. The fallback selects the text so a keyboard copy still works\n // rather than leaving a button that does nothing.\n if (navigator.clipboard?.writeText !== undefined) navigator.clipboard.writeText(text).then(done, () => showText(text));\n else showText(text);\n });\n return button;\n}\n\nfunction downloadButton(label: string, produce: () => string, filename: string): HTMLButtonElement {\n const button = el(\"button\", null, label);\n button.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n download(`${produce()}\\n`, filename, \"application/x-ndjson\");\n });\n return button;\n}\n\nfunction showText(text: string): void {\n const box = el(\"pre\", \"code\", text);\n const host = $(\"policy-result\");\n host.hidden = false;\n host.className = \"result\";\n clear(host);\n host.appendChild(el(\"div\", \"ev-meta\", \"Copying needs a secure context; here is the text.\"));\n host.appendChild(box);\n const selection = getSelection();\n if (selection !== null) {\n const range = document.createRange();\n range.selectNodeContents(box);\n selection.removeAllRanges();\n selection.addRange(range);\n }\n}\n","import { $ } from \"./dom.js\";\nimport { API, SECTIONS } from \"./boot.js\";\nimport { app } from \"./app.js\";\nimport { clearFeed, ingest, state } from \"./store.js\";\nimport { getJson } from \"./api.js\";\nimport { resetFeedCache } from \"./feed.js\";\nimport type { DashboardEntry, Snapshot } from \"./types.js\";\n\n/**\n * The event stream.\n *\n * `EventSource` reconnects on its own and resends the last `id:` it saw as\n * `Last-Event-ID`, so the server can answer a reconnect with the handful of frames\n * that were missed instead of the whole ring — five hundred entries with their\n * headers, evidence and actor history attached, every time a laptop lid closes. The\n * page's side of that bargain is this: nothing here tracks the cursor, because the\n * browser already does, and a second copy would be the one that is wrong.\n */\n/**\n * The open connection, so it can be closed again.\n *\n * It used to be a local, which meant nothing could stop it. Embedded in somebody's page\n * that mattered: removing the element left the stream open and the client drawing into a\n * detached tree for the life of the page — one held server connection and a steady trickle\n * of work for a dashboard nobody was looking at.\n */\nlet source: EventSource | undefined;\n\n/**\n * Closes the stream, keeping everything the page has drawn.\n *\n * Reconnecting resumes from where this left off: the server is told the last id seen and\n * sends only what was missed, so suspending across a route change costs a reconnect rather\n * than a reload.\n */\nexport function suspendStream(): void {\n source?.close();\n source = undefined;\n}\n\nexport function connectStream(): void {\n if (!SECTIONS.feed) {\n $(\"dot\").className = \"dot\";\n $(\"conn\").textContent = \"feed off\";\n return;\n }\n if (source !== undefined) return;\n\n source = new EventSource(`${API}/api/stream`);\n\n source.addEventListener(\"open\", () => {\n $(\"dot\").className = \"dot on\";\n $(\"conn\").textContent = \"live\";\n });\n\n // A full backlog is on its way and it replaces what the page holds. Sent when the\n // server cannot honour the cursor — a restarted process, a cleared feed, or a gap\n // longer than the ring — so keeping the old rows would mean showing requests\n // nothing will ever correct.\n source.addEventListener(\"sync\", (event) => {\n const detail = JSON.parse((event as MessageEvent<string>).data) as { replace: boolean };\n if (!detail.replace) return;\n clearFeed();\n resetFeedCache();\n });\n\n source.addEventListener(\"entry\", (event) => {\n ingest(JSON.parse((event as MessageEvent<string>).data) as DashboardEntry);\n if (state.paused) {\n // Paused means the page stops redrawing, so the only thing that may move is the\n // button that says how much you are not being shown.\n state.bufferedWhilePaused++;\n $(\"pause\").textContent = `Resume (${state.bufferedWhilePaused})`;\n }\n app.draw();\n });\n\n source.addEventListener(\"update\", (event) => {\n ingest(JSON.parse((event as MessageEvent<string>).data) as DashboardEntry);\n app.draw();\n });\n\n // Somebody pressed Reset — possibly in another browser. Without this a second viewer\n // goes on showing a feed of requests the server has forgotten.\n source.addEventListener(\"reset\", () => {\n clearFeed();\n resetFeedCache();\n app.draw();\n });\n\n // The server could not keep up with this connection and skipped part of the feed\n // rather than queueing it in its own memory. Said out loud: a gap the viewer knows\n // about is a different thing from one it does not.\n source.addEventListener(\"lagged\", (event) => {\n const detail = JSON.parse((event as MessageEvent<string>).data) as { dropped: number };\n state.laggedDrops += detail.dropped;\n app.draw();\n });\n\n source.addEventListener(\"stats\", (event) => {\n state.snapshot = JSON.parse((event as MessageEvent<string>).data) as Snapshot;\n app.draw();\n });\n\n source.addEventListener(\"error\", () => {\n $(\"dot\").className = \"dot off\";\n $(\"conn\").textContent = \"reconnecting…\";\n // EventSource reconnects on its own; this only reports it. A stream closed by the\n // server on shutdown ends up here too, which is why the dot matters.\n });\n}\n\n/**\n * A first fetch, so the page has counters before the stream opens — and so a browser\n * with `EventSource` blocked still shows something useful.\n */\nexport async function loadInitialSnapshot(): Promise<void> {\n try {\n state.snapshot = await getJson<Snapshot>(\"/api/stats\");\n app.drawNow();\n } catch {\n /* the stream is the primary path */\n }\n}\n","import { $, clear, el, rootNode } from \"./dom.js\";\nimport { BOOT, SECTIONS } from \"./boot.js\";\nimport { aggregate, oldestAt, state } from \"./store.js\";\nimport { drawBars, pairs } from \"./bars.js\";\nimport { clockTime, ms, n, pct, rangeLabel, uptime, windowLabel } from \"./format.js\";\nimport { provenBots } from \"./outcome.js\";\nimport type { Aggregates } from \"./store.js\";\n\n/**\n * The counter tiles.\n *\n * Redrawn only when the snapshot behind them changes, which is every two seconds\n * rather than every frame. They sit above the tab strip, so they are the one part of\n * the page that is on screen whichever view is showing, and they were being rebuilt on\n * every incoming request for numbers that could not have moved.\n */\nlet paintedSnapshot: unknown;\n\nexport function drawTiles(force = false): void {\n // The tiles are the statistics section's, and `applySections` removes the node when\n // that section is off — so this has to check before it looks.\n if (!SECTIONS.statistics) return;\n const box = $(\"tiles\");\n const snapshot = state.snapshot;\n if (snapshot === undefined) return;\n if (!force && paintedSnapshot === snapshot) return;\n paintedSnapshot = snapshot;\n\n const metrics = snapshot.metrics;\n clear(box);\n if (metrics === undefined) {\n box.appendChild(el(\"div\", \"note\", \"Counters are switched off on this handler (metrics: false). The live feed still works.\"));\n return;\n }\n\n const actions = metrics.actions;\n const denied = actions.block + actions.drop + actions.redirect;\n const mitigated = actions.challenge + actions[\"rate-limit\"] + actions.delay;\n const served = actions.allow + actions.tag + actions.log;\n const total = metrics.requests;\n const unremarkable = metrics.verdicts.unknown + metrics.verdicts.human;\n // From the verdicts rather than from `metrics.proven`: that counter includes proven\n // *humans* — a clearance token is certain evidence too — so a dashboard watching a\n // logged-in audience reported all of them as proven bots. See `provenBots`.\n const provenBotCount = provenBots(metrics.verdicts);\n\n const tiles: Array<[string, string, string, string]> = [\n [\"\", n(total), \"Requests\", \"since start\"],\n [\"proven\", n(provenBotCount), \"Proven bots\", `${pct(provenBotCount, total)} of traffic`],\n [\"warn\", n(metrics.verdicts[\"suspected-bot\"]), \"Suspected\", \"never denied on this alone\"],\n [\"\", n(unremarkable), \"Unremarkable\", `${pct(unremarkable, total)} of traffic`],\n [\"warn\", n(metrics.downgrades), \"Guard stops\", metrics.downgrades > 0 ? \"rules asking for more than the evidence\" : \"no rule overreached\"],\n [\"crit\", n(denied), \"Denied\", `${pct(denied, total)} of traffic`],\n [\n \"\",\n n(mitigated),\n \"Mitigated\",\n metrics.challenges.issued > 0 ? `${n(metrics.challenges.solved)} of ${n(metrics.challenges.issued)} challenges solved` : \"challenged, limited or delayed\",\n ],\n [\"good\", n(served), \"Served\", `${pct(served, total)} of traffic`],\n [\"\", n(metrics.actorsTracked), \"Actors tracked\", \"in the registry now\"],\n ];\n for (const [kind, value, key, sub] of tiles) {\n const tile = el(\"div\", `tile ${kind}`);\n tile.appendChild(el(\"div\", \"v tnum\", value));\n tile.appendChild(el(\"div\", \"k\", key));\n tile.appendChild(el(\"div\", \"s\", sub));\n box.appendChild(tile);\n }\n}\n\n/**\n * The configuration facts at the end of the tab strip.\n *\n * Label first, value second, every time — \"suspect at 60\", not \"60 suspect at\" — and\n * as plain text, which is the one treatment nothing else on the page uses for a\n * control.\n */\nexport function drawChips(): void {\n const snapshot = state.snapshot;\n if (snapshot === undefined) return;\n const box = $(\"chips\");\n clear(box);\n const facts: Array<[string, string]> = [\n // Which process this is, first, because everything after it is a fact about this\n // process and nothing else. Behind a load balancer there are as many of these\n // dashboards as there are pods, each showing its own share of the traffic.\n [\"instance\", snapshot.instance],\n [\"guard\", snapshot.policy.falsePositivePolicy],\n [\"suspect at\", String(snapshot.policy.suspectThreshold)],\n ];\n if (SECTIONS.statistics) facts.push([\"detectors\", String(snapshot.detectors.length)]);\n if (SECTIONS.policy) facts.push([\"rules\", String(snapshot.rules.length)]);\n facts.push([\"uptime\", uptime(snapshot.now - snapshot.startedAt)]);\n\n for (const [label, value] of facts) {\n const fact = el(\"span\");\n fact.appendChild(document.createTextNode(`${label} `));\n fact.appendChild(el(\"b\", null, value));\n box.appendChild(fact);\n }\n}\n\n/**\n * Every panel that counts the retained ring says how much ring there is.\n *\n * Half the Statistics screen counts the window and half counts since the process\n * started; they were wearing the same grey subtitle, which invited exactly the\n * comparison that does not hold.\n */\nexport function updateWindowLabels(): void {\n const label = windowLabel(state.rows.length, oldestAt(), Date.now());\n for (const node of Array.from(rootNode().querySelectorAll<HTMLElement>(\".win\"))) node.textContent = label;\n}\n\nexport function drawLivePanels(): void {\n if (!SECTIONS.statistics) return;\n const totals = aggregate(state.rows);\n drawBars($(\"live-detectors\"), totals.detectors, \"Nothing has fired in this window.\");\n if (SECTIONS.actors) drawBars($(\"live-actors\"), totals.actors, \"No traffic in this window.\");\n}\n\nexport function drawStatsPanels(): void {\n const snapshot = state.snapshot;\n if (snapshot === undefined) return;\n const metrics = snapshot.metrics;\n\n if (metrics !== undefined) {\n drawBars($(\"stat-verdicts\"), pairs(metrics.verdicts), \"Nothing assessed yet.\");\n drawBars($(\"stat-actions\"), pairs(metrics.actions), \"No decisions yet.\");\n drawBars($(\"stat-classes\"), pairs(metrics.botClasses), \"Nothing classified yet.\");\n drawBars($(\"stat-detectors\"), pairs(metrics.detectorFirings), \"No detector has produced evidence yet.\");\n\n const challenges = $(\"stat-challenges\");\n clear(challenges);\n if (!snapshot.policy.challengeEnabled) {\n challenges.appendChild(el(\"div\", \"note\", \"No challenge is configured, so a rule asking for one degrades to a tag. Set challenge.secrets to enable it.\"));\n } else {\n const funnel: Array<[string, string]> = [\n [\"Issued\", n(metrics.challenges.issued)],\n [\"Solved\", n(metrics.challenges.solved)],\n [\"Rejected\", n(metrics.challenges.rejected)],\n [\"Solve rate\", metrics.challenges.issued > 0 ? pct(metrics.challenges.solved, metrics.challenges.issued) : \"—\"],\n ];\n for (const [key, value] of funnel) challenges.appendChild(statRow(key, value));\n }\n\n const health = $(\"stat-health\");\n clear(health);\n const bypassed = metrics.bypassed.allowlist + metrics.bypassed[\"ignored-path\"];\n const rows: Array<[string, string]> = [\n [\"Requests assessed\", n(metrics.requests)],\n [\"Bypassed — allowlist\", n(metrics.bypassed.allowlist)],\n [\"Bypassed — ignored path\", n(metrics.bypassed[\"ignored-path\"])],\n [\"Detection ran on\", pct(metrics.requests - bypassed, metrics.requests)],\n [\"Actors tracked\", n(metrics.actorsTracked)],\n [\"Guard stops\", n(metrics.downgrades)],\n ];\n const failures = pairs(metrics.detectorFailures);\n for (const [detector, count] of failures) rows.push([`Detector failures — ${detector}`, n(count)]);\n for (const [key, value] of rows) health.appendChild(statRow(key, value));\n if (failures.length === 0) health.appendChild(el(\"div\", \"note\", \"No detector has thrown or timed out.\"));\n }\n\n const totals = aggregate(state.rows);\n if (SECTIONS.actors) drawBars($(\"stat-identities\"), totals.identities, \"No client has named itself in this window.\");\n drawBars($(\"stat-paths\"), totals.paths, \"No traffic in this window.\");\n drawBars($(\"stat-denied-paths\"), totals.deniedPaths, \"Nothing has been denied in this window.\");\n drawBars($(\"stat-guard\"), totals.guardStops, \"No rule has asked for more than its evidence supports.\");\n drawBars($(\"stat-bypassed\"), totals.bypassed, \"Nothing bypassed detection.\");\n drawRuleHits(totals);\n\n const list = $(\"stat-detector-list\");\n clear(list);\n $(\"detector-count\").textContent = `${snapshot.detectors.length} installed`;\n for (const detector of snapshot.detectors) {\n const row = el(\"div\", \"det\");\n const left = el(\"div\");\n left.appendChild(el(\"div\", \"mono\", detector.id));\n left.appendChild(el(\"div\", \"d\", detector.description));\n row.appendChild(left);\n const fires = metrics?.detectorFirings[detector.id] ?? 0;\n const timing = metrics?.detectorTimings[detector.id];\n let right = `${n(fires)} · ${detector.cost} · ${detector.stage}`;\n // Only when the operator asked for timing; the field is empty otherwise.\n if (timing !== undefined && timing.count > 0) right += ` · ${ms(timing.totalMs / timing.count)} avg`;\n row.appendChild(el(\"div\", \"n\", right));\n list.appendChild(row);\n }\n}\n\nfunction statRow(key: string, value: string): HTMLElement {\n const line = el(\"div\", \"stat-row\");\n line.appendChild(el(\"span\", \"k\", key));\n line.appendChild(el(\"span\", \"v\", value));\n return line;\n}\n\n/**\n * Rule hit counts, including the rules that never fire.\n *\n * The zeros are the point. A rule that has matched nothing is either dead\n * configuration or a rule sitting behind a broader one that swallows its traffic, and\n * both are invisible in a chart that only draws what happened.\n */\nfunction drawRuleHits(totals: Aggregates): void {\n const target = $(\"stat-rule-hits\");\n clear(target);\n const rules = state.snapshot?.rules ?? [];\n if (rules.length === 0) {\n target.appendChild(el(\"div\", \"note\", \"No rules configured.\"));\n return;\n }\n\n let max = 1;\n for (const rule of rules) max = Math.max(max, totals.ruleHits.get(rule) ?? 0);\n for (const rule of rules) {\n const value = totals.ruleHits.get(rule) ?? 0;\n const bar = el(\"div\", `bar${value === 0 ? \" dead\" : \"\"}`);\n const track = el(\"div\", \"track\");\n if (value > 0) {\n const fill = el(\"div\", \"fill\");\n fill.style.width = `${Math.max(2, Math.round((value / max) * 100))}%`;\n track.appendChild(fill);\n }\n track.appendChild(el(\"div\", \"lbl\", rule));\n bar.appendChild(track);\n bar.appendChild(el(\"div\", \"v tnum\", value === 0 ? \"never\" : n(value)));\n target.appendChild(bar);\n }\n}\n\n/**\n * The audit panel: the window against its baseline.\n *\n * Both, side by side, because that is the only way the audit's output means anything.\n * A bot share of 60% is a number; a bot share of 60% against a baseline of 12% is an\n * incident.\n */\nexport function drawAudit(): void {\n const snapshot = state.snapshot;\n if (snapshot === undefined) return;\n const body = $(\"audit-body\");\n const checks = $(\"audit-checks\");\n clear(body);\n clear(checks);\n\n const audit = snapshot.audit;\n if (audit === undefined) {\n $(\"audit-spans\").textContent = \"\";\n body.appendChild(\n el(\n \"div\",\n \"note\",\n \"The traffic audit is switched off on this handler (audit: false). It watches the shape of your traffic rather than any one request — a spike in automation, a collapse in human traffic, a policy suddenly denying far more than usual.\",\n ),\n );\n return;\n }\n\n const current = audit.window;\n const baseline = audit.baseline;\n $(\"audit-spans\").textContent = `last ${rangeLabel(current.spanMs)} against the ${rangeLabel(baseline.spanMs)} before`;\n\n // A share is only legible next to the one it is being compared with, so every row\n // carries both. The delta is left to the reader: an arrow implying \"worse\" would be\n // the dashboard editorialising about traffic it cannot see the purpose of.\n const rows: Array<[string, string, string]> = [\n [\"Requests\", n(current.requests), n(baseline.requests)],\n [\"Rate\", `${current.rate.toFixed(1)}/min`, `${baseline.rate.toFixed(1)}/min`],\n [\"Bot share\", `${Math.round(current.botShare * 100)}%`, `${Math.round(baseline.botShare * 100)}%`],\n [\"Bots\", n(current.bots), n(baseline.bots)],\n [\"Humans\", n(current.humans), n(baseline.humans)],\n [\"Denials\", n(current.denials), n(baseline.denials)],\n [\"Challenges\", n(current.challenges), n(baseline.challenges)],\n [\"Guard stops\", n(current.downgrades), n(baseline.downgrades)],\n [\"Detector failures\", n(current.failures), n(baseline.failures)],\n [\"Bypassed\", n(current.bypassed), n(baseline.bypassed)],\n ];\n for (const [key, now, was] of rows) {\n const line = el(\"div\", \"stat-row\");\n line.appendChild(el(\"span\", \"k\", key));\n const values = el(\"span\", \"v\");\n values.appendChild(el(\"b\", null, now));\n values.appendChild(el(\"span\", \"was\", ` was ${was}`));\n line.appendChild(values);\n body.appendChild(line);\n }\n\n if (audit.checks.length === 0) {\n checks.appendChild(el(\"div\", \"note\", \"No checks are installed, so nothing here will ever raise an anomaly.\"));\n return;\n }\n for (const check of audit.checks) {\n const row = el(\"div\", \"det\");\n const left = el(\"div\");\n left.appendChild(el(\"div\", \"mono\", check.id));\n left.appendChild(el(\"div\", \"d\", check.description));\n row.appendChild(left);\n checks.appendChild(row);\n }\n}\n\n/** The count on the Policy tab. Cheap, so it runs on every frame; the panel itself does not. */\nexport function drawNoticeBadge(): void {\n const notices = state.snapshot?.notices ?? [];\n const badge = $(\"notice-badge\");\n badge.hidden = notices.length === 0 || !SECTIONS.notices;\n badge.textContent = String(notices.length);\n}\n\nexport function drawNotices(): void {\n const box = $(\"stat-notices\");\n const notices = state.snapshot?.notices ?? [];\n clear(box);\n $(\"notice-count\").textContent = notices.length > 0 ? `${notices.length} total` : \"\";\n if (notices.length === 0) {\n box.appendChild(el(\"div\", \"note\", \"Nothing to report: no startup warnings, no detector errors.\"));\n return;\n }\n for (const notice of notices.slice().reverse().slice(0, 40)) {\n const row = el(\"div\", `notice ${notice.kind}`);\n const when = el(\"div\", \"when\");\n when.appendChild(el(\"div\", \"tag\", notice.kind));\n when.appendChild(el(\"div\", null, clockTime(notice.at)));\n row.appendChild(when);\n const body = el(\"div\");\n body.appendChild(el(\"div\", null, notice.message));\n if (notice.source !== undefined) body.appendChild(el(\"div\", \"ev-meta\", notice.source));\n row.appendChild(body);\n box.appendChild(row);\n }\n}\n\n/**\n * What somebody did to this process, and when.\n *\n * The same list the traffic timeline marks, written out — because a marker answers\n * \"was there a change here?\" and a person auditing wants \"what were they, in order\".\n * Bounded and in memory, which the panel says: the durable copy is the change events.\n */\nexport function drawChanges(): void {\n if (!SECTIONS.changes) return;\n const box = $(\"stat-changes\");\n const changes = state.snapshot?.changes ?? [];\n clear(box);\n $(\"change-count\").textContent = changes.length > 0 ? `${changes.length} this run` : \"\";\n\n if (changes.length === 0) {\n box.appendChild(el(\"div\", \"note\", \"Nothing has been changed at runtime. Rules, guard and ranges are as the code that built this handler left them.\"));\n return;\n }\n\n for (const change of changes.slice().reverse()) {\n const row = el(\"div\", \"notice\");\n const when = el(\"div\", \"when\");\n when.appendChild(el(\"div\", \"tag\", change.kind));\n when.appendChild(el(\"div\", null, clockTime(change.at)));\n row.appendChild(when);\n const body = el(\"div\");\n body.appendChild(el(\"div\", null, change.summary));\n // \"by nobody in particular\" is a fact about the dashboard's `auth`, not a gap to\n // hide: a bearer token is a credential and not an identity.\n body.appendChild(el(\"div\", \"ev-meta\", change.by === undefined || change.by === \"\" ? \"by an unnamed viewer — this listener's auth carries no identity\" : `by ${change.by}`));\n row.appendChild(body);\n box.appendChild(row);\n }\n}\n\n/** Sibling instances, when the operator listed any. */\nexport function drawPeers(): void {\n const box = $(\"peers\");\n if (BOOT.peers.length === 0) {\n box.hidden = true;\n return;\n }\n if (box.childElementCount > 0) return;\n box.appendChild(el(\"span\", \"hint\", \"also:\"));\n for (const peer of BOOT.peers) {\n const link = el(\"a\", \"linkbtn\", peer.label);\n link.href = peer.href;\n link.rel = \"noreferrer noopener\";\n box.appendChild(link);\n }\n}\n","import { $, clear, css, el, svgEl, svgText } from \"./dom.js\";\nimport { n, pct, ms, rangeLabel, windowLabel } from \"./format.js\";\nimport { oldestAt, state } from \"./store.js\";\nimport { outcome } from \"./outcome.js\";\n\nconst BUCKETS = 60;\nconst LATENCY_BOUNDS = [0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100];\nconst SCORE_BOUNDS = 10;\n\ninterface Bucket {\n at: number;\n served: number;\n mitigated: number;\n denied: number;\n total: number;\n}\n\n/** Puts a tooltip beside the pointer without letting it hang off the panel. */\nfunction positionTip(tip: HTMLElement, host: HTMLElement, clientX: number, boxLeft: number): void {\n tip.style.opacity = \"1\";\n tip.style.left = `${Math.min(host.clientWidth - 150, Math.max(4, clientX - boxLeft - 60))}px`;\n tip.style.top = \"14px\";\n}\n\nfunction tipRow(label: string, value: string, colour?: string): HTMLElement {\n const line = el(\"div\", \"r\");\n const left = el(\"em\");\n if (colour !== undefined) {\n const swatch = el(\"span\", \"swatch\");\n swatch.style.background = colour;\n left.appendChild(swatch);\n }\n left.appendChild(document.createTextNode(label));\n line.appendChild(left);\n line.appendChild(el(\"b\", null, value));\n return line;\n}\n\nfunction timeline(): Bucket[] {\n const bucketMs = state.rangeMs / BUCKETS;\n const now = Date.now();\n const start = now - state.rangeMs;\n const buckets: Bucket[] = [];\n for (let i = 0; i < BUCKETS; i++) buckets.push({ at: start + i * bucketMs, served: 0, mitigated: 0, denied: 0, total: 0 });\n for (const { entry } of state.rows) {\n const index = Math.floor((entry.at - start) / bucketMs);\n if (index < 0 || index >= BUCKETS) continue;\n const bucket = buckets[index];\n if (bucket === undefined) continue;\n bucket.total++;\n const out = outcome(entry);\n if (out === \"deny\") bucket.denied++;\n else if (out === \"mitigate\") bucket.mitigated++;\n else bucket.served++;\n }\n return buckets;\n}\n\n/**\n * Stacked bars, two series, plus a marker row for denials.\n *\n * Two series rather than three because served/mitigated/denied as three fills would\n * need a third hue that survives colour-vision simulation beside these two, and none\n * does; a denial is a status rather than a series, so it gets a status marker and a\n * label instead.\n */\nexport function drawTraffic(): void {\n const host = $(\"traffic-chart\");\n const svg = $(\"traffic\");\n const width = Math.max(320, host.clientWidth - 30);\n const height = 190;\n const padBottom = 26;\n const markerRow = 8;\n const plot = height - padBottom - markerRow;\n const buckets = timeline();\n const now = Date.now();\n const start = now - state.rangeMs;\n let peak = 1;\n for (const bucket of buckets) if (bucket.total > peak) peak = bucket.total;\n // Round the ceiling up to an even number so the mid gridline lands on a whole\n // request rather than on 1.5 rendered as \"2\".\n const max = Math.max(2, Math.ceil(peak / 2) * 2);\n\n clear(svg);\n svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n svg.setAttribute(\"height\", String(height));\n\n const s1 = css(\"--s1\");\n const s2 = css(\"--s2\");\n const crit = css(\"--crit\");\n const grid = css(\"--grid\");\n const muted = css(\"--muted\");\n const step = width / BUCKETS;\n const barWidth = Math.max(2, step - 2);\n\n // Three gridlines: enough to read a magnitude, few enough to stay recessive.\n for (const fraction of [0, 0.5, 1]) {\n const y = markerRow + plot - fraction * plot;\n svg.appendChild(svgEl(\"line\", { class: \"gridline\", x1: 0, x2: width, y1: y, y2: y, stroke: grid, \"stroke-width\": 1 }));\n if (fraction > 0) svg.appendChild(svgText({ x: 2, y: y - 3, fill: muted, \"font-size\": 10 }, Math.round(max * fraction)));\n }\n\n buckets.forEach((bucket, index) => {\n const x = index * step + 1;\n const servedHeight = (bucket.served / max) * plot;\n const mitigatedHeight = (bucket.mitigated / max) * plot;\n let y = markerRow + plot;\n\n if (servedHeight > 0) {\n y -= servedHeight;\n svg.appendChild(svgEl(\"rect\", { x, y, width: barWidth, height: servedHeight, fill: s1, rx: 2 }));\n }\n if (mitigatedHeight > 0) {\n // A 2px gap between stacked segments, so the boundary is a surface line rather\n // than two colours meeting.\n y -= mitigatedHeight + (servedHeight > 0 ? 2 : 0);\n svg.appendChild(svgEl(\"rect\", { x, y: Math.max(markerRow, y), width: barWidth, height: mitigatedHeight, fill: s2, rx: 2 }));\n }\n if (bucket.denied > 0) svg.appendChild(svgEl(\"rect\", { x, y: 0, width: barWidth, height: 5, fill: crit, rx: 2 }));\n });\n\n svg.appendChild(svgEl(\"line\", { x1: 0, x2: width, y1: markerRow + plot, y2: markerRow + plot, stroke: css(\"--line\"), \"stroke-width\": 1 }));\n const span = rangeLabel(state.rangeMs);\n const labels: Array<[number, string]> = [\n [0, `${span} ago`],\n [BUCKETS / 2, rangeLabel(state.rangeMs / 2)],\n [BUCKETS - 1, \"now\"],\n ];\n for (const [position, text] of labels) {\n svg.appendChild(svgText({ x: Math.min(width - 26, Math.max(0, position * step)), y: height - 8, fill: muted, \"font-size\": 10 }, text));\n }\n\n // Runtime changes, on the same axis as the traffic they changed.\n //\n // A preview says \"44 of 151 requests would be treated differently\", which is a\n // prediction. Nothing on the page used to tell you whether it held: the moment of the\n // change left no trace, so the traffic before and after it were the same undifferentiated\n // line. A marker per change turns the chart into the answer.\n const changes = (state.snapshot?.changes ?? []).filter((change) => change.at >= start && change.at <= now);\n const markColour = css(\"--proven-text\");\n for (const change of changes) {\n const x = ((change.at - start) / state.rangeMs) * width;\n svg.appendChild(svgEl(\"line\", { x1: x, x2: x, y1: 0, y2: markerRow + plot, stroke: markColour, \"stroke-width\": 1.5, \"stroke-dasharray\": \"3 2\", opacity: 0.85 }));\n const dot = svgEl(\"circle\", { cx: x, cy: 3, r: 3, fill: markColour });\n const title = svgEl(\"title\");\n title.textContent = `${new Date(change.at).toLocaleTimeString()} · ${change.kind}: ${change.summary}${change.by === undefined ? \"\" : ` (by ${change.by})`}`;\n dot.appendChild(title);\n svg.appendChild(dot);\n }\n\n const hover = svgEl(\"rect\", { x: 0, y: 0, width: 0, height: markerRow + plot, fill: css(\"--ink\"), opacity: 0.06 });\n svg.appendChild(hover);\n\n const tip = $(\"traffic-tip\");\n host.onmousemove = (event: MouseEvent): void => {\n const box = svg.getBoundingClientRect();\n const index = Math.floor(((event.clientX - box.left) / box.width) * BUCKETS);\n const bucket = buckets[index];\n if (bucket === undefined) {\n tip.style.opacity = \"0\";\n hover.setAttribute(\"width\", \"0\");\n return;\n }\n hover.setAttribute(\"x\", String(index * step));\n hover.setAttribute(\"width\", String(step));\n clear(tip);\n tip.appendChild(el(\"div\", \"t\", `${new Date(bucket.at).toLocaleTimeString()} · ${Math.round(state.rangeMs / BUCKETS / 1000)}s`));\n tip.appendChild(tipRow(\"Served\", n(bucket.served), s1));\n tip.appendChild(tipRow(\"Mitigated\", n(bucket.mitigated), s2));\n tip.appendChild(tipRow(\"Denied\", n(bucket.denied), crit));\n positionTip(tip, host, event.clientX, box.left);\n };\n host.onmouseleave = (): void => {\n tip.style.opacity = \"0\";\n hover.setAttribute(\"width\", \"0\");\n };\n\n const total = buckets.reduce((sum, bucket) => sum + bucket.total, 0);\n $(\"traffic-window\").textContent = `last ${rangeLabel(state.rangeMs)}`;\n\n // What the chart says, in words.\n //\n // `role=\"img\"` with a label told a screen reader that a picture is here and its name.\n // It did not tell anybody what is *in* it, so every number on this screen was\n // unreachable to a reader who cannot see the bars. The chart is the presentation; this\n // is the data, and it is the same data.\n const served = buckets.reduce((sum, bucket) => sum + bucket.served, 0);\n const mitigated = buckets.reduce((sum, bucket) => sum + bucket.mitigated, 0);\n const denied = buckets.reduce((sum, bucket) => sum + bucket.denied, 0);\n const busiest = buckets.reduce((best, bucket) => (bucket.total > best.total ? bucket : best), buckets[0] ?? { at: now, total: 0, served: 0, mitigated: 0, denied: 0 });\n $(\"traffic-alt\").textContent =\n `Traffic over the last ${rangeLabel(state.rangeMs)}: ${n(total)} requests — ${n(served)} served, ${n(mitigated)} mitigated, ${n(denied)} denied. ` +\n (total === 0\n ? \"No traffic in this range.\"\n : `Busiest ${Math.round(state.rangeMs / BUCKETS / 1000)}-second interval: ${n(busiest.total)} requests at ${new Date(busiest.at).toLocaleTimeString()}.`) +\n (changes.length === 0 ? \"\" : ` ${n(changes.length)} runtime change${changes.length === 1 ? \"\" : \"s\"} in this range: ${changes.map((change) => `${change.kind}, ${change.summary}`).join(\"; \")}.`);\n\n const legend = $(\"traffic-legend\");\n clear(legend);\n legend.appendChild(el(\"span\", null, `${n(total)} requests in the last ${rangeLabel(state.rangeMs)} ·`));\n // The ring is finite. Saying so beats drawing an empty half-hour that only means the\n // feed forgot it.\n const oldest = oldestAt();\n if (oldest !== undefined && Date.now() - oldest < state.rangeMs * 0.9) {\n legend.appendChild(el(\"span\", null, `window holds ${rangeLabel(Date.now() - oldest)} ·`));\n }\n for (const [label, colour] of [\n [\"Served\", s1],\n [\"Mitigated — challenged, limited or delayed\", s2],\n [\"Denied\", crit],\n ] as Array<[string, string]>) {\n const item = el(\"span\");\n const swatch = el(\"span\", \"swatch\");\n swatch.style.background = colour;\n item.appendChild(swatch);\n item.appendChild(document.createTextNode(label));\n legend.appendChild(item);\n }\n if (changes.length > 0) {\n const item = el(\"span\");\n item.appendChild(el(\"span\", \"mark\"));\n item.appendChild(document.createTextNode(`${n(changes.length)} runtime change${changes.length === 1 ? \"\" : \"s\"} — hover a marker`));\n legend.appendChild(item);\n }\n}\n\n/** Cumulative bucket counts to per-bucket ones. Prometheus wants the first; a histogram needs the second. */\nfunction differences(cumulative: readonly number[]): number[] {\n const counts: number[] = [];\n for (let i = 0; i < cumulative.length; i++) counts.push((cumulative[i] ?? 0) - (i > 0 ? (cumulative[i - 1] ?? 0) : 0));\n return counts;\n}\n\n/**\n * The score distribution: how close ordinary traffic runs to the line.\n *\n * Two populations, and the panel now says which one it is drawing rather than leaving\n * the reader to assume. **Since start** comes from the same counters as the Prometheus\n * endpoint, so it covers the whole run and agrees with whatever your alerting says.\n * **This window** counts the few hundred requests the page is still holding, which is\n * the right answer to \"what is happening right now\" and the wrong answer to \"where\n * should the threshold be\" — and it was the only answer available before, sitting\n * beside panels counting since start under an identical grey subtitle.\n *\n * Proven requests are excluded from both rather than piled into the last bucket: their\n * score is 100 by definition and plays no part in any decision.\n */\nexport function drawScores(): void {\n const host = $(\"score-chart\");\n const svg = $(\"scores\");\n clear(svg);\n\n const metrics = state.snapshot?.metrics;\n const fromRun = state.scoreScope === \"run\" && metrics !== undefined;\n\n let buckets: number[];\n let scored: number;\n let proven: number;\n if (fromRun && metrics !== undefined) {\n buckets = differences(metrics.scores.buckets);\n scored = metrics.scores.count;\n proven = metrics.proven;\n } else {\n buckets = new Array<number>(SCORE_BOUNDS).fill(0);\n scored = 0;\n proven = 0;\n for (const { entry } of state.rows) {\n if (entry.bypass !== undefined) continue;\n if (entry.certain) {\n proven++;\n continue;\n }\n const index = Math.min(SCORE_BOUNDS - 1, Math.floor(entry.score / 10));\n buckets[index] = (buckets[index] ?? 0) + 1;\n scored++;\n }\n }\n\n const width = Math.max(280, host.clientWidth - 30);\n const height = 190;\n const padBottom = 30;\n const plot = height - padBottom;\n let max = 1;\n for (const value of buckets) if (value > max) max = value;\n\n svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n svg.setAttribute(\"height\", String(height));\n\n const fill = css(\"--s1\");\n const muted = css(\"--muted\");\n const grid = css(\"--grid\");\n const crit = css(\"--crit\");\n svg.appendChild(svgEl(\"line\", { class: \"gridline\", x1: 0, x2: width, y1: plot, y2: plot, stroke: grid, \"stroke-width\": 1 }));\n\n const step = width / SCORE_BOUNDS;\n buckets.forEach((value, index) => {\n const barHeight = (value / max) * (plot - 8);\n // Capped and centred: ten buckets across a wide panel would otherwise be ten slabs,\n // and a histogram reads better as marks than as a wall.\n const barWidth = Math.max(2, Math.min(step - 6, 56));\n if (barHeight > 0) {\n svg.appendChild(svgEl(\"rect\", { x: index * step + (step - barWidth) / 2, y: plot - barHeight, width: barWidth, height: barHeight, fill, rx: 3 }));\n }\n svg.appendChild(svgText({ x: index * step + 1, y: height - 14, fill: muted, \"font-size\": 9.5 }, index * 10));\n });\n\n const threshold = state.snapshot?.policy.suspectThreshold ?? 60;\n const x = (threshold / 100) * width;\n svg.appendChild(svgEl(\"line\", { x1: x, x2: x, y1: 0, y2: plot, stroke: crit, \"stroke-width\": 2, \"stroke-dasharray\": \"4 3\" }));\n svg.appendChild(svgText({ x: Math.min(width - 92, x + 5), y: 11, fill: crit, \"font-size\": 10 }, `suspect at ${threshold}`));\n svg.appendChild(svgText({ x: 0, y: height - 2, fill: muted, \"font-size\": 10 }, \"score (probabilistic requests only)\"));\n\n const tip = $(\"score-tip\");\n host.onmousemove = (event: MouseEvent): void => {\n const box = svg.getBoundingClientRect();\n const index = Math.floor(((event.clientX - box.left) / box.width) * SCORE_BOUNDS);\n const value = buckets[index];\n if (value === undefined) {\n tip.style.opacity = \"0\";\n return;\n }\n clear(tip);\n tip.appendChild(el(\"div\", \"t\", `score ${index * 10}–${index * 10 + 9}`));\n tip.appendChild(tipRow(\"requests\", n(value)));\n tip.appendChild(tipRow(\"share\", pct(value, scored)));\n positionTip(tip, host, event.clientX, box.left);\n };\n host.onmouseleave = (): void => {\n tip.style.opacity = \"0\";\n };\n\n let over = 0;\n for (let bucket = 0; bucket < SCORE_BOUNDS; bucket++) if (bucket * 10 >= threshold) over += buckets[bucket] ?? 0;\n\n const legend = $(\"score-legend\");\n clear(legend);\n legend.appendChild(el(\"span\", null, `${n(scored)} scored · ${n(over)} at or over the threshold · ${n(proven)} proven, which carry no score`));\n\n $(\"score-alt\").textContent =\n `Distribution of probabilistic scores ${fromRun ? \"since start\" : \"in the retained window\"}, with the suspect threshold at ${threshold}. ` +\n `${n(scored)} scored requests, ${n(over)} at or over the threshold, ${n(proven)} proven and therefore unscored. ` +\n (scored === 0 ? \"Nothing scored yet.\" : `By ten-point band: ${buckets.map((value, index) => `${index * 10}–${index * 10 + 9}: ${n(value)}`).join(\", \")}.`);\n $(\"score-window\").textContent = fromRun ? \"since start\" : windowLabel(state.rows.length, oldestAt(), Date.now());\n if (state.scoreScope === \"run\" && metrics === undefined) {\n legend.appendChild(el(\"span\", null, \"· counters are off on this handler, so this is the retained window\"));\n }\n}\n\nexport function drawLatency(): void {\n const host = $(\"latency-chart\");\n const svg = $(\"latency\");\n clear(svg);\n const metrics = state.snapshot?.metrics;\n if (metrics === undefined || metrics.duration.count === 0) {\n $(\"latency-summary\").textContent = \"No assessments yet.\";\n $(\"latency-alt\").textContent = \"Assessment latency: no assessments yet.\";\n svg.setAttribute(\"viewBox\", \"0 0 100 40\");\n svg.setAttribute(\"height\", \"40\");\n svg.appendChild(svgText({ x: 0, y: 20, fill: css(\"--muted\"), \"font-size\": 11 }, \"No assessments yet.\"));\n return;\n }\n\n const cumulative = metrics.duration.buckets;\n const counts = differences(cumulative);\n\n const width = Math.max(280, host.clientWidth - 30);\n const height = 190;\n const padBottom = 30;\n const plot = height - padBottom;\n let max = 1;\n for (const value of counts) if (value > max) max = value;\n\n svg.setAttribute(\"viewBox\", `0 0 ${width} ${height}`);\n svg.setAttribute(\"height\", String(height));\n\n const fill = css(\"--s1\");\n const muted = css(\"--muted\");\n const grid = css(\"--grid\");\n svg.appendChild(svgEl(\"line\", { class: \"gridline\", x1: 0, x2: width, y1: plot, y2: plot, stroke: grid, \"stroke-width\": 1 }));\n\n const step = width / counts.length;\n counts.forEach((value, index) => {\n const barHeight = (value / max) * (plot - 6);\n if (barHeight > 0) {\n svg.appendChild(svgEl(\"rect\", { x: index * step + 1, y: plot - barHeight, width: Math.max(2, step - 3), height: barHeight, fill, rx: 3 }));\n }\n if (index % 2 === 0) {\n svg.appendChild(svgText({ x: index * step + 1, y: height - 14, fill: muted, \"font-size\": 9.5 }, index < LATENCY_BOUNDS.length ? String(LATENCY_BOUNDS[index]) : \"more\"));\n }\n });\n svg.appendChild(svgText({ x: 0, y: height - 2, fill: muted, \"font-size\": 10 }, \"milliseconds (upper bound of each bucket)\"));\n\n const tip = $(\"latency-tip\");\n host.onmousemove = (event: MouseEvent): void => {\n const box = svg.getBoundingClientRect();\n const index = Math.floor(((event.clientX - box.left) / box.width) * counts.length);\n const value = counts[index];\n if (value === undefined) {\n tip.style.opacity = \"0\";\n return;\n }\n clear(tip);\n tip.appendChild(el(\"div\", \"t\", index < LATENCY_BOUNDS.length ? `≤ ${LATENCY_BOUNDS[index]}ms` : \"over 100ms\"));\n tip.appendChild(tipRow(\"requests\", n(value)));\n tip.appendChild(tipRow(\"share\", pct(value, metrics.duration.count)));\n positionTip(tip, host, event.clientX, box.left);\n };\n host.onmouseleave = (): void => {\n tip.style.opacity = \"0\";\n };\n\n const mean = metrics.duration.totalMs / metrics.duration.count;\n const p95 = percentile(cumulative, metrics.duration.count, 0.95);\n $(\"latency-summary\").textContent = `Time spent in detection, per request · mean ${ms(mean)} · p95 ${ms(p95)} · max ${ms(metrics.duration.maxMs)}`;\n $(\"latency-alt\").textContent =\n `Assessment latency since start over ${n(metrics.duration.count)} requests: mean ${ms(mean)}, 95th percentile ${ms(p95)}, maximum ${ms(metrics.duration.maxMs)}. ` +\n `By bucket: ${counts.map((value, index) => `${index < LATENCY_BOUNDS.length ? `up to ${LATENCY_BOUNDS[index]}ms` : \"over 100ms\"}: ${n(value)}`).join(\", \")}.`;\n}\n\n/** Bucket-boundary percentile. Coarse by construction — it is a histogram, not a series. */\nfunction percentile(cumulative: readonly number[], count: number, fraction: number): number {\n const target = count * fraction;\n const last = LATENCY_BOUNDS[LATENCY_BOUNDS.length - 1] ?? 100;\n for (let i = 0; i < cumulative.length; i++) {\n if ((cumulative[i] ?? 0) >= target) return i < LATENCY_BOUNDS.length ? (LATENCY_BOUNDS[i] ?? last) : last;\n }\n return last;\n}\n","import { $, byId, clear, el } from \"./dom.js\";\nimport { SECTIONS } from \"./boot.js\";\nimport { actorActions, isConfirming } from \"./actions.js\";\nimport { app } from \"./app.js\";\nimport { clockTime, n } from \"./format.js\";\nimport { getJson } from \"./api.js\";\nimport { setSearch, state } from \"./store.js\";\nimport type { ActorRow } from \"./types.js\";\n\n/**\n * The Actors screen.\n *\n * The feed is a ring of *requests* — five hundred of them, which on a busy origin is a\n * few seconds. The registry is a bounded map of *clients*, up to twenty thousand of\n * them, each carrying the rate series, path breadth and confirmation count that\n * `cadence`, `crawl-breadth` and `rate-anomaly` are reading. Until this screen existed\n * the page could only show you the actors that happened to appear in the last few\n * hundred requests, which is a different and much smaller question than \"who is hitting\n * me hardest right now\".\n *\n * It is fetched rather than streamed, and refreshed while it is the visible tab.\n * Streaming twenty thousand actors to every viewer to keep a sorted list of fifty\n * fresh would be a great deal of traffic to answer a question nobody asks continuously.\n */\n\ninterface ActorsBody {\n actors: ActorRow[];\n tracked: number;\n actionable: boolean;\n}\n\nlet timer: ReturnType<typeof setInterval> | undefined;\n\nexport async function loadActors(): Promise<void> {\n if (!SECTIONS.registry) return;\n try {\n const body = await getJson<ActorsBody>(\"/api/actors?limit=100\");\n state.actors = body.actors;\n state.actorsTracked = body.tracked;\n drawActors();\n } catch {\n /* the rest of the page is unaffected */\n }\n}\n\n/** Polls while the screen is showing, and stops the moment it is not. */\nexport function trackActors(): void {\n if (timer !== undefined) clearInterval(timer);\n timer = undefined;\n if (state.tab !== \"actors\") return;\n void loadActors();\n timer = setInterval(() => {\n if (state.tab !== \"actors\" || state.paused || isConfirming()) return;\n void loadActors();\n }, 4000);\n}\n\nexport function drawActors(): void {\n if (!SECTIONS.registry) return;\n // A repaint replaces every button in the table, including a confirmation somebody is\n // halfway through. The list holds still while they decide.\n if (isConfirming()) return;\n const body = byId<HTMLTableSectionElement>(\"actor-rows\");\n clear(body);\n\n const actors = state.actors;\n $(\"actors-count\").textContent = `${n(actors.length)} shown · ${n(state.actorsTracked)} tracked`;\n byId<HTMLElement>(\"actors-empty\").hidden = actors.length > 0;\n\n for (const actor of actors) {\n const row = el(\"tr\");\n row.appendChild(el(\"td\", \"who\", actor.key));\n row.appendChild(el(\"td\", \"num tnum\", n(actor.requests)));\n row.appendChild(el(\"td\", \"num tnum\", n(actor.recentRate)));\n row.appendChild(el(\"td\", \"num tnum\", n(actor.distinctPaths)));\n\n // The coefficient of variation of the gaps between requests. Near zero is a\n // metronome, which no person is; a dash is \"too few gaps to say\", which is not the\n // same as regular and must not look like it.\n const cadence = el(\"td\", \"num tnum\", actor.cadenceCv === undefined ? \"—\" : actor.cadenceCv.toFixed(2));\n if (actor.cadenceCv !== undefined && actor.cadenceCv < 0.15) cadence.className += \" warn-text\";\n row.appendChild(cadence);\n\n row.appendChild(el(\"td\", \"num tnum\", n(actor.priorConfirmations)));\n\n // Outstanding challenges. Amber past two, because one abandoned challenge is a\n // person having a moment and three is a client that does not answer.\n const unsolved = el(\"td\", \"num tnum\", n(actor.unsolvedChallenges));\n if (actor.unsolvedChallenges >= 3) unsolved.className += \" warn-text\";\n row.appendChild(unsolved);\n\n const stateCell = el(\"td\");\n const tags: string[] = [];\n if (actor.cleared) tags.push(\"cleared as human\");\n if (actor.distinctUserAgents > 1) tags.push(`${actor.distinctUserAgents} User-Agents`);\n tags.push(`first seen ${clockTime(actor.firstSeen)}`);\n stateCell.appendChild(el(\"div\", \"tagline\", tags.join(\" · \")));\n row.appendChild(stateCell);\n\n const actions = el(\"td\", \"acts\");\n const inFeed = el(\"button\", null, \"In feed\");\n inFeed.title = \"Show this actor's requests in the live feed\";\n inFeed.addEventListener(\"click\", () => {\n // Reuses the feed's own search rather than a second mechanism, which means the\n // result is a shareable URL like every other view of the feed.\n setSearch(`actor:${actor.key}`);\n const search = byId<HTMLInputElement>(\"search\");\n search.value = state.search;\n app.showTab(\"live\");\n app.syncUrl();\n });\n actions.appendChild(inFeed);\n for (const button of actorActions(actor.key, () => void loadActors())) actions.appendChild(button);\n row.appendChild(actions);\n\n body.appendChild(row);\n }\n}\n","import { $, byId, clear, el } from \"./dom.js\";\nimport { SECTIONS } from \"./boot.js\";\nimport { ms } from \"./format.js\";\nimport { outcome, verdictBadge } from \"./outcome.js\";\nimport { postJson } from \"./api.js\";\nimport { toast } from \"./app.js\";\nimport type { DashboardEntry } from \"./types.js\";\n\n/**\n * The request tester.\n *\n * The page's answer to the question that arrives by ticket rather than by traffic: *why\n * is this client being challenged?* Until now the only way to find out was to wait for\n * them to come back and hope you were watching, or to reproduce their request by hand\n * against the live site — which puts it in the feed, in the counters and in that\n * actor's history, so investigating a complaint changed the thing being investigated.\n *\n * This runs a **dry run** on the server: every detector runs and the decision is real,\n * including the guard, but nothing is recorded anywhere. The one thing it cannot know\n * is history — it gets an actor with no past — so what it answers precisely is \"what\n * would this look like as a first request\", which is what a ticket is asking.\n */\ninterface TestResult {\n entry: DashboardEntry;\n reason: string;\n assumed: string[];\n error?: string;\n}\n\nexport function initTester(): void {\n if (!SECTIONS.tester) return;\n $(\"test-run\").addEventListener(\"click\", () => void run());\n byId<HTMLTextAreaElement>(\"test-input\").addEventListener(\"keydown\", (event) => {\n // Enter is a newline in a textarea, so the shortcut is the one every console uses\n // for \"send this\".\n if (event.key === \"Enter\" && (event.metaKey || event.ctrlKey)) {\n event.preventDefault();\n void run();\n }\n });\n}\n\nasync function run(): Promise<void> {\n const raw = byId<HTMLTextAreaElement>(\"test-input\").value;\n if (raw.trim() === \"\") {\n toast(\"warn\", \"Nothing to assess\", \"Paste a User-Agent, a curl command, or a header block.\");\n return;\n }\n const result = await postJson<TestResult>(\"/api/test\", {\n raw,\n ip: byId<HTMLInputElement>(\"test-ip\").value.trim(),\n url: byId<HTMLInputElement>(\"test-url\").value.trim(),\n });\n\n const box = $(\"test-result\");\n box.hidden = false;\n clear(box);\n\n if (!result.ok) {\n box.className = \"result bad\";\n box.appendChild(el(\"div\", null, result.error ?? \"The server could not read that.\"));\n return;\n }\n\n const { entry, reason, assumed } = result.data;\n const out = outcome(entry);\n box.className = `result ${out === \"deny\" ? \"bad\" : out === \"mitigate\" ? \"warn\" : \"ok\"}`;\n\n const head = el(\"div\");\n const [badgeClass, badgeLabel] = verdictBadge(entry);\n head.appendChild(el(\"span\", `badge ${badgeClass}`, badgeLabel));\n head.appendChild(document.createTextNode(\" \"));\n head.appendChild(el(\"b\", null, entry.action ?? \"no decision\"));\n if (entry.rule !== undefined) head.appendChild(el(\"span\", \"ev-meta\", ` via ${entry.rule}`));\n box.appendChild(head);\n\n box.appendChild(el(\"div\", \"ev-meta\", `${entry.certain ? \"proven\" : `score ${entry.score}`} · assessed in ${ms(entry.durationMs)}`));\n if (entry.downgradedFrom !== undefined) {\n box.appendChild(el(\"div\", \"guard\", `The guard stopped ${entry.downgradedFrom} here.`));\n if (entry.downgradeReason !== undefined) box.appendChild(el(\"div\", \"basis\", entry.downgradeReason));\n }\n\n if (entry.evidence.length === 0) {\n box.appendChild(el(\"div\", \"ev-meta\", entry.bypass !== undefined ? `Detection was skipped: ${entry.bypass}.` : \"No detector produced any evidence.\"));\n } else {\n const list = el(\"div\", \"ev\");\n for (const item of entry.evidence) {\n const row = el(\"div\", `ev-item${item.direction === \"human\" ? \" human\" : \"\"}`);\n row.appendChild(el(\"div\", `tier t-${item.certainty}`, item.certainty));\n const detail = el(\"div\");\n detail.appendChild(el(\"div\", null, item.summary));\n detail.appendChild(el(\"div\", \"ev-meta\", `${item.detector} · points to ${item.direction}`));\n if (item.deterministicBasis !== undefined) detail.appendChild(el(\"div\", \"basis\", item.deterministicBasis));\n row.appendChild(detail);\n list.appendChild(row);\n }\n box.appendChild(list);\n }\n\n box.appendChild(el(\"div\", \"ev-meta\", reason));\n\n // Said every time, not only when it matters. A tester that silently invents a client\n // address is a tester whose answer about `ip-intelligence` cannot be trusted, and the\n // reader has no way to know which run was which.\n const notes = [...assumed, \"no history: this is assessed as a first request, so cadence and crawl breadth have nothing to read\"];\n box.appendChild(el(\"div\", \"assumed\", `Assumed — ${notes.join(\"; \")}.`));\n}\n","import { $, byId, el, eventTarget, isEmbedded, rootNode, themeElement } from \"./dom.js\";\nimport { API, BOOT, SECTIONS } from \"./boot.js\";\nimport { app, toast } from \"./app.js\";\nimport { clearFeed, setSearch, state } from \"./store.js\";\nimport { drawActor, initActor } from \"./actor.js\";\nimport { connectStream, loadInitialSnapshot } from \"./stream.js\";\nimport { drawAudit, drawChanges, drawChips, drawLivePanels, drawNoticeBadge, drawNotices, drawPeers, drawStatsPanels, drawTiles, updateWindowLabels } from \"./panels.js\";\nimport { drawFeed, initFeed, reflectFilterButtons, resetFeedCache } from \"./feed.js\";\nimport { drawLatency, drawScores, drawTraffic } from \"./charts.js\";\nimport { drawActors, trackActors } from \"./registry.js\";\nimport { drawPolicyTab, initPolicy, loadPolicy } from \"./policy.js\";\nimport { loadRanges } from \"./ranges.js\";\nimport { initTester } from \"./tester.js\";\nimport type { FilterName } from \"./query.js\";\nimport type { TabName } from \"./types.js\";\n\n/** The views this listener has, in tab-strip order. Sections decide which exist. */\nconst TABS: Array<[id: string, name: TabName, enabled: boolean]> = [\n [\"tab-live\", \"live\", SECTIONS.feed],\n [\"tab-actors\", \"actors\", SECTIONS.registry],\n [\"tab-stats\", \"stats\", SECTIONS.statistics],\n [\"tab-policy\", \"policy\", SECTIONS.policy],\n];\n\nconst available = TABS.filter(([, , enabled]) => enabled);\nconst FIRST: TabName = available[0]?.[1] ?? \"live\";\n\nfunction tabIndexOf(name: TabName): number {\n const at = available.findIndex(([, tab]) => tab === name);\n return at === -1 ? 0 : at;\n}\n\nfunction isTab(value: string): value is TabName {\n return available.some(([, name]) => name === value);\n}\n\n// ---- header ----------------------------------------------------------------\n\n/**\n * The skip link, which is a plain fragment anchor and therefore inert inside a shadow\n * root: fragment navigation does not cross the boundary, so the one affordance that lets\n * a keyboard past the header did nothing at all when embedded. Given the same behaviour\n * by hand.\n */\nfunction initSkipLink(): void {\n if (!isEmbedded()) return;\n const skip = rootNode().querySelector<HTMLAnchorElement>(\"a.skip\");\n if (skip === null) return;\n skip.addEventListener(\"click\", (event) => {\n event.preventDefault();\n const target = rootNode().querySelector<HTMLElement>(`#view-${state.tab}`) ?? rootNode().querySelector<HTMLElement>(\"#view-live\");\n if (target === null) return;\n target.tabIndex = -1;\n target.focus();\n target.scrollIntoView({ block: \"start\" });\n });\n}\n\nfunction initHeader(): void {\n const box = $(\"links\");\n for (const link of BOOT.links) {\n const anchor = el(\"a\", \"linkbtn\", link.label);\n anchor.href = link.href;\n anchor.rel = \"noreferrer noopener\";\n box.appendChild(anchor);\n }\n\n let stored: string | null = null;\n try {\n stored = localStorage.getItem(\"bothandler-dashboard-theme\");\n } catch {\n stored = null;\n }\n if (stored === \"dark\" || stored === \"light\") themeElement().setAttribute(\"data-theme\", stored);\n $(\"theme\").addEventListener(\"click\", () => {\n let current = themeElement().getAttribute(\"data-theme\");\n if (current === null) current = matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n const next = current === \"dark\" ? \"light\" : \"dark\";\n themeElement().setAttribute(\"data-theme\", next);\n try {\n localStorage.setItem(\"bothandler-dashboard-theme\", next);\n } catch {\n /* private mode */\n }\n // The charts read their colours from the CSS custom properties, so they have to be\n // redrawn rather than restyled.\n drawNow();\n });\n\n const pause = byId<HTMLButtonElement>(\"pause\");\n pause.hidden = !SECTIONS.feed;\n pause.addEventListener(\"click\", () => {\n state.paused = !state.paused;\n pause.setAttribute(\"aria-pressed\", String(state.paused));\n pause.textContent = state.paused ? `Resume${state.bufferedWhilePaused > 0 ? ` (${state.bufferedWhilePaused})` : \"\"}` : \"Pause\";\n if (!state.paused) {\n state.bufferedWhilePaused = 0;\n drawNow();\n }\n });\n\n if (BOOT.allowReset) {\n const reset = byId<HTMLButtonElement>(\"reset\");\n reset.hidden = false;\n reset.addEventListener(\"click\", () => {\n reset.disabled = true;\n void fetch(`${API}/api/reset`, { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: \"{}\" })\n .then(async (response) => {\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n toast(\"bad\", \"Reset refused\", body.error ?? String(response.status));\n return;\n }\n clearFeed();\n resetFeedCache();\n drawNow();\n })\n .catch(() => {\n /* the stream will resync */\n })\n .finally(() => {\n reset.disabled = false;\n });\n });\n }\n\n /**\n * The sticky column headers used to sit at a hard-coded 54px — the height of the top\n * row alone — so the tab strip covered them the moment the feed scrolled. The header\n * is not a fixed height: the chip row wraps on a narrow window and grows. Measure it,\n * and let the CSS read the measurement.\n */\n const header = rootNode().querySelector(\"header\");\n if (header !== null) {\n const apply = (): void => {\n themeElement().style.setProperty(\"--header-h\", `${header.getBoundingClientRect().height}px`);\n };\n apply();\n if (typeof ResizeObserver === \"function\") new ResizeObserver(apply).observe(header);\n else addEventListener(\"resize\", apply);\n }\n}\n\n// ---- navigation -------------------------------------------------------------\n\n/**\n * The view, the filter and the search live in the URL.\n *\n * The view already did. The other two are what make a screen shareable — \"look at the\n * guard stops on /export\" is a link now rather than a set of instructions — and what\n * survives the refresh that is everybody's reflex when a live feed looks stuck.\n * `replaceState` for the filter and the search, because a back button that walks\n * backwards through every keystroke is not a back button.\n */\nfunction syncUrl(replace = true): void {\n // Embedded, the URL belongs to the page around us. Writing a tab into it rewrites\n // somebody else's address bar and puts a history entry between them and wherever they\n // were going.\n if (isEmbedded()) return;\n const params = new URLSearchParams();\n if (state.filter !== \"all\") params.set(\"f\", state.filter);\n if (state.search !== \"\") params.set(\"q\", state.search);\n const query = params.toString();\n const hash = `#${state.tab}${query === \"\" ? \"\" : `?${query}`}`;\n if (location.hash === hash) return;\n history[replace ? \"replaceState\" : \"pushState\"]({ tab: state.tab }, \"\", hash);\n}\n\nfunction readUrl(): { tab: TabName; filter: FilterName; search: string } {\n // And it is not ours to read either: a host page using hash routing would otherwise\n // decide which tab this opens on.\n const raw = isEmbedded() ? \"\" : location.hash.slice(1);\n const split = raw.indexOf(\"?\");\n const name = split === -1 ? raw : raw.slice(0, split);\n const params = new URLSearchParams(split === -1 ? \"\" : raw.slice(split + 1));\n const filter = params.get(\"f\") ?? \"all\";\n return {\n tab: isTab(name) ? name : FIRST,\n filter: filter as FilterName,\n search: params.get(\"q\") ?? \"\",\n };\n}\n\nfunction showTab(name: TabName, options: { focus?: boolean; replace?: boolean; push?: boolean } = {}): void {\n const target = isTab(name) ? name : FIRST;\n state.tab = target;\n for (const [id, tab, enabled] of TABS) {\n const selected = tab === target;\n const node = $(id);\n node.hidden = !enabled;\n node.setAttribute(\"aria-selected\", String(selected));\n // Roving tabindex: the selected tab is the strip's single stop in the tab order.\n node.tabIndex = selected ? 0 : -1;\n $(`view-${tab}`).hidden = !selected || !enabled;\n }\n if (options.focus === true) $(available[tabIndexOf(target)]?.[0] ?? \"tab-live\").focus();\n if (target === \"policy\" && state.policy === undefined) {\n void loadPolicy();\n void loadRanges();\n }\n // The Actors screen is fetched rather than streamed, and polled only while it is the\n // screen somebody is looking at.\n trackActors();\n if (options.push !== false) syncUrl(options.replace !== false);\n drawNow();\n}\n\nfunction initTabs(): void {\n available.forEach(([id, name], index) => {\n const tab = $(id);\n tab.addEventListener(\"click\", () => showTab(name, { replace: false }));\n tab.addEventListener(\"keydown\", (event) => {\n let next = -1;\n if (event.key === \"ArrowRight\") next = (index + 1) % available.length;\n else if (event.key === \"ArrowLeft\") next = (index - 1 + available.length) % available.length;\n else if (event.key === \"Home\") next = 0;\n else if (event.key === \"End\") next = available.length - 1;\n if (next === -1) return;\n event.preventDefault();\n showTab(available[next]?.[1] ?? FIRST, { focus: true, replace: false });\n });\n });\n\n if (isEmbedded()) return;\n addEventListener(\"popstate\", () => {\n const url = readUrl();\n state.filter = url.filter;\n setSearch(url.search);\n reflectFilterButtons();\n showTab(url.tab, { push: false });\n });\n}\n\n// ---- keyboard ---------------------------------------------------------------\n\nfunction initKeyboard(): void {\n // Scoped to this dashboard when embedded: a digit pressed while the host page has\n // focus switched a tab in here, which is somebody else's keyboard being taken.\n eventTarget().addEventListener(\"keydown\", ((event: KeyboardEvent) => {\n const target = event.target as HTMLElement | null;\n const typing = target !== null && (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\" || target.tagName === \"SELECT\");\n\n if (event.key === \"Escape\") {\n if (typing && target?.id === \"search\" && (target as HTMLInputElement).value !== \"\") {\n (target as HTMLInputElement).value = \"\";\n setSearch(\"\");\n syncUrl();\n drawNow();\n return;\n }\n if (typing) {\n target?.blur();\n return;\n }\n // Otherwise close whatever is open, innermost first.\n if (state.actor !== undefined) {\n state.actor = undefined;\n drawNow();\n return;\n }\n if (state.open.size > 0) {\n state.open.clear();\n drawNow();\n }\n return;\n }\n\n if (typing || event.metaKey || event.ctrlKey || event.altKey) return;\n\n if (event.key === \"/\" && SECTIONS.feed) {\n event.preventDefault();\n showTab(\"live\");\n const search = byId<HTMLInputElement>(\"search\");\n search.focus();\n search.select();\n return;\n }\n // 1-2-3-4 for the views, the way every tabbed console does it. They index the tabs\n // this listener actually has, so a dashboard with two of them has two shortcuts.\n const digit = [\"1\", \"2\", \"3\", \"4\"].indexOf(event.key);\n if (digit !== -1 && digit < available.length) {\n event.preventDefault();\n showTab(available[digit]?.[1] ?? FIRST, { focus: true, replace: false });\n }\n }) as EventListener);\n}\n\n// ---- the two range controls --------------------------------------------------\n\nfunction initRanges(): void {\n const ranges: Array<[string, number]> = [\n [\"1m\", 60_000],\n [\"5m\", 300_000],\n [\"15m\", 900_000],\n [\"1h\", 3_600_000],\n ];\n const host = $(\"traffic-range\");\n for (const [label, value] of ranges) {\n const button = el(\"button\", null, label);\n button.setAttribute(\"aria-pressed\", String(value === state.rangeMs));\n button.addEventListener(\"click\", () => {\n state.rangeMs = value;\n for (const other of Array.from(host.children)) other.setAttribute(\"aria-pressed\", \"false\");\n button.setAttribute(\"aria-pressed\", \"true\");\n drawTraffic();\n });\n host.appendChild(button);\n }\n\n const scopes: Array<[string, \"run\" | \"window\"]> = [\n [\"since start\", \"run\"],\n [\"this window\", \"window\"],\n ];\n const scopeHost = $(\"score-scope\");\n for (const [label, value] of scopes) {\n const button = el(\"button\", null, label);\n button.setAttribute(\"aria-pressed\", String(value === state.scoreScope));\n button.addEventListener(\"click\", () => {\n state.scoreScope = value;\n for (const other of Array.from(scopeHost.children)) other.setAttribute(\"aria-pressed\", \"false\");\n button.setAttribute(\"aria-pressed\", \"true\");\n drawScores();\n });\n scopeHost.appendChild(button);\n }\n}\n\n// ---- draw --------------------------------------------------------------------\n\nlet pending = false;\n\nfunction schedule(): void {\n if (state.paused || pending) return;\n pending = true;\n requestAnimationFrame(() => {\n pending = false;\n draw();\n });\n}\n\n/**\n * Draws the view that is on screen, and nothing else.\n *\n * It used to draw all three. Every incoming request rebuilt eight statistics bar\n * panels into a hidden tab, on top of rebuilding three hundred feed rows — which is\n * work nobody could see, done at the frame rate, on the one screen where the work that\n * can be seen matters most.\n */\nfunction draw(): void {\n drawChips();\n drawTiles();\n drawNoticeBadge();\n updateWindowLabels();\n\n if (state.tab === \"live\") {\n drawActor();\n drawFeed();\n drawLivePanels();\n } else if (state.tab === \"actors\") {\n drawActors();\n } else if (state.tab === \"stats\") {\n drawTraffic();\n drawLatency();\n drawScores();\n drawStatsPanels();\n if (SECTIONS.audit) drawAudit();\n } else {\n drawPolicyTab();\n if (SECTIONS.notices) drawNotices();\n if (SECTIONS.changes) drawChanges();\n }\n}\n\nfunction drawNow(): void {\n draw();\n}\n\n// ---- start -------------------------------------------------------------------\n\nfunction applySections(): void {\n // A section that is off is removed from the document rather than hidden, so nothing\n // can tab into it and no panel is left waiting for data that will never arrive.\n const gated: Array<[string, boolean]> = [\n [\"tiles\", SECTIONS.statistics],\n [\"tester-panel\", SECTIONS.tester],\n [\"ranges-panel\", SECTIONS.ranges],\n [\"changes-panel\", SECTIONS.changes],\n [\"actor-panel\", SECTIONS.actors],\n [\"live-actors-panel\", SECTIONS.actors],\n [\"audit-panel\", SECTIONS.audit],\n [\"audit-checks-panel\", SECTIONS.audit],\n [\"notices-panel\", SECTIONS.notices],\n [\"guard-panel\", SECTIONS.guard],\n [\"robots-panel\", SECTIONS.robots],\n [\"identities-panel\", SECTIONS.actors],\n [\"evidence-legend\", SECTIONS.evidence],\n ];\n for (const [id, enabled] of gated) {\n const node = rootNode().querySelector<HTMLElement>(`#${id}`);\n if (node !== null && !enabled) node.remove();\n }\n}\n\nfunction start(): void {\n app.draw = schedule;\n app.drawNow = drawNow;\n app.showTab = showTab;\n app.syncUrl = () => syncUrl();\n\n applySections();\n initHeader();\n drawPeers();\n initTabs();\n initSkipLink();\n initKeyboard();\n initRanges();\n if (SECTIONS.feed) initFeed();\n initActor();\n initTester();\n initPolicy();\n\n let resizeTimer: ReturnType<typeof setTimeout> | undefined;\n addEventListener(\"resize\", () => {\n if (resizeTimer !== undefined) clearTimeout(resizeTimer);\n resizeTimer = setTimeout(() => {\n if (state.tab === \"stats\") {\n drawTraffic();\n drawLatency();\n drawScores();\n }\n }, 120);\n });\n\n // Open on whatever view the URL names, so a link to #policy lands there and a refresh\n // keeps your place — filter and search included. replace:true so the first entry does\n // not add a step the back button has to walk through on the way out.\n const url = readUrl();\n state.filter = url.filter;\n setSearch(url.search);\n if (SECTIONS.feed) reflectFilterButtons();\n showTab(url.tab, { replace: true });\n\n void loadInitialSnapshot();\n connectStream();\n}\n\nstart();\n","/**\n * `<bot-dashboard>` — the operator dashboard as an element you drop into your own page.\n *\n * ```html\n * <bot-dashboard src=\"/_bots\"></bot-dashboard>\n * <script type=\"module\">\n * import { defineBotDashboard } from \"@osqd/bothandlerjs/element\";\n * defineBotDashboard();\n * </script>\n * ```\n *\n * The data still comes from a mounted handler — `createDashboardHandler(botHandler, {\n * basePath: \"/_bots\" })` — because there is nowhere else for it to come from. What the\n * element removes is having to build, style and route a page around it.\n *\n * ## What running it in your page costs you\n *\n * This renders into a shadow root inside your document, which is what makes it sit in\n * your layout rather than in a frame. The shadow root is a styling boundary and **not a\n * security boundary**: any script that can run on the host page can reach into it, read\n * every visitor address and verdict on screen, and call the dashboard's API with your\n * credentials. On the standalone page an injected script in your application could do\n * none of that.\n *\n * So mount it on a page that is already behind your admin authentication, and treat an\n * XSS on that page as equivalent to handing over the dashboard. If you would rather have\n * the isolation than the layout, serve the standalone page instead — it is the same\n * dashboard, and `createDashboardHandler` already returns it.\n */\n\nimport { cssEscape } from \"../dashboard/client/css.js\";\nimport { cellText, explainStatus, parseMount, resolveSections, shapeOf } from \"./config.js\";\nimport type { BotDashboardConfig, BotDashboardPanel, BotDashboardRows, BotDashboardTab, BotDashboardTabId, BotDashboardTheme } from \"./config.js\";\nimport { DASHBOARD_CSS, DASHBOARD_MARKUP } from \"../dashboard/page.js\";\n\n/** One screen of the dashboard. */\nexport type { BotDashboardConfig, BotDashboardPanel, BotDashboardRows, BotDashboardTab, BotDashboardTabId, BotDashboardTheme };\n\n/**\n * Most rows a panel may put on the page.\n *\n * A panel is a summary, and a source that returns everything it has should not be able to\n * lock up somebody's admin page while fifty thousand rows are laid out — which is exactly\n * what an unbounded version did when asked. Anything past this is dropped and said so, so\n * the number on screen is never quietly wrong.\n */\nconst MAX_PANEL_ROWS = 200;\n\nconst TAB_ELEMENT: Record<BotDashboardTabId, string> = {\n live: \"tab-live\",\n actors: \"tab-actors\",\n stats: \"tab-stats\",\n policy: \"tab-policy\",\n};\n\n\nconst EXTRA_CSS = `\n/* What the body element carries on the standalone page.\n A shadow root has no body, so without this nothing sets the base colour, background or\n type: every element inherited the *host page's* colour instead. On a white page that\n passed for correct; in dark mode it was near-black text on a near-black surface, which\n axe reported as a serious contrast failure and which is exactly the kind of thing this\n library is not supposed to ship. */\n:host {\n display: block;\n background: var(--page);\n color: var(--ink);\n font: 14px/1.55 system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n:host([hidden]) { display: none; }\n/* The page's own chrome assumes it owns the viewport. Inside somebody else's layout the\n element is the viewport, so the sticky header sticks to the element and the body\n metrics come from the host box. */\n:host { position: relative; }\n:host header { position: sticky; top: 0; }\n.bd-extra { display: grid; gap: 10px; padding: 14px 15px; }\n.bd-notice { font: 14px/1.5 system-ui, sans-serif; padding: 14px; margin: 0; color: var(--crit-text, #8a1c1c); }\n.bd-extra .row { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }\n.bd-extra .row b { font-weight: 600; font-variant-numeric: tabular-nums; }\n.bd-extra .row span { color: var(--muted); font-size: 12px; }\n:host([data-density=\"compact\"]) tbody td,\n:host([data-density=\"compact\"]) thead th { padding-top: 5px; padding-bottom: 5px; }\n:host([data-density=\"compact\"]) .panel > h2 { padding-top: 8px; padding-bottom: 8px; }\n`;\n\n/**\n * The dashboard's rendered subtree, kept across mounts.\n *\n * An SPA route unmounts and remounts the element, and the first version refused the second\n * mount outright — \"a bot dashboard is already running on this page\" — which made the\n * element unusable in React, Vue or anything else with a router. The client is a module\n * graph that evaluates once and holds its own state, so it cannot simply be started again.\n *\n * Keeping the subtree and re-parenting it into the new shadow root solves both halves: the\n * client's element references stay valid because they are the same nodes, and the feed\n * history survives the navigation instead of starting empty. What changes on a remount is\n * the host element the theme hangs off, and the stylesheet, which belongs to the shadow\n * root rather than to the subtree.\n */\nlet container: HTMLElement | undefined;\nlet owner: BotDashboardElement | undefined;\n/**\n * The theme most recently applied, remembered across mounts.\n *\n * A router builds a *new* element on the way back to a route, and that element carries no\n * configuration until the framework sets it — which for an attribute-driven or set-once\n * integration is never. Without this the dashboard came back unthemed: the tokens had been\n * set as inline properties on the previous host, which is gone.\n */\nlet lastTheme: BotDashboardTheme | undefined;\n\n/**\n * Registers `<bot-dashboard>`. Safe to call more than once.\n *\n * @param name - element name, if `bot-dashboard` is taken.\n */\nexport function defineBotDashboard(name = \"bot-dashboard\"): void {\n if (typeof customElements === \"undefined\") return;\n const taken = customElements.get(name);\n if (taken !== undefined) {\n // Calling this twice is ordinary and stays silent — it is the documented way to be\n // safe about ordering. A name held by *something else* is a different situation\n // entirely: this call does nothing, every `<bot-dashboard>` on the page is the other\n // library's element, and nothing about the resulting blank space says so.\n if (taken !== BotDashboardElement && !(taken.prototype instanceof BotDashboardElement) && !clashed.has(name)) {\n clashed.add(name);\n console.warn(`bot-dashboard: <${name}> is already registered on this page by something else, so this did nothing and your <${name}> elements are not this dashboard. Register it under a name of your own instead: defineBotDashboard(\"ops-dashboard\").`);\n }\n return;\n }\n // A constructor may be registered once per registry, so a second name cannot reuse this\n // class — `customElements.define` throws NotSupportedError, uncaught, from inside a\n // function whose whole job is to register a name. A fresh subclass per name is what the\n // platform wants, and it costs nothing: the behaviour is inherited whole.\n //\n // Only one dashboard runs at a time whatever it is called; that is enforced separately,\n // in `boot`.\n customElements.define(name, registered ? class extends BotDashboardElement {} : BotDashboardElement);\n registered = true;\n}\n\n/** Whether the base class has been handed to the registry yet. See {@link defineBotDashboard}. */\nlet registered = false;\n\n/** Names already reported as taken, so calling `defineBotDashboard` twice says it once. */\nconst clashed = new Set<string>();\n\n/**\n * `HTMLElement`, or a stand-in where there is no DOM.\n *\n * `class X extends HTMLElement` is evaluated when the module is *loaded*, not when the\n * element is used — so on a server, where there is no `HTMLElement`, merely importing this\n * file threw `ReferenceError` before a line of anyone's code ran. That is not an exotic\n * case: it is what every framework with server rendering does with a top-level\n * `import { defineBotDashboard } from \"@osqd/bothandlerjs/element\"`, which is exactly the\n * line the documentation tells people to write.\n *\n * `defineBotDashboard` already declines to do anything without a `customElements`\n * registry, so the intent was there; the class declaration simply ran first. With this the\n * module imports anywhere, and does nothing at all until it is in a browser.\n */\nconst ElementBase: typeof HTMLElement = typeof HTMLElement === \"undefined\" ? (class {} as unknown as typeof HTMLElement) : HTMLElement;\n\nexport class BotDashboardElement extends ElementBase {\n private settings: BotDashboardConfig = {};\n private booted = false;\n /**\n * Booted *and* finished drawing. Distinct from `booted`, which is set on the way in:\n * `connectedCallback` replays a config assigned before the upgrade through the setter,\n * and at that moment `booted` is already true — so a warning keyed on it fired on a\n * perfectly ordinary mount, complaining that the config had changed when what had\n * happened was that it had arrived.\n */\n private rendered = false;\n\n /**\n * Set before the element is attached, or pass the same things as attributes.\n *\n * An accessor rather than a field, and the difference is load-bearing. A page that\n * writes `el.config = {...}` before the element is defined puts an *own property* on\n * the instance, and a class field initialiser then runs at upgrade and overwrites it —\n * so the configuration silently disappears and the dashboard renders its defaults. The\n * accessor plus the upgrade dance in `connectedCallback` is the documented way round\n * it, and the order it protects (write the config, then define the element) is the\n * natural one to write.\n */\n get config(): BotDashboardConfig {\n return this.settings;\n }\n\n set config(value: BotDashboardConfig) {\n const before = this.rendered && this.isConnected ? shapeOf(this.settings) : undefined;\n this.settings = value ?? {};\n // Applied immediately when the element is already running, so a framework that sets\n // its props after mount — which most of them do on a re-render — changes the theme\n // rather than being quietly ignored.\n if (this.booted && this.isConnected) {\n this.applyTheme();\n // `tabs` and `panels` are settled at boot, and until now changing them did nothing\n // at all — no effect and no word about it, which is the failure `src` a few lines\n // down already refuses to make. Compared by shape rather than by identity, because\n // a framework hands over a freshly built object on every render and warning about\n // that would be noise nobody could act on.\n if (before !== undefined && shapeOf(this.settings) !== before) {\n console.warn(\"bot-dashboard: `tabs` and `panels` are read once, when the element first mounts. Changing them afterwards has no effect — remount the element instead. (`theme` does update live.)\");\n }\n }\n }\n private timers: number[] = [];\n /** Custom properties this element put on itself, so it can take them off again. */\n private appliedTokens = new Set<string>();\n private warned = new Set<string>();\n\n static get observedAttributes(): string[] {\n return [\"src\", \"scheme\", \"density\"];\n }\n\n connectedCallback(): void {\n if (this.booted) return;\n this.booted = true;\n // Reclaim anything assigned before the upgrade: delete the own property so the write\n // below reaches the accessor rather than sitting in front of it for ever.\n if (Object.prototype.hasOwnProperty.call(this, \"config\")) {\n const preset = (this as unknown as { config: BotDashboardConfig }).config;\n // The own property has to be removed, not emptied: assigning undefined leaves it in\n // place, shadowing the accessor on the prototype for the life of the element, and\n // the configuration never arrives. `Reflect.deleteProperty` rather than `delete`\n // says the same thing without tripping a lint rule aimed at hot loops; this runs\n // once per element.\n Reflect.deleteProperty(this, \"config\");\n this.config = preset;\n }\n void this.boot();\n }\n\n disconnectedCallback(): void {\n for (const timer of this.timers) clearInterval(timer);\n this.timers = [];\n // The subtree stays alive and detached, ready to be re-parented. Releasing ownership\n // is what lets a later element — the same one after a route change, or a different\n // one — pick it up.\n if (owner === this) owner = undefined;\n // And the stream goes with it. Left open it holds a server connection and keeps\n // drawing into a tree nobody can see, for as long as the page lives. Reconnecting\n // resumes from the last id seen, so the cost of a route change is a reconnect.\n void import(\"../dashboard/client/stream.js\").then((stream) => {\n if (!this.isConnected) stream.suspendStream();\n });\n }\n\n attributeChangedCallback(attribute: string, previous: string | null, value: string | null): void {\n // Through the same two checks as the config path, so an attribute cannot set a value\n // the config would have refused.\n if (attribute === \"scheme\") this.applyScheme(value);\n if (attribute === \"density\") this.applyDensity(value);\n // `src` decides where the client fetches from, and the client reads it once as it\n // loads. Changing it later does nothing, and doing nothing quietly is how somebody\n // spends an afternoon on it.\n if (attribute === \"src\" && previous !== null && previous !== value && this.booted) {\n console.warn(\"bot-dashboard: `src` is read once, when the element first mounts. Changing it afterwards has no effect — replace the element instead.\");\n }\n }\n\n /**\n * Give up on this attempt without giving up on the element.\n *\n * Every way a boot can end early — it lost the one-at-a-time race, its `src` is\n * cross-origin, its handler did not answer, it was unmounted mid-flight — is about this\n * moment rather than about this element, and all four are things a remount can fix: the\n * other dashboard has gone, the attribute was corrected, the server finished starting.\n * Releasing ownership without clearing `booted` was the bug: `connectedCallback` returns\n * early on a latched `booted`, so the element stayed dead for the life of the instance\n * and a router that remounts it drew a stale error over a handler that now works.\n */\n private standDown(): void {\n if (owner === this) owner = undefined;\n this.booted = false;\n this.rendered = false;\n }\n\n /**\n * Where the handler is mounted, from `config.src` or the attribute.\n *\n * A trailing slash is dropped, and so are a query string and a fragment — the element\n * appends `/api/bootstrap` to this, so anything after the path cannot survive that\n * concatenation and never could. Left in, `src=\"/_bots?token=x\"` failed with \"it\n * answered text/html — is createDashboardHandler mounted at /_bots?token=x?\", which\n * sends somebody to check the one thing that was right.\n */\n private mountPath(): string {\n const { base, warning } = parseMount(this.config.src ?? this.getAttribute(\"src\") ?? \"\");\n if (warning !== undefined) this.warnOnce(`bot-dashboard: ${warning}`);\n return base;\n }\n\n private async boot(): Promise<void> {\n const base = this.mountPath();\n const shadow = this.shadowRoot ?? this.attachShadow({ mode: \"open\" });\n // A retry after a refusal or a failed boot: drop the message from that attempt, or the\n // dashboard renders underneath a line that is no longer true.\n //\n // Direct children only. The same class marks a panel's own \"could not load\" message,\n // and on a remount the built tree is still hanging in this shadow root — so a plain\n // `querySelectorAll(\".bd-notice\")` reached inside the panels and wiped those too. They\n // came back on the next repaint, which is why it looked harmless; it was still this\n // function deleting other code's output.\n //\n // Walked rather than selected, because `:scope > .bd-notice` matches nothing here: the\n // scoping root is a shadow root, `:scope` only matches an element, and the tidier\n // selector silently removed nothing at all.\n for (const child of Array.from(shadow.children)) {\n if (child.classList.contains(\"bd-notice\")) child.remove();\n }\n\n // Still only one at a time. The client is a module graph with its own state, and two\n // live instances would share it — two feeds writing to one store, and the second\n // element silently drawing the first one's traffic. But *sequentially* is fine, and\n // is what a router does, so the test is whether one is currently connected rather\n // than whether one has ever been.\n if (owner !== undefined && owner !== this && owner.isConnected) {\n shadow.append(notice(\"A bot dashboard is already running on this page. Only one can be.\"));\n this.standDown();\n return;\n }\n owner = this;\n\n // A remount: re-parent what is already built and rendered, and re-point the theme.\n if (container !== undefined) {\n const dom = await import(\"../dashboard/client/dom.js\");\n dom.setThemeHost(this);\n this.applyTheme();\n adoptStyles(shadow);\n shadow.append(container);\n this.renderPanels(container);\n // Pick the feed back up where it was left.\n const stream = await import(\"../dashboard/client/stream.js\");\n stream.connectStream();\n this.rendered = true;\n this.dispatchEvent(new CustomEvent(\"bot-dashboard-ready\", { bubbles: true }));\n return;\n }\n\n // A cross-origin `src` cannot work and should say why rather than failing as a bare\n // \"Failed to fetch\". The dashboard deliberately sends no Access-Control-Allow-Origin —\n // that is what stops another site reading your traffic through a logged-in browser —\n // so it is same-origin only, and pointing at another origin is a configuration\n // mistake rather than something to retry.\n if (base !== \"\" && /^https?:\\/\\//i.test(base)) {\n try {\n if (new URL(base).origin !== location.origin) {\n const why = `src points at ${new URL(base).origin}, which is not this page's origin. The dashboard is same-origin only: it sends no CORS headers, which is what stops another site reading your traffic. Mount it under this origin and use a path, e.g. src=\"/_bots\".`;\n shadow.append(notice(`The bot dashboard could not start: ${why}`));\n console.error(\"bot-dashboard:\", why);\n this.standDown();\n return;\n }\n } catch {\n /* an unparseable src falls through to the fetch, which reports it. */\n }\n }\n\n let boot: Record<string, unknown>;\n try {\n const response = await fetch(`${base}/api/bootstrap`, { credentials: \"same-origin\", headers: { accept: \"application/json\" } });\n if (!response.ok) throw new Error(explainStatus(response.status, base));\n // Checked before parsing, because the failure it catches is the common one and the\n // parser's account of it is useless: with no `src` the element asks its own origin,\n // is handed the host page's HTML, and reports \"Unexpected token '<'\" to a developer\n // whose actual mistake was forgetting an attribute.\n const type = response.headers.get(\"content-type\") ?? \"\";\n if (!type.includes(\"json\")) {\n throw new Error(\n base === \"\"\n ? \"no `src` was given, so it asked this page's own origin and got HTML back. Point `src` at where createDashboardHandler is mounted, e.g. src=\\\"/_bots\\\"\"\n : `it answered ${type || \"no content type\"} rather than JSON. Is createDashboardHandler mounted at ${base}?`,\n );\n }\n boot = (await response.json()) as Record<string, unknown>;\n // Removed while that was in flight — a route change during the round trip. Ownership\n // was already handed back by `disconnectedCallback`, and somebody else may hold it\n // by now, so building into a detached tree here would be a second client racing the\n // live one for the same module state.\n if (!this.isConnected) {\n this.standDown();\n return;\n }\n } catch (error) {\n // A dashboard that cannot reach its handler should say so where somebody will see\n // it, rather than rendering an empty shell that looks like no traffic.\n const why = String(error instanceof Error ? error.message : error);\n shadow.append(notice(`The bot dashboard could not start: ${why}`));\n // Also to the console, because the element may be somewhere nobody is looking and\n // this is always a configuration mistake rather than a condition.\n console.error(\"bot-dashboard:\", why);\n this.standDown();\n return;\n }\n\n // The mount path the element was given wins over whatever the server thinks it is:\n // the element is the thing that knows where it is pointing.\n boot[\"base\"] = base;\n // Which screens survive the server's answer, `hide` and `tabs` — worked out in\n // `config.ts`, which has no document in it and can therefore be tested directly.\n const { sections, warnings } = resolveSections(boot[\"sections\"] as Record<string, boolean>, this.config);\n for (const warning of warnings) this.warnOnce(`bot-dashboard: ${warning}`);\n boot[\"sections\"] = sections;\n\n adoptStyles(shadow);\n\n const frame = document.createElement(\"div\");\n frame.className = \"bd-root\";\n // The markup is this package's own, built from a template literal in `page.ts` and\n // containing no interpolation of anything a request supplied. It is the one place the\n // no-innerHTML rule does not apply, and a test asserts that rather than trusting it.\n //\n // The one placeholder it carries is the title, which the standalone renderer\n // substitutes and this has to as well — the first version did not, and the header\n // read \"__TITLE__\" to anyone who looked at it.\n frame.innerHTML = DASHBOARD_MARKUP;\n shadow.append(frame);\n const title = typeof boot[\"title\"] === \"string\" ? boot[\"title\"] : \"bothandlerjs\";\n\n // The page's own <main> is a landmark, and a landmark inside somebody else's page that\n // already has one is two mains: a screen reader user gets two \"main\" landmarks to\n // choose between, and axe reports landmark-no-duplicate-main and landmark-unique.\n //\n // Replaced rather than re-roled. Setting role=\"region\" on a <main> silences those two\n // and earns aria-allowed-role instead, because <main> permits no role but its own — so\n // the honest fix is to not be a <main>. The class carries the layout, so swapping the\n // tag changes nothing visually.\n const main = frame.querySelector(\"main\");\n if (main !== null) {\n // Swapped rather than re-roled, because <main> permits no role but its own —\n // setting role=\"region\" silences the duplicate-landmark rules and earns\n // aria-allowed-role instead. Its class carries the layout, so the tag is all that\n // changes.\n const region = document.createElement(\"div\");\n region.className = main.className;\n region.setAttribute(\"role\", \"region\");\n region.setAttribute(\"aria-label\", `${title} bot dashboard`);\n while (main.firstChild !== null) region.append(main.firstChild);\n main.replaceWith(region);\n }\n\n // The <header> is a banner landmark for the same reason, and a page may have one\n // banner. Re-roled rather than swapped, because <header> does permit role=\"none\" and\n // because its styling is by element rather than by class — a div here would lose it.\n // `none` drops the element's own semantics and leaves its children where they are.\n frame.querySelector(\"header\")?.setAttribute(\"role\", \"none\");\n for (const node of Array.from(frame.querySelectorAll(\"*\"))) {\n for (const child of Array.from(node.childNodes)) {\n // textContent, so a title containing markup is a title containing markup.\n if (child.nodeType === 3 && child.nodeValue?.includes(\"__TITLE__\") === true) {\n child.nodeValue = child.nodeValue.replace(/__TITLE__/g, title);\n }\n }\n }\n\n this.applyTheme();\n\n // Order matters. `setRoot` has to run before the rest of the client evaluates, since\n // its modules resolve their elements as they initialise — so the dom module is\n // imported and pointed at the shadow root first, and only then the entry.\n container = frame;\n const dom = await import(\"../dashboard/client/dom.js\");\n // The subtree rather than the shadow root, so a remount can carry it to a new one.\n dom.setRoot(frame, this);\n (globalThis as unknown as { __BOOTSTRAP__?: unknown }).__BOOTSTRAP__ = boot;\n\n await import(\"../dashboard/client/index.js\");\n // After the client, because it draws the strip from the boot object and would undo a\n // reorder applied before it ran.\n this.relabelTabs(shadow);\n this.renderPanels(frame);\n\n warnIfFramed();\n\n this.rendered = true;\n this.dispatchEvent(new CustomEvent(\"bot-dashboard-ready\", { bubbles: true }));\n }\n\n private applyTheme(): void {\n const theme = this.config.theme ?? lastTheme ?? {};\n if (this.config.theme !== undefined) lastTheme = this.config.theme;\n this.applyScheme(theme.scheme ?? this.getAttribute(\"scheme\"));\n this.applyDensity(theme.density ?? this.getAttribute(\"density\"));\n\n const next = new Set<string>();\n for (const [token, value] of Object.entries(theme.tokens ?? {})) {\n const name = token.startsWith(\"--\") ? token : `--${token}`;\n next.add(name);\n this.style.setProperty(name, value);\n }\n // A token dropped from the config has to be dropped from the element, or a theme\n // switch leaves the colours it stopped asking for painted on for ever. Removed by\n // name, one at a time, rather than by clearing the style attribute: the client keeps\n // its own measurements there — `--header-h` among them — and clearing it would take\n // the sticky header's layout with it.\n for (const name of this.appliedTokens) {\n if (!next.has(name)) this.style.removeProperty(name);\n }\n this.appliedTokens = next;\n }\n\n /**\n * `scheme` and `density` take two values each, and until now anything else was accepted\n * in silence — a mis-typed `scheme` did nothing, and a mis-typed `density` was written\n * onto the element as `data-density=\"cozy\"`, matching no rule and looking, from the\n * outside, exactly like a dashboard that ignores its configuration.\n *\n * Warned once per bad value per element, because `applyTheme` runs on every re-render\n * and a typo that repeats a hundred times is a typo the console has stopped conveying.\n */\n private applyScheme(value: string | null | undefined): void {\n if (value === null || value === undefined) return;\n if (value === \"light\" || value === \"dark\") {\n this.setAttribute(\"data-theme\", value);\n return;\n }\n this.warnOnce(`bot-dashboard: scheme \"${value}\" is not \"light\" or \"dark\", so it was ignored. Leave it unset to follow the viewer's own setting.`);\n }\n\n private applyDensity(value: string | null | undefined): void {\n if (value === null || value === undefined) return;\n if (value === \"comfortable\" || value === \"compact\") {\n this.setAttribute(\"data-density\", value);\n return;\n }\n this.warnOnce(`bot-dashboard: density \"${value}\" is not \"comfortable\" or \"compact\", so it was ignored.`);\n }\n\n private warnOnce(message: string): void {\n if (this.warned.has(message)) return;\n this.warned.add(message);\n console.warn(message);\n }\n\n /**\n * Reorders and relabels the strip the client has just drawn.\n *\n * Only the tabs that survived: which screens *exist* was settled in the boot object\n * before the client ran, because the client builds its list from there and looks each\n * one up by id — deleting an element here instead crashed it with \"no element\n * #tab-actors\", which is what this comment is standing in for.\n */\n private relabelTabs(shadow: ShadowRoot): void {\n const wanted = this.config.tabs;\n if (wanted === undefined || wanted.length === 0) return;\n const strip = shadow.querySelector(\".tabs\");\n if (strip === null) return;\n\n for (const tab of wanted) {\n const node = shadow.querySelector(`#${TAB_ELEMENT[tab.id]}`);\n if (node === null) continue;\n if (tab.label !== undefined) node.textContent = tab.label;\n // Appending a node already in the strip moves it, which is the reorder.\n strip.append(node);\n }\n }\n\n private renderPanels(within: ParentNode): void {\n const panels = this.config.panels ?? [];\n\n // The rendered tree outlives the element that built it — it is reused across a remount\n // and, on a route change, adopted by a *different* element with a different config. So\n // a panel that this config does not ask for has to go, or the new dashboard shows the\n // old one's panels alongside its own, frozen, because the timer that fed them was\n // cleared when their element was disconnected. Keyed on screen as well as id, so a\n // panel moved to another screen leaves rather than being drawn in both.\n const wanted = new Set(panels.map((panel) => `${panel.id}@${panel.screen}`));\n for (const section of Array.from(within.querySelectorAll(\"[data-bd-panel]\"))) {\n const key = `${section.getAttribute(\"data-bd-panel\")}@${section.getAttribute(\"data-bd-screen\")}`;\n if (!wanted.has(key)) section.remove();\n }\n\n const drawn = new Set<string>();\n for (const panel of panels) {\n // Two panels under one id land on one section: the first builds it, the second finds\n // it and paints over it, and then both refresh timers write into the same body,\n // which flickers between two sources every second. The section keeps the first\n // panel's heading while showing the second one's rows, so the screen actively\n // misdescribes itself.\n const key = `${panel.id}@${panel.screen}`;\n if (drawn.has(key)) {\n this.warnOnce(`bot-dashboard: two panels share the id \"${panel.id}\" on screen \"${panel.screen}\". Ids identify a panel, so the second was ignored — give them different ids.`);\n continue;\n }\n drawn.add(key);\n\n // Escaped, because `screen` and `id` come from whoever wrote the config and a\n // selector built by interpolation is a parser waiting to be handed something it\n // cannot read. An id containing a quote threw `SyntaxError` out of `querySelector`\n // — from here, outside the per-paint try below — which took down the whole loop\n // before the first panel was built, left `bot-dashboard-ready` undispatched, and\n // reported itself as a complaint about a selector nobody wrote.\n const host = within.querySelector(`#view-${cssEscape(panel.screen)}`);\n if (host === null) {\n // Silence here means a panel that simply never appears, and a developer reading\n // their own config for an hour. `src` gets the same treatment a few lines up.\n // The list is read off the rendered markup rather than kept alongside it, so it\n // stays right when a screen is added and cannot drift into naming one that a\n // `tabs` or `hide` setting has already removed.\n const known = Array.from(within.querySelectorAll(\"[id^='view-']\"))\n .map((node) => node.id.slice(\"view-\".length))\n .join(\", \");\n console.warn(`bot-dashboard: panel \"${panel.id}\" asks for screen \"${panel.screen}\", which this dashboard does not have. It has: ${known}.`);\n continue;\n }\n\n // A remount runs this against a subtree that already has the panels, so the section\n // is reused rather than rebuilt — but the timer below is *not*, because\n // `disconnectedCallback` cleared it. Skipping the whole loop here left a refreshing\n // panel frozen on whatever it last drew after the first route change.\n const existing = host.querySelector(`[data-bd-panel=\"${cssEscape(panel.id)}\"]`);\n let body: HTMLElement;\n if (existing !== null) {\n body = existing.querySelector(\".bd-extra\") as HTMLElement;\n } else {\n const section = document.createElement(\"section\");\n section.className = \"panel\";\n section.setAttribute(\"data-bd-panel\", panel.id);\n section.setAttribute(\"data-bd-screen\", panel.screen);\n const heading = document.createElement(\"h2\");\n heading.textContent = panel.title;\n body = document.createElement(\"div\");\n body.className = \"bd-extra\";\n section.append(heading, body);\n host.append(section);\n }\n\n const paint = async (): Promise<void> => {\n try {\n const data = typeof panel.source === \"string\"\n ? ((await (await fetch(panel.source, { credentials: \"same-origin\" })).json()) as BotDashboardRows)\n : await panel.source();\n while (body.firstChild) body.removeChild(body.firstChild);\n // Checked rather than assumed, because a source is somebody else's endpoint and\n // the failures are not hypothetical: `{ rows: \"nope\" }` is *iterable*, so\n // without this it rendered one row per character, each reading \"undefined\".\n if (!Array.isArray(data?.rows)) {\n body.append(notice(`${panel.title} returned no rows.`));\n return;\n }\n const all = data.rows;\n for (const row of all.slice(0, MAX_PANEL_ROWS)) {\n // A row with nothing to say is skipped rather than drawn as \"undefined\".\n if (typeof row !== \"object\" || row === null || row.label === undefined || row.value === undefined) continue;\n const line = document.createElement(\"div\");\n line.className = \"row\";\n const label = document.createElement(\"span\");\n label.textContent = cellText(row.label);\n const value = document.createElement(\"b\");\n // textContent throughout. Whatever your endpoint returns is somebody's data,\n // and this file will not turn it into markup.\n value.textContent = cellText(row.value);\n line.append(label, value);\n if (row.note !== undefined) {\n const note = document.createElement(\"span\");\n note.textContent = cellText(row.note);\n line.append(note);\n }\n body.append(line);\n }\n if (all.length > MAX_PANEL_ROWS) {\n const more = document.createElement(\"div\");\n more.className = \"row\";\n const label = document.createElement(\"span\");\n label.textContent = `${all.length - MAX_PANEL_ROWS} more not shown`;\n more.append(label);\n body.append(more);\n }\n } catch {\n while (body.firstChild) body.removeChild(body.firstChild);\n body.append(notice(`Could not load ${panel.title}.`));\n }\n };\n\n void paint();\n if (panel.refreshMs !== undefined && panel.refreshMs > 0) {\n this.timers.push(setInterval(paint, Math.max(1000, panel.refreshMs)) as unknown as number);\n }\n }\n }\n}\n\n/**\n * The stylesheet, built once and adopted by every shadow root that needs it.\n *\n * A constructable stylesheet rather than a `<style>` element, and the reason is Content\n * Security Policy. An injected `<style>` is inline style, so a host page carrying\n * `style-src 'self'` — which any admin page worth mounting this on does — blocks it, and\n * the dashboard renders with no colours, no radii and no layout at all. A stylesheet built\n * by script that has already satisfied `script-src` is not inline style and is not\n * blocked.\n *\n * It is also the cheaper answer: one parse of forty kilobytes of CSS shared by every\n * mount, rather than one per mount.\n */\nlet sheet: CSSStyleSheet | undefined;\n\nfunction adoptStyles(shadow: ShadowRoot): void {\n if (typeof CSSStyleSheet !== \"undefined\" && \"replaceSync\" in CSSStyleSheet.prototype && \"adoptedStyleSheets\" in shadow) {\n if (sheet === undefined) {\n sheet = new CSSStyleSheet();\n sheet.replaceSync(`${DASHBOARD_CSS}\\n${EXTRA_CSS}`);\n }\n shadow.adoptedStyleSheets = [...shadow.adoptedStyleSheets, sheet];\n return;\n }\n // Older engines without constructable stylesheets. Blocked by a strict style-src, which\n // is the trade those browsers get rather than no dashboard at all.\n const style = document.createElement(\"style\");\n style.textContent = `${DASHBOARD_CSS}\\n${EXTRA_CSS}`;\n shadow.append(style);\n}\n\n\n/**\n * What of a config actually decides the rendered screens: which tabs, and which panels\n * where. Deliberately not the panel `source` functions — a framework rebuilds those\n * closures on every render, and a comparison that counted them would report a change on\n * every render regardless of whether anything meaningful differed.\n */\n\nlet framingWarned = false;\n\n/**\n * The one protection that does not survive being embedded.\n *\n * The standalone dashboard is served by this package and its response carries\n * `frame-ancestors 'none'`, so a page on another origin cannot frame it and trick an\n * operator into clicking a control they cannot see. The element renders into a document\n * this package does not serve and cannot set a header on — so whether the same attack\n * works is decided by the host page's own headers, and the failure mode is silent.\n *\n * A warning rather than a refusal: an admin app framing its own pages is ordinary and\n * legitimate, which is why the check is specifically for a *cross-origin* ancestor —\n * reading `top.location` throws for one and reads fine for one of our own — and why the\n * decision to render anyway is not this element's to make.\n */\nfunction warnIfFramed(): void {\n if (framingWarned) return;\n if (globalThis.top === globalThis.self) return;\n try {\n void (globalThis.top as Window).location.origin;\n return;\n } catch {\n framingWarned = true;\n console.warn(\n \"bot-dashboard: this page is framed by another origin, and a framed dashboard can be clickjacked — an operator clicks a control they cannot see. The standalone page refuses framing with `frame-ancestors 'none'`; a page you serve yourself has to send that header itself, or `X-Frame-Options: DENY`.\",\n );\n }\n}\n\n\nfunction notice(text: string): HTMLElement {\n // A class rather than `style.cssText`. Writing the style attribute is inline style, and\n // a host page carrying `style-src 'self'` blocks it — so the one element whose whole job\n // is to explain a failure would have appeared unstyled, in the situation where somebody\n // most needs to notice it.\n const node = document.createElement(\"p\");\n node.className = \"bd-notice\";\n node.textContent = text;\n return node;\n}\n\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"bot-dashboard\": BotDashboardElement;\n }\n}\n","/**\n * The element's decisions, with no document in sight.\n *\n * `src/element/` is excluded from the coverage report for the same reason\n * `src/dashboard/client/` is — it needs a document and a custom-element registry, and a\n * number collected from a runner that cannot execute it would describe the runner. That\n * exclusion carries a second half in the client's case, and it was missing here: the pure\n * modules inside an excluded directory still get unit tests, they are simply not counted.\n * This is that module for the element.\n *\n * Everything below answers a question with an argument rather than by looking at the page:\n * which screens survive a config, what a status code means, where the handler is mounted,\n * whether two configs describe the same thing. It is also where the fiddly parts live —\n * `tabs` and `hide` naming different vocabularies, a screen the server withheld — which is\n * exactly the logic that had bugs in it and no test able to reach them.\n */\n\nimport type { DashboardSections } from \"../dashboard/sections.js\";\n\nexport type BotDashboardTabId = \"live\" | \"actors\" | \"stats\" | \"policy\";\n\nexport interface BotDashboardTab {\n id: BotDashboardTabId;\n /** Shown in the tab strip. Defaults to the built-in name. */\n label?: string;\n}\n\n/** A panel of your own, rendered beside the built-in ones. */\nexport interface BotDashboardPanel {\n id: string;\n /** Which screen it appears on. */\n screen: BotDashboardTabId;\n title: string;\n /**\n * Where its rows come from: a URL returning `{ rows: [{ label, value, note? }] }`, or a\n * function returning the same shape.\n *\n * Rendered as text, always — the same rule the rest of the client follows, and for the\n * same reason. Nothing here interprets markup, so a value that happens to contain a tag\n * appears as that tag rather than becoming one.\n */\n source: string | (() => Promise<BotDashboardRows> | BotDashboardRows);\n /** How often to refresh, ms. Omit for once on load. */\n refreshMs?: number;\n}\n\nexport interface BotDashboardRows {\n rows: ReadonlyArray<{ label: string; value: string | number; note?: string }>;\n}\n\nexport interface BotDashboardTheme {\n /**\n * Token overrides, by custom property name without the leading dashes:\n * `{ accent: \"#7c3aed\", surface: \"#fff\" }`.\n *\n * Applied to the host, so they cascade into the shadow root the same way the built-in\n * tokens do. Every token the stylesheet defines can be replaced; the contrast\n * guarantees the built-in palette was measured against are yours to keep once you do.\n */\n tokens?: Record<string, string>;\n /** Force a scheme instead of following the host page and the OS. */\n scheme?: \"light\" | \"dark\";\n /** `compact` tightens the row and panel padding. */\n density?: \"comfortable\" | \"compact\";\n}\n\nexport interface BotDashboardConfig {\n /** Where the dashboard handler is mounted. Also settable as the `src` attribute. */\n src?: string;\n /** Which screens appear, in which order, under which labels. */\n tabs?: readonly BotDashboardTab[];\n /** Panels of your own. */\n panels?: readonly BotDashboardPanel[];\n theme?: BotDashboardTheme;\n /**\n * Hide parts of the page the *server* is still serving.\n *\n * Cosmetic, and worth being clear about: a section switched off here is removed from\n * the screen and stays on the wire. The `sections` option on the handler is the one\n * that stops the data leaving the process, and it is the one to use when the point is\n * that somebody should not have it.\n */\n hide?: DashboardSections;\n}\n\n/**\n * The section flag that decides whether each screen exists.\n *\n * Omitting a tab has to be expressed this way rather than by deleting the element: the\n * client builds its tab list from the sections it was booted with and then looks each one\n * up, so a missing element is a crash rather than a smaller strip. Saying it in the boot\n * object means the client removes the screen itself, the way it already does for a\n * section the server switched off.\n */\nexport const TAB_SECTION: Record<BotDashboardTabId, keyof DashboardSections> = {\n live: \"feed\",\n actors: \"registry\",\n stats: \"statistics\",\n policy: \"policy\",\n};\n\n/** `hide: { policy: true }` means the client's `sections.policy` is false. */\nfunction invert(hide: DashboardSections): Record<string, boolean> {\n const out: Record<string, boolean> = {};\n for (const [key, value] of Object.entries(hide)) if (value === true) out[key] = false;\n return out;\n}\n\n/**\n * Which sections the client should be booted with, and what to say about the config.\n *\n * Three inputs settle it, in this order: what the server offered, what `hide` removes, and\n * what `tabs` narrows to. The warnings are returned rather than logged because the reasons\n * a screen goes missing are the interesting part and the caller is the only one that knows\n * whether it has already said them.\n */\nexport function resolveSections(\n fromServer: Readonly<Record<string, boolean>>,\n config: Pick<BotDashboardConfig, \"hide\" | \"tabs\">,\n): { sections: Record<string, boolean>; warnings: string[] } {\n const sections: Record<string, boolean> = { ...fromServer };\n const warnings: string[] = [];\n\n // `tabs` and `hide` do not share a vocabulary — `tabs` names screens (`live`, `actors`,\n // `stats`, `policy`) and `hide` names sections (`feed`, `registry`, `statistics`,\n // `policy`, and the finer-grained ones that are not screens at all). A name from the\n // wrong list is not a type error to anyone writing plain JavaScript, and did nothing\n // whatsoever: `hide: { live: true }` is the natural thing to write after reading about\n // `tabs`, and it left the live feed exactly where it was.\n for (const key of Object.keys(config.hide ?? {})) {\n if (Object.hasOwn(sections, key)) continue;\n const suggestion = TAB_SECTION[key as BotDashboardTabId];\n warnings.push(\n suggestion === undefined\n ? `hide.${key} is not a section this dashboard has, so it did nothing. It has: ${Object.keys(sections).sort().join(\", \")}.`\n : `hide.${key} did nothing — \"${key}\" is a tab name, and \\`hide\\` takes section names. You want hide.${suggestion}, or leave \"${key}\" out of \\`tabs\\`.`,\n );\n }\n for (const [key, value] of Object.entries(invert(config.hide ?? {}))) sections[key] = value;\n\n // A tab left out of `tabs` is a screen the client should not build at all.\n const wanted = config.tabs;\n if (wanted !== undefined && wanted.length > 0) {\n const keep = new Set(wanted.map((tab) => tab.id));\n for (const id of keep) {\n // Dropped in silence until now, so asking for two screens and being given one looked\n // like the element choosing for itself.\n if (TAB_SECTION[id] === undefined) {\n warnings.push(`tabs lists \"${id}\", which is not a screen. The screens are: ${Object.keys(TAB_SECTION).join(\", \")}.`);\n continue;\n }\n // Asked for, and withheld by the server rather than by anything on this page. The\n // element cannot override it and should not pretend to: `sections` is enforced where\n // the data is, which is the whole point of it. But a developer who lists two screens\n // and is given one deserves to be told which file to go and look in.\n if (fromServer[TAB_SECTION[id]] === false && config.hide?.[TAB_SECTION[id]] !== true) {\n warnings.push(\n `tabs lists \"${id}\", but this dashboard's server has the \"${TAB_SECTION[id]}\" section switched off, so that screen does not exist. That is the \\`sections\\` option on createDashboardHandler, and it is enforced there rather than here.`,\n );\n }\n }\n for (const id of Object.keys(TAB_SECTION) as BotDashboardTabId[]) {\n if (!keep.has(id)) sections[TAB_SECTION[id]] = false;\n }\n }\n\n return { sections, warnings };\n}\n\n/**\n * Where the handler is mounted, from a raw `src`.\n *\n * A trailing slash is dropped, and so are a query string and a fragment — the element\n * appends `/api/bootstrap` to this, so anything after the path cannot survive that\n * concatenation and never could. Left in, `src=\"/_bots?token=x\"` failed with \"it answered\n * text/html — is createDashboardHandler mounted at /_bots?token=x?\", which sends somebody\n * to check the one thing that was right.\n */\nexport function parseMount(raw: string): { base: string; warning?: string } {\n const cut = raw.search(/[?#]/);\n if (cut === -1) return { base: raw.replace(/\\/$/, \"\") };\n return {\n base: raw.slice(0, cut).replace(/\\/$/, \"\"),\n warning: `src \"${raw}\" has a ${raw[cut] === \"?\" ? \"query string\" : \"fragment\"} on it. The element asks for \\`<src>/api/bootstrap\\`, so only the path can mean anything here; the rest was ignored.`,\n };\n}\n\n/**\n * What a refusal from the handler actually means to whoever has to fix it.\n *\n * \"it answered 401\" is true and useless, and 401 is not an exotic case: the documented\n * setup is a dashboard with `auth` set, embedded in an admin page, and a background fetch\n * cannot put up the sign-in prompt that a browser would show for a navigation. So the\n * dashboard is simply blank with a number on it, and the number does not say that the\n * credentials the operator already has are the answer.\n */\nexport function explainStatus(status: number, base: string): string {\n if (status === 401) {\n return `it answered 401 Unauthorized. The dashboard has \\`auth\\` set, and the element fetches in the background, where a browser cannot offer the sign-in prompt it would show for a normal navigation. Open ${base || \"the dashboard\"} directly and sign in once — the browser then sends those credentials with the element's requests too — or put this page behind the same authentication.`;\n }\n if (status === 403) {\n return `it answered 403 Forbidden, which is this dashboard refusing the caller rather than the password: check \\`allowedHosts\\` and \\`allowedClients\\`, and that this page is on the same origin as \\`src\\`.`;\n }\n return `it answered ${status}`;\n}\n\n/**\n * What of a config actually decides the rendered screens: which tabs, and which panels\n * where. Deliberately not the panel `source` functions — a framework rebuilds those\n * closures on every render, and a comparison that counted them would report a change on\n * every render regardless of whether anything meaningful differed.\n */\nexport function shapeOf(config: BotDashboardConfig): string {\n const tabs = (config.tabs ?? []).map((tab) => tab.id).join(\",\");\n const panels = (config.panels ?? []).map((panel) => `${panel.id}@${panel.screen}:${panel.title}`).join(\",\");\n return `${tabs}|${panels}`;\n}\n\n/** A panel cell, as text and never as markup, and never longer than a cell should be. */\nexport function cellText(value: unknown): string {\n if (typeof value === \"string\") return value.slice(0, 200);\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n if (value === null || value === undefined) return \"\";\n try {\n return JSON.stringify(value)?.slice(0, 200) ?? \"\";\n } catch {\n return \"\";\n }\n}\n","import { CLIENT_SCRIPT } from \"./client.generated.js\";\nimport type { DashboardSections } from \"./types.js\";\n\n/**\n * The dashboard page.\n *\n * Shipped as a template rather than as a file on disk: a bundled library cannot assume\n * anything about what sits next to its own JavaScript, and a dashboard that works from\n * `src` and 404s from `dist` is the worst kind of bug to find. The markup and the CSS\n * are the template below; the behaviour is a real TypeScript module under `client/`,\n * bundled into {@link CLIENT_SCRIPT} at build time by `scripts/build-client.mjs` and\n * stamped in here. It used to be two thousand lines of JavaScript inside this string,\n * where the type-checker could not see it, Biome did not lint it and no test could\n * import a function out of it — which is how a call to a function nobody had written\n * shipped and blanked the whole Statistics tab.\n *\n * Three rules govern everything here, and all three are about the fact that this page\n * renders **attacker-supplied text**. A User-Agent is written by the client. So is the\n * path. So is anything a detector quoted into an evidence summary.\n *\n * 1. **Nothing is ever assembled into HTML.** Every value reaches the document through\n * `textContent`. There is no `innerHTML` in the page or in the bundle, and the\n * build refuses to produce one.\n * 2. **No `style` attributes.** The page is served under a nonce-based CSP, which\n * blocks inline styles; geometry is set through the CSSOM (`el.style.width`), which\n * is not a style attribute and is allowed.\n * 3. **No network access of any kind.** `default-src 'none'` and no external font,\n * script or image. Everything is here.\n */\n\nexport interface DashboardPageOptions {\n title: string;\n basePath: string;\n links: ReadonlyArray<{ label: string; href: string }>;\n allowReset: boolean;\n allowEdit: boolean;\n allowGuardEdit: boolean;\n allowActing: boolean;\n peers: ReadonlyArray<{ label: string; href: string }>;\n /** Which parts of the page this listener has. The client removes the rest of them. */\n sections: Required<DashboardSections>;\n}\n\n/**\n * Builds the page once and returns a function that stamps a per-response nonce into\n * it. The HTML is a few tens of kilobytes and identical on every request; rebuilding\n * it per view would be pure waste.\n */\n/**\n * What the page is told about itself.\n *\n * Built here rather than inline in the renderer so that the embeddable element can be\n * served the identical object from `/api/bootstrap`. The page carries it stamped into its\n * one nonced script; the element has to ask, because it is rendered into somebody else's\n * document and there is nothing to stamp.\n */\nexport function bootFor(options: DashboardPageOptions): Record<string, unknown> {\n return {\n base: options.basePath === \"/\" ? \"\" : options.basePath,\n title: options.title,\n allowReset: options.allowReset,\n allowEdit: options.allowEdit,\n allowGuardEdit: options.allowGuardEdit,\n allowActing: options.allowActing,\n peers: options.peers.map((peer) => ({ label: String(peer.label), href: String(peer.href) })),\n sections: options.sections,\n links: options.links.map((link) => ({ label: String(link.label), href: String(link.href) })),\n };\n}\n\nexport function renderDashboardPage(options: DashboardPageOptions): (nonce: string) => string {\n const bootstrap = escapeForScript(JSON.stringify(JSON.stringify(bootFor(options))));\n\n // Function replacements throughout, never string ones. `String.prototype.replace`\n // reads `$&`, `` $` `` and `$'` out of a *string* replacement and substitutes match\n // context for them — so a title, a link label or a minified bundle containing one of\n // those sequences would otherwise rewrite itself on the way into the page.\n const html = PAGE.replace(\"__BOOT_JSON__\", () => bootstrap)\n .replace(\"__SCRIPT__\", () => CLIENT_SCRIPT)\n .replace(/__TITLE__/g, () => escapeHtml(options.title));\n return (nonce: string) => html.replace(/__NONCE__/g, () => nonce);\n}\n\n/**\n * Makes a JSON literal safe to sit inside a `<script>` element.\n *\n * `JSON.stringify` is not enough, and the gap is not theoretical: HTML ends a script\n * element at the first `</script>` in the source, *including one inside a string\n * literal*. A caller passing that sequence as a dashboard `title` — or a link label —\n * would otherwise close the tag and open one of their own. Escaping the angle brackets\n * as `\\u003c` / `\\u003e` keeps the value identical after `JSON.parse` while making the\n * sequence unrecognisable to the HTML parser. The two line separators are the same\n * class of problem for older parsers.\n */\nfunction escapeForScript(json: string): string {\n return json\n .replace(/</g, \"\\\\u003c\")\n .replace(/>/g, \"\\\\u003e\")\n .replace(/\\u2028/g, \"\\\\u2028\")\n .replace(/\\u2029/g, \"\\\\u2029\");\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (character) => ({ \"&\": \"&\", \"<\": \"<\", \">\": \">\", '\"': \""\", \"'\": \"'\" })[character] ?? character);\n}\n\n/**\n * The dashboard's stylesheet, on its own.\n *\n * Separated from the document so the same rules can be adopted into a shadow root by the\n * embeddable element. The selectors are written `:root, :host` throughout for that\n * reason: `:root` matches the document element on the standalone page and matches nothing\n * inside a shadow tree, where `:host` is the element the tokens have to hang off.\n */\nexport const DASHBOARD_CSS = String.raw`/* ---------------------------------------------------------------------------\n Tokens.\n\n The two series colours and the critical status step are the validated data\n palette: blue and orange clear every colour-vision gate against both surfaces as\n an adjacent pair, and every chart that uses them also carries a legend and a\n label, so hue is never the only thing distinguishing anything. Text never wears a\n series colour — the ink tokens below are all at least 4.5:1 on their surface.\n--------------------------------------------------------------------------- */\n:root, :host {\n color-scheme: light;\n --page: #f4f5f7;\n --surface: #ffffff;\n --surface-2: #fafafa;\n --raised: #ffffff;\n --line: #e4e6ea;\n --line-soft: #eef0f3;\n --ink: #0b0d12;\n --ink-2: #3c414b;\n --muted: #5f646e;\n --s1: #2a78d6;\n --s2: #eb6834;\n --crit: #d03b3b;\n /* The series blue carries white text at about 3.9:1, which is under the 4.5 floor —\n fine for a bar, not for a label. Solid buttons get a darker blue of their own\n rather than a lighter series colour, because the series colour is validated for\n charts and moving it would move every chart with it. */\n --accent-solid: #1b5fb0;\n --good-text: #006300;\n /* Darker than it looks like it needs to be, and measured rather than judged. This ink\n appears on a 16%-amber chip, and that chip can sit on the open row's blue tint —\n three layers, each lightening the background under the same text. At #8a5a00 the\n suspected badge measured 3.52:1 there, under the 4.5 floor for 11px bold. */\n --warn-text: #6d4700;\n --crit-text: #b02525;\n --info-text: #1c5cab;\n --proven-text: #5b34a8;\n --grid: #eceef1;\n --shadow: 0 1px 2px rgba(11,13,18,.06), 0 1px 8px rgba(11,13,18,.04);\n --focus: #2a78d6;\n}\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]), :host(:not([data-theme=\"light\"])) {\n color-scheme: dark;\n --page: #0d0f12;\n --surface: #16181d;\n --surface-2: #1b1e24;\n --raised: #1f232a;\n --line: #272b33;\n --line-soft: #202329;\n --ink: #f2f4f7;\n --ink-2: #c4cad3;\n --muted: #8b929c;\n --s1: #3987e5;\n --s2: #d95926;\n --crit: #d03b3b;\n --accent-solid: #2364b4;\n --good-text: #4ec97a;\n --warn-text: #fab219;\n --crit-text: #ff8078;\n --info-text: #86b6ef;\n --proven-text: #b3a4f5;\n --grid: #23272e;\n --shadow: none;\n --focus: #86b6ef;\n }\n}\n:root[data-theme=\"dark\"], :host([data-theme=\"dark\"]) {\n color-scheme: dark;\n --page: #0d0f12;\n --surface: #16181d;\n --surface-2: #1b1e24;\n --raised: #1f232a;\n --line: #272b33;\n --line-soft: #202329;\n --ink: #f2f4f7;\n --ink-2: #c4cad3;\n --muted: #8b929c;\n --s1: #3987e5;\n --s2: #d95926;\n --crit: #d03b3b;\n --accent-solid: #2364b4;\n --good-text: #4ec97a;\n --warn-text: #fab219;\n --crit-text: #ff8078;\n --info-text: #86b6ef;\n --proven-text: #b3a4f5;\n --grid: #23272e;\n --shadow: none;\n --focus: #86b6ef;\n}\n\n* { box-sizing: border-box; }\nhtml, body { height: 100%; }\nbody {\n margin: 0; background: var(--page); color: var(--ink);\n font: 14px/1.55 system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n -webkit-font-smoothing: antialiased;\n}\n.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.tnum { font-variant-numeric: tabular-nums; }\n:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 6px; }\n\n/* --- header -------------------------------------------------------------\n Two rows, and the split is the whole idea.\n\n The first carries identity, connection state and the controls — everything you\n can *press*. The second carries the tab strip and, at the far end, the handful of\n configuration facts that used to sit up top wearing the same bordered pill as the\n buttons beside them. Ten identical rounded rectangles in a row, five of which did\n nothing when clicked, is not a toolbar; it is a guessing game. The facts are now\n plain text — the one treatment nothing else on the page uses for a control — and\n they fill the tab strip's empty right-hand side rather than needing room of their\n own.\n\n Nothing here has a fixed height. The old row was pinned to 54px with children that\n refused to shrink, so on a narrow window the title and the live indicator drew on\n top of each other rather than wrapping.\n------------------------------------------------------------------------- */\nheader {\n position: sticky; top: 0; z-index: 20;\n background: color-mix(in srgb, var(--surface) 92%, transparent);\n backdrop-filter: saturate(180%) blur(8px);\n border-bottom: 1px solid var(--line);\n padding: 0 20px;\n}\n.head-row { display: flex; align-items: center; gap: 10px 14px; min-height: 54px; flex-wrap: wrap; padding: 7px 0; }\n.brand { display: flex; align-items: baseline; gap: 8px; min-width: 0; }\n/* A real top-level heading, because the page had none: a screen reader's \"jump to the\n heading\" found nothing to jump to. Styled back down to the size it always was — it is\n a name in a toolbar, not a title on a poster. */\n.brand h1 { font-size: 15px; font-weight: 640; letter-spacing: -.015em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; }\n.brand span { color: var(--muted); font-size: 12.5px; white-space: nowrap; }\n\n/* Status, not a control: a filled pill with no border, so it never reads as pressable. */\n.live {\n display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ink-2);\n white-space: nowrap; background: color-mix(in srgb, var(--ink) 5%, transparent);\n border-radius: 999px; padding: 3px 10px 3px 8px; font-weight: 520;\n}\n.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); flex: none; }\n.dot.on { background: var(--good-text); box-shadow: 0 0 0 3px color-mix(in srgb, var(--good-text) 22%, transparent); }\n.dot.off { background: var(--crit-text); box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit-text) 20%, transparent); }\n.grow { flex: 1 1 auto; min-width: 8px; }\n\n.head-links { display: flex; gap: 6px; flex-wrap: wrap; }\n/* A hairline between \"somewhere else\" and \"do something here\". */\n.head-actions { display: flex; gap: 6px; align-items: center; }\n.head-links:not(:empty) + .head-actions { padding-left: 12px; border-left: 1px solid var(--line); }\n\n.nav-row { display: flex; align-items: flex-end; gap: 20px; }\n.facts {\n display: flex; gap: 14px; flex-wrap: wrap; margin-left: auto;\n font-size: 11.5px; color: var(--muted); padding-bottom: 9px; min-width: 0;\n}\n.facts span { white-space: nowrap; }\n/* Sibling instances. A dashboard reports on one process, and this is the honest amount\n of help the page can give with that: a way to reach the others. */\n.peers { display: flex; gap: 6px; align-items: center; padding-bottom: 7px; flex-wrap: wrap; }\n.peers a { font-size: 11.5px; padding: 2px 8px; border-radius: 999px; }\n@media (max-width: 900px) { .peers { display: none; } }\n.facts b { font-weight: 600; color: var(--ink-2); font-variant-numeric: tabular-nums; }\n\n/* Secondary by definition, so it is the first thing to go when room runs out. */\n@media (max-width: 900px) { .facts { display: none; } }\n@media (max-width: 560px) { .brand span { display: none; } }\nbutton, .linkbtn {\n font: inherit; font-size: 12.5px; line-height: 1.4; padding: 6px 11px; border-radius: 8px;\n border: 1px solid var(--line); background: var(--surface); color: var(--ink-2);\n cursor: pointer; white-space: nowrap; text-decoration: none; display: inline-block;\n}\nbutton:hover, .linkbtn:hover { background: var(--surface-2); color: var(--ink); border-color: color-mix(in srgb, var(--ink) 22%, var(--line)); }\nbutton[aria-pressed=\"true\"] { background: color-mix(in srgb, var(--s1) 12%, var(--surface)); border-color: color-mix(in srgb, var(--s1) 45%, var(--line)); color: var(--ink); }\nbutton[disabled] { opacity: .5; cursor: default; }\n\n.tabs { display: flex; gap: 2px; margin-bottom: -1px; flex: none; }\n.tab {\n border: 0; background: none; border-bottom: 2px solid transparent; border-radius: 0;\n padding: 9px 12px; font-size: 13px; color: var(--muted); font-weight: 500;\n}\n.tab:hover { background: none; color: var(--ink); }\n.tab[aria-selected=\"true\"] { color: var(--ink); border-bottom-color: var(--s1); }\n\n/* --- layout ------------------------------------------------------------- */\nmain { padding: 18px 20px 64px; max-width: 1680px; margin: 0 auto; }\n.stack { display: grid; gap: 16px; }\n.tiles { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); }\n.tile {\n background: var(--surface); border: 1px solid var(--line); border-radius: 12px;\n padding: 12px 14px 13px; box-shadow: var(--shadow);\n}\n.tile .v { font-size: 26px; font-weight: 620; letter-spacing: -.025em; line-height: 1.1; }\n.tile .k { font-size: 11.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .055em; margin-top: 4px; font-weight: 560; }\n.tile .s { font-size: 11.5px; color: var(--muted); margin-top: 3px; }\n.tile.good .v { color: var(--good-text); }\n.tile.warn .v { color: var(--warn-text); }\n.tile.crit .v { color: var(--crit-text); }\n.tile.proven .v { color: var(--proven-text); }\n\n/* \"clip\" rather than \"hidden\", and the difference is load-bearing. Both round off the\n corners, but \"hidden\" makes the panel a scroll container — which becomes the\n containing block for anything sticky inside it, so the feed's column headers were\n anchored to a box that never scrolls and simply never stuck. They have not worked\n since they were written; the hard-coded offset above them was dead code. \"clip\"\n clips without creating a scrollport, so the headers stick to the viewport, under\n the page header, where they were always meant to. */\n.panel { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow); overflow: clip; }\n.panel > h2 {\n margin: 0; padding: 12px 15px; font-size: 12px; font-weight: 620; color: var(--muted);\n text-transform: uppercase; letter-spacing: .055em; border-bottom: 1px solid var(--line);\n display: flex; align-items: center; gap: 10px;\n}\n.panel > h2 .sub { font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 11.5px; margin-left: auto; }\n/* A container, so the two-column grid below can ask how much room it actually has.\n\n It used to ask the viewport, which is the same mistake the feed table's breakpoints\n made one level down: the constraint is the width of this column, not of the window.\n Embedded in a 320px sidebar on a 1280px screen the media query never fired, the second\n column held its 280px minimum, and the feed was squeezed to twenty-two pixels. */\n.stack { container: dash / inline-size; }\n.two { display: grid; gap: 16px; grid-template-columns: minmax(0, 1.9fr) minmax(280px, 1fr); align-items: start; }\n.grid3 { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }\n@container dash (max-width: 1080px) { .two { grid-template-columns: minmax(0, 1fr); } }\n\n/* The feed table has a floor — six columns of request text will not go below about\n 940px — and under it the panel was simply amputating the right-hand columns.\n\n These are **container** queries, not media queries, and the difference is the whole\n fix. What the table has to fit inside is the panel, and the panel is one column of a\n two-column grid: at a 1400px viewport it is about 880px wide, so a viewport-based\n breakpoint at 980px never fires and the right-hand columns are clipped on a perfectly\n ordinary desktop. That is exactly the bug a media query was written to fix, and the\n media query was measuring the wrong box the whole time. It only appeared to work\n because five columns happened to fit.\n\n Four things happen on the way down, in the order that costs least. First the\n duration goes: it is the column people narrow a window least to read and it is still\n in the row detail. Then the timestamp, which is also still in the row detail and is\n implied by the order while the stream is live. Then the User-Agent line stops\n ellipsing and wraps, which is what actually buys the room — it takes the table's floor\n from about 100ch to about 56ch, because the cap on that one line is most of what the\n table cannot go below. Only under *that* does the table get a scrollbar of its own.\n\n The scrollbar is deliberately last and deliberately rare, and it is worth being\n precise about why, because it is the rung that gets set too high. A scroll container\n is the containing block for anything sticky inside it, so the moment it turns on the\n column headers stop tracking the viewport — and a row becomes wider than the box it\n sits in, which is enough to make it hard to click and, for a keyboard or a pointer\n being driven by a test, hard to reach at all. It cost both when it fired at 1440px on\n a wider face: the headers scrolled away with the rows and a click on a row never\n landed. Wrapping costs a line of height and nothing else, so it goes first and this\n goes last.\n\n The thresholds are in **ch**, and that is the second half of the same lesson. They\n used to be pixels, which quietly assumed a font: what six columns of request text\n actually need is set by the width of a glyph — the User-Agent below is capped in ch —\n and a hard pixel breakpoint is only correct for the font it was measured on. On this\n machine the six columns needed 951px and the breakpoint sat at 960px, so it passed by\n nine pixels; on a CI runner with a wider default face the same six columns needed more\n than the panel had, the query did not fire, and the right-hand columns were clipped on\n an ordinary 1600px desktop. The five-column rung was already over its own floor by\n 8px and nobody had noticed, because nothing measures a layout that merely looks fine.\n\n In ch, both sides move together: a wider face makes the content wider and makes the\n threshold wider by the same proportion. The floors, measured, are 119ch / 111ch /\n 100ch ellipsing and 56ch wrapped, and each rung sits above its own floor with room to\n spare so that the arithmetic does not have to be exact. */\n.feed-panel { container: feed / inline-size; }\n\n/* The rungs themselves are further down, immediately after the table's own rules, and\n they have to be: a container query adds no specificity, so an override written above\n the declaration it overrides simply loses on source order and does nothing. The wrap\n rung sat here for exactly that reason and was dead the whole time — the table never\n wrapped, its floor never dropped, and nothing said so. See \"the column ladder\" below. */\n\n/* The header's own subtitle, which is a viewport thing rather than a panel thing: it\n sits in the page header and has no container to be measured against. */\n@media (max-width: 700px) {\n .sub { white-space: normal; max-width: none; }\n}\n\n/* --- feed table --------------------------------------------------------- */\n.toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 10px 15px; border-bottom: 1px solid var(--line); }\n.filters { display: flex; gap: 5px; flex-wrap: wrap; }\n.filters button { font-size: 11.5px; padding: 3px 10px; border-radius: 999px; }\ninput[type=\"search\"] {\n font: inherit; font-size: 12.5px; padding: 5px 10px; min-width: 180px; flex: 1 1 180px;\n border: 1px solid var(--line); border-radius: 8px; background: var(--surface-2); color: var(--ink);\n}\ninput[type=\"search\"]::placeholder { color: var(--muted); }\n\ntable { width: 100%; border-collapse: collapse; }\nthead th {\n /* Measured at runtime — see trackHeaderHeight(). The literal is the fallback for\n the instant before the first measurement, and for the tab strip wrapping. */\n position: sticky; top: var(--header-h, 91px); z-index: 2; background: var(--surface);\n text-align: left; font-size: 11px; font-weight: 600; color: var(--muted);\n text-transform: uppercase; letter-spacing: .05em; padding: 9px 15px; border-bottom: 1px solid var(--line);\n}\ntbody td { padding: 9px 15px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }\n/* Narrow, quiet, and never the reason a row wraps: the time is for scanning down, not\n for reading across. */\ntbody td.when { color: var(--muted); font-size: 11.5px; white-space: nowrap; width: 1%; padding-right: 4px; }\ntbody tr.row { cursor: pointer; }\ntbody tr.row:hover { background: color-mix(in srgb, var(--ink) 3.5%, transparent); }\ntbody tr.row.open { background: color-mix(in srgb, var(--s1) 7%, transparent); }\ntd.edge { border-left: 3px solid transparent; padding-left: 12px; }\ntr.a-allow td.edge { border-left-color: var(--good-text); }\ntr.a-mitigate td.edge { border-left-color: var(--s2); }\ntr.a-deny td.edge { border-left-color: var(--crit); }\ntr.a-guard td.edge { border-left-color: var(--warn-text); }\n.req { font-size: 12.5px; overflow-wrap: anywhere; }\n/* The disclosure for a row.\n The whole row used to be role=\"button\", with the actor link inside it — a control\n nested in a control, which leaves a screen reader with two things to announce and no\n way to say which one Enter belongs to. The row still opens on click, because that is\n a convenience rather than a role; the keyboard gets this, which is one control with\n one name and one job. */\n.row-toggle {\n border: 0; background: none; padding: 0; margin: 0; border-radius: 4px;\n font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12.5px; color: inherit; text-align: left; cursor: pointer; display: block; width: 100%;\n}\n.row-toggle:hover { background: none; color: inherit; border-color: transparent; }\n.req .ua { color: var(--muted); font-size: 11.5px; display: block; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 64ch; }\n.req .ua a { color: inherit; text-decoration: none; border-bottom: 1px dotted color-mix(in srgb, var(--muted) 60%, transparent); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n.req .ua a:hover { color: var(--ink); border-bottom-color: var(--ink); }\n.num { text-align: right; }\n\n/* The column ladder. Read the note beside .feed-panel above for what it is for and why\n the thresholds are in ch; this is where it has to live, below every declaration it\n overrides. */\n@container feed (max-width: 128ch) {\n thead th:nth-child(6), tbody td:nth-child(6) { display: none; }\n}\n@container feed (max-width: 119ch) {\n thead th:nth-child(1), tbody td.when { display: none; }\n}\n@container feed (max-width: 107ch) {\n .req .ua { white-space: normal; max-width: none; }\n}\n@container feed (max-width: 62ch) {\n .feed-scroll { overflow-x: auto; }\n}\n\n.badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; white-space: nowrap; border: 1px solid transparent; }\n.b-proven { background: color-mix(in srgb, var(--proven-text) 14%, transparent); color: var(--proven-text); }\n.b-suspected { background: color-mix(in srgb, var(--warn-text) 16%, transparent); color: var(--warn-text); }\n.b-human { background: color-mix(in srgb, var(--good-text) 14%, transparent); color: var(--good-text); }\n/* The secondary ink rather than the muted one, for the same reason as the weak tier:\n muted ink on its own muted chip is 4.31:1 on a plain row and 3.70:1 on an open one. */\n.b-unknown { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--ink-2); }\n.act { font-weight: 600; font-size: 12.5px; }\n.act-allow { color: var(--good-text); }\n.act-mitigate { color: var(--warn-text); }\n.act-deny { color: var(--crit-text); }\n.act-tag { color: var(--ink-2); }\n.sub { color: var(--muted); font-size: 11.5px; display: block; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 22ch; }\n.guard { color: var(--warn-text); font-size: 11.5px; display: block; margin-top: 3px; font-weight: 500; }\n\ntr.detail > td { background: var(--surface-2); padding: 14px 15px 16px; border-bottom: 1px solid var(--line); }\n.ev { display: grid; gap: 9px; }\n.ev-item { display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 12px; align-items: start; font-size: 12.5px; }\n.tier { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; padding: 3px 6px; border-radius: 6px; text-align: center; }\n.t-certain { background: color-mix(in srgb, var(--proven-text) 15%, transparent); color: var(--proven-text); }\n.t-strong { background: color-mix(in srgb, var(--crit-text) 14%, transparent); color: var(--crit-text); }\n.t-moderate { background: color-mix(in srgb, var(--warn-text) 16%, transparent); color: var(--warn-text); }\n/* The secondary ink rather than the muted one: muted ink on its own muted chip clears\n 4.5:1 on the light surface and does not on the dark one, and this is 10px bold text\n saying how much a piece of evidence is worth. It still reads as the quiet tier — the\n only one with no hue — without being the one nobody can read at night. */\n.t-weak { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--ink-2); }\n.ev-item.human .tier { background: color-mix(in srgb, var(--good-text) 14%, transparent); color: var(--good-text); }\n.ev-meta { color: var(--muted); font-size: 11.5px; margin-top: 2px; }\n.basis { margin-top: 5px; color: var(--ink-2); font-size: 12px; border-left: 2px solid color-mix(in srgb, var(--proven-text) 40%, transparent); padding: 2px 0 2px 9px; }\n.detail-foot { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--line); color: var(--muted); font-size: 11.5px; display: flex; gap: 14px; flex-wrap: wrap; }\n\n/* --- bars --------------------------------------------------------------- */\n.bars { padding: 12px 15px 14px; display: grid; gap: 8px; }\n.bar { display: grid; grid-template-columns: minmax(0, 1fr) 46px; gap: 10px; align-items: center; font-size: 12.5px; }\n.bar .track { position: relative; height: 20px; background: color-mix(in srgb, var(--ink) 5%, transparent); border-radius: 6px; overflow: hidden; }\n.bar .fill { position: absolute; inset: 0 auto 0 0; background: color-mix(in srgb, var(--s1) 26%, transparent); border-radius: 0 5px 5px 0; }\n.bar .lbl { position: relative; padding: 0 8px; line-height: 20px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.bar .v { text-align: right; color: var(--ink-2); font-size: 12px; }\n\n/* --- charts ------------------------------------------------------------- */\n.chart { padding: 12px 15px 14px; position: relative; }\n.chart svg { display: block; width: 100%; overflow: visible; }\n.legend { display: flex; gap: 14px; flex-wrap: wrap; padding: 0 15px 12px; font-size: 12px; color: var(--ink-2); }\n.legend span { display: inline-flex; align-items: center; gap: 6px; }\n/* The marker for a runtime change. A line rather than a swatch, because it is an\n instant rather than a quantity. */\n.legend .mark { width: 2px; height: 11px; background: var(--proven-text); border-radius: 1px; flex: none; }\n.swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }\n.tip {\n position: absolute; pointer-events: none; opacity: 0; transition: opacity .1s;\n background: var(--raised); border: 1px solid var(--line); border-radius: 8px;\n padding: 8px 10px; font-size: 12px; box-shadow: 0 4px 16px rgba(0,0,0,.16); z-index: 10; min-width: 128px;\n}\n.tip .t { color: var(--muted); font-size: 11px; margin-bottom: 4px; }\n.tip .r { display: flex; align-items: center; gap: 7px; justify-content: space-between; }\n.tip .r em { font-style: normal; color: var(--ink-2); display: inline-flex; align-items: center; gap: 6px; }\n.tip .r b { font-weight: 600; font-variant-numeric: tabular-nums; }\n\n.stat-row { display: flex; justify-content: space-between; gap: 12px; padding: 7px 15px; font-size: 12.5px; border-bottom: 1px solid var(--line-soft); }\n.stat-row:last-child { border-bottom: 0; }\n.stat-row .k { color: var(--muted); }\n.stat-row .v { font-variant-numeric: tabular-nums; color: var(--ink); font-weight: 560; }\n/* The baseline a window is being read against. Quieter than the current value, and\n beside it rather than under it, because the comparison is the whole point. */\n.stat-row .v .was { color: var(--muted); font-weight: 400; font-size: 11.5px; }\n.note { padding: 13px 15px; font-size: 12.5px; color: var(--ink-2); }\n.note p { margin: 0 0 9px; }\n.note p:last-child { margin: 0; }\n.note b { color: var(--ink); }\n.empty { padding: 40px 15px; text-align: center; color: var(--muted); font-size: 13px; }\n.empty code { background: color-mix(in srgb, var(--ink) 7%, transparent); padding: .15em .4em; border-radius: 5px; }\n.detectors { padding: 4px 0; }\n.det { display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 10px; padding: 8px 15px; border-bottom: 1px solid var(--line-soft); font-size: 12.5px; }\n.det:last-child { border-bottom: 0; }\n.det .d { color: var(--muted); font-size: 11.5px; margin-top: 2px; }\n.det .n { color: var(--ink-2); font-variant-numeric: tabular-nums; white-space: nowrap; }\n.rules { padding: 10px 15px; display: flex; gap: 6px; flex-wrap: wrap; }\n.rules .chip { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n[hidden] { display: none !important; }\n/* Present to a screen reader, absent to everyone else. Clipped rather than moved\n off-screen or hidden with display:none, both of which take an element out of the\n accessibility tree along with the viewport. */\n.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; white-space: nowrap; clip-path: inset(50%); border: 0; }\n\n/* --- navigation --------------------------------------------------------- */\n/* Present for a keyboard, out of the way of everything else. */\n.skip {\n position: absolute; left: 10px; top: 10px; z-index: 40;\n background: var(--raised); color: var(--ink); border: 1px solid var(--line);\n border-radius: 8px; padding: 8px 13px; font-size: 12.5px; text-decoration: none;\n /* Hidden by clipping rather than by being moved off-screen. An element parked\n above the viewport has its whole border box outside it, and Chrome's sequential\n focus navigation skips those — so a skip link done that way is invisible to the\n one input device it exists for. Clipped to a pixel it stays in the tab order. */\n width: 1px; height: 1px; padding: 0; overflow: hidden; white-space: nowrap; clip-path: inset(50%);\n}\n.skip:focus { width: auto; height: auto; padding: 8px 13px; overflow: visible; clip-path: none; }\n\n/* A row is a control now, so it has to look like one when focused. outline-offset\n is negative because an outline drawn outside a table row is clipped by the cell. */\ntbody tr.row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }\ntbody tr.row:focus-visible td.edge { border-left-color: var(--focus); }\n\n.search { position: relative; display: flex; flex: 1 1 180px; min-width: 180px; }\n.search input[type=\"search\"] { flex: 1 1 auto; padding-right: 26px; }\n.search kbd {\n position: absolute; right: 7px; top: 50%; transform: translateY(-50%);\n font: 500 10.5px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n color: var(--muted); background: color-mix(in srgb, var(--ink) 6%, transparent);\n border: 1px solid var(--line); border-radius: 4px; padding: 3px 5px; pointer-events: none;\n}\n/* The hint is an affordance for a mouse user who has not found the key yet. Once the\n box is in use it is noise, and it sits where the clear button wants to be. */\n.search input[type=\"search\"]:focus + kbd, .search input[type=\"search\"]:not(:placeholder-shown) + kbd { display: none; }\n/* --- added panels ------------------------------------------------------- */\n.range { display: flex; gap: 4px; }\n.range button { font-size: 11px; padding: 2px 8px; border-radius: 999px; }\n\n.kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; padding: 12px 15px; font-size: 12.5px; }\n.kv dt { color: var(--muted); }\n.kv dd { margin: 0; font-variant-numeric: tabular-nums; }\n\n.hdr { width: 100%; border-collapse: collapse; margin-top: 10px; }\n.hdr td { padding: 3px 8px 3px 0; vertical-align: top; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11.5px; border: 0; }\n.hdr td.n { color: var(--muted); white-space: nowrap; width: 1%; }\n.hdr td.v { overflow-wrap: anywhere; }\n.hdr td.v.red { color: var(--muted); font-style: italic; }\n\n.tools { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px; }\n.tools button { font-size: 11.5px; padding: 4px 9px; }\n\n.editor { padding: 12px 15px 14px; display: grid; gap: 10px; }\ntextarea {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.5;\n width: 100%; min-height: 320px; resize: vertical; padding: 11px 12px; tab-size: 2;\n border: 1px solid var(--line); border-radius: 9px; background: var(--surface-2); color: var(--ink);\n}\ntextarea[readonly] { opacity: .8; }\n.editor-bar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.result { font-size: 12.5px; border-radius: 9px; padding: 10px 12px; border: 1px solid var(--line); background: var(--surface-2); }\n.result.ok { border-color: color-mix(in srgb, var(--good-text) 45%, var(--line)); }\n.result.bad { border-color: color-mix(in srgb, var(--crit-text) 45%, var(--line)); color: var(--crit-text); }\n.result.warn { border-color: color-mix(in srgb, var(--warn-text) 45%, var(--line)); }\n.result b { font-weight: 620; }\n.diff { width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 12px; }\n.diff th { text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: 4px 8px 4px 0; border-bottom: 1px solid var(--line); }\n.diff td { padding: 5px 8px 5px 0; border-bottom: 1px solid var(--line-soft); vertical-align: top; }\n.diff .from { color: var(--muted); }\n.diff .to { font-weight: 600; }\n.diff .to.deny { color: var(--crit-text); }\n.diff .to.allow { color: var(--good-text); }\npre.code {\n margin: 0; padding: 11px 12px; border-radius: 9px; border: 1px solid var(--line);\n background: var(--surface-2); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11.5px; line-height: 1.5; overflow-x: auto; white-space: pre; color: var(--ink-2);\n}\n.notice { display: grid; grid-template-columns: 74px 1fr; gap: 10px; padding: 8px 15px; border-bottom: 1px solid var(--line-soft); font-size: 12.5px; }\n.notice:last-child { border-bottom: 0; }\n.notice .when { color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }\n.notice.warning .tag { color: var(--warn-text); font-weight: 600; }\n.notice.error .tag { color: var(--crit-text); font-weight: 600; }\n.dead { color: var(--muted); }\n.dead .lbl { text-decoration: line-through; }\n.pill { font-size: 10.5px; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--line); color: var(--muted); margin-left: 6px; }\n.actor-head { display: flex; align-items: center; gap: 10px; padding: 12px 15px; border-bottom: 1px solid var(--line); }\n.actor-head .who { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; font-weight: 600; }\n/* --- the rule editor ----------------------------------------------------- */\n.bar-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; padding: 10px 15px; border-bottom: 1px solid var(--line); background: var(--surface); }\n.bar-actions + .bar-actions, .rulelist + .bar-actions { border-bottom: 0; border-top: 1px solid var(--line); }\n.bar-actions .grow { flex: 1 1 auto; }\nbutton.primary { background: var(--accent-solid); border-color: var(--accent-solid); color: #fff; font-weight: 560; }\nbutton.primary:hover { background: color-mix(in srgb, var(--accent-solid) 86%, #000); border-color: transparent; color: #fff; }\nbutton.danger { color: var(--crit-text); }\nbutton.danger:hover { border-color: color-mix(in srgb, var(--crit-text) 50%, var(--line)); }\nbutton.icon { padding: 3px 8px; font-size: 12px; line-height: 1.2; }\n.dirty { font-size: 11.5px; color: var(--warn-text); font-weight: 560; display: inline-flex; align-items: center; gap: 6px; }\n.dirty::before { content: \"\"; width: 7px; height: 7px; border-radius: 50%; background: var(--warn-text); }\n\n.rulelist { display: grid; gap: 12px; padding: 14px 15px 16px; }\n.rule {\n border: 1px solid var(--line); border-radius: 11px; background: var(--surface-2);\n transition: border-color .12s, box-shadow .12s;\n}\n.rule:focus-within { border-color: color-mix(in srgb, var(--s1) 55%, var(--line)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--s1) 12%, transparent); }\n.rule.locked { background: color-mix(in srgb, var(--ink) 3%, var(--surface)); }\n.rule-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; flex-wrap: nowrap; }\n.rule:not(.collapsed) .rule-head { border-bottom: 1px solid var(--line); }\n.rule-head .ord { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; min-width: 20px; text-align: right; }\n.rule-head input[type=\"text\"] { flex: 0 1 240px; width: auto; min-width: 130px; }\n.rule-head select { flex: 0 0 auto; }\n.chev { border: 0; background: none; padding: 2px 4px; color: var(--muted); font-size: 11px; line-height: 1; }\n.chev:hover { background: none; color: var(--ink); }\n.rule-summary { flex: 1 1 auto; display: flex; gap: 5px; flex-wrap: wrap; align-items: center; min-width: 0; overflow: hidden; }\n.rule-summary .k { font-size: 10.5px; color: var(--muted); }\n.rule-summary .t { font-size: 10.5px; padding: 1px 6px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 6%, transparent); color: var(--ink-2); white-space: nowrap; }\n.rule-summary .none { font-size: 11px; color: var(--muted); font-style: italic; }\n.act-pill { font-size: 11px; font-weight: 620; padding: 2px 9px; border-radius: 999px; white-space: nowrap; }\n.act-pill.deny { background: color-mix(in srgb, var(--crit-text) 14%, transparent); color: var(--crit-text); }\n.act-pill.mitigate { background: color-mix(in srgb, var(--warn-text) 16%, transparent); color: var(--warn-text); }\n.act-pill.allow { background: color-mix(in srgb, var(--good-text) 14%, transparent); color: var(--good-text); }\n.act-pill.tag { background: color-mix(in srgb, var(--ink) 8%, transparent); color: var(--ink-2); }\n.rule-body { padding: 11px 12px 13px; display: grid; gap: 11px; }\n.rule.collapsed .rule-body { display: none; }\n.field { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 10px; align-items: start; font-size: 12.5px; }\n.field > label { color: var(--muted); padding-top: 5px; }\n.field-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }\n.hint { color: var(--muted); font-size: 11px; }\n\ninput[type=\"text\"], input[type=\"number\"], select {\n font: inherit; font-size: 12.5px; padding: 5px 9px; border-radius: 7px;\n border: 1px solid var(--line); background: var(--surface); color: var(--ink); min-width: 0;\n}\ninput[type=\"text\"] { width: 100%; }\ninput[type=\"number\"] { width: 92px; }\nselect { cursor: pointer; }\ninput::placeholder { color: color-mix(in srgb, var(--muted) 80%, transparent); }\n.mono-input { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n\n.chips-select { display: flex; gap: 4px; flex-wrap: wrap; }\n.chips-select button {\n font-size: 11px; padding: 2px 8px; border-radius: 999px; color: var(--muted);\n background: var(--surface); border: 1px solid var(--line);\n}\n.chips-select button[aria-pressed=\"true\"] { background: color-mix(in srgb, var(--s1) 14%, var(--surface)); border-color: color-mix(in srgb, var(--s1) 50%, var(--line)); color: var(--ink); font-weight: 560; }\n\n.seg { display: inline-flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }\n.seg button { border: 0; border-radius: 0; padding: 4px 10px; font-size: 11.5px; background: var(--surface); }\n.seg button + button { border-left: 1px solid var(--line); }\n.seg button[aria-pressed=\"true\"] { background: color-mix(in srgb, var(--s1) 14%, var(--surface)); color: var(--ink); font-weight: 560; }\n\n.drop { outline: 2px dashed color-mix(in srgb, var(--s1) 60%, transparent); outline-offset: -6px; }\n\n/* --- the actors table, the tester and the range sets --------------------- */\n.warn-text { color: var(--warn-text); }\n#actor-rows td { font-size: 12.5px; }\n#actor-rows td.who { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; overflow-wrap: anywhere; }\n#actor-rows td.acts { text-align: right; white-space: nowrap; }\n#actor-rows td.acts button { font-size: 11px; padding: 3px 8px; margin-left: 4px; }\n/* A tag, not a warning: a cleared actor is a decision somebody made, and a metronomic\n one is a measurement. Neither is a verdict, so neither gets a verdict's colour. */\n.tagline { font-size: 11px; color: var(--muted); }\n.tagline b { color: var(--ink-2); font-weight: 600; }\n\n#test-input { min-height: 82px; }\n#tester-panel .editor { gap: 8px; }\n#tester-panel .field-row { gap: 6px; }\n#tester-panel input[type=\"text\"] { flex: 1 1 120px; min-width: 90px; }\n.assumed { color: var(--muted); font-size: 11px; margin-top: 6px; }\n\n.rangeset { border-bottom: 1px solid var(--line-soft); padding: 10px 15px; }\n.rangeset:last-child { border-bottom: 0; }\n.rangeset h3 { margin: 0 0 6px; font-size: 12.5px; font-weight: 620; display: flex; align-items: baseline; gap: 8px; }\n.rangeset h3 span { font-weight: 400; color: var(--muted); font-size: 11.5px; }\n.cidrs { display: flex; gap: 5px; flex-wrap: wrap; }\n.cidr {\n display: inline-flex; align-items: center; gap: 5px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px; background: color-mix(in srgb, var(--ink) 6%, transparent); border-radius: 999px; padding: 2px 4px 2px 9px;\n}\n.cidr button { border: 0; background: none; padding: 0 4px; font-size: 12px; line-height: 1; color: var(--muted); border-radius: 999px; }\n.cidr button:hover { background: none; color: var(--crit-text); }\n.cidr.readonly { padding-right: 9px; }\n.toasts { position: fixed; right: 18px; bottom: 18px; z-index: 60; display: grid; gap: 8px; max-width: 380px; }\n.toast {\n background: var(--raised); border: 1px solid var(--line); border-left: 3px solid var(--s1);\n border-radius: 9px; padding: 10px 12px; font-size: 12.5px; box-shadow: 0 6px 22px rgba(0,0,0,.18);\n animation: toast-in .16s ease-out;\n}\n.toast.ok { border-left-color: var(--good-text); }\n.toast.bad { border-left-color: var(--crit-text); }\n.toast.warn { border-left-color: var(--warn-text); }\n.toast b { display: block; margin-bottom: 2px; }\n.toast span { color: var(--muted); }\n@keyframes toast-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }\n@media (prefers-reduced-motion: reduce) { .toast { animation: none; } * { transition: none !important; } }\n\n/* Forced colours — Windows High Contrast and the like.\n The browser repaints text, backgrounds and borders from the user's palette but leaves\n SVG fill and stroke alone, which is the right outcome for the two series: they stay\n tellable apart instead of collapsing into one system colour. The gridlines are the\n casualty. They are drawn in --grid, a colour picked to recede against *this* dashboard's\n background, and against a forced black one they recede to 1.4:1 — measured, on the\n statistics charts — which is not recessive, it is gone. GrayText is the palette's own\n answer to \"present but secondary\", so the grid follows the user's colours while the data\n keeps the ones that carry meaning. */\n@media (forced-colors: active) {\n .gridline { stroke: GrayText; }\n}`;\n\n/**\n * Everything between `<body>` and the boot script.\n *\n * The standalone page and the embedded element render identical markup; only what they\n * render it *into* differs.\n */\nexport const DASHBOARD_MARKUP = String.raw`\n<a class=\"skip\" href=\"#view-live\">Skip to the feed</a>\n\n<header>\n <div class=\"head-row\">\n <div class=\"brand\"><h1 id=\"title\">__TITLE__</h1> <span>bot dashboard</span></div>\n <output class=\"live\" id=\"live\" aria-live=\"polite\"><span class=\"dot\" id=\"dot\"></span><span id=\"conn\">connecting…</span></output>\n <div class=\"grow\"></div>\n <nav class=\"head-links\" id=\"links\" aria-label=\"Related\"></nav>\n <div class=\"head-actions\">\n <button id=\"theme\" aria-label=\"Switch between light and dark\">Theme</button>\n <button id=\"pause\" aria-pressed=\"false\">Pause</button>\n <button id=\"reset\" class=\"danger\" hidden>Reset</button>\n </div>\n </div>\n <div class=\"nav-row\">\n <div class=\"tabs\" role=\"tablist\" aria-label=\"Dashboard views\">\n <button class=\"tab\" id=\"tab-live\" role=\"tab\" aria-selected=\"true\" aria-controls=\"view-live\">Live feed</button>\n <button class=\"tab\" id=\"tab-actors\" role=\"tab\" aria-selected=\"false\" aria-controls=\"view-actors\">Actors</button>\n <button class=\"tab\" id=\"tab-stats\" role=\"tab\" aria-selected=\"false\" aria-controls=\"view-stats\">Statistics</button>\n <button class=\"tab\" id=\"tab-policy\" role=\"tab\" aria-selected=\"false\" aria-controls=\"view-policy\">Policy<span id=\"notice-badge\" class=\"pill\" hidden></span></button>\n </div>\n <div class=\"facts\" id=\"chips\"></div>\n <div class=\"peers\" id=\"peers\"></div>\n </div>\n</header>\n\n<main class=\"stack\">\n <section class=\"tiles\" id=\"tiles\"></section>\n\n <div id=\"view-live\" role=\"tabpanel\" aria-labelledby=\"tab-live\" class=\"stack\">\n <section class=\"panel\" id=\"actor-panel\" hidden>\n <div class=\"actor-head\">\n <span class=\"who\" id=\"actor-key\"></span>\n <span class=\"grow\"></span>\n <button id=\"actor-close\">Close</button>\n </div>\n <div class=\"two\">\n <dl class=\"kv\" id=\"actor-stats\"></dl>\n <div class=\"bars\" id=\"actor-mix\"></div>\n </div>\n <div class=\"bar-actions\" id=\"actor-actions\"></div>\n </section>\n\n <div class=\"two\">\n <section class=\"panel feed-panel\">\n <h2>Requests <span class=\"sub\" id=\"feed-count\"></span><span class=\"sub win\" id=\"feed-window\"></span><span class=\"sub warn-text\" id=\"feed-skipped\" hidden></span></h2>\n <div class=\"toolbar\">\n <div class=\"filters\" id=\"filters\"></div>\n <div class=\"search\">\n <input type=\"search\" id=\"search\" placeholder=\"path:/api actor:203.0.113.4 -rule:allow-crawlers\" spellcheck=\"false\" autocomplete=\"off\">\n <kbd aria-hidden=\"true\">/</kbd>\n </div>\n <button id=\"feed-export\" title=\"Download every request matching this filter as replay JSONL\">Export</button>\n </div>\n <div class=\"feed-scroll\">\n <table>\n <thead>\n <tr>\n <th class=\"num\">Time</th><th>Request</th><th>Verdict</th><th class=\"num\">Score</th><th>Action</th><th class=\"num\">ms</th>\n </tr>\n </thead>\n <tbody id=\"rows\"></tbody>\n </table>\n </div>\n <div class=\"empty\" id=\"empty\">Nothing assessed yet. Send some traffic through the handler and it appears here within a moment.</div>\n </section>\n\n <div class=\"stack\">\n <section class=\"panel\">\n <h2>Detectors firing <span class=\"sub win\"></span></h2>\n <div class=\"bars\" id=\"live-detectors\"></div>\n </section>\n <section class=\"panel\" id=\"live-actors-panel\">\n <h2>Busiest actors <span class=\"sub win\"></span></h2>\n <div class=\"bars\" id=\"live-actors\"></div>\n </section>\n <section class=\"panel\" id=\"tester-panel\">\n <h2>Test a request <span class=\"sub\">nothing is recorded</span></h2>\n <div class=\"editor\">\n <textarea id=\"test-input\" rows=\"4\" spellcheck=\"false\" autocomplete=\"off\" aria-label=\"A User-Agent, a curl command, or a block of request headers\"\n placeholder=\"Paste a User-Agent, a curl command, or a block of request headers\"></textarea>\n <div class=\"field-row\">\n <input type=\"text\" id=\"test-ip\" class=\"mono-input\" placeholder=\"client address (optional)\" aria-label=\"Client address\">\n <input type=\"text\" id=\"test-url\" class=\"mono-input\" placeholder=\"/path (optional)\" aria-label=\"Path\">\n <button id=\"test-run\" class=\"primary\">Assess</button>\n </div>\n <div class=\"result\" id=\"test-result\" hidden></div>\n </div>\n </section>\n\n <section class=\"panel\" id=\"evidence-legend\">\n <h2>Reading this</h2>\n <div class=\"note\">\n <p><b>Purple</b> evidence is <em>proven</em>: a self-declaration, a refuted\n identity, a trap, a protocol violation. Only proven evidence may deny anybody.</p>\n <p><b>Red, amber and grey</b> are probabilistic. They accumulate into a score\n and may tag, delay, rate-limit or challenge — never block.</p>\n <p>An <b>amber left edge</b> is the safety guard stopping a rule: the policy\n asked for a terminal action the evidence could not support, and got the\n fallback instead. Those rows are the library working, not failing.</p>\n </div>\n </section>\n </div>\n </div>\n </div>\n\n <div id=\"view-actors\" role=\"tabpanel\" aria-labelledby=\"tab-actors\" class=\"stack\" hidden>\n <section class=\"panel\">\n <h2>Actors in the registry <span class=\"sub\" id=\"actors-count\"></span></h2>\n <div class=\"note\" id=\"actors-note\">Everyone the engine is currently remembering, busiest first — a far larger\n population than the feed's ring, which holds requests rather than clients. This is\n what <code>cadence</code>, <code>crawl-breadth</code> and <code>rate-anomaly</code> are reading.</div>\n <div class=\"feed-scroll\">\n <table>\n <thead>\n <tr>\n <th>Actor</th><th class=\"num\">Requests</th><th class=\"num\">Per min</th><th class=\"num\">Paths</th>\n <th class=\"num\">Cadence</th><th class=\"num\">Proven</th><th class=\"num\">Unsolved</th><th>State</th><th><span class=\"sr-only\">Actions</span></th>\n </tr>\n </thead>\n <tbody id=\"actor-rows\"></tbody>\n </table>\n </div>\n <div class=\"empty\" id=\"actors-empty\" hidden>Nothing in the registry yet.</div>\n </section>\n </div>\n\n <div id=\"view-stats\" role=\"tabpanel\" aria-labelledby=\"tab-stats\" class=\"stack\" hidden>\n <div class=\"two\">\n <section class=\"panel\">\n <h2>Traffic <span class=\"range\" id=\"traffic-range\"></span><span class=\"sub\" id=\"traffic-window\"></span></h2>\n <div class=\"chart\" id=\"traffic-chart\"><svg id=\"traffic\" role=\"img\" aria-labelledby=\"traffic-alt\"></svg><div class=\"tip\" id=\"traffic-tip\"></div></div>\n <p class=\"sr-only\" id=\"traffic-alt\"></p>\n <div class=\"legend\" id=\"traffic-legend\"></div>\n </section>\n <section class=\"panel\">\n <h2>Assessment latency <span class=\"sub\">since start</span></h2>\n <div class=\"chart\" id=\"latency-chart\"><svg id=\"latency\" role=\"img\" aria-labelledby=\"latency-alt\"></svg><div class=\"tip\" id=\"latency-tip\"></div></div>\n <p class=\"sr-only\" id=\"latency-alt\"></p>\n <div class=\"legend\"><span id=\"latency-summary\"></span></div>\n </section>\n </div>\n\n <div class=\"two\">\n <section class=\"panel\">\n <h2>Score distribution <span class=\"range\" id=\"score-scope\"></span><span class=\"sub\" id=\"score-window\"></span></h2>\n <div class=\"chart\" id=\"score-chart\"><svg id=\"scores\" role=\"img\" aria-labelledby=\"score-alt\"></svg><div class=\"tip\" id=\"score-tip\"></div></div>\n <p class=\"sr-only\" id=\"score-alt\"></p>\n <div class=\"legend\" id=\"score-legend\"></div>\n </section>\n <section class=\"panel\">\n <h2>Guard stops by rule <span class=\"sub win\"></span></h2>\n <div class=\"bars\" id=\"stat-guard\"></div>\n </section>\n </div>\n\n <div class=\"grid3\">\n <section class=\"panel\"><h2>Verdicts <span class=\"sub\">since start</span></h2><div class=\"bars\" id=\"stat-verdicts\"></div></section>\n <section class=\"panel\"><h2>Actions taken <span class=\"sub\">since start</span></h2><div class=\"bars\" id=\"stat-actions\"></div></section>\n <section class=\"panel\"><h2>Bot classes <span class=\"sub\">since start</span></h2><div class=\"bars\" id=\"stat-classes\"></div></section>\n </div>\n\n <div class=\"grid3\">\n <section class=\"panel\"><h2>Detector firings <span class=\"sub\">since start</span></h2><div class=\"bars\" id=\"stat-detectors\"></div></section>\n <section class=\"panel\">\n <h2>Challenges</h2>\n <div id=\"stat-challenges\"></div>\n </section>\n <section class=\"panel\">\n <h2>Health</h2>\n <div id=\"stat-health\"></div>\n </section>\n </div>\n\n <div class=\"two\">\n <section class=\"panel\" id=\"audit-panel\">\n <h2>Audit <span class=\"sub\" id=\"audit-spans\"></span></h2>\n <div id=\"audit-body\"></div>\n </section>\n <section class=\"panel\" id=\"audit-checks-panel\">\n <h2>Checks installed</h2>\n <div class=\"detectors\" id=\"audit-checks\"></div>\n </section>\n </div>\n\n <div class=\"grid3\">\n <section class=\"panel\" id=\"identities-panel\"><h2>Identities seen <span class=\"sub win\"></span></h2><div class=\"bars\" id=\"stat-identities\"></div></section>\n <section class=\"panel\"><h2>Busiest paths <span class=\"sub win\"></span></h2><div class=\"bars\" id=\"stat-paths\"></div></section>\n <section class=\"panel\"><h2>Denied paths <span class=\"sub win\"></span></h2><div class=\"bars\" id=\"stat-denied-paths\"></div></section>\n </div>\n\n <div class=\"two\">\n <section class=\"panel\">\n <h2>Installed detectors <span class=\"sub\" id=\"detector-count\"></span></h2>\n <div class=\"detectors\" id=\"stat-detector-list\"></div>\n </section>\n <div class=\"stack\">\n <section class=\"panel\">\n <h2>Rules that fired <span class=\"sub win\"></span></h2>\n <div class=\"bars\" id=\"stat-rule-hits\"></div>\n </section>\n <section class=\"panel\">\n <h2>Bypassed <span class=\"sub\">detection never ran</span></h2>\n <div class=\"bars\" id=\"stat-bypassed\"></div>\n </section>\n </div>\n </div>\n </div>\n\n <div id=\"view-policy\" role=\"tabpanel\" aria-labelledby=\"tab-policy\" class=\"stack\" hidden>\n <div class=\"two\">\n <section class=\"panel\" id=\"editor-panel\">\n <h2>Rules <span class=\"sub\" id=\"policy-mode\"></span></h2>\n <div class=\"bar-actions\">\n <div class=\"seg\" id=\"editor-mode\">\n <button id=\"mode-gui\" aria-pressed=\"true\">Editor</button>\n <button id=\"mode-json\" aria-pressed=\"false\">JSON</button>\n </div>\n <button id=\"policy-preview\">Preview</button>\n <button id=\"policy-apply\" class=\"primary\" hidden>Apply</button>\n <button id=\"policy-revert\">Revert</button>\n <span class=\"grow\"></span>\n <button id=\"policy-import\">Import…</button>\n <button id=\"policy-export\">Export</button>\n <span class=\"dirty\" id=\"policy-dirty\" hidden>unsaved</span>\n </div>\n <div id=\"editor-gui\">\n <div class=\"rulelist\" id=\"rulelist\"></div>\n <div class=\"bar-actions\">\n <button id=\"rule-add\">+ Add rule</button>\n <button id=\"rule-expand\">Expand all</button>\n <span class=\"hint\" id=\"policy-note\"></span>\n </div>\n </div>\n <div class=\"editor\" id=\"editor-json\" hidden>\n <textarea id=\"policy-json\" spellcheck=\"false\" autocomplete=\"off\" aria-label=\"Policy rules as JSON\"></textarea>\n </div>\n <div class=\"editor\"><div class=\"result\" id=\"policy-result\" hidden></div></div>\n <input type=\"file\" id=\"policy-file\" accept=\"application/json,.json\" hidden>\n </section>\n\n <div class=\"stack\">\n <section class=\"panel\" id=\"guard-panel\">\n <h2>Guard <span class=\"sub\" id=\"guard-mode\"></span></h2>\n <div id=\"stat-policy\"></div>\n <div class=\"note\" id=\"guard-note\"></div>\n </section>\n <section class=\"panel\" id=\"ranges-panel\">\n <h2>Range sets <span class=\"sub\" id=\"ranges-mode\"></span></h2>\n <div id=\"ranges-body\"></div>\n <div class=\"note\" id=\"ranges-note\"></div>\n </section>\n <section class=\"panel\">\n <h2>Preview a shipped preset</h2>\n <div class=\"rules\" id=\"preset-buttons\"></div>\n <div class=\"note\">Runs the preset against the requests still in the window and shows what would change. Nothing is applied.</div>\n </section>\n <section class=\"panel\" id=\"changes-panel\">\n <h2>Changes <span class=\"sub\" id=\"change-count\"></span></h2>\n <div id=\"stat-changes\"></div>\n <div class=\"note\">What was applied to this process at runtime, and by whom when the dashboard's\n <code>auth</code> could say. Bounded and in memory: the durable copy is the\n <code>policy-change</code>, <code>guard-change</code>, <code>range-change</code> and\n <code>actor-change</code> events.</div>\n </section>\n <section class=\"panel\" id=\"notices-panel\">\n <h2>Notices <span class=\"sub\" id=\"notice-count\"></span></h2>\n <div id=\"stat-notices\"></div>\n </section>\n </div>\n </div>\n\n <div class=\"two\">\n <section class=\"panel\">\n <h2>Rules, in evaluation order</h2>\n <div class=\"rules\" id=\"stat-rules\"></div>\n </section>\n <section class=\"panel\" id=\"robots-panel\">\n <h2>robots.txt this policy implies</h2>\n <div class=\"editor\"><pre class=\"code\" id=\"robots-preview\"></pre><div id=\"robots-notes\"></div></div>\n </section>\n </div>\n </div>\n</main>\n\n<div class=\"toasts\" id=\"toasts\" aria-live=\"polite\"></div>\n`;\n\nconst PAGE = String.raw`<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"robots\" content=\"noindex, nofollow\">\n<title>__TITLE__ · bot dashboard</title>\n<style nonce=\"__NONCE__\">\n${DASHBOARD_CSS}\n</style>\n</head>\n<body>\n${DASHBOARD_MARKUP}\n<script nonce=\"__NONCE__\">\nwindow.__BOOTSTRAP__ = JSON.parse(__BOOT_JSON__);\n__SCRIPT__\n</script>\n</body>\n</html>\n`;\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgoB7B,IAAM,mBAAmB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["field","oldestAt","clear","label","run","pending","timer","label","label","label","segmented","label","rendered","label","draft","label","notice","label","start","raw","raw","label","raw","timer","dom","label"]}
|