UNPKG

@humanspeak/svelte-markdown

Version:

Markdown and HTML renderer for Svelte 5 — built for rendering streaming AI agent output from Claude Code, ChatGPT, and agentic workflows. XSS-safe defaults, streaming-aware sanitization, token caching, TypeScript types, and Svelte 5 runes.

77 lines (76 loc) 3.29 kB
/** * Tail-window safety markers for extension tokenizers. * * The streaming `IncrementalParser` normally disables its tail-window (which * re-lexes only the appended tail and reuses a stable token prefix) the moment * any extension tokenizer is registered, because a tokenizer whose output * depends on prefix content would produce wrong tokens when the prefix is not * re-lexed. Tokenizers carrying the marker defined here promise to be * **block-anchored and stateless** — they inspect only `src` from the current * position, exactly like Marked's built-in block rules — so re-lexing the tail * from a stable block boundary is guaranteed to match a full re-lex. * * Marked's `use()` stores the tokenizer **function reference** in * `options.extensions.block` / `options.extensions.inline`, so the marker must * live on the function itself (which survives by reference); it is invisible * to `Marked` otherwise. This is a global-registry symbol so the promise is * checkable across module boundaries without importing the same binding. * * @remarks This is a promise about tokenizer statelessness. Any extension with * cross-block state (e.g. footnotes, whose definitions and references interact * across blocks) must **not** carry it. * * @module tail-window */ /** Marker key carried by tail-window-safe tokenizer functions. */ export const TAIL_WINDOW_SAFE = Symbol.for('svelte-markdown.tailWindowSafe'); /** * Tags a single extension tokenizer function as tail-window safe (see the * module doc). Only apply to block-anchored, stateless tokenizers; prefer * {@link tailWindowSafeExtension} to mark a whole extension atomically. * * @param tokenizer - The tokenizer function to mark * @returns Nothing — the marker is attached to the function in place * @example * ```ts * markTailWindowSafe(myBlockTokenizer) * ``` */ export const markTailWindowSafe = (tokenizer) => { ; tokenizer[TAIL_WINDOW_SAFE] = true; }; /** * Marks every tokenizer in a Marked extension as tail-window safe and returns * the extension. This is the whole-extension form of the promise: all of the * extension's tokenizers are block-anchored and stateless, so none of them can * be half-applied or forgotten when a tokenizer is added later. * * @param ext - The Marked extension whose tokenizers are all tail-window safe * @returns The same extension, with every tokenizer marked * @example * ```ts * return tailWindowSafeExtension({ extensions: [{ name: 'math', level: 'block', tokenizer }] }) * ``` */ export const tailWindowSafeExtension = (ext) => { for (const entry of ext.extensions ?? []) { if ('tokenizer' in entry && typeof entry.tokenizer === 'function') { markTailWindowSafe(entry.tokenizer); } } return ext; }; /** * True when `value` is a tail-window-safe tokenizer function carrying the * {@link TAIL_WINDOW_SAFE} marker. * * @param value - Candidate entry from `options.extensions.block`/`.inline` * @returns `true` if the entry is a function marked tail-window safe * @example * ```ts * isTailWindowSafe(options.extensions?.block?.[0]) // true only for marked tokenizers * ``` */ export const isTailWindowSafe = (value) => typeof value === 'function' && value[TAIL_WINDOW_SAFE] === true;