@beyondwork/docx-react-component 1.0.29 → 1.0.30

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 (381) hide show
  1. package/package.json +65 -96
  2. package/src/README.md +85 -0
  3. package/src/api/README.md +26 -0
  4. package/src/api/public-types.ts +1952 -0
  5. package/src/api/session-state.ts +62 -0
  6. package/src/compare/diff-engine.ts +623 -0
  7. package/src/compare/export-redlines.ts +280 -0
  8. package/src/compare/index.ts +25 -0
  9. package/src/compare/snapshot.ts +97 -0
  10. package/src/component-inventory.md +99 -0
  11. package/src/core/README.md +10 -0
  12. package/src/core/commands/README.md +3 -0
  13. package/{dist/chunk-TJBP2K4T.js → src/core/commands/formatting-commands.ts} +536 -196
  14. package/src/core/commands/image-commands.ts +373 -0
  15. package/src/core/commands/index.ts +1879 -0
  16. package/src/core/commands/list-commands.ts +565 -0
  17. package/src/core/commands/paragraph-layout-commands.ts +339 -0
  18. package/src/core/commands/review-commands.ts +108 -0
  19. package/{dist/core/commands/section-layout-commands.cjs → src/core/commands/section-layout-commands.ts} +340 -137
  20. package/src/core/commands/structural-helpers.ts +309 -0
  21. package/{dist/core/commands/style-commands.cjs → src/core/commands/style-commands.ts} +113 -65
  22. package/src/core/commands/table-structure-commands.ts +854 -0
  23. package/{dist/chunk-UZXBISGO.js → src/core/commands/text-commands.ts} +142 -86
  24. package/src/core/schema/README.md +3 -0
  25. package/src/core/schema/text-schema.ts +516 -0
  26. package/src/core/search/search-text.ts +357 -0
  27. package/src/core/selection/README.md +3 -0
  28. package/src/core/selection/mapping.ts +289 -0
  29. package/src/core/selection/review-anchors.ts +183 -0
  30. package/src/core/state/README.md +3 -0
  31. package/src/core/state/editor-state.ts +892 -0
  32. package/src/core/state/text-transaction.ts +869 -0
  33. package/src/formats/xlsx/io/parse-shared-strings.ts +41 -0
  34. package/src/formats/xlsx/io/parse-sheet.ts +459 -0
  35. package/src/formats/xlsx/io/parse-styles.ts +59 -0
  36. package/src/formats/xlsx/io/parse-workbook.ts +75 -0
  37. package/src/formats/xlsx/io/serialize-shared-strings.ts +72 -0
  38. package/src/formats/xlsx/io/serialize-sheet.ts +333 -0
  39. package/src/formats/xlsx/io/serialize-styles.ts +98 -0
  40. package/src/formats/xlsx/io/serialize-workbook.ts +429 -0
  41. package/src/formats/xlsx/io/xlsx-session.ts +314 -0
  42. package/src/formats/xlsx/model/cell.ts +189 -0
  43. package/src/formats/xlsx/model/sheet.ts +326 -0
  44. package/src/formats/xlsx/model/styles.ts +118 -0
  45. package/src/formats/xlsx/model/workbook.ts +453 -0
  46. package/src/formats/xlsx/runtime/cell-commands.ts +567 -0
  47. package/src/formats/xlsx/runtime/sheet-commands.ts +206 -0
  48. package/src/formats/xlsx/runtime/workbook-runtime.ts +177 -0
  49. package/src/formats/xlsx/runtime/workbook-transaction.ts +822 -0
  50. package/src/index.ts +142 -0
  51. package/src/io/README.md +10 -0
  52. package/src/io/docx-session.ts +3175 -0
  53. package/src/io/export/README.md +3 -0
  54. package/src/io/export/export-session.ts +220 -0
  55. package/src/io/export/minimal-docx.ts +115 -0
  56. package/src/io/export/reattach-preserved-parts.ts +54 -0
  57. package/src/io/export/serialize-comments.ts +947 -0
  58. package/src/io/export/serialize-footnotes.ts +394 -0
  59. package/src/io/export/serialize-headers-footers.ts +368 -0
  60. package/src/io/export/serialize-main-document.ts +1342 -0
  61. package/src/io/export/serialize-numbering.ts +218 -0
  62. package/src/io/export/serialize-revisions.ts +389 -0
  63. package/src/io/export/serialize-runtime-revisions.ts +463 -0
  64. package/src/io/export/serialize-tables.ts +174 -0
  65. package/src/io/export/split-review-boundaries.ts +356 -0
  66. package/src/io/export/split-story-blocks-for-runtime-revisions.ts +252 -0
  67. package/src/io/export/table-properties-xml.ts +318 -0
  68. package/src/io/normalize/README.md +3 -0
  69. package/src/io/normalize/normalize-text.ts +670 -0
  70. package/src/io/ooxml/README.md +3 -0
  71. package/src/io/ooxml/highlight-colors.ts +39 -0
  72. package/src/io/ooxml/numbering-sentinels.ts +44 -0
  73. package/src/io/ooxml/parse-comments.ts +852 -0
  74. package/src/io/ooxml/parse-complex-content.ts +287 -0
  75. package/src/io/ooxml/parse-fields.ts +834 -0
  76. package/src/io/ooxml/parse-footnotes.ts +952 -0
  77. package/src/io/ooxml/parse-headers-footers.ts +1212 -0
  78. package/src/io/ooxml/parse-inline-media.ts +461 -0
  79. package/src/io/ooxml/parse-main-document.ts +2947 -0
  80. package/src/io/ooxml/parse-numbering.ts +747 -0
  81. package/src/io/ooxml/parse-revisions.ts +1045 -0
  82. package/src/io/ooxml/parse-settings.ts +184 -0
  83. package/src/io/ooxml/parse-shapes.ts +296 -0
  84. package/src/io/ooxml/parse-styles.ts +639 -0
  85. package/src/io/ooxml/parse-tables.ts +627 -0
  86. package/src/io/ooxml/parse-theme.ts +346 -0
  87. package/src/io/ooxml/part-manifest.ts +136 -0
  88. package/src/io/ooxml/revision-boundaries.ts +475 -0
  89. package/src/io/ooxml/workflow-payload.ts +544 -0
  90. package/src/io/opc/README.md +3 -0
  91. package/src/io/opc/corrupt-package.ts +166 -0
  92. package/src/io/opc/docx-package.ts +74 -0
  93. package/src/io/opc/package-reader.ts +325 -0
  94. package/src/io/opc/package-writer.ts +273 -0
  95. package/src/io/source-package-provenance.ts +241 -0
  96. package/{dist/chunk-RMH72RZI.js → src/legal/bookmarks.ts} +130 -44
  97. package/src/legal/cross-references.ts +414 -0
  98. package/src/legal/defined-terms.ts +203 -0
  99. package/src/legal/index.ts +32 -0
  100. package/src/legal/signature-blocks.ts +259 -0
  101. package/src/model/README.md +3 -0
  102. package/src/model/canonical-document.ts +2722 -0
  103. package/src/model/cds-1.0.0.ts +212 -0
  104. package/src/model/snapshot.ts +760 -0
  105. package/src/preservation/README.md +3 -0
  106. package/src/preservation/markup-compatibility.ts +48 -0
  107. package/src/preservation/opaque-fragment-store.ts +89 -0
  108. package/src/preservation/opaque-region.ts +233 -0
  109. package/src/preservation/package-preservation.ts +113 -0
  110. package/src/preservation/preserved-part-manifest.ts +56 -0
  111. package/src/preservation/relationship-retention.ts +57 -0
  112. package/src/preservation/store.ts +255 -0
  113. package/src/review/README.md +16 -0
  114. package/src/review/store/README.md +3 -0
  115. package/src/review/store/comment-anchors.ts +70 -0
  116. package/src/review/store/comment-remapping.ts +154 -0
  117. package/src/review/store/comment-store.ts +349 -0
  118. package/src/review/store/comment-thread.ts +109 -0
  119. package/src/review/store/revision-actions.ts +423 -0
  120. package/src/review/store/revision-store.ts +323 -0
  121. package/src/review/store/revision-types.ts +182 -0
  122. package/src/review/store/runtime-comment-store.ts +43 -0
  123. package/src/runtime/README.md +3 -0
  124. package/src/runtime/ai-action-policy.ts +764 -0
  125. package/src/runtime/context-analytics.ts +824 -0
  126. package/src/runtime/document-layout.ts +332 -0
  127. package/src/runtime/document-locations.ts +521 -0
  128. package/src/runtime/document-navigation.ts +616 -0
  129. package/src/runtime/document-outline.ts +440 -0
  130. package/src/runtime/document-runtime.ts +4055 -0
  131. package/src/runtime/document-search.ts +145 -0
  132. package/src/runtime/event-refresh-hints.ts +137 -0
  133. package/src/runtime/numbering-prefix.ts +244 -0
  134. package/src/runtime/page-layout-estimation.ts +305 -0
  135. package/src/runtime/read-only-diagnostics-runtime.ts +241 -0
  136. package/src/runtime/resolved-numbering-geometry.ts +293 -0
  137. package/src/runtime/review-runtime.ts +44 -0
  138. package/src/runtime/revision-runtime.ts +107 -0
  139. package/src/runtime/session-capabilities.ts +192 -0
  140. package/src/runtime/story-context.ts +164 -0
  141. package/src/runtime/story-targeting.ts +162 -0
  142. package/src/runtime/suggestions-snapshot.ts +137 -0
  143. package/src/runtime/surface-projection.ts +1553 -0
  144. package/src/runtime/table-commands.ts +173 -0
  145. package/src/runtime/table-schema.ts +309 -0
  146. package/src/runtime/table-style-resolver.ts +409 -0
  147. package/src/runtime/view-state.ts +493 -0
  148. package/src/runtime/virtualized-rendering.ts +258 -0
  149. package/src/runtime/workflow-markup.ts +393 -0
  150. package/src/ui/README.md +30 -0
  151. package/src/ui/WordReviewEditor.tsx +5268 -0
  152. package/src/ui/browser-export.ts +52 -0
  153. package/src/ui/comments/README.md +3 -0
  154. package/src/ui/compatibility/README.md +3 -0
  155. package/src/ui/editor-command-bag.ts +127 -0
  156. package/src/ui/editor-runtime-boundary.ts +1558 -0
  157. package/src/ui/editor-shell-view.tsx +144 -0
  158. package/src/ui/editor-surface/README.md +3 -0
  159. package/src/ui/editor-surface-controller.tsx +66 -0
  160. package/src/ui/headless/comment-decoration-model.ts +124 -0
  161. package/src/ui/headless/preserve-editor-selection.ts +5 -0
  162. package/src/ui/headless/revision-decoration-model.ts +128 -0
  163. package/src/ui/headless/selection-helpers.ts +54 -0
  164. package/src/ui/headless/selection-tool-context.ts +19 -0
  165. package/src/ui/headless/selection-tool-resolver.ts +752 -0
  166. package/src/ui/headless/selection-tool-types.ts +129 -0
  167. package/src/ui/headless/selection-toolbar-model.ts +11 -0
  168. package/src/ui/headless/use-editor-keyboard.ts +103 -0
  169. package/src/ui/review/README.md +3 -0
  170. package/src/ui/runtime-shortcut-dispatch.ts +365 -0
  171. package/src/ui/runtime-snapshot-selectors.ts +197 -0
  172. package/src/ui/shared/revision-filters.ts +31 -0
  173. package/src/ui/status/README.md +3 -0
  174. package/src/ui/theme/README.md +3 -0
  175. package/src/ui/toolbar/README.md +3 -0
  176. package/src/ui/workflow-surface-blocked-rails.ts +94 -0
  177. package/src/ui-tailwind/chrome/chrome-preset-model.ts +107 -0
  178. package/src/ui-tailwind/chrome/chrome-preset-toolbar.tsx +15 -0
  179. package/src/ui-tailwind/chrome/review-queue-bar.tsx +97 -0
  180. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +64 -0
  181. package/src/ui-tailwind/chrome/tw-context-analytics-summary.tsx +122 -0
  182. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +121 -0
  183. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
  184. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +30 -0
  185. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +365 -0
  186. package/src/ui-tailwind/chrome/tw-selection-tool-blocked.tsx +23 -0
  187. package/src/ui-tailwind/chrome/tw-selection-tool-comment.tsx +35 -0
  188. package/src/ui-tailwind/chrome/tw-selection-tool-formatting.tsx +37 -0
  189. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +298 -0
  190. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +116 -0
  191. package/src/ui-tailwind/chrome/tw-selection-tool-suggestion.tsx +29 -0
  192. package/src/ui-tailwind/chrome/tw-selection-tool-workflow.tsx +27 -0
  193. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +186 -0
  194. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +139 -0
  195. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +200 -0
  196. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +58 -0
  197. package/src/ui-tailwind/chrome/use-before-unload.ts +20 -0
  198. package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
  199. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +189 -0
  200. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
  201. package/src/ui-tailwind/editor-surface/pm-decorations.ts +411 -0
  202. package/src/ui-tailwind/editor-surface/pm-position-map.ts +123 -0
  203. package/src/ui-tailwind/editor-surface/pm-schema.ts +927 -0
  204. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +567 -0
  205. package/src/ui-tailwind/editor-surface/search-plugin.ts +168 -0
  206. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +65 -0
  207. package/src/ui-tailwind/editor-surface/tw-caret.tsx +12 -0
  208. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +150 -0
  209. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +129 -0
  210. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +58 -0
  211. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +151 -0
  212. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +1047 -0
  213. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +111 -0
  214. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +503 -0
  215. package/src/ui-tailwind/index.ts +62 -0
  216. package/src/ui-tailwind/page-chrome-model.ts +27 -0
  217. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +406 -0
  218. package/src/ui-tailwind/review/tw-health-panel.tsx +149 -0
  219. package/src/ui-tailwind/review/tw-review-rail.tsx +122 -0
  220. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +164 -0
  221. package/src/ui-tailwind/status/tw-status-bar.tsx +65 -0
  222. package/{dist → src}/ui-tailwind/theme/editor-theme.css +58 -40
  223. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +52 -0
  224. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +1133 -0
  225. package/src/ui-tailwind/tw-review-workspace.tsx +1460 -0
  226. package/src/validation/README.md +3 -0
  227. package/src/validation/compatibility-engine.ts +878 -0
  228. package/src/validation/compatibility-report.ts +161 -0
  229. package/src/validation/diagnostics.ts +204 -0
  230. package/src/validation/docx-comment-proof.ts +720 -0
  231. package/src/validation/import-diagnostics.ts +128 -0
  232. package/src/validation/low-priority-word-surfaces.ts +373 -0
  233. package/dist/canonical-document-BLEbzL2J.d.cts +0 -844
  234. package/dist/canonical-document-BLEbzL2J.d.ts +0 -844
  235. package/dist/chunk-2FJS5GZM.js +0 -763
  236. package/dist/chunk-2FJS5GZM.js.map +0 -1
  237. package/dist/chunk-2OQBZS3F.js +0 -446
  238. package/dist/chunk-2OQBZS3F.js.map +0 -1
  239. package/dist/chunk-2S7W4KFO.js +0 -127
  240. package/dist/chunk-2S7W4KFO.js.map +0 -1
  241. package/dist/chunk-2TG72QSW.js +0 -3874
  242. package/dist/chunk-2TG72QSW.js.map +0 -1
  243. package/dist/chunk-36QNIZBO.js +0 -532
  244. package/dist/chunk-36QNIZBO.js.map +0 -1
  245. package/dist/chunk-4AQOYAW4.js +0 -3069
  246. package/dist/chunk-4AQOYAW4.js.map +0 -1
  247. package/dist/chunk-4D5EWJ3P.js +0 -77
  248. package/dist/chunk-4D5EWJ3P.js.map +0 -1
  249. package/dist/chunk-5FN54NDH.js +0 -2257
  250. package/dist/chunk-5FN54NDH.js.map +0 -1
  251. package/dist/chunk-BOYGQYRQ.js +0 -7306
  252. package/dist/chunk-BOYGQYRQ.js.map +0 -1
  253. package/dist/chunk-CN3XMECL.js +0 -212
  254. package/dist/chunk-CN3XMECL.js.map +0 -1
  255. package/dist/chunk-EBI3BX6U.js +0 -164
  256. package/dist/chunk-EBI3BX6U.js.map +0 -1
  257. package/dist/chunk-EILUG3VB.js +0 -1275
  258. package/dist/chunk-EILUG3VB.js.map +0 -1
  259. package/dist/chunk-FUDY333O.js +0 -70
  260. package/dist/chunk-FUDY333O.js.map +0 -1
  261. package/dist/chunk-GBVOWFIK.js +0 -1237
  262. package/dist/chunk-GBVOWFIK.js.map +0 -1
  263. package/dist/chunk-H4TQ3H3Y.js +0 -262
  264. package/dist/chunk-H4TQ3H3Y.js.map +0 -1
  265. package/dist/chunk-JGB3IXZO.js +0 -189
  266. package/dist/chunk-JGB3IXZO.js.map +0 -1
  267. package/dist/chunk-KD2QRQPY.js +0 -4342
  268. package/dist/chunk-KD2QRQPY.js.map +0 -1
  269. package/dist/chunk-KLMXQVYK.js +0 -369
  270. package/dist/chunk-KLMXQVYK.js.map +0 -1
  271. package/dist/chunk-KZUG5KFQ.js +0 -214
  272. package/dist/chunk-KZUG5KFQ.js.map +0 -1
  273. package/dist/chunk-QDAQ4CJU.js +0 -345
  274. package/dist/chunk-QDAQ4CJU.js.map +0 -1
  275. package/dist/chunk-RMH72RZI.js.map +0 -1
  276. package/dist/chunk-SWKWQZXM.js +0 -117
  277. package/dist/chunk-SWKWQZXM.js.map +0 -1
  278. package/dist/chunk-TJBP2K4T.js.map +0 -1
  279. package/dist/chunk-TLCEAQDQ.js +0 -542
  280. package/dist/chunk-TLCEAQDQ.js.map +0 -1
  281. package/dist/chunk-UZXBISGO.js.map +0 -1
  282. package/dist/chunk-WGBAKP3Q.js +0 -3220
  283. package/dist/chunk-WGBAKP3Q.js.map +0 -1
  284. package/dist/compare/index.cjs +0 -5475
  285. package/dist/compare/index.cjs.map +0 -1
  286. package/dist/compare/index.d.cts +0 -114
  287. package/dist/compare/index.d.ts +0 -114
  288. package/dist/compare/index.js +0 -731
  289. package/dist/compare/index.js.map +0 -1
  290. package/dist/core/commands/formatting-commands.cjs +0 -828
  291. package/dist/core/commands/formatting-commands.cjs.map +0 -1
  292. package/dist/core/commands/formatting-commands.d.cts +0 -63
  293. package/dist/core/commands/formatting-commands.d.ts +0 -63
  294. package/dist/core/commands/formatting-commands.js +0 -37
  295. package/dist/core/commands/formatting-commands.js.map +0 -1
  296. package/dist/core/commands/image-commands.cjs +0 -2023
  297. package/dist/core/commands/image-commands.cjs.map +0 -1
  298. package/dist/core/commands/image-commands.d.cts +0 -58
  299. package/dist/core/commands/image-commands.d.ts +0 -58
  300. package/dist/core/commands/image-commands.js +0 -18
  301. package/dist/core/commands/image-commands.js.map +0 -1
  302. package/dist/core/commands/section-layout-commands.cjs.map +0 -1
  303. package/dist/core/commands/section-layout-commands.d.cts +0 -62
  304. package/dist/core/commands/section-layout-commands.d.ts +0 -62
  305. package/dist/core/commands/section-layout-commands.js +0 -21
  306. package/dist/core/commands/section-layout-commands.js.map +0 -1
  307. package/dist/core/commands/style-commands.cjs.map +0 -1
  308. package/dist/core/commands/style-commands.d.cts +0 -13
  309. package/dist/core/commands/style-commands.d.ts +0 -13
  310. package/dist/core/commands/style-commands.js +0 -9
  311. package/dist/core/commands/style-commands.js.map +0 -1
  312. package/dist/core/commands/table-structure-commands.cjs +0 -1883
  313. package/dist/core/commands/table-structure-commands.cjs.map +0 -1
  314. package/dist/core/commands/table-structure-commands.d.cts +0 -59
  315. package/dist/core/commands/table-structure-commands.d.ts +0 -59
  316. package/dist/core/commands/table-structure-commands.js +0 -12
  317. package/dist/core/commands/table-structure-commands.js.map +0 -1
  318. package/dist/core/commands/text-commands.cjs +0 -2391
  319. package/dist/core/commands/text-commands.cjs.map +0 -1
  320. package/dist/core/commands/text-commands.d.cts +0 -24
  321. package/dist/core/commands/text-commands.d.ts +0 -24
  322. package/dist/core/commands/text-commands.js +0 -28
  323. package/dist/core/commands/text-commands.js.map +0 -1
  324. package/dist/core/selection/mapping.cjs +0 -200
  325. package/dist/core/selection/mapping.cjs.map +0 -1
  326. package/dist/core/selection/mapping.d.cts +0 -2
  327. package/dist/core/selection/mapping.d.ts +0 -2
  328. package/dist/core/selection/mapping.js +0 -31
  329. package/dist/core/selection/mapping.js.map +0 -1
  330. package/dist/core/state/editor-state.cjs +0 -2278
  331. package/dist/core/state/editor-state.cjs.map +0 -1
  332. package/dist/core/state/editor-state.d.cts +0 -2
  333. package/dist/core/state/editor-state.d.ts +0 -2
  334. package/dist/core/state/editor-state.js +0 -26
  335. package/dist/core/state/editor-state.js.map +0 -1
  336. package/dist/index.cjs +0 -38553
  337. package/dist/index.cjs.map +0 -1
  338. package/dist/index.d.cts +0 -15
  339. package/dist/index.d.ts +0 -15
  340. package/dist/index.js +0 -7856
  341. package/dist/index.js.map +0 -1
  342. package/dist/io/docx-session.cjs +0 -16236
  343. package/dist/io/docx-session.cjs.map +0 -1
  344. package/dist/io/docx-session.d.cts +0 -21
  345. package/dist/io/docx-session.d.ts +0 -21
  346. package/dist/io/docx-session.js +0 -18
  347. package/dist/io/docx-session.js.map +0 -1
  348. package/dist/legal/index.cjs +0 -3900
  349. package/dist/legal/index.cjs.map +0 -1
  350. package/dist/legal/index.d.cts +0 -86
  351. package/dist/legal/index.d.ts +0 -86
  352. package/dist/legal/index.js +0 -616
  353. package/dist/legal/index.js.map +0 -1
  354. package/dist/public-types-7ZL_94cz.d.ts +0 -1573
  355. package/dist/public-types-CeMaDueh.d.cts +0 -1573
  356. package/dist/public-types.cjs +0 -19
  357. package/dist/public-types.cjs.map +0 -1
  358. package/dist/public-types.d.cts +0 -2
  359. package/dist/public-types.d.ts +0 -2
  360. package/dist/public-types.js +0 -1
  361. package/dist/public-types.js.map +0 -1
  362. package/dist/runtime/document-runtime.cjs +0 -11140
  363. package/dist/runtime/document-runtime.cjs.map +0 -1
  364. package/dist/runtime/document-runtime.d.cts +0 -231
  365. package/dist/runtime/document-runtime.d.ts +0 -231
  366. package/dist/runtime/document-runtime.js +0 -21
  367. package/dist/runtime/document-runtime.js.map +0 -1
  368. package/dist/structural-helpers-CilgOVhh.d.cts +0 -10
  369. package/dist/structural-helpers-q0Gd-eBN.d.ts +0 -10
  370. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +0 -313
  371. package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +0 -1
  372. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +0 -67
  373. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +0 -67
  374. package/dist/ui-tailwind/editor-surface/search-plugin.js +0 -23
  375. package/dist/ui-tailwind/editor-surface/search-plugin.js.map +0 -1
  376. package/dist/ui-tailwind/index.cjs +0 -4833
  377. package/dist/ui-tailwind/index.cjs.map +0 -1
  378. package/dist/ui-tailwind/index.d.cts +0 -617
  379. package/dist/ui-tailwind/index.d.ts +0 -617
  380. package/dist/ui-tailwind/index.js +0 -575
  381. package/dist/ui-tailwind/index.js.map +0 -1
@@ -0,0 +1,1558 @@
1
+ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
2
+
3
+ import type {
4
+ AutosaveState,
5
+ CompatibilityReport,
6
+ DocumentNavigationSnapshot,
7
+ EditorDatastoreAdapter,
8
+ EditorError,
9
+ EditorHostAdapter,
10
+ EditorSessionState,
11
+ EditorViewStateSnapshot,
12
+ EditorWarning,
13
+ ExportDocxOptions,
14
+ ExportResult,
15
+ FieldSnapshot,
16
+ PersistedEditorSnapshot,
17
+ RuntimeRenderSnapshot,
18
+ SuggestionsSnapshot,
19
+ TocRefreshResult,
20
+ UpdateFieldsResult,
21
+ ViewMode as EditorViewMode,
22
+ WordReviewEditorEvent,
23
+ WordReviewEditorProps,
24
+ WorkspaceMode,
25
+ ZoomLevel,
26
+ } from "../api/public-types";
27
+ import {
28
+ editorSessionStateFromPersistedSnapshot,
29
+ persistedSnapshotFromEditorSessionState,
30
+ } from "../api/session-state.ts";
31
+ import { createCanonicalDocumentId } from "../core/state/editor-state.ts";
32
+ import {
33
+ createDocumentRuntime,
34
+ type DocumentRuntimeEvent,
35
+ type DocumentRuntime,
36
+ } from "../runtime/document-runtime.ts";
37
+ import { loadDocxEditorSession } from "../io/docx-session.ts";
38
+ import {
39
+ decodePersistedSourcePackageBytes,
40
+ hasValidPersistedSourcePackageDigest,
41
+ } from "../io/source-package-provenance.ts";
42
+ import {
43
+ createEditorViewStateSnapshot,
44
+ createViewState,
45
+ } from "../runtime/view-state.ts";
46
+ import {
47
+ recordPerfSample,
48
+ } from "../ui-tailwind/editor-surface/perf-probe.ts";
49
+ import {
50
+ downloadExportResult,
51
+ withExportDelivery,
52
+ } from "./browser-export";
53
+
54
+ export interface ResolvedSource {
55
+ source: "docx" | "session" | "snapshot";
56
+ sourceLabel?: string;
57
+ initialDocx?: Uint8Array | ArrayBuffer;
58
+ initialSessionState?: EditorSessionState;
59
+ initialSnapshot?: PersistedEditorSnapshot;
60
+ }
61
+
62
+ export interface CreateRuntimeArgs {
63
+ documentId: string;
64
+ readOnly: boolean;
65
+ source: ResolvedSource;
66
+ initialViewState?: {
67
+ workspaceMode?: WorkspaceMode;
68
+ zoomLevel?: ZoomLevel;
69
+ };
70
+ hostAdapter?: EditorHostAdapter;
71
+ datastore?: EditorDatastoreAdapter;
72
+ currentUserId?: string;
73
+ }
74
+
75
+ interface RuntimeLifecycleHandlers {
76
+ onWarning?: (warning: EditorWarning) => void;
77
+ onError?: (error: EditorError) => void;
78
+ onEvent?: (event: WordReviewEditorEvent) => void;
79
+ }
80
+
81
+ export interface WordReviewEditorRuntime extends DocumentRuntime {
82
+ getFatalError?(): EditorError | undefined;
83
+ dispose?(): void;
84
+ setDefaultAuthorId?(authorId?: string): void;
85
+ drainBootstrapEvents?(): DocumentRuntimeEvent[];
86
+ emitBlockedCommand(
87
+ command: string,
88
+ reasons: Extract<WordReviewEditorEvent, { type: "command_blocked" }>["reasons"],
89
+ ): void;
90
+ }
91
+
92
+ type PackageBackedDocxSession = ReturnType<typeof loadDocxEditorSession>;
93
+
94
+ interface SnapshotExportBarrier {
95
+ reason:
96
+ | "missing_source_package_provenance"
97
+ | "invalid_source_package_provenance";
98
+ message: string;
99
+ }
100
+
101
+ export interface EditorRuntimeBoundaryState {
102
+ runtime: WordReviewEditorRuntime | null;
103
+ loadError: EditorError | null;
104
+ activeRuntime: WordReviewEditorRuntime;
105
+ fallbackSnapshot: RuntimeRenderSnapshot;
106
+ loadingSessionState: EditorSessionState;
107
+ loadingViewState: EditorViewStateSnapshot;
108
+ loadingNavigation: DocumentNavigationSnapshot;
109
+ hostAdapterRef: React.MutableRefObject<EditorHostAdapter | undefined>;
110
+ datastoreRef: React.MutableRefObject<EditorDatastoreAdapter | undefined>;
111
+ onEventRef: React.MutableRefObject<((event: WordReviewEditorEvent) => void) | undefined>;
112
+ onWarningRef: React.MutableRefObject<((warning: EditorWarning) => void) | undefined>;
113
+ onErrorRef: React.MutableRefObject<((error: EditorError) => void) | undefined>;
114
+ autosaveTimerRef: React.MutableRefObject<ReturnType<typeof setTimeout> | null>;
115
+ lastSavedRevisionTokenRef: React.MutableRefObject<string | null>;
116
+ runtimeViewStateSeedRef: React.MutableRefObject<{
117
+ workspaceMode: WorkspaceMode;
118
+ zoomLevel: ZoomLevel;
119
+ }>;
120
+ }
121
+
122
+ export async function __resolveWordReviewEditorSource(
123
+ props: Pick<
124
+ WordReviewEditorProps,
125
+ | "documentId"
126
+ | "hostAdapter"
127
+ | "datastore"
128
+ | "externalDocSource"
129
+ | "initialDocx"
130
+ | "initialSessionState"
131
+ | "initialSnapshot"
132
+ | "initialSourceLabel"
133
+ | "loadRevision"
134
+ | "loadSourcePolicy"
135
+ >,
136
+ ): Promise<ResolvedSource> {
137
+ const explicitInitialCount =
138
+ Number(Boolean(props.initialDocx)) +
139
+ Number(Boolean(props.initialSessionState)) +
140
+ Number(Boolean(props.initialSnapshot));
141
+ if (explicitInitialCount > 1) {
142
+ throw new Error(
143
+ "Provide exactly one of initialDocx, initialSessionState, or initialSnapshot.",
144
+ );
145
+ }
146
+
147
+ if (props.externalDocSource) {
148
+ if (props.externalDocSource.kind === "docx") {
149
+ return {
150
+ source: "docx",
151
+ initialDocx: props.externalDocSource.bytes,
152
+ sourceLabel: props.externalDocSource.sourceLabel,
153
+ };
154
+ }
155
+
156
+ if (props.externalDocSource.kind === "session") {
157
+ return {
158
+ source: "session",
159
+ initialSessionState: props.externalDocSource.sessionState,
160
+ sourceLabel:
161
+ props.externalDocSource.sourceLabel ??
162
+ props.externalDocSource.sessionState.sourcePackage?.sourceLabel,
163
+ };
164
+ }
165
+
166
+ return {
167
+ source: "snapshot",
168
+ initialSnapshot: props.externalDocSource.snapshot,
169
+ sourceLabel: props.externalDocSource.sourceLabel,
170
+ };
171
+ }
172
+
173
+ if (props.initialSessionState) {
174
+ return {
175
+ source: "session",
176
+ initialSessionState: props.initialSessionState,
177
+ sourceLabel:
178
+ props.initialSourceLabel ??
179
+ props.initialSessionState.sourcePackage?.sourceLabel,
180
+ };
181
+ }
182
+
183
+ if (props.initialSnapshot) {
184
+ return {
185
+ source: "snapshot",
186
+ initialSnapshot: props.initialSnapshot,
187
+ sourceLabel: props.initialSourceLabel,
188
+ };
189
+ }
190
+
191
+ if (props.initialDocx) {
192
+ return {
193
+ source: "docx",
194
+ initialDocx: props.initialDocx,
195
+ sourceLabel: props.initialSourceLabel,
196
+ };
197
+ }
198
+
199
+ const loader = props.hostAdapter?.load ?? props.datastore?.load;
200
+ if (!loader) {
201
+ throw new Error(
202
+ `WordReviewEditor ${props.documentId} needs initialDocx, initialSessionState, initialSnapshot, or a host/datastore load source.`,
203
+ );
204
+ }
205
+
206
+ const loadResult = await loader({
207
+ documentId: props.documentId,
208
+ loadRevision: props.loadRevision,
209
+ loadSourcePolicy: props.loadSourcePolicy,
210
+ });
211
+
212
+ if (!loadResult.source) {
213
+ throw new Error(
214
+ `Host or datastore loader did not return a loadable source for ${props.documentId}.`,
215
+ );
216
+ }
217
+
218
+ if (loadResult.source.kind === "docx") {
219
+ return {
220
+ source: "docx",
221
+ initialDocx: loadResult.source.bytes,
222
+ sourceLabel: loadResult.source.sourceLabel,
223
+ };
224
+ }
225
+
226
+ if (loadResult.source.kind === "session") {
227
+ return {
228
+ source: "session",
229
+ initialSessionState: loadResult.source.sessionState,
230
+ sourceLabel:
231
+ loadResult.source.sourceLabel ??
232
+ loadResult.source.sessionState.sourcePackage?.sourceLabel,
233
+ };
234
+ }
235
+
236
+ return {
237
+ source: "snapshot",
238
+ initialSnapshot: loadResult.source.snapshot,
239
+ sourceLabel: loadResult.source.sourceLabel,
240
+ };
241
+ }
242
+
243
+ export function __createFallbackRuntime(args: CreateRuntimeArgs): WordReviewEditorRuntime {
244
+ return createRuntime(args);
245
+ }
246
+
247
+ export function useEditorRuntimeBoundary(
248
+ props: WordReviewEditorProps,
249
+ ): EditorRuntimeBoundaryState {
250
+ const {
251
+ currentUser,
252
+ datastore,
253
+ documentId,
254
+ externalDocSource,
255
+ externalDocumentRevision,
256
+ hostAdapter,
257
+ initialDocx,
258
+ initialSessionState,
259
+ initialSnapshot,
260
+ initialSourceLabel,
261
+ loadRevision,
262
+ loadSourcePolicy = "prefer-saved-state",
263
+ onError,
264
+ onEvent,
265
+ onWarning,
266
+ readOnly = false,
267
+ reviewMode = "review",
268
+ } = props;
269
+
270
+ const [runtime, setRuntime] = useState<WordReviewEditorRuntime | null>(null);
271
+ const [loadError, setLoadError] = useState<EditorError | null>(null);
272
+ const runtimeRef = useRef<WordReviewEditorRuntime | null>(null);
273
+ const pendingReadySourceRef = useRef<"docx" | "session" | "snapshot" | null>(null);
274
+ const autosaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
275
+ const lastSavedRevisionTokenRef = useRef<string | null>(null);
276
+ const hostAdapterRef = useRef(hostAdapter);
277
+ const datastoreRef = useRef(datastore);
278
+ const onEventRef = useRef(onEvent);
279
+ const onWarningRef = useRef(onWarning);
280
+ const onErrorRef = useRef(onError);
281
+ const currentUserIdRef = useRef(currentUser.userId);
282
+ const runtimeViewStateSeedRef = useRef<{
283
+ workspaceMode: WorkspaceMode;
284
+ zoomLevel: ZoomLevel;
285
+ }>({
286
+ workspaceMode: "canvas",
287
+ zoomLevel: 100,
288
+ });
289
+ const initialSourceRef = useRef<{
290
+ documentId: string;
291
+ initialDocx?: Uint8Array | ArrayBuffer;
292
+ initialSessionState?: EditorSessionState;
293
+ initialSnapshot?: PersistedEditorSnapshot;
294
+ initialSourceLabel?: string;
295
+ } | null>(null);
296
+
297
+ if (!initialSourceRef.current || initialSourceRef.current.documentId !== documentId) {
298
+ initialSourceRef.current = {
299
+ documentId,
300
+ initialDocx,
301
+ initialSessionState,
302
+ initialSnapshot,
303
+ initialSourceLabel,
304
+ };
305
+ }
306
+
307
+ currentUserIdRef.current = currentUser.userId;
308
+
309
+ const stableInitialSource = initialSourceRef.current;
310
+ const sourceReloadKey = externalDocSource
311
+ ? `external:${externalDocSource.kind}:${externalDocumentRevision ?? "static"}`
312
+ : stableInitialSource?.initialSessionState
313
+ ? "initial-session"
314
+ : stableInitialSource?.initialSnapshot
315
+ ? "initial-snapshot"
316
+ : stableInitialSource?.initialDocx
317
+ ? "initial-docx"
318
+ : hostAdapter
319
+ ? `host-adapter:${loadRevision ?? "static"}`
320
+ : `datastore:${loadRevision ?? "static"}`;
321
+
322
+ useEffect(() => {
323
+ hostAdapterRef.current = hostAdapter;
324
+ datastoreRef.current = datastore;
325
+ onEventRef.current = onEvent;
326
+ onWarningRef.current = onWarning;
327
+ onErrorRef.current = onError;
328
+ }, [datastore, hostAdapter, onError, onEvent, onWarning]);
329
+
330
+ useLayoutEffect(() => {
331
+ runtimeRef.current?.setDefaultAuthorId?.(currentUser.userId);
332
+ }, [currentUser.userId]);
333
+
334
+ useEffect(() => {
335
+ let cancelled = false;
336
+
337
+ async function loadRuntime(): Promise<void> {
338
+ setLoadError(null);
339
+ pendingReadySourceRef.current = null;
340
+ if (autosaveTimerRef.current) {
341
+ clearTimeout(autosaveTimerRef.current);
342
+ autosaveTimerRef.current = null;
343
+ }
344
+ lastSavedRevisionTokenRef.current = null;
345
+ runtimeRef.current?.dispose?.();
346
+ runtimeRef.current = null;
347
+ setRuntime(null);
348
+
349
+ try {
350
+ const source = await __resolveWordReviewEditorSource({
351
+ documentId,
352
+ hostAdapter: hostAdapterRef.current,
353
+ datastore: datastoreRef.current,
354
+ externalDocSource,
355
+ initialDocx: stableInitialSource?.initialDocx,
356
+ initialSessionState: stableInitialSource?.initialSessionState,
357
+ initialSnapshot: stableInitialSource?.initialSnapshot,
358
+ initialSourceLabel: stableInitialSource?.initialSourceLabel,
359
+ loadRevision,
360
+ loadSourcePolicy,
361
+ });
362
+
363
+ if (cancelled) {
364
+ return;
365
+ }
366
+
367
+ const nextRuntime = createRuntime(
368
+ {
369
+ documentId,
370
+ readOnly,
371
+ source,
372
+ initialViewState: runtimeViewStateSeedRef.current,
373
+ hostAdapter: hostAdapterRef.current,
374
+ datastore: datastoreRef.current,
375
+ currentUserId: currentUserIdRef.current,
376
+ },
377
+ {
378
+ onWarning: onWarningRef.current,
379
+ onError: onErrorRef.current,
380
+ onEvent: onEventRef.current,
381
+ },
382
+ );
383
+ recordPerfSample("runtime.create");
384
+ runtimeRef.current = nextRuntime;
385
+ pendingReadySourceRef.current = source.source;
386
+ setRuntime(nextRuntime);
387
+ } catch (error) {
388
+ if (cancelled) {
389
+ return;
390
+ }
391
+
392
+ const normalized = normalizeEditorError(error);
393
+ setLoadError(normalized);
394
+ onErrorRef.current?.(normalized);
395
+ emitEditorEvent({
396
+ hostAdapter: hostAdapterRef.current,
397
+ datastore: datastoreRef.current,
398
+ onEvent: onEventRef.current,
399
+ event: {
400
+ type: "error",
401
+ documentId,
402
+ error: normalized,
403
+ },
404
+ });
405
+ }
406
+ }
407
+
408
+ void loadRuntime();
409
+
410
+ return () => {
411
+ cancelled = true;
412
+ };
413
+ }, [
414
+ documentId,
415
+ readOnly,
416
+ sourceReloadKey,
417
+ ]);
418
+
419
+ useEffect(() => {
420
+ if (!runtime || !pendingReadySourceRef.current) {
421
+ return;
422
+ }
423
+
424
+ emitEditorEvent({
425
+ hostAdapter: hostAdapterRef.current,
426
+ datastore: datastoreRef.current,
427
+ onEvent: onEventRef.current,
428
+ event: createReadyEvent(runtime, pendingReadySourceRef.current),
429
+ });
430
+ for (const event of runtime.drainBootstrapEvents?.() ?? []) {
431
+ if (event.type === "ready") {
432
+ continue;
433
+ }
434
+ const hostEvent = event as Exclude<WordReviewEditorEvent, { type: "ready" }>;
435
+ emitEditorEvent({
436
+ hostAdapter: hostAdapterRef.current,
437
+ datastore: datastoreRef.current,
438
+ onEvent: onEventRef.current,
439
+ event: hostEvent,
440
+ });
441
+ }
442
+ pendingReadySourceRef.current = null;
443
+ }, [runtime]);
444
+
445
+ useEffect(() => {
446
+ if (!runtime?.subscribeToEvents) {
447
+ return;
448
+ }
449
+
450
+ return runtime.subscribeToEvents((event) => {
451
+ if (event.type === "export_completed" || event.type === "ready") {
452
+ return;
453
+ }
454
+ emitEditorEvent({
455
+ hostAdapter: hostAdapterRef.current,
456
+ datastore: datastoreRef.current,
457
+ onEvent: onEventRef.current,
458
+ event,
459
+ });
460
+ });
461
+ }, [runtime]);
462
+
463
+ useEffect(() => {
464
+ return () => {
465
+ if (autosaveTimerRef.current) {
466
+ clearTimeout(autosaveTimerRef.current);
467
+ autosaveTimerRef.current = null;
468
+ }
469
+ runtimeRef.current?.dispose?.();
470
+ runtimeRef.current = null;
471
+ };
472
+ }, []);
473
+
474
+ const fallbackSnapshot = useMemo(
475
+ () =>
476
+ loadError
477
+ ? createErrorSnapshot(documentId, loadError)
478
+ : createLoadingSnapshot(
479
+ documentId,
480
+ readOnly,
481
+ guessSourceLabel(
482
+ initialSourceLabel,
483
+ initialSessionState,
484
+ initialSnapshot,
485
+ externalDocSource,
486
+ ),
487
+ ),
488
+ [
489
+ documentId,
490
+ externalDocSource,
491
+ initialSessionState,
492
+ initialSnapshot,
493
+ initialSourceLabel,
494
+ loadError,
495
+ readOnly,
496
+ ],
497
+ );
498
+ const loadingPersistedSnapshot = useMemo(
499
+ () => createFallbackPersistedSnapshot(documentId, initialSourceLabel),
500
+ [documentId, initialSourceLabel],
501
+ );
502
+ const loadingSessionState = useMemo(
503
+ () =>
504
+ initialSessionState ??
505
+ (initialSnapshot
506
+ ? editorSessionStateFromPersistedSnapshot(initialSnapshot)
507
+ : editorSessionStateFromPersistedSnapshot(loadingPersistedSnapshot)),
508
+ [initialSessionState, initialSnapshot, loadingPersistedSnapshot],
509
+ );
510
+ const loadingViewState = useMemo(
511
+ () =>
512
+ createLoadingViewStateSnapshot(
513
+ fallbackSnapshot,
514
+ runtimeViewStateSeedRef.current,
515
+ deriveEditorViewMode(fallbackSnapshot.readOnly, reviewMode),
516
+ ),
517
+ [fallbackSnapshot, reviewMode],
518
+ );
519
+ const loadingNavigation = useMemo(
520
+ () => createLoadingNavigationSnapshot(),
521
+ [],
522
+ );
523
+ const loadingRuntimeBridge = useMemo(
524
+ () =>
525
+ createLoadingRuntimeBridge({
526
+ snapshot: fallbackSnapshot,
527
+ sessionState: loadingSessionState,
528
+ viewState: loadingViewState,
529
+ navigation: loadingNavigation,
530
+ }),
531
+ [fallbackSnapshot, loadingNavigation, loadingSessionState, loadingViewState],
532
+ );
533
+
534
+ return {
535
+ runtime,
536
+ loadError,
537
+ activeRuntime: runtime ?? loadingRuntimeBridge,
538
+ fallbackSnapshot,
539
+ loadingSessionState,
540
+ loadingViewState,
541
+ loadingNavigation,
542
+ hostAdapterRef,
543
+ datastoreRef,
544
+ onEventRef,
545
+ onWarningRef,
546
+ onErrorRef,
547
+ autosaveTimerRef,
548
+ lastSavedRevisionTokenRef,
549
+ runtimeViewStateSeedRef,
550
+ };
551
+ }
552
+
553
+ function createRuntime(
554
+ args: CreateRuntimeArgs,
555
+ handlers: RuntimeLifecycleHandlers = {},
556
+ ): WordReviewEditorRuntime {
557
+ const bootstrapEvents: DocumentRuntimeEvent[] = [];
558
+ const docxSession = args.source.initialDocx
559
+ ? loadDocxEditorSession({
560
+ documentId: args.documentId,
561
+ sourceLabel: args.source.sourceLabel,
562
+ bytes: args.source.initialDocx,
563
+ editorBuild: "dev",
564
+ })
565
+ : undefined;
566
+ const snapshotExportResolution = !args.source.initialDocx
567
+ ? resolvePackageBackedExportSession(args)
568
+ : undefined;
569
+ const initialSessionState =
570
+ args.source.initialSessionState ??
571
+ docxSession?.initialSessionState ??
572
+ (args.source.initialSnapshot
573
+ ? editorSessionStateFromPersistedSnapshot(args.source.initialSnapshot)
574
+ : editorSessionStateFromPersistedSnapshot(
575
+ createFallbackPersistedSnapshot(
576
+ args.documentId,
577
+ args.source.sourceLabel ?? "Generated shell snapshot",
578
+ ),
579
+ ));
580
+ const runtimeSessionState = snapshotExportResolution?.barrier
581
+ ? applySessionExportBarrier(initialSessionState, snapshotExportResolution.barrier)
582
+ : initialSessionState;
583
+
584
+ const runtime: WordReviewEditorRuntime = Object.assign(createDocumentRuntime({
585
+ documentId: args.documentId,
586
+ initialSessionState: runtimeSessionState,
587
+ sourceKind: args.source.source,
588
+ sourceLabel: args.source.sourceLabel,
589
+ initialViewState: args.initialViewState,
590
+ readOnly: args.readOnly || docxSession?.readOnly,
591
+ editorBuild: runtimeSessionState.editorBuild,
592
+ fatalError: docxSession?.fatalError,
593
+ protectionSnapshot: docxSession?.protectionSnapshot,
594
+ exportDocx: async (sessionState, options) => {
595
+ if (docxSession) {
596
+ return docxSession.exportDocx(sessionState, options);
597
+ }
598
+
599
+ if (snapshotExportResolution?.session) {
600
+ return snapshotExportResolution.session.exportDocx(sessionState, options);
601
+ }
602
+
603
+ throw createSnapshotExportBlockedError(
604
+ args.documentId,
605
+ snapshotExportResolution?.barrier ?? {
606
+ reason: "missing_source_package_provenance",
607
+ message:
608
+ "DOCX export is blocked because this session does not carry embedded source package provenance.",
609
+ },
610
+ );
611
+ },
612
+ onWarning: handlers.onWarning,
613
+ onError: handlers.onError,
614
+ onEvent: (event) => {
615
+ bootstrapEvents.push(event);
616
+ },
617
+ defaultAuthorId: args.currentUserId,
618
+ }), {
619
+ drainBootstrapEvents: () => bootstrapEvents.splice(0, bootstrapEvents.length),
620
+ emitBlockedCommand: (
621
+ command: string,
622
+ reasons: Extract<WordReviewEditorEvent, { type: "command_blocked" }>["reasons"],
623
+ ) => {
624
+ emitEditorEvent({
625
+ hostAdapter: args.hostAdapter,
626
+ datastore: args.datastore,
627
+ onEvent: handlers.onEvent,
628
+ event: {
629
+ type: "command_blocked",
630
+ documentId: args.documentId,
631
+ command,
632
+ reasons,
633
+ },
634
+ });
635
+ },
636
+ });
637
+
638
+ return runtime;
639
+ }
640
+
641
+ function createLoadingSnapshot(
642
+ documentId: string,
643
+ readOnly: boolean,
644
+ sourceLabel?: string,
645
+ ): RuntimeRenderSnapshot {
646
+ return {
647
+ documentId,
648
+ sessionId: `${documentId}-loading`,
649
+ sourceLabel,
650
+ revisionToken: `${documentId}:loading`,
651
+ isReady: false,
652
+ isDirty: false,
653
+ readOnly,
654
+ documentMode: "editing",
655
+ selection: collapsedSelection(),
656
+ activeStory: { kind: "main" },
657
+ documentStats: {
658
+ storyLength: 0,
659
+ commentCount: 0,
660
+ revisionCount: 0,
661
+ opaqueFragmentCount: 0,
662
+ },
663
+ comments: {
664
+ openCommentIds: [],
665
+ resolvedCommentIds: [],
666
+ detachedCommentIds: [],
667
+ totalCount: 0,
668
+ threads: [],
669
+ },
670
+ trackedChanges: {
671
+ pendingChangeIds: [],
672
+ acceptedChangeIds: [],
673
+ rejectedChangeIds: [],
674
+ detachedChangeIds: [],
675
+ actionableChangeIds: [],
676
+ preserveOnlyChangeIds: [],
677
+ totalCount: 0,
678
+ revisions: [],
679
+ },
680
+ compatibility: {
681
+ blockExport: false,
682
+ blockExportReasons: [],
683
+ warningCount: 0,
684
+ errorCount: 0,
685
+ featureEntries: [],
686
+ },
687
+ warnings: [],
688
+ commandState: {
689
+ canUndo: false,
690
+ canRedo: false,
691
+ readOnly,
692
+ },
693
+ protectionSnapshot: {
694
+ hasDocumentProtection: false,
695
+ enforcementActive: false,
696
+ ranges: [],
697
+ enforcedRangeCount: 0,
698
+ preservedRangeCount: 0,
699
+ },
700
+ };
701
+ }
702
+
703
+ function createLoadingNavigationSnapshot(): DocumentNavigationSnapshot {
704
+ return {
705
+ pageCount: 0,
706
+ pages: [],
707
+ headings: [],
708
+ activePageIndex: 0,
709
+ activeSectionIndex: 0,
710
+ };
711
+ }
712
+
713
+ function createLoadingViewStateSnapshot(
714
+ snapshot: RuntimeRenderSnapshot,
715
+ seed: {
716
+ workspaceMode?: WorkspaceMode;
717
+ zoomLevel?: ZoomLevel;
718
+ },
719
+ viewMode: EditorViewMode,
720
+ ): EditorViewStateSnapshot {
721
+ return createEditorViewStateSnapshot(
722
+ createViewState({
723
+ viewMode,
724
+ workspaceMode: seed.workspaceMode,
725
+ zoomLevel: seed.zoomLevel,
726
+ }),
727
+ snapshot.activeStory,
728
+ snapshot.selection,
729
+ snapshot.surface,
730
+ snapshot.surface,
731
+ snapshot.pageLayout,
732
+ );
733
+ }
734
+
735
+ function createLoadingRuntimeBridge(input: {
736
+ snapshot: RuntimeRenderSnapshot;
737
+ sessionState: EditorSessionState;
738
+ viewState: EditorViewStateSnapshot;
739
+ navigation: DocumentNavigationSnapshot;
740
+ }): WordReviewEditorRuntime {
741
+ const emptyFieldSnapshot: FieldSnapshot = {
742
+ totalCount: 0,
743
+ supportedCount: 0,
744
+ preserveOnlyCount: 0,
745
+ fields: [],
746
+ };
747
+ const emptyFieldResult: UpdateFieldsResult = {
748
+ totalCount: 0,
749
+ updatedCount: 0,
750
+ preserveOnlyCount: 0,
751
+ };
752
+ const emptyTocResult: TocRefreshResult = {
753
+ entryCount: 0,
754
+ entries: [],
755
+ };
756
+ const emptySuggestions: SuggestionsSnapshot = {
757
+ totalCount: 0,
758
+ actionableSuggestionIds: [],
759
+ preserveOnlySuggestionIds: [],
760
+ activeSuggestionIds: [],
761
+ acceptedSuggestionIds: [],
762
+ rejectedSuggestionIds: [],
763
+ detachedSuggestionIds: [],
764
+ suggestions: [],
765
+ };
766
+
767
+ return {
768
+ subscribe: () => () => undefined,
769
+ subscribeToEvents: () => () => undefined,
770
+ emitBlockedCommand: () => undefined,
771
+ getRenderSnapshot: () => input.snapshot,
772
+ getCanonicalDocument: () => input.sessionState.canonicalDocument,
773
+ getSourcePackage: () => input.sessionState.sourcePackage,
774
+ replaceText: () => undefined,
775
+ applyActiveStoryTextCommand: () => undefined,
776
+ dispatch: () => undefined,
777
+ undo: () => undefined,
778
+ redo: () => undefined,
779
+ focus: () => undefined,
780
+ blur: () => undefined,
781
+ addComment: () => {
782
+ throw createLoadingBoundaryError(input.snapshot.documentId, "comment");
783
+ },
784
+ openComment: () => undefined,
785
+ resolveComment: () => undefined,
786
+ reopenComment: () => undefined,
787
+ addCommentReply: () => undefined,
788
+ editCommentBody: () => undefined,
789
+ acceptChange: () => undefined,
790
+ rejectChange: () => undefined,
791
+ acceptAllChanges: () => undefined,
792
+ rejectAllChanges: () => undefined,
793
+ openStory: () => false,
794
+ closeStory: () => undefined,
795
+ getActiveStory: () => input.snapshot.activeStory,
796
+ getViewState: () => input.viewState,
797
+ setViewMode: () => undefined,
798
+ setDocumentMode: () => undefined,
799
+ getProtectionSnapshot: () => input.snapshot.protectionSnapshot,
800
+ setWorkspaceMode: () => undefined,
801
+ setZoom: () => undefined,
802
+ getPageLayoutSnapshot: () => null,
803
+ getDocumentNavigationSnapshot: () => input.navigation,
804
+ getCurrentLocation: () => null,
805
+ getLocationForSelection: () => null,
806
+ getLocationForAnchor: () => null,
807
+ captureRestorePoint: () => null,
808
+ restoreToPoint: () => ({
809
+ status: "detached",
810
+ reasons: ["loading"],
811
+ }),
812
+ getOutlineSnapshot: () => ({
813
+ headings: [],
814
+ }),
815
+ getTocSnapshot: () => null,
816
+ getSections: () => [],
817
+ getSectionSnapshot: () => null,
818
+ describeEventImpact: () => ({
819
+ invalidate: ["render"],
820
+ staleTargets: ["none"],
821
+ changeKinds: ["content"],
822
+ }),
823
+ getFieldSnapshot: () => emptyFieldSnapshot,
824
+ updateFields: () => emptyFieldResult,
825
+ updateTableOfContents: () => emptyTocResult,
826
+ getSessionState: () => {
827
+ throw createLoadingBoundaryError(input.snapshot.documentId, "session");
828
+ },
829
+ getPersistedSnapshot: () => {
830
+ throw createLoadingBoundaryError(input.snapshot.documentId, "snapshot");
831
+ },
832
+ getCompatibilityReport: () => ({
833
+ reportVersion: "compatibility-report/1",
834
+ generatedAt: input.sessionState.updatedAt,
835
+ blockExport: input.snapshot.compatibility.blockExport,
836
+ featureEntries: input.snapshot.compatibility.featureEntries,
837
+ warnings: input.snapshot.warnings,
838
+ errors: input.snapshot.fatalError ? [input.snapshot.fatalError] : [],
839
+ }),
840
+ getWarnings: () => input.snapshot.warnings,
841
+ getSuggestionsSnapshot: () => emptySuggestions,
842
+ exportDocx: () =>
843
+ Promise.reject(createLoadingBoundaryError(input.snapshot.documentId, "export")),
844
+ setWorkflowOverlay: () => undefined,
845
+ clearWorkflowOverlay: () => undefined,
846
+ getWorkflowScopeSnapshot: () => null,
847
+ getInteractionGuardSnapshot: () => ({ effectiveMode: "edit", blockedReasons: [] }),
848
+ getWorkflowMarkupSnapshot: () => ({
849
+ totalCount: 0,
850
+ items: [],
851
+ highlights: [],
852
+ metadata: [],
853
+ comments: [],
854
+ revisions: [],
855
+ fields: [],
856
+ protectedRanges: [],
857
+ opaqueFragments: [],
858
+ }),
859
+ setWorkflowMetadataDefinitions: () => undefined,
860
+ clearWorkflowMetadataDefinitions: () => undefined,
861
+ setWorkflowMetadataEntries: () => undefined,
862
+ clearWorkflowMetadataEntries: () => undefined,
863
+ getWorkflowMetadataSnapshot: () => ({
864
+ definitions: [],
865
+ entries: [],
866
+ }),
867
+ setHostAnnotationOverlay: () => undefined,
868
+ clearHostAnnotationOverlay: () => undefined,
869
+ getHostAnnotationSnapshot: () => ({
870
+ totalCount: 0,
871
+ annotations: [],
872
+ }),
873
+ getWorkflowCandidateRanges: () => [],
874
+ replaceWorkflowMarkupText: () => undefined,
875
+ getDocumentTextStream: () => [],
876
+ getStoryTextStream: () => null,
877
+ getDocumentChunks: () => [],
878
+ getWorkflowChunks: () => [],
879
+ getReviewWorkSnapshot: () => ({
880
+ openCommentCount: 0,
881
+ actionableRevisionCount: 0,
882
+ items: [],
883
+ }),
884
+ getRuntimeContextAnalytics: () => null,
885
+ };
886
+ }
887
+
888
+ function createLoadingBoundaryError(
889
+ documentId: string,
890
+ target: "comment" | "session" | "snapshot" | "export",
891
+ ): EditorError {
892
+ return {
893
+ errorId: `${documentId}-loading-${target}`,
894
+ code: "internal_invariant",
895
+ message: "Editor is still loading.",
896
+ isFatal: false,
897
+ source: "runtime",
898
+ };
899
+ }
900
+
901
+ function deriveEditorViewMode(
902
+ readOnly: boolean,
903
+ reviewMode: WordReviewEditorProps["reviewMode"] = "review",
904
+ ): EditorViewMode {
905
+ if (readOnly) {
906
+ return "view";
907
+ }
908
+ return reviewMode === "editing" ? "editing" : "review";
909
+ }
910
+
911
+ function createErrorSnapshot(documentId: string, error: EditorError): RuntimeRenderSnapshot {
912
+ return {
913
+ ...createLoadingSnapshot(documentId, true),
914
+ isReady: true,
915
+ sessionId: `${documentId}-error`,
916
+ revisionToken: `${documentId}:error`,
917
+ compatibility: {
918
+ blockExport: true,
919
+ blockExportReasons: [error.message],
920
+ warningCount: 0,
921
+ errorCount: 1,
922
+ featureEntries: [],
923
+ },
924
+ fatalError: error,
925
+ };
926
+ }
927
+
928
+ export async function persistAndExport(input: {
929
+ hostAdapter?: EditorHostAdapter;
930
+ datastore?: EditorDatastoreAdapter;
931
+ documentId: string;
932
+ runtime: WordReviewEditorRuntime;
933
+ onError?: (error: EditorError) => void;
934
+ onEvent?: (event: WordReviewEditorEvent) => void;
935
+ options?: ExportDocxOptions;
936
+ lastSavedRevisionTokenRef: React.MutableRefObject<string | null>;
937
+ autosaveTimerRef: React.MutableRefObject<ReturnType<typeof setTimeout> | null>;
938
+ }): Promise<ExportResult> {
939
+ if (input.autosaveTimerRef.current) {
940
+ clearTimeout(input.autosaveTimerRef.current);
941
+ input.autosaveTimerRef.current = null;
942
+ }
943
+
944
+ const revisionToken = input.runtime.getRenderSnapshot().revisionToken;
945
+ const previousSavedRevisionToken = input.lastSavedRevisionTokenRef.current;
946
+ input.lastSavedRevisionTokenRef.current = revisionToken;
947
+
948
+ await persistSession({
949
+ hostAdapter: input.hostAdapter,
950
+ datastore: input.datastore,
951
+ documentId: input.documentId,
952
+ runtime: input.runtime,
953
+ isAutosave: false,
954
+ onError: input.onError,
955
+ onEvent: input.onEvent,
956
+ lastSavedRevisionTokenRef: input.lastSavedRevisionTokenRef,
957
+ }).catch((error) => {
958
+ input.lastSavedRevisionTokenRef.current = previousSavedRevisionToken;
959
+ throw error;
960
+ });
961
+
962
+ let result: ExportResult;
963
+ try {
964
+ result = await input.runtime.exportDocx(input.options);
965
+ } catch (error) {
966
+ const normalized = normalizeExportError(error, input.documentId, input.options);
967
+ input.onError?.(normalized);
968
+ emitEditorEvent({
969
+ hostAdapter: input.hostAdapter,
970
+ datastore: input.datastore,
971
+ onEvent: input.onEvent,
972
+ event: {
973
+ type: "error",
974
+ documentId: input.documentId,
975
+ error: normalized,
976
+ },
977
+ });
978
+ throw normalized;
979
+ }
980
+
981
+ const saveExport = input.hostAdapter?.saveExport ?? input.datastore?.saveExport;
982
+ const saveExportSource = input.hostAdapter?.saveExport ? "host" : "datastore";
983
+ if (!saveExport) {
984
+ result = downloadExportResult(result);
985
+ emitEditorEvent({
986
+ hostAdapter: input.hostAdapter,
987
+ datastore: input.datastore,
988
+ onEvent: input.onEvent,
989
+ event: {
990
+ type: "export_completed",
991
+ documentId: input.documentId,
992
+ result,
993
+ },
994
+ });
995
+ return result;
996
+ }
997
+
998
+ try {
999
+ const saveResult = await saveExport({
1000
+ documentId: input.documentId,
1001
+ result,
1002
+ });
1003
+ result = withExportDelivery(result, {
1004
+ mode: "persisted-by-host",
1005
+ savedAt: saveResult.savedAt,
1006
+ });
1007
+ } catch (error) {
1008
+ const normalized = normalizeStorageError(error, {
1009
+ message: "Export persisted bytes could not be stored.",
1010
+ source: saveExportSource,
1011
+ details: {
1012
+ operation: "saveExport",
1013
+ },
1014
+ });
1015
+ input.onError?.(normalized);
1016
+ emitEditorEvent({
1017
+ hostAdapter: input.hostAdapter,
1018
+ datastore: input.datastore,
1019
+ onEvent: input.onEvent,
1020
+ event: {
1021
+ type: "error",
1022
+ documentId: input.documentId,
1023
+ error: normalized,
1024
+ },
1025
+ });
1026
+ result = withExportDelivery(result, {
1027
+ mode: "exported-bytes-only",
1028
+ });
1029
+ }
1030
+
1031
+ emitEditorEvent({
1032
+ hostAdapter: input.hostAdapter,
1033
+ datastore: input.datastore,
1034
+ onEvent: input.onEvent,
1035
+ event: {
1036
+ type: "export_completed",
1037
+ documentId: input.documentId,
1038
+ result,
1039
+ },
1040
+ });
1041
+
1042
+ return result;
1043
+ }
1044
+
1045
+ export function rejectExportWhileLoading(input: {
1046
+ documentId: string;
1047
+ hostAdapter?: EditorHostAdapter;
1048
+ datastore?: EditorDatastoreAdapter;
1049
+ onError?: (error: EditorError) => void;
1050
+ onEvent?: (event: WordReviewEditorEvent) => void;
1051
+ }): Promise<never> {
1052
+ const error: EditorError = {
1053
+ errorId: "word-review-editor-loading-export",
1054
+ code: "internal_invariant",
1055
+ message: "WordReviewEditor is still loading and cannot export yet.",
1056
+ isFatal: false,
1057
+ source: "runtime",
1058
+ };
1059
+ input.onError?.(error);
1060
+ emitEditorEvent({
1061
+ hostAdapter: input.hostAdapter,
1062
+ datastore: input.datastore,
1063
+ onEvent: input.onEvent,
1064
+ event: {
1065
+ type: "error",
1066
+ documentId: input.documentId,
1067
+ error,
1068
+ },
1069
+ });
1070
+ return Promise.reject(error);
1071
+ }
1072
+
1073
+ export async function persistSession(input: {
1074
+ hostAdapter?: EditorHostAdapter;
1075
+ datastore?: EditorDatastoreAdapter;
1076
+ documentId: string;
1077
+ runtime: WordReviewEditorRuntime;
1078
+ isAutosave: boolean;
1079
+ onError?: (error: EditorError) => void;
1080
+ onEvent?: (event: WordReviewEditorEvent) => void;
1081
+ lastSavedRevisionTokenRef: React.MutableRefObject<string | null>;
1082
+ }): Promise<void> {
1083
+ const saveSession = input.hostAdapter?.saveSession;
1084
+ const saveSnapshot = input.datastore?.saveSnapshot;
1085
+ if (!saveSession && !saveSnapshot) {
1086
+ return;
1087
+ }
1088
+
1089
+ const sessionState = input.runtime.getSessionState();
1090
+ const snapshot = input.runtime.getPersistedSnapshot();
1091
+ const revisionToken = input.runtime.getRenderSnapshot().revisionToken;
1092
+
1093
+ if (input.isAutosave && input.lastSavedRevisionTokenRef.current === revisionToken) {
1094
+ return;
1095
+ }
1096
+
1097
+ if (input.isAutosave) {
1098
+ emitEditorEvent({
1099
+ hostAdapter: input.hostAdapter,
1100
+ datastore: input.datastore,
1101
+ onEvent: input.onEvent,
1102
+ event: {
1103
+ type: "autosave_state",
1104
+ documentId: input.documentId,
1105
+ state: {
1106
+ status: "saving",
1107
+ } satisfies AutosaveState,
1108
+ },
1109
+ });
1110
+ }
1111
+
1112
+ try {
1113
+ const result = saveSession
1114
+ ? await saveSession({
1115
+ documentId: input.documentId,
1116
+ sessionState,
1117
+ isAutosave: input.isAutosave,
1118
+ })
1119
+ : await saveSnapshot!({
1120
+ documentId: input.documentId,
1121
+ snapshot,
1122
+ isAutosave: input.isAutosave,
1123
+ });
1124
+ input.lastSavedRevisionTokenRef.current = revisionToken;
1125
+ emitEditorEvent({
1126
+ hostAdapter: input.hostAdapter,
1127
+ datastore: input.datastore,
1128
+ onEvent: input.onEvent,
1129
+ event: saveSession
1130
+ ? {
1131
+ type: "session_saved",
1132
+ documentId: input.documentId,
1133
+ sessionState: input.runtime.getSessionState(),
1134
+ savedAt: result.savedAt,
1135
+ isAutosave: input.isAutosave,
1136
+ }
1137
+ : {
1138
+ type: "snapshot_saved",
1139
+ documentId: input.documentId,
1140
+ snapshot: {
1141
+ ...snapshot,
1142
+ savedAt: result.savedAt,
1143
+ },
1144
+ isAutosave: input.isAutosave,
1145
+ },
1146
+ });
1147
+ if (input.isAutosave) {
1148
+ emitEditorEvent({
1149
+ hostAdapter: input.hostAdapter,
1150
+ datastore: input.datastore,
1151
+ onEvent: input.onEvent,
1152
+ event: {
1153
+ type: "autosave_state",
1154
+ documentId: input.documentId,
1155
+ state: {
1156
+ status: "saved",
1157
+ savedAt: result.savedAt,
1158
+ } satisfies AutosaveState,
1159
+ },
1160
+ });
1161
+ }
1162
+ } catch (error) {
1163
+ const normalized = normalizeStorageError(error, {
1164
+ message: input.isAutosave
1165
+ ? saveSession
1166
+ ? "Autosave failed while storing the editor session."
1167
+ : "Autosave failed while storing the editor snapshot."
1168
+ : saveSession
1169
+ ? "Session save failed while preparing the export checkpoint."
1170
+ : "Snapshot save failed while preparing the export checkpoint.",
1171
+ source: saveSession ? "host" : "datastore",
1172
+ details: {
1173
+ operation: saveSession ? "saveSession" : "saveSnapshot",
1174
+ isAutosave: input.isAutosave,
1175
+ },
1176
+ });
1177
+ input.onError?.(normalized);
1178
+ emitEditorEvent({
1179
+ hostAdapter: input.hostAdapter,
1180
+ datastore: input.datastore,
1181
+ onEvent: input.onEvent,
1182
+ event: {
1183
+ type: "error",
1184
+ documentId: input.documentId,
1185
+ error: normalized,
1186
+ },
1187
+ });
1188
+ if (input.isAutosave) {
1189
+ emitEditorEvent({
1190
+ hostAdapter: input.hostAdapter,
1191
+ datastore: input.datastore,
1192
+ onEvent: input.onEvent,
1193
+ event: {
1194
+ type: "autosave_state",
1195
+ documentId: input.documentId,
1196
+ state: {
1197
+ status: "error",
1198
+ error: normalized,
1199
+ } satisfies AutosaveState,
1200
+ },
1201
+ });
1202
+ }
1203
+ if (!input.isAutosave) {
1204
+ throw normalized;
1205
+ }
1206
+ }
1207
+ }
1208
+
1209
+ function emitEditorEvent(input: {
1210
+ hostAdapter?: EditorHostAdapter;
1211
+ datastore?: EditorDatastoreAdapter;
1212
+ onEvent?: (event: WordReviewEditorEvent) => void;
1213
+ event: WordReviewEditorEvent;
1214
+ }): void {
1215
+ input.onEvent?.(input.event);
1216
+ const logEvent = input.hostAdapter?.logEvent ?? input.datastore?.logEvent;
1217
+ logEvent?.({
1218
+ type: input.event.type,
1219
+ documentId: input.event.documentId,
1220
+ detail: summarizeEventDetail(input.event),
1221
+ });
1222
+ }
1223
+
1224
+ function summarizeEventDetail(
1225
+ event: WordReviewEditorEvent,
1226
+ ): Record<string, unknown> | undefined {
1227
+ switch (event.type) {
1228
+ case "dirty_changed":
1229
+ return { isDirty: event.isDirty };
1230
+ case "comment_added":
1231
+ case "comment_resolved":
1232
+ return { commentId: event.commentId };
1233
+ case "change_accepted":
1234
+ case "change_rejected":
1235
+ return { changeId: event.changeId };
1236
+ case "warning_added":
1237
+ return { warningId: event.warning.warningId, code: event.warning.code };
1238
+ case "warning_cleared":
1239
+ return { warningId: event.warningId, code: event.code };
1240
+ case "error":
1241
+ return { errorId: event.error.errorId, code: event.error.code };
1242
+ case "autosave_state":
1243
+ return { status: event.state.status };
1244
+ case "snapshot_saved":
1245
+ return { isAutosave: event.isAutosave, savedAt: event.snapshot.savedAt };
1246
+ case "session_saved":
1247
+ return { isAutosave: event.isAutosave, savedAt: event.savedAt };
1248
+ case "export_completed":
1249
+ return {
1250
+ fileName: event.result.fileName,
1251
+ deliveryMode: event.result.delivery?.mode,
1252
+ savedAt: event.result.delivery?.savedAt,
1253
+ };
1254
+ case "story_changed":
1255
+ return { activeStory: event.activeStory };
1256
+ case "selection_changed":
1257
+ return {
1258
+ anchor: event.selection.anchor,
1259
+ head: event.selection.head,
1260
+ };
1261
+ case "ready":
1262
+ return {
1263
+ source: event.source,
1264
+ blockExport: event.compatibility.blockExport,
1265
+ };
1266
+ }
1267
+ }
1268
+
1269
+ function createReadyEvent(
1270
+ runtime: Pick<WordReviewEditorRuntime, "getCompatibilityReport" | "getRenderSnapshot">,
1271
+ source: "docx" | "session" | "snapshot",
1272
+ ): Extract<WordReviewEditorEvent, { type: "ready" }> {
1273
+ const snapshot = runtime.getRenderSnapshot();
1274
+ return {
1275
+ type: "ready",
1276
+ documentId: snapshot.documentId,
1277
+ sessionId: snapshot.sessionId,
1278
+ source,
1279
+ stats: snapshot.documentStats,
1280
+ compatibility: runtime.getCompatibilityReport(),
1281
+ comments: snapshot.comments,
1282
+ trackedChanges: snapshot.trackedChanges,
1283
+ };
1284
+ }
1285
+
1286
+ function normalizeEditorError(error: unknown): EditorError {
1287
+ if (
1288
+ typeof error === "object" &&
1289
+ error !== null &&
1290
+ "errorId" in error &&
1291
+ "code" in error &&
1292
+ "message" in error
1293
+ ) {
1294
+ return error as EditorError;
1295
+ }
1296
+
1297
+ return {
1298
+ errorId: "word-review-editor-load",
1299
+ code: "internal_invariant",
1300
+ message: error instanceof Error ? error.message : "Unknown editor load failure.",
1301
+ isFatal: true,
1302
+ source: "runtime",
1303
+ };
1304
+ }
1305
+
1306
+ function guessSourceLabel(
1307
+ initialSourceLabel?: string,
1308
+ initialSessionState?: EditorSessionState,
1309
+ initialSnapshot?: PersistedEditorSnapshot,
1310
+ externalDocSource?: WordReviewEditorProps["externalDocSource"],
1311
+ ): string | undefined {
1312
+ return (
1313
+ externalDocSource?.sourceLabel ??
1314
+ (externalDocSource?.kind === "session"
1315
+ ? externalDocSource.sessionState.sourcePackage?.sourceLabel
1316
+ : undefined) ??
1317
+ (externalDocSource?.kind === "snapshot"
1318
+ ? externalDocSource.snapshot.sourcePackage?.sourceLabel
1319
+ : undefined) ??
1320
+ initialSourceLabel ??
1321
+ initialSessionState?.sourcePackage?.sourceLabel ??
1322
+ initialSessionState?.editorBuild ??
1323
+ initialSnapshot?.sourcePackage?.sourceLabel ??
1324
+ initialSnapshot?.editorBuild ??
1325
+ undefined
1326
+ );
1327
+ }
1328
+
1329
+ function createFallbackPersistedSnapshot(
1330
+ documentId: string,
1331
+ label = "Generated shell snapshot",
1332
+ ): PersistedEditorSnapshot {
1333
+ const docId = createCanonicalDocumentId(documentId);
1334
+ return {
1335
+ snapshotVersion: "persisted-editor-snapshot/2",
1336
+ schemaVersion: "cds/1.0.0",
1337
+ documentId,
1338
+ docId,
1339
+ createdAt: "1970-01-01T00:00:00.000Z",
1340
+ updatedAt: "1970-01-01T00:00:00.000Z",
1341
+ savedAt: "1970-01-01T00:00:00.000Z",
1342
+ editorBuild: label,
1343
+ canonicalDocument: {
1344
+ schemaVersion: "cds/1.0.0",
1345
+ docId,
1346
+ createdAt: "1970-01-01T00:00:00.000Z",
1347
+ updatedAt: "1970-01-01T00:00:00.000Z",
1348
+ metadata: { customProperties: {} },
1349
+ styles: { paragraphs: {}, characters: {}, tables: {} },
1350
+ numbering: { abstractDefinitions: {}, instances: {} },
1351
+ media: { items: {} },
1352
+ content: {
1353
+ type: "doc",
1354
+ children: [{ type: "paragraph", children: [] }],
1355
+ },
1356
+ review: { comments: {}, revisions: {} },
1357
+ preservation: { opaqueFragments: {}, packageParts: {} },
1358
+ diagnostics: { warnings: [], errors: [] },
1359
+ },
1360
+ compatibility: emptyCompatibilityReport(),
1361
+ warningLog: [],
1362
+ };
1363
+ }
1364
+
1365
+ function emptyCompatibilityReport(): CompatibilityReport {
1366
+ return {
1367
+ reportVersion: "compatibility-report/1",
1368
+ generatedAt: "1970-01-01T00:00:00.000Z",
1369
+ blockExport: false,
1370
+ featureEntries: [],
1371
+ warnings: [],
1372
+ errors: [],
1373
+ };
1374
+ }
1375
+
1376
+ function resolvePackageBackedExportSession(args: CreateRuntimeArgs): {
1377
+ session?: PackageBackedDocxSession;
1378
+ barrier?: SnapshotExportBarrier;
1379
+ } {
1380
+ const sourcePackage =
1381
+ args.source.initialSessionState?.sourcePackage ??
1382
+ args.source.initialSnapshot?.sourcePackage;
1383
+ if (!sourcePackage) {
1384
+ return {
1385
+ barrier: {
1386
+ reason: "missing_source_package_provenance",
1387
+ message:
1388
+ "DOCX export is blocked because this session was loaded without embedded source package provenance.",
1389
+ },
1390
+ };
1391
+ }
1392
+
1393
+ try {
1394
+ const bytes = decodePersistedSourcePackageBytes(sourcePackage);
1395
+ if (!hasValidPersistedSourcePackageDigest(sourcePackage, bytes)) {
1396
+ return {
1397
+ barrier: {
1398
+ reason: "invalid_source_package_provenance",
1399
+ message:
1400
+ "DOCX export is blocked because the embedded source package provenance failed its integrity check.",
1401
+ },
1402
+ };
1403
+ }
1404
+
1405
+ const session = loadDocxEditorSession({
1406
+ documentId: args.documentId,
1407
+ sourceLabel: sourcePackage.sourceLabel ?? args.source.sourceLabel,
1408
+ bytes,
1409
+ editorBuild:
1410
+ args.source.initialSessionState?.editorBuild ??
1411
+ args.source.initialSnapshot?.editorBuild ??
1412
+ "dev",
1413
+ });
1414
+ if (session.readOnly || session.fatalError) {
1415
+ return {
1416
+ barrier: {
1417
+ reason: "invalid_source_package_provenance",
1418
+ message:
1419
+ "DOCX export is blocked because the embedded source package provenance is no longer loadable as a valid package-backed session.",
1420
+ },
1421
+ };
1422
+ }
1423
+
1424
+ return { session };
1425
+ } catch {
1426
+ return {
1427
+ barrier: {
1428
+ reason: "invalid_source_package_provenance",
1429
+ message:
1430
+ "DOCX export is blocked because the embedded source package provenance could not be decoded into a package-backed session.",
1431
+ },
1432
+ };
1433
+ }
1434
+ }
1435
+
1436
+ function applySessionExportBarrier(
1437
+ sessionState: EditorSessionState,
1438
+ barrier: SnapshotExportBarrier,
1439
+ ): EditorSessionState {
1440
+ const featureEntryId = `feature:source-package-provenance:${barrier.reason}`;
1441
+ const featureEntries = sessionState.compatibility.featureEntries.some(
1442
+ (entry) => entry.featureEntryId === featureEntryId,
1443
+ )
1444
+ ? sessionState.compatibility.featureEntries
1445
+ : [
1446
+ ...sessionState.compatibility.featureEntries,
1447
+ {
1448
+ featureEntryId,
1449
+ featureKey: "source-package-provenance",
1450
+ featureClass: "unsupported-fatal" as const,
1451
+ message: barrier.message,
1452
+ details: {
1453
+ reason: barrier.reason,
1454
+ },
1455
+ },
1456
+ ];
1457
+
1458
+ return {
1459
+ ...sessionState,
1460
+ compatibility: {
1461
+ ...sessionState.compatibility,
1462
+ blockExport: true,
1463
+ featureEntries,
1464
+ },
1465
+ };
1466
+ }
1467
+
1468
+ function createSnapshotExportBlockedError(
1469
+ documentId: string,
1470
+ barrier: SnapshotExportBarrier,
1471
+ ): EditorError {
1472
+ return {
1473
+ errorId: `${documentId}:export:${barrier.reason}`,
1474
+ code: "export_failed",
1475
+ message: barrier.message,
1476
+ isFatal: false,
1477
+ source: "export",
1478
+ details: {
1479
+ reason: barrier.reason,
1480
+ },
1481
+ };
1482
+ }
1483
+
1484
+ function normalizeStorageError(
1485
+ error: unknown,
1486
+ fallback: {
1487
+ message: string;
1488
+ source: "host" | "datastore";
1489
+ details?: Record<string, unknown>;
1490
+ },
1491
+ ): EditorError {
1492
+ if (
1493
+ typeof error === "object" &&
1494
+ error !== null &&
1495
+ "errorId" in error &&
1496
+ "code" in error &&
1497
+ "message" in error
1498
+ ) {
1499
+ return error as EditorError;
1500
+ }
1501
+
1502
+ return {
1503
+ errorId:
1504
+ fallback.source === "host"
1505
+ ? "word-review-editor-host"
1506
+ : "word-review-editor-datastore",
1507
+ code: "datastore_failed",
1508
+ message: error instanceof Error ? error.message : fallback.message,
1509
+ isFatal: false,
1510
+ source: fallback.source === "host" ? "host" : "datastore",
1511
+ details: fallback.details,
1512
+ };
1513
+ }
1514
+
1515
+ function normalizeExportError(
1516
+ error: unknown,
1517
+ documentId: string,
1518
+ options?: ExportDocxOptions,
1519
+ ): EditorError {
1520
+ if (
1521
+ typeof error === "object" &&
1522
+ error !== null &&
1523
+ "errorId" in error &&
1524
+ "code" in error &&
1525
+ "message" in error
1526
+ ) {
1527
+ return error as EditorError;
1528
+ }
1529
+
1530
+ return {
1531
+ errorId: `${documentId}:export:failed`,
1532
+ code: "export_failed",
1533
+ message:
1534
+ error instanceof Error ? error.message : "DOCX export failed for an unknown reason.",
1535
+ isFatal: false,
1536
+ source: "export",
1537
+ details: {
1538
+ requestedOptions: options ?? {},
1539
+ },
1540
+ };
1541
+ }
1542
+
1543
+ function collapsedSelection(): RuntimeRenderSnapshot["selection"] {
1544
+ return {
1545
+ anchor: 0,
1546
+ head: 0,
1547
+ isCollapsed: true,
1548
+ activeRange: {
1549
+ kind: "range",
1550
+ from: 0,
1551
+ to: 0,
1552
+ assoc: {
1553
+ start: -1,
1554
+ end: 1,
1555
+ },
1556
+ },
1557
+ };
1558
+ }