docxodus 9.9.0 → 10.0.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.
Files changed (111) hide show
  1. package/README.md +178 -0
  2. package/dist/canonical.d.ts +7 -0
  3. package/dist/canonical.d.ts.map +1 -0
  4. package/dist/canonical.js +56 -0
  5. package/dist/canonical.js.map +1 -0
  6. package/dist/docxodus.worker.d.ts +2 -1
  7. package/dist/docxodus.worker.d.ts.map +1 -1
  8. package/dist/docxodus.worker.js +244 -3
  9. package/dist/docxodus.worker.js.map +1 -1
  10. package/dist/editor.bundle.js +1914 -419
  11. package/dist/editor.d.ts +22 -2
  12. package/dist/editor.d.ts.map +1 -1
  13. package/dist/editor.js +94 -9
  14. package/dist/editor.js.map +1 -1
  15. package/dist/embed.bundle.js +2803 -469
  16. package/dist/embed.iife.js +2801 -467
  17. package/dist/export-assets.json +283 -0
  18. package/dist/export-browser.bundle.js +7990 -0
  19. package/dist/export-browser.d.ts +266 -0
  20. package/dist/export-browser.d.ts.map +1 -0
  21. package/dist/export-browser.js +2915 -0
  22. package/dist/export-browser.js.map +1 -0
  23. package/dist/export-resource-limits-v1.json +57 -0
  24. package/dist/font-contract.d.ts +150 -0
  25. package/dist/font-contract.d.ts.map +1 -0
  26. package/dist/font-contract.js +59 -0
  27. package/dist/font-contract.js.map +1 -0
  28. package/dist/font-runtime.d.ts +33 -0
  29. package/dist/font-runtime.d.ts.map +1 -0
  30. package/dist/font-runtime.js +1100 -0
  31. package/dist/font-runtime.js.map +1 -0
  32. package/dist/index.d.ts +62 -5
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +130 -16
  35. package/dist/index.js.map +1 -1
  36. package/dist/page-geometry.d.ts +8 -1
  37. package/dist/page-geometry.d.ts.map +1 -1
  38. package/dist/page-geometry.js +37 -11
  39. package/dist/page-geometry.js.map +1 -1
  40. package/dist/pagination.bundle.js +1827 -410
  41. package/dist/pagination.d.ts +249 -15
  42. package/dist/pagination.d.ts.map +1 -1
  43. package/dist/pagination.js +1942 -453
  44. package/dist/pagination.js.map +1 -1
  45. package/dist/react.d.ts +11 -4
  46. package/dist/react.d.ts.map +1 -1
  47. package/dist/react.js +38 -5
  48. package/dist/react.js.map +1 -1
  49. package/dist/render-report-v2.schema.json +1581 -0
  50. package/dist/ribbon-chrome.d.ts +1 -1
  51. package/dist/ribbon-chrome.d.ts.map +1 -1
  52. package/dist/ribbon-chrome.js +45 -0
  53. package/dist/ribbon-chrome.js.map +1 -1
  54. package/dist/ribbon.js +70 -0
  55. package/dist/ribbon.js.map +1 -1
  56. package/dist/session.bundle.js +695 -29
  57. package/dist/session.d.ts +134 -19
  58. package/dist/session.d.ts.map +1 -1
  59. package/dist/session.js +562 -25
  60. package/dist/session.js.map +1 -1
  61. package/dist/types.d.ts +1207 -16
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js.map +1 -1
  64. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm +0 -0
  65. package/dist/wasm/_framework/DocumentFormat.OpenXml.Framework.wasm.br +0 -0
  66. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm +0 -0
  67. package/dist/wasm/_framework/DocumentFormat.OpenXml.wasm.br +0 -0
  68. package/dist/wasm/_framework/Docxodus.wasm +0 -0
  69. package/dist/wasm/_framework/Docxodus.wasm.br +0 -0
  70. package/dist/wasm/_framework/DocxodusWasm.wasm +0 -0
  71. package/dist/wasm/_framework/DocxodusWasm.wasm.br +0 -0
  72. package/dist/wasm/_framework/System.Collections.Concurrent.wasm +0 -0
  73. package/dist/wasm/_framework/System.Collections.Concurrent.wasm.br +0 -0
  74. package/dist/wasm/_framework/System.Collections.Immutable.wasm +0 -0
  75. package/dist/wasm/_framework/System.Collections.Immutable.wasm.br +0 -0
  76. package/dist/wasm/_framework/System.Collections.wasm +0 -0
  77. package/dist/wasm/_framework/System.Collections.wasm.br +0 -0
  78. package/dist/wasm/_framework/System.IO.Compression.wasm +0 -0
  79. package/dist/wasm/_framework/System.IO.Compression.wasm.br +0 -0
  80. package/dist/wasm/_framework/System.IO.Packaging.wasm +0 -0
  81. package/dist/wasm/_framework/System.IO.Packaging.wasm.br +0 -0
  82. package/dist/wasm/_framework/System.Linq.wasm +0 -0
  83. package/dist/wasm/_framework/System.Linq.wasm.br +0 -0
  84. package/dist/wasm/_framework/System.Private.CoreLib.wasm +0 -0
  85. package/dist/wasm/_framework/System.Private.CoreLib.wasm.br +0 -0
  86. package/dist/wasm/_framework/System.Private.Uri.wasm +0 -0
  87. package/dist/wasm/_framework/System.Private.Uri.wasm.br +0 -0
  88. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm +0 -0
  89. package/dist/wasm/_framework/System.Private.Xml.Linq.wasm.br +0 -0
  90. package/dist/wasm/_framework/System.Private.Xml.wasm +0 -0
  91. package/dist/wasm/_framework/System.Private.Xml.wasm.br +0 -0
  92. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  93. package/dist/wasm/_framework/System.Runtime.InteropServices.JavaScript.wasm.br +0 -0
  94. package/dist/wasm/_framework/System.Runtime.wasm +0 -0
  95. package/dist/wasm/_framework/System.Runtime.wasm.br +0 -0
  96. package/dist/wasm/_framework/System.Security.Cryptography.wasm +0 -0
  97. package/dist/wasm/_framework/System.Security.Cryptography.wasm.br +0 -0
  98. package/dist/wasm/_framework/System.Text.Json.wasm +0 -0
  99. package/dist/wasm/_framework/System.Text.Json.wasm.br +0 -0
  100. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm +0 -0
  101. package/dist/wasm/_framework/System.Text.RegularExpressions.wasm.br +0 -0
  102. package/dist/wasm/_framework/dotnet.boot.js +21 -21
  103. package/dist/wasm/_framework/dotnet.boot.js.br +0 -0
  104. package/dist/wasm/_framework/dotnet.native.wasm +0 -0
  105. package/dist/wasm/_framework/dotnet.native.wasm.br +0 -0
  106. package/dist/worker-proxy.bundle.js +196 -29
  107. package/dist/worker-proxy.d.ts +36 -3
  108. package/dist/worker-proxy.d.ts.map +1 -1
  109. package/dist/worker-proxy.js +186 -33
  110. package/dist/worker-proxy.js.map +1 -1
  111. package/package.json +22 -6
package/README.md CHANGED
@@ -23,6 +23,13 @@ linked as one operation instead of reported as an unrelated delete and insert.*
23
23
  `getRevisions()` gives you the same changes as structured data — typed, with move pairing and stable
24
24
  `kind:scope:unid` anchors — so you can drive a review UI without parsing OOXML.
25
25
 
26
+ For audit and verification workflows, `docxDiffGetSemanticChanges(left, right)` returns the stable
27
+ `docxodus.semantic-changes` v1 object. It distinguishes content, formatting, structure, table,
28
+ story/review, relationship/media, and opaque-package families, with typed before/after values and
29
+ deterministic ids. An open session exposes the same opening-to-current comparison through
30
+ `session.getSemanticChanges()`; it requires the default `captureInitialProjection: true` setting.
31
+ See the [semantic diff contract](../docs/architecture/semantic_diff.md).
32
+
26
33
  ## Render a document faithfully
27
34
 
28
35
  `convertDocxToHtml()` keeps what naive converters drop: justification, style inheritance, legal
@@ -33,6 +40,23 @@ numbering, tables, images, comments, headers and footers, and real footnotes wit
33
40
  Paginated mode flows content into real page boxes with per-page numbers and page-anchored footnotes —
34
41
  a print-accurate preview in the browser.
35
42
 
43
+ Supply an exact layout token to materialize portable page citations from that layout:
44
+
45
+ ```ts
46
+ const result = paginateHtml(html, viewer, {
47
+ layoutToken: { documentVersion: session.getVersion(), rendererFingerprint: 'chromium-layout-v1' },
48
+ });
49
+ session.registerPageMap(result.pageMap!, 'chromium-layout-v1');
50
+ const citation = session.getPageCitation(anchorId, {
51
+ documentVersion: session.getVersion(),
52
+ rendererFingerprint: 'chromium-layout-v1',
53
+ });
54
+ navigateToPageCitation(viewer, citation);
55
+ ```
56
+
57
+ Continuous renderers return explicit unavailability; Docxodus never estimates a citation page.
58
+ See the [PageMap contract](../docs/architecture/page_map.md).
59
+
36
60
  ## Edit it in place
37
61
 
38
62
  `DocxEditor` is a framework-agnostic block editor over the live document. Edits go through a
@@ -82,8 +106,80 @@ Bundler users get the same API from `docxodus/embed`; classic-script pages can l
82
106
  `convertWmlToMarkdown()` renders the document as markdown where **every block carries a stable id**,
83
107
  so an agent can point at a clause and edit it — and `openDocxSession()` writes back to that same id.
84
108
 
109
+ Multi-step plans are atomic by default. Each callback may call any synchronous
110
+ session mutation; success is one version/undo unit, while a failure or throw
111
+ restores the complete package and history checkpoint:
112
+
113
+ ```ts
114
+ const result = session.executeBatch([
115
+ { tool: 'docx_edit', action: 'replace_text',
116
+ mutation: () => session.replaceText(firstAnchor, 'Replacement text') },
117
+ { tool: 'docx_create', action: 'set_header_text',
118
+ mutation: () => session.setHeaderText(firstAnchor, 'default', 'Confidential') },
119
+ ]);
120
+
121
+ if (!result.success) console.error(result.failure);
122
+ ```
123
+
124
+ Pass `'best_effort'` explicitly only when partial successes should be retained.
125
+
126
+ `previewBatch` answers "what would this do?" without touching the live session. It runs the
127
+ same steps against a complete isolated clone — each callback is handed the shadow session to
128
+ mutate — and returns the same receipt plus optional predicted HTML:
129
+
130
+ ```ts
131
+ const preview = session.previewBatch([
132
+ { tool: 'docx_edit', action: 'replace_text',
133
+ mutation: shadow => shadow.replaceText(firstAnchor, 'Proposed replacement') },
134
+ ], 'atomic', { html: 'full' });
135
+
136
+ console.log(preview.html, preview.revisionChanges.added, preview.warnings);
137
+ ```
138
+
139
+ The live document's bytes, version and undo/redo history are unchanged either way. Preview
140
+ HTML shows tracked changes, comments, annotations, notes and headers/footers — the document
141
+ the batch would produce, matching what the Python and MCP clients render for the same batch.
142
+ `packageHash` is `null` when it could not be computed, so never assert replay equality
143
+ without checking for it.
144
+
85
145
  ![Markdown projection beside the rendered document](https://raw.githubusercontent.com/JSv4/Docxodus/main/docs/images/projection.png)
86
146
 
147
+ Native links and bookmarks use the same stable anchors and exact character spans:
148
+
149
+ ```ts
150
+ const session = openDocxSession(docxBytes, { persistAnchorIds: true });
151
+ const paragraph = Object.keys(session.project().anchorIndex)
152
+ .find(id => id.startsWith('p:body:'))!;
153
+ session.addBookmark('Definitions', {
154
+ startAnchorId: paragraph, startOffset: 0,
155
+ endAnchorId: paragraph, endOffset: 11,
156
+ });
157
+ session.addHyperlink(paragraph, { start: 0, length: 11 }, 'internal', 'Definitions');
158
+ const links = session.listHyperlinks(); // ids feed updateHyperlink/removeHyperlink
159
+ const bookmarks = session.listBookmarks(); // exact endpoint range + per-paragraph segments
160
+ ```
161
+
162
+ External links own their relationship in the actual body/header/footer/footnote/endnote part;
163
+ internal links are relationship-free bookmark targets. Bookmark rename retargets inbound links
164
+ atomically, and unsafe removal or cross-part ranges return typed `EditResult` errors.
165
+
166
+ Native images are occurrence-addressed too, with bytes kept explicit at the API boundary:
167
+
168
+ ```ts
169
+ const capabilities = session.getImageCapabilities();
170
+ const inserted = session.insertImage(paragraph, 0, pngBytes, {
171
+ widthPoints: 144,
172
+ altText: 'Revenue by quarter',
173
+ });
174
+ const image = session.listImages().find(value => value.id === inserted.imageId)!;
175
+ session.setImageDimensions(image.id, { widthPoints: 108 });
176
+ ```
177
+
178
+ PNG/JPEG/GIF/BMP/TIFF are writable. Existing WebP, external links, legacy VML, and unsupported
179
+ DrawingML remain enumerable with `canMutate: false`. Dimensions use points; floating offsets use
180
+ exact EMUs, and omitted insert dimensions use 96 DPI. The browser API accepts `Uint8Array` and
181
+ does not fetch image URLs or read paths.
182
+
87
183
  ---
88
184
 
89
185
  ## Everything else
@@ -179,6 +275,48 @@ function DocumentComparer() {
179
275
 
180
276
  ## API Reference
181
277
 
278
+ ### Package verification
279
+
280
+ `generatePackageManifest(document)` inspects supplied bytes without opening an editable package;
281
+ `session.getPackageManifest()` describes the current logical session checkpoint, including unsaved
282
+ edits, without advancing its version. Both return the same schema-v1 `PackageManifest` with exact
283
+ package, ordered OPC-content, and normalized-semantic SHA-256 identities plus entries,
284
+ relationships, facts, and structured findings. Worker users can call
285
+ `worker.generatePackageManifest(document)` or `await workerSession.getPackageManifest()`. See
286
+ [`package_manifests.md`](../docs/architecture/package_manifests.md) for the normalization boundary.
287
+
288
+ ### Stateful inspection and editing
289
+
290
+ `openDocxSession(bytes)` exposes the live document rather than a one-shot conversion. Inspect the
291
+ source before writing it: `listStyles()` returns the document's actual paragraph/character/table
292
+ styles, `getFormatting(anchorId)` keeps `directParagraph` separate from `effectiveParagraph`, and
293
+ `listInlineSpans(anchorId)` returns `anchorId` + `span` pairs accepted unchanged by `applyFormat`.
294
+
295
+ ```typescript
296
+ const session = openDocxSession(bytes);
297
+ try {
298
+ const anchorId = Object.keys(session.project().anchorIndex)[0];
299
+ const style = session.listStyles().find(s => s.name === "Strong Custom");
300
+ const run = session.listInlineSpans(anchorId).find(s => s.text === "Defined Term");
301
+ if (style && run) {
302
+ session.applyFormat(run.anchorId, run.span, { runStyle: style.id });
303
+ }
304
+ } finally {
305
+ session.close();
306
+ }
307
+ ```
308
+
309
+ An omitted property in a `direct` record means "not written at this layer"; it must not be treated
310
+ as false or zero. The matching `effective` record resolves document defaults and the full style
311
+ chain. `getListMembership` and `getSectionInfo` likewise return their query `anchorId` so callers
312
+ do not have to translate between inspection and mutation coordinate systems.
313
+
314
+ `effective` is deliberately a shorter cascade than the renderer's: it excludes the numbering
315
+ level's own paragraph properties and the table style's conditional formatting, so a list item
316
+ indented only by its numbering definition reports `leftIndentTwips: 0` and a run bolded by a
317
+ `firstRow` table style reports `bold: false`. Use `getListMembership` for the real numbering
318
+ indentation. See `docs/architecture/docx_mutation_api.md` for the exact layer list.
319
+
182
320
  ### Core Functions
183
321
 
184
322
  #### `initialize(basePath?: string): Promise<void>`
@@ -500,6 +638,46 @@ first `<bundle dir>/wasm/`, then `<bundle dir>/` as a fallback. On a CDN that re
500
638
  - First load fetches ~18 MB uncompressed across about 49 requests; subsequent loads hit the
501
639
  browser cache.
502
640
 
641
+ ## Standalone paginated HTML
642
+
643
+ `docxodus/export-browser` turns a DOCX into a complete offline HTML document whose body contains
644
+ the finalized fixed page boxes—not the hidden measurement tree. It returns the HTML together with
645
+ the exact PageMap, renderer fingerprint, warnings, and a schema-v2 render report from that same
646
+ sanitized tree.
647
+
648
+ ```ts
649
+ import { convertDocxToPaginatedHtml } from 'docxodus/export-browser';
650
+
651
+ const source = new Uint8Array(await file.arrayBuffer());
652
+ const result = await convertDocxToPaginatedHtml(source, {
653
+ reviewProfile: 'final',
654
+ commentProfile: 'endnotes',
655
+ documentVersion: 12,
656
+ expectedSourceDigest: verifiedSha256,
657
+ });
658
+
659
+ download(new Blob([result.html], { type: 'text/html' }));
660
+ console.log(result.pageCount, result.pageMap, result.renderReport);
661
+ ```
662
+
663
+ The exporter copies caller bytes before its first asynchronous boundary, verifies them with the
664
+ package manifest API, performs layout in an attached script-disabled frame, embeds or removes every
665
+ automatic resource, and reopens the serialized result to verify page count and physical geometry.
666
+ External HTTPS/mail/tel links remain user-activated links and are inventoried in the report; the
667
+ exporter never follows them. `unsupportedContent: 'strict'` rejects visible placeholders or omitted
668
+ resources instead of returning a nominally complete artifact.
669
+
670
+ All three revision profiles (`final`, `original`, `markup`) are supported; `final` and `original`
671
+ derive their projection out-of-place and never mutate the caller's bytes. `strictFonts: true`
672
+ turns unresolved or unverified font families into failures; supplying a `fontResolver` (in Node,
673
+ `fontDirectories` on `@docxodus/export`) is what makes families verifiable. A font the browser
674
+ supplied on its own is labelled `browserObserved`; it is not presented as a verified host-font
675
+ environment.
676
+
677
+ By default the worker loads from `dist/wasm/` beside the package entry point. A deployment that
678
+ hosts those files elsewhere may pass `wasmBasePath`. `docxodus/export-assets.json` is the closed,
679
+ SHA-256-addressed runtime asset graph, and `docxodus/render-report.schema.json` is the report schema.
680
+
503
681
  ## Hosting WASM Files
504
682
 
505
683
  The WASM files need to be served from your web server. After building:
@@ -0,0 +1,7 @@
1
+ /** Shared canonical-JSON serialization, used by both the export orchestrator and the browser
2
+ * font runtime wherever a value needs a stable digest or a deterministic wire form. */
3
+ export declare function isWellFormedUnicode(value: string): boolean;
4
+ export declare function assertWellFormedUnicode(value: string): void;
5
+ /** Serialize report/schema values with recursively sorted object keys and no insignificant space. */
6
+ export declare function canonicalJson(value: unknown): string;
7
+ //# sourceMappingURL=canonical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.d.ts","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;uFACuF;AAEvF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAW1D;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI3D;AA6BD,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEpD"}
@@ -0,0 +1,56 @@
1
+ /** Shared canonical-JSON serialization, used by both the export orchestrator and the browser
2
+ * font runtime wherever a value needs a stable digest or a deterministic wire form. */
3
+ export function isWellFormedUnicode(value) {
4
+ for (let index = 0; index < value.length; index++) {
5
+ const unit = value.charCodeAt(index);
6
+ if (unit >= 0xd800 && unit <= 0xdbff) {
7
+ const next = value.charCodeAt(++index);
8
+ if (!(next >= 0xdc00 && next <= 0xdfff))
9
+ return false;
10
+ }
11
+ else if (unit >= 0xdc00 && unit <= 0xdfff) {
12
+ return false;
13
+ }
14
+ }
15
+ return true;
16
+ }
17
+ export function assertWellFormedUnicode(value) {
18
+ if (!isWellFormedUnicode(value)) {
19
+ throw new TypeError("Canonical JSON does not support unpaired UTF-16 surrogates");
20
+ }
21
+ }
22
+ function canonicalValue(value) {
23
+ if (value === null || typeof value === "boolean")
24
+ return value;
25
+ if (typeof value === "string") {
26
+ assertWellFormedUnicode(value);
27
+ return value;
28
+ }
29
+ if (typeof value === "number") {
30
+ if (!Number.isFinite(value))
31
+ throw new TypeError("Canonical JSON does not support non-finite numbers");
32
+ return Object.is(value, -0) ? 0 : value;
33
+ }
34
+ if (Array.isArray(value))
35
+ return value.map(canonicalValue);
36
+ if (typeof value === "object") {
37
+ const prototype = Object.getPrototypeOf(value);
38
+ if (prototype !== Object.prototype && prototype !== null) {
39
+ throw new TypeError("Canonical JSON supports only plain objects");
40
+ }
41
+ const result = {};
42
+ for (const key of Object.keys(value).sort()) {
43
+ assertWellFormedUnicode(key);
44
+ const member = value[key];
45
+ if (member !== undefined)
46
+ result[key] = canonicalValue(member);
47
+ }
48
+ return result;
49
+ }
50
+ throw new TypeError(`Canonical JSON does not support ${typeof value}`);
51
+ }
52
+ /** Serialize report/schema values with recursively sorted object keys and no insignificant space. */
53
+ export function canonicalJson(value) {
54
+ return JSON.stringify(canonicalValue(value));
55
+ }
56
+ //# sourceMappingURL=canonical.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical.js","sourceRoot":"","sources":["../src/canonical.ts"],"names":[],"mappings":"AAAA;uFACuF;AAEvF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACrC,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YACrC,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;YACvC,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC;gBAAE,OAAO,KAAK,CAAC;QACxD,CAAC;aAAM,IAAI,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,4DAA4D,CAAC,CAAC;IACpF,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;QACvG,OAAO,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC1C,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACzD,MAAM,IAAI,SAAS,CAAC,4CAA4C,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAgC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACvE,uBAAuB,CAAC,GAAG,CAAC,CAAC;YAC7B,MAAM,MAAM,GAAI,KAAiC,CAAC,GAAG,CAAC,CAAC;YACvD,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,MAAM,IAAI,SAAS,CAAC,mCAAmC,OAAO,KAAK,EAAE,CAAC,CAAC;AACzE,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/C,CAAC"}
@@ -5,7 +5,8 @@
5
5
  * thread free for UI updates and user interactions.
6
6
  *
7
7
  * Communication is via postMessage with structured request/response types.
8
- * Document bytes are transferred (not copied) for efficiency.
8
+ * Caller-owned Uint8Array inputs are first cloned to preserve their exact view and avoid
9
+ * detachment; that private clone is then transferred to the worker.
9
10
  */
10
11
  export {};
11
12
  //# sourceMappingURL=docxodus.worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"docxodus.worker.d.ts","sourceRoot":"","sources":["../src/docxodus.worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
1
+ {"version":3,"file":"docxodus.worker.d.ts","sourceRoot":"","sources":["../src/docxodus.worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -39,7 +39,89 @@ function ensureInitialized() {
39
39
  return wasmExports;
40
40
  }
41
41
  function isErrorResponse(result) {
42
- return result.startsWith("{") && result.includes('"Error"');
42
+ try {
43
+ const parsed = JSON.parse(result);
44
+ return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) && "Error" in parsed;
45
+ } catch {
46
+ return false;
47
+ }
48
+ }
49
+ function handleGeneratePackageManifest(request) {
50
+ try {
51
+ const json = request.limits ? ensureInitialized().DocumentConverter.GeneratePackageManifestWithOptions(
52
+ request.documentBytes,
53
+ request.limits.opcEntries,
54
+ request.limits.expandedOpcBytes,
55
+ request.limits.xmlPartBytes,
56
+ request.limits.opcCompressionRatio,
57
+ request.limits.opcUriCharacters
58
+ ) : ensureInitialized().DocumentConverter.GeneratePackageManifest(
59
+ request.documentBytes
60
+ );
61
+ const representation = request.representation ?? "both";
62
+ return {
63
+ manifest: representation === "json" ? void 0 : JSON.parse(json),
64
+ manifestJson: representation === "object" ? void 0 : json
65
+ };
66
+ } catch (error) {
67
+ return { error: String(error) };
68
+ }
69
+ }
70
+ function handleProjectReviewProfile(request) {
71
+ try {
72
+ const result = request.profile === "final" ? ensureInitialized().DocxDiffBridge.AcceptRevisions(request.documentBytes) : ensureInitialized().DocxDiffBridge.RejectRevisions(request.documentBytes);
73
+ if (!result || result.byteLength === 0) {
74
+ return { error: `Failed to derive the ${request.profile} review profile` };
75
+ }
76
+ if (request.maximumOutputBytes !== void 0 && result.byteLength > request.maximumOutputBytes) {
77
+ return {
78
+ error: `Derived ${request.profile} package exceeds compressedDocxBytes (${result.byteLength} > ${request.maximumOutputBytes})`,
79
+ errorCode: "resource_limit"
80
+ };
81
+ }
82
+ return { documentBytes: result };
83
+ } catch (error) {
84
+ return { error: String(error) };
85
+ }
86
+ }
87
+ function handleProveRedlineReversibility(request) {
88
+ try {
89
+ const json = ensureInitialized().DocumentConverter.ProveRedlineReversibility(
90
+ request.baselineBytes,
91
+ request.intendedFinalBytes,
92
+ request.redlineBytes
93
+ );
94
+ return { proof: JSON.parse(json) };
95
+ } catch (error) {
96
+ return { error: String(error) };
97
+ }
98
+ }
99
+ function handleVerifyDeliverable(request) {
100
+ try {
101
+ const converter = ensureInitialized().DocumentConverter;
102
+ const json = request.baselineBytes === void 0 ? converter.VerifyDeliverable(request.documentBytes) : converter.VerifyDeliverableWithBaseline(
103
+ request.baselineBytes,
104
+ request.documentBytes
105
+ );
106
+ return {
107
+ verification: JSON.parse(json)
108
+ };
109
+ } catch (error) {
110
+ return { error: String(error) };
111
+ }
112
+ }
113
+ function handleGetSemanticChanges(request) {
114
+ try {
115
+ const json = ensureInitialized().DocxDiffBridge.GetSemanticChangesJson(
116
+ request.leftBytes,
117
+ request.rightBytes,
118
+ request.settings ? JSON.stringify(request.settings) : ""
119
+ );
120
+ if (isErrorResponse(json)) return { error: parseError(json).error };
121
+ return { semanticChanges: JSON.parse(json) };
122
+ } catch (error) {
123
+ return { error: String(error) };
124
+ }
43
125
  }
44
126
  function parseError(result) {
45
127
  try {
@@ -110,6 +192,24 @@ function handleConvert(request) {
110
192
  if (isErrorResponse(result)) {
111
193
  return parseError(result);
112
194
  }
195
+ if (request.maximumOutputBytes !== void 0) {
196
+ let byteLength = 0;
197
+ for (let index = 0; index < result.length; index++) {
198
+ const unit = result.charCodeAt(index);
199
+ if (unit < 128) byteLength++;
200
+ else if (unit < 2048) byteLength += 2;
201
+ else if (unit >= 55296 && unit <= 56319 && result.charCodeAt(index + 1) >= 56320 && result.charCodeAt(index + 1) <= 57343) {
202
+ byteLength += 4;
203
+ index++;
204
+ } else byteLength += 3;
205
+ if (byteLength > request.maximumOutputBytes) {
206
+ return {
207
+ error: `Converted HTML exceeds htmlOutputBytes (${byteLength} > ${request.maximumOutputBytes})`,
208
+ errorCode: "resource_limit"
209
+ };
210
+ }
211
+ }
212
+ }
113
213
  return { html: result };
114
214
  } catch (error) {
115
215
  return { error: String(error) };
@@ -249,7 +349,8 @@ function handleGetDocumentMetadata(request) {
249
349
  hasEndnotes: parsed.HasEndnotes ?? parsed.hasEndnotes,
250
350
  hasTrackedChanges: parsed.HasTrackedChanges ?? parsed.hasTrackedChanges,
251
351
  hasComments: parsed.HasComments ?? parsed.hasComments,
252
- estimatedPageCount: parsed.EstimatedPageCount ?? parsed.estimatedPageCount
352
+ estimatedPageCount: parsed.EstimatedPageCount ?? parsed.estimatedPageCount,
353
+ estimatedPageCountSource: parsed.EstimatedPageCountSource ?? parsed.estimatedPageCountSource ?? "heuristic"
253
354
  };
254
355
  return { metadata };
255
356
  } catch (error) {
@@ -269,6 +370,35 @@ function handleSessionOpen(request) {
269
370
  return { error: String(error) };
270
371
  }
271
372
  }
373
+ function handleSessionGetPackageManifest(request) {
374
+ try {
375
+ const json = ensureInitialized().DocxSessionBridge.GetPackageManifest(request.handle);
376
+ return { manifest: JSON.parse(json) };
377
+ } catch (error) {
378
+ return { error: String(error) };
379
+ }
380
+ }
381
+ function handleSessionGetSemanticChanges(request) {
382
+ try {
383
+ const json = ensureInitialized().DocxSessionBridge.GetSemanticChanges(request.handle);
384
+ if (isErrorResponse(json)) return { error: parseError(json).error };
385
+ return { semanticChanges: JSON.parse(json) };
386
+ } catch (error) {
387
+ return { error: String(error) };
388
+ }
389
+ }
390
+ function handleSessionVerifyDeliverable(request) {
391
+ try {
392
+ const json = ensureInitialized().DocxSessionBridge.VerifyDeliverable(
393
+ request.handle
394
+ );
395
+ return {
396
+ verification: JSON.parse(json)
397
+ };
398
+ } catch (error) {
399
+ return { error: String(error) };
400
+ }
401
+ }
272
402
  function handleSessionClose(request) {
273
403
  const exports = ensureInitialized();
274
404
  try {
@@ -388,6 +518,64 @@ self.addEventListener("message", async (event) => {
388
518
  }
389
519
  break;
390
520
  }
521
+ case "generatePackageManifest": {
522
+ const result = handleGeneratePackageManifest(
523
+ request
524
+ );
525
+ response = {
526
+ id: request.id,
527
+ type: "generatePackageManifest",
528
+ success: !result.error,
529
+ manifest: result.manifest,
530
+ manifestJson: result.manifestJson,
531
+ error: result.error
532
+ };
533
+ break;
534
+ }
535
+ case "verifyDeliverable": {
536
+ const result = handleVerifyDeliverable(
537
+ request
538
+ );
539
+ response = {
540
+ id: request.id,
541
+ type: "verifyDeliverable",
542
+ success: !result.error,
543
+ verification: result.verification,
544
+ error: result.error
545
+ };
546
+ break;
547
+ }
548
+ case "proveRedlineReversibility": {
549
+ const result = handleProveRedlineReversibility(
550
+ request
551
+ );
552
+ response = {
553
+ id: request.id,
554
+ type: "proveRedlineReversibility",
555
+ success: !result.error,
556
+ proof: result.proof,
557
+ error: result.error
558
+ };
559
+ break;
560
+ }
561
+ case "projectReviewProfile": {
562
+ const result = handleProjectReviewProfile(
563
+ request
564
+ );
565
+ response = {
566
+ id: request.id,
567
+ type: "projectReviewProfile",
568
+ success: !result.error,
569
+ documentBytes: result.documentBytes,
570
+ error: result.error,
571
+ errorCode: result.errorCode
572
+ };
573
+ if (result.documentBytes) {
574
+ self.postMessage(response, { transfer: [result.documentBytes.buffer] });
575
+ return;
576
+ }
577
+ break;
578
+ }
391
579
  case "convertDocxToHtml": {
392
580
  const convertRequest = request;
393
581
  const result = handleConvert(convertRequest);
@@ -396,7 +584,8 @@ self.addEventListener("message", async (event) => {
396
584
  type: "convertDocxToHtml",
397
585
  success: !result.error,
398
586
  html: result.html,
399
- error: result.error
587
+ error: result.error,
588
+ errorCode: result.errorCode
400
589
  };
401
590
  break;
402
591
  }
@@ -428,6 +617,19 @@ self.addEventListener("message", async (event) => {
428
617
  };
429
618
  break;
430
619
  }
620
+ case "getSemanticChanges": {
621
+ const result = handleGetSemanticChanges(
622
+ request
623
+ );
624
+ response = {
625
+ id: request.id,
626
+ type: "getSemanticChanges",
627
+ success: !result.error,
628
+ semanticChanges: result.semanticChanges,
629
+ error: result.error
630
+ };
631
+ break;
632
+ }
431
633
  case "getRevisions": {
432
634
  const getRevisionsRequest = request;
433
635
  const result = handleGetRevisions(getRevisionsRequest);
@@ -485,6 +687,45 @@ self.addEventListener("message", async (event) => {
485
687
  };
486
688
  break;
487
689
  }
690
+ case "sessionGetPackageManifest": {
691
+ const result = handleSessionGetPackageManifest(
692
+ request
693
+ );
694
+ response = {
695
+ id: request.id,
696
+ type: "sessionGetPackageManifest",
697
+ success: !result.error,
698
+ manifest: result.manifest,
699
+ error: result.error
700
+ };
701
+ break;
702
+ }
703
+ case "sessionGetSemanticChanges": {
704
+ const result = handleSessionGetSemanticChanges(
705
+ request
706
+ );
707
+ response = {
708
+ id: request.id,
709
+ type: "sessionGetSemanticChanges",
710
+ success: !result.error,
711
+ semanticChanges: result.semanticChanges,
712
+ error: result.error
713
+ };
714
+ break;
715
+ }
716
+ case "sessionVerifyDeliverable": {
717
+ const result = handleSessionVerifyDeliverable(
718
+ request
719
+ );
720
+ response = {
721
+ id: request.id,
722
+ type: "sessionVerifyDeliverable",
723
+ success: !result.error,
724
+ verification: result.verification,
725
+ error: result.error
726
+ };
727
+ break;
728
+ }
488
729
  case "sessionClose": {
489
730
  const sessionCloseRequest = request;
490
731
  const result = handleSessionClose(sessionCloseRequest);