@takazudo/zfb-md-wasm 2.7.0 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,69 @@ binary directly.
22
22
  pnpm add @takazudo/zfb-md-wasm
23
23
  ```
24
24
 
25
- ## Two API tiers
25
+ ## Choose an entry
26
+
27
+ The package has four additive entries. Keep using `.` for the complete current
28
+ API (`compile` plus render, parse, and highlight); root imports do not need a
29
+ migration. `./highlight` is also backward-compatible. New `./render` and
30
+ `./parse` entries are isolated **SWC-free** graphs: they omit `swc_core` and
31
+ `zfb-render` but intentionally retain `zfb-content` and `syntect-fancy`.
32
+ `./parse` is not syntect-free.
33
+
34
+ | Entry | Exact runtime values | Exact exported types |
35
+ | --- | --- | --- |
36
+ | `.` | `init`, `compile`, `renderHtml`, `parseToAst`, `highlightCode`, `version`, `__forceTrapForTests`, `__getTrapRecoveryStateForTests`, `toMdastRoot`, `ZfbMdWasmTrapError`, `ZfbMdWasmTrapRecoveryLimitError`, `MdastAdapterError` | Full current compile/render/parse/raw-mdast/highlight surface |
37
+ | `./highlight` | `init`, `highlightCode`, `version`, `__forceTrapForTests`, `__getTrapRecoveryStateForTests`, `ZfbMdWasmTrapError`, `ZfbMdWasmTrapRecoveryLimitError` | `HighlightRole`, `HighlightCodeOptions`, `HighlightCodeResult`, `HighlightDiagnostic`, `HighlightDiagnosticSource` |
38
+ | `./render` | `init`, `renderHtml`, `version`, `ZfbMdWasmTrapError`, `ZfbMdWasmTrapRecoveryLimitError`, `__forceTrapForTests`, `__getTrapRecoveryStateForTests` | `RenderHtmlResult`, `Diagnostic`, `DiagnosticSource`, `ZfbMdWasmOptions`, `ParseDialect`, `PipelineOptions`, `GfmOptions`, `CodeHighlightMode`, `CodeHighlightOptions`, `MarkdownFeaturesConfig`, `JsxRuntime`, `HighlightRole` |
39
+ | `./parse` | `init`, `parseToAst`, `toMdastRoot`, `MdastAdapterError`, `version`, `ZfbMdWasmTrapError`, `ZfbMdWasmTrapRecoveryLimitError`, `__forceTrapForTests`, `__getTrapRecoveryStateForTests` | `ParseToAstResult`, `ParseToAstOptions`, `ParseDialect`, `FrontmatterPolicy`, `ParsePipelineOptions`, `Diagnostic`, `DiagnosticSource`, `AstPoint`, `AstPosition`, `RawMdastData`, `MarkdownRsStop`, `MdastNode`, `MdastRoot`, `UnknownMdastNode`, `Root`, `Paragraph`, `Heading`, `ThematicBreak`, `Blockquote`, `List`, `ListItem`, `Html`, `Code`, `Definition`, `Text`, `DirectiveNodeBase`, `ContainerDirective`, `LeafDirective`, `TextDirective`, `Emphasis`, `Strong`, `InlineCode`, `Break`, `Link`, `Image`, `ReferenceKind`, `LinkReference`, `ImageReference`, `FootnoteDefinition`, `FootnoteReference`, `TableAlign`, `Table`, `TableRow`, `TableCell`, `Delete`, `Yaml`, `MdxFlowExpression`, `MdxTextExpression`, `MdxJsxFlowElement`, `MdxJsxTextElement`, `MdxJsxAttributeContent`, `MdxJsxAttribute`, `MdxJsxAttributeValueExpression`, `MdxJsxExpressionAttribute` |
40
+
41
+ The focused entries own private resource pairs:
42
+
43
+ ```text
44
+ wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs
45
+ wasm-render/zfb_md_wasm_render_bg.wasm
46
+ wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs
47
+ wasm-parse/zfb_md_wasm_parse_bg.wasm
48
+ ```
49
+
50
+ Each also has only its matching declaration sidecars. Every entry creates its
51
+ own compiled-module, wasm-instance, generation, retry, and terminal state.
52
+ Importing multiple entries intentionally loads independent pairs and instances;
53
+ it does not deduplicate their resources.
54
+
55
+ Migration is only needed when a root consumer calls one focused function:
56
+
57
+ ```ts
58
+ // Direct Node import and browser-aware bundler import.
59
+ import { renderHtml } from "@takazudo/zfb-md-wasm/render";
60
+ import { parseToAst, toMdastRoot } from "@takazudo/zfb-md-wasm/parse";
61
+ ```
62
+
63
+ For browser lazy loading, import from the user action. The conditional browser
64
+ entry uses static URL edges for only that entry's own resource pair:
65
+
66
+ ```ts
67
+ button.addEventListener("click", async () => {
68
+ const { renderHtml } = await import("@takazudo/zfb-md-wasm/render");
69
+ const { html } = await renderHtml(source, { filename: "preview.md" });
70
+ preview.innerHTML = html ?? "";
71
+ });
72
+
73
+ parseButton.addEventListener("click", async () => {
74
+ const { parseToAst, toMdastRoot } = await import("@takazudo/zfb-md-wasm/parse");
75
+ const parsed = await parseToAst(source, { filename: "preview.md" });
76
+ const root = parsed.ast === null ? null : toMdastRoot(parsed.ast);
77
+ inspect(root);
78
+ });
79
+ ```
80
+
81
+ `compile()` remains root-only. It returns module source, not an evaluated
82
+ module; host evaluation must supply the JSX runtime and components. Controlled
83
+ consumer code may interpret an already-parsed AST in a controlled AST-to-React
84
+ renderer, but no slim entry evaluates author JavaScript. `renderHtml` is not a sanitizer and raw HTML remains
85
+ untrusted; MDX JSX, expression, and ESM-shaped AST nodes are inert data.
86
+
87
+ ## Root API
26
88
 
27
89
  Both functions take the markdown/MDX `source` and an options object (every
28
90
  field optional; `{}` selects all defaults). Both return a result object plus a
@@ -72,14 +134,17 @@ plain-markdown preview when you don't need to evaluate a component module.
72
134
  ```ts
73
135
  import { renderHtml } from "@takazudo/zfb-md-wasm";
74
136
 
75
- const { html, frontmatter, diagnostics } = await renderHtml("# Heading\n\nSome **bold** text.\n", {
137
+ const { html, frontmatter, diagnostics } = await renderHtml("Budget <8 ms\n", {
76
138
  filename: "post.md",
77
139
  });
78
- // html -> "<h1>Heading</h1><p>Some <strong>bold</strong> text.</p>"
140
+ // html -> "<p>Budget &lt;8 ms</p>"
79
141
  ```
80
142
 
81
- `renderHtml` accepts and ignores `jsxRuntime` / `development`, so one options
82
- object can serve both tiers.
143
+ `renderHtml` infers CommonMark for `.md` and MDX for `.mdx`; an explicit
144
+ `dialect: "markdown" | "mdx"` overrides either valid extension. Omitting the
145
+ filename uses `<anonymous>.md`, hence CommonMark. `compile` remains MDX-only
146
+ and accepts/ignores `dialect`, while `renderHtml` accepts/ignores
147
+ `jsxRuntime` / `development`, so one options object can serve both tiers.
83
148
 
84
149
  ### `version()` / `init()`
85
150
 
@@ -99,7 +164,7 @@ Shiki classes.
99
164
 
100
165
  If `highlightCode` is the only thing you use, import it from
101
166
  `@takazudo/zfb-md-wasm/highlight` instead of the package root — that entry
102
- ships a separate, much smaller wasm artifact with no `compile`/`renderHtml`
167
+ ships a separate highlight-only wasm artifact with no `compile`/`renderHtml`
103
168
  (and no md/MDX/JSX pipeline behind them at all). Same function, same result
104
169
  shape, same `init()`/`version()`; see "Artifact size" below for the byte
105
170
  savings.
@@ -110,7 +175,7 @@ import {
110
175
  type HighlightCodeOptions,
111
176
  type HighlightCodeResult,
112
177
  } from "@takazudo/zfb-md-wasm";
113
- // or, for the smaller highlight-only artifact:
178
+ // or, for the highlight-only artifact:
114
179
  // } from "@takazudo/zfb-md-wasm/highlight";
115
180
 
116
181
  const output: HighlightCodeResult = await highlightCode("const answer = 42;", {
@@ -403,25 +468,37 @@ for your own environment.
403
468
 
404
469
  ## Browser loading and emitted resources
405
470
 
406
- The package root has a `browser` export condition. Its browser entry imports
407
- the generated glue and Wasm binary through an explicit bundler `?url` asset
471
+ Every entry has a `browser` export condition. Its browser entry imports the
472
+ generated glue and Wasm binary through an explicit bundler `?url` asset
408
473
  contract. Vite and zfb's pinned esbuild setup therefore keep separate resource
409
- edges for exactly `zfb_md_wasm_glue.zfb-resource.mjs` and
410
- `zfb_md_wasm_bg.wasm`; a zfb production build emits them under hashed names:
474
+ edges for exactly its own pair. The focused pairs are:
411
475
 
412
476
  ```text
413
- assets/islands-resource-zfb_md_wasm_glue.zfb-resource-<hash>.mjs
414
- assets/islands-resource-zfb_md_wasm_bg-<hash>.wasm
477
+ wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs
478
+ wasm-render/zfb_md_wasm_render_bg.wasm
479
+ wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs
480
+ wasm-parse/zfb_md_wasm_parse_bg.wasm
415
481
  ```
416
482
 
483
+ Root and highlight retain their existing `wasm/` and `wasm-highlight/` pairs;
484
+ each directory is closed with its matching glue/wasm declaration sidecars. A
485
+ zfb production build emits each selected pair under hashed names.
486
+
417
487
  Keep the package import in a user action when first-load cost matters:
418
488
 
419
489
  ```ts
420
490
  button.addEventListener("click", async () => {
421
- const { highlightCode } = await import("@takazudo/zfb-md-wasm");
422
- const result = await highlightCode(editor.value, { language: "javascript" });
491
+ const { renderHtml } = await import("@takazudo/zfb-md-wasm/render");
492
+ const result = await renderHtml(editor.value, { filename: "preview.md" });
423
493
  preview.innerHTML = result.html ?? "";
424
494
  });
495
+
496
+ parseButton.addEventListener("click", async () => {
497
+ const { parseToAst, toMdastRoot } = await import("@takazudo/zfb-md-wasm/parse");
498
+ const parsed = await parseToAst(editor.value, { filename: "preview.md" });
499
+ const root = parsed.ast === null ? null : toMdastRoot(parsed.ast);
500
+ inspect(root);
501
+ });
425
502
  ```
426
503
 
427
504
  That import/call boundary keeps both resources unloaded before the action;
@@ -432,18 +509,17 @@ replace the static imports with source paths or manually copied resource
432
509
  files, which breaks the emitted URL graph. No Vite plugin, alias, or
433
510
  package-specific consumer configuration is required.
434
511
 
435
- The `./highlight` subpath (see "Artifact size" above) has the identical
436
- `browser` export condition and resource-loading contract, pointed at its own
437
- separate resources: `zfb_md_wasm_highlight_glue.zfb-resource.mjs` and
438
- `zfb_md_wasm_highlight_bg.wasm`. Importing `@takazudo/zfb-md-wasm` and
439
- `@takazudo/zfb-md-wasm/highlight` in the same bundle loads BOTH wasm
440
- artifacts — pick one entry per bundle.
512
+ The `./highlight` subpath has the identical `browser` export condition and
513
+ resource-loading contract, pointed at its own separate resources. Importing
514
+ multiple entries in the same bundle intentionally loads each private pair and
515
+ creates independent wasm state; no entry evicts or shares another's instance.
441
516
 
442
517
  ## Options shape
443
518
 
444
519
  ```ts
445
520
  interface ZfbMdWasmOptions {
446
521
  filename?: string; // must end .md/.mdx; drives frontmatter dispatch + diagnostics
522
+ dialect?: "markdown" | "mdx"; // renderHtml only; inferred from filename when absent
447
523
  jsxRuntime?: "preact" | "react"; // compile only; default "preact"
448
524
  development?: boolean; // compile only; default false
449
525
  pipeline?: {
@@ -574,9 +650,20 @@ alias.
574
650
  ## Node usage
575
651
 
576
652
  For tests and tooling, the package loads and runs under Node ≥ 20 with no extra
577
- setup the same `compile` / `renderHtml` / `parseToAst` / `version` API. This
578
- is exactly how this package's own vitest suite (and `parseToAst`'s Node
579
- benchmark against `remark-parse`) exercises the wasm.
653
+ setup. Direct imports select the resource pair that matches the operation:
654
+
655
+ ```ts
656
+ import { renderHtml } from "@takazudo/zfb-md-wasm/render";
657
+ import { parseToAst, toMdastRoot } from "@takazudo/zfb-md-wasm/parse";
658
+
659
+ const { html } = await renderHtml("# Hello from Node\n");
660
+ const parsed = await parseToAst("# Hello from Node\n", { filename: "post.md" });
661
+ const root = parsed.ast === null ? null : toMdastRoot(parsed.ast);
662
+ ```
663
+
664
+ The root import remains the compatibility choice for code that also calls
665
+ `compile`, and the existing highlight import remains compatible. This is also
666
+ how the package's vitest suite and parse benchmark exercise the built wasm.
580
667
 
581
668
  ## Parity guarantee & limitations
582
669
 
@@ -593,37 +680,54 @@ suite gates exact-match). Deliberate limitations of the browser build:
593
680
  - **No cross-file features.** Route-table link resolution and cross-file anchor
594
681
  resolution need the whole project graph, which a single-document browser call
595
682
  doesn't have.
596
- - **The default artifact carries SWC even for `renderHtml`-only use.** One
597
- cdylib can't tree-shake SWC away when only `renderHtml` is called; a slim
598
- `renderHtml`-only artifact is a documented possible follow-up. If you only
599
- ever call `highlightCode`, use the `./highlight` entry instead (see
600
- "Artifact size" below) — it drops `compile`/`renderHtml` and the whole
601
- md/MDX/JSX pipeline entirely.
602
- - **Grammar subsetting is not built.** Both artifacts ship every bundled
683
+ - **Choose a focused artifact for non-compile calls.** The root remains the
684
+ compatibility entry and carries the complete compiler graph. `./highlight`
685
+ keeps its public API and resources while the post-#2449/#2450 graph is
686
+ proven SWC-free. `./render` and `./parse` are also SWC-free and omit `zfb-render`;
687
+ parse intentionally retains `zfb-content`/`syntect-fancy`, so it is not
688
+ syntect-free. Use `./render` or `./parse` when a consumer does not need
689
+ `compile`; root and highlight callers otherwise require no migration.
690
+ - **`compile` is the execution boundary.** It returns ES-module source and
691
+ only the root entry exposes it. Host code must explicitly evaluate that
692
+ source and provide the JSX runtime/components. Controlled consumer code may
693
+ interpret an already-parsed AST; no slim entry evaluates author JavaScript.
694
+ - **`renderHtml` is not a sanitizer.** Raw HTML remains untrusted, and JSX,
695
+ expression, or ESM-shaped AST nodes from MDX remain inert data.
696
+ - **`renderHtml` selects syntax from the filename.** `.md` uses CommonMark,
697
+ `.mdx` uses MDX, and an explicit `dialect` overrides either valid extension.
698
+ `compile` remains MDX-only.
699
+ - **Grammar subsetting is not built.** All four artifacts ship every bundled
603
700
  syntect grammar; there is no per-language allowlist knob.
604
701
  - **Syntax highlighting uses syntect's `fancy-regex` backend** (native zfb uses
605
702
  `oniguruma`, which can't compile to wasm). The two are byte-identical on
606
703
  zfb's fixture corpus; any grammar-level divergences are tracked in the
607
704
  crate's informational backend-divergence test.
608
705
 
609
- ## Artifact size
610
-
611
- Shipping SWC in the bytes makes the default module large. The build applies a
612
- size-optimized cargo profile (`opt-level = "z"`, LTO, one codegen unit,
613
- `panic = "abort"`) plus `wasm-opt`, which roughly halves the raw binary either
614
- way. The package ships **two** wasm artifacts (zfb#1849, epic zfb#1845):
615
-
616
- | Entry | Import | What it has | Raw `.wasm` | Gzipped |
617
- | -------------- | --------------------------------- | ----------------------------------------------------- | ----------- | ------- |
618
- | Default | `@takazudo/zfb-md-wasm` | `compile` + `renderHtml` + `highlightCode` | ~2.9 MB | ~1.3 MB |
619
- | Highlight-only | `@takazudo/zfb-md-wasm/highlight` | `highlightCode` only (no md/MDX/JSX pipeline, no SWC) | ~1.4 MB | ~0.7 MB |
620
-
621
- The highlight-only artifact drops the `pipeline` Cargo feature entirely (see
622
- `crates/zfb-md-wasm/Cargo.toml`) rather than subsetting syntect grammars
623
- both artifacts bundle every grammar (see "Grammar subsetting is not built"
624
- above). The CI `wasm-md` job prints the authoritative gzipped size for BOTH
625
- artifacts on every run treat that as the source of truth rather than this
626
- table, which can drift.
706
+ ## Artifact size and locked ceilings
707
+
708
+ The decision snapshot uses the optimized final wasm after wasm-bindgen and
709
+ wasm-opt, Node `gzipSync(..., { level: 9 })`, and glue bytes/gzip. These exact
710
+ #2447 rows are measurements, not permanent promises:
711
+
712
+ | Entry/graph | final wasm | gzip-9 | glue | glue gzip-9 |
713
+ | --- | ---: | ---: | ---: | ---: |
714
+ | root (full) | 3,336,933 B | 1,491,970 B | 14,881 B | 4,151 B |
715
+ | highlight (historical current SWC-retaining baseline) | 1,484,705 B | 767,009 B | 8,758 B | 2,637 B |
716
+ | render (selected SWC-free candidate) | 2,123,309 B | 1,032,557 B | 8,637 B | 2,597 B |
717
+ | parse (selected SWC-free candidate) | 650,458 B | 289,523 B | 11,141 B | 3,795 B |
718
+
719
+ The measured split package was 3,638,607 B versus 2,314,818 B for the current
720
+ root-plus-highlight package. Locked gzip-9 ceilings are root 1,600,000 B,
721
+ highlight 820,000 B, render 1,100,000 B, and parse 325,000 B; the complete
722
+ packed tarball ceiling is 3,900,000 B. The clean four-step production ceiling
723
+ is 210 seconds; the selected #2447 median was 155.015 s [153.496, 165.977].
724
+
725
+ The highlight row is explicitly historical: #2447's current
726
+ `highlight,compiler-surface` baseline retained `swc_core` because the content
727
+ edge was unconditional. The shipped post-#2449 highlight graph is proven
728
+ SWC-free by #2450's exact parity and no-`swc_core` assertion, with its public API
729
+ and resources unchanged, but its post-gating size was not measured. Do not
730
+ invent or project a highlight byte value.
627
731
 
628
732
  ## Error / trap / re-init contract
629
733
 
@@ -0,0 +1,12 @@
1
+ export declare const init: () => Promise<void>, parseToAst: (source: string, options?: import("./types.js").ParseToAstOptions) => Promise<import("./types.js").ParseToAstResult>, version: () => Promise<string>, __forceTrapForTests: () => Promise<void>, __getTrapRecoveryStateForTests: () => {
2
+ compiledModuleLoads: number;
3
+ currentGeneration: number;
4
+ freshInstanceStarts: number;
5
+ glueImportAttempts: number;
6
+ maxTrapRecoveries: number;
7
+ trapRecoveriesStarted: number;
8
+ terminal: boolean;
9
+ };
10
+ export { MdastAdapterError, toMdastRoot } from "./mdast.js";
11
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
12
+ export type { ParseToAstResult, ParseToAstOptions, ParseDialect, FrontmatterPolicy, ParsePipelineOptions, Diagnostic, DiagnosticSource, AstPoint, AstPosition, RawMdastData, MarkdownRsStop, MdastNode, MdastRoot, UnknownMdastNode, Root, Paragraph, Heading, ThematicBreak, Blockquote, List, ListItem, Html, Code, Definition, Text, DirectiveNodeBase, ContainerDirective, LeafDirective, TextDirective, Emphasis, Strong, InlineCode, Break, Link, Image, ReferenceKind, LinkReference, ImageReference, FootnoteDefinition, FootnoteReference, TableAlign, Table, TableRow, TableCell, Delete, Yaml, MdxFlowExpression, MdxTextExpression, MdxJsxFlowElement, MdxJsxTextElement, MdxJsxAttributeContent, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, } from "./types.js";
@@ -0,0 +1,17 @@
1
+ import glueHref from "./wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs?url";
2
+ import wasmHref from "./wasm-parse/zfb_md_wasm_parse_bg.wasm?url";
3
+ import { createWasmApi } from "./runtime.js";
4
+ const GLUE_URL = new URL(glueHref, import.meta.url);
5
+ const WASM_URL = new URL(wasmHref, import.meta.url);
6
+ async function loadBrowserWasmBytes() {
7
+ const response = await fetch(WASM_URL);
8
+ if (!response.ok) {
9
+ throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`);
10
+ }
11
+ return response.arrayBuffer();
12
+ }
13
+ const api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadBrowserWasmBytes });
14
+ export const { init, parseToAst, version, __forceTrapForTests, __getTrapRecoveryStateForTests } = api;
15
+ export { MdastAdapterError, toMdastRoot } from "./mdast.js";
16
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
17
+ //# sourceMappingURL=parse-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-browser.js","sourceRoot":"","sources":["../src/parse-browser.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,0DAA0D,CAAC;AAChF,OAAO,QAAQ,MAAM,4CAA4C,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEpD,KAAK,UAAU,oBAAoB;IACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6CAA6C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC,CAAC;AAEtF,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,EAAE,8BAA8B,EAAE,GAC7F,GAAG,CAAC;AAEN,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import glueHref from \"./wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs?url\";\nimport wasmHref from \"./wasm-parse/zfb_md_wasm_parse_bg.wasm?url\";\nimport { createWasmApi } from \"./runtime.js\";\n\nconst GLUE_URL = new URL(glueHref, import.meta.url);\nconst WASM_URL = new URL(wasmHref, import.meta.url);\n\nasync function loadBrowserWasmBytes(): Promise<ArrayBuffer> {\n const response = await fetch(WASM_URL);\n if (!response.ok) {\n throw new Error(\n `zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`,\n );\n }\n return response.arrayBuffer();\n}\n\nconst api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadBrowserWasmBytes });\n\nexport const { init, parseToAst, version, __forceTrapForTests, __getTrapRecoveryStateForTests } =\n api;\n\nexport { MdastAdapterError, toMdastRoot } from \"./mdast.js\";\nexport { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from \"./runtime.js\";\nexport type {\n ParseToAstResult,\n ParseToAstOptions,\n ParseDialect,\n FrontmatterPolicy,\n ParsePipelineOptions,\n Diagnostic,\n DiagnosticSource,\n AstPoint,\n AstPosition,\n RawMdastData,\n MarkdownRsStop,\n MdastNode,\n MdastRoot,\n UnknownMdastNode,\n Root,\n Paragraph,\n Heading,\n ThematicBreak,\n Blockquote,\n List,\n ListItem,\n Html,\n Code,\n Definition,\n Text,\n DirectiveNodeBase,\n ContainerDirective,\n LeafDirective,\n TextDirective,\n Emphasis,\n Strong,\n InlineCode,\n Break,\n Link,\n Image,\n ReferenceKind,\n LinkReference,\n ImageReference,\n FootnoteDefinition,\n FootnoteReference,\n TableAlign,\n Table,\n TableRow,\n TableCell,\n Delete,\n Yaml,\n MdxFlowExpression,\n MdxTextExpression,\n MdxJsxFlowElement,\n MdxJsxTextElement,\n MdxJsxAttributeContent,\n MdxJsxAttribute,\n MdxJsxAttributeValueExpression,\n MdxJsxExpressionAttribute,\n} from \"./types.js\";\n"]}
@@ -0,0 +1,12 @@
1
+ export declare const init: () => Promise<void>, parseToAst: (source: string, options?: import("./types.js").ParseToAstOptions) => Promise<import("./types.js").ParseToAstResult>, version: () => Promise<string>, __forceTrapForTests: () => Promise<void>, __getTrapRecoveryStateForTests: () => {
2
+ compiledModuleLoads: number;
3
+ currentGeneration: number;
4
+ freshInstanceStarts: number;
5
+ glueImportAttempts: number;
6
+ maxTrapRecoveries: number;
7
+ trapRecoveriesStarted: number;
8
+ terminal: boolean;
9
+ };
10
+ export { MdastAdapterError, toMdastRoot } from "./mdast.js";
11
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
12
+ export type { ParseToAstResult, ParseToAstOptions, ParseDialect, FrontmatterPolicy, ParsePipelineOptions, Diagnostic, DiagnosticSource, AstPoint, AstPosition, RawMdastData, MarkdownRsStop, MdastNode, MdastRoot, UnknownMdastNode, Root, Paragraph, Heading, ThematicBreak, Blockquote, List, ListItem, Html, Code, Definition, Text, DirectiveNodeBase, ContainerDirective, LeafDirective, TextDirective, Emphasis, Strong, InlineCode, Break, Link, Image, ReferenceKind, LinkReference, ImageReference, FootnoteDefinition, FootnoteReference, TableAlign, Table, TableRow, TableCell, Delete, Yaml, MdxFlowExpression, MdxTextExpression, MdxJsxFlowElement, MdxJsxTextElement, MdxJsxAttributeContent, MdxJsxAttribute, MdxJsxAttributeValueExpression, MdxJsxExpressionAttribute, } from "./types.js";
package/dist/parse.js ADDED
@@ -0,0 +1,23 @@
1
+ import { createWasmApi } from "./runtime.js";
2
+ const GLUE_URL = new URL("./wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs", import.meta.url);
3
+ const WASM_URL = new URL("./wasm-parse/zfb_md_wasm_parse_bg.wasm", import.meta.url);
4
+ async function loadDirectWasmBytes() {
5
+ if (typeof process !== "undefined" && process.versions?.node) {
6
+ const [{ readFile }, { fileURLToPath }] = await Promise.all([
7
+ import("node:fs/promises"),
8
+ import("node:url"),
9
+ ]);
10
+ const bytes = await readFile(fileURLToPath(WASM_URL));
11
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
12
+ }
13
+ const response = await fetch(WASM_URL);
14
+ if (!response.ok) {
15
+ throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`);
16
+ }
17
+ return response.arrayBuffer();
18
+ }
19
+ const api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadDirectWasmBytes });
20
+ export const { init, parseToAst, version, __forceTrapForTests, __getTrapRecoveryStateForTests } = api;
21
+ export { MdastAdapterError, toMdastRoot } from "./mdast.js";
22
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
23
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,sDAAsD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClG,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,wCAAwC,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEpF,KAAK,UAAU,mBAAmB;IAChC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC7D,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC1D,MAAM,CAAC,kBAAkB,CAAC;YAC1B,MAAM,CAAC,UAAU,CAAC;SACnB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAgB,CAAC;IAClG,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6CAA6C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC,CAAC;AAErF,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,EAAE,8BAA8B,EAAE,GAC7F,GAAG,CAAC;AAEN,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { createWasmApi } from \"./runtime.js\";\n\nconst GLUE_URL = new URL(\"./wasm-parse/zfb_md_wasm_parse_glue.zfb-resource.mjs\", import.meta.url);\nconst WASM_URL = new URL(\"./wasm-parse/zfb_md_wasm_parse_bg.wasm\", import.meta.url);\n\nasync function loadDirectWasmBytes(): Promise<ArrayBuffer> {\n if (typeof process !== \"undefined\" && process.versions?.node) {\n const [{ readFile }, { fileURLToPath }] = await Promise.all([\n import(\"node:fs/promises\"),\n import(\"node:url\"),\n ]);\n const bytes = await readFile(fileURLToPath(WASM_URL));\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n }\n const response = await fetch(WASM_URL);\n if (!response.ok) {\n throw new Error(\n `zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`,\n );\n }\n return response.arrayBuffer();\n}\n\nconst api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadDirectWasmBytes });\n\nexport const { init, parseToAst, version, __forceTrapForTests, __getTrapRecoveryStateForTests } =\n api;\n\nexport { MdastAdapterError, toMdastRoot } from \"./mdast.js\";\nexport { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from \"./runtime.js\";\nexport type {\n ParseToAstResult,\n ParseToAstOptions,\n ParseDialect,\n FrontmatterPolicy,\n ParsePipelineOptions,\n Diagnostic,\n DiagnosticSource,\n AstPoint,\n AstPosition,\n RawMdastData,\n MarkdownRsStop,\n MdastNode,\n MdastRoot,\n UnknownMdastNode,\n Root,\n Paragraph,\n Heading,\n ThematicBreak,\n Blockquote,\n List,\n ListItem,\n Html,\n Code,\n Definition,\n Text,\n DirectiveNodeBase,\n ContainerDirective,\n LeafDirective,\n TextDirective,\n Emphasis,\n Strong,\n InlineCode,\n Break,\n Link,\n Image,\n ReferenceKind,\n LinkReference,\n ImageReference,\n FootnoteDefinition,\n FootnoteReference,\n TableAlign,\n Table,\n TableRow,\n TableCell,\n Delete,\n Yaml,\n MdxFlowExpression,\n MdxTextExpression,\n MdxJsxFlowElement,\n MdxJsxTextElement,\n MdxJsxAttributeContent,\n MdxJsxAttribute,\n MdxJsxAttributeValueExpression,\n MdxJsxExpressionAttribute,\n} from \"./types.js\";\n"]}
@@ -0,0 +1,11 @@
1
+ export declare const init: () => Promise<void>, renderHtml: (source: string, options?: import("./types.js").ZfbMdWasmOptions) => Promise<import("./types.js").RenderHtmlResult>, version: () => Promise<string>, __forceTrapForTests: () => Promise<void>, __getTrapRecoveryStateForTests: () => {
2
+ compiledModuleLoads: number;
3
+ currentGeneration: number;
4
+ freshInstanceStarts: number;
5
+ glueImportAttempts: number;
6
+ maxTrapRecoveries: number;
7
+ trapRecoveriesStarted: number;
8
+ terminal: boolean;
9
+ };
10
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
11
+ export type { RenderHtmlResult, Diagnostic, DiagnosticSource, ZfbMdWasmOptions, ParseDialect, PipelineOptions, GfmOptions, CodeHighlightMode, CodeHighlightOptions, MarkdownFeaturesConfig, JsxRuntime, HighlightRole, } from "./types.js";
@@ -0,0 +1,16 @@
1
+ import glueHref from "./wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs?url";
2
+ import wasmHref from "./wasm-render/zfb_md_wasm_render_bg.wasm?url";
3
+ import { createWasmApi } from "./runtime.js";
4
+ const GLUE_URL = new URL(glueHref, import.meta.url);
5
+ const WASM_URL = new URL(wasmHref, import.meta.url);
6
+ async function loadBrowserWasmBytes() {
7
+ const response = await fetch(WASM_URL);
8
+ if (!response.ok) {
9
+ throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`);
10
+ }
11
+ return response.arrayBuffer();
12
+ }
13
+ const api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadBrowserWasmBytes });
14
+ export const { init, renderHtml, version, __forceTrapForTests, __getTrapRecoveryStateForTests } = api;
15
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
16
+ //# sourceMappingURL=render-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-browser.js","sourceRoot":"","sources":["../src/render-browser.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,4DAA4D,CAAC;AAClF,OAAO,QAAQ,MAAM,8CAA8C,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpD,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEpD,KAAK,UAAU,oBAAoB;IACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6CAA6C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC,CAAC;AAEtF,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,EAAE,8BAA8B,EAAE,GAC7F,GAAG,CAAC;AAEN,OAAO,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import glueHref from \"./wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs?url\";\nimport wasmHref from \"./wasm-render/zfb_md_wasm_render_bg.wasm?url\";\nimport { createWasmApi } from \"./runtime.js\";\n\nconst GLUE_URL = new URL(glueHref, import.meta.url);\nconst WASM_URL = new URL(wasmHref, import.meta.url);\n\nasync function loadBrowserWasmBytes(): Promise<ArrayBuffer> {\n const response = await fetch(WASM_URL);\n if (!response.ok) {\n throw new Error(\n `zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`,\n );\n }\n return response.arrayBuffer();\n}\n\nconst api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadBrowserWasmBytes });\n\nexport const { init, renderHtml, version, __forceTrapForTests, __getTrapRecoveryStateForTests } =\n api;\n\nexport { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from \"./runtime.js\";\nexport type {\n RenderHtmlResult,\n Diagnostic,\n DiagnosticSource,\n ZfbMdWasmOptions,\n ParseDialect,\n PipelineOptions,\n GfmOptions,\n CodeHighlightMode,\n CodeHighlightOptions,\n MarkdownFeaturesConfig,\n JsxRuntime,\n HighlightRole,\n} from \"./types.js\";\n"]}
@@ -0,0 +1,11 @@
1
+ export declare const init: () => Promise<void>, renderHtml: (source: string, options?: import("./types.js").ZfbMdWasmOptions) => Promise<import("./types.js").RenderHtmlResult>, version: () => Promise<string>, __forceTrapForTests: () => Promise<void>, __getTrapRecoveryStateForTests: () => {
2
+ compiledModuleLoads: number;
3
+ currentGeneration: number;
4
+ freshInstanceStarts: number;
5
+ glueImportAttempts: number;
6
+ maxTrapRecoveries: number;
7
+ trapRecoveriesStarted: number;
8
+ terminal: boolean;
9
+ };
10
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
11
+ export type { RenderHtmlResult, Diagnostic, DiagnosticSource, ZfbMdWasmOptions, ParseDialect, PipelineOptions, GfmOptions, CodeHighlightMode, CodeHighlightOptions, MarkdownFeaturesConfig, JsxRuntime, HighlightRole, } from "./types.js";
package/dist/render.js ADDED
@@ -0,0 +1,22 @@
1
+ import { createWasmApi } from "./runtime.js";
2
+ const GLUE_URL = new URL("./wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs", import.meta.url);
3
+ const WASM_URL = new URL("./wasm-render/zfb_md_wasm_render_bg.wasm", import.meta.url);
4
+ async function loadDirectWasmBytes() {
5
+ if (typeof process !== "undefined" && process.versions?.node) {
6
+ const [{ readFile }, { fileURLToPath }] = await Promise.all([
7
+ import("node:fs/promises"),
8
+ import("node:url"),
9
+ ]);
10
+ const bytes = await readFile(fileURLToPath(WASM_URL));
11
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
12
+ }
13
+ const response = await fetch(WASM_URL);
14
+ if (!response.ok) {
15
+ throw new Error(`zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`);
16
+ }
17
+ return response.arrayBuffer();
18
+ }
19
+ const api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadDirectWasmBytes });
20
+ export const { init, renderHtml, version, __forceTrapForTests, __getTrapRecoveryStateForTests } = api;
21
+ export { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from "./runtime.js";
22
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,wDAAwD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACpG,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,0CAA0C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEtF,KAAK,UAAU,mBAAmB;IAChC,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC7D,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YAC1D,MAAM,CAAC,kBAAkB,CAAC;YAC1B,MAAM,CAAC,UAAU,CAAC;SACnB,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC;QACtD,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAgB,CAAC;IAClG,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,6CAA6C,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,GAAG,GAAG,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC,CAAC;AAErF,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,mBAAmB,EAAE,8BAA8B,EAAE,GAC7F,GAAG,CAAC;AAEN,OAAO,EAAE,kBAAkB,EAAE,+BAA+B,EAAE,MAAM,cAAc,CAAC","sourcesContent":["import { createWasmApi } from \"./runtime.js\";\n\nconst GLUE_URL = new URL(\"./wasm-render/zfb_md_wasm_render_glue.zfb-resource.mjs\", import.meta.url);\nconst WASM_URL = new URL(\"./wasm-render/zfb_md_wasm_render_bg.wasm\", import.meta.url);\n\nasync function loadDirectWasmBytes(): Promise<ArrayBuffer> {\n if (typeof process !== \"undefined\" && process.versions?.node) {\n const [{ readFile }, { fileURLToPath }] = await Promise.all([\n import(\"node:fs/promises\"),\n import(\"node:url\"),\n ]);\n const bytes = await readFile(fileURLToPath(WASM_URL));\n return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n }\n const response = await fetch(WASM_URL);\n if (!response.ok) {\n throw new Error(\n `zfb-md-wasm: failed to fetch wasm binary: ${response.status} ${response.statusText}`,\n );\n }\n return response.arrayBuffer();\n}\n\nconst api = createWasmApi({ glueUrl: GLUE_URL, loadWasmBytes: loadDirectWasmBytes });\n\nexport const { init, renderHtml, version, __forceTrapForTests, __getTrapRecoveryStateForTests } =\n api;\n\nexport { ZfbMdWasmTrapError, ZfbMdWasmTrapRecoveryLimitError } from \"./runtime.js\";\nexport type {\n RenderHtmlResult,\n Diagnostic,\n DiagnosticSource,\n ZfbMdWasmOptions,\n ParseDialect,\n PipelineOptions,\n GfmOptions,\n CodeHighlightMode,\n CodeHighlightOptions,\n MarkdownFeaturesConfig,\n JsxRuntime,\n HighlightRole,\n} from \"./types.js\";\n"]}
package/dist/runtime.d.ts CHANGED
@@ -6,7 +6,7 @@ interface WasmGlueModule {
6
6
  compile?(source: string, optionsJson: string): string;
7
7
  renderHtml?(source: string, optionsJson: string): string;
8
8
  parseToAst?(source: string, optionsJson: string): string;
9
- highlightCode(code: string, optionsJson: string): string;
9
+ highlightCode?(code: string, optionsJson: string): string;
10
10
  version(): string;
11
11
  __forceTrapForTests(): void;
12
12
  }
package/dist/runtime.js CHANGED
@@ -122,26 +122,23 @@ export function createWasmApi({ glueUrl, loadWasmBytes, compileWasm = (bytes) =>
122
122
  async function init() {
123
123
  await getInstance();
124
124
  }
125
- // Both throw helpers below fire only if `compile`/`renderHtml` were
126
- // somehow invoked against the highlight-only glue -- unreachable through
127
- // the public API surface, since `src/highlight.ts`/`src/highlight-browser.ts`
128
- // never re-export these two functions. Kept as a clear runtime error
129
- // rather than a silent `undefined()` crash.
130
- function requirePipelineExport(fn, name) {
125
+ // Public entry surfaces make missing calls unreachable. This guard keeps
126
+ // structural glue mismatches artifact-neutral and actionable.
127
+ function requireCapability(fn, name) {
131
128
  if (!fn) {
132
- throw new Error(`zfb-md-wasm: ${name}() is not available in the highlight-only wasm artifact ` +
133
- `(built with the \`pipeline\` Cargo feature off) -- use the default \`.\` entry instead.`);
129
+ throw new Error(`zfb-md-wasm: ${name}() is not available in this wasm artifact. ` +
130
+ `Import an entry whose artifact provides that capability.`);
134
131
  }
135
132
  return fn;
136
133
  }
137
134
  async function compile(source, options = {}) {
138
135
  const optionsJson = JSON.stringify(options);
139
- const json = await callWasm(({ glue }) => requirePipelineExport(glue.compile, "compile").call(glue, source, optionsJson));
136
+ const json = await callWasm(({ glue }) => requireCapability(glue.compile, "compile").call(glue, source, optionsJson));
140
137
  return JSON.parse(json);
141
138
  }
142
139
  async function renderHtml(source, options = {}) {
143
140
  const optionsJson = JSON.stringify(options);
144
- const json = await callWasm(({ glue }) => requirePipelineExport(glue.renderHtml, "renderHtml").call(glue, source, optionsJson));
141
+ const json = await callWasm(({ glue }) => requireCapability(glue.renderHtml, "renderHtml").call(glue, source, optionsJson));
145
142
  return JSON.parse(json);
146
143
  }
147
144
  /**
@@ -153,12 +150,12 @@ export function createWasmApi({ glueUrl, loadWasmBytes, compileWasm = (bytes) =>
153
150
  */
154
151
  async function parseToAst(source, options = {}) {
155
152
  const optionsJson = JSON.stringify(options);
156
- const json = await callWasm(({ glue }) => requirePipelineExport(glue.parseToAst, "parseToAst").call(glue, source, optionsJson));
153
+ const json = await callWasm(({ glue }) => requireCapability(glue.parseToAst, "parseToAst").call(glue, source, optionsJson));
157
154
  return JSON.parse(json);
158
155
  }
159
156
  async function highlightCode(code, options) {
160
157
  const optionsJson = JSON.stringify(options);
161
- const json = await callWasm(({ glue }) => glue.highlightCode(code, optionsJson));
158
+ const json = await callWasm(({ glue }) => requireCapability(glue.highlightCode, "highlightCode").call(glue, code, optionsJson));
162
159
  return JSON.parse(json);
163
160
  }
164
161
  async function version() {
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CompileResult,\n HighlightCodeOptions,\n HighlightCodeResult,\n ParseToAstOptions,\n ParseToAstResult,\n RenderHtmlResult,\n ZfbMdWasmOptions,\n} from \"./types.js\";\n\ninterface WasmGlueModule {\n // Keep this deliberately structural instead of importing the generated\n // glue's declaration file. The browser entry imports that file as a *URL\n // resource*, while the direct entry imports it dynamically. Both paths\n // nevertheless use the same wasm-bindgen surface and recovery\n // implementation below.\n //\n // `compile`/`renderHtml` are optional: the highlight-only wasm artifact\n // (`./highlight` entry, zfb#1849, epic zfb#1845) is compiled with the\n // `pipeline` Cargo feature off (see crates/zfb-md-wasm/Cargo.toml), so its\n // wasm-bindgen glue never generates these exports at all. `createWasmApi`\n // below is shared by both entries; only `src/index.ts`/`src/browser.ts`\n // re-export the functions that call them.\n initSync(input?: { module: WebAssembly.Module }): unknown;\n compile?(source: string, optionsJson: string): string;\n renderHtml?(source: string, optionsJson: string): string;\n // Raw-mdast export (zfb#1857, epic zfb#1854); pipeline-gated like\n // compile/renderHtml -- the highlight-only artifact never generates it.\n parseToAst?(source: string, optionsJson: string): string;\n highlightCode(code: string, optionsJson: string): string;\n version(): string;\n __forceTrapForTests(): void;\n}\n\nexport interface WasmResourceConfig {\n glueUrl: URL;\n loadWasmBytes(): Promise<ArrayBuffer>;\n /** @internal Deterministic test seam; production uses WebAssembly.compile. */\n compileWasm?(bytes: ArrayBuffer): Promise<WebAssembly.Module>;\n /** @internal Deterministic test seam; production dynamically imports the glue URL. */\n importGlue?(specifier: string): Promise<WasmGlueModule>;\n}\n\nexport class ZfbMdWasmTrapError extends Error {\n constructor(cause: unknown) {\n super(\n \"zfb-md-wasm: the wasm instance trapped (a Rust panic or internal fault) and has been \" +\n \"automatically re-instantiated. This is always a bug in zfb-md-wasm -- please report it \" +\n \"with the input that triggered it.\",\n );\n this.name = \"ZfbMdWasmTrapError\";\n this.cause = cause;\n }\n}\n\nexport class ZfbMdWasmTrapRecoveryLimitError extends Error {\n constructor(maxRecoveries: number, cause: unknown) {\n super(\n `zfb-md-wasm: wasm trap recovery limit reached after ${maxRecoveries} ` +\n `successful re-instantiations. Further automatic recovery is disabled to avoid ` +\n `unbounded ES module record growth. Reload the JS realm before using zfb-md-wasm ` +\n `again, and please report the input that triggered the repeated traps.`,\n );\n this.name = \"ZfbMdWasmTrapRecoveryLimitError\";\n this.cause = cause;\n }\n}\n\nexport function createWasmApi({\n glueUrl,\n loadWasmBytes,\n compileWasm = (bytes) => WebAssembly.compile(bytes),\n importGlue = (specifier) => import(/* @vite-ignore */ specifier) as Promise<WasmGlueModule>,\n}: WasmResourceConfig) {\n let compiledModulePromise: Promise<WebAssembly.Module> | undefined;\n let compiledModuleLoads = 0;\n\n function getCompiledModule(): Promise<WebAssembly.Module> {\n if (!compiledModulePromise) {\n compiledModuleLoads += 1;\n const attempt = Promise.resolve().then(loadWasmBytes).then(compileWasm);\n compiledModulePromise = attempt;\n void attempt.catch(() => {\n // A rejected attempt must be retryable, but it must not erase a newer\n // attempt if its rejection cleanup runs late.\n if (compiledModulePromise === attempt) {\n compiledModulePromise = undefined;\n }\n });\n }\n return compiledModulePromise;\n }\n\n const MAX_TRAP_RECOVERIES = 16;\n\n let currentGeneration = 0;\n let trapRecoveriesStarted = 0;\n let freshInstanceStarts = 0;\n let glueImportAttempts = 0;\n let terminalTrapRecoveryError: ZfbMdWasmTrapRecoveryLimitError | undefined;\n\n interface Instance {\n generation: number;\n glue: WasmGlueModule;\n }\n\n /**\n * Every fresh instance attempt creates a new wasm-bindgen glue module\n * record. The import-attempt nonce is deliberately independent of the trap\n * generation: transient import/initSync failures need a new module record\n * without consuming the bounded trap-recovery budget.\n */\n async function freshInstance(generation: number): Promise<Instance> {\n freshInstanceStarts += 1;\n glueImportAttempts += 1;\n const glueSpecifier = new URL(glueUrl.href);\n glueSpecifier.searchParams.set(\"zfbMdWasmGen\", String(generation));\n glueSpecifier.searchParams.set(\"zfbMdWasmAttempt\", String(glueImportAttempts));\n const [module, glue] = await Promise.all([\n getCompiledModule(),\n // Deferring the injected function also converts a synchronous test-seam\n // throw into the rejection handled by the retryable instance cache.\n Promise.resolve().then(() => importGlue(glueSpecifier.href)),\n ]);\n glue.initSync({ module });\n return { generation, glue };\n }\n\n let instancePromise: Promise<Instance> | undefined;\n\n function startInstance(generation: number): Promise<Instance> {\n const attempt = freshInstance(generation);\n instancePromise = attempt;\n void attempt.catch(() => {\n // Trap recovery can replace the installed instance promise. Never let a\n // stale initialization rejection clear that newer attempt.\n if (instancePromise === attempt) {\n instancePromise = undefined;\n }\n });\n return attempt;\n }\n\n function getInstance(): Promise<Instance> {\n if (terminalTrapRecoveryError) {\n return Promise.reject(terminalTrapRecoveryError);\n }\n if (!instancePromise) {\n return startInstance(currentGeneration);\n }\n return instancePromise;\n }\n\n function isTrap(err: unknown): boolean {\n return typeof WebAssembly !== \"undefined\" && err instanceof WebAssembly.RuntimeError;\n }\n\n async function recoverAfterTrap(observedGeneration: number, cause: unknown): Promise<void> {\n if (terminalTrapRecoveryError) {\n throw terminalTrapRecoveryError;\n }\n\n // CAS-style single-flight: reporters for an already-replaced generation\n // await its replacement instead of creating another glue module record.\n if (observedGeneration !== currentGeneration) {\n await getInstance();\n return;\n }\n\n if (trapRecoveriesStarted >= MAX_TRAP_RECOVERIES) {\n terminalTrapRecoveryError = new ZfbMdWasmTrapRecoveryLimitError(MAX_TRAP_RECOVERIES, cause);\n instancePromise = undefined;\n throw terminalTrapRecoveryError;\n }\n\n trapRecoveriesStarted += 1;\n currentGeneration += 1;\n await startInstance(currentGeneration);\n }\n\n async function callWasm<T>(fn: (instance: Instance) => T): Promise<T> {\n const instance = await getInstance();\n try {\n return fn(instance);\n } catch (err) {\n if (!isTrap(err)) {\n throw err;\n }\n await recoverAfterTrap(instance.generation, err);\n throw new ZfbMdWasmTrapError(err);\n }\n }\n\n async function init(): Promise<void> {\n await getInstance();\n }\n\n // Both throw helpers below fire only if `compile`/`renderHtml` were\n // somehow invoked against the highlight-only glue -- unreachable through\n // the public API surface, since `src/highlight.ts`/`src/highlight-browser.ts`\n // never re-export these two functions. Kept as a clear runtime error\n // rather than a silent `undefined()` crash.\n function requirePipelineExport<T>(fn: T | undefined, name: string): T {\n if (!fn) {\n throw new Error(\n `zfb-md-wasm: ${name}() is not available in the highlight-only wasm artifact ` +\n `(built with the \\`pipeline\\` Cargo feature off) -- use the default \\`.\\` entry instead.`,\n );\n }\n return fn;\n }\n\n async function compile(source: string, options: ZfbMdWasmOptions = {}): Promise<CompileResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requirePipelineExport(glue.compile, \"compile\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as CompileResult;\n }\n\n async function renderHtml(\n source: string,\n options: ZfbMdWasmOptions = {},\n ): Promise<RenderHtmlResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requirePipelineExport(glue.renderHtml, \"renderHtml\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as RenderHtmlResult;\n }\n\n /**\n * Parse markdown/MDX into a raw mdast tree (zfb#1857, epic zfb#1854).\n * The `parseToAst + JSON.parse` round trip here IS the product cost the\n * epic's benchmark measures against remark-parse. See `types.ts`'s\n * `ParseToAstResult`/`MdastNode` docs for the result shape and the\n * UTF-16 position contract.\n */\n async function parseToAst(\n source: string,\n options: ParseToAstOptions = {},\n ): Promise<ParseToAstResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requirePipelineExport(glue.parseToAst, \"parseToAst\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as ParseToAstResult;\n }\n\n async function highlightCode(\n code: string,\n options: HighlightCodeOptions,\n ): Promise<HighlightCodeResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) => glue.highlightCode(code, optionsJson));\n return JSON.parse(json) as HighlightCodeResult;\n }\n\n async function version(): Promise<string> {\n return callWasm(({ glue }) => glue.version());\n }\n\n /** @internal Test-only hook that forces the current instance to trap. */\n async function __forceTrapForTests(): Promise<void> {\n await callWasm(({ glue }) => glue.__forceTrapForTests());\n }\n\n /** @internal Test-only observability for the bounded recovery contract. */\n function __getTrapRecoveryStateForTests(): {\n compiledModuleLoads: number;\n currentGeneration: number;\n freshInstanceStarts: number;\n glueImportAttempts: number;\n maxTrapRecoveries: number;\n trapRecoveriesStarted: number;\n terminal: boolean;\n } {\n return {\n compiledModuleLoads,\n currentGeneration,\n freshInstanceStarts,\n glueImportAttempts,\n maxTrapRecoveries: MAX_TRAP_RECOVERIES,\n trapRecoveriesStarted,\n terminal: !!terminalTrapRecoveryError,\n };\n }\n\n return {\n init,\n compile,\n renderHtml,\n parseToAst,\n highlightCode,\n version,\n __forceTrapForTests,\n __getTrapRecoveryStateForTests,\n };\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CompileResult,\n HighlightCodeOptions,\n HighlightCodeResult,\n ParseToAstOptions,\n ParseToAstResult,\n RenderHtmlResult,\n ZfbMdWasmOptions,\n} from \"./types.js\";\n\ninterface WasmGlueModule {\n // Keep this deliberately structural instead of importing the generated\n // glue's declaration file. The browser entry imports that file as a *URL\n // resource*, while the direct entry imports it dynamically. Both paths\n // nevertheless use the same wasm-bindgen surface and recovery\n // implementation below.\n //\n // Capability functions are structural and optional because each singleton\n // artifact's wasm-bindgen glue contains only its selected Rust export.\n // Public entries expose only the matching calls from `createWasmApi`.\n initSync(input?: { module: WebAssembly.Module }): unknown;\n compile?(source: string, optionsJson: string): string;\n renderHtml?(source: string, optionsJson: string): string;\n // Raw-mdast export (zfb#1857, epic zfb#1854).\n parseToAst?(source: string, optionsJson: string): string;\n highlightCode?(code: string, optionsJson: string): string;\n version(): string;\n __forceTrapForTests(): void;\n}\n\nexport interface WasmResourceConfig {\n glueUrl: URL;\n loadWasmBytes(): Promise<ArrayBuffer>;\n /** @internal Deterministic test seam; production uses WebAssembly.compile. */\n compileWasm?(bytes: ArrayBuffer): Promise<WebAssembly.Module>;\n /** @internal Deterministic test seam; production dynamically imports the glue URL. */\n importGlue?(specifier: string): Promise<WasmGlueModule>;\n}\n\nexport class ZfbMdWasmTrapError extends Error {\n constructor(cause: unknown) {\n super(\n \"zfb-md-wasm: the wasm instance trapped (a Rust panic or internal fault) and has been \" +\n \"automatically re-instantiated. This is always a bug in zfb-md-wasm -- please report it \" +\n \"with the input that triggered it.\",\n );\n this.name = \"ZfbMdWasmTrapError\";\n this.cause = cause;\n }\n}\n\nexport class ZfbMdWasmTrapRecoveryLimitError extends Error {\n constructor(maxRecoveries: number, cause: unknown) {\n super(\n `zfb-md-wasm: wasm trap recovery limit reached after ${maxRecoveries} ` +\n `successful re-instantiations. Further automatic recovery is disabled to avoid ` +\n `unbounded ES module record growth. Reload the JS realm before using zfb-md-wasm ` +\n `again, and please report the input that triggered the repeated traps.`,\n );\n this.name = \"ZfbMdWasmTrapRecoveryLimitError\";\n this.cause = cause;\n }\n}\n\nexport function createWasmApi({\n glueUrl,\n loadWasmBytes,\n compileWasm = (bytes) => WebAssembly.compile(bytes),\n importGlue = (specifier) => import(/* @vite-ignore */ specifier) as Promise<WasmGlueModule>,\n}: WasmResourceConfig) {\n let compiledModulePromise: Promise<WebAssembly.Module> | undefined;\n let compiledModuleLoads = 0;\n\n function getCompiledModule(): Promise<WebAssembly.Module> {\n if (!compiledModulePromise) {\n compiledModuleLoads += 1;\n const attempt = Promise.resolve().then(loadWasmBytes).then(compileWasm);\n compiledModulePromise = attempt;\n void attempt.catch(() => {\n // A rejected attempt must be retryable, but it must not erase a newer\n // attempt if its rejection cleanup runs late.\n if (compiledModulePromise === attempt) {\n compiledModulePromise = undefined;\n }\n });\n }\n return compiledModulePromise;\n }\n\n const MAX_TRAP_RECOVERIES = 16;\n\n let currentGeneration = 0;\n let trapRecoveriesStarted = 0;\n let freshInstanceStarts = 0;\n let glueImportAttempts = 0;\n let terminalTrapRecoveryError: ZfbMdWasmTrapRecoveryLimitError | undefined;\n\n interface Instance {\n generation: number;\n glue: WasmGlueModule;\n }\n\n /**\n * Every fresh instance attempt creates a new wasm-bindgen glue module\n * record. The import-attempt nonce is deliberately independent of the trap\n * generation: transient import/initSync failures need a new module record\n * without consuming the bounded trap-recovery budget.\n */\n async function freshInstance(generation: number): Promise<Instance> {\n freshInstanceStarts += 1;\n glueImportAttempts += 1;\n const glueSpecifier = new URL(glueUrl.href);\n glueSpecifier.searchParams.set(\"zfbMdWasmGen\", String(generation));\n glueSpecifier.searchParams.set(\"zfbMdWasmAttempt\", String(glueImportAttempts));\n const [module, glue] = await Promise.all([\n getCompiledModule(),\n // Deferring the injected function also converts a synchronous test-seam\n // throw into the rejection handled by the retryable instance cache.\n Promise.resolve().then(() => importGlue(glueSpecifier.href)),\n ]);\n glue.initSync({ module });\n return { generation, glue };\n }\n\n let instancePromise: Promise<Instance> | undefined;\n\n function startInstance(generation: number): Promise<Instance> {\n const attempt = freshInstance(generation);\n instancePromise = attempt;\n void attempt.catch(() => {\n // Trap recovery can replace the installed instance promise. Never let a\n // stale initialization rejection clear that newer attempt.\n if (instancePromise === attempt) {\n instancePromise = undefined;\n }\n });\n return attempt;\n }\n\n function getInstance(): Promise<Instance> {\n if (terminalTrapRecoveryError) {\n return Promise.reject(terminalTrapRecoveryError);\n }\n if (!instancePromise) {\n return startInstance(currentGeneration);\n }\n return instancePromise;\n }\n\n function isTrap(err: unknown): boolean {\n return typeof WebAssembly !== \"undefined\" && err instanceof WebAssembly.RuntimeError;\n }\n\n async function recoverAfterTrap(observedGeneration: number, cause: unknown): Promise<void> {\n if (terminalTrapRecoveryError) {\n throw terminalTrapRecoveryError;\n }\n\n // CAS-style single-flight: reporters for an already-replaced generation\n // await its replacement instead of creating another glue module record.\n if (observedGeneration !== currentGeneration) {\n await getInstance();\n return;\n }\n\n if (trapRecoveriesStarted >= MAX_TRAP_RECOVERIES) {\n terminalTrapRecoveryError = new ZfbMdWasmTrapRecoveryLimitError(MAX_TRAP_RECOVERIES, cause);\n instancePromise = undefined;\n throw terminalTrapRecoveryError;\n }\n\n trapRecoveriesStarted += 1;\n currentGeneration += 1;\n await startInstance(currentGeneration);\n }\n\n async function callWasm<T>(fn: (instance: Instance) => T): Promise<T> {\n const instance = await getInstance();\n try {\n return fn(instance);\n } catch (err) {\n if (!isTrap(err)) {\n throw err;\n }\n await recoverAfterTrap(instance.generation, err);\n throw new ZfbMdWasmTrapError(err);\n }\n }\n\n async function init(): Promise<void> {\n await getInstance();\n }\n\n // Public entry surfaces make missing calls unreachable. This guard keeps\n // structural glue mismatches artifact-neutral and actionable.\n function requireCapability<T>(fn: T | undefined, name: string): T {\n if (!fn) {\n throw new Error(\n `zfb-md-wasm: ${name}() is not available in this wasm artifact. ` +\n `Import an entry whose artifact provides that capability.`,\n );\n }\n return fn;\n }\n\n async function compile(source: string, options: ZfbMdWasmOptions = {}): Promise<CompileResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requireCapability(glue.compile, \"compile\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as CompileResult;\n }\n\n async function renderHtml(\n source: string,\n options: ZfbMdWasmOptions = {},\n ): Promise<RenderHtmlResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requireCapability(glue.renderHtml, \"renderHtml\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as RenderHtmlResult;\n }\n\n /**\n * Parse markdown/MDX into a raw mdast tree (zfb#1857, epic zfb#1854).\n * The `parseToAst + JSON.parse` round trip here IS the product cost the\n * epic's benchmark measures against remark-parse. See `types.ts`'s\n * `ParseToAstResult`/`MdastNode` docs for the result shape and the\n * UTF-16 position contract.\n */\n async function parseToAst(\n source: string,\n options: ParseToAstOptions = {},\n ): Promise<ParseToAstResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requireCapability(glue.parseToAst, \"parseToAst\").call(glue, source, optionsJson),\n );\n return JSON.parse(json) as ParseToAstResult;\n }\n\n async function highlightCode(\n code: string,\n options: HighlightCodeOptions,\n ): Promise<HighlightCodeResult> {\n const optionsJson = JSON.stringify(options);\n const json = await callWasm(({ glue }) =>\n requireCapability(glue.highlightCode, \"highlightCode\").call(glue, code, optionsJson),\n );\n return JSON.parse(json) as HighlightCodeResult;\n }\n\n async function version(): Promise<string> {\n return callWasm(({ glue }) => glue.version());\n }\n\n /** @internal Test-only hook that forces the current instance to trap. */\n async function __forceTrapForTests(): Promise<void> {\n await callWasm(({ glue }) => glue.__forceTrapForTests());\n }\n\n /** @internal Test-only observability for the bounded recovery contract. */\n function __getTrapRecoveryStateForTests(): {\n compiledModuleLoads: number;\n currentGeneration: number;\n freshInstanceStarts: number;\n glueImportAttempts: number;\n maxTrapRecoveries: number;\n trapRecoveriesStarted: number;\n terminal: boolean;\n } {\n return {\n compiledModuleLoads,\n currentGeneration,\n freshInstanceStarts,\n glueImportAttempts,\n maxTrapRecoveries: MAX_TRAP_RECOVERIES,\n trapRecoveriesStarted,\n terminal: !!terminalTrapRecoveryError,\n };\n }\n\n return {\n init,\n compile,\n renderHtml,\n parseToAst,\n highlightCode,\n version,\n __forceTrapForTests,\n __getTrapRecoveryStateForTests,\n };\n}\n"]}