@stream-mdx/core 0.0.1 → 0.0.2
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 +13 -0
- package/README.md +26 -37
- package/dist/code-highlighting.cjs +0 -1
- package/dist/code-highlighting.mjs +0 -1
- package/dist/index.cjs +0 -1
- package/dist/index.mjs +0 -1
- package/dist/inline-parser.cjs +0 -1
- package/dist/inline-parser.mjs +0 -1
- package/dist/mixed-content.cjs +0 -1
- package/dist/mixed-content.mjs +0 -1
- package/dist/perf/backpressure.cjs +0 -1
- package/dist/perf/backpressure.mjs +0 -1
- package/dist/perf/patch-batching.cjs +0 -1
- package/dist/perf/patch-batching.mjs +0 -1
- package/dist/perf/patch-coalescing.cjs +0 -1
- package/dist/perf/patch-coalescing.mjs +0 -1
- package/dist/security.cjs +0 -1
- package/dist/security.mjs +0 -1
- package/dist/streaming/custom-matcher.cjs +0 -1
- package/dist/streaming/custom-matcher.mjs +0 -1
- package/dist/types.cjs +0 -1
- package/dist/types.mjs +0 -1
- package/dist/utils.cjs +0 -1
- package/dist/utils.mjs +0 -1
- package/dist/worker-html-sanitizer.cjs +0 -1
- package/dist/worker-html-sanitizer.mjs +0 -1
- package/package.json +4 -2
- package/dist/code-highlighting.cjs.map +0 -1
- package/dist/code-highlighting.mjs.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/inline-parser.cjs.map +0 -1
- package/dist/inline-parser.mjs.map +0 -1
- package/dist/mixed-content.cjs.map +0 -1
- package/dist/mixed-content.mjs.map +0 -1
- package/dist/perf/backpressure.cjs.map +0 -1
- package/dist/perf/backpressure.mjs.map +0 -1
- package/dist/perf/patch-batching.cjs.map +0 -1
- package/dist/perf/patch-batching.mjs.map +0 -1
- package/dist/perf/patch-coalescing.cjs.map +0 -1
- package/dist/perf/patch-coalescing.mjs.map +0 -1
- package/dist/security.cjs.map +0 -1
- package/dist/security.mjs.map +0 -1
- package/dist/streaming/custom-matcher.cjs.map +0 -1
- package/dist/streaming/custom-matcher.mjs.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.mjs.map +0 -1
- package/dist/utils.cjs.map +0 -1
- package/dist/utils.mjs.map +0 -1
- package/dist/worker-html-sanitizer.cjs.map +0 -1
- package/dist/worker-html-sanitizer.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker-html-sanitizer.ts","../src/mixed-content.ts"],"sourcesContent":["import * as rehypeParse from \"rehype-parse\";\nimport * as rehypeSanitize from \"rehype-sanitize\";\nimport * as rehypeStringify from \"rehype-stringify\";\nimport { unified } from \"unified\";\n\nconst { defaultSchema } = rehypeSanitize;\n\ntype Schema = typeof defaultSchema;\ntype AttributeDefinition = string | [string, ...(string | number | boolean | RegExp | null | undefined)[]];\n\nconst SANITIZED_SCHEMA: Schema = createSchema();\n\nconst sanitizeProcessor = unified()\n .use(rehypeParse.default, { fragment: true })\n .use(rehypeSanitize.default, SANITIZED_SCHEMA)\n .use(rehypeStringify.default)\n .freeze();\n\nexport function sanitizeHtmlInWorker(html: string): string {\n if (!html) return \"\";\n try {\n return sanitizeProcessor.processSync(html).toString();\n } catch (error) {\n console.warn(\"[markdown-v2] Failed to sanitize HTML in worker:\", error);\n return \"\";\n }\n}\n\nfunction createSchema(): Schema {\n const base = JSON.parse(JSON.stringify(defaultSchema)) as Schema;\n const tagSet = new Set<string>(base.tagNames ?? []);\n const allowedTags = [\n \"div\",\n \"span\",\n \"p\",\n \"br\",\n \"hr\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"tr\",\n \"th\",\n \"td\",\n \"a\",\n \"img\",\n \"section\",\n \"article\",\n \"aside\",\n \"nav\",\n \"header\",\n \"footer\",\n \"main\",\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n \"sub\",\n \"sup\",\n \"kbd\",\n ];\n for (const tag of allowedTags) {\n tagSet.add(tag);\n }\n base.tagNames = Array.from(tagSet);\n\n base.attributes = {\n ...(base.attributes || {}),\n \"*\": mergeAttributes(base.attributes?.[\"*\"] as AttributeDefinition[] | undefined, [\"className\", \"id\", \"title\", \"style\", \"data-*\", \"aria-*\"]),\n a: mergeAttributes(base.attributes?.a as AttributeDefinition[] | undefined, [\"href\", \"title\", \"target\", \"rel\"]),\n img: mergeAttributes(base.attributes?.img as AttributeDefinition[] | undefined, [\"src\", \"alt\", \"title\"]),\n table: mergeAttributes(base.attributes?.table as AttributeDefinition[] | undefined, [\"align\", \"border\", \"cellpadding\", \"cellspacing\"]),\n th: mergeAttributes(base.attributes?.th as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n td: mergeAttributes(base.attributes?.td as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n tr: mergeAttributes(base.attributes?.tr as AttributeDefinition[] | undefined, [\"align\"]),\n } as Schema[\"attributes\"];\n\n base.protocols = {\n ...(base.protocols || {}),\n href: [\"http\", \"https\", \"mailto\", \"tel\", \"callto\"],\n src: [\"http\", \"https\", \"data\"],\n };\n\n return base;\n}\n\nfunction mergeAttributes(existing: AttributeDefinition[] | undefined, additions: string[]): AttributeDefinition[] {\n const next: AttributeDefinition[] = Array.isArray(existing) ? [...existing] : [];\n const existingStrings = new Set<string>();\n for (const entry of next) {\n if (typeof entry === \"string\") {\n existingStrings.add(entry);\n }\n }\n for (const attr of additions) {\n if (!existingStrings.has(attr)) {\n next.push(attr);\n existingStrings.add(attr);\n }\n }\n return next;\n}\n","import type { InlineNode, MixedContentSegment } from \"./types\";\nimport { sanitizeHtmlInWorker } from \"./worker-html-sanitizer\";\n\nexport function extractMixedContentSegments(\n raw: string,\n baseOffset: number | undefined,\n parseInline: (content: string) => InlineNode[],\n): MixedContentSegment[] {\n if (!raw) return [];\n const initial = splitByTagSegments(raw, baseOffset, parseInline);\n const expanded: MixedContentSegment[] = [];\n for (const segment of initial) {\n if (segment.kind === \"text\") {\n expanded.push(...splitTextSegmentByExpressions(segment, parseInline));\n } else {\n expanded.push(segment);\n }\n }\n return mergeAdjacentTextSegments(expanded, parseInline);\n}\n\nfunction splitByTagSegments(source: string, baseOffset: number | undefined, parseInline: (content: string) => InlineNode[]): MixedContentSegment[] {\n const segments: MixedContentSegment[] = [];\n const lowerSource = source.toLowerCase();\n const tagPattern = /<([A-Za-z][\\w:-]*)([^<>]*?)\\/?>/g;\n let cursor = 0;\n let match: RegExpExecArray | null = tagPattern.exec(source);\n const baseIsFinite = typeof baseOffset === \"number\" && Number.isFinite(baseOffset);\n\n while (match !== null) {\n const start = match.index;\n const tagName = match[1];\n const matchText = match[0];\n const isSelfClosing = matchText.endsWith(\"/>\") || isVoidHtmlTag(tagName);\n let end = tagPattern.lastIndex;\n\n if (!isSelfClosing && !isLikelyMdxComponent(tagName)) {\n const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);\n if (closingIndex === -1) {\n // Tag not closed (common during streaming). Treat it as plain text and\n // continue scanning after the \"<\" so we don't get stuck on the same match.\n tagPattern.lastIndex = start + 1;\n match = tagPattern.exec(source);\n continue;\n }\n end = closingIndex;\n }\n\n if (start > cursor) {\n const absoluteFrom = baseIsFinite ? (baseOffset as number) + cursor : undefined;\n const absoluteTo = baseIsFinite ? (baseOffset as number) + start : undefined;\n pushTextSegment(segments, source.slice(cursor, start), absoluteFrom, absoluteTo, parseInline);\n }\n\n const rawSegment = source.slice(start, end);\n const kind: MixedContentSegment[\"kind\"] = isLikelyMdxComponent(tagName) ? \"mdx\" : \"html\";\n const segment: MixedContentSegment = {\n kind,\n value: rawSegment,\n range: createSegmentRange(baseOffset, start, end),\n };\n if (kind === \"html\") {\n segment.sanitized = sanitizeHtmlInWorker(rawSegment);\n } else {\n segment.status = \"pending\";\n }\n segments.push(segment);\n cursor = end;\n tagPattern.lastIndex = end;\n match = tagPattern.exec(source);\n }\n\n if (cursor < source.length) {\n const absoluteFrom = baseIsFinite ? (baseOffset as number) + cursor : undefined;\n const absoluteTo = baseIsFinite ? (baseOffset as number) + source.length : undefined;\n pushTextSegment(segments, source.slice(cursor), absoluteFrom, absoluteTo, parseInline);\n }\n\n return segments;\n}\n\nfunction splitTextSegmentByExpressions(segment: MixedContentSegment, parseInline: (content: string) => InlineNode[]): MixedContentSegment[] {\n if (segment.kind !== \"text\" || !segment.value) {\n return [segment];\n }\n const { value } = segment;\n if (isLikelyMathSegment(value)) {\n return [segment];\n }\n const hasRange = segment.range && typeof segment.range.from === \"number\" && typeof segment.range.to === \"number\";\n const rangeStart = hasRange ? segment.range?.from : undefined;\n const exprPattern = /\\{[^{}]+\\}/g;\n const results: MixedContentSegment[] = [];\n let cursor = 0;\n let match: RegExpExecArray | null = exprPattern.exec(value);\n\n while (match !== null) {\n const start = match.index;\n const end = exprPattern.lastIndex;\n if (start > cursor) {\n const textValue = value.slice(cursor, start);\n results.push({\n kind: \"text\",\n value: textValue,\n range: createSegmentRange(rangeStart, cursor, start),\n inline: parseInline(textValue),\n });\n }\n const expressionValue = match[0];\n results.push({\n kind: \"mdx\",\n value: expressionValue,\n range: createSegmentRange(rangeStart, start, end),\n status: \"pending\",\n });\n cursor = end;\n match = exprPattern.exec(value);\n }\n\n if (cursor < value.length) {\n const textValue = value.slice(cursor);\n results.push({\n kind: \"text\",\n value: textValue,\n range: createSegmentRange(rangeStart, cursor, value.length),\n inline: parseInline(textValue),\n });\n }\n\n return results.length > 0 ? results : [segment];\n}\n\nfunction isLikelyMathSegment(value: string): boolean {\n if (!value) return false;\n if (value.includes(\"$$\")) {\n return true;\n }\n if (/\\\\begin\\{[^}]+\\}/.test(value) || /\\\\end\\{[^}]+\\}/.test(value)) {\n return true;\n }\n if (/\\\\\\(|\\\\\\)|\\\\\\[|\\\\\\]/.test(value)) {\n return true;\n }\n if (/\\$[^$]*\\$/.test(value)) {\n return true;\n }\n if (/\\\\[a-zA-Z]+\\{/.test(value)) {\n return true;\n }\n return false;\n}\n\nfunction mergeAdjacentTextSegments(segments: MixedContentSegment[], parseInline: (content: string) => InlineNode[]): MixedContentSegment[] {\n if (segments.length === 0) return segments;\n const merged: MixedContentSegment[] = [];\n for (const segment of segments) {\n if (segment.kind === \"text\" && segment.value.length === 0) {\n continue;\n }\n const last = merged[merged.length - 1];\n if (segment.kind === \"text\" && last && last.kind === \"text\" && last.range && segment.range && last.range.to === segment.range.from) {\n last.value += segment.value;\n if (last.range && segment.range) {\n last.range.to = segment.range.to;\n }\n last.inline = parseInline(last.value);\n } else {\n merged.push(segment);\n }\n }\n return merged;\n}\n\nfunction pushTextSegment(\n target: MixedContentSegment[],\n value: string,\n from: number | undefined,\n to: number | undefined,\n parseInline: (content: string) => InlineNode[],\n): void {\n if (value.length === 0) return;\n target.push({\n kind: \"text\",\n value,\n range: createSegmentRange(from, 0, value.length, to),\n inline: parseInline(value),\n });\n}\n\nfunction createSegmentRange(base: number | undefined, relativeFrom: number, relativeTo: number, absoluteTo?: number): { from: number; to: number } | undefined {\n if (typeof base !== \"number\" || !Number.isFinite(base)) {\n return undefined;\n }\n const from = base + relativeFrom;\n const to = absoluteTo !== undefined ? absoluteTo : base + relativeTo;\n if (!Number.isFinite(from) || !Number.isFinite(to) || to < from) {\n return undefined;\n }\n return { from, to };\n}\n\nconst VOID_HTML_TAGS = new Set([\"br\", \"hr\", \"img\", \"meta\", \"input\", \"link\", \"source\", \"track\", \"area\", \"base\", \"col\", \"embed\"]);\n\nfunction isVoidHtmlTag(tagName: string): boolean {\n return VOID_HTML_TAGS.has(tagName.toLowerCase());\n}\n\nexport function isLikelyMdxComponent(tagName: string): boolean {\n const first = tagName.charAt(0);\n return first.toUpperCase() === first && first.toLowerCase() !== first;\n}\n\nexport function findClosingHtmlTag(lowerSource: string, lowerTagName: string, startIndex: number): number {\n let depth = 1;\n let searchIndex = startIndex;\n while (searchIndex < lowerSource.length) {\n const nextOpen = lowerSource.indexOf(`<${lowerTagName}`, searchIndex);\n const nextClose = lowerSource.indexOf(`</${lowerTagName}`, searchIndex);\n if (nextClose === -1) {\n return -1;\n }\n if (nextOpen !== -1 && nextOpen < nextClose) {\n depth++;\n searchIndex = nextOpen + 1;\n continue;\n }\n const closeEnd = lowerSource.indexOf(\">\", nextClose);\n if (closeEnd === -1) {\n return -1;\n }\n depth--;\n const absoluteEnd = closeEnd + 1;\n if (depth === 0) {\n return absoluteEnd;\n }\n searchIndex = absoluteEnd;\n }\n return -1;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/backpressure.ts"],"sourcesContent":["export interface BackpressureConfig {\n targetQueueDepth: number;\n maxQueueDepth: number;\n smoothingFactor: number;\n maxHeavyPatchBudget: number;\n minHeavyPatchBudget: number;\n lowCreditCutoff: number;\n}\n\nexport const DEFAULT_BACKPRESSURE_CONFIG: BackpressureConfig = {\n targetQueueDepth: 1.25,\n maxQueueDepth: 3,\n smoothingFactor: 0.7,\n maxHeavyPatchBudget: 4,\n minHeavyPatchBudget: 1,\n lowCreditCutoff: 0.5,\n};\n\nexport function calculateRawCredit(queueDepth: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n const depth = Number(queueDepth);\n if (!Number.isFinite(depth)) {\n return 1;\n }\n if (depth <= config.targetQueueDepth) {\n return 1;\n }\n if (depth >= config.maxQueueDepth) {\n return 0;\n }\n const range = Math.max(0.0001, config.maxQueueDepth - config.targetQueueDepth);\n const normalized = (depth - config.targetQueueDepth) / range;\n const credit = 1 - normalized;\n return clampCredit(credit);\n}\n\nexport function smoothCredit(previousCredit: number, rawCredit: number, smoothingFactor = DEFAULT_BACKPRESSURE_CONFIG.smoothingFactor): number {\n const prev = Number.isFinite(previousCredit) ? previousCredit : 1;\n const raw = Number.isFinite(rawCredit) ? rawCredit : prev;\n const factor = clamp01(smoothingFactor);\n if (factor <= 0) {\n return clampCredit(raw);\n }\n if (factor >= 1) {\n return clampCredit(raw);\n }\n const blended = prev + (raw - prev) * factor;\n return clampCredit(blended);\n}\n\nexport function calculateSmoothedCredit(queueDepth: number, previousCredit: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n const raw = calculateRawCredit(queueDepth, config);\n return smoothCredit(previousCredit, raw, config.smoothingFactor);\n}\n\nexport function computeHeavyPatchBudget(credit: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n if (!Number.isFinite(credit) || credit <= 0) {\n return 0;\n }\n if (credit <= config.lowCreditCutoff) {\n return 0;\n }\n const scaled = Math.floor(credit * config.maxHeavyPatchBudget);\n const budget = Math.max(config.minHeavyPatchBudget, scaled);\n return Math.min(config.maxHeavyPatchBudget, budget);\n}\n\nexport function clampCredit(value: number): number {\n if (!Number.isFinite(value)) return 1;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n}\n\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,IAAM,8BAAkD;AAAA,EAC7D,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,iBAAiB;AACnB;AAEO,SAAS,mBAAmB,YAAoB,SAA6B,6BAAqC;AACvH,QAAM,QAAQ,OAAO,UAAU;AAC/B,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,eAAe;AACjC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK,IAAI,MAAQ,OAAO,gBAAgB,OAAO,gBAAgB;AAC7E,QAAM,cAAc,QAAQ,OAAO,oBAAoB;AACvD,QAAM,SAAS,IAAI;AACnB,SAAO,YAAY,MAAM;AAC3B;AAEO,SAAS,aAAa,gBAAwB,WAAmB,kBAAkB,4BAA4B,iBAAyB;AAC7I,QAAM,OAAO,OAAO,SAAS,cAAc,IAAI,iBAAiB;AAChE,QAAM,MAAM,OAAO,SAAS,SAAS,IAAI,YAAY;AACrD,QAAM,SAAS,QAAQ,eAAe;AACtC,MAAI,UAAU,GAAG;AACf,WAAO,YAAY,GAAG;AAAA,EACxB;AACA,MAAI,UAAU,GAAG;AACf,WAAO,YAAY,GAAG;AAAA,EACxB;AACA,QAAM,UAAU,QAAQ,MAAM,QAAQ;AACtC,SAAO,YAAY,OAAO;AAC5B;AAEO,SAAS,wBAAwB,YAAoB,gBAAwB,SAA6B,6BAAqC;AACpJ,QAAM,MAAM,mBAAmB,YAAY,MAAM;AACjD,SAAO,aAAa,gBAAgB,KAAK,OAAO,eAAe;AACjE;AAEO,SAAS,wBAAwB,QAAgB,SAA6B,6BAAqC;AACxH,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,OAAO,iBAAiB;AACpC,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,MAAM,SAAS,OAAO,mBAAmB;AAC7D,QAAM,SAAS,KAAK,IAAI,OAAO,qBAAqB,MAAM;AAC1D,SAAO,KAAK,IAAI,OAAO,qBAAqB,MAAM;AACpD;AAEO,SAAS,YAAY,OAAuB;AACjD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO;AACT;AAEA,SAAS,QAAQ,OAAuB;AACtC,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/backpressure.ts"],"sourcesContent":["export interface BackpressureConfig {\n targetQueueDepth: number;\n maxQueueDepth: number;\n smoothingFactor: number;\n maxHeavyPatchBudget: number;\n minHeavyPatchBudget: number;\n lowCreditCutoff: number;\n}\n\nexport const DEFAULT_BACKPRESSURE_CONFIG: BackpressureConfig = {\n targetQueueDepth: 1.25,\n maxQueueDepth: 3,\n smoothingFactor: 0.7,\n maxHeavyPatchBudget: 4,\n minHeavyPatchBudget: 1,\n lowCreditCutoff: 0.5,\n};\n\nexport function calculateRawCredit(queueDepth: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n const depth = Number(queueDepth);\n if (!Number.isFinite(depth)) {\n return 1;\n }\n if (depth <= config.targetQueueDepth) {\n return 1;\n }\n if (depth >= config.maxQueueDepth) {\n return 0;\n }\n const range = Math.max(0.0001, config.maxQueueDepth - config.targetQueueDepth);\n const normalized = (depth - config.targetQueueDepth) / range;\n const credit = 1 - normalized;\n return clampCredit(credit);\n}\n\nexport function smoothCredit(previousCredit: number, rawCredit: number, smoothingFactor = DEFAULT_BACKPRESSURE_CONFIG.smoothingFactor): number {\n const prev = Number.isFinite(previousCredit) ? previousCredit : 1;\n const raw = Number.isFinite(rawCredit) ? rawCredit : prev;\n const factor = clamp01(smoothingFactor);\n if (factor <= 0) {\n return clampCredit(raw);\n }\n if (factor >= 1) {\n return clampCredit(raw);\n }\n const blended = prev + (raw - prev) * factor;\n return clampCredit(blended);\n}\n\nexport function calculateSmoothedCredit(queueDepth: number, previousCredit: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n const raw = calculateRawCredit(queueDepth, config);\n return smoothCredit(previousCredit, raw, config.smoothingFactor);\n}\n\nexport function computeHeavyPatchBudget(credit: number, config: BackpressureConfig = DEFAULT_BACKPRESSURE_CONFIG): number {\n if (!Number.isFinite(credit) || credit <= 0) {\n return 0;\n }\n if (credit <= config.lowCreditCutoff) {\n return 0;\n }\n const scaled = Math.floor(credit * config.maxHeavyPatchBudget);\n const budget = Math.max(config.minHeavyPatchBudget, scaled);\n return Math.min(config.maxHeavyPatchBudget, budget);\n}\n\nexport function clampCredit(value: number): number {\n if (!Number.isFinite(value)) return 1;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n}\n\nfunction clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n if (value <= 0) return 0;\n if (value >= 1) return 1;\n return value;\n}\n"],"mappings":";AASO,IAAM,8BAAkD;AAAA,EAC7D,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,iBAAiB;AACnB;AAEO,SAAS,mBAAmB,YAAoB,SAA6B,6BAAqC;AACvH,QAAM,QAAQ,OAAO,UAAU;AAC/B,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AAC3B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AACA,MAAI,SAAS,OAAO,eAAe;AACjC,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,KAAK,IAAI,MAAQ,OAAO,gBAAgB,OAAO,gBAAgB;AAC7E,QAAM,cAAc,QAAQ,OAAO,oBAAoB;AACvD,QAAM,SAAS,IAAI;AACnB,SAAO,YAAY,MAAM;AAC3B;AAEO,SAAS,aAAa,gBAAwB,WAAmB,kBAAkB,4BAA4B,iBAAyB;AAC7I,QAAM,OAAO,OAAO,SAAS,cAAc,IAAI,iBAAiB;AAChE,QAAM,MAAM,OAAO,SAAS,SAAS,IAAI,YAAY;AACrD,QAAM,SAAS,QAAQ,eAAe;AACtC,MAAI,UAAU,GAAG;AACf,WAAO,YAAY,GAAG;AAAA,EACxB;AACA,MAAI,UAAU,GAAG;AACf,WAAO,YAAY,GAAG;AAAA,EACxB;AACA,QAAM,UAAU,QAAQ,MAAM,QAAQ;AACtC,SAAO,YAAY,OAAO;AAC5B;AAEO,SAAS,wBAAwB,YAAoB,gBAAwB,SAA6B,6BAAqC;AACpJ,QAAM,MAAM,mBAAmB,YAAY,MAAM;AACjD,SAAO,aAAa,gBAAgB,KAAK,OAAO,eAAe;AACjE;AAEO,SAAS,wBAAwB,QAAgB,SAA6B,6BAAqC;AACxH,MAAI,CAAC,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,UAAU,OAAO,iBAAiB;AACpC,WAAO;AAAA,EACT;AACA,QAAM,SAAS,KAAK,MAAM,SAAS,OAAO,mBAAmB;AAC7D,QAAM,SAAS,KAAK,IAAI,OAAO,qBAAqB,MAAM;AAC1D,SAAO,KAAK,IAAI,OAAO,qBAAqB,MAAM;AACpD;AAEO,SAAS,YAAY,OAAuB;AACjD,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO;AACT;AAEA,SAAS,QAAQ,OAAuB;AACtC,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACpC,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,SAAS,EAAG,QAAO;AACvB,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/patch-batching.ts"],"sourcesContent":["import type { Block, NodeSnapshot, Patch, SetPropsBatchEntry } from \"../types\";\n\nconst DEFAULT_MAX_LIGHT_PATCHES_PER_CHUNK = 32;\nconst LIGHT_APPEND_LINE_THRESHOLD = 4;\n\nfunction isBlockCandidate(value: unknown): value is Block {\n if (!value || typeof value !== \"object\") return false;\n return typeof (value as { id?: unknown }).id === \"string\" && typeof (value as { type?: unknown }).type === \"string\";\n}\n\nconst LIGHTWEIGHT_NODE_TYPES = new Set([\n \"paragraph\",\n \"paragraph-text\",\n \"blockquote\",\n \"blockquote-text\",\n \"heading\",\n \"heading-text\",\n \"list\",\n \"list-item\",\n \"list-item-text\",\n \"footnote-def\",\n \"footnotes\",\n]);\n\nfunction isLightweightInsert(node?: NodeSnapshot | null): boolean {\n if (!node) return false;\n if (LIGHTWEIGHT_NODE_TYPES.has(node.type)) {\n return true;\n }\n const blockCandidate = (node.props as { block?: unknown } | undefined)?.block;\n if (isBlockCandidate(blockCandidate)) {\n const blockType = (blockCandidate as Block).type;\n return blockType === \"paragraph\" || blockType === \"heading\" || blockType === \"blockquote\" || blockType === \"list\";\n }\n return false;\n}\n\nfunction isHeavyPropsPayload(props: Record<string, unknown>): boolean {\n if (\"html\" in props) {\n return true;\n }\n if (\"block\" in props && isBlockCandidate((props as Record<string, unknown>).block)) {\n const block = (props as { block: Block }).block;\n if (block.type === \"code\" || block.type === \"html\") {\n return true;\n }\n if (typeof block.payload?.highlightedHtml === \"string\" || typeof block.payload?.sanitizedHtml === \"string\") {\n return true;\n }\n return true;\n }\n return false;\n}\n\nfunction isHeavySetProps(patch: Extract<Patch, { op: \"setProps\" }>): boolean {\n return isHeavyPropsPayload(patch.props ?? {});\n}\n\nfunction isHeavySetPropsBatch(entries: SetPropsBatchEntry[]): boolean {\n for (const entry of entries) {\n if (!entry) continue;\n if (isHeavyPropsPayload(entry.props ?? {})) {\n return true;\n }\n }\n return false;\n}\n\nexport function isHeavyPatch(patch: Patch): boolean {\n switch (patch.op) {\n case \"setHTML\":\n return true;\n case \"appendLines\":\n return (patch.lines?.length ?? LIGHT_APPEND_LINE_THRESHOLD + 1) > LIGHT_APPEND_LINE_THRESHOLD;\n case \"insertChild\":\n return !isLightweightInsert(patch.node);\n case \"replaceChild\":\n return !isLightweightInsert(patch.node);\n case \"deleteChild\":\n return false;\n case \"reorder\":\n return false;\n case \"setProps\":\n return isHeavySetProps(patch);\n case \"setPropsBatch\":\n return isHeavySetPropsBatch(patch.entries ?? []);\n default:\n return false;\n }\n}\n\nexport function splitPatchBatch(patches: Patch[], maxLightChunk = DEFAULT_MAX_LIGHT_PATCHES_PER_CHUNK): Patch[][] {\n if (patches.length === 0) return [];\n\n const groups: Patch[][] = [];\n let current: Patch[] = [];\n\n const flush = () => {\n if (current.length > 0) {\n groups.push(current);\n current = [];\n }\n };\n\n for (const patch of patches) {\n const heavy = isHeavyPatch(patch);\n if (heavy) {\n flush();\n groups.push([patch]);\n continue;\n }\n\n current.push(patch);\n if (current.length >= maxLightChunk) {\n flush();\n }\n }\n\n flush();\n\n return groups;\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAM,sCAAsC;AAC5C,IAAM,8BAA8B;AAEpC,SAAS,iBAAiB,OAAgC;AACxD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,SAAO,OAAQ,MAA2B,OAAO,YAAY,OAAQ,MAA6B,SAAS;AAC7G;AAEA,IAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,oBAAoB,MAAqC;AAChE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,uBAAuB,IAAI,KAAK,IAAI,GAAG;AACzC,WAAO;AAAA,EACT;AACA,QAAM,iBAAkB,KAAK,OAA2C;AACxE,MAAI,iBAAiB,cAAc,GAAG;AACpC,UAAM,YAAa,eAAyB;AAC5C,WAAO,cAAc,eAAe,cAAc,aAAa,cAAc,gBAAgB,cAAc;AAAA,EAC7G;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAyC;AACpE,MAAI,UAAU,OAAO;AACnB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,SAAS,iBAAkB,MAAkC,KAAK,GAAG;AAClF,UAAM,QAAS,MAA2B;AAC1C,QAAI,MAAM,SAAS,UAAU,MAAM,SAAS,QAAQ;AAClD,aAAO;AAAA,IACT;AACA,QAAI,OAAO,MAAM,SAAS,oBAAoB,YAAY,OAAO,MAAM,SAAS,kBAAkB,UAAU;AAC1G,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAoD;AAC3E,SAAO,oBAAoB,MAAM,SAAS,CAAC,CAAC;AAC9C;AAEA,SAAS,qBAAqB,SAAwC;AACpE,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAO;AACZ,QAAI,oBAAoB,MAAM,SAAS,CAAC,CAAC,GAAG;AAC1C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,OAAuB;AAClD,UAAQ,MAAM,IAAI;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,cAAQ,MAAM,OAAO,UAAU,8BAA8B,KAAK;AAAA,IACpE,KAAK;AACH,aAAO,CAAC,oBAAoB,MAAM,IAAI;AAAA,IACxC,KAAK;AACH,aAAO,CAAC,oBAAoB,MAAM,IAAI;AAAA,IACxC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,gBAAgB,KAAK;AAAA,IAC9B,KAAK;AACH,aAAO,qBAAqB,MAAM,WAAW,CAAC,CAAC;AAAA,IACjD;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,gBAAgB,SAAkB,gBAAgB,qCAAgD;AAChH,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,QAAM,SAAoB,CAAC;AAC3B,MAAI,UAAmB,CAAC;AAExB,QAAM,QAAQ,MAAM;AAClB,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO,KAAK,OAAO;AACnB,gBAAU,CAAC;AAAA,IACb;AAAA,EACF;AAEA,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,aAAa,KAAK;AAChC,QAAI,OAAO;AACT,YAAM;AACN,aAAO,KAAK,CAAC,KAAK,CAAC;AACnB;AAAA,IACF;AAEA,YAAQ,KAAK,KAAK;AAClB,QAAI,QAAQ,UAAU,eAAe;AACnC,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM;AAEN,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/patch-batching.ts"],"sourcesContent":["import type { Block, NodeSnapshot, Patch, SetPropsBatchEntry } from \"../types\";\n\nconst DEFAULT_MAX_LIGHT_PATCHES_PER_CHUNK = 32;\nconst LIGHT_APPEND_LINE_THRESHOLD = 4;\n\nfunction isBlockCandidate(value: unknown): value is Block {\n if (!value || typeof value !== \"object\") return false;\n return typeof (value as { id?: unknown }).id === \"string\" && typeof (value as { type?: unknown }).type === \"string\";\n}\n\nconst LIGHTWEIGHT_NODE_TYPES = new Set([\n \"paragraph\",\n \"paragraph-text\",\n \"blockquote\",\n \"blockquote-text\",\n \"heading\",\n \"heading-text\",\n \"list\",\n \"list-item\",\n \"list-item-text\",\n \"footnote-def\",\n \"footnotes\",\n]);\n\nfunction isLightweightInsert(node?: NodeSnapshot | null): boolean {\n if (!node) return false;\n if (LIGHTWEIGHT_NODE_TYPES.has(node.type)) {\n return true;\n }\n const blockCandidate = (node.props as { block?: unknown } | undefined)?.block;\n if (isBlockCandidate(blockCandidate)) {\n const blockType = (blockCandidate as Block).type;\n return blockType === \"paragraph\" || blockType === \"heading\" || blockType === \"blockquote\" || blockType === \"list\";\n }\n return false;\n}\n\nfunction isHeavyPropsPayload(props: Record<string, unknown>): boolean {\n if (\"html\" in props) {\n return true;\n }\n if (\"block\" in props && isBlockCandidate((props as Record<string, unknown>).block)) {\n const block = (props as { block: Block }).block;\n if (block.type === \"code\" || block.type === \"html\") {\n return true;\n }\n if (typeof block.payload?.highlightedHtml === \"string\" || typeof block.payload?.sanitizedHtml === \"string\") {\n return true;\n }\n return true;\n }\n return false;\n}\n\nfunction isHeavySetProps(patch: Extract<Patch, { op: \"setProps\" }>): boolean {\n return isHeavyPropsPayload(patch.props ?? {});\n}\n\nfunction isHeavySetPropsBatch(entries: SetPropsBatchEntry[]): boolean {\n for (const entry of entries) {\n if (!entry) continue;\n if (isHeavyPropsPayload(entry.props ?? {})) {\n return true;\n }\n }\n return false;\n}\n\nexport function isHeavyPatch(patch: Patch): boolean {\n switch (patch.op) {\n case \"setHTML\":\n return true;\n case \"appendLines\":\n return (patch.lines?.length ?? LIGHT_APPEND_LINE_THRESHOLD + 1) > LIGHT_APPEND_LINE_THRESHOLD;\n case \"insertChild\":\n return !isLightweightInsert(patch.node);\n case \"replaceChild\":\n return !isLightweightInsert(patch.node);\n case \"deleteChild\":\n return false;\n case \"reorder\":\n return false;\n case \"setProps\":\n return isHeavySetProps(patch);\n case \"setPropsBatch\":\n return isHeavySetPropsBatch(patch.entries ?? []);\n default:\n return false;\n }\n}\n\nexport function splitPatchBatch(patches: Patch[], maxLightChunk = DEFAULT_MAX_LIGHT_PATCHES_PER_CHUNK): Patch[][] {\n if (patches.length === 0) return [];\n\n const groups: Patch[][] = [];\n let current: Patch[] = [];\n\n const flush = () => {\n if (current.length > 0) {\n groups.push(current);\n current = [];\n }\n };\n\n for (const patch of patches) {\n const heavy = isHeavyPatch(patch);\n if (heavy) {\n flush();\n groups.push([patch]);\n continue;\n }\n\n current.push(patch);\n if (current.length >= maxLightChunk) {\n flush();\n }\n }\n\n flush();\n\n return groups;\n}\n\n"],"mappings":";AAEA,IAAM,sCAAsC;AAC5C,IAAM,8BAA8B;AAEpC,SAAS,iBAAiB,OAAgC;AACxD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,SAAO,OAAQ,MAA2B,OAAO,YAAY,OAAQ,MAA6B,SAAS;AAC7G;AAEA,IAAM,yBAAyB,oBAAI,IAAI;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,oBAAoB,MAAqC;AAChE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,uBAAuB,IAAI,KAAK,IAAI,GAAG;AACzC,WAAO;AAAA,EACT;AACA,QAAM,iBAAkB,KAAK,OAA2C;AACxE,MAAI,iBAAiB,cAAc,GAAG;AACpC,UAAM,YAAa,eAAyB;AAC5C,WAAO,cAAc,eAAe,cAAc,aAAa,cAAc,gBAAgB,cAAc;AAAA,EAC7G;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,OAAyC;AACpE,MAAI,UAAU,OAAO;AACnB,WAAO;AAAA,EACT;AACA,MAAI,WAAW,SAAS,iBAAkB,MAAkC,KAAK,GAAG;AAClF,UAAM,QAAS,MAA2B;AAC1C,QAAI,MAAM,SAAS,UAAU,MAAM,SAAS,QAAQ;AAClD,aAAO;AAAA,IACT;AACA,QAAI,OAAO,MAAM,SAAS,oBAAoB,YAAY,OAAO,MAAM,SAAS,kBAAkB,UAAU;AAC1G,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAoD;AAC3E,SAAO,oBAAoB,MAAM,SAAS,CAAC,CAAC;AAC9C;AAEA,SAAS,qBAAqB,SAAwC;AACpE,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAO;AACZ,QAAI,oBAAoB,MAAM,SAAS,CAAC,CAAC,GAAG;AAC1C,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,OAAuB;AAClD,UAAQ,MAAM,IAAI;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,cAAQ,MAAM,OAAO,UAAU,8BAA8B,KAAK;AAAA,IACpE,KAAK;AACH,aAAO,CAAC,oBAAoB,MAAM,IAAI;AAAA,IACxC,KAAK;AACH,aAAO,CAAC,oBAAoB,MAAM,IAAI;AAAA,IACxC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,gBAAgB,KAAK;AAAA,IAC9B,KAAK;AACH,aAAO,qBAAqB,MAAM,WAAW,CAAC,CAAC;AAAA,IACjD;AACE,aAAO;AAAA,EACX;AACF;AAEO,SAAS,gBAAgB,SAAkB,gBAAgB,qCAAgD;AAChH,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,QAAM,SAAoB,CAAC;AAC3B,MAAI,UAAmB,CAAC;AAExB,QAAM,QAAQ,MAAM;AAClB,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO,KAAK,OAAO;AACnB,gBAAU,CAAC;AAAA,IACb;AAAA,EACF;AAEA,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,aAAa,KAAK;AAChC,QAAI,OAAO;AACT,YAAM;AACN,aAAO,KAAK,CAAC,KAAK,CAAC;AACnB;AAAA,IACF;AAEA,YAAQ,KAAK,KAAK;AAClB,QAAI,QAAQ,UAAU,eAAe;AACnC,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM;AAEN,SAAO;AACT;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/patch-coalescing.ts"],"sourcesContent":["import type { CoalescingMetrics, NodePath, NodeSnapshot, Patch, SetPropsBatchEntry } from \"../types\";\nexport type { CoalescingMetrics } from \"../types\";\n\nexport interface CoalesceConfig {\n enabled: boolean;\n maxCoalesceWindow: number; // Maximum patches to consider for coalescing (for performance)\n coalesceableOps: Set<Patch[\"op\"]>; // Operations that can be coalesced\n}\n\nexport const DEFAULT_COALESCE_CONFIG: CoalesceConfig = {\n enabled: true,\n maxCoalesceWindow: 50, // Look at up to 50 patches for coalescing opportunities\n coalesceableOps: new Set([\"appendLines\", \"insertChild\", \"setProps\"]),\n};\n\nconst MAX_BATCHED_SET_PROPS = 24;\nconst APPEND_MERGE_LIMIT = 10;\nconst INSERT_MERGE_LIMIT = 20;\nconst SET_PROPS_MERGE_LIMIT = 10;\nconst USE_LINEAR_COALESCING = typeof process === \"undefined\" ? true : process.env.V2_USE_LINEAR_COALESCING !== \"false\" && process.env.NODE_ENV !== \"test\";\n\nfunction cloneNodePath(path: NodePath): NodePath {\n return {\n blockId: path.blockId,\n nodeId: path.nodeId,\n indexPath: path.indexPath ? [...path.indexPath] : undefined,\n };\n}\n\nfunction nodePathKey(path: NodePath): string {\n const nodePart = path.nodeId ?? \"\";\n const indexPart = path.indexPath && path.indexPath.length > 0 ? `::${path.indexPath.join(\".\")}` : \"\";\n return `${path.blockId}::${nodePart}${indexPart}`;\n}\n\nfunction runCoalescer(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (USE_LINEAR_COALESCING) {\n return coalescePatchesLinear(patches, config);\n }\n return coalescePatchesQuadratic(patches, config);\n}\n\nexport function coalescePatches(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n return runCoalescer(patches, config);\n}\n\nexport function coalescePatchesWithMetrics(\n patches: Patch[],\n config: CoalesceConfig = DEFAULT_COALESCE_CONFIG,\n): { patches: Patch[]; metrics: CoalescingMetrics } {\n const start = typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n\n const output = runCoalescer(patches, config);\n const durationMs = (typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now()) - start;\n\n const inputCounts = countOps(patches);\n const outputCounts = countOps(output);\n\n const metrics: CoalescingMetrics = {\n inputPatchCount: patches.length,\n outputPatchCount: output.length,\n coalescedCount: Math.max(0, patches.length - output.length),\n durationMs,\n appendLinesCoalesced: Math.max(0, (inputCounts.appendLines ?? 0) - (outputCounts.appendLines ?? 0)),\n setPropsCoalesced: Math.max(0, (inputCounts.setProps ?? 0) - (outputCounts.setProps ?? 0)),\n insertChildCoalesced: Math.max(0, (inputCounts.insertChild ?? 0) - (outputCounts.insertChild ?? 0)),\n };\n\n return { patches: output, metrics };\n}\n\nexport function coalescePatchesLinear(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (!config.enabled || patches.length === 0) {\n return patches;\n }\n\n const windowSize = Math.min(patches.length, config.maxCoalesceWindow);\n const window = patches.slice(0, windowSize);\n const rest = patches.slice(windowSize);\n\n const result: Patch[] = [];\n\n let i = 0;\n while (i < window.length) {\n const patch = window[i];\n if (!config.coalesceableOps.has(patch.op)) {\n result.push(patch);\n i++;\n continue;\n }\n\n if (patch.op === \"appendLines\") {\n const merged = mergeAppendLines(window, i);\n result.push(merged.patch);\n i = merged.nextIndex;\n continue;\n }\n\n if (patch.op === \"insertChild\") {\n const merged = collectInsertChildren(window, i);\n result.push(...merged.patches);\n i = merged.nextIndex;\n continue;\n }\n\n if (patch.op === \"setProps\") {\n const merged = collectSetProps(window, i);\n result.push(...merged.patches);\n i = merged.nextIndex;\n continue;\n }\n\n result.push(patch);\n i++;\n }\n\n if (result.length > 1) {\n const dedup: Patch[] = [];\n const seenSetProps = new Set<string>();\n for (let idx = result.length - 1; idx >= 0; idx--) {\n const current = result[idx];\n if (current.op === \"setProps\") {\n const key = `${current.at.blockId}::$${current.at.nodeId ?? \"\"}`;\n if (seenSetProps.has(key)) {\n continue;\n }\n seenSetProps.add(key);\n }\n dedup.push(current);\n }\n dedup.reverse();\n result.length = 0;\n result.push(...dedup);\n }\n\n return [...result, ...rest];\n}\n\nfunction mergeAppendLines(window: Patch[], startIndex: number): { patch: Patch; nextIndex: number } {\n const base = window[startIndex];\n if (!base || base.op !== \"appendLines\") {\n return { patch: base, nextIndex: startIndex + 1 };\n }\n\n const lines = [...(base.lines ?? [])];\n const highlight: Array<string | null> = Array.isArray(base.highlight) ? [...base.highlight] : [];\n const baseStart = base.startIndex;\n let expectedStart = baseStart + lines.length;\n let j = startIndex + 1;\n let mergedCount = 0;\n\n while (j < window.length && mergedCount < APPEND_MERGE_LIMIT) {\n const next = window[j];\n if (\n next.op === \"appendLines\" &&\n next.at.blockId === base.at.blockId &&\n next.at.nodeId === base.at.nodeId &&\n typeof next.startIndex === \"number\" &&\n next.startIndex === expectedStart\n ) {\n lines.push(...(next.lines ?? []));\n const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];\n const appendedCount = next.lines?.length ?? 0;\n if (nextHighlights.length > 0) {\n for (let idx = 0; idx < appendedCount; idx++) {\n const highlightValue = idx < nextHighlights.length ? (nextHighlights[idx] ?? null) : null;\n highlight.push(highlightValue);\n }\n } else {\n for (let idx = 0; idx < appendedCount; idx++) {\n highlight.push(null);\n }\n }\n expectedStart = baseStart + lines.length;\n mergedCount++;\n j++;\n continue;\n }\n break;\n }\n\n const combined: Patch = {\n ...base,\n lines,\n highlight: highlight.length > 0 ? highlight : undefined,\n };\n\n return { patch: combined, nextIndex: j };\n}\n\nfunction collectInsertChildren(window: Patch[], startIndex: number): { patches: Patch[]; nextIndex: number } {\n const base = window[startIndex];\n if (!base || base.op !== \"insertChild\") {\n return { patches: [base], nextIndex: startIndex + 1 };\n }\n const inserts: Array<{ index: number; node: NodeSnapshot }> = [];\n if (typeof base.index === \"number\") {\n inserts.push({ index: base.index, node: base.node });\n }\n let j = startIndex + 1;\n let mergedCount = 0;\n\n while (j < window.length && mergedCount < INSERT_MERGE_LIMIT) {\n const next = window[j];\n if (\n next.op === \"insertChild\" &&\n next.at.blockId === base.at.blockId &&\n next.at.nodeId === base.at.nodeId &&\n typeof next.index === \"number\" &&\n typeof base.index === \"number\" &&\n next.index === (base.index ?? 0) + inserts.length\n ) {\n inserts.push({ index: next.index, node: next.node });\n mergedCount++;\n j++;\n continue;\n }\n break;\n }\n\n if (inserts.length <= 1) {\n return { patches: [base], nextIndex: j };\n }\n\n const clones = inserts.map((entry) => ({\n ...base,\n index: entry.index,\n node: entry.node,\n })) as Patch[];\n\n return { patches: clones, nextIndex: j };\n}\n\nfunction collectSetProps(window: Patch[], startIndex: number): { patches: Patch[]; nextIndex: number } {\n const first = window[startIndex];\n if (!first || first.op !== \"setProps\") {\n return { patches: [first], nextIndex: startIndex + 1 };\n }\n const entries: SetPropsBatchEntry[] = [];\n let j = startIndex;\n\n while (j < window.length && entries.length < MAX_BATCHED_SET_PROPS) {\n const current = window[j];\n if (current.op !== \"setProps\") {\n break;\n }\n const mergedProps = { ...(current.props ?? {}) };\n let k = j + 1;\n let mergedCount = 0;\n\n while (k < window.length && mergedCount < SET_PROPS_MERGE_LIMIT) {\n const next = window[k];\n if (next.op === \"setProps\" && nodePathKey(next.at) === nodePathKey(current.at)) {\n Object.assign(mergedProps, next.props ?? {});\n mergedCount++;\n k++;\n } else {\n break;\n }\n }\n\n entries.push({\n at: cloneNodePath(current.at),\n props: mergedProps,\n });\n\n j = k;\n }\n\n if (entries.length === 0) {\n return { patches: [window[startIndex]], nextIndex: startIndex + 1 };\n }\n\n if (entries.length === 1) {\n const single = entries[0];\n const patch: Patch = {\n ...(first as Patch),\n op: \"setProps\",\n props: single.props,\n } as Patch;\n return { patches: [patch], nextIndex: j };\n }\n\n const batchPatch: Patch = {\n op: \"setPropsBatch\",\n entries,\n } as Patch;\n\n return { patches: [batchPatch], nextIndex: j };\n}\n\nfunction countOps(patches: Patch[]): Record<string, number> {\n const counts: Record<string, number> = Object.create(null);\n for (const patch of patches) {\n counts[patch.op] = (counts[patch.op] ?? 0) + 1;\n }\n return counts;\n}\n\n/**\n * Coalesce adjacent patches that target the same parent and can be batched together.\n * This reduces the number of DOM operations and React re-renders.\n *\n * Currently coalesces:\n * - Multiple `appendLines` for the same code block → single `appendLines` with combined lines\n * - Multiple `insertChild` for the same parent at consecutive indices → single batch insert\n * - Multiple `setProps` for the same node → single `setProps` with merged props\n */\nexport function coalescePatchesQuadratic(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (!config.enabled || patches.length === 0) {\n return patches;\n }\n\n // Limit the window for performance (coalescing is O(n²) worst case)\n const windowSize = Math.min(patches.length, config.maxCoalesceWindow);\n const window = patches.slice(0, windowSize);\n const rest = patches.slice(windowSize);\n\n const coalesced: Patch[] = [];\n let i = 0;\n\n while (i < window.length) {\n const current = window[i];\n if (!config.coalesceableOps.has(current.op)) {\n coalesced.push(current);\n i++;\n continue;\n }\n\n // Try to coalesce current patch with following patches\n let coalescedCount = 0;\n\n if (current.op === \"appendLines\") {\n // Coalesce multiple appendLines for the same block\n const lines: string[] = [...current.lines];\n const highlights: Array<string | null> = current.highlight ? [...current.highlight] : [];\n let j = i + 1;\n\n while (j < window.length && coalescedCount < 10) {\n const next = window[j];\n if (\n next.op === \"appendLines\" &&\n next.at.blockId === current.at.blockId &&\n next.at.nodeId === current.at.nodeId &&\n typeof next.startIndex === \"number\" &&\n typeof current.startIndex === \"number\" &&\n next.startIndex === (current.startIndex ?? 0) + lines.length\n ) {\n lines.push(...next.lines);\n if (next.highlight) {\n highlights.push(...next.highlight);\n } else {\n highlights.push(...new Array(next.lines.length).fill(null));\n }\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n if (coalescedCount > 0) {\n coalesced.push({\n ...current,\n lines,\n highlight: highlights.length > 0 ? highlights : undefined,\n });\n i = j;\n continue;\n }\n } else if (current.op === \"insertChild\") {\n // Coalesce multiple insertChild for the same parent at consecutive indices\n const inserts: Array<{ index: number; node: typeof current.node }> = [{ index: current.index, node: current.node }];\n let j = i + 1;\n let lastIndex = current.index;\n\n while (j < window.length && coalescedCount < 20) {\n const next = window[j];\n if (next.op === \"insertChild\" && next.at.blockId === current.at.blockId && next.at.nodeId === current.at.nodeId && next.index === lastIndex + 1) {\n inserts.push({ index: next.index, node: next.node });\n lastIndex = next.index;\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n // For now, keep individual inserts (batching would require a new patch op)\n // Future: could add `insertChildren` batch op\n if (coalescedCount === 0) {\n coalesced.push(current);\n i++;\n continue;\n }\n // Add all inserts individually for now (coalescing helped us skip some processing)\n coalesced.push(...inserts.map((ins) => ({ ...current, index: ins.index, node: ins.node })));\n i = j;\n continue;\n } else if (current.op === \"setProps\") {\n // Coalesce multiple setProps for the same node\n const mergedProps = { ...(current.props ?? {}) };\n let j = i + 1;\n\n while (j < window.length && coalescedCount < 10) {\n const next = window[j];\n if (next.op === \"setProps\" && next.at.blockId === current.at.blockId && next.at.nodeId === current.at.nodeId) {\n Object.assign(mergedProps, next.props ?? {});\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n const batchEntries = [\n {\n at: cloneNodePath(current.at),\n props: mergedProps,\n },\n ];\n\n let k = j;\n while (k < window.length && batchEntries.length < MAX_BATCHED_SET_PROPS) {\n const candidate = window[k];\n if (candidate.op !== \"setProps\") {\n break;\n }\n\n const candidateMergedProps = { ...(candidate.props ?? {}) };\n let m = k + 1;\n let localMerged = 0;\n\n while (m < window.length && localMerged < 10) {\n const follow = window[m];\n if (follow.op === \"setProps\" && nodePathKey(follow.at) === nodePathKey(candidate.at)) {\n Object.assign(candidateMergedProps, follow.props ?? {});\n m++;\n localMerged++;\n } else {\n break;\n }\n }\n\n batchEntries.push({\n at: cloneNodePath(candidate.at),\n props: candidateMergedProps,\n });\n k = m;\n }\n\n if (batchEntries.length > 1) {\n coalesced.push({\n op: \"setPropsBatch\",\n entries: batchEntries,\n } as Patch);\n i = k;\n continue;\n }\n\n if (coalescedCount > 0) {\n coalesced.push({\n ...current,\n props: mergedProps,\n });\n i = j;\n continue;\n }\n\n coalesced.push({\n ...current,\n props: mergedProps,\n });\n i++;\n continue;\n }\n\n // No coalescing found, add current patch\n coalesced.push(current);\n i++;\n }\n\n // Deduplicate sequential setProps so only the latest per node survives within the window\n if (coalesced.length > 1) {\n const deduped: Patch[] = [];\n const seenSetProps = new Set<string>();\n for (let idx = coalesced.length - 1; idx >= 0; idx--) {\n const patch = coalesced[idx];\n if (patch.op === \"setProps\") {\n const key = `${patch.at.blockId}::$${patch.at.nodeId ?? \"\"}`;\n if (seenSetProps.has(key)) {\n continue;\n }\n seenSetProps.add(key);\n }\n deduped.push(patch);\n }\n deduped.reverse();\n coalesced.length = 0;\n coalesced.push(...deduped);\n }\n\n // Add remaining patches that weren't in the coalescing window\n return [...coalesced, ...rest];\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/perf/patch-coalescing.ts"],"sourcesContent":["import type { CoalescingMetrics, NodePath, NodeSnapshot, Patch, SetPropsBatchEntry } from \"../types\";\nexport type { CoalescingMetrics } from \"../types\";\n\nexport interface CoalesceConfig {\n enabled: boolean;\n maxCoalesceWindow: number; // Maximum patches to consider for coalescing (for performance)\n coalesceableOps: Set<Patch[\"op\"]>; // Operations that can be coalesced\n}\n\nexport const DEFAULT_COALESCE_CONFIG: CoalesceConfig = {\n enabled: true,\n maxCoalesceWindow: 50, // Look at up to 50 patches for coalescing opportunities\n coalesceableOps: new Set([\"appendLines\", \"insertChild\", \"setProps\"]),\n};\n\nconst MAX_BATCHED_SET_PROPS = 24;\nconst APPEND_MERGE_LIMIT = 10;\nconst INSERT_MERGE_LIMIT = 20;\nconst SET_PROPS_MERGE_LIMIT = 10;\nconst USE_LINEAR_COALESCING = typeof process === \"undefined\" ? true : process.env.V2_USE_LINEAR_COALESCING !== \"false\" && process.env.NODE_ENV !== \"test\";\n\nfunction cloneNodePath(path: NodePath): NodePath {\n return {\n blockId: path.blockId,\n nodeId: path.nodeId,\n indexPath: path.indexPath ? [...path.indexPath] : undefined,\n };\n}\n\nfunction nodePathKey(path: NodePath): string {\n const nodePart = path.nodeId ?? \"\";\n const indexPart = path.indexPath && path.indexPath.length > 0 ? `::${path.indexPath.join(\".\")}` : \"\";\n return `${path.blockId}::${nodePart}${indexPart}`;\n}\n\nfunction runCoalescer(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (USE_LINEAR_COALESCING) {\n return coalescePatchesLinear(patches, config);\n }\n return coalescePatchesQuadratic(patches, config);\n}\n\nexport function coalescePatches(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n return runCoalescer(patches, config);\n}\n\nexport function coalescePatchesWithMetrics(\n patches: Patch[],\n config: CoalesceConfig = DEFAULT_COALESCE_CONFIG,\n): { patches: Patch[]; metrics: CoalescingMetrics } {\n const start = typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now();\n\n const output = runCoalescer(patches, config);\n const durationMs = (typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance.now() : Date.now()) - start;\n\n const inputCounts = countOps(patches);\n const outputCounts = countOps(output);\n\n const metrics: CoalescingMetrics = {\n inputPatchCount: patches.length,\n outputPatchCount: output.length,\n coalescedCount: Math.max(0, patches.length - output.length),\n durationMs,\n appendLinesCoalesced: Math.max(0, (inputCounts.appendLines ?? 0) - (outputCounts.appendLines ?? 0)),\n setPropsCoalesced: Math.max(0, (inputCounts.setProps ?? 0) - (outputCounts.setProps ?? 0)),\n insertChildCoalesced: Math.max(0, (inputCounts.insertChild ?? 0) - (outputCounts.insertChild ?? 0)),\n };\n\n return { patches: output, metrics };\n}\n\nexport function coalescePatchesLinear(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (!config.enabled || patches.length === 0) {\n return patches;\n }\n\n const windowSize = Math.min(patches.length, config.maxCoalesceWindow);\n const window = patches.slice(0, windowSize);\n const rest = patches.slice(windowSize);\n\n const result: Patch[] = [];\n\n let i = 0;\n while (i < window.length) {\n const patch = window[i];\n if (!config.coalesceableOps.has(patch.op)) {\n result.push(patch);\n i++;\n continue;\n }\n\n if (patch.op === \"appendLines\") {\n const merged = mergeAppendLines(window, i);\n result.push(merged.patch);\n i = merged.nextIndex;\n continue;\n }\n\n if (patch.op === \"insertChild\") {\n const merged = collectInsertChildren(window, i);\n result.push(...merged.patches);\n i = merged.nextIndex;\n continue;\n }\n\n if (patch.op === \"setProps\") {\n const merged = collectSetProps(window, i);\n result.push(...merged.patches);\n i = merged.nextIndex;\n continue;\n }\n\n result.push(patch);\n i++;\n }\n\n if (result.length > 1) {\n const dedup: Patch[] = [];\n const seenSetProps = new Set<string>();\n for (let idx = result.length - 1; idx >= 0; idx--) {\n const current = result[idx];\n if (current.op === \"setProps\") {\n const key = `${current.at.blockId}::$${current.at.nodeId ?? \"\"}`;\n if (seenSetProps.has(key)) {\n continue;\n }\n seenSetProps.add(key);\n }\n dedup.push(current);\n }\n dedup.reverse();\n result.length = 0;\n result.push(...dedup);\n }\n\n return [...result, ...rest];\n}\n\nfunction mergeAppendLines(window: Patch[], startIndex: number): { patch: Patch; nextIndex: number } {\n const base = window[startIndex];\n if (!base || base.op !== \"appendLines\") {\n return { patch: base, nextIndex: startIndex + 1 };\n }\n\n const lines = [...(base.lines ?? [])];\n const highlight: Array<string | null> = Array.isArray(base.highlight) ? [...base.highlight] : [];\n const baseStart = base.startIndex;\n let expectedStart = baseStart + lines.length;\n let j = startIndex + 1;\n let mergedCount = 0;\n\n while (j < window.length && mergedCount < APPEND_MERGE_LIMIT) {\n const next = window[j];\n if (\n next.op === \"appendLines\" &&\n next.at.blockId === base.at.blockId &&\n next.at.nodeId === base.at.nodeId &&\n typeof next.startIndex === \"number\" &&\n next.startIndex === expectedStart\n ) {\n lines.push(...(next.lines ?? []));\n const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];\n const appendedCount = next.lines?.length ?? 0;\n if (nextHighlights.length > 0) {\n for (let idx = 0; idx < appendedCount; idx++) {\n const highlightValue = idx < nextHighlights.length ? (nextHighlights[idx] ?? null) : null;\n highlight.push(highlightValue);\n }\n } else {\n for (let idx = 0; idx < appendedCount; idx++) {\n highlight.push(null);\n }\n }\n expectedStart = baseStart + lines.length;\n mergedCount++;\n j++;\n continue;\n }\n break;\n }\n\n const combined: Patch = {\n ...base,\n lines,\n highlight: highlight.length > 0 ? highlight : undefined,\n };\n\n return { patch: combined, nextIndex: j };\n}\n\nfunction collectInsertChildren(window: Patch[], startIndex: number): { patches: Patch[]; nextIndex: number } {\n const base = window[startIndex];\n if (!base || base.op !== \"insertChild\") {\n return { patches: [base], nextIndex: startIndex + 1 };\n }\n const inserts: Array<{ index: number; node: NodeSnapshot }> = [];\n if (typeof base.index === \"number\") {\n inserts.push({ index: base.index, node: base.node });\n }\n let j = startIndex + 1;\n let mergedCount = 0;\n\n while (j < window.length && mergedCount < INSERT_MERGE_LIMIT) {\n const next = window[j];\n if (\n next.op === \"insertChild\" &&\n next.at.blockId === base.at.blockId &&\n next.at.nodeId === base.at.nodeId &&\n typeof next.index === \"number\" &&\n typeof base.index === \"number\" &&\n next.index === (base.index ?? 0) + inserts.length\n ) {\n inserts.push({ index: next.index, node: next.node });\n mergedCount++;\n j++;\n continue;\n }\n break;\n }\n\n if (inserts.length <= 1) {\n return { patches: [base], nextIndex: j };\n }\n\n const clones = inserts.map((entry) => ({\n ...base,\n index: entry.index,\n node: entry.node,\n })) as Patch[];\n\n return { patches: clones, nextIndex: j };\n}\n\nfunction collectSetProps(window: Patch[], startIndex: number): { patches: Patch[]; nextIndex: number } {\n const first = window[startIndex];\n if (!first || first.op !== \"setProps\") {\n return { patches: [first], nextIndex: startIndex + 1 };\n }\n const entries: SetPropsBatchEntry[] = [];\n let j = startIndex;\n\n while (j < window.length && entries.length < MAX_BATCHED_SET_PROPS) {\n const current = window[j];\n if (current.op !== \"setProps\") {\n break;\n }\n const mergedProps = { ...(current.props ?? {}) };\n let k = j + 1;\n let mergedCount = 0;\n\n while (k < window.length && mergedCount < SET_PROPS_MERGE_LIMIT) {\n const next = window[k];\n if (next.op === \"setProps\" && nodePathKey(next.at) === nodePathKey(current.at)) {\n Object.assign(mergedProps, next.props ?? {});\n mergedCount++;\n k++;\n } else {\n break;\n }\n }\n\n entries.push({\n at: cloneNodePath(current.at),\n props: mergedProps,\n });\n\n j = k;\n }\n\n if (entries.length === 0) {\n return { patches: [window[startIndex]], nextIndex: startIndex + 1 };\n }\n\n if (entries.length === 1) {\n const single = entries[0];\n const patch: Patch = {\n ...(first as Patch),\n op: \"setProps\",\n props: single.props,\n } as Patch;\n return { patches: [patch], nextIndex: j };\n }\n\n const batchPatch: Patch = {\n op: \"setPropsBatch\",\n entries,\n } as Patch;\n\n return { patches: [batchPatch], nextIndex: j };\n}\n\nfunction countOps(patches: Patch[]): Record<string, number> {\n const counts: Record<string, number> = Object.create(null);\n for (const patch of patches) {\n counts[patch.op] = (counts[patch.op] ?? 0) + 1;\n }\n return counts;\n}\n\n/**\n * Coalesce adjacent patches that target the same parent and can be batched together.\n * This reduces the number of DOM operations and React re-renders.\n *\n * Currently coalesces:\n * - Multiple `appendLines` for the same code block → single `appendLines` with combined lines\n * - Multiple `insertChild` for the same parent at consecutive indices → single batch insert\n * - Multiple `setProps` for the same node → single `setProps` with merged props\n */\nexport function coalescePatchesQuadratic(patches: Patch[], config: CoalesceConfig = DEFAULT_COALESCE_CONFIG): Patch[] {\n if (!config.enabled || patches.length === 0) {\n return patches;\n }\n\n // Limit the window for performance (coalescing is O(n²) worst case)\n const windowSize = Math.min(patches.length, config.maxCoalesceWindow);\n const window = patches.slice(0, windowSize);\n const rest = patches.slice(windowSize);\n\n const coalesced: Patch[] = [];\n let i = 0;\n\n while (i < window.length) {\n const current = window[i];\n if (!config.coalesceableOps.has(current.op)) {\n coalesced.push(current);\n i++;\n continue;\n }\n\n // Try to coalesce current patch with following patches\n let coalescedCount = 0;\n\n if (current.op === \"appendLines\") {\n // Coalesce multiple appendLines for the same block\n const lines: string[] = [...current.lines];\n const highlights: Array<string | null> = current.highlight ? [...current.highlight] : [];\n let j = i + 1;\n\n while (j < window.length && coalescedCount < 10) {\n const next = window[j];\n if (\n next.op === \"appendLines\" &&\n next.at.blockId === current.at.blockId &&\n next.at.nodeId === current.at.nodeId &&\n typeof next.startIndex === \"number\" &&\n typeof current.startIndex === \"number\" &&\n next.startIndex === (current.startIndex ?? 0) + lines.length\n ) {\n lines.push(...next.lines);\n if (next.highlight) {\n highlights.push(...next.highlight);\n } else {\n highlights.push(...new Array(next.lines.length).fill(null));\n }\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n if (coalescedCount > 0) {\n coalesced.push({\n ...current,\n lines,\n highlight: highlights.length > 0 ? highlights : undefined,\n });\n i = j;\n continue;\n }\n } else if (current.op === \"insertChild\") {\n // Coalesce multiple insertChild for the same parent at consecutive indices\n const inserts: Array<{ index: number; node: typeof current.node }> = [{ index: current.index, node: current.node }];\n let j = i + 1;\n let lastIndex = current.index;\n\n while (j < window.length && coalescedCount < 20) {\n const next = window[j];\n if (next.op === \"insertChild\" && next.at.blockId === current.at.blockId && next.at.nodeId === current.at.nodeId && next.index === lastIndex + 1) {\n inserts.push({ index: next.index, node: next.node });\n lastIndex = next.index;\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n // For now, keep individual inserts (batching would require a new patch op)\n // Future: could add `insertChildren` batch op\n if (coalescedCount === 0) {\n coalesced.push(current);\n i++;\n continue;\n }\n // Add all inserts individually for now (coalescing helped us skip some processing)\n coalesced.push(...inserts.map((ins) => ({ ...current, index: ins.index, node: ins.node })));\n i = j;\n continue;\n } else if (current.op === \"setProps\") {\n // Coalesce multiple setProps for the same node\n const mergedProps = { ...(current.props ?? {}) };\n let j = i + 1;\n\n while (j < window.length && coalescedCount < 10) {\n const next = window[j];\n if (next.op === \"setProps\" && next.at.blockId === current.at.blockId && next.at.nodeId === current.at.nodeId) {\n Object.assign(mergedProps, next.props ?? {});\n coalescedCount++;\n j++;\n } else {\n break;\n }\n }\n\n const batchEntries = [\n {\n at: cloneNodePath(current.at),\n props: mergedProps,\n },\n ];\n\n let k = j;\n while (k < window.length && batchEntries.length < MAX_BATCHED_SET_PROPS) {\n const candidate = window[k];\n if (candidate.op !== \"setProps\") {\n break;\n }\n\n const candidateMergedProps = { ...(candidate.props ?? {}) };\n let m = k + 1;\n let localMerged = 0;\n\n while (m < window.length && localMerged < 10) {\n const follow = window[m];\n if (follow.op === \"setProps\" && nodePathKey(follow.at) === nodePathKey(candidate.at)) {\n Object.assign(candidateMergedProps, follow.props ?? {});\n m++;\n localMerged++;\n } else {\n break;\n }\n }\n\n batchEntries.push({\n at: cloneNodePath(candidate.at),\n props: candidateMergedProps,\n });\n k = m;\n }\n\n if (batchEntries.length > 1) {\n coalesced.push({\n op: \"setPropsBatch\",\n entries: batchEntries,\n } as Patch);\n i = k;\n continue;\n }\n\n if (coalescedCount > 0) {\n coalesced.push({\n ...current,\n props: mergedProps,\n });\n i = j;\n continue;\n }\n\n coalesced.push({\n ...current,\n props: mergedProps,\n });\n i++;\n continue;\n }\n\n // No coalescing found, add current patch\n coalesced.push(current);\n i++;\n }\n\n // Deduplicate sequential setProps so only the latest per node survives within the window\n if (coalesced.length > 1) {\n const deduped: Patch[] = [];\n const seenSetProps = new Set<string>();\n for (let idx = coalesced.length - 1; idx >= 0; idx--) {\n const patch = coalesced[idx];\n if (patch.op === \"setProps\") {\n const key = `${patch.at.blockId}::$${patch.at.nodeId ?? \"\"}`;\n if (seenSetProps.has(key)) {\n continue;\n }\n seenSetProps.add(key);\n }\n deduped.push(patch);\n }\n deduped.reverse();\n coalesced.length = 0;\n coalesced.push(...deduped);\n }\n\n // Add remaining patches that weren't in the coalescing window\n return [...coalesced, ...rest];\n}\n"],"mappings":"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","names":[]}
|
package/dist/security.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/security.ts"],"sourcesContent":["// Security layer with sanitization and Trusted Types\n// Handles HTML sanitization for safe rendering\n\nimport createDOMPurify, { type Config as DOMPurifyConfig } from \"dompurify\";\n\ntype DOMPurifyInstance = {\n sanitize: (html: string, config: DOMPurifyConfig) => unknown;\n addHook?: (name: string, hook: (node: unknown) => unknown) => void;\n};\n\ntype TrustedTypePolicyLike = {\n createHTML: (input: string) => string;\n};\n\ntype TrustedTypePolicyFactoryLike = {\n createPolicy: (\n name: string,\n rules: {\n createHTML: (input: string) => string;\n createScript?: (input: string) => string;\n createScriptURL?: (input: string) => string;\n },\n ) => TrustedTypePolicyLike;\n};\n\nlet domPurifyInstance: DOMPurifyInstance | null = null;\n\nfunction resolveDOMPurify(): DOMPurifyInstance {\n const maybeInstance = createDOMPurify as unknown as DOMPurifyInstance;\n if (maybeInstance && typeof maybeInstance.sanitize === \"function\") {\n return maybeInstance;\n }\n if (domPurifyInstance && typeof domPurifyInstance.sanitize === \"function\") {\n return domPurifyInstance;\n }\n if (typeof window === \"undefined\") {\n throw new Error(\"[markdown-v2] DOMPurify requires a DOM `window` to sanitize HTML. Provide a DOM (e.g. via jsdom) before calling sanitize helpers.\");\n }\n domPurifyInstance = (createDOMPurify as unknown as (win: Window) => DOMPurifyInstance)(window);\n return domPurifyInstance;\n}\n\n/**\n * Trusted Types policy for safe HTML rendering\n */\nlet trustedTypesPolicy: TrustedTypePolicyLike | undefined;\n\n/**\n * Initialize Trusted Types policy\n */\nexport function initializeTrustedTypesPolicy(): void {\n if (typeof window === \"undefined\" || trustedTypesPolicy) {\n return;\n }\n const trustedWindow = window as typeof window & { trustedTypes?: TrustedTypePolicyFactoryLike };\n const factory = trustedWindow.trustedTypes;\n if (!factory || trustedTypesPolicy) {\n return;\n }\n trustedTypesPolicy = factory.createPolicy(\"markdown-renderer-v2\", {\n createHTML: (input: string) => {\n const out = resolveDOMPurify().sanitize(input, {\n ALLOWED_TAGS: [\n // Block elements\n \"div\",\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"sub\",\n \"sup\",\n \"kbd\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"th\",\n \"td\",\n \"hr\",\n \"br\",\n\n // Inline elements\n \"span\",\n \"strong\",\n \"em\",\n \"a\",\n \"img\",\n\n // Code highlighting\n 'span[class^=\"token\"]',\n\n // Math rendering (KaTeX)\n 'span[class^=\"katex\"]',\n 'span[class^=\"mord\"]',\n 'span[class^=\"mopen\"]',\n 'span[class^=\"mclose\"]',\n 'span[class^=\"mop\"]',\n 'span[class^=\"mbin\"]',\n 'span[class^=\"mrel\"]',\n 'span[class^=\"mpunct\"]',\n 'span[class^=\"minner\"]',\n 'span[class^=\"mspace\"]',\n 'span[class^=\"sizing\"]',\n 'span[class^=\"reset-size\"]',\n \"div[class^='katex-block-wrapper']\",\n\n // Custom components\n \"div[data-component]\",\n \"span[data-component]\",\n ],\n ALLOWED_ATTR: [\n \"class\",\n \"id\",\n \"data-*\",\n \"href\",\n \"src\",\n \"alt\",\n \"title\",\n \"type\",\n \"value\",\n \"checked\",\n \"disabled\",\n \"style\", // Limited style for math rendering\n ],\n ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i,\n RETURN_DOM: false,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n });\n return typeof out === \"string\" ? out : String(out);\n },\n createScript: (input: string) => input,\n createScriptURL: (input: string) => input,\n });\n}\n\n/**\n * Create trusted HTML using DOMPurify and Trusted Types\n */\nexport function createTrustedHTML(html: string): string {\n // Initialize policy if not already done\n initializeTrustedTypesPolicy();\n\n if (trustedTypesPolicy) {\n return trustedTypesPolicy.createHTML(html);\n }\n\n // Fallback to DOMPurify without Trusted Types\n return sanitizeHTML(html);\n}\n\n/**\n * Sanitize HTML content for safe rendering\n */\nexport function sanitizeHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, getSanitizationConfig());\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Get DOMPurify configuration\n */\nfunction getSanitizationConfig(): DOMPurifyConfig {\n return {\n ALLOWED_TAGS: [\n // Standard HTML elements\n \"div\",\n \"span\",\n \"p\",\n \"br\",\n \"hr\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"sub\",\n \"sup\",\n \"kbd\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"tr\",\n \"th\",\n \"td\",\n \"a\",\n \"img\",\n\n // Code highlighting (Shiki/Prism classes)\n \"span\",\n \"div\",\n\n // Math rendering (KaTeX)\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n\n // Custom extension points\n \"section\",\n \"article\",\n \"aside\",\n \"nav\",\n \"header\",\n \"footer\",\n \"main\",\n ],\n\n ALLOWED_ATTR: [\n \"class\",\n \"id\",\n \"data-*\",\n \"href\",\n \"src\",\n \"alt\",\n \"title\",\n \"width\",\n \"height\",\n \"type\",\n \"value\",\n \"placeholder\",\n \"disabled\",\n \"readonly\",\n \"role\",\n \"aria-*\",\n \"style\", // Limited for math/highlighting\n \"target\",\n \"rel\",\n ],\n\n ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i,\n\n // Remove scripts and potentially dangerous content\n FORBID_TAGS: [\"script\", \"object\", \"embed\", \"form\", \"input\", \"button\"],\n FORBID_ATTR: [\"onerror\", \"onload\", \"onclick\", \"onmouseover\"],\n\n // Keep data attributes for component identification\n KEEP_CONTENT: true,\n\n // Return configuration\n RETURN_DOM: false,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n\n // Additional security\n SANITIZE_DOM: true,\n WHOLE_DOCUMENT: false,\n\n // Hook for custom processing\n SANITIZE_NAMED_PROPS: true,\n\n // Custom hooks\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,\n attributeNameCheck: /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,\n allowCustomizedBuiltInElements: false,\n },\n };\n}\n\n/**\n * Sanitization policy for different content types\n */\nexport interface SanitizationPolicy {\n allowMath: boolean;\n allowSyntaxHighlighting: boolean;\n allowCustomComponents: boolean;\n allowInlineStyles: boolean;\n allowExternalLinks: boolean;\n customTags?: string[];\n customAttributes?: string[];\n}\n\n/**\n * Create custom sanitization config based on policy\n */\nexport function createSanitizationConfig(policy: SanitizationPolicy): DOMPurifyConfig {\n const baseConfig = getSanitizationConfig();\n\n // Extend allowed tags based on policy\n const allowedTags = [...(baseConfig.ALLOWED_TAGS || [])];\n const allowedAttr = [...(baseConfig.ALLOWED_ATTR || [])];\n\n if (!policy.allowMath) {\n // Remove math-related tags\n const mathTags = [\"annotation\", \"semantics\", \"mtext\", \"mn\", \"mo\", \"mi\", \"mspace\"];\n allowedTags.splice(0, allowedTags.length, ...allowedTags.filter((tag) => !mathTags.includes(tag)));\n }\n\n if (!policy.allowSyntaxHighlighting) {\n const classIndex = allowedAttr.indexOf(\"class\");\n if (classIndex > -1) {\n allowedAttr.splice(classIndex, 1);\n }\n }\n\n if (!policy.allowInlineStyles) {\n // Remove style attribute\n const styleIndex = allowedAttr.indexOf(\"style\");\n if (styleIndex > -1) {\n allowedAttr.splice(styleIndex, 1);\n }\n }\n\n if (!policy.allowExternalLinks) {\n // Restrict URI pattern to relative links only\n baseConfig.ALLOWED_URI_REGEXP = /^(?:[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i;\n }\n\n // Add custom tags and attributes\n if (policy.customTags) {\n allowedTags.push(...policy.customTags);\n }\n\n if (policy.customAttributes) {\n allowedAttr.push(...policy.customAttributes);\n }\n\n return {\n ...baseConfig,\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttr,\n };\n}\n\n/**\n * Sanitize code block HTML from syntax highlighters\n */\nexport function sanitizeCodeHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, {\n ALLOWED_TAGS: [\"pre\", \"code\", \"span\", \"div\", \"br\"],\n ALLOWED_ATTR: [\"class\", \"data-*\", \"style\"],\n KEEP_CONTENT: true,\n RETURN_DOM: false,\n });\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Sanitize math HTML from KaTeX\n */\nexport function sanitizeMathHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, {\n ALLOWED_TAGS: [\n \"span\",\n \"div\",\n \"br\",\n // KaTeX specific tags\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n ],\n ALLOWED_ATTR: [\"class\", \"style\", \"data-*\"],\n KEEP_CONTENT: true,\n RETURN_DOM: false,\n });\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Validate and sanitize URLs\n */\nexport function sanitizeURL(url: string): string | null {\n // Basic URL validation and sanitization\n try {\n const parsed = new URL(url, window.location.origin);\n\n // Allow http, https, mailto, and relative URLs\n if (![\"http:\", \"https:\", \"mailto:\", \"tel:\"].includes(parsed.protocol)) {\n return null;\n }\n\n return parsed.toString();\n } catch {\n // If URL parsing fails, treat as relative\n if (url.startsWith(\"/\") || url.startsWith(\"./\") || url.startsWith(\"../\")) {\n return url;\n }\n return null;\n }\n}\n\n/**\n * Content Security Policy utilities\n */\nexport const CSP_HEADERS = {\n // Strict CSP for markdown rendering\n strict: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\", // Required for KaTeX\n \"img-src 'self' data: https:\",\n \"connect-src 'self'\",\n \"font-src 'self' data:\",\n \"require-trusted-types-for 'script'\",\n \"trusted-types markdown-renderer-v2\",\n ].join(\"; \"),\n\n // Relaxed CSP for development\n development: [\n \"default-src 'self'\",\n \"script-src 'self' 'unsafe-eval'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"connect-src 'self' ws:\",\n \"font-src 'self' data:\",\n ].join(\"; \"),\n};\n\n/**\n * Initialize security features\n */\nexport function initializeSecurity(): void {\n // Initialize Trusted Types\n initializeTrustedTypesPolicy();\n\n // Set up DOMPurify hooks if needed\n try {\n const purifier = resolveDOMPurify();\n if (typeof purifier.addHook !== \"function\") {\n return;\n }\n\n purifier.addHook(\"beforeSanitizeElements\", (node: unknown) => node);\n purifier.addHook(\"afterSanitizeElements\", (node: unknown) => node);\n } catch {\n // ignore missing DOM environment\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,uBAAgE;AAsBhE,IAAI,oBAA8C;AAElD,SAAS,mBAAsC;AAC7C,QAAM,gBAAgB,iBAAAA;AACtB,MAAI,iBAAiB,OAAO,cAAc,aAAa,YAAY;AACjE,WAAO;AAAA,EACT;AACA,MAAI,qBAAqB,OAAO,kBAAkB,aAAa,YAAY;AACzE,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,MAAM,mIAAmI;AAAA,EACrJ;AACA,0BAAqB,iBAAAA,SAAkE,MAAM;AAC7F,SAAO;AACT;AAKA,IAAI;AAKG,SAAS,+BAAqC;AACnD,MAAI,OAAO,WAAW,eAAe,oBAAoB;AACvD;AAAA,EACF;AACA,QAAM,gBAAgB;AACtB,QAAM,UAAU,cAAc;AAC9B,MAAI,CAAC,WAAW,oBAAoB;AAClC;AAAA,EACF;AACA,uBAAqB,QAAQ,aAAa,wBAAwB;AAAA,IAChE,YAAY,CAAC,UAAkB;AAC7B,YAAM,MAAM,iBAAiB,EAAE,SAAS,OAAO;AAAA,QAC7C,cAAc;AAAA;AAAA,UAEZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UAGA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UAGA;AAAA;AAAA,UAGA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,UAGA;AAAA,UACA;AAAA,QACF;AAAA,QACA,cAAc;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,QACF;AAAA,QACA,oBAAoB;AAAA,QACpB,YAAY;AAAA,QACZ,qBAAqB;AAAA,QACrB,qBAAqB;AAAA,MACvB,CAAC;AACD,aAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AAAA,IACnD;AAAA,IACA,cAAc,CAAC,UAAkB;AAAA,IACjC,iBAAiB,CAAC,UAAkB;AAAA,EACtC,CAAC;AACH;AAKO,SAAS,kBAAkB,MAAsB;AAEtD,+BAA6B;AAE7B,MAAI,oBAAoB;AACtB,WAAO,mBAAmB,WAAW,IAAI;AAAA,EAC3C;AAGA,SAAO,aAAa,IAAI;AAC1B;AAKO,SAAS,aAAa,MAAsB;AACjD,QAAM,MAAM,iBAAiB,EAAE,SAAS,MAAM,sBAAsB,CAAC;AACrE,SAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AACnD;AAKA,SAAS,wBAAyC;AAChD,SAAO;AAAA,IACL,cAAc;AAAA;AAAA,MAEZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAGA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IAEA,oBAAoB;AAAA;AAAA,IAGpB,aAAa,CAAC,UAAU,UAAU,SAAS,QAAQ,SAAS,QAAQ;AAAA,IACpE,aAAa,CAAC,WAAW,UAAU,WAAW,aAAa;AAAA;AAAA,IAG3D,cAAc;AAAA;AAAA,IAGd,YAAY;AAAA,IACZ,qBAAqB;AAAA,IACrB,qBAAqB;AAAA;AAAA,IAGrB,cAAc;AAAA,IACd,gBAAgB;AAAA;AAAA,IAGhB,sBAAsB;AAAA;AAAA,IAGtB,yBAAyB;AAAA,MACvB,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,gCAAgC;AAAA,IAClC;AAAA,EACF;AACF;AAkBO,SAAS,yBAAyB,QAA6C;AACpF,QAAM,aAAa,sBAAsB;AAGzC,QAAM,cAAc,CAAC,GAAI,WAAW,gBAAgB,CAAC,CAAE;AACvD,QAAM,cAAc,CAAC,GAAI,WAAW,gBAAgB,CAAC,CAAE;AAEvD,MAAI,CAAC,OAAO,WAAW;AAErB,UAAM,WAAW,CAAC,cAAc,aAAa,SAAS,MAAM,MAAM,MAAM,QAAQ;AAChF,gBAAY,OAAO,GAAG,YAAY,QAAQ,GAAG,YAAY,OAAO,CAAC,QAAQ,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC;AAAA,EACnG;AAEA,MAAI,CAAC,OAAO,yBAAyB;AACnC,UAAM,aAAa,YAAY,QAAQ,OAAO;AAC9C,QAAI,aAAa,IAAI;AACnB,kBAAY,OAAO,YAAY,CAAC;AAAA,IAClC;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,mBAAmB;AAE7B,UAAM,aAAa,YAAY,QAAQ,OAAO;AAC9C,QAAI,aAAa,IAAI;AACnB,kBAAY,OAAO,YAAY,CAAC;AAAA,IAClC;AAAA,EACF;AAEA,MAAI,CAAC,OAAO,oBAAoB;AAE9B,eAAW,qBAAqB;AAAA,EAClC;AAGA,MAAI,OAAO,YAAY;AACrB,gBAAY,KAAK,GAAG,OAAO,UAAU;AAAA,EACvC;AAEA,MAAI,OAAO,kBAAkB;AAC3B,gBAAY,KAAK,GAAG,OAAO,gBAAgB;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,cAAc;AAAA,IACd,cAAc;AAAA,EAChB;AACF;AAKO,SAAS,iBAAiB,MAAsB;AACrD,QAAM,MAAM,iBAAiB,EAAE,SAAS,MAAM;AAAA,IAC5C,cAAc,CAAC,OAAO,QAAQ,QAAQ,OAAO,IAAI;AAAA,IACjD,cAAc,CAAC,SAAS,UAAU,OAAO;AAAA,IACzC,cAAc;AAAA,IACd,YAAY;AAAA,EACd,CAAC;AACD,SAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AACnD;AAKO,SAAS,iBAAiB,MAAsB;AACrD,QAAM,MAAM,iBAAiB,EAAE,SAAS,MAAM;AAAA,IAC5C,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA;AAAA,MAEA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,cAAc,CAAC,SAAS,SAAS,QAAQ;AAAA,IACzC,cAAc;AAAA,IACd,YAAY;AAAA,EACd,CAAC;AACD,SAAO,OAAO,QAAQ,WAAW,MAAM,OAAO,GAAG;AACnD;AAKO,SAAS,YAAY,KAA4B;AAEtD,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,KAAK,OAAO,SAAS,MAAM;AAGlD,QAAI,CAAC,CAAC,SAAS,UAAU,WAAW,MAAM,EAAE,SAAS,OAAO,QAAQ,GAAG;AACrE,aAAO;AAAA,IACT;AAEA,WAAO,OAAO,SAAS;AAAA,EACzB,QAAQ;AAEN,QAAI,IAAI,WAAW,GAAG,KAAK,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,KAAK,GAAG;AACxE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;AAKO,IAAM,cAAc;AAAA;AAAA,EAEzB,QAAQ;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AAAA;AAAA,EAGX,aAAa;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAKO,SAAS,qBAA2B;AAEzC,+BAA6B;AAG7B,MAAI;AACF,UAAM,WAAW,iBAAiB;AAClC,QAAI,OAAO,SAAS,YAAY,YAAY;AAC1C;AAAA,IACF;AAEA,aAAS,QAAQ,0BAA0B,CAAC,SAAkB,IAAI;AAClE,aAAS,QAAQ,yBAAyB,CAAC,SAAkB,IAAI;AAAA,EACnE,QAAQ;AAAA,EAER;AACF;","names":["createDOMPurify"]}
|
package/dist/security.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/security.ts"],"sourcesContent":["// Security layer with sanitization and Trusted Types\n// Handles HTML sanitization for safe rendering\n\nimport createDOMPurify, { type Config as DOMPurifyConfig } from \"dompurify\";\n\ntype DOMPurifyInstance = {\n sanitize: (html: string, config: DOMPurifyConfig) => unknown;\n addHook?: (name: string, hook: (node: unknown) => unknown) => void;\n};\n\ntype TrustedTypePolicyLike = {\n createHTML: (input: string) => string;\n};\n\ntype TrustedTypePolicyFactoryLike = {\n createPolicy: (\n name: string,\n rules: {\n createHTML: (input: string) => string;\n createScript?: (input: string) => string;\n createScriptURL?: (input: string) => string;\n },\n ) => TrustedTypePolicyLike;\n};\n\nlet domPurifyInstance: DOMPurifyInstance | null = null;\n\nfunction resolveDOMPurify(): DOMPurifyInstance {\n const maybeInstance = createDOMPurify as unknown as DOMPurifyInstance;\n if (maybeInstance && typeof maybeInstance.sanitize === \"function\") {\n return maybeInstance;\n }\n if (domPurifyInstance && typeof domPurifyInstance.sanitize === \"function\") {\n return domPurifyInstance;\n }\n if (typeof window === \"undefined\") {\n throw new Error(\"[markdown-v2] DOMPurify requires a DOM `window` to sanitize HTML. Provide a DOM (e.g. via jsdom) before calling sanitize helpers.\");\n }\n domPurifyInstance = (createDOMPurify as unknown as (win: Window) => DOMPurifyInstance)(window);\n return domPurifyInstance;\n}\n\n/**\n * Trusted Types policy for safe HTML rendering\n */\nlet trustedTypesPolicy: TrustedTypePolicyLike | undefined;\n\n/**\n * Initialize Trusted Types policy\n */\nexport function initializeTrustedTypesPolicy(): void {\n if (typeof window === \"undefined\" || trustedTypesPolicy) {\n return;\n }\n const trustedWindow = window as typeof window & { trustedTypes?: TrustedTypePolicyFactoryLike };\n const factory = trustedWindow.trustedTypes;\n if (!factory || trustedTypesPolicy) {\n return;\n }\n trustedTypesPolicy = factory.createPolicy(\"markdown-renderer-v2\", {\n createHTML: (input: string) => {\n const out = resolveDOMPurify().sanitize(input, {\n ALLOWED_TAGS: [\n // Block elements\n \"div\",\n \"p\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"sub\",\n \"sup\",\n \"kbd\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"th\",\n \"td\",\n \"hr\",\n \"br\",\n\n // Inline elements\n \"span\",\n \"strong\",\n \"em\",\n \"a\",\n \"img\",\n\n // Code highlighting\n 'span[class^=\"token\"]',\n\n // Math rendering (KaTeX)\n 'span[class^=\"katex\"]',\n 'span[class^=\"mord\"]',\n 'span[class^=\"mopen\"]',\n 'span[class^=\"mclose\"]',\n 'span[class^=\"mop\"]',\n 'span[class^=\"mbin\"]',\n 'span[class^=\"mrel\"]',\n 'span[class^=\"mpunct\"]',\n 'span[class^=\"minner\"]',\n 'span[class^=\"mspace\"]',\n 'span[class^=\"sizing\"]',\n 'span[class^=\"reset-size\"]',\n \"div[class^='katex-block-wrapper']\",\n\n // Custom components\n \"div[data-component]\",\n \"span[data-component]\",\n ],\n ALLOWED_ATTR: [\n \"class\",\n \"id\",\n \"data-*\",\n \"href\",\n \"src\",\n \"alt\",\n \"title\",\n \"type\",\n \"value\",\n \"checked\",\n \"disabled\",\n \"style\", // Limited style for math rendering\n ],\n ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i,\n RETURN_DOM: false,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n });\n return typeof out === \"string\" ? out : String(out);\n },\n createScript: (input: string) => input,\n createScriptURL: (input: string) => input,\n });\n}\n\n/**\n * Create trusted HTML using DOMPurify and Trusted Types\n */\nexport function createTrustedHTML(html: string): string {\n // Initialize policy if not already done\n initializeTrustedTypesPolicy();\n\n if (trustedTypesPolicy) {\n return trustedTypesPolicy.createHTML(html);\n }\n\n // Fallback to DOMPurify without Trusted Types\n return sanitizeHTML(html);\n}\n\n/**\n * Sanitize HTML content for safe rendering\n */\nexport function sanitizeHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, getSanitizationConfig());\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Get DOMPurify configuration\n */\nfunction getSanitizationConfig(): DOMPurifyConfig {\n return {\n ALLOWED_TAGS: [\n // Standard HTML elements\n \"div\",\n \"span\",\n \"p\",\n \"br\",\n \"hr\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"sub\",\n \"sup\",\n \"kbd\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"tr\",\n \"th\",\n \"td\",\n \"a\",\n \"img\",\n\n // Code highlighting (Shiki/Prism classes)\n \"span\",\n \"div\",\n\n // Math rendering (KaTeX)\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n\n // Custom extension points\n \"section\",\n \"article\",\n \"aside\",\n \"nav\",\n \"header\",\n \"footer\",\n \"main\",\n ],\n\n ALLOWED_ATTR: [\n \"class\",\n \"id\",\n \"data-*\",\n \"href\",\n \"src\",\n \"alt\",\n \"title\",\n \"width\",\n \"height\",\n \"type\",\n \"value\",\n \"placeholder\",\n \"disabled\",\n \"readonly\",\n \"role\",\n \"aria-*\",\n \"style\", // Limited for math/highlighting\n \"target\",\n \"rel\",\n ],\n\n ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp|data):|[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i,\n\n // Remove scripts and potentially dangerous content\n FORBID_TAGS: [\"script\", \"object\", \"embed\", \"form\", \"input\", \"button\"],\n FORBID_ATTR: [\"onerror\", \"onload\", \"onclick\", \"onmouseover\"],\n\n // Keep data attributes for component identification\n KEEP_CONTENT: true,\n\n // Return configuration\n RETURN_DOM: false,\n RETURN_DOM_FRAGMENT: false,\n RETURN_TRUSTED_TYPE: false,\n\n // Additional security\n SANITIZE_DOM: true,\n WHOLE_DOCUMENT: false,\n\n // Hook for custom processing\n SANITIZE_NAMED_PROPS: true,\n\n // Custom hooks\n CUSTOM_ELEMENT_HANDLING: {\n tagNameCheck: /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,\n attributeNameCheck: /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/,\n allowCustomizedBuiltInElements: false,\n },\n };\n}\n\n/**\n * Sanitization policy for different content types\n */\nexport interface SanitizationPolicy {\n allowMath: boolean;\n allowSyntaxHighlighting: boolean;\n allowCustomComponents: boolean;\n allowInlineStyles: boolean;\n allowExternalLinks: boolean;\n customTags?: string[];\n customAttributes?: string[];\n}\n\n/**\n * Create custom sanitization config based on policy\n */\nexport function createSanitizationConfig(policy: SanitizationPolicy): DOMPurifyConfig {\n const baseConfig = getSanitizationConfig();\n\n // Extend allowed tags based on policy\n const allowedTags = [...(baseConfig.ALLOWED_TAGS || [])];\n const allowedAttr = [...(baseConfig.ALLOWED_ATTR || [])];\n\n if (!policy.allowMath) {\n // Remove math-related tags\n const mathTags = [\"annotation\", \"semantics\", \"mtext\", \"mn\", \"mo\", \"mi\", \"mspace\"];\n allowedTags.splice(0, allowedTags.length, ...allowedTags.filter((tag) => !mathTags.includes(tag)));\n }\n\n if (!policy.allowSyntaxHighlighting) {\n const classIndex = allowedAttr.indexOf(\"class\");\n if (classIndex > -1) {\n allowedAttr.splice(classIndex, 1);\n }\n }\n\n if (!policy.allowInlineStyles) {\n // Remove style attribute\n const styleIndex = allowedAttr.indexOf(\"style\");\n if (styleIndex > -1) {\n allowedAttr.splice(styleIndex, 1);\n }\n }\n\n if (!policy.allowExternalLinks) {\n // Restrict URI pattern to relative links only\n baseConfig.ALLOWED_URI_REGEXP = /^(?:[^a-z]|[a-z+.\\-]+(?:[^a-z+.\\-:]|$))/i;\n }\n\n // Add custom tags and attributes\n if (policy.customTags) {\n allowedTags.push(...policy.customTags);\n }\n\n if (policy.customAttributes) {\n allowedAttr.push(...policy.customAttributes);\n }\n\n return {\n ...baseConfig,\n ALLOWED_TAGS: allowedTags,\n ALLOWED_ATTR: allowedAttr,\n };\n}\n\n/**\n * Sanitize code block HTML from syntax highlighters\n */\nexport function sanitizeCodeHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, {\n ALLOWED_TAGS: [\"pre\", \"code\", \"span\", \"div\", \"br\"],\n ALLOWED_ATTR: [\"class\", \"data-*\", \"style\"],\n KEEP_CONTENT: true,\n RETURN_DOM: false,\n });\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Sanitize math HTML from KaTeX\n */\nexport function sanitizeMathHTML(html: string): string {\n const out = resolveDOMPurify().sanitize(html, {\n ALLOWED_TAGS: [\n \"span\",\n \"div\",\n \"br\",\n // KaTeX specific tags\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n ],\n ALLOWED_ATTR: [\"class\", \"style\", \"data-*\"],\n KEEP_CONTENT: true,\n RETURN_DOM: false,\n });\n return typeof out === \"string\" ? out : String(out);\n}\n\n/**\n * Validate and sanitize URLs\n */\nexport function sanitizeURL(url: string): string | null {\n // Basic URL validation and sanitization\n try {\n const parsed = new URL(url, window.location.origin);\n\n // Allow http, https, mailto, and relative URLs\n if (![\"http:\", \"https:\", \"mailto:\", \"tel:\"].includes(parsed.protocol)) {\n return null;\n }\n\n return parsed.toString();\n } catch {\n // If URL parsing fails, treat as relative\n if (url.startsWith(\"/\") || url.startsWith(\"./\") || url.startsWith(\"../\")) {\n return url;\n }\n return null;\n }\n}\n\n/**\n * Content Security Policy utilities\n */\nexport const CSP_HEADERS = {\n // Strict CSP for markdown rendering\n strict: [\n \"default-src 'self'\",\n \"script-src 'self'\",\n \"style-src 'self' 'unsafe-inline'\", // Required for KaTeX\n \"img-src 'self' data: https:\",\n \"connect-src 'self'\",\n \"font-src 'self' data:\",\n \"require-trusted-types-for 'script'\",\n \"trusted-types markdown-renderer-v2\",\n ].join(\"; \"),\n\n // Relaxed CSP for development\n development: [\n \"default-src 'self'\",\n \"script-src 'self' 'unsafe-eval'\",\n \"style-src 'self' 'unsafe-inline'\",\n \"img-src 'self' data: https:\",\n \"connect-src 'self' ws:\",\n \"font-src 'self' data:\",\n ].join(\"; \"),\n};\n\n/**\n * Initialize security features\n */\nexport function initializeSecurity(): void {\n // Initialize Trusted Types\n initializeTrustedTypesPolicy();\n\n // Set up DOMPurify hooks if needed\n try {\n const purifier = resolveDOMPurify();\n if (typeof purifier.addHook !== \"function\") {\n return;\n }\n\n purifier.addHook(\"beforeSanitizeElements\", (node: unknown) => node);\n purifier.addHook(\"afterSanitizeElements\", (node: unknown) => node);\n } catch {\n // ignore missing DOM environment\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/streaming/custom-matcher.ts"],"sourcesContent":["// Custom streaming regex matcher to replace problematic incr-regex-package\n\nexport class CustomStreamingMatcher {\n private pattern: RegExp;\n private buffer = \"\";\n private possibleMatches: PossibleMatch[] = [];\n\n constructor(pattern: RegExp) {\n this.pattern = pattern;\n }\n\n addCharacter(char: string): MatchResult {\n this.buffer += char;\n\n const fullMatch = this.buffer.match(this.pattern);\n if (fullMatch && fullMatch.index === 0) {\n const match = {\n matched: true,\n content: fullMatch[0],\n length: fullMatch[0].length,\n isComplete: true,\n };\n\n this.buffer = this.buffer.slice(fullMatch[0].length);\n this.possibleMatches = [];\n\n return match;\n }\n\n const confidence = this.calculatePartialConfidence();\n\n return {\n matched: false,\n content: this.buffer,\n length: this.buffer.length,\n isComplete: false,\n confidence,\n };\n }\n\n addString(str: string): MatchResult[] {\n const results: MatchResult[] = [];\n\n for (const char of str) {\n const result = this.addCharacter(char);\n if (result.matched) {\n results.push(result);\n }\n }\n\n if (results.length === 0 && this.buffer.length > 0) {\n results.push({\n matched: false,\n content: this.buffer,\n length: this.buffer.length,\n isComplete: false,\n confidence: this.calculatePartialConfidence(),\n });\n }\n\n return results;\n }\n\n couldMatch(): boolean {\n if (this.buffer.length === 0) return true;\n\n const patternSource = this.pattern.source;\n const flags = this.pattern.flags;\n\n try {\n const partialPattern = new RegExp(`^${patternSource.replace(/\\\\$$/, \"\")}`, flags);\n const testString = this.buffer + \"X\".repeat(100);\n return partialPattern.test(testString);\n } catch {\n return this.isValidPrefix();\n }\n }\n\n reset(): void {\n this.buffer = \"\";\n this.possibleMatches = [];\n }\n\n getBuffer(): string {\n return this.buffer;\n }\n\n private calculatePartialConfidence(): number {\n if (this.buffer.length === 0) return 0;\n\n let confidence = 0.1;\n confidence += Math.min(0.4, this.buffer.length * 0.1);\n\n if (this.couldMatch()) {\n confidence += 0.3;\n }\n\n if (this.buffer.startsWith(\"$\")) confidence += 0.2;\n if (this.buffer.startsWith(\"$$\")) confidence += 0.3;\n\n return Math.min(1, confidence);\n }\n\n private isValidPrefix(): boolean {\n const patternStr = this.pattern.source;\n\n if (patternStr.includes(\"\\\\$\\\\$\") && (this.buffer === \"$\" || this.buffer === \"$$\")) {\n return true;\n }\n\n if (patternStr.includes(\"\\\\$\") && this.buffer === \"$\") {\n return true;\n }\n\n return false;\n }\n}\n\nexport interface MatchResult {\n matched: boolean;\n content: string;\n length: number;\n isComplete: boolean;\n confidence?: number;\n}\n\ninterface PossibleMatch {\n startIndex: number;\n pattern: RegExp;\n confidence: number;\n}\n\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEO,IAAM,yBAAN,MAA6B;AAAA,EAKlC,YAAY,SAAiB;AAH7B,SAAQ,SAAS;AACjB,SAAQ,kBAAmC,CAAC;AAG1C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAa,MAA2B;AACtC,SAAK,UAAU;AAEf,UAAM,YAAY,KAAK,OAAO,MAAM,KAAK,OAAO;AAChD,QAAI,aAAa,UAAU,UAAU,GAAG;AACtC,YAAM,QAAQ;AAAA,QACZ,SAAS;AAAA,QACT,SAAS,UAAU,CAAC;AAAA,QACpB,QAAQ,UAAU,CAAC,EAAE;AAAA,QACrB,YAAY;AAAA,MACd;AAEA,WAAK,SAAS,KAAK,OAAO,MAAM,UAAU,CAAC,EAAE,MAAM;AACnD,WAAK,kBAAkB,CAAC;AAExB,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,2BAA2B;AAEnD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK,OAAO;AAAA,MACpB,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,KAA4B;AACpC,UAAM,UAAyB,CAAC;AAEhC,eAAW,QAAQ,KAAK;AACtB,YAAM,SAAS,KAAK,aAAa,IAAI;AACrC,UAAI,OAAO,SAAS;AAClB,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,KAAK,KAAK,OAAO,SAAS,GAAG;AAClD,cAAQ,KAAK;AAAA,QACX,SAAS;AAAA,QACT,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK,OAAO;AAAA,QACpB,YAAY;AAAA,QACZ,YAAY,KAAK,2BAA2B;AAAA,MAC9C,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAsB;AACpB,QAAI,KAAK,OAAO,WAAW,EAAG,QAAO;AAErC,UAAM,gBAAgB,KAAK,QAAQ;AACnC,UAAM,QAAQ,KAAK,QAAQ;AAE3B,QAAI;AACF,YAAM,iBAAiB,IAAI,OAAO,IAAI,cAAc,QAAQ,QAAQ,EAAE,CAAC,IAAI,KAAK;AAChF,YAAM,aAAa,KAAK,SAAS,IAAI,OAAO,GAAG;AAC/C,aAAO,eAAe,KAAK,UAAU;AAAA,IACvC,QAAQ;AACN,aAAO,KAAK,cAAc;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,SAAK,kBAAkB,CAAC;AAAA,EAC1B;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,6BAAqC;AAC3C,QAAI,KAAK,OAAO,WAAW,EAAG,QAAO;AAErC,QAAI,aAAa;AACjB,kBAAc,KAAK,IAAI,KAAK,KAAK,OAAO,SAAS,GAAG;AAEpD,QAAI,KAAK,WAAW,GAAG;AACrB,oBAAc;AAAA,IAChB;AAEA,QAAI,KAAK,OAAO,WAAW,GAAG,EAAG,eAAc;AAC/C,QAAI,KAAK,OAAO,WAAW,IAAI,EAAG,eAAc;AAEhD,WAAO,KAAK,IAAI,GAAG,UAAU;AAAA,EAC/B;AAAA,EAEQ,gBAAyB;AAC/B,UAAM,aAAa,KAAK,QAAQ;AAEhC,QAAI,WAAW,SAAS,QAAQ,MAAM,KAAK,WAAW,OAAO,KAAK,WAAW,OAAO;AAClF,aAAO;AAAA,IACT;AAEA,QAAI,WAAW,SAAS,KAAK,KAAK,KAAK,WAAW,KAAK;AACrD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/streaming/custom-matcher.ts"],"sourcesContent":["// Custom streaming regex matcher to replace problematic incr-regex-package\n\nexport class CustomStreamingMatcher {\n private pattern: RegExp;\n private buffer = \"\";\n private possibleMatches: PossibleMatch[] = [];\n\n constructor(pattern: RegExp) {\n this.pattern = pattern;\n }\n\n addCharacter(char: string): MatchResult {\n this.buffer += char;\n\n const fullMatch = this.buffer.match(this.pattern);\n if (fullMatch && fullMatch.index === 0) {\n const match = {\n matched: true,\n content: fullMatch[0],\n length: fullMatch[0].length,\n isComplete: true,\n };\n\n this.buffer = this.buffer.slice(fullMatch[0].length);\n this.possibleMatches = [];\n\n return match;\n }\n\n const confidence = this.calculatePartialConfidence();\n\n return {\n matched: false,\n content: this.buffer,\n length: this.buffer.length,\n isComplete: false,\n confidence,\n };\n }\n\n addString(str: string): MatchResult[] {\n const results: MatchResult[] = [];\n\n for (const char of str) {\n const result = this.addCharacter(char);\n if (result.matched) {\n results.push(result);\n }\n }\n\n if (results.length === 0 && this.buffer.length > 0) {\n results.push({\n matched: false,\n content: this.buffer,\n length: this.buffer.length,\n isComplete: false,\n confidence: this.calculatePartialConfidence(),\n });\n }\n\n return results;\n }\n\n couldMatch(): boolean {\n if (this.buffer.length === 0) return true;\n\n const patternSource = this.pattern.source;\n const flags = this.pattern.flags;\n\n try {\n const partialPattern = new RegExp(`^${patternSource.replace(/\\\\$$/, \"\")}`, flags);\n const testString = this.buffer + \"X\".repeat(100);\n return partialPattern.test(testString);\n } catch {\n return this.isValidPrefix();\n }\n }\n\n reset(): void {\n this.buffer = \"\";\n this.possibleMatches = [];\n }\n\n getBuffer(): string {\n return this.buffer;\n }\n\n private calculatePartialConfidence(): number {\n if (this.buffer.length === 0) return 0;\n\n let confidence = 0.1;\n confidence += Math.min(0.4, this.buffer.length * 0.1);\n\n if (this.couldMatch()) {\n confidence += 0.3;\n }\n\n if (this.buffer.startsWith(\"$\")) confidence += 0.2;\n if (this.buffer.startsWith(\"$$\")) confidence += 0.3;\n\n return Math.min(1, confidence);\n }\n\n private isValidPrefix(): boolean {\n const patternStr = this.pattern.source;\n\n if (patternStr.includes(\"\\\\$\\\\$\") && (this.buffer === \"$\" || this.buffer === \"$$\")) {\n return true;\n }\n\n if (patternStr.includes(\"\\\\$\") && this.buffer === \"$\") {\n return true;\n }\n\n return false;\n }\n}\n\nexport interface MatchResult {\n matched: boolean;\n content: string;\n length: number;\n isComplete: boolean;\n confidence?: number;\n}\n\ninterface PossibleMatch {\n startIndex: number;\n pattern: RegExp;\n confidence: number;\n}\n\n"],"mappings":";AAEO,IAAM,yBAAN,MAA6B;AAAA,EAKlC,YAAY,SAAiB;AAH7B,SAAQ,SAAS;AACjB,SAAQ,kBAAmC,CAAC;AAG1C,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,aAAa,MAA2B;AACtC,SAAK,UAAU;AAEf,UAAM,YAAY,KAAK,OAAO,MAAM,KAAK,OAAO;AAChD,QAAI,aAAa,UAAU,UAAU,GAAG;AACtC,YAAM,QAAQ;AAAA,QACZ,SAAS;AAAA,QACT,SAAS,UAAU,CAAC;AAAA,QACpB,QAAQ,UAAU,CAAC,EAAE;AAAA,QACrB,YAAY;AAAA,MACd;AAEA,WAAK,SAAS,KAAK,OAAO,MAAM,UAAU,CAAC,EAAE,MAAM;AACnD,WAAK,kBAAkB,CAAC;AAExB,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,KAAK,2BAA2B;AAEnD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK,OAAO;AAAA,MACpB,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,KAA4B;AACpC,UAAM,UAAyB,CAAC;AAEhC,eAAW,QAAQ,KAAK;AACtB,YAAM,SAAS,KAAK,aAAa,IAAI;AACrC,UAAI,OAAO,SAAS;AAClB,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,QAAQ,WAAW,KAAK,KAAK,OAAO,SAAS,GAAG;AAClD,cAAQ,KAAK;AAAA,QACX,SAAS;AAAA,QACT,SAAS,KAAK;AAAA,QACd,QAAQ,KAAK,OAAO;AAAA,QACpB,YAAY;AAAA,QACZ,YAAY,KAAK,2BAA2B;AAAA,MAC9C,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAsB;AACpB,QAAI,KAAK,OAAO,WAAW,EAAG,QAAO;AAErC,UAAM,gBAAgB,KAAK,QAAQ;AACnC,UAAM,QAAQ,KAAK,QAAQ;AAE3B,QAAI;AACF,YAAM,iBAAiB,IAAI,OAAO,IAAI,cAAc,QAAQ,QAAQ,EAAE,CAAC,IAAI,KAAK;AAChF,YAAM,aAAa,KAAK,SAAS,IAAI,OAAO,GAAG;AAC/C,aAAO,eAAe,KAAK,UAAU;AAAA,IACvC,QAAQ;AACN,aAAO,KAAK,cAAc;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,SAAS;AACd,SAAK,kBAAkB,CAAC;AAAA,EAC1B;AAAA,EAEA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,6BAAqC;AAC3C,QAAI,KAAK,OAAO,WAAW,EAAG,QAAO;AAErC,QAAI,aAAa;AACjB,kBAAc,KAAK,IAAI,KAAK,KAAK,OAAO,SAAS,GAAG;AAEpD,QAAI,KAAK,WAAW,GAAG;AACrB,oBAAc;AAAA,IAChB;AAEA,QAAI,KAAK,OAAO,WAAW,GAAG,EAAG,eAAc;AAC/C,QAAI,KAAK,OAAO,WAAW,IAAI,EAAG,eAAc;AAEhD,WAAO,KAAK,IAAI,GAAG,UAAU;AAAA,EAC/B;AAAA,EAEQ,gBAAyB;AAC/B,UAAM,aAAa,KAAK,QAAQ;AAEhC,QAAI,WAAW,SAAS,QAAQ,MAAM,KAAK,WAAW,OAAO,KAAK,WAAW,OAAO;AAClF,aAAO;AAAA,IACT;AAEA,QAAI,WAAW,SAAS,KAAK,KAAK,KAAK,WAAW,KAAK;AACrD,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/types.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// Core contracts (public types) - V2 Markdown Renderer\n// Based on final_spec.md architecture\n\n/**\n * Core Block representation for streaming markdown\n */\nexport interface Block {\n id: string; // stable content hash\n type: \"paragraph\" | \"heading\" | \"code\" | \"list\" | \"blockquote\" | \"mdx\" | \"html\" | \"table\" | \"footnote-def\" | \"footnotes\" | string;\n isFinalized: boolean;\n payload: {\n raw: string;\n inline?: InlineNode[]; // serialized inline tree\n highlightedHtml?: string; // safe HTML (code only)\n sanitizedHtml?: string; // sanitized HTML (html blocks)\n compiledMdxRef?: { id: string } | null; // server-compiled artifact key\n compiledMdxModule?: CompiledMdxModule | null; // inline compiled module (worker/client side)\n meta?: Record<string, unknown>; // e.g., code fence info, table align\n range?: { from: number; to: number }; // original source offsets\n };\n}\n\nexport interface CompiledMdxModule {\n id: string;\n code: string;\n dependencies?: string[];\n source?: \"server\" | \"worker\";\n}\n\nexport interface MixedContentSegment {\n kind: \"text\" | \"html\" | \"mdx\";\n value: string;\n range?: { from: number; to: number };\n inline?: InlineNode[];\n sanitized?: string;\n status?: \"pending\" | \"compiled\" | \"error\";\n error?: string;\n}\n\nexport interface InlineHtmlDescriptor {\n tagName: string;\n attributes: Record<string, string>;\n raw: string;\n sanitized: string;\n rawInner: string;\n sanitizedInner: string;\n text: string;\n}\n\nexport type ProtectedRangeKind = \"math-inline\" | \"math-display\" | \"code-inline\" | \"code-block\" | \"code-fence\" | \"html-inline\" | \"html-block\" | \"autolink\";\n\nexport interface ProtectedRange {\n from: number;\n to: number;\n kind: ProtectedRangeKind;\n}\n\n/**\n * Inline node types for rich text rendering\n */\nexport type InlineNode =\n | { kind: \"text\"; text: string }\n | { kind: \"strong\"; children: InlineNode[] }\n | { kind: \"em\"; children: InlineNode[] }\n | { kind: \"strike\"; children: InlineNode[] }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; href?: string; title?: string; children: InlineNode[] }\n | { kind: \"image\"; src: string; alt?: string; title?: string }\n | { kind: \"br\" }\n // extensible:\n | { kind: \"mention\"; handle: string }\n | { kind: \"citation\"; id: string }\n | { kind: \"math-inline\"; tex: string }\n | { kind: \"math-display\"; tex: string }\n | { kind: \"footnote-ref\"; label: string; number?: number };\n\n/**\n * Worker communication protocol\n */\nexport type WorkerIn =\n | {\n type: \"INIT\";\n initialContent?: string;\n prewarmLangs?: string[];\n docPlugins?: { footnotes?: boolean; html?: boolean; mdx?: boolean; tables?: boolean; callouts?: boolean };\n mdx?: { compileMode?: \"server\" | \"worker\" };\n }\n | { type: \"APPEND\"; text: string }\n | { type: \"FINALIZE\" }\n | { type: \"MDX_COMPILED\"; blockId: string; compiledId: string }\n | { type: \"MDX_ERROR\"; blockId: string; error?: string }\n | { type: \"SET_CREDITS\"; credits: number };\n\nexport type WorkerPhase = WorkerIn[\"type\"] | \"UNKNOWN\";\n\nexport interface WorkerErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n}\n\nexport type WorkerOut =\n | { type: \"INITIALIZED\"; blocks: Block[] }\n | { type: \"PATCH\"; tx: number; patches: Patch[]; metrics?: PatchMetrics }\n | { type: \"RESET\"; reason: string }\n | { type: \"METRICS\"; metrics: PerformanceMetrics }\n | { type: \"ERROR\"; phase: WorkerPhase; error: WorkerErrorPayload; blockId?: string; meta?: Record<string, unknown>; timestamp?: number };\n\n/**\n * Inline plugin system\n */\nexport interface InlinePlugin {\n id: string;\n priority: number; // lower runs earlier\n apply(nodes: InlineNode[]): InlineNode[];\n}\n\nexport interface RegexInlinePlugin extends InlinePlugin {\n re: RegExp; // global; must not match across newlines\n toNode: (match: RegExpExecArray) => InlineNode | InlineNode[];\n /**\n * Optional fast-path predicate. If provided and returns false for a given text node,\n * the regex is skipped for that node.\n */\n fastCheck?: (text: string) => boolean;\n}\n\nexport interface ASTInlinePlugin extends InlinePlugin {\n visit: (node: InlineNode, ctx: { replace(node: InlineNode, next: InlineNode | InlineNode[]): void }) => void;\n}\n\n/**\n * Language normalization\n */\nexport const LANGUAGE_ALIASES: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"tsx\",\n js: \"javascript\",\n sh: \"bash\",\n shell: \"bash\",\n zsh: \"bash\",\n yml: \"yaml\",\n tf: \"terraform\",\n py: \"python\",\n rb: \"ruby\",\n // ...extendable map\n};\n\n/**\n * Performance metrics from worker\n */\nexport interface PerformanceMetrics {\n tx?: number;\n timestamp?: number;\n parseMs?: number;\n parseTime: number;\n enrichMs?: number;\n diffMs?: number;\n serializeMs?: number;\n highlightTime: number;\n shikiMs?: number;\n mdxDetectMs?: number;\n patchBytes?: number;\n patchCount?: number;\n queueDepth?: number;\n blocksProduced: number;\n grammarEngine: \"js\" | \"wasm\";\n blockCountByType?: Record<string, number>;\n blockEnrichMsByType?: Record<string, number>;\n blockSizeByType?: Record<string, number>;\n highlightByLanguage?: Record<string, { timeMs: number; count: number }>;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\n/**\n * Incremental patch protocol (initial block-level implementation).\n * These types will evolve to support subtree updates.\n */\nexport const PATCH_ROOT_ID = \"__root__\";\n\nexport interface NodeSnapshot {\n id: string;\n type: string;\n props?: Record<string, unknown>;\n children?: NodeSnapshot[];\n range?: { from: number; to: number };\n meta?: Record<string, unknown>;\n}\n\nexport interface NodePath {\n blockId: string;\n nodeId?: string;\n indexPath?: number[];\n}\n\nexport type Patch =\n | { op: \"insertChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"deleteChild\"; at: NodePath; index: number }\n | { op: \"replaceChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"setProps\"; at: NodePath; props: Record<string, unknown> }\n | { op: \"setPropsBatch\"; entries: SetPropsBatchEntry[] }\n | { op: \"finalize\"; at: NodePath }\n | { op: \"reorder\"; at: NodePath; from: number; to: number; count: number }\n | {\n op: \"appendLines\";\n at: NodePath;\n startIndex: number;\n lines: string[];\n highlight?: Array<string | null>;\n }\n | {\n op: \"setHTML\";\n at: NodePath;\n html: string;\n policy?: string;\n block?: Block;\n meta?: Record<string, unknown>;\n sanitized?: boolean;\n };\n\nexport interface PatchMetrics {\n patchCount: number;\n changedBlocks: number;\n diffTime?: number;\n parseTime?: number;\n enrichTime?: number;\n queueDepth?: number;\n patchBytes?: number;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\nexport interface SetPropsBatchEntry {\n at: NodePath;\n props: Record<string, unknown>;\n}\n\nexport interface CoalescingMetrics {\n inputPatchCount: number;\n outputPatchCount: number;\n coalescedCount: number;\n durationMs: number;\n appendLinesCoalesced: number;\n setPropsCoalesced: number;\n insertChildCoalesced: number;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsIO,IAAM,mBAA2C;AAAA,EACtD,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA;AAEN;AAkCO,IAAM,gBAAgB;","names":[]}
|
package/dist/types.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["// Core contracts (public types) - V2 Markdown Renderer\n// Based on final_spec.md architecture\n\n/**\n * Core Block representation for streaming markdown\n */\nexport interface Block {\n id: string; // stable content hash\n type: \"paragraph\" | \"heading\" | \"code\" | \"list\" | \"blockquote\" | \"mdx\" | \"html\" | \"table\" | \"footnote-def\" | \"footnotes\" | string;\n isFinalized: boolean;\n payload: {\n raw: string;\n inline?: InlineNode[]; // serialized inline tree\n highlightedHtml?: string; // safe HTML (code only)\n sanitizedHtml?: string; // sanitized HTML (html blocks)\n compiledMdxRef?: { id: string } | null; // server-compiled artifact key\n compiledMdxModule?: CompiledMdxModule | null; // inline compiled module (worker/client side)\n meta?: Record<string, unknown>; // e.g., code fence info, table align\n range?: { from: number; to: number }; // original source offsets\n };\n}\n\nexport interface CompiledMdxModule {\n id: string;\n code: string;\n dependencies?: string[];\n source?: \"server\" | \"worker\";\n}\n\nexport interface MixedContentSegment {\n kind: \"text\" | \"html\" | \"mdx\";\n value: string;\n range?: { from: number; to: number };\n inline?: InlineNode[];\n sanitized?: string;\n status?: \"pending\" | \"compiled\" | \"error\";\n error?: string;\n}\n\nexport interface InlineHtmlDescriptor {\n tagName: string;\n attributes: Record<string, string>;\n raw: string;\n sanitized: string;\n rawInner: string;\n sanitizedInner: string;\n text: string;\n}\n\nexport type ProtectedRangeKind = \"math-inline\" | \"math-display\" | \"code-inline\" | \"code-block\" | \"code-fence\" | \"html-inline\" | \"html-block\" | \"autolink\";\n\nexport interface ProtectedRange {\n from: number;\n to: number;\n kind: ProtectedRangeKind;\n}\n\n/**\n * Inline node types for rich text rendering\n */\nexport type InlineNode =\n | { kind: \"text\"; text: string }\n | { kind: \"strong\"; children: InlineNode[] }\n | { kind: \"em\"; children: InlineNode[] }\n | { kind: \"strike\"; children: InlineNode[] }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; href?: string; title?: string; children: InlineNode[] }\n | { kind: \"image\"; src: string; alt?: string; title?: string }\n | { kind: \"br\" }\n // extensible:\n | { kind: \"mention\"; handle: string }\n | { kind: \"citation\"; id: string }\n | { kind: \"math-inline\"; tex: string }\n | { kind: \"math-display\"; tex: string }\n | { kind: \"footnote-ref\"; label: string; number?: number };\n\n/**\n * Worker communication protocol\n */\nexport type WorkerIn =\n | {\n type: \"INIT\";\n initialContent?: string;\n prewarmLangs?: string[];\n docPlugins?: { footnotes?: boolean; html?: boolean; mdx?: boolean; tables?: boolean; callouts?: boolean };\n mdx?: { compileMode?: \"server\" | \"worker\" };\n }\n | { type: \"APPEND\"; text: string }\n | { type: \"FINALIZE\" }\n | { type: \"MDX_COMPILED\"; blockId: string; compiledId: string }\n | { type: \"MDX_ERROR\"; blockId: string; error?: string }\n | { type: \"SET_CREDITS\"; credits: number };\n\nexport type WorkerPhase = WorkerIn[\"type\"] | \"UNKNOWN\";\n\nexport interface WorkerErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n}\n\nexport type WorkerOut =\n | { type: \"INITIALIZED\"; blocks: Block[] }\n | { type: \"PATCH\"; tx: number; patches: Patch[]; metrics?: PatchMetrics }\n | { type: \"RESET\"; reason: string }\n | { type: \"METRICS\"; metrics: PerformanceMetrics }\n | { type: \"ERROR\"; phase: WorkerPhase; error: WorkerErrorPayload; blockId?: string; meta?: Record<string, unknown>; timestamp?: number };\n\n/**\n * Inline plugin system\n */\nexport interface InlinePlugin {\n id: string;\n priority: number; // lower runs earlier\n apply(nodes: InlineNode[]): InlineNode[];\n}\n\nexport interface RegexInlinePlugin extends InlinePlugin {\n re: RegExp; // global; must not match across newlines\n toNode: (match: RegExpExecArray) => InlineNode | InlineNode[];\n /**\n * Optional fast-path predicate. If provided and returns false for a given text node,\n * the regex is skipped for that node.\n */\n fastCheck?: (text: string) => boolean;\n}\n\nexport interface ASTInlinePlugin extends InlinePlugin {\n visit: (node: InlineNode, ctx: { replace(node: InlineNode, next: InlineNode | InlineNode[]): void }) => void;\n}\n\n/**\n * Language normalization\n */\nexport const LANGUAGE_ALIASES: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"tsx\",\n js: \"javascript\",\n sh: \"bash\",\n shell: \"bash\",\n zsh: \"bash\",\n yml: \"yaml\",\n tf: \"terraform\",\n py: \"python\",\n rb: \"ruby\",\n // ...extendable map\n};\n\n/**\n * Performance metrics from worker\n */\nexport interface PerformanceMetrics {\n tx?: number;\n timestamp?: number;\n parseMs?: number;\n parseTime: number;\n enrichMs?: number;\n diffMs?: number;\n serializeMs?: number;\n highlightTime: number;\n shikiMs?: number;\n mdxDetectMs?: number;\n patchBytes?: number;\n patchCount?: number;\n queueDepth?: number;\n blocksProduced: number;\n grammarEngine: \"js\" | \"wasm\";\n blockCountByType?: Record<string, number>;\n blockEnrichMsByType?: Record<string, number>;\n blockSizeByType?: Record<string, number>;\n highlightByLanguage?: Record<string, { timeMs: number; count: number }>;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\n/**\n * Incremental patch protocol (initial block-level implementation).\n * These types will evolve to support subtree updates.\n */\nexport const PATCH_ROOT_ID = \"__root__\";\n\nexport interface NodeSnapshot {\n id: string;\n type: string;\n props?: Record<string, unknown>;\n children?: NodeSnapshot[];\n range?: { from: number; to: number };\n meta?: Record<string, unknown>;\n}\n\nexport interface NodePath {\n blockId: string;\n nodeId?: string;\n indexPath?: number[];\n}\n\nexport type Patch =\n | { op: \"insertChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"deleteChild\"; at: NodePath; index: number }\n | { op: \"replaceChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"setProps\"; at: NodePath; props: Record<string, unknown> }\n | { op: \"setPropsBatch\"; entries: SetPropsBatchEntry[] }\n | { op: \"finalize\"; at: NodePath }\n | { op: \"reorder\"; at: NodePath; from: number; to: number; count: number }\n | {\n op: \"appendLines\";\n at: NodePath;\n startIndex: number;\n lines: string[];\n highlight?: Array<string | null>;\n }\n | {\n op: \"setHTML\";\n at: NodePath;\n html: string;\n policy?: string;\n block?: Block;\n meta?: Record<string, unknown>;\n sanitized?: boolean;\n };\n\nexport interface PatchMetrics {\n patchCount: number;\n changedBlocks: number;\n diffTime?: number;\n parseTime?: number;\n enrichTime?: number;\n queueDepth?: number;\n patchBytes?: number;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\nexport interface SetPropsBatchEntry {\n at: NodePath;\n props: Record<string, unknown>;\n}\n\nexport interface CoalescingMetrics {\n inputPatchCount: number;\n outputPatchCount: number;\n coalescedCount: number;\n durationMs: number;\n appendLinesCoalesced: number;\n setPropsCoalesced: number;\n insertChildCoalesced: number;\n}\n"],"mappings":";AAsIO,IAAM,mBAA2C;AAAA,EACtD,IAAI;AAAA,EACJ,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,KAAK;AAAA,EACL,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA;AAEN;AAkCO,IAAM,gBAAgB;","names":[]}
|
package/dist/utils.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/types.ts"],"sourcesContent":["// Utility functions for V2 Markdown Renderer\n\nimport { type Block, LANGUAGE_ALIASES, type ProtectedRange } from \"./types\";\n\n// Browser-compatible crypto import\nconst createHash = (_algorithm: string) => {\n let hash = 0;\n return {\n update(data: string) {\n for (let i = 0; i < data.length; i++) {\n hash = ((hash << 5) - hash + data.charCodeAt(i)) & 0xffffffff;\n }\n return this;\n },\n digest(_encoding: string) {\n return Math.abs(hash).toString(16);\n },\n };\n};\n\n/**\n * Generate stable content hash for blocks\n */\nexport function generateBlockId(content: string, type: string): string {\n const hash = createHash(\"sha256\");\n hash.update(`${type}:${content}`);\n return hash.digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Normalize language names for syntax highlighting\n */\nexport function normalizeLang(raw?: string): string {\n const k = (raw || \"\").trim().toLowerCase();\n return LANGUAGE_ALIASES[k] ?? (k || \"text\");\n}\n\n/**\n * Apply tail splice (immutability & guards)\n */\nexport function applyUpdate(blocks: Block[], update: { start: number; tail: Block[] }): Block[] {\n // Guards\n if (update.start < 0 || update.start > blocks.length) return blocks;\n\n // Ensure finalized invariants\n for (let i = 0; i < update.start; i++) {\n if (!blocks[i].isFinalized) {\n throw new Error(\"Invariant violated: non-finalized before start\");\n }\n }\n\n // Splice tail immutably\n const next = blocks.slice(0, update.start).concat(update.tail);\n\n // Ensure only last is dirty (if any)\n for (let i = 0; i < next.length - 1; i++) {\n if (!next[i].isFinalized) throw new Error(\"Only tail may be dirty\");\n }\n\n return next;\n}\n\n/**\n * Generate stable React keys for blocks\n */\nexport function getBlockKey(block: Block): string {\n return `${block.id}:${block.isFinalized ? 1 : 0}`;\n}\n\n/**\n * Check if content contains MDX-like syntax\n */\nexport function detectMDX(content: string, options?: { protectedRanges?: ReadonlyArray<ProtectedRange>; baseOffset?: number }): boolean {\n const inlineCodeRanges = collectInlineCodeRanges(content);\n\n // Basic heuristic for MDX detection\n const componentPattern = /<([A-Z][\\w-]*)(\\s|\\/?>)/g;\n let componentMatch: RegExpExecArray | null = componentPattern.exec(content);\n while (componentMatch !== null) {\n const start = componentMatch.index;\n const end = start + componentMatch[0].length;\n if (!isWithinRanges(start, end, inlineCodeRanges)) {\n return true;\n }\n componentMatch = componentPattern.exec(content);\n }\n\n // Detect import/export statements (MDX/ESM)\n if (/(^|\\n)\\s*(import|export)\\s/.test(content)) {\n return true;\n }\n\n // Detect inline JSX expressions while ignoring TeX/LaTeX braces (`\\command{}`)\n const expressionPattern = /\\{[^{}]+\\}/g;\n const protectedRanges = options?.protectedRanges ?? [];\n const baseOffset = typeof options?.baseOffset === \"number\" ? options.baseOffset : 0;\n const protectedKinds = protectedRanges.length ? new Set<ProtectedRange[\"kind\"]>([\"math-inline\", \"math-display\", \"code-inline\", \"code-block\"]) : null;\n\n for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {\n const index = match.index;\n const prev = index > 0 ? content[index - 1] : \"\";\n if (prev === \"\\\\\" || prev === \"$\" || prev === \"^\" || prev === \"_\") {\n continue;\n }\n const prefix = content.slice(Math.max(0, index - 8), index);\n if (/\\\\[a-zA-Z]+$/.test(prefix.trimEnd())) {\n continue;\n }\n const inner = match[0].slice(1, -1).trimStart();\n if (/^[\\s0-9+\\-*/(),.=\\\\^_]+$/.test(inner)) {\n continue;\n }\n if (inner.startsWith(\"\\\\\") || inner.startsWith(\"^\") || inner.startsWith(\"_\") || inner.startsWith(\"$\")) {\n continue;\n }\n const exprStart = index;\n const exprEnd = exprStart + match[0].length;\n if (isWithinRanges(exprStart, exprEnd, inlineCodeRanges)) {\n continue;\n }\n if (protectedKinds) {\n const absoluteStart = baseOffset + index;\n const absoluteEnd = absoluteStart + match[0].length;\n const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);\n if (covered) {\n continue;\n }\n }\n return true;\n }\n\n return false;\n}\n\nfunction collectInlineCodeRanges(content: string): Array<{ from: number; to: number }> {\n if (!content) return [];\n const ranges: Array<{ from: number; to: number }> = [];\n const codeSpanRegex = /`(?:\\\\`|[^`])*?`/g;\n let codeMatch: RegExpExecArray | null = codeSpanRegex.exec(content);\n while (codeMatch !== null) {\n ranges.push({ from: codeMatch.index, to: codeMatch.index + codeMatch[0].length });\n codeMatch = codeSpanRegex.exec(content);\n }\n return ranges;\n}\n\nfunction isWithinRanges(start: number, end: number, ranges: Array<{ from: number; to: number }>): boolean {\n return ranges.some((range) => range.from <= start && range.to >= end);\n}\n\n/**\n * Parse code fence info (language and metadata)\n */\nexport function parseCodeFenceInfo(info: string): { lang: string; meta: Record<string, unknown> } {\n const parts = info.trim().split(/\\s+/);\n const lang = normalizeLang(parts[0]);\n\n const meta: Record<string, unknown> = {};\n for (let i = 1; i < parts.length; i++) {\n const part = parts[i];\n if (part.includes(\"=\")) {\n const [key, value] = part.split(\"=\", 2);\n meta[key] = value;\n } else {\n meta[part] = true;\n }\n }\n\n return { lang, meta };\n}\n\n/**\n * Normalize blockquote text by removing a single leading \">\" (and optional whitespace) per line\n * while preserving internal blank lines that represent separate paragraphs.\n */\nexport function normalizeBlockquoteText(raw: string): string {\n if (!raw) return \"\";\n const lines = raw.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\n const normalized = lines.map((line) => line.replace(/^\\s*>?\\s?/, \"\"));\n // Trim leading/trailing empty lines but preserve internal spacing\n while (normalized.length > 0 && normalized[0].trim().length === 0) {\n normalized.shift();\n }\n while (normalized.length > 0 && normalized[normalized.length - 1].trim().length === 0) {\n normalized.pop();\n }\n return normalized.join(\"\\n\");\n}\n\n/**\n * Debounce function for performance optimization\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(func: T, delay: number): (...args: Parameters<T>) => void {\n let timeoutId: NodeJS.Timeout;\n\n return (...args: Parameters<T>) => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => func(...args), delay);\n };\n}\n\n/**\n * Performance measurement utility\n */\nexport class PerformanceTimer {\n private marks: Map<string, number> = new Map();\n\n mark(name: string): void {\n this.marks.set(name, performance.now());\n }\n\n measure(name: string): number | null {\n const start = this.marks.get(name);\n if (start === undefined) return null;\n return performance.now() - start;\n }\n\n reset(): void {\n this.marks.clear();\n }\n}\n\n/**\n * Memory-efficient string manipulation\n */\nexport function removeHeadingMarkers(input: string): string {\n return input\n .replace(/^(#{1,6})\\s+/, \"\")\n .replace(/\\s+={2,}\\s*$|\\s+-{2,}\\s*$/m, \"\")\n .trim();\n}\n\nexport class StringBuffer {\n private chunks: string[] = [];\n private length = 0;\n\n append(str: string): void {\n this.chunks.push(str);\n this.length += str.length;\n }\n\n toString(): string {\n const result = this.chunks.join(\"\");\n this.chunks = [result]; // Consolidate for future operations\n return result;\n }\n\n getLength(): number {\n return this.length;\n }\n\n clear(): void {\n this.chunks = [];\n this.length = 0;\n }\n}\n","// Core contracts (public types) - V2 Markdown Renderer\n// Based on final_spec.md architecture\n\n/**\n * Core Block representation for streaming markdown\n */\nexport interface Block {\n id: string; // stable content hash\n type: \"paragraph\" | \"heading\" | \"code\" | \"list\" | \"blockquote\" | \"mdx\" | \"html\" | \"table\" | \"footnote-def\" | \"footnotes\" | string;\n isFinalized: boolean;\n payload: {\n raw: string;\n inline?: InlineNode[]; // serialized inline tree\n highlightedHtml?: string; // safe HTML (code only)\n sanitizedHtml?: string; // sanitized HTML (html blocks)\n compiledMdxRef?: { id: string } | null; // server-compiled artifact key\n compiledMdxModule?: CompiledMdxModule | null; // inline compiled module (worker/client side)\n meta?: Record<string, unknown>; // e.g., code fence info, table align\n range?: { from: number; to: number }; // original source offsets\n };\n}\n\nexport interface CompiledMdxModule {\n id: string;\n code: string;\n dependencies?: string[];\n source?: \"server\" | \"worker\";\n}\n\nexport interface MixedContentSegment {\n kind: \"text\" | \"html\" | \"mdx\";\n value: string;\n range?: { from: number; to: number };\n inline?: InlineNode[];\n sanitized?: string;\n status?: \"pending\" | \"compiled\" | \"error\";\n error?: string;\n}\n\nexport interface InlineHtmlDescriptor {\n tagName: string;\n attributes: Record<string, string>;\n raw: string;\n sanitized: string;\n rawInner: string;\n sanitizedInner: string;\n text: string;\n}\n\nexport type ProtectedRangeKind = \"math-inline\" | \"math-display\" | \"code-inline\" | \"code-block\" | \"code-fence\" | \"html-inline\" | \"html-block\" | \"autolink\";\n\nexport interface ProtectedRange {\n from: number;\n to: number;\n kind: ProtectedRangeKind;\n}\n\n/**\n * Inline node types for rich text rendering\n */\nexport type InlineNode =\n | { kind: \"text\"; text: string }\n | { kind: \"strong\"; children: InlineNode[] }\n | { kind: \"em\"; children: InlineNode[] }\n | { kind: \"strike\"; children: InlineNode[] }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; href?: string; title?: string; children: InlineNode[] }\n | { kind: \"image\"; src: string; alt?: string; title?: string }\n | { kind: \"br\" }\n // extensible:\n | { kind: \"mention\"; handle: string }\n | { kind: \"citation\"; id: string }\n | { kind: \"math-inline\"; tex: string }\n | { kind: \"math-display\"; tex: string }\n | { kind: \"footnote-ref\"; label: string; number?: number };\n\n/**\n * Worker communication protocol\n */\nexport type WorkerIn =\n | {\n type: \"INIT\";\n initialContent?: string;\n prewarmLangs?: string[];\n docPlugins?: { footnotes?: boolean; html?: boolean; mdx?: boolean; tables?: boolean; callouts?: boolean };\n mdx?: { compileMode?: \"server\" | \"worker\" };\n }\n | { type: \"APPEND\"; text: string }\n | { type: \"FINALIZE\" }\n | { type: \"MDX_COMPILED\"; blockId: string; compiledId: string }\n | { type: \"MDX_ERROR\"; blockId: string; error?: string }\n | { type: \"SET_CREDITS\"; credits: number };\n\nexport type WorkerPhase = WorkerIn[\"type\"] | \"UNKNOWN\";\n\nexport interface WorkerErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n}\n\nexport type WorkerOut =\n | { type: \"INITIALIZED\"; blocks: Block[] }\n | { type: \"PATCH\"; tx: number; patches: Patch[]; metrics?: PatchMetrics }\n | { type: \"RESET\"; reason: string }\n | { type: \"METRICS\"; metrics: PerformanceMetrics }\n | { type: \"ERROR\"; phase: WorkerPhase; error: WorkerErrorPayload; blockId?: string; meta?: Record<string, unknown>; timestamp?: number };\n\n/**\n * Inline plugin system\n */\nexport interface InlinePlugin {\n id: string;\n priority: number; // lower runs earlier\n apply(nodes: InlineNode[]): InlineNode[];\n}\n\nexport interface RegexInlinePlugin extends InlinePlugin {\n re: RegExp; // global; must not match across newlines\n toNode: (match: RegExpExecArray) => InlineNode | InlineNode[];\n /**\n * Optional fast-path predicate. If provided and returns false for a given text node,\n * the regex is skipped for that node.\n */\n fastCheck?: (text: string) => boolean;\n}\n\nexport interface ASTInlinePlugin extends InlinePlugin {\n visit: (node: InlineNode, ctx: { replace(node: InlineNode, next: InlineNode | InlineNode[]): void }) => void;\n}\n\n/**\n * Language normalization\n */\nexport const LANGUAGE_ALIASES: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"tsx\",\n js: \"javascript\",\n sh: \"bash\",\n shell: \"bash\",\n zsh: \"bash\",\n yml: \"yaml\",\n tf: \"terraform\",\n py: \"python\",\n rb: \"ruby\",\n // ...extendable map\n};\n\n/**\n * Performance metrics from worker\n */\nexport interface PerformanceMetrics {\n tx?: number;\n timestamp?: number;\n parseMs?: number;\n parseTime: number;\n enrichMs?: number;\n diffMs?: number;\n serializeMs?: number;\n highlightTime: number;\n shikiMs?: number;\n mdxDetectMs?: number;\n patchBytes?: number;\n patchCount?: number;\n queueDepth?: number;\n blocksProduced: number;\n grammarEngine: \"js\" | \"wasm\";\n blockCountByType?: Record<string, number>;\n blockEnrichMsByType?: Record<string, number>;\n blockSizeByType?: Record<string, number>;\n highlightByLanguage?: Record<string, { timeMs: number; count: number }>;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\n/**\n * Incremental patch protocol (initial block-level implementation).\n * These types will evolve to support subtree updates.\n */\nexport const PATCH_ROOT_ID = \"__root__\";\n\nexport interface NodeSnapshot {\n id: string;\n type: string;\n props?: Record<string, unknown>;\n children?: NodeSnapshot[];\n range?: { from: number; to: number };\n meta?: Record<string, unknown>;\n}\n\nexport interface NodePath {\n blockId: string;\n nodeId?: string;\n indexPath?: number[];\n}\n\nexport type Patch =\n | { op: \"insertChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"deleteChild\"; at: NodePath; index: number }\n | { op: \"replaceChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"setProps\"; at: NodePath; props: Record<string, unknown> }\n | { op: \"setPropsBatch\"; entries: SetPropsBatchEntry[] }\n | { op: \"finalize\"; at: NodePath }\n | { op: \"reorder\"; at: NodePath; from: number; to: number; count: number }\n | {\n op: \"appendLines\";\n at: NodePath;\n startIndex: number;\n lines: string[];\n highlight?: Array<string | null>;\n }\n | {\n op: \"setHTML\";\n at: NodePath;\n html: string;\n policy?: string;\n block?: Block;\n meta?: Record<string, unknown>;\n sanitized?: boolean;\n };\n\nexport interface PatchMetrics {\n patchCount: number;\n changedBlocks: number;\n diffTime?: number;\n parseTime?: number;\n enrichTime?: number;\n queueDepth?: number;\n patchBytes?: number;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\nexport interface SetPropsBatchEntry {\n at: NodePath;\n props: Record<string, unknown>;\n}\n\nexport interface CoalescingMetrics {\n inputPatchCount: number;\n outputPatchCount: number;\n coalescedCount: number;\n durationMs: number;\n appendLinesCoalesced: number;\n setPropsCoalesced: number;\n insertChildCoalesced: number;\n}\n"],"mappings":"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","names":[]}
|
package/dist/utils.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/utils.ts"],"sourcesContent":["// Core contracts (public types) - V2 Markdown Renderer\n// Based on final_spec.md architecture\n\n/**\n * Core Block representation for streaming markdown\n */\nexport interface Block {\n id: string; // stable content hash\n type: \"paragraph\" | \"heading\" | \"code\" | \"list\" | \"blockquote\" | \"mdx\" | \"html\" | \"table\" | \"footnote-def\" | \"footnotes\" | string;\n isFinalized: boolean;\n payload: {\n raw: string;\n inline?: InlineNode[]; // serialized inline tree\n highlightedHtml?: string; // safe HTML (code only)\n sanitizedHtml?: string; // sanitized HTML (html blocks)\n compiledMdxRef?: { id: string } | null; // server-compiled artifact key\n compiledMdxModule?: CompiledMdxModule | null; // inline compiled module (worker/client side)\n meta?: Record<string, unknown>; // e.g., code fence info, table align\n range?: { from: number; to: number }; // original source offsets\n };\n}\n\nexport interface CompiledMdxModule {\n id: string;\n code: string;\n dependencies?: string[];\n source?: \"server\" | \"worker\";\n}\n\nexport interface MixedContentSegment {\n kind: \"text\" | \"html\" | \"mdx\";\n value: string;\n range?: { from: number; to: number };\n inline?: InlineNode[];\n sanitized?: string;\n status?: \"pending\" | \"compiled\" | \"error\";\n error?: string;\n}\n\nexport interface InlineHtmlDescriptor {\n tagName: string;\n attributes: Record<string, string>;\n raw: string;\n sanitized: string;\n rawInner: string;\n sanitizedInner: string;\n text: string;\n}\n\nexport type ProtectedRangeKind = \"math-inline\" | \"math-display\" | \"code-inline\" | \"code-block\" | \"code-fence\" | \"html-inline\" | \"html-block\" | \"autolink\";\n\nexport interface ProtectedRange {\n from: number;\n to: number;\n kind: ProtectedRangeKind;\n}\n\n/**\n * Inline node types for rich text rendering\n */\nexport type InlineNode =\n | { kind: \"text\"; text: string }\n | { kind: \"strong\"; children: InlineNode[] }\n | { kind: \"em\"; children: InlineNode[] }\n | { kind: \"strike\"; children: InlineNode[] }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; href?: string; title?: string; children: InlineNode[] }\n | { kind: \"image\"; src: string; alt?: string; title?: string }\n | { kind: \"br\" }\n // extensible:\n | { kind: \"mention\"; handle: string }\n | { kind: \"citation\"; id: string }\n | { kind: \"math-inline\"; tex: string }\n | { kind: \"math-display\"; tex: string }\n | { kind: \"footnote-ref\"; label: string; number?: number };\n\n/**\n * Worker communication protocol\n */\nexport type WorkerIn =\n | {\n type: \"INIT\";\n initialContent?: string;\n prewarmLangs?: string[];\n docPlugins?: { footnotes?: boolean; html?: boolean; mdx?: boolean; tables?: boolean; callouts?: boolean };\n mdx?: { compileMode?: \"server\" | \"worker\" };\n }\n | { type: \"APPEND\"; text: string }\n | { type: \"FINALIZE\" }\n | { type: \"MDX_COMPILED\"; blockId: string; compiledId: string }\n | { type: \"MDX_ERROR\"; blockId: string; error?: string }\n | { type: \"SET_CREDITS\"; credits: number };\n\nexport type WorkerPhase = WorkerIn[\"type\"] | \"UNKNOWN\";\n\nexport interface WorkerErrorPayload {\n message: string;\n name?: string;\n stack?: string;\n}\n\nexport type WorkerOut =\n | { type: \"INITIALIZED\"; blocks: Block[] }\n | { type: \"PATCH\"; tx: number; patches: Patch[]; metrics?: PatchMetrics }\n | { type: \"RESET\"; reason: string }\n | { type: \"METRICS\"; metrics: PerformanceMetrics }\n | { type: \"ERROR\"; phase: WorkerPhase; error: WorkerErrorPayload; blockId?: string; meta?: Record<string, unknown>; timestamp?: number };\n\n/**\n * Inline plugin system\n */\nexport interface InlinePlugin {\n id: string;\n priority: number; // lower runs earlier\n apply(nodes: InlineNode[]): InlineNode[];\n}\n\nexport interface RegexInlinePlugin extends InlinePlugin {\n re: RegExp; // global; must not match across newlines\n toNode: (match: RegExpExecArray) => InlineNode | InlineNode[];\n /**\n * Optional fast-path predicate. If provided and returns false for a given text node,\n * the regex is skipped for that node.\n */\n fastCheck?: (text: string) => boolean;\n}\n\nexport interface ASTInlinePlugin extends InlinePlugin {\n visit: (node: InlineNode, ctx: { replace(node: InlineNode, next: InlineNode | InlineNode[]): void }) => void;\n}\n\n/**\n * Language normalization\n */\nexport const LANGUAGE_ALIASES: Record<string, string> = {\n ts: \"typescript\",\n tsx: \"tsx\",\n js: \"javascript\",\n sh: \"bash\",\n shell: \"bash\",\n zsh: \"bash\",\n yml: \"yaml\",\n tf: \"terraform\",\n py: \"python\",\n rb: \"ruby\",\n // ...extendable map\n};\n\n/**\n * Performance metrics from worker\n */\nexport interface PerformanceMetrics {\n tx?: number;\n timestamp?: number;\n parseMs?: number;\n parseTime: number;\n enrichMs?: number;\n diffMs?: number;\n serializeMs?: number;\n highlightTime: number;\n shikiMs?: number;\n mdxDetectMs?: number;\n patchBytes?: number;\n patchCount?: number;\n queueDepth?: number;\n blocksProduced: number;\n grammarEngine: \"js\" | \"wasm\";\n blockCountByType?: Record<string, number>;\n blockEnrichMsByType?: Record<string, number>;\n blockSizeByType?: Record<string, number>;\n highlightByLanguage?: Record<string, { timeMs: number; count: number }>;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\n/**\n * Incremental patch protocol (initial block-level implementation).\n * These types will evolve to support subtree updates.\n */\nexport const PATCH_ROOT_ID = \"__root__\";\n\nexport interface NodeSnapshot {\n id: string;\n type: string;\n props?: Record<string, unknown>;\n children?: NodeSnapshot[];\n range?: { from: number; to: number };\n meta?: Record<string, unknown>;\n}\n\nexport interface NodePath {\n blockId: string;\n nodeId?: string;\n indexPath?: number[];\n}\n\nexport type Patch =\n | { op: \"insertChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"deleteChild\"; at: NodePath; index: number }\n | { op: \"replaceChild\"; at: NodePath; index: number; node: NodeSnapshot }\n | { op: \"setProps\"; at: NodePath; props: Record<string, unknown> }\n | { op: \"setPropsBatch\"; entries: SetPropsBatchEntry[] }\n | { op: \"finalize\"; at: NodePath }\n | { op: \"reorder\"; at: NodePath; from: number; to: number; count: number }\n | {\n op: \"appendLines\";\n at: NodePath;\n startIndex: number;\n lines: string[];\n highlight?: Array<string | null>;\n }\n | {\n op: \"setHTML\";\n at: NodePath;\n html: string;\n policy?: string;\n block?: Block;\n meta?: Record<string, unknown>;\n sanitized?: boolean;\n };\n\nexport interface PatchMetrics {\n patchCount: number;\n changedBlocks: number;\n diffTime?: number;\n parseTime?: number;\n enrichTime?: number;\n queueDepth?: number;\n patchBytes?: number;\n appendLineBatches?: number;\n appendLineTotalLines?: number;\n appendLineMaxLines?: number;\n}\n\nexport interface SetPropsBatchEntry {\n at: NodePath;\n props: Record<string, unknown>;\n}\n\nexport interface CoalescingMetrics {\n inputPatchCount: number;\n outputPatchCount: number;\n coalescedCount: number;\n durationMs: number;\n appendLinesCoalesced: number;\n setPropsCoalesced: number;\n insertChildCoalesced: number;\n}\n","// Utility functions for V2 Markdown Renderer\n\nimport { type Block, LANGUAGE_ALIASES, type ProtectedRange } from \"./types\";\n\n// Browser-compatible crypto import\nconst createHash = (_algorithm: string) => {\n let hash = 0;\n return {\n update(data: string) {\n for (let i = 0; i < data.length; i++) {\n hash = ((hash << 5) - hash + data.charCodeAt(i)) & 0xffffffff;\n }\n return this;\n },\n digest(_encoding: string) {\n return Math.abs(hash).toString(16);\n },\n };\n};\n\n/**\n * Generate stable content hash for blocks\n */\nexport function generateBlockId(content: string, type: string): string {\n const hash = createHash(\"sha256\");\n hash.update(`${type}:${content}`);\n return hash.digest(\"hex\").slice(0, 16);\n}\n\n/**\n * Normalize language names for syntax highlighting\n */\nexport function normalizeLang(raw?: string): string {\n const k = (raw || \"\").trim().toLowerCase();\n return LANGUAGE_ALIASES[k] ?? (k || \"text\");\n}\n\n/**\n * Apply tail splice (immutability & guards)\n */\nexport function applyUpdate(blocks: Block[], update: { start: number; tail: Block[] }): Block[] {\n // Guards\n if (update.start < 0 || update.start > blocks.length) return blocks;\n\n // Ensure finalized invariants\n for (let i = 0; i < update.start; i++) {\n if (!blocks[i].isFinalized) {\n throw new Error(\"Invariant violated: non-finalized before start\");\n }\n }\n\n // Splice tail immutably\n const next = blocks.slice(0, update.start).concat(update.tail);\n\n // Ensure only last is dirty (if any)\n for (let i = 0; i < next.length - 1; i++) {\n if (!next[i].isFinalized) throw new Error(\"Only tail may be dirty\");\n }\n\n return next;\n}\n\n/**\n * Generate stable React keys for blocks\n */\nexport function getBlockKey(block: Block): string {\n return `${block.id}:${block.isFinalized ? 1 : 0}`;\n}\n\n/**\n * Check if content contains MDX-like syntax\n */\nexport function detectMDX(content: string, options?: { protectedRanges?: ReadonlyArray<ProtectedRange>; baseOffset?: number }): boolean {\n const inlineCodeRanges = collectInlineCodeRanges(content);\n\n // Basic heuristic for MDX detection\n const componentPattern = /<([A-Z][\\w-]*)(\\s|\\/?>)/g;\n let componentMatch: RegExpExecArray | null = componentPattern.exec(content);\n while (componentMatch !== null) {\n const start = componentMatch.index;\n const end = start + componentMatch[0].length;\n if (!isWithinRanges(start, end, inlineCodeRanges)) {\n return true;\n }\n componentMatch = componentPattern.exec(content);\n }\n\n // Detect import/export statements (MDX/ESM)\n if (/(^|\\n)\\s*(import|export)\\s/.test(content)) {\n return true;\n }\n\n // Detect inline JSX expressions while ignoring TeX/LaTeX braces (`\\command{}`)\n const expressionPattern = /\\{[^{}]+\\}/g;\n const protectedRanges = options?.protectedRanges ?? [];\n const baseOffset = typeof options?.baseOffset === \"number\" ? options.baseOffset : 0;\n const protectedKinds = protectedRanges.length ? new Set<ProtectedRange[\"kind\"]>([\"math-inline\", \"math-display\", \"code-inline\", \"code-block\"]) : null;\n\n for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {\n const index = match.index;\n const prev = index > 0 ? content[index - 1] : \"\";\n if (prev === \"\\\\\" || prev === \"$\" || prev === \"^\" || prev === \"_\") {\n continue;\n }\n const prefix = content.slice(Math.max(0, index - 8), index);\n if (/\\\\[a-zA-Z]+$/.test(prefix.trimEnd())) {\n continue;\n }\n const inner = match[0].slice(1, -1).trimStart();\n if (/^[\\s0-9+\\-*/(),.=\\\\^_]+$/.test(inner)) {\n continue;\n }\n if (inner.startsWith(\"\\\\\") || inner.startsWith(\"^\") || inner.startsWith(\"_\") || inner.startsWith(\"$\")) {\n continue;\n }\n const exprStart = index;\n const exprEnd = exprStart + match[0].length;\n if (isWithinRanges(exprStart, exprEnd, inlineCodeRanges)) {\n continue;\n }\n if (protectedKinds) {\n const absoluteStart = baseOffset + index;\n const absoluteEnd = absoluteStart + match[0].length;\n const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);\n if (covered) {\n continue;\n }\n }\n return true;\n }\n\n return false;\n}\n\nfunction collectInlineCodeRanges(content: string): Array<{ from: number; to: number }> {\n if (!content) return [];\n const ranges: Array<{ from: number; to: number }> = [];\n const codeSpanRegex = /`(?:\\\\`|[^`])*?`/g;\n let codeMatch: RegExpExecArray | null = codeSpanRegex.exec(content);\n while (codeMatch !== null) {\n ranges.push({ from: codeMatch.index, to: codeMatch.index + codeMatch[0].length });\n codeMatch = codeSpanRegex.exec(content);\n }\n return ranges;\n}\n\nfunction isWithinRanges(start: number, end: number, ranges: Array<{ from: number; to: number }>): boolean {\n return ranges.some((range) => range.from <= start && range.to >= end);\n}\n\n/**\n * Parse code fence info (language and metadata)\n */\nexport function parseCodeFenceInfo(info: string): { lang: string; meta: Record<string, unknown> } {\n const parts = info.trim().split(/\\s+/);\n const lang = normalizeLang(parts[0]);\n\n const meta: Record<string, unknown> = {};\n for (let i = 1; i < parts.length; i++) {\n const part = parts[i];\n if (part.includes(\"=\")) {\n const [key, value] = part.split(\"=\", 2);\n meta[key] = value;\n } else {\n meta[part] = true;\n }\n }\n\n return { lang, meta };\n}\n\n/**\n * Normalize blockquote text by removing a single leading \">\" (and optional whitespace) per line\n * while preserving internal blank lines that represent separate paragraphs.\n */\nexport function normalizeBlockquoteText(raw: string): string {\n if (!raw) return \"\";\n const lines = raw.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\n const normalized = lines.map((line) => line.replace(/^\\s*>?\\s?/, \"\"));\n // Trim leading/trailing empty lines but preserve internal spacing\n while (normalized.length > 0 && normalized[0].trim().length === 0) {\n normalized.shift();\n }\n while (normalized.length > 0 && normalized[normalized.length - 1].trim().length === 0) {\n normalized.pop();\n }\n return normalized.join(\"\\n\");\n}\n\n/**\n * Debounce function for performance optimization\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(func: T, delay: number): (...args: Parameters<T>) => void {\n let timeoutId: NodeJS.Timeout;\n\n return (...args: Parameters<T>) => {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(() => func(...args), delay);\n };\n}\n\n/**\n * Performance measurement utility\n */\nexport class PerformanceTimer {\n private marks: Map<string, number> = new Map();\n\n mark(name: string): void {\n this.marks.set(name, performance.now());\n }\n\n measure(name: string): number | null {\n const start = this.marks.get(name);\n if (start === undefined) return null;\n return performance.now() - start;\n }\n\n reset(): void {\n this.marks.clear();\n }\n}\n\n/**\n * Memory-efficient string manipulation\n */\nexport function removeHeadingMarkers(input: string): string {\n return input\n .replace(/^(#{1,6})\\s+/, \"\")\n .replace(/\\s+={2,}\\s*$|\\s+-{2,}\\s*$/m, \"\")\n .trim();\n}\n\nexport class StringBuffer {\n private chunks: string[] = [];\n private length = 0;\n\n append(str: string): void {\n this.chunks.push(str);\n this.length += str.length;\n }\n\n toString(): string {\n const result = this.chunks.join(\"\");\n this.chunks = [result]; // Consolidate for future operations\n return result;\n }\n\n getLength(): number {\n return this.length;\n }\n\n clear(): void {\n this.chunks = [];\n this.length = 0;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker-html-sanitizer.ts"],"sourcesContent":["import * as rehypeParse from \"rehype-parse\";\nimport * as rehypeSanitize from \"rehype-sanitize\";\nimport * as rehypeStringify from \"rehype-stringify\";\nimport { unified } from \"unified\";\n\nconst { defaultSchema } = rehypeSanitize;\n\ntype Schema = typeof defaultSchema;\ntype AttributeDefinition = string | [string, ...(string | number | boolean | RegExp | null | undefined)[]];\n\nconst SANITIZED_SCHEMA: Schema = createSchema();\n\nconst sanitizeProcessor = unified()\n .use(rehypeParse.default, { fragment: true })\n .use(rehypeSanitize.default, SANITIZED_SCHEMA)\n .use(rehypeStringify.default)\n .freeze();\n\nexport function sanitizeHtmlInWorker(html: string): string {\n if (!html) return \"\";\n try {\n return sanitizeProcessor.processSync(html).toString();\n } catch (error) {\n console.warn(\"[markdown-v2] Failed to sanitize HTML in worker:\", error);\n return \"\";\n }\n}\n\nfunction createSchema(): Schema {\n const base = JSON.parse(JSON.stringify(defaultSchema)) as Schema;\n const tagSet = new Set<string>(base.tagNames ?? []);\n const allowedTags = [\n \"div\",\n \"span\",\n \"p\",\n \"br\",\n \"hr\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"tr\",\n \"th\",\n \"td\",\n \"a\",\n \"img\",\n \"section\",\n \"article\",\n \"aside\",\n \"nav\",\n \"header\",\n \"footer\",\n \"main\",\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n \"sub\",\n \"sup\",\n \"kbd\",\n ];\n for (const tag of allowedTags) {\n tagSet.add(tag);\n }\n base.tagNames = Array.from(tagSet);\n\n base.attributes = {\n ...(base.attributes || {}),\n \"*\": mergeAttributes(base.attributes?.[\"*\"] as AttributeDefinition[] | undefined, [\"className\", \"id\", \"title\", \"style\", \"data-*\", \"aria-*\"]),\n a: mergeAttributes(base.attributes?.a as AttributeDefinition[] | undefined, [\"href\", \"title\", \"target\", \"rel\"]),\n img: mergeAttributes(base.attributes?.img as AttributeDefinition[] | undefined, [\"src\", \"alt\", \"title\"]),\n table: mergeAttributes(base.attributes?.table as AttributeDefinition[] | undefined, [\"align\", \"border\", \"cellpadding\", \"cellspacing\"]),\n th: mergeAttributes(base.attributes?.th as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n td: mergeAttributes(base.attributes?.td as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n tr: mergeAttributes(base.attributes?.tr as AttributeDefinition[] | undefined, [\"align\"]),\n } as Schema[\"attributes\"];\n\n base.protocols = {\n ...(base.protocols || {}),\n href: [\"http\", \"https\", \"mailto\", \"tel\", \"callto\"],\n src: [\"http\", \"https\", \"data\"],\n };\n\n return base;\n}\n\nfunction mergeAttributes(existing: AttributeDefinition[] | undefined, additions: string[]): AttributeDefinition[] {\n const next: AttributeDefinition[] = Array.isArray(existing) ? [...existing] : [];\n const existingStrings = new Set<string>();\n for (const entry of next) {\n if (typeof entry === \"string\") {\n existingStrings.add(entry);\n }\n }\n for (const attr of additions) {\n if (!existingStrings.has(attr)) {\n next.push(attr);\n existingStrings.add(attr);\n }\n }\n return next;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker-html-sanitizer.ts"],"sourcesContent":["import * as rehypeParse from \"rehype-parse\";\nimport * as rehypeSanitize from \"rehype-sanitize\";\nimport * as rehypeStringify from \"rehype-stringify\";\nimport { unified } from \"unified\";\n\nconst { defaultSchema } = rehypeSanitize;\n\ntype Schema = typeof defaultSchema;\ntype AttributeDefinition = string | [string, ...(string | number | boolean | RegExp | null | undefined)[]];\n\nconst SANITIZED_SCHEMA: Schema = createSchema();\n\nconst sanitizeProcessor = unified()\n .use(rehypeParse.default, { fragment: true })\n .use(rehypeSanitize.default, SANITIZED_SCHEMA)\n .use(rehypeStringify.default)\n .freeze();\n\nexport function sanitizeHtmlInWorker(html: string): string {\n if (!html) return \"\";\n try {\n return sanitizeProcessor.processSync(html).toString();\n } catch (error) {\n console.warn(\"[markdown-v2] Failed to sanitize HTML in worker:\", error);\n return \"\";\n }\n}\n\nfunction createSchema(): Schema {\n const base = JSON.parse(JSON.stringify(defaultSchema)) as Schema;\n const tagSet = new Set<string>(base.tagNames ?? []);\n const allowedTags = [\n \"div\",\n \"span\",\n \"p\",\n \"br\",\n \"hr\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"strong\",\n \"em\",\n \"u\",\n \"s\",\n \"del\",\n \"ins\",\n \"blockquote\",\n \"pre\",\n \"code\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tfoot\",\n \"tr\",\n \"th\",\n \"td\",\n \"a\",\n \"img\",\n \"section\",\n \"article\",\n \"aside\",\n \"nav\",\n \"header\",\n \"footer\",\n \"main\",\n \"annotation\",\n \"semantics\",\n \"mtext\",\n \"mn\",\n \"mo\",\n \"mi\",\n \"mspace\",\n \"mrow\",\n \"mfrac\",\n \"msup\",\n \"msub\",\n \"msubsup\",\n \"munder\",\n \"mover\",\n \"munderover\",\n \"msqrt\",\n \"mroot\",\n \"mtable\",\n \"mtr\",\n \"mtd\",\n \"sub\",\n \"sup\",\n \"kbd\",\n ];\n for (const tag of allowedTags) {\n tagSet.add(tag);\n }\n base.tagNames = Array.from(tagSet);\n\n base.attributes = {\n ...(base.attributes || {}),\n \"*\": mergeAttributes(base.attributes?.[\"*\"] as AttributeDefinition[] | undefined, [\"className\", \"id\", \"title\", \"style\", \"data-*\", \"aria-*\"]),\n a: mergeAttributes(base.attributes?.a as AttributeDefinition[] | undefined, [\"href\", \"title\", \"target\", \"rel\"]),\n img: mergeAttributes(base.attributes?.img as AttributeDefinition[] | undefined, [\"src\", \"alt\", \"title\"]),\n table: mergeAttributes(base.attributes?.table as AttributeDefinition[] | undefined, [\"align\", \"border\", \"cellpadding\", \"cellspacing\"]),\n th: mergeAttributes(base.attributes?.th as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n td: mergeAttributes(base.attributes?.td as AttributeDefinition[] | undefined, [\"align\", \"colspan\", \"rowspan\"]),\n tr: mergeAttributes(base.attributes?.tr as AttributeDefinition[] | undefined, [\"align\"]),\n } as Schema[\"attributes\"];\n\n base.protocols = {\n ...(base.protocols || {}),\n href: [\"http\", \"https\", \"mailto\", \"tel\", \"callto\"],\n src: [\"http\", \"https\", \"data\"],\n };\n\n return base;\n}\n\nfunction mergeAttributes(existing: AttributeDefinition[] | undefined, additions: string[]): AttributeDefinition[] {\n const next: AttributeDefinition[] = Array.isArray(existing) ? [...existing] : [];\n const existingStrings = new Set<string>();\n for (const entry of next) {\n if (typeof entry === \"string\") {\n existingStrings.add(entry);\n }\n }\n for (const attr of additions) {\n if (!existingStrings.has(attr)) {\n next.push(attr);\n existingStrings.add(attr);\n }\n }\n return next;\n}\n"],"mappings":"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","names":[]}
|