@markup-carve/carve-wasm 0.1.5 → 0.1.7

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/carve_wasm_bg.js CHANGED
@@ -1,3 +1,72 @@
1
+ /**
2
+ * A parser snapshot retained between editor updates. Call `free()` when done.
3
+ */
4
+ export class ParserSession {
5
+ __destroy_into_raw() {
6
+ const ptr = this.__wbg_ptr;
7
+ this.__wbg_ptr = 0;
8
+ ParserSessionFinalization.unregister(this);
9
+ return ptr;
10
+ }
11
+ free() {
12
+ const ptr = this.__destroy_into_raw();
13
+ wasm.__wbg_parsersession_free(ptr, 0);
14
+ }
15
+ /**
16
+ * Apply byte edits; a rejected edit leaves the session unchanged.
17
+ * @param {string} changes
18
+ * @returns {string}
19
+ */
20
+ edit(changes) {
21
+ let deferred3_0;
22
+ let deferred3_1;
23
+ try {
24
+ const ptr0 = passStringToWasm0(changes, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
25
+ const len0 = WASM_VECTOR_LEN;
26
+ const ret = wasm.parsersession_edit(this.__wbg_ptr, ptr0, len0);
27
+ var ptr2 = ret[0];
28
+ var len2 = ret[1];
29
+ if (ret[3]) {
30
+ ptr2 = 0; len2 = 0;
31
+ throw takeFromExternrefTable0(ret[2]);
32
+ }
33
+ deferred3_0 = ptr2;
34
+ deferred3_1 = len2;
35
+ return getStringFromWasm0(ptr2, len2);
36
+ } finally {
37
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
38
+ }
39
+ }
40
+ /**
41
+ * @param {string} source
42
+ */
43
+ constructor(source) {
44
+ const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
45
+ const len0 = WASM_VECTOR_LEN;
46
+ const ret = wasm.parsersession_new(ptr0, len0);
47
+ this.__wbg_ptr = ret >>> 0;
48
+ ParserSessionFinalization.register(this, this.__wbg_ptr, this);
49
+ return this;
50
+ }
51
+ /**
52
+ * The current result, with byte ranges and parser-work counters.
53
+ * @returns {string}
54
+ */
55
+ snapshot() {
56
+ let deferred1_0;
57
+ let deferred1_1;
58
+ try {
59
+ const ret = wasm.parsersession_snapshot(this.__wbg_ptr);
60
+ deferred1_0 = ret[0];
61
+ deferred1_1 = ret[1];
62
+ return getStringFromWasm0(ret[0], ret[1]);
63
+ } finally {
64
+ wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
65
+ }
66
+ }
67
+ }
68
+ if (Symbol.dispose) ParserSession.prototype[Symbol.dispose] = ParserSession.prototype.free;
69
+
1
70
  /**
2
71
  * Replay patch JSON onto a PART 12 tree, returning the result as AST JSON.
3
72
  * @param {string} ast
@@ -99,7 +168,7 @@ export function applyReversibleAstPatch(ast, patch, inverse) {
99
168
  /**
100
169
  * Apply a trusted patch after its source length and fingerprint still match.
101
170
  * @param {string} source
102
- * @param {any} patch
171
+ * @param {SourcePatch} patch
103
172
  * @returns {string}
104
173
  */
105
174
  export function applySourcePatch(source, patch) {
@@ -127,7 +196,7 @@ export function applySourcePatch(source, patch) {
127
196
  * Render an AST-JSON document (PART 12) to ANSI-styled text. See
128
197
  * [`ast_json_to_markdown`], including what a tree without positions costs.
129
198
  * @param {string} json
130
- * @param {object | null} [options]
199
+ * @param {RenderOptions | null} options
131
200
  * @returns {string}
132
201
  */
133
202
  export function astJsonToAnsi(json, options) {
@@ -191,7 +260,7 @@ export function astJsonToCarve(json) {
191
260
  * Takes the same options object as [`to_html_with_options`], so an edited tree
192
261
  * renders under the profile, labels and switches the host already configured.
193
262
  * @param {string} json
194
- * @param {object | null} [options]
263
+ * @param {RenderOptions | null} options
195
264
  * @returns {string}
196
265
  */
197
266
  export function astJsonToHtml(json, options) {
@@ -235,7 +304,7 @@ export function astJsonToHtml(json, options) {
235
304
  * order available to a tree without spans, so it is reported here rather than
236
305
  * repaired.
237
306
  * @param {string} json
238
- * @param {object | null} [options]
307
+ * @param {RenderOptions | null} options
239
308
  * @returns {string}
240
309
  */
241
310
  export function astJsonToMarkdown(json, options) {
@@ -263,7 +332,7 @@ export function astJsonToMarkdown(json, options) {
263
332
  * Render an AST-JSON document (PART 12) to plain text. See
264
333
  * [`ast_json_to_markdown`], including what a tree without positions costs.
265
334
  * @param {string} json
266
- * @param {object | null} [options]
335
+ * @param {RenderOptions | null} options
267
336
  * @returns {string}
268
337
  */
269
338
  export function astJsonToPlainText(json, options) {
@@ -438,7 +507,7 @@ export function createSourcePatch(source, replacement, kind, code) {
438
507
  * and a span on a node that came from a child would point into a source the
439
508
  * caller did not pass.
440
509
  * @param {string} source
441
- * @param {object} options
510
+ * @param {IncludeOptions} options
442
511
  * @returns {IncludeExpansion}
443
512
  */
444
513
  export function expandIncludes(source, options) {
@@ -666,8 +735,9 @@ export function lintAccessibility(source) {
666
735
  *
667
736
  * Returns an array of `{ line, column, rule, message, start, end }`. The rule
668
737
  * ids are shared with carve-js and carve-php, so the same trigger reports the
669
- * same id everywhere. Offsets are BYTE offsets into the source, matching the
670
- * engine.
738
+ * same id everywhere. Offsets count UTF-16 CODE UNITS, so they index the
739
+ * JavaScript string the caller holds; the engine answers in bytes and this
740
+ * wrapper converts (#164).
671
741
  *
672
742
  * Built as JS objects rather than a JSON string: a message carries arbitrary
673
743
  * document text, and hand-rolled JSON escaping is where that goes wrong.
@@ -690,8 +760,8 @@ export function lintCarve(source) {
690
760
  * `lintCarve` is the option-less form. Which degradations exist depends on the
691
761
  * extensions in play, so a host that renders with a set has to lint with it.
692
762
  * @param {string} source
693
- * @param {object | null} [options]
694
- * @returns {LintWarning[]}
763
+ * @param {RenderOptions | null} options
764
+ * @returns {any}
695
765
  */
696
766
  export function lintCarveWithOptions(source, options) {
697
767
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -714,17 +784,23 @@ export function lintCarveWithOptions(source, options) {
714
784
  * @returns {string}
715
785
  */
716
786
  export function markdownToAstJson(source) {
717
- let deferred2_0;
718
- let deferred2_1;
787
+ let deferred3_0;
788
+ let deferred3_1;
719
789
  try {
720
790
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
721
791
  const len0 = WASM_VECTOR_LEN;
722
792
  const ret = wasm.markdownToAstJson(ptr0, len0);
723
- deferred2_0 = ret[0];
724
- deferred2_1 = ret[1];
725
- return getStringFromWasm0(ret[0], ret[1]);
793
+ var ptr2 = ret[0];
794
+ var len2 = ret[1];
795
+ if (ret[3]) {
796
+ ptr2 = 0; len2 = 0;
797
+ throw takeFromExternrefTable0(ret[2]);
798
+ }
799
+ deferred3_0 = ptr2;
800
+ deferred3_1 = len2;
801
+ return getStringFromWasm0(ptr2, len2);
726
802
  } finally {
727
- wasm.__wbindgen_free(deferred2_0, deferred2_1, 1);
803
+ wasm.__wbindgen_free(deferred3_0, deferred3_1, 1);
728
804
  }
729
805
  }
730
806
 
@@ -872,7 +948,7 @@ export function parseJson(source) {
872
948
  * `positions` is not read: every serialized tree this binding produces carries
873
949
  * them, as in [`parse_json`].
874
950
  * @param {string} source
875
- * @param {object | null} [options]
951
+ * @param {RenderOptions | null} options
876
952
  * @returns {string}
877
953
  */
878
954
  export function parseJsonWithOptions(source, options) {
@@ -995,8 +1071,8 @@ export function readStamp(source) {
995
1071
  *
996
1072
  * `changes` is a JSON array of `{ range: [start, end], replacement }`.
997
1073
  *
998
- * THE OFFSETS ARE UTF-8 BYTE OFFSETS, which is what `parseJson` positions and
999
- * `createSourcePatch` ranges already mean. A browser editor counts UTF-16 code
1074
+ * THE OFFSETS ARE UTF-8 BYTE OFFSETS, matching `createSourcePatch` ranges.
1075
+ * `parseJson` positions count Unicode codepoints instead. A browser editor counts UTF-16 code
1000
1076
  * units, so a host holding a `selectionStart` converts before calling - an
1001
1077
  * offset that lands inside a multi-byte character is refused rather than
1002
1078
  * guessed at.
@@ -1006,9 +1082,9 @@ export function readStamp(source) {
1006
1082
  * result the caller asked for.
1007
1083
  *
1008
1084
  * `reusedPreviousTree` says whether the parse reused any of the previous one.
1009
- * The pinned engine always reports `false` - it validates and applies the
1010
- * edits and then parses the whole source - so a host should read this as the
1011
- * engine's own answer rather than assume work was saved.
1085
+ * The engine can reuse unchanged plain paragraphs. Other syntax falls back
1086
+ * to a full parse. This function rebuilds the initial snapshot from source;
1087
+ * use a parser session to retain it across edits.
1012
1088
  * @param {string} source
1013
1089
  * @param {string} changes
1014
1090
  * @returns {string}
@@ -1122,7 +1198,7 @@ export function toAnsi(source) {
1122
1198
  * Render to ANSI text with an options object. See
1123
1199
  * [`to_markdown_with_options`].
1124
1200
  * @param {string} source
1125
- * @param {object | null} [options]
1201
+ * @param {RenderOptions | null} options
1126
1202
  * @returns {string}
1127
1203
  */
1128
1204
  export function toAnsiWithOptions(source, options) {
@@ -1148,14 +1224,14 @@ export function toAnsiWithOptions(source, options) {
1148
1224
 
1149
1225
  /**
1150
1226
  * @param {string} source
1151
- * @param {boolean | null} [strict]
1152
- * @param {number | null} [maximum]
1227
+ * @param {boolean | null} strict
1228
+ * @param {number | null} maximum
1153
1229
  * @returns {any}
1154
1230
  */
1155
1231
  export function toAnsiWithReport(source, strict, maximum) {
1156
1232
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1157
1233
  const len0 = WASM_VECTOR_LEN;
1158
- const ret = wasm.toAnsiWithReport(ptr0, len0, isLikeNone(strict) ? 0xFFFFFF : strict ? 1 : 0, isLikeNone(maximum) ? 0x100000001 : (maximum) >>> 0);
1234
+ const ret = wasm.toAnsiWithReport(ptr0, len0, isLikeNone(strict) ? 0 : addToExternrefTable0(strict), isLikeNone(maximum) ? 0 : addToExternrefTable0(maximum));
1159
1235
  if (ret[2]) {
1160
1236
  throw takeFromExternrefTable0(ret[1]);
1161
1237
  }
@@ -1202,7 +1278,7 @@ export function toCarvePatch(source) {
1202
1278
  * `profile` is the only option this target reads; see
1203
1279
  * [`to_markdown_with_options`] for why.
1204
1280
  * @param {string} source
1205
- * @param {object | null} [options]
1281
+ * @param {RenderOptions | null} options
1206
1282
  * @returns {string}
1207
1283
  */
1208
1284
  export function toCarveWithOptions(source, options) {
@@ -1228,14 +1304,14 @@ export function toCarveWithOptions(source, options) {
1228
1304
 
1229
1305
  /**
1230
1306
  * @param {string} source
1231
- * @param {boolean | null} [strict]
1232
- * @param {number | null} [maximum]
1307
+ * @param {boolean | null} strict
1308
+ * @param {number | null} maximum
1233
1309
  * @returns {any}
1234
1310
  */
1235
1311
  export function toCarveWithReport(source, strict, maximum) {
1236
1312
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1237
1313
  const len0 = WASM_VECTOR_LEN;
1238
- const ret = wasm.toCarveWithReport(ptr0, len0, isLikeNone(strict) ? 0xFFFFFF : strict ? 1 : 0, isLikeNone(maximum) ? 0x100000001 : (maximum) >>> 0);
1314
+ const ret = wasm.toCarveWithReport(ptr0, len0, isLikeNone(strict) ? 0 : addToExternrefTable0(strict), isLikeNone(maximum) ? 0 : addToExternrefTable0(maximum));
1239
1315
  if (ret[2]) {
1240
1316
  throw takeFromExternrefTable0(ret[1]);
1241
1317
  }
@@ -1352,8 +1428,9 @@ export function toHtmlFull(source, symbols) {
1352
1428
  * * `labels` - override the engine-written strings (admonition names, the
1353
1429
  * endnotes heading, backlink text) for a page that is not in English. These
1354
1430
  * are TEXT and are escaped where they land, unlike `symbols`.
1355
- * * `smartTypography` - `"glyph"` (default) resolves `...` to an ellipsis,
1356
- * `"source"` keeps the author's run.
1431
+ * * `smartTypography` - `"glyph"` or `true` (default) resolves `...` to an
1432
+ * ellipsis, `"source"` or `false` keeps the author's run, and
1433
+ * `{ quotes: false }` keeps only the author's quotes.
1357
1434
  * * `lowercaseHeadingIds` (default `false`) and `asciiHeadingIds`
1358
1435
  * (`"off"` (default), `"fold"`, `"strict"`) - the slug policy, for a host
1359
1436
  * whose anchors have to match another generator's.
@@ -1379,7 +1456,7 @@ export function toHtmlFull(source, symbols) {
1379
1456
  * endnotes `<section role="doc-endnotes">` is a separate construct that is
1380
1457
  * still emitted.
1381
1458
  * @param {string} source
1382
- * @param {object | null} [options]
1459
+ * @param {RenderOptions | null} options
1383
1460
  * @returns {string}
1384
1461
  */
1385
1462
  export function toHtmlWithOptions(source, options) {
@@ -1461,8 +1538,8 @@ export function toHtmlWithOptions(source, options) {
1461
1538
  * string with nowhere to put that, and so it rejects `renderers` rather than
1462
1539
  * dropping the failures.
1463
1540
  * @param {string} source
1464
- * @param {object | null} [options]
1465
- * @returns {StaticRenderResult}
1541
+ * @param {StaticRenderOptions | null} options
1542
+ * @returns {any}
1466
1543
  */
1467
1544
  export function toHtmlWithRenderers(source, options) {
1468
1545
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
@@ -1476,14 +1553,14 @@ export function toHtmlWithRenderers(source, options) {
1476
1553
 
1477
1554
  /**
1478
1555
  * @param {string} source
1479
- * @param {boolean | null} [strict]
1480
- * @param {number | null} [maximum]
1556
+ * @param {boolean | null} strict
1557
+ * @param {number | null} maximum
1481
1558
  * @returns {any}
1482
1559
  */
1483
1560
  export function toHtmlWithReport(source, strict, maximum) {
1484
1561
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1485
1562
  const len0 = WASM_VECTOR_LEN;
1486
- const ret = wasm.toHtmlWithReport(ptr0, len0, isLikeNone(strict) ? 0xFFFFFF : strict ? 1 : 0, isLikeNone(maximum) ? 0x100000001 : (maximum) >>> 0);
1563
+ const ret = wasm.toHtmlWithReport(ptr0, len0, isLikeNone(strict) ? 0 : addToExternrefTable0(strict), isLikeNone(maximum) ? 0 : addToExternrefTable0(maximum));
1487
1564
  if (ret[2]) {
1488
1565
  throw takeFromExternrefTable0(ret[1]);
1489
1566
  }
@@ -1571,7 +1648,7 @@ export function toMarkdown(source) {
1571
1648
  * `smartTypography` are inert there. They are accepted rather than refused
1572
1649
  * because one options object is meant to serve every target.
1573
1650
  * @param {string} source
1574
- * @param {object | null} [options]
1651
+ * @param {RenderOptions | null} options
1575
1652
  * @returns {string}
1576
1653
  */
1577
1654
  export function toMarkdownWithOptions(source, options) {
@@ -1597,14 +1674,14 @@ export function toMarkdownWithOptions(source, options) {
1597
1674
 
1598
1675
  /**
1599
1676
  * @param {string} source
1600
- * @param {boolean | null} [strict]
1601
- * @param {number | null} [maximum]
1677
+ * @param {boolean | null} strict
1678
+ * @param {number | null} maximum
1602
1679
  * @returns {any}
1603
1680
  */
1604
1681
  export function toMarkdownWithReport(source, strict, maximum) {
1605
1682
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1606
1683
  const len0 = WASM_VECTOR_LEN;
1607
- const ret = wasm.toMarkdownWithReport(ptr0, len0, isLikeNone(strict) ? 0xFFFFFF : strict ? 1 : 0, isLikeNone(maximum) ? 0x100000001 : (maximum) >>> 0);
1684
+ const ret = wasm.toMarkdownWithReport(ptr0, len0, isLikeNone(strict) ? 0 : addToExternrefTable0(strict), isLikeNone(maximum) ? 0 : addToExternrefTable0(maximum));
1608
1685
  if (ret[2]) {
1609
1686
  throw takeFromExternrefTable0(ret[1]);
1610
1687
  }
@@ -1634,7 +1711,7 @@ export function toPlainText(source) {
1634
1711
  * Render to plain text with an options object. See
1635
1712
  * [`to_markdown_with_options`].
1636
1713
  * @param {string} source
1637
- * @param {object | null} [options]
1714
+ * @param {RenderOptions | null} options
1638
1715
  * @returns {string}
1639
1716
  */
1640
1717
  export function toPlainTextWithOptions(source, options) {
@@ -1660,14 +1737,14 @@ export function toPlainTextWithOptions(source, options) {
1660
1737
 
1661
1738
  /**
1662
1739
  * @param {string} source
1663
- * @param {boolean | null} [strict]
1664
- * @param {number | null} [maximum]
1740
+ * @param {boolean | null} strict
1741
+ * @param {number | null} maximum
1665
1742
  * @returns {any}
1666
1743
  */
1667
1744
  export function toPlainTextWithReport(source, strict, maximum) {
1668
1745
  const ptr0 = passStringToWasm0(source, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
1669
1746
  const len0 = WASM_VECTOR_LEN;
1670
- const ret = wasm.toPlainTextWithReport(ptr0, len0, isLikeNone(strict) ? 0xFFFFFF : strict ? 1 : 0, isLikeNone(maximum) ? 0x100000001 : (maximum) >>> 0);
1747
+ const ret = wasm.toPlainTextWithReport(ptr0, len0, isLikeNone(strict) ? 0 : addToExternrefTable0(strict), isLikeNone(maximum) ? 0 : addToExternrefTable0(maximum));
1671
1748
  if (ret[2]) {
1672
1749
  throw takeFromExternrefTable0(ret[1]);
1673
1750
  }
@@ -1927,6 +2004,10 @@ export function __wbg_iterator_de403ef31815a3e6() {
1927
2004
  const ret = Symbol.iterator;
1928
2005
  return ret;
1929
2006
  }
2007
+ export function __wbg_keys_e611eeb7873788db(arg0) {
2008
+ const ret = Object.keys(arg0);
2009
+ return ret;
2010
+ }
1930
2011
  export function __wbg_length_0c32cb8543c8e4c8(arg0) {
1931
2012
  const ret = arg0.length;
1932
2013
  return ret;
@@ -2023,6 +2104,10 @@ export function __wbindgen_init_externref_table() {
2023
2104
  table.set(offset + 2, true);
2024
2105
  table.set(offset + 3, false);
2025
2106
  }
2107
+ const ParserSessionFinalization = (typeof FinalizationRegistry === 'undefined')
2108
+ ? { register: () => {}, unregister: () => {} }
2109
+ : new FinalizationRegistry(ptr => wasm.__wbg_parsersession_free(ptr >>> 0, 1));
2110
+
2026
2111
  function addToExternrefTable0(obj) {
2027
2112
  const idx = wasm.__externref_table_alloc();
2028
2113
  wasm.__wbindgen_externrefs.set(idx, obj);
Binary file
@@ -0,0 +1,90 @@
1
+ # Package compatibility
2
+
3
+ The release tarball contains these entry points:
4
+
5
+ | Import | Initialization | Intended host |
6
+ | --- | --- | --- |
7
+ | `@markup-carve/carve-wasm` | Automatic | Node or a browser bundler with native WASM imports |
8
+ | `@markup-carve/carve-wasm/node` | Automatic | Node ESM and CommonJS |
9
+ | `@markup-carve/carve-wasm/web` | `await init()` | Browser modules and module workers |
10
+ | `@markup-carve/carve-wasm/render` | `await init()` | Browser hosts that only render HTML |
11
+ | `@markup-carve/carve-wasm/offsets` | None | UTF-16, UTF-8 and codepoint boundary conversion |
12
+
13
+ The root export selects the Node entry through the `node` condition. Browser
14
+ bundlers select the bundler entry. A bundler that enables Node conditions for a
15
+ browser build should use `/web` explicitly.
16
+
17
+ ## Browser modules
18
+
19
+ Resolve the package through a bundler or serve the package directory, retaining
20
+ its hierarchy. The full entry points share the package-root WASM file. Initialization is asynchronous; rendering after it is
21
+ synchronous.
22
+
23
+ ```js
24
+ import init, { toHtmlWithOptions } from '@markup-carve/carve-wasm/web'
25
+ await init()
26
+ const html = toHtmlWithOptions(source, { rawHtml: false, profile: 'comment' })
27
+ ```
28
+
29
+ The rendering-only entry uses the same pattern. It has no parser JSON,
30
+ importers, reports, includes or editor APIs. Both entries export a default
31
+ initializer that can take the WASM module or URL when the default relative URL
32
+ is unsuitable. Serve `.wasm` as `application/wasm` for streaming compilation.
33
+
34
+ CI installs the npm tarball and tests Vite 8.3.2 production bundles in
35
+ Chromium, Firefox and WebKit through Playwright 1.61.1. Vite's build target is
36
+ `esnext` because these entries use top-level await. Earlier Vite releases may
37
+ need a WASM plugin for the root bundler entry; they are outside this tested
38
+ matrix. This matrix does not establish a minimum browser version.
39
+
40
+ ## Node and TypeScript
41
+
42
+ Node 22 is tested with both `import` and `require`. The Node build loads its
43
+ package-root WASM file synchronously, so copying only its JavaScript file fails.
44
+
45
+ TypeScript 5.9.3 is tested with `strict: true`, `skipLibCheck: false` and
46
+ `NodeNext` module resolution against the installed tarball. wasm-bindgen emits
47
+ `Symbol.dispose` declarations for `ParserSession`; include `ESNext.Disposable`
48
+ in `compilerOptions.lib` when the rest of the project targets ES2022. Explicit
49
+ `session.free()` remains supported.
50
+
51
+ ## Editors and workers
52
+
53
+ Edit ranges use UTF-8 byte offsets. Browser selection ranges use UTF-16 code
54
+ units; AST positions use Unicode codepoints. Convert against the source the
55
+ edit applies to, before sending it to a worker. See `/offsets` and the
56
+ [incremental API reference](reference.md#re-parsing-while-someone-types).
57
+
58
+ [carve-worker.mjs](../examples/carve-worker.mjs) initializes `/web` once and
59
+ retains one `ParserSession`. Messages carry `id` and `action`: `open` takes
60
+ `source`, `edit` takes `changes`, `render` takes `source`, and `close` frees the
61
+ session. Replies contain `value` or `error`. The consumer suite tests this
62
+ protocol in each browser. Create it with a bundler-resolved module worker:
63
+
64
+ ```js
65
+ const worker = new Worker(new URL('./carve-worker.mjs', import.meta.url), {
66
+ type: 'module',
67
+ })
68
+ ```
69
+
70
+ Copy the example into the application beside this call. Free retained sessions
71
+ when documents close, and terminate workers when the host closes them.
72
+
73
+ ## Fidelity and resource limits
74
+
75
+ HTML import projects through a DOM and cannot retain all Carve source
76
+ constructs. Its `roundtrip` mode is for trusted Carve-produced HTML. The corpus
77
+ ledger records output differences, while the stability gate requires a second
78
+ import to settle. Inspect conversion reports before accepting imported source.
79
+ Markdown reports assess supported constructs and report known losses. Incomplete
80
+ assessments, including nonliteral Djot and BBCode inputs, remain unverified.
81
+
82
+ ProseMirror reports dropped nodes, degraded nodes and canonical source changes.
83
+ A nonempty report requires a host decision before replacing the original
84
+ source. Preserve the original source when an editor cannot represent it.
85
+
86
+ The engine limits nesting to 200 levels. CI checks that depth in browsers and
87
+ measures the Node stack needed by serialization separately. Larger documents
88
+ still consume synchronous CPU time; move work into a worker when it would block
89
+ an editor. `scripts/benchmark.mjs` measures the shipped package, but its elapsed
90
+ times are machine-dependent and are not latency guarantees.