@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +26 -37
  3. package/dist/code-highlighting.cjs +0 -1
  4. package/dist/code-highlighting.mjs +0 -1
  5. package/dist/index.cjs +0 -1
  6. package/dist/index.mjs +0 -1
  7. package/dist/inline-parser.cjs +0 -1
  8. package/dist/inline-parser.mjs +0 -1
  9. package/dist/mixed-content.cjs +0 -1
  10. package/dist/mixed-content.mjs +0 -1
  11. package/dist/perf/backpressure.cjs +0 -1
  12. package/dist/perf/backpressure.mjs +0 -1
  13. package/dist/perf/patch-batching.cjs +0 -1
  14. package/dist/perf/patch-batching.mjs +0 -1
  15. package/dist/perf/patch-coalescing.cjs +0 -1
  16. package/dist/perf/patch-coalescing.mjs +0 -1
  17. package/dist/security.cjs +0 -1
  18. package/dist/security.mjs +0 -1
  19. package/dist/streaming/custom-matcher.cjs +0 -1
  20. package/dist/streaming/custom-matcher.mjs +0 -1
  21. package/dist/types.cjs +0 -1
  22. package/dist/types.mjs +0 -1
  23. package/dist/utils.cjs +0 -1
  24. package/dist/utils.mjs +0 -1
  25. package/dist/worker-html-sanitizer.cjs +0 -1
  26. package/dist/worker-html-sanitizer.mjs +0 -1
  27. package/package.json +4 -2
  28. package/dist/code-highlighting.cjs.map +0 -1
  29. package/dist/code-highlighting.mjs.map +0 -1
  30. package/dist/index.cjs.map +0 -1
  31. package/dist/index.mjs.map +0 -1
  32. package/dist/inline-parser.cjs.map +0 -1
  33. package/dist/inline-parser.mjs.map +0 -1
  34. package/dist/mixed-content.cjs.map +0 -1
  35. package/dist/mixed-content.mjs.map +0 -1
  36. package/dist/perf/backpressure.cjs.map +0 -1
  37. package/dist/perf/backpressure.mjs.map +0 -1
  38. package/dist/perf/patch-batching.cjs.map +0 -1
  39. package/dist/perf/patch-batching.mjs.map +0 -1
  40. package/dist/perf/patch-coalescing.cjs.map +0 -1
  41. package/dist/perf/patch-coalescing.mjs.map +0 -1
  42. package/dist/security.cjs.map +0 -1
  43. package/dist/security.mjs.map +0 -1
  44. package/dist/streaming/custom-matcher.cjs.map +0 -1
  45. package/dist/streaming/custom-matcher.mjs.map +0 -1
  46. package/dist/types.cjs.map +0 -1
  47. package/dist/types.mjs.map +0 -1
  48. package/dist/utils.cjs.map +0 -1
  49. package/dist/utils.mjs.map +0 -1
  50. package/dist/worker-html-sanitizer.cjs.map +0 -1
  51. package/dist/worker-html-sanitizer.mjs.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,13 @@
1
+ # @stream-mdx/core
2
+
3
+ ## 0.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 9e94660: Docs and release-quality improvements: ship package READMEs/CHANGELOGs, add pack+install smoke tests, expose MDX parity helper entrypoints, and add a deployable docs site workflow.
8
+
9
+ ## 0.0.1
10
+
11
+ ### Patch Changes
12
+
13
+ - Release maintenance: CI/build fixes, missing runtime deps (e.g. `rehype-katex`), and improved docs/README wiring for the `stream-mdx` package page.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # `@stream-mdx/core`
2
2
 
3
- Pure TypeScript primitives shared across the streaming renderer stack. This package intentionally contains **no DOM/React code**; everything exported is structured-clone-safe so it can cross `postMessage` boundaries without serialization hacks.
3
+ Core types + utilities shared across the StreamMDX stack.
4
+
5
+ This package is intentionally React-free. It contains structured-clone-safe types and helpers used by both the worker and the renderer.
6
+
7
+ Most consumers should install `stream-mdx` and follow the main docs. Use `@stream-mdx/core` directly if you’re building tooling or customizing lower-level behavior.
4
8
 
5
9
  ## Install
6
10
 
@@ -8,47 +12,32 @@ Pure TypeScript primitives shared across the streaming renderer stack. This pack
8
12
  npm install @stream-mdx/core
9
13
  ```
10
14
 
11
- ## Exports
15
+ ## Entry points
12
16
 
13
- | Module | Description |
14
- | --- | --- |
15
- | `types` | `Block`, `InlineNode`, `Patch`, `PatchBatch`, `RendererMetrics`, worker message unions. |
16
- | `block-snapshot` | Helpers for cloning/mutating block trees in the worker. |
17
- | `code-highlighting` | Shiki token utilities (`CodeLine`, `flattenHighlights`). |
18
- | `mixed-content` | Inline HTML/Multi-block parsing helpers. |
19
- | `inline-parser` | Incremental inline AST parser (Lezer wrappers). |
20
- | `security` | Sanitization schema + trusted-types friendly helpers. |
21
- | `utils` | Tree traversal, node ID generation, list depth normalization. |
22
- | `perf/backpressure` | Back-pressure defaults + smoothing functions shared by worker/renderer. |
23
- | `worker-html-sanitizer` | Preconfigured DOMPurify-like sanitization for worker context. |
17
+ - `@stream-mdx/core` (root)
18
+ - `@stream-mdx/core/types`
19
+ - `@stream-mdx/core/utils`
20
+ - `@stream-mdx/core/code-highlighting`
21
+ - `@stream-mdx/core/inline-parser`
22
+ - `@stream-mdx/core/mixed-content`
23
+ - `@stream-mdx/core/worker-html-sanitizer`
24
+ - `@stream-mdx/core/security`
25
+ - `@stream-mdx/core/perf/backpressure`
26
+ - `@stream-mdx/core/perf/patch-batching`
27
+ - `@stream-mdx/core/perf/patch-coalescing`
28
+ - `@stream-mdx/core/streaming/custom-matcher`
24
29
 
25
- See `packages/markdown-v2-core/src/index.ts` for the canonical export list.
26
-
27
- ## Usage
30
+ ## Example
28
31
 
29
32
  ```ts
30
- import type { Patch } from "@stream-mdx/core";
31
- import { applyPatchBatch } from "@stream-mdx/core/block-snapshot";
32
-
33
- export function replay(patches: Patch[]) {
34
- const snapshot = createInitialSnapshot();
35
- for (const patch of patches) {
36
- applyPatchBatch(snapshot, [patch]);
37
- }
38
- return snapshot;
33
+ import { DEFAULT_BACKPRESSURE_CONFIG } from "@stream-mdx/core/perf/backpressure";
34
+
35
+ export function makeConfig(overrides?: Partial<typeof DEFAULT_BACKPRESSURE_CONFIG>) {
36
+ return { ...DEFAULT_BACKPRESSURE_CONFIG, ...overrides };
39
37
  }
40
38
  ```
41
39
 
42
- The worker bundle (`@stream-mdx/worker`) consumes these helpers to build `Patch` batches, and the renderer uses the same types for its store/scheduler.
43
-
44
- > For end-to-end math/MDX registration steps (worker + renderer), see [`docs/STREAMING_MARKDOWN_PLUGINS_COOKBOOK.md#5-math--mdx-workerrenderer-registration`](../../docs/STREAMING_MARKDOWN_PLUGINS_COOKBOOK.md#5-math--mdx-workerrenderer-registration).
45
-
46
- ## Security notes
47
-
48
- - Sanitization helpers assume you pass trusted markdown inputs or run the worker in an isolated thread. If you enable raw HTML rendering, ensure you serve KaTeX/MDX assets from trusted origins and set CSP headers accordingly.
49
- - `worker-html-sanitizer` exports a minimal schema. Override/augment it if you need to allow additional tags/attributes (e.g., custom `data-*` props).
50
-
51
- ## Roadmap
40
+ ## Docs
52
41
 
53
- - Document semver guarantees once `1.0.0` ships.
54
- - Publish detailed inline docs for every exported type (linkable from `docs/PUBLIC_API.md`).
42
+ - API reference: `docs/PUBLIC_API.md`
43
+ - Security model: `docs/SECURITY_MODEL.md`
@@ -217,4 +217,3 @@ function filterAllowedAttributes(attrs) {
217
217
  extractHighlightedLines,
218
218
  stripCodeFence
219
219
  });
220
- //# sourceMappingURL=code-highlighting.cjs.map
@@ -188,4 +188,3 @@ export {
188
188
  extractHighlightedLines,
189
189
  stripCodeFence
190
190
  };
191
- //# sourceMappingURL=code-highlighting.mjs.map
package/dist/index.cjs CHANGED
@@ -2854,4 +2854,3 @@ var CustomStreamingMatcher = class {
2854
2854
  smoothCredit,
2855
2855
  stripCodeFence
2856
2856
  });
2857
- //# sourceMappingURL=index.cjs.map
package/dist/index.mjs CHANGED
@@ -2766,4 +2766,3 @@ export {
2766
2766
  smoothCredit,
2767
2767
  stripCodeFence
2768
2768
  };
2769
- //# sourceMappingURL=index.mjs.map
@@ -293,4 +293,3 @@ function splitTextByRegexWithPrecedence(text, regex, toNode) {
293
293
  applyASTPlugin,
294
294
  applyRegexPlugin
295
295
  });
296
- //# sourceMappingURL=inline-parser.cjs.map
@@ -266,4 +266,3 @@ export {
266
266
  applyASTPlugin,
267
267
  applyRegexPlugin
268
268
  };
269
- //# sourceMappingURL=inline-parser.mjs.map
@@ -370,4 +370,3 @@ function findClosingHtmlTag(lowerSource, lowerTagName, startIndex) {
370
370
  findClosingHtmlTag,
371
371
  isLikelyMdxComponent
372
372
  });
373
- //# sourceMappingURL=mixed-content.cjs.map
@@ -331,4 +331,3 @@ export {
331
331
  findClosingHtmlTag,
332
332
  isLikelyMdxComponent
333
333
  };
334
- //# sourceMappingURL=mixed-content.mjs.map
@@ -101,4 +101,3 @@ function clamp01(value) {
101
101
  computeHeavyPatchBudget,
102
102
  smoothCredit
103
103
  });
104
- //# sourceMappingURL=backpressure.cjs.map
@@ -71,4 +71,3 @@ export {
71
71
  computeHeavyPatchBudget,
72
72
  smoothCredit
73
73
  };
74
- //# sourceMappingURL=backpressure.mjs.map
@@ -135,4 +135,3 @@ function splitPatchBatch(patches, maxLightChunk = DEFAULT_MAX_LIGHT_PATCHES_PER_
135
135
  isHeavyPatch,
136
136
  splitPatchBatch
137
137
  });
138
- //# sourceMappingURL=patch-batching.cjs.map
@@ -109,4 +109,3 @@ export {
109
109
  isHeavyPatch,
110
110
  splitPatchBatch
111
111
  };
112
- //# sourceMappingURL=patch-batching.mjs.map
@@ -427,4 +427,3 @@ function coalescePatchesQuadratic(patches, config = DEFAULT_COALESCE_CONFIG) {
427
427
  coalescePatchesQuadratic,
428
428
  coalescePatchesWithMetrics
429
429
  });
430
- //# sourceMappingURL=patch-coalescing.cjs.map
@@ -398,4 +398,3 @@ export {
398
398
  coalescePatchesQuadratic,
399
399
  coalescePatchesWithMetrics
400
400
  };
401
- //# sourceMappingURL=patch-coalescing.mjs.map
package/dist/security.cjs CHANGED
@@ -416,4 +416,3 @@ function initializeSecurity() {
416
416
  sanitizeMathHTML,
417
417
  sanitizeURL
418
418
  });
419
- //# sourceMappingURL=security.cjs.map
package/dist/security.mjs CHANGED
@@ -373,4 +373,3 @@ export {
373
373
  sanitizeMathHTML,
374
374
  sanitizeURL
375
375
  };
376
- //# sourceMappingURL=security.mjs.map
@@ -116,4 +116,3 @@ var CustomStreamingMatcher = class {
116
116
  0 && (module.exports = {
117
117
  CustomStreamingMatcher
118
118
  });
119
- //# sourceMappingURL=custom-matcher.cjs.map
@@ -91,4 +91,3 @@ var CustomStreamingMatcher = class {
91
91
  export {
92
92
  CustomStreamingMatcher
93
93
  };
94
- //# sourceMappingURL=custom-matcher.mjs.map
package/dist/types.cjs CHANGED
@@ -43,4 +43,3 @@ var PATCH_ROOT_ID = "__root__";
43
43
  LANGUAGE_ALIASES,
44
44
  PATCH_ROOT_ID
45
45
  });
46
- //# sourceMappingURL=types.cjs.map
package/dist/types.mjs CHANGED
@@ -17,4 +17,3 @@ export {
17
17
  LANGUAGE_ALIASES,
18
18
  PATCH_ROOT_ID
19
19
  };
20
- //# sourceMappingURL=types.mjs.map
package/dist/utils.cjs CHANGED
@@ -245,4 +245,3 @@ var StringBuffer = class {
245
245
  parseCodeFenceInfo,
246
246
  removeHeadingMarkers
247
247
  });
248
- //# sourceMappingURL=utils.cjs.map
package/dist/utils.mjs CHANGED
@@ -208,4 +208,3 @@ export {
208
208
  parseCodeFenceInfo,
209
209
  removeHeadingMarkers
210
210
  };
211
- //# sourceMappingURL=utils.mjs.map
@@ -157,4 +157,3 @@ function mergeAttributes(existing, additions) {
157
157
  0 && (module.exports = {
158
158
  sanitizeHtmlInWorker
159
159
  });
160
- //# sourceMappingURL=worker-html-sanitizer.cjs.map
@@ -122,4 +122,3 @@ function mergeAttributes(existing, additions) {
122
122
  export {
123
123
  sanitizeHtmlInWorker
124
124
  };
125
- //# sourceMappingURL=worker-html-sanitizer.mjs.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-mdx/core",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Core types, snapshot utilities, and perf helpers for the Streaming Markdown V2 stack",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -78,7 +78,9 @@
78
78
  }
79
79
  },
80
80
  "files": [
81
- "dist"
81
+ "dist",
82
+ "README.md",
83
+ "CHANGELOG.md"
82
84
  ],
83
85
  "sideEffects": false,
84
86
  "scripts": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/code-highlighting.ts"],"sourcesContent":["export type HighlightedLine = string | null;\n\nfunction normalizeNewlines(input: string): string {\n return input.replace(/\\r\\n?/g, \"\\n\");\n}\n\nfunction isFenceLine(line: string): boolean {\n return /^```/.test(line.trim());\n}\n\nexport function stripCodeFence(raw: string): { code: string; info: string; hadFence: boolean } {\n if (!raw) {\n return { code: \"\", info: \"\", hadFence: false };\n }\n const normalized = normalizeNewlines(raw);\n const lines = normalized.split(\"\\n\");\n if (lines.length === 0) {\n return { code: normalized, info: \"\", hadFence: false };\n }\n\n const firstLine = lines[0];\n if (!isFenceLine(firstLine)) {\n return { code: normalized, info: \"\", hadFence: false };\n }\n\n const info = firstLine.slice(3).trim();\n let endIndex = lines.length - 1;\n while (endIndex > 0 && lines[endIndex].trim().length === 0) {\n endIndex--;\n }\n if (endIndex > 0 && isFenceLine(lines[endIndex])) {\n const codeLines = lines.slice(1, endIndex);\n return { code: codeLines.join(\"\\n\"), info, hadFence: true };\n }\n\n // During streaming, code block may not have closing fence yet\n // Still extract and return the info from the opening fence\n const codeLines = lines.slice(1);\n return { code: codeLines.join(\"\\n\"), info, hadFence: true };\n}\n\nfunction getDomParser(): DOMParser | null {\n if (typeof window !== \"undefined\" && typeof window.DOMParser === \"function\") {\n return new window.DOMParser();\n }\n if (typeof DOMParser !== \"undefined\") {\n try {\n return new DOMParser();\n } catch (e) {\n return null;\n }\n }\n return null;\n}\n\nexport function extractHighlightedLines(html: string, fallbackLength: number): HighlightedLine[] {\n if (!html) {\n return new Array(Math.max(0, fallbackLength)).fill(null);\n }\n\n const parser = getDomParser();\n if (parser) {\n try {\n const doc = parser.parseFromString(`<div>${html}</div>`, \"text/html\");\n const nodes = doc.querySelectorAll(\"span.line\");\n if (nodes.length > 0) {\n const lines: HighlightedLine[] = [];\n for (const node of nodes) {\n lines.push(node instanceof Element ? node.innerHTML : null);\n }\n return normalizeHighlightedLines(lines, fallbackLength);\n }\n } catch (error) {\n // fall through to manual extraction\n }\n }\n\n return manualExtractHighlightedLines(html, fallbackLength);\n}\n\nfunction normalizeHighlightedLines(lines: HighlightedLine[], fallbackLength: number): HighlightedLine[] {\n if (!lines || lines.length === 0) {\n return new Array(Math.max(0, fallbackLength)).fill(null);\n }\n const length = Math.max(fallbackLength, lines.length);\n const result: HighlightedLine[] = new Array(length).fill(null);\n for (let i = 0; i < lines.length; i++) {\n result[i] = lines[i];\n }\n return result;\n}\n\nfunction manualExtractHighlightedLines(html: string, fallbackLength: number): HighlightedLine[] {\n const lineRegex = /<span class=\"line\"(?:\\s+[^>]*)?>/gi;\n const lines: string[] = [];\n let match: RegExpExecArray | null = lineRegex.exec(html);\n\n while (match !== null) {\n const openTagEnd = html.indexOf(\">\", match.index);\n if (openTagEnd === -1) break;\n let cursor = openTagEnd + 1;\n let depth = 1;\n let buffer = \"\";\n\n while (cursor < html.length && depth > 0) {\n const nextTagStart = html.indexOf(\"<\", cursor);\n if (nextTagStart === -1) {\n buffer += html.slice(cursor);\n cursor = html.length;\n break;\n }\n if (nextTagStart > cursor) {\n buffer += html.slice(cursor, nextTagStart);\n }\n cursor = nextTagStart;\n if (html.startsWith(\"</span>\", cursor)) {\n depth -= 1;\n cursor += 7;\n if (depth === 0) break;\n buffer += \"</span>\";\n continue;\n }\n const tagEnd = html.indexOf(\">\", cursor);\n if (tagEnd === -1) {\n buffer += html.slice(cursor);\n cursor = html.length;\n break;\n }\n const tag = html.slice(cursor, tagEnd + 1);\n if (/^<span\\b/i.test(tag)) {\n depth += 1;\n }\n buffer += tag;\n cursor = tagEnd + 1;\n }\n\n lines.push(buffer);\n lineRegex.lastIndex = cursor;\n match = lineRegex.exec(html);\n }\n\n return normalizeHighlightedLines(lines, fallbackLength);\n}\n\nexport function dedentIndentedCode(raw: string): string {\n if (!raw) return \"\";\n const normalized = normalizeNewlines(raw);\n const lines = normalized.split(\"\\n\");\n let minIndent = Number.POSITIVE_INFINITY;\n for (const line of lines) {\n const match = line.match(/^\\s+/);\n if (!match) continue;\n minIndent = Math.min(minIndent, match[0].length);\n }\n if (!Number.isFinite(minIndent) || minIndent === 0) {\n return normalized;\n }\n return lines.map((line) => (line.startsWith(\" \".repeat(minIndent)) ? line.slice(minIndent) : line)).join(\"\\n\");\n}\n\nexport function extractCodeLines(raw: string): string[] {\n if (!raw) return [];\n const normalized = normalizeNewlines(raw);\n const { code, hadFence } = stripCodeFence(normalized);\n if (hadFence) {\n return code.split(\"\\n\");\n }\n if (/^\\s{4}/m.test(normalized)) {\n return dedentIndentedCode(normalized).split(\"\\n\");\n }\n return normalized.split(\"\\n\");\n}\n\nexport function extractCodeWrapperAttributes(html: string): {\n preAttrs?: Record<string, string>;\n codeAttrs?: Record<string, string>;\n} {\n if (!html) {\n return {};\n }\n const preMatch = html.match(/<pre\\b([^>]*)>/i);\n const codeMatch = html.match(/<code\\b([^>]*)>/i);\n return {\n preAttrs: preMatch ? filterAllowedAttributes(parseAttributeFragment(preMatch[1] ?? \"\")) : undefined,\n codeAttrs: codeMatch ? filterAllowedAttributes(parseAttributeFragment(codeMatch[1] ?? \"\")) : undefined,\n };\n}\n\nfunction parseAttributeFragment(fragment: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const regex = /([a-zA-Z_:][\\w:.-]*)\\s*=\\s*\"([^\"]*)\"/g;\n let match: RegExpExecArray | null = regex.exec(fragment);\n while (match !== null) {\n const [, name, value] = match;\n attrs[name] = value;\n match = regex.exec(fragment);\n }\n return attrs;\n}\n\nfunction filterAllowedAttributes(attrs: Record<string, string>): Record<string, string> {\n const allowed = new Set([\"class\", \"style\", \"data-theme\"]);\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (allowed.has(key) || key.startsWith(\"data-\")) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n"],"mappings":"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","names":["codeLines"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/code-highlighting.ts"],"sourcesContent":["export type HighlightedLine = string | null;\n\nfunction normalizeNewlines(input: string): string {\n return input.replace(/\\r\\n?/g, \"\\n\");\n}\n\nfunction isFenceLine(line: string): boolean {\n return /^```/.test(line.trim());\n}\n\nexport function stripCodeFence(raw: string): { code: string; info: string; hadFence: boolean } {\n if (!raw) {\n return { code: \"\", info: \"\", hadFence: false };\n }\n const normalized = normalizeNewlines(raw);\n const lines = normalized.split(\"\\n\");\n if (lines.length === 0) {\n return { code: normalized, info: \"\", hadFence: false };\n }\n\n const firstLine = lines[0];\n if (!isFenceLine(firstLine)) {\n return { code: normalized, info: \"\", hadFence: false };\n }\n\n const info = firstLine.slice(3).trim();\n let endIndex = lines.length - 1;\n while (endIndex > 0 && lines[endIndex].trim().length === 0) {\n endIndex--;\n }\n if (endIndex > 0 && isFenceLine(lines[endIndex])) {\n const codeLines = lines.slice(1, endIndex);\n return { code: codeLines.join(\"\\n\"), info, hadFence: true };\n }\n\n // During streaming, code block may not have closing fence yet\n // Still extract and return the info from the opening fence\n const codeLines = lines.slice(1);\n return { code: codeLines.join(\"\\n\"), info, hadFence: true };\n}\n\nfunction getDomParser(): DOMParser | null {\n if (typeof window !== \"undefined\" && typeof window.DOMParser === \"function\") {\n return new window.DOMParser();\n }\n if (typeof DOMParser !== \"undefined\") {\n try {\n return new DOMParser();\n } catch (e) {\n return null;\n }\n }\n return null;\n}\n\nexport function extractHighlightedLines(html: string, fallbackLength: number): HighlightedLine[] {\n if (!html) {\n return new Array(Math.max(0, fallbackLength)).fill(null);\n }\n\n const parser = getDomParser();\n if (parser) {\n try {\n const doc = parser.parseFromString(`<div>${html}</div>`, \"text/html\");\n const nodes = doc.querySelectorAll(\"span.line\");\n if (nodes.length > 0) {\n const lines: HighlightedLine[] = [];\n for (const node of nodes) {\n lines.push(node instanceof Element ? node.innerHTML : null);\n }\n return normalizeHighlightedLines(lines, fallbackLength);\n }\n } catch (error) {\n // fall through to manual extraction\n }\n }\n\n return manualExtractHighlightedLines(html, fallbackLength);\n}\n\nfunction normalizeHighlightedLines(lines: HighlightedLine[], fallbackLength: number): HighlightedLine[] {\n if (!lines || lines.length === 0) {\n return new Array(Math.max(0, fallbackLength)).fill(null);\n }\n const length = Math.max(fallbackLength, lines.length);\n const result: HighlightedLine[] = new Array(length).fill(null);\n for (let i = 0; i < lines.length; i++) {\n result[i] = lines[i];\n }\n return result;\n}\n\nfunction manualExtractHighlightedLines(html: string, fallbackLength: number): HighlightedLine[] {\n const lineRegex = /<span class=\"line\"(?:\\s+[^>]*)?>/gi;\n const lines: string[] = [];\n let match: RegExpExecArray | null = lineRegex.exec(html);\n\n while (match !== null) {\n const openTagEnd = html.indexOf(\">\", match.index);\n if (openTagEnd === -1) break;\n let cursor = openTagEnd + 1;\n let depth = 1;\n let buffer = \"\";\n\n while (cursor < html.length && depth > 0) {\n const nextTagStart = html.indexOf(\"<\", cursor);\n if (nextTagStart === -1) {\n buffer += html.slice(cursor);\n cursor = html.length;\n break;\n }\n if (nextTagStart > cursor) {\n buffer += html.slice(cursor, nextTagStart);\n }\n cursor = nextTagStart;\n if (html.startsWith(\"</span>\", cursor)) {\n depth -= 1;\n cursor += 7;\n if (depth === 0) break;\n buffer += \"</span>\";\n continue;\n }\n const tagEnd = html.indexOf(\">\", cursor);\n if (tagEnd === -1) {\n buffer += html.slice(cursor);\n cursor = html.length;\n break;\n }\n const tag = html.slice(cursor, tagEnd + 1);\n if (/^<span\\b/i.test(tag)) {\n depth += 1;\n }\n buffer += tag;\n cursor = tagEnd + 1;\n }\n\n lines.push(buffer);\n lineRegex.lastIndex = cursor;\n match = lineRegex.exec(html);\n }\n\n return normalizeHighlightedLines(lines, fallbackLength);\n}\n\nexport function dedentIndentedCode(raw: string): string {\n if (!raw) return \"\";\n const normalized = normalizeNewlines(raw);\n const lines = normalized.split(\"\\n\");\n let minIndent = Number.POSITIVE_INFINITY;\n for (const line of lines) {\n const match = line.match(/^\\s+/);\n if (!match) continue;\n minIndent = Math.min(minIndent, match[0].length);\n }\n if (!Number.isFinite(minIndent) || minIndent === 0) {\n return normalized;\n }\n return lines.map((line) => (line.startsWith(\" \".repeat(minIndent)) ? line.slice(minIndent) : line)).join(\"\\n\");\n}\n\nexport function extractCodeLines(raw: string): string[] {\n if (!raw) return [];\n const normalized = normalizeNewlines(raw);\n const { code, hadFence } = stripCodeFence(normalized);\n if (hadFence) {\n return code.split(\"\\n\");\n }\n if (/^\\s{4}/m.test(normalized)) {\n return dedentIndentedCode(normalized).split(\"\\n\");\n }\n return normalized.split(\"\\n\");\n}\n\nexport function extractCodeWrapperAttributes(html: string): {\n preAttrs?: Record<string, string>;\n codeAttrs?: Record<string, string>;\n} {\n if (!html) {\n return {};\n }\n const preMatch = html.match(/<pre\\b([^>]*)>/i);\n const codeMatch = html.match(/<code\\b([^>]*)>/i);\n return {\n preAttrs: preMatch ? filterAllowedAttributes(parseAttributeFragment(preMatch[1] ?? \"\")) : undefined,\n codeAttrs: codeMatch ? filterAllowedAttributes(parseAttributeFragment(codeMatch[1] ?? \"\")) : undefined,\n };\n}\n\nfunction parseAttributeFragment(fragment: string): Record<string, string> {\n const attrs: Record<string, string> = {};\n const regex = /([a-zA-Z_:][\\w:.-]*)\\s*=\\s*\"([^\"]*)\"/g;\n let match: RegExpExecArray | null = regex.exec(fragment);\n while (match !== null) {\n const [, name, value] = match;\n attrs[name] = value;\n match = regex.exec(fragment);\n }\n return attrs;\n}\n\nfunction filterAllowedAttributes(attrs: Record<string, string>): Record<string, string> {\n const allowed = new Set([\"class\", \"style\", \"data-theme\"]);\n const filtered: Record<string, string> = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (allowed.has(key) || key.startsWith(\"data-\")) {\n filtered[key] = value;\n }\n }\n return filtered;\n}\n"],"mappings":"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","names":["codeLines"]}