@beyondwork/docx-react-component 1.0.28 → 1.0.29

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 (354) hide show
  1. package/dist/canonical-document-BLEbzL2J.d.cts +844 -0
  2. package/dist/canonical-document-BLEbzL2J.d.ts +844 -0
  3. package/dist/chunk-2FJS5GZM.js +763 -0
  4. package/dist/chunk-2FJS5GZM.js.map +1 -0
  5. package/{src/core/commands/section-layout-commands.ts → dist/chunk-2OQBZS3F.js} +106 -340
  6. package/dist/chunk-2OQBZS3F.js.map +1 -0
  7. package/dist/chunk-2S7W4KFO.js +127 -0
  8. package/dist/chunk-2S7W4KFO.js.map +1 -0
  9. package/dist/chunk-2TG72QSW.js +3874 -0
  10. package/dist/chunk-2TG72QSW.js.map +1 -0
  11. package/{src/core/commands/table-structure-commands.ts → dist/chunk-36QNIZBO.js} +126 -315
  12. package/dist/chunk-36QNIZBO.js.map +1 -0
  13. package/dist/chunk-4AQOYAW4.js +3069 -0
  14. package/dist/chunk-4AQOYAW4.js.map +1 -0
  15. package/dist/chunk-4D5EWJ3P.js +77 -0
  16. package/dist/chunk-4D5EWJ3P.js.map +1 -0
  17. package/dist/chunk-5FN54NDH.js +2257 -0
  18. package/dist/chunk-5FN54NDH.js.map +1 -0
  19. package/dist/chunk-BOYGQYRQ.js +7306 -0
  20. package/dist/chunk-BOYGQYRQ.js.map +1 -0
  21. package/dist/chunk-CN3XMECL.js +212 -0
  22. package/dist/chunk-CN3XMECL.js.map +1 -0
  23. package/dist/chunk-EBI3BX6U.js +164 -0
  24. package/dist/chunk-EBI3BX6U.js.map +1 -0
  25. package/dist/chunk-EILUG3VB.js +1275 -0
  26. package/dist/chunk-EILUG3VB.js.map +1 -0
  27. package/dist/chunk-FUDY333O.js +70 -0
  28. package/dist/chunk-FUDY333O.js.map +1 -0
  29. package/dist/chunk-GBVOWFIK.js +1237 -0
  30. package/dist/chunk-GBVOWFIK.js.map +1 -0
  31. package/dist/chunk-H4TQ3H3Y.js +262 -0
  32. package/dist/chunk-H4TQ3H3Y.js.map +1 -0
  33. package/{src/core/commands/style-commands.ts → dist/chunk-JGB3IXZO.js} +40 -113
  34. package/dist/chunk-JGB3IXZO.js.map +1 -0
  35. package/dist/chunk-KD2QRQPY.js +4342 -0
  36. package/dist/chunk-KD2QRQPY.js.map +1 -0
  37. package/dist/chunk-KLMXQVYK.js +369 -0
  38. package/dist/chunk-KLMXQVYK.js.map +1 -0
  39. package/dist/chunk-KZUG5KFQ.js +214 -0
  40. package/dist/chunk-KZUG5KFQ.js.map +1 -0
  41. package/{src/core/state/text-transaction.ts → dist/chunk-QDAQ4CJU.js} +79 -236
  42. package/dist/chunk-QDAQ4CJU.js.map +1 -0
  43. package/{src/legal/bookmarks.ts → dist/chunk-RMH72RZI.js} +44 -130
  44. package/dist/chunk-RMH72RZI.js.map +1 -0
  45. package/dist/chunk-SWKWQZXM.js +117 -0
  46. package/dist/chunk-SWKWQZXM.js.map +1 -0
  47. package/{src/core/commands/formatting-commands.ts → dist/chunk-TJBP2K4T.js} +196 -536
  48. package/dist/chunk-TJBP2K4T.js.map +1 -0
  49. package/dist/chunk-TLCEAQDQ.js +542 -0
  50. package/dist/chunk-TLCEAQDQ.js.map +1 -0
  51. package/{src/core/commands/text-commands.ts → dist/chunk-UZXBISGO.js} +86 -142
  52. package/dist/chunk-UZXBISGO.js.map +1 -0
  53. package/dist/chunk-WGBAKP3Q.js +3220 -0
  54. package/dist/chunk-WGBAKP3Q.js.map +1 -0
  55. package/dist/compare/index.cjs +5475 -0
  56. package/dist/compare/index.cjs.map +1 -0
  57. package/dist/compare/index.d.cts +114 -0
  58. package/dist/compare/index.d.ts +114 -0
  59. package/dist/compare/index.js +731 -0
  60. package/dist/compare/index.js.map +1 -0
  61. package/dist/core/commands/formatting-commands.cjs +828 -0
  62. package/dist/core/commands/formatting-commands.cjs.map +1 -0
  63. package/dist/core/commands/formatting-commands.d.cts +63 -0
  64. package/dist/core/commands/formatting-commands.d.ts +63 -0
  65. package/dist/core/commands/formatting-commands.js +37 -0
  66. package/dist/core/commands/formatting-commands.js.map +1 -0
  67. package/dist/core/commands/image-commands.cjs +2023 -0
  68. package/dist/core/commands/image-commands.cjs.map +1 -0
  69. package/dist/core/commands/image-commands.d.cts +58 -0
  70. package/dist/core/commands/image-commands.d.ts +58 -0
  71. package/dist/core/commands/image-commands.js +18 -0
  72. package/dist/core/commands/image-commands.js.map +1 -0
  73. package/dist/core/commands/section-layout-commands.cjs +477 -0
  74. package/dist/core/commands/section-layout-commands.cjs.map +1 -0
  75. package/dist/core/commands/section-layout-commands.d.cts +62 -0
  76. package/dist/core/commands/section-layout-commands.d.ts +62 -0
  77. package/dist/core/commands/section-layout-commands.js +21 -0
  78. package/dist/core/commands/section-layout-commands.js.map +1 -0
  79. package/dist/core/commands/style-commands.cjs +214 -0
  80. package/dist/core/commands/style-commands.cjs.map +1 -0
  81. package/dist/core/commands/style-commands.d.cts +13 -0
  82. package/dist/core/commands/style-commands.d.ts +13 -0
  83. package/dist/core/commands/style-commands.js +9 -0
  84. package/dist/core/commands/style-commands.js.map +1 -0
  85. package/dist/core/commands/table-structure-commands.cjs +1883 -0
  86. package/dist/core/commands/table-structure-commands.cjs.map +1 -0
  87. package/dist/core/commands/table-structure-commands.d.cts +59 -0
  88. package/dist/core/commands/table-structure-commands.d.ts +59 -0
  89. package/dist/core/commands/table-structure-commands.js +12 -0
  90. package/dist/core/commands/table-structure-commands.js.map +1 -0
  91. package/dist/core/commands/text-commands.cjs +2391 -0
  92. package/dist/core/commands/text-commands.cjs.map +1 -0
  93. package/dist/core/commands/text-commands.d.cts +24 -0
  94. package/dist/core/commands/text-commands.d.ts +24 -0
  95. package/dist/core/commands/text-commands.js +28 -0
  96. package/dist/core/commands/text-commands.js.map +1 -0
  97. package/dist/core/selection/mapping.cjs +200 -0
  98. package/dist/core/selection/mapping.cjs.map +1 -0
  99. package/dist/core/selection/mapping.d.cts +2 -0
  100. package/dist/core/selection/mapping.d.ts +2 -0
  101. package/dist/core/selection/mapping.js +31 -0
  102. package/dist/core/selection/mapping.js.map +1 -0
  103. package/dist/core/state/editor-state.cjs +2278 -0
  104. package/dist/core/state/editor-state.cjs.map +1 -0
  105. package/dist/core/state/editor-state.d.cts +2 -0
  106. package/dist/core/state/editor-state.d.ts +2 -0
  107. package/dist/core/state/editor-state.js +26 -0
  108. package/dist/core/state/editor-state.js.map +1 -0
  109. package/dist/index.cjs +38553 -0
  110. package/dist/index.cjs.map +1 -0
  111. package/dist/index.d.cts +15 -0
  112. package/dist/index.d.ts +15 -0
  113. package/dist/index.js +7856 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/io/docx-session.cjs +16236 -0
  116. package/dist/io/docx-session.cjs.map +1 -0
  117. package/dist/io/docx-session.d.cts +21 -0
  118. package/dist/io/docx-session.d.ts +21 -0
  119. package/dist/io/docx-session.js +18 -0
  120. package/dist/io/docx-session.js.map +1 -0
  121. package/dist/legal/index.cjs +3900 -0
  122. package/dist/legal/index.cjs.map +1 -0
  123. package/dist/legal/index.d.cts +86 -0
  124. package/dist/legal/index.d.ts +86 -0
  125. package/dist/legal/index.js +616 -0
  126. package/dist/legal/index.js.map +1 -0
  127. package/dist/public-types-7ZL_94cz.d.ts +1573 -0
  128. package/dist/public-types-CeMaDueh.d.cts +1573 -0
  129. package/dist/public-types.cjs +19 -0
  130. package/dist/public-types.cjs.map +1 -0
  131. package/dist/public-types.d.cts +2 -0
  132. package/dist/public-types.d.ts +2 -0
  133. package/dist/public-types.js +1 -0
  134. package/dist/public-types.js.map +1 -0
  135. package/dist/runtime/document-runtime.cjs +11140 -0
  136. package/dist/runtime/document-runtime.cjs.map +1 -0
  137. package/dist/runtime/document-runtime.d.cts +231 -0
  138. package/dist/runtime/document-runtime.d.ts +231 -0
  139. package/dist/runtime/document-runtime.js +21 -0
  140. package/dist/runtime/document-runtime.js.map +1 -0
  141. package/dist/structural-helpers-CilgOVhh.d.cts +10 -0
  142. package/dist/structural-helpers-q0Gd-eBN.d.ts +10 -0
  143. package/dist/ui-tailwind/editor-surface/search-plugin.cjs +313 -0
  144. package/dist/ui-tailwind/editor-surface/search-plugin.cjs.map +1 -0
  145. package/dist/ui-tailwind/editor-surface/search-plugin.d.cts +67 -0
  146. package/dist/ui-tailwind/editor-surface/search-plugin.d.ts +67 -0
  147. package/dist/ui-tailwind/editor-surface/search-plugin.js +23 -0
  148. package/dist/ui-tailwind/editor-surface/search-plugin.js.map +1 -0
  149. package/dist/ui-tailwind/index.cjs +4833 -0
  150. package/dist/ui-tailwind/index.cjs.map +1 -0
  151. package/dist/ui-tailwind/index.d.cts +617 -0
  152. package/dist/ui-tailwind/index.d.ts +617 -0
  153. package/dist/ui-tailwind/index.js +575 -0
  154. package/dist/ui-tailwind/index.js.map +1 -0
  155. package/package.json +61 -41
  156. package/src/README.md +0 -85
  157. package/src/api/README.md +0 -26
  158. package/src/api/public-types.ts +0 -1421
  159. package/src/api/session-state.ts +0 -60
  160. package/src/compare/diff-engine.ts +0 -623
  161. package/src/compare/export-redlines.ts +0 -280
  162. package/src/compare/index.ts +0 -25
  163. package/src/compare/snapshot.ts +0 -97
  164. package/src/component-inventory.md +0 -99
  165. package/src/core/README.md +0 -10
  166. package/src/core/commands/README.md +0 -3
  167. package/src/core/commands/image-commands.ts +0 -373
  168. package/src/core/commands/index.ts +0 -1757
  169. package/src/core/commands/list-commands.ts +0 -565
  170. package/src/core/commands/paragraph-layout-commands.ts +0 -339
  171. package/src/core/commands/review-commands.ts +0 -108
  172. package/src/core/commands/structural-helpers.ts +0 -309
  173. package/src/core/schema/README.md +0 -3
  174. package/src/core/schema/text-schema.ts +0 -516
  175. package/src/core/search/search-text.ts +0 -357
  176. package/src/core/selection/README.md +0 -3
  177. package/src/core/selection/mapping.ts +0 -289
  178. package/src/core/selection/review-anchors.ts +0 -183
  179. package/src/core/state/README.md +0 -3
  180. package/src/core/state/editor-state.ts +0 -892
  181. package/src/formats/xlsx/io/parse-shared-strings.ts +0 -41
  182. package/src/formats/xlsx/io/parse-sheet.ts +0 -459
  183. package/src/formats/xlsx/io/parse-styles.ts +0 -59
  184. package/src/formats/xlsx/io/parse-workbook.ts +0 -75
  185. package/src/formats/xlsx/io/serialize-shared-strings.ts +0 -72
  186. package/src/formats/xlsx/io/serialize-sheet.ts +0 -333
  187. package/src/formats/xlsx/io/serialize-styles.ts +0 -98
  188. package/src/formats/xlsx/io/serialize-workbook.ts +0 -429
  189. package/src/formats/xlsx/io/xlsx-session.ts +0 -314
  190. package/src/formats/xlsx/model/cell.ts +0 -189
  191. package/src/formats/xlsx/model/sheet.ts +0 -326
  192. package/src/formats/xlsx/model/styles.ts +0 -118
  193. package/src/formats/xlsx/model/workbook.ts +0 -453
  194. package/src/formats/xlsx/runtime/cell-commands.ts +0 -567
  195. package/src/formats/xlsx/runtime/sheet-commands.ts +0 -206
  196. package/src/formats/xlsx/runtime/workbook-runtime.ts +0 -177
  197. package/src/formats/xlsx/runtime/workbook-transaction.ts +0 -822
  198. package/src/index.ts +0 -101
  199. package/src/io/README.md +0 -10
  200. package/src/io/docx-session.ts +0 -2882
  201. package/src/io/export/README.md +0 -3
  202. package/src/io/export/export-session.ts +0 -220
  203. package/src/io/export/minimal-docx.ts +0 -115
  204. package/src/io/export/reattach-preserved-parts.ts +0 -54
  205. package/src/io/export/serialize-comments.ts +0 -947
  206. package/src/io/export/serialize-footnotes.ts +0 -399
  207. package/src/io/export/serialize-headers-footers.ts +0 -372
  208. package/src/io/export/serialize-main-document.ts +0 -1376
  209. package/src/io/export/serialize-numbering.ts +0 -118
  210. package/src/io/export/serialize-revisions.ts +0 -389
  211. package/src/io/export/serialize-runtime-revisions.ts +0 -269
  212. package/src/io/export/serialize-tables.ts +0 -174
  213. package/src/io/export/split-review-boundaries.ts +0 -356
  214. package/src/io/normalize/README.md +0 -3
  215. package/src/io/normalize/normalize-text.ts +0 -639
  216. package/src/io/ooxml/README.md +0 -3
  217. package/src/io/ooxml/highlight-colors.ts +0 -39
  218. package/src/io/ooxml/numbering-sentinels.ts +0 -44
  219. package/src/io/ooxml/parse-comments.ts +0 -846
  220. package/src/io/ooxml/parse-complex-content.ts +0 -287
  221. package/src/io/ooxml/parse-fields.ts +0 -834
  222. package/src/io/ooxml/parse-footnotes.ts +0 -896
  223. package/src/io/ooxml/parse-headers-footers.ts +0 -1169
  224. package/src/io/ooxml/parse-inline-media.ts +0 -461
  225. package/src/io/ooxml/parse-main-document.ts +0 -2877
  226. package/src/io/ooxml/parse-numbering.ts +0 -432
  227. package/src/io/ooxml/parse-revisions.ts +0 -931
  228. package/src/io/ooxml/parse-settings.ts +0 -184
  229. package/src/io/ooxml/parse-shapes.ts +0 -296
  230. package/src/io/ooxml/parse-styles.ts +0 -463
  231. package/src/io/ooxml/parse-tables.ts +0 -618
  232. package/src/io/ooxml/parse-theme.ts +0 -346
  233. package/src/io/ooxml/part-manifest.ts +0 -136
  234. package/src/io/ooxml/revision-boundaries.ts +0 -351
  235. package/src/io/opc/README.md +0 -3
  236. package/src/io/opc/corrupt-package.ts +0 -166
  237. package/src/io/opc/docx-package.ts +0 -74
  238. package/src/io/opc/package-reader.ts +0 -325
  239. package/src/io/opc/package-writer.ts +0 -273
  240. package/src/io/source-package-provenance.ts +0 -241
  241. package/src/legal/cross-references.ts +0 -414
  242. package/src/legal/defined-terms.ts +0 -203
  243. package/src/legal/index.ts +0 -32
  244. package/src/legal/signature-blocks.ts +0 -259
  245. package/src/model/README.md +0 -3
  246. package/src/model/canonical-document.ts +0 -2632
  247. package/src/model/cds-1.0.0.ts +0 -212
  248. package/src/model/snapshot.ts +0 -649
  249. package/src/preservation/README.md +0 -3
  250. package/src/preservation/markup-compatibility.ts +0 -48
  251. package/src/preservation/opaque-fragment-store.ts +0 -89
  252. package/src/preservation/opaque-region.ts +0 -233
  253. package/src/preservation/package-preservation.ts +0 -113
  254. package/src/preservation/preserved-part-manifest.ts +0 -56
  255. package/src/preservation/relationship-retention.ts +0 -57
  256. package/src/preservation/store.ts +0 -185
  257. package/src/review/README.md +0 -16
  258. package/src/review/store/README.md +0 -3
  259. package/src/review/store/comment-anchors.ts +0 -70
  260. package/src/review/store/comment-remapping.ts +0 -154
  261. package/src/review/store/comment-store.ts +0 -331
  262. package/src/review/store/comment-thread.ts +0 -109
  263. package/src/review/store/revision-actions.ts +0 -394
  264. package/src/review/store/revision-store.ts +0 -312
  265. package/src/review/store/revision-types.ts +0 -171
  266. package/src/review/store/runtime-comment-store.ts +0 -43
  267. package/src/runtime/README.md +0 -3
  268. package/src/runtime/ai-action-policy.ts +0 -764
  269. package/src/runtime/document-layout.ts +0 -332
  270. package/src/runtime/document-navigation.ts +0 -603
  271. package/src/runtime/document-runtime.ts +0 -3159
  272. package/src/runtime/document-search.ts +0 -145
  273. package/src/runtime/numbering-prefix.ts +0 -216
  274. package/src/runtime/page-layout-estimation.ts +0 -212
  275. package/src/runtime/read-only-diagnostics-runtime.ts +0 -241
  276. package/src/runtime/review-runtime.ts +0 -44
  277. package/src/runtime/revision-runtime.ts +0 -107
  278. package/src/runtime/session-capabilities.ts +0 -192
  279. package/src/runtime/story-context.ts +0 -164
  280. package/src/runtime/story-targeting.ts +0 -162
  281. package/src/runtime/surface-projection.ts +0 -1357
  282. package/src/runtime/table-commands.ts +0 -173
  283. package/src/runtime/table-schema.ts +0 -309
  284. package/src/runtime/view-state.ts +0 -477
  285. package/src/runtime/virtualized-rendering.ts +0 -258
  286. package/src/runtime/workflow-markup.ts +0 -353
  287. package/src/ui/README.md +0 -30
  288. package/src/ui/WordReviewEditor.tsx +0 -4086
  289. package/src/ui/browser-export.ts +0 -52
  290. package/src/ui/comments/README.md +0 -3
  291. package/src/ui/compatibility/README.md +0 -3
  292. package/src/ui/editor-command-bag.ts +0 -120
  293. package/src/ui/editor-runtime-boundary.ts +0 -1457
  294. package/src/ui/editor-shell-view.tsx +0 -142
  295. package/src/ui/editor-surface/README.md +0 -3
  296. package/src/ui/editor-surface-controller.tsx +0 -61
  297. package/src/ui/headless/comment-decoration-model.ts +0 -124
  298. package/src/ui/headless/preserve-editor-selection.ts +0 -5
  299. package/src/ui/headless/revision-decoration-model.ts +0 -128
  300. package/src/ui/headless/selection-helpers.ts +0 -54
  301. package/src/ui/headless/selection-toolbar-model.ts +0 -34
  302. package/src/ui/headless/use-editor-keyboard.ts +0 -103
  303. package/src/ui/review/README.md +0 -3
  304. package/src/ui/runtime-snapshot-selectors.ts +0 -197
  305. package/src/ui/shared/revision-filters.ts +0 -31
  306. package/src/ui/status/README.md +0 -3
  307. package/src/ui/theme/README.md +0 -3
  308. package/src/ui/toolbar/README.md +0 -3
  309. package/src/ui/workflow-surface-blocked-rails.ts +0 -94
  310. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +0 -64
  311. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +0 -129
  312. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +0 -114
  313. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +0 -34
  314. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +0 -386
  315. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +0 -186
  316. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +0 -139
  317. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +0 -128
  318. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +0 -58
  319. package/src/ui-tailwind/chrome/use-before-unload.ts +0 -20
  320. package/src/ui-tailwind/editor-surface/perf-probe.ts +0 -179
  321. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +0 -184
  322. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +0 -31
  323. package/src/ui-tailwind/editor-surface/pm-decorations.ts +0 -427
  324. package/src/ui-tailwind/editor-surface/pm-position-map.ts +0 -123
  325. package/src/ui-tailwind/editor-surface/pm-schema.ts +0 -876
  326. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +0 -504
  327. package/src/ui-tailwind/editor-surface/search-plugin.ts +0 -168
  328. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +0 -61
  329. package/src/ui-tailwind/editor-surface/tw-caret.tsx +0 -12
  330. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +0 -150
  331. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +0 -129
  332. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +0 -58
  333. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +0 -151
  334. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +0 -944
  335. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +0 -111
  336. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -436
  337. package/src/ui-tailwind/index.ts +0 -62
  338. package/src/ui-tailwind/page-chrome-model.ts +0 -27
  339. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +0 -406
  340. package/src/ui-tailwind/review/tw-health-panel.tsx +0 -149
  341. package/src/ui-tailwind/review/tw-review-rail.tsx +0 -120
  342. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +0 -164
  343. package/src/ui-tailwind/status/tw-status-bar.tsx +0 -61
  344. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +0 -52
  345. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +0 -1064
  346. package/src/ui-tailwind/tw-review-workspace.tsx +0 -1417
  347. package/src/validation/README.md +0 -3
  348. package/src/validation/compatibility-engine.ts +0 -634
  349. package/src/validation/compatibility-report.ts +0 -161
  350. package/src/validation/diagnostics.ts +0 -204
  351. package/src/validation/docx-comment-proof.ts +0 -707
  352. package/src/validation/import-diagnostics.ts +0 -128
  353. package/src/validation/low-priority-word-surfaces.ts +0 -373
  354. /package/{src → dist}/ui-tailwind/theme/editor-theme.css +0 -0
@@ -1,2632 +0,0 @@
1
- import {
2
- CDS_SCHEMA_VERSION,
3
- type ISO8601DateTime,
4
- type ModelValidationIssue,
5
- type UUID,
6
- asPlainObject,
7
- assertValid,
8
- expectExactString,
9
- expectIso8601UtcTimestamp,
10
- expectString,
11
- expectStringAllowEmpty,
12
- expectUuid,
13
- stableStringify,
14
- } from "./cds-1.0.0.ts";
15
-
16
- const CANONICAL_DOCUMENT_TOP_LEVEL_KEYS = [
17
- "schemaVersion",
18
- "docId",
19
- "createdAt",
20
- "updatedAt",
21
- "metadata",
22
- "styles",
23
- "numbering",
24
- "media",
25
- "content",
26
- "review",
27
- "preservation",
28
- "diagnostics",
29
- ] as const;
30
-
31
- const CANONICAL_DOCUMENT_OPTIONAL_KEYS = ["subParts", "fieldRegistry"] as const;
32
-
33
- const ID_PATTERNS = {
34
- styleId: /^[A-Za-z_][A-Za-z0-9._-]{0,127}$/,
35
- abstractNumberingId: /^abstract-num:[A-Za-z0-9._-]{1,120}$/,
36
- numberingInstanceId: /^num:[A-Za-z0-9._-]{1,120}$/,
37
- mediaId: /^media:[A-Za-z0-9._/-]{1,120}$/,
38
- commentId:
39
- /^(?:comment:[A-Za-z0-9._-]{1,120}|comment-[A-Za-z0-9._-]{1,120}|[0-9]{1,18})$/,
40
- revisionId:
41
- /^(?:revision:[A-Za-z0-9._-]{1,120}|change-[A-Za-z0-9._-]{1,120})$/,
42
- fragmentId: /^fragment:[A-Za-z0-9._-]{1,120}$/,
43
- warningId: /^warning:[A-Za-z0-9._:-]{1,120}$/,
44
- diagnosticId: /^diagnostic:[A-Za-z0-9._-]{1,120}$/,
45
- packagePartName: /^\/[A-Za-z0-9_.\-\/]+\.[A-Za-z0-9]+$/,
46
- relationshipId: /^rId[A-Za-z0-9._-]{1,120}$/,
47
- } as const;
48
-
49
- type StableIdDomain = keyof typeof ID_PATTERNS;
50
-
51
- export interface CanonicalDocument {
52
- schemaVersion: typeof CDS_SCHEMA_VERSION;
53
- docId: UUID;
54
- createdAt: ISO8601DateTime;
55
- updatedAt: ISO8601DateTime;
56
- metadata: DocumentMetadata;
57
- styles: StylesCatalog;
58
- numbering: NumberingCatalog;
59
- media: MediaCatalog;
60
- content: DocumentRootNode;
61
- review: ReviewStore;
62
- preservation: PreservationStore;
63
- diagnostics: DiagnosticStore;
64
- subParts?: SubPartsCatalog;
65
- /** Package-backed field registry for supported field families. */
66
- fieldRegistry?: FieldRegistry;
67
- }
68
-
69
- export interface DocumentMetadata {
70
- title?: string;
71
- subject?: string;
72
- description?: string;
73
- language?: string;
74
- keywords?: string[];
75
- category?: string;
76
- importMode?: string;
77
- customProperties: Record<string, string>;
78
- }
79
-
80
- export interface StylesCatalog {
81
- paragraphs: Record<string, ParagraphStyleDefinition>;
82
- characters: Record<string, CharacterStyleDefinition>;
83
- tables: Record<string, TableStyleDefinition>;
84
- latentStyles?: Record<string, LatentStyleDefinition>;
85
- fromPackage?: boolean;
86
- }
87
-
88
- export interface ParagraphStyleDefinition {
89
- styleId: string;
90
- basedOn?: string;
91
- nextStyle?: string;
92
- outlineLevel?: number;
93
- displayName: string;
94
- kind: "paragraph";
95
- isDefault: boolean;
96
- }
97
-
98
- export interface CharacterStyleDefinition {
99
- styleId: string;
100
- basedOn?: string;
101
- displayName: string;
102
- kind: "character";
103
- isDefault: boolean;
104
- }
105
-
106
- export interface TableStyleDefinition {
107
- styleId: string;
108
- basedOn?: string;
109
- displayName: string;
110
- kind: "table";
111
- isDefault: boolean;
112
- }
113
-
114
- export interface LatentStyleDefinition {
115
- name: string;
116
- locked?: boolean;
117
- semiHidden?: boolean;
118
- unhideWhenUsed?: boolean;
119
- qFormat?: boolean;
120
- uiPriority?: number;
121
- }
122
-
123
- export interface NumberingCatalog {
124
- abstractDefinitions: Record<string, AbstractNumberingDefinition>;
125
- instances: Record<string, NumberingInstance>;
126
- }
127
-
128
- export interface AbstractNumberingDefinition {
129
- abstractNumberingId: string;
130
- levels: NumberingLevelDefinition[];
131
- }
132
-
133
- export interface NumberingLevelDefinition {
134
- level: number;
135
- format: string;
136
- text: string;
137
- startAt?: number;
138
- paragraphStyleId?: string;
139
- isLegalNumbering?: boolean;
140
- suffix?: "tab" | "space" | "nothing";
141
- }
142
-
143
- export interface NumberingInstance {
144
- numberingInstanceId: string;
145
- abstractNumberingId: string;
146
- overrides: NumberingLevelOverride[];
147
- }
148
-
149
- export interface NumberingLevelOverride {
150
- level: number;
151
- startAt?: number;
152
- }
153
-
154
- export interface MediaCatalog {
155
- items: Record<string, MediaItem>;
156
- }
157
-
158
- export interface MediaItem {
159
- mediaId: string;
160
- contentType: string;
161
- filename: string;
162
- relationshipId?: string;
163
- packagePartName: string;
164
- altText?: string;
165
- display?: "inline" | "floating";
166
- widthEmu?: number;
167
- heightEmu?: number;
168
- }
169
-
170
- // ---- Sub-part canonical types ----
171
-
172
- export type HeaderFooterVariant = "default" | "first" | "even";
173
-
174
- export interface HeaderDocument {
175
- variant: HeaderFooterVariant;
176
- partPath: string;
177
- relationshipId: string;
178
- blocks: BlockNode[];
179
- sectionIndex?: number;
180
- }
181
-
182
- export interface FooterDocument {
183
- variant: HeaderFooterVariant;
184
- partPath: string;
185
- relationshipId: string;
186
- blocks: BlockNode[];
187
- sectionIndex?: number;
188
- }
189
-
190
- export interface FootnoteDefinition {
191
- noteId: string;
192
- kind: "footnote" | "endnote";
193
- blocks: BlockNode[];
194
- }
195
-
196
- export interface FootnoteCollection {
197
- footnotes: Record<string, FootnoteDefinition>;
198
- endnotes: Record<string, FootnoteDefinition>;
199
- }
200
-
201
- export interface ThemeColorScheme {
202
- name: string;
203
- colors: Record<string, string>;
204
- }
205
-
206
- export interface ThemeFontScheme {
207
- name: string;
208
- majorFont?: string;
209
- minorFont?: string;
210
- }
211
-
212
- export interface ThemeDefinition {
213
- name?: string;
214
- colorScheme?: ThemeColorScheme;
215
- fontScheme?: ThemeFontScheme;
216
- }
217
-
218
- export interface DocumentSettings {
219
- evenAndOddHeaders?: boolean;
220
- zoomLevel?: "pageWidth" | "onePage" | number;
221
- }
222
-
223
- export interface SubPartsCatalog {
224
- headers: HeaderDocument[];
225
- footers: FooterDocument[];
226
- footnoteCollection?: FootnoteCollection;
227
- theme?: ThemeDefinition;
228
- finalSectionProperties?: SectionProperties;
229
- resolvedTheme?: ResolvedTheme;
230
- settings?: DocumentSettings;
231
- }
232
-
233
- export interface ResolvedTheme {
234
- colors: Record<string, string>;
235
- majorFont?: string;
236
- minorFont?: string;
237
- }
238
-
239
- // ---- Inline footnote reference node ----
240
-
241
- export interface FootnoteRefNode {
242
- type: "footnote_ref";
243
- noteId: string;
244
- noteKind: "footnote" | "endnote";
245
- }
246
-
247
- export type DocumentNode =
248
- | DocumentRootNode
249
- | ParagraphNode
250
- | TableNode
251
- | TableRowNode
252
- | TableCellNode
253
- | SdtNode
254
- | CustomXmlNode
255
- | AltChunkNode
256
- | TextNode
257
- | HardBreakNode
258
- | TabNode
259
- | ColumnBreakNode
260
- | SymbolNode
261
- | HyperlinkNode
262
- | ImageNode
263
- | FieldNode
264
- | BookmarkStartNode
265
- | BookmarkEndNode
266
- | SectionBreakNode
267
- | OpaqueInlineNode
268
- | OpaqueBlockNode
269
- | FootnoteRefNode
270
- | ChartPreviewNode
271
- | SmartArtPreviewNode
272
- | ShapeNode
273
- | WordArtNode
274
- | VmlShapeNode;
275
-
276
- export interface DocumentRootNode {
277
- type: "doc";
278
- children: BlockNode[];
279
- }
280
-
281
- export type BlockNode =
282
- | ParagraphNode
283
- | TableNode
284
- | SdtNode
285
- | CustomXmlNode
286
- | AltChunkNode
287
- | SectionBreakNode
288
- | OpaqueBlockNode;
289
-
290
- export interface ParagraphSpacing {
291
- before?: number;
292
- after?: number;
293
- line?: number;
294
- lineRule?: "auto" | "exact" | "atLeast";
295
- }
296
-
297
- export interface ParagraphIndentation {
298
- left?: number;
299
- right?: number;
300
- firstLine?: number;
301
- hanging?: number;
302
- }
303
-
304
- export interface TabStop {
305
- position: number;
306
- align: "left" | "center" | "right" | "decimal" | "bar" | "clear";
307
- leader?: "none" | "dot" | "hyphen" | "underscore" | "heavy" | "middleDot";
308
- }
309
-
310
- export interface ParagraphBorders {
311
- top?: BorderSpec;
312
- left?: BorderSpec;
313
- bottom?: BorderSpec;
314
- right?: BorderSpec;
315
- bar?: BorderSpec;
316
- between?: BorderSpec;
317
- }
318
-
319
- export interface ParagraphShading {
320
- fill?: string;
321
- color?: string;
322
- val?: string;
323
- }
324
-
325
- export interface ParagraphNode {
326
- type: "paragraph";
327
- styleId?: string;
328
- numbering?: {
329
- numberingInstanceId: string;
330
- level: number;
331
- };
332
- alignment?: "left" | "center" | "right" | "both" | "distribute";
333
- spacing?: ParagraphSpacing;
334
- contextualSpacing?: boolean;
335
- indentation?: ParagraphIndentation;
336
- tabStops?: TabStop[];
337
- keepNext?: boolean;
338
- keepLines?: boolean;
339
- outlineLevel?: number;
340
- pageBreakBefore?: boolean;
341
- widowControl?: boolean;
342
- borders?: ParagraphBorders;
343
- shading?: ParagraphShading;
344
- bidi?: boolean;
345
- suppressLineNumbers?: boolean;
346
- cnfStyle?: string;
347
- children: InlineNode[];
348
- }
349
-
350
- export interface BorderSpec {
351
- value?: string;
352
- size?: number;
353
- space?: number;
354
- color?: string;
355
- }
356
-
357
- export interface TableBorders {
358
- top?: BorderSpec;
359
- left?: BorderSpec;
360
- bottom?: BorderSpec;
361
- right?: BorderSpec;
362
- insideH?: BorderSpec;
363
- insideV?: BorderSpec;
364
- }
365
-
366
- export interface TableCellBorders {
367
- top?: BorderSpec;
368
- left?: BorderSpec;
369
- bottom?: BorderSpec;
370
- right?: BorderSpec;
371
- insideH?: BorderSpec;
372
- insideV?: BorderSpec;
373
- }
374
-
375
- export interface TableWidth {
376
- value: number;
377
- type: "dxa" | "auto" | "pct" | "nil";
378
- }
379
-
380
- export interface CellShading {
381
- fill?: string;
382
- color?: string;
383
- val?: string;
384
- }
385
-
386
- export interface TableCellMargins {
387
- top?: number;
388
- left?: number;
389
- bottom?: number;
390
- right?: number;
391
- }
392
-
393
- export interface TableLook {
394
- val?: string;
395
- firstRow?: boolean;
396
- lastRow?: boolean;
397
- firstColumn?: boolean;
398
- lastColumn?: boolean;
399
- noHBand?: boolean;
400
- noVBand?: boolean;
401
- }
402
-
403
- export interface TableNode {
404
- type: "table";
405
- styleId?: string;
406
- propertiesXml?: string;
407
- gridColumns: number[];
408
- rows: TableRowNode[];
409
- width?: TableWidth;
410
- alignment?: "left" | "center" | "right";
411
- borders?: TableBorders;
412
- cellMargins?: TableCellMargins;
413
- tblLook?: TableLook;
414
- }
415
-
416
- export interface TableRowNode {
417
- type: "table_row";
418
- propertiesXml?: string;
419
- cells: TableCellNode[];
420
- gridBefore?: number;
421
- widthBefore?: TableWidth;
422
- gridAfter?: number;
423
- widthAfter?: TableWidth;
424
- height?: number;
425
- heightRule?: "auto" | "atLeast" | "exact";
426
- isHeader?: boolean;
427
- }
428
-
429
- export interface TableCellNode {
430
- type: "table_cell";
431
- propertiesXml?: string;
432
- gridSpan?: number;
433
- verticalMerge?: "restart" | "continue";
434
- children: BlockNode[];
435
- width?: TableWidth;
436
- borders?: TableCellBorders;
437
- shading?: CellShading;
438
- verticalAlign?: "top" | "center" | "bottom";
439
- }
440
-
441
- export interface SdtCheckboxState {
442
- checked: boolean;
443
- checkedChar?: string;
444
- uncheckedChar?: string;
445
- }
446
-
447
- export interface SdtDatePickerState {
448
- fullDate?: string;
449
- dateFormat?: string;
450
- lid?: string;
451
- }
452
-
453
- export interface SdtDropdownListItem {
454
- displayText?: string;
455
- value: string;
456
- }
457
-
458
- export interface SdtNode {
459
- type: "sdt";
460
- properties: {
461
- sdtType?: string;
462
- alias?: string;
463
- tag?: string;
464
- lock?: string;
465
- propertiesXml?: string;
466
- checkbox?: SdtCheckboxState;
467
- datePicker?: SdtDatePickerState;
468
- dropdownList?: SdtDropdownListItem[];
469
- comboBox?: SdtDropdownListItem[];
470
- showingPlcHdr?: boolean;
471
- };
472
- children: BlockNode[];
473
- }
474
-
475
- export interface CustomXmlNode {
476
- type: "custom_xml";
477
- uri?: string;
478
- element?: string;
479
- children: BlockNode[];
480
- }
481
-
482
- export interface AltChunkNode {
483
- type: "alt_chunk";
484
- relationshipId: string;
485
- }
486
-
487
- /**
488
- * Supported field families that receive first-class canonical treatment.
489
- * These families have stable registry IDs, dependency metadata, and
490
- * runtime-owned refresh behavior.
491
- */
492
- export type SupportedFieldFamily = "REF" | "PAGEREF" | "NOTEREF" | "TOC";
493
-
494
- /**
495
- * Unsupported field families that remain preserve-only.
496
- * They survive round-trip but do not participate in runtime refresh.
497
- */
498
- export type PreserveOnlyFieldFamily =
499
- | "PAGE"
500
- | "NUMPAGES"
501
- | "DATE"
502
- | "TIME"
503
- | "AUTHOR"
504
- | "FILENAME"
505
- | "MERGEFIELD"
506
- | "IF"
507
- | "SEQ"
508
- | "INDEX"
509
- | "TC"
510
- | "STYLEREF"
511
- | "FORMULA"
512
- | "UNKNOWN";
513
-
514
- export type FieldFamily = SupportedFieldFamily | PreserveOnlyFieldFamily;
515
-
516
- /** Runtime refresh status for a field instance. */
517
- export type FieldRefreshStatus =
518
- | "current"
519
- | "stale"
520
- | "unresolvable"
521
- | "preserve-only";
522
-
523
- export interface FieldNode {
524
- type: "field";
525
- fieldType: "simple" | "complex";
526
- instruction: string;
527
- children: InlineNode[];
528
- /** Classified field family. Undefined for legacy snapshots. */
529
- fieldFamily?: FieldFamily;
530
- /** Target bookmark name for REF/PAGEREF/NOTEREF fields. */
531
- fieldTarget?: string;
532
- /** Runtime refresh status. Undefined for legacy or preserve-only fields. */
533
- refreshStatus?: FieldRefreshStatus;
534
- }
535
-
536
- // ─── Field registry ─────────────────────────────────────────────────────────
537
-
538
- /**
539
- * Package-backed field registry that catalogs every field instance in the
540
- * document, grouped by supported vs preserve-only families.
541
- *
542
- * Supported field entries carry dependency metadata (bookmark targets) and
543
- * participate in deterministic refresh. Preserve-only entries survive
544
- * round-trip but are not refreshable.
545
- */
546
- export interface FieldRegistry {
547
- /** Supported field instances that participate in refresh. */
548
- supported: FieldRegistryEntry[];
549
- /** Preserve-only field instances cataloged for round-trip safety. */
550
- preserveOnly: FieldRegistryEntry[];
551
- /** Generated TOC structure extracted from heading-driven TOC fields. */
552
- tocStructure?: TocStructure;
553
- }
554
-
555
- export interface FieldRegistryEntry {
556
- /** Stable document-order index of this field (0-based). */
557
- fieldIndex: number;
558
- /** Classified field family. */
559
- fieldFamily: FieldFamily;
560
- /** Whether the field is in the supported refresh slice. */
561
- supported: boolean;
562
- /** Field instruction text. */
563
- instruction: string;
564
- /** Target bookmark name for REF/PAGEREF/NOTEREF fields. */
565
- fieldTarget?: string;
566
- /** Current display text extracted from field content. */
567
- displayText: string;
568
- /** Paragraph index in document order where this field appears. */
569
- paragraphIndex: number;
570
- /** Runtime refresh status. */
571
- refreshStatus: FieldRefreshStatus;
572
- }
573
-
574
- /**
575
- * Generated table-of-contents structure extracted from TOC fields and
576
- * heading-styled paragraphs in the document.
577
- */
578
- export interface TocStructure {
579
- /** The raw TOC field instruction (e.g. "TOC \\o \"1-3\" \\h"). */
580
- instruction: string;
581
- /** Heading level range the TOC covers. */
582
- levelRange: { from: number; to: number };
583
- /** Ordered TOC entries derived from heading paragraphs. */
584
- entries: TocEntry[];
585
- /** Whether the TOC content is current with the heading structure. */
586
- status: "current" | "stale";
587
- }
588
-
589
- export interface TocEntry {
590
- /** Heading text. */
591
- text: string;
592
- /** Heading outline level (1-9). */
593
- level: number;
594
- /** Paragraph index of the heading in document order. */
595
- paragraphIndex: number;
596
- /** Style ID of the heading paragraph, if available. */
597
- styleId?: string;
598
- /** Bookmark name anchoring this heading, if present. */
599
- bookmarkName?: string;
600
- }
601
-
602
- export interface BookmarkStartNode {
603
- type: "bookmark_start";
604
- bookmarkId: string;
605
- name: string;
606
- }
607
-
608
- export interface BookmarkEndNode {
609
- type: "bookmark_end";
610
- bookmarkId: string;
611
- }
612
-
613
- export interface SectionBreakNode {
614
- type: "section_break";
615
- sectionPropertiesXml?: string;
616
- /**
617
- * @deprecated Legacy field from older snapshots. New exports should use
618
- * sectionPropertiesXml and only contain raw <w:sectPr> content.
619
- */
620
- propertiesXml?: string;
621
- sectionProperties?: SectionProperties;
622
- }
623
-
624
- export interface SectionProperties {
625
- pageSize?: PageSize;
626
- pageMargins?: PageMargins;
627
- columns?: ColumnProperties;
628
- pageNumbering?: PageNumbering;
629
- lineNumbering?: SectionLineNumbering;
630
- pageBorders?: SectionPageBorders;
631
- documentGrid?: SectionDocumentGrid;
632
- headerReferences?: HeaderFooterReference[];
633
- footerReferences?: HeaderFooterReference[];
634
- sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
635
- titlePage?: boolean;
636
- }
637
-
638
- export interface PageSize {
639
- width: number;
640
- height: number;
641
- orientation?: "portrait" | "landscape";
642
- }
643
-
644
- export interface PageMargins {
645
- top: number;
646
- right: number;
647
- bottom: number;
648
- left: number;
649
- header?: number;
650
- footer?: number;
651
- gutter?: number;
652
- }
653
-
654
- export interface ColumnProperties {
655
- count?: number;
656
- space?: number;
657
- equalWidth?: boolean;
658
- columns?: Array<{ width: number; space?: number }>;
659
- separator?: boolean;
660
- }
661
-
662
- export interface PageNumbering {
663
- format?: string;
664
- start?: number;
665
- chapStyle?: string;
666
- chapSep?: string;
667
- }
668
-
669
- export interface SectionLineNumbering {
670
- countBy?: number;
671
- start?: number;
672
- distance?: number;
673
- restart?: "newPage" | "newSection" | "continuous";
674
- }
675
-
676
- export interface SectionPageBorders {
677
- top?: BorderSpec;
678
- left?: BorderSpec;
679
- bottom?: BorderSpec;
680
- right?: BorderSpec;
681
- offsetFrom?: "page" | "text";
682
- display?: "allPages" | "firstPage" | "notFirstPage";
683
- zOrder?: "front" | "back";
684
- }
685
-
686
- export interface SectionDocumentGrid {
687
- type?: "default" | "lines" | "linesAndChars" | "snapToChars";
688
- linePitch?: number;
689
- charSpace?: number;
690
- }
691
-
692
- export interface HeaderFooterReference {
693
- variant: HeaderFooterVariant;
694
- relationshipId: string;
695
- }
696
-
697
- export type InlineNode =
698
- | TextNode
699
- | HardBreakNode
700
- | ColumnBreakNode
701
- | TabNode
702
- | SymbolNode
703
- | HyperlinkNode
704
- | ImageNode
705
- | FieldNode
706
- | BookmarkStartNode
707
- | BookmarkEndNode
708
- | OpaqueInlineNode
709
- | FootnoteRefNode
710
- | ChartPreviewNode
711
- | SmartArtPreviewNode
712
- | ShapeNode
713
- | WordArtNode
714
- | VmlShapeNode;
715
-
716
- export interface TextNode {
717
- type: "text";
718
- text: string;
719
- marks?: TextMark[];
720
- }
721
-
722
- export type TextMark =
723
- | { type: "bold" }
724
- | { type: "italic" }
725
- | { type: "underline" }
726
- | { type: "strikethrough" }
727
- | { type: "doubleStrikethrough" }
728
- | { type: "vanish" }
729
- | { type: "lang"; val: string }
730
- | { type: "highlight"; color: string; val: string }
731
- | { type: "backgroundColor"; color: string }
732
- | { type: "charSpacing"; val: number }
733
- | { type: "kerning"; val: number }
734
- | { type: "emboss" }
735
- | { type: "imprint" }
736
- | { type: "shadow" }
737
- | { type: "position"; val: number }
738
- | { type: "textFill"; xml: string }
739
- | { type: "fontFamily"; val: string }
740
- | { type: "fontSize"; val: number }
741
- | { type: "textColor"; color: string }
742
- | { type: "smallCaps" }
743
- | { type: "allCaps" };
744
-
745
- export interface HardBreakNode {
746
- type: "hard_break";
747
- }
748
-
749
- export interface ColumnBreakNode {
750
- type: "column_break";
751
- }
752
-
753
- export interface TabNode {
754
- type: "tab";
755
- }
756
-
757
- export interface SymbolNode {
758
- type: "symbol";
759
- char: string;
760
- font?: string;
761
- marks?: TextMark[];
762
- }
763
-
764
- export interface HyperlinkNode {
765
- type: "hyperlink";
766
- href: string;
767
- children: Array<TextNode | HardBreakNode | ColumnBreakNode | TabNode | SymbolNode>;
768
- }
769
-
770
- export interface ImageNode {
771
- type: "image";
772
- mediaId: string;
773
- altText?: string;
774
- placementXml?: string;
775
- display?: "inline" | "floating";
776
- floating?: FloatingImageProperties;
777
- }
778
-
779
- export interface FloatingImageProperties {
780
- horizontalPosition?: FloatingAxisPosition;
781
- verticalPosition?: FloatingAxisPosition;
782
- wrap?: "none" | "square" | "tight" | "through" | "topAndBottom";
783
- behindDoc?: boolean;
784
- layoutInCell?: boolean;
785
- allowOverlap?: boolean;
786
- }
787
-
788
- export interface FloatingAxisPosition {
789
- relativeFrom?: string;
790
- align?: string;
791
- offset?: number;
792
- }
793
-
794
- export interface OpaqueInlineNode {
795
- type: "opaque_inline";
796
- fragmentId: string;
797
- warningId: string;
798
- }
799
-
800
- // ---- Complex rendering inline nodes (read-only previews) ----
801
-
802
- /**
803
- * Read-only preview of a chart (c:chart). The original drawing XML is stored in
804
- * rawXml for lossless round-trip export. If a fallback image was present in
805
- * mc:AlternateContent it is referenced by previewMediaId.
806
- */
807
- export interface ChartPreviewNode {
808
- type: "chart_preview";
809
- previewMediaId?: string;
810
- rawXml: string;
811
- }
812
-
813
- /**
814
- * Read-only preview of a SmartArt diagram (dgm:*). The original drawing XML is
815
- * stored in rawXml for lossless round-trip export.
816
- */
817
- export interface SmartArtPreviewNode {
818
- type: "smartart_preview";
819
- previewMediaId?: string;
820
- rawXml: string;
821
- }
822
-
823
- /**
824
- * Read-only rendering of a wps:wsp WordprocessingShape. Text content is
825
- * extracted for display. The original drawing XML is preserved in rawXml.
826
- */
827
- export interface ShapeNode {
828
- type: "shape";
829
- text?: string;
830
- geometry?: string;
831
- isTextBox?: boolean;
832
- rawXml: string;
833
- }
834
-
835
- /**
836
- * Read-only rendering of WordArt — a wps:wsp shape with a text-geometry preset.
837
- * Text is extracted for display. The original drawing XML is preserved in rawXml.
838
- */
839
- export interface WordArtNode {
840
- type: "wordart";
841
- text: string;
842
- geometry?: string;
843
- rawXml: string;
844
- }
845
-
846
- /**
847
- * Read-only rendering of a VML shape (v:shape, v:rect, v:textbox) from a w:pict
848
- * element. Text is extracted for display. The original w:pict XML is preserved
849
- * in rawXml for lossless round-trip export.
850
- */
851
- export interface VmlShapeNode {
852
- type: "vml_shape";
853
- text?: string;
854
- shapeType?: string;
855
- rawXml: string;
856
- }
857
-
858
- export interface OpaqueBlockNode {
859
- type: "opaque_block";
860
- fragmentId: string;
861
- warningId: string;
862
- rawXml?: string;
863
- }
864
-
865
- export interface DocRange {
866
- from: number;
867
- to: number;
868
- }
869
-
870
- export interface BoundaryAssoc {
871
- start: -1 | 1;
872
- end: -1 | 1;
873
- }
874
-
875
- export type CanonicalAnchor =
876
- | {
877
- kind: "range";
878
- range: DocRange;
879
- assoc: BoundaryAssoc;
880
- }
881
- | {
882
- kind: "node";
883
- at: number;
884
- assoc: -1 | 1;
885
- }
886
- | {
887
- kind: "detached";
888
- lastKnownRange: DocRange;
889
- reason: "deleted" | "invalidatedByStructureChange" | "importAmbiguity";
890
- };
891
-
892
- export interface ReviewStore {
893
- comments: Record<string, CommentThread>;
894
- revisions: Record<string, RevisionRecord>;
895
- }
896
-
897
- export interface CommentThread {
898
- commentId: string;
899
- status: "open" | "resolved" | "detached";
900
- anchor: CanonicalAnchor;
901
- createdAt: ISO8601DateTime;
902
- createdBy?: string;
903
- authorId?: string;
904
- body?: string;
905
- entries?: CommentEntry[];
906
- resolution?: CommentResolution;
907
- resolvedAt?: ISO8601DateTime;
908
- warningIds: string[];
909
- isResolved?: boolean;
910
- metadata?: CommentThreadMetadata;
911
- }
912
-
913
- export interface CommentEntry {
914
- entryId: string;
915
- authorId: string;
916
- createdAt: ISO8601DateTime;
917
- body: string;
918
- metadata?: CommentEntryMetadata;
919
- }
920
-
921
- export interface CommentEntryMetadata {
922
- ooxmlCommentId?: string;
923
- paraId?: string;
924
- parentParaId?: string;
925
- durableId?: string;
926
- initials?: string;
927
- }
928
-
929
- export interface CommentResolution {
930
- resolvedAt: ISO8601DateTime;
931
- resolvedBy: string;
932
- }
933
-
934
- export interface CommentThreadMetadata {
935
- source?: "runtime" | "import";
936
- rootOoxmlCommentId?: string;
937
- rootParaId?: string;
938
- }
939
-
940
- export interface RevisionPropertyChangeData {
941
- xmlTag: "pPrChange" | "sectPrChange" | "tblPrChange" | "rPrChange";
942
- beforeXml: string;
943
- }
944
-
945
- export interface RevisionMoveData {
946
- moveId: string;
947
- direction: "from" | "to";
948
- }
949
-
950
- export type RevisionStoryTargetRecord =
951
- | { kind: "main" }
952
- | {
953
- kind: "header";
954
- relationshipId: string;
955
- variant: "default" | "first" | "even";
956
- sectionIndex?: number;
957
- }
958
- | {
959
- kind: "footer";
960
- relationshipId: string;
961
- variant: "default" | "first" | "even";
962
- sectionIndex?: number;
963
- }
964
- | { kind: "footnote"; noteId: string }
965
- | { kind: "endnote"; noteId: string };
966
-
967
- export interface RevisionRecord {
968
- changeId: string;
969
- kind: "insertion" | "deletion" | "formatting" | "move" | "property-change";
970
- anchor: CanonicalAnchor;
971
- authorId?: string;
972
- createdAt: ISO8601DateTime;
973
- warningIds?: string[];
974
- metadata?: RevisionMetadataRecord;
975
- status: "open" | "accepted" | "rejected" | "detached";
976
- }
977
-
978
- export interface RevisionMetadataRecord {
979
- source?: "runtime" | "import";
980
- storyTarget?: RevisionStoryTargetRecord;
981
- preserveOnlyReason?: string;
982
- importedRevisionForm?:
983
- | "run-insertion"
984
- | "run-deletion"
985
- | "paragraph-insertion"
986
- | "paragraph-deletion";
987
- originalRevisionType?: string;
988
- ooxmlRevisionId?: string;
989
- propertyChangeData?: RevisionPropertyChangeData;
990
- moveData?: RevisionMoveData;
991
- }
992
-
993
- export interface PreservationStore {
994
- opaqueFragments: Record<string, OpaqueFragmentRecord>;
995
- packageParts: Record<string, PreservedPackagePart>;
996
- }
997
-
998
- export interface OpaqueFragmentRecord {
999
- fragmentId: string;
1000
- payloadKind: "xml-subtree" | "package-part";
1001
- payloadReference: string;
1002
- featureClass: "preserve-only";
1003
- lastKnownRange: DocRange;
1004
- warningId: string;
1005
- packagePartName?: string;
1006
- relationshipId?: string;
1007
- }
1008
-
1009
- export interface PreservedPackagePart {
1010
- packagePartName: string;
1011
- contentType: string;
1012
- relationshipIds: string[];
1013
- }
1014
-
1015
- export interface DiagnosticStore {
1016
- warnings: DiagnosticWarningEntry[];
1017
- errors: DiagnosticErrorEntry[];
1018
- }
1019
-
1020
- export interface DiagnosticWarningEntry {
1021
- diagnosticId: string;
1022
- warningId: string;
1023
- source:
1024
- | "import"
1025
- | "runtime"
1026
- | "review"
1027
- | "preservation"
1028
- | "validation"
1029
- | "export";
1030
- message: string;
1031
- }
1032
-
1033
- export interface DiagnosticErrorEntry {
1034
- diagnosticId: string;
1035
- code:
1036
- | "load_failed"
1037
- | "import_failed"
1038
- | "export_failed"
1039
- | "package_corrupt"
1040
- | "validation_failed"
1041
- | "datastore_failed"
1042
- | "internal_invariant";
1043
- message: string;
1044
- isFatal: boolean;
1045
- source: "import" | "runtime" | "validation" | "datastore" | "host" | "export";
1046
- details?: unknown;
1047
- }
1048
-
1049
- export function createCanonicalDocument(
1050
- input: Omit<CanonicalDocument, "schemaVersion">,
1051
- ): CanonicalDocument {
1052
- const document: CanonicalDocument = {
1053
- schemaVersion: CDS_SCHEMA_VERSION,
1054
- ...input,
1055
- };
1056
-
1057
- assertCanonicalDocument(document);
1058
- return document;
1059
- }
1060
-
1061
- export function serializeCanonicalDocument(document: CanonicalDocument): string {
1062
- assertCanonicalDocument(document);
1063
- return stableStringify(document);
1064
- }
1065
-
1066
- export function createCanonicalDocumentSignature(document: unknown): string {
1067
- return stableStringify(document);
1068
- }
1069
-
1070
- export function parseCanonicalDocument(json: string): CanonicalDocument {
1071
- const parsed = JSON.parse(json) as unknown;
1072
- assertCanonicalDocument(parsed);
1073
- return parsed;
1074
- }
1075
-
1076
- export function projectCanonicalDocument(
1077
- document: CanonicalDocument,
1078
- ): CanonicalDocument {
1079
- assertCanonicalDocument(document);
1080
- return JSON.parse(stableStringify(document)) as CanonicalDocument;
1081
- }
1082
-
1083
- export function assertCanonicalDocument(
1084
- value: unknown,
1085
- ): asserts value is CanonicalDocument {
1086
- const issues = validateCanonicalDocument(value);
1087
- assertValid(issues, "Invalid canonical document.");
1088
- }
1089
-
1090
- export function validateCanonicalDocument(
1091
- value: unknown,
1092
- ): ModelValidationIssue[] {
1093
- const issues: ModelValidationIssue[] = [];
1094
- const record = asPlainObject(value, "$", issues);
1095
- if (!record) {
1096
- return issues;
1097
- }
1098
-
1099
- validateExactObjectKeys(record, CANONICAL_DOCUMENT_TOP_LEVEL_KEYS, "$", issues, CANONICAL_DOCUMENT_OPTIONAL_KEYS);
1100
- expectExactString(record.schemaVersion, CDS_SCHEMA_VERSION, "$.schemaVersion", issues);
1101
- expectUuid(record.docId, "$.docId", issues);
1102
- expectIso8601UtcTimestamp(record.createdAt, "$.createdAt", issues);
1103
- expectIso8601UtcTimestamp(record.updatedAt, "$.updatedAt", issues);
1104
-
1105
- validateMetadata(record.metadata, "$.metadata", issues);
1106
- validateStylesCatalog(record.styles, "$.styles", issues);
1107
- validateNumberingCatalog(record.numbering, "$.numbering", issues);
1108
- validateMediaCatalog(record.media, "$.media", issues);
1109
- validateDocumentNode(record.content, "$.content", issues);
1110
- validateReviewStore(record.review, "$.review", issues);
1111
- validatePreservationStore(record.preservation, "$.preservation", issues);
1112
- validateDiagnosticStore(record.diagnostics, "$.diagnostics", issues);
1113
- if (record.subParts !== undefined) {
1114
- validateSubPartsCatalog(record.subParts, "$.subParts", issues);
1115
- }
1116
- validateDocumentReferences(record, issues);
1117
-
1118
- return issues;
1119
- }
1120
-
1121
- function validateMetadata(
1122
- value: unknown,
1123
- path: string,
1124
- issues: ModelValidationIssue[],
1125
- ): void {
1126
- const record = asPlainObject(value, path, issues);
1127
- if (!record) {
1128
- return;
1129
- }
1130
-
1131
- const customProperties = asPlainObject(record.customProperties, `${path}.customProperties`, issues);
1132
- if (!customProperties) {
1133
- return;
1134
- }
1135
-
1136
- for (const [propertyKey, propertyValue] of Object.entries(customProperties)) {
1137
- if (typeof propertyValue !== "string") {
1138
- issues.push({
1139
- path: `${path}.customProperties.${propertyKey}`,
1140
- message: "customProperties values must be strings.",
1141
- });
1142
- }
1143
- }
1144
- }
1145
-
1146
- function validateStylesCatalog(
1147
- value: unknown,
1148
- path: string,
1149
- issues: ModelValidationIssue[],
1150
- ): void {
1151
- const record = asPlainObject(value, path, issues);
1152
- if (!record) {
1153
- return;
1154
- }
1155
-
1156
- validateStyleMap(record.paragraphs, `${path}.paragraphs`, issues);
1157
- validateStyleMap(record.characters, `${path}.characters`, issues);
1158
- validateStyleMap(record.tables, `${path}.tables`, issues);
1159
- if (record.latentStyles !== undefined) {
1160
- validateLatentStyleMap(record.latentStyles, `${path}.latentStyles`, issues);
1161
- }
1162
- }
1163
-
1164
- function validateStyleMap(
1165
- value: unknown,
1166
- path: string,
1167
- issues: ModelValidationIssue[],
1168
- ): void {
1169
- const record = asPlainObject(value, path, issues);
1170
- if (!record) {
1171
- return;
1172
- }
1173
-
1174
- for (const [styleId, definition] of Object.entries(record)) {
1175
- expectDomainString(styleId, "styleId", `${path}.${styleId}`, issues);
1176
- const definitionRecord = asPlainObject(definition, `${path}.${styleId}`, issues);
1177
- if (!definitionRecord) {
1178
- continue;
1179
- }
1180
- if (definitionRecord.styleId !== styleId) {
1181
- issues.push({
1182
- path: `${path}.${styleId}.styleId`,
1183
- message: "styleId must match the map key.",
1184
- });
1185
- }
1186
- }
1187
- }
1188
-
1189
- function validateLatentStyleMap(
1190
- value: unknown,
1191
- path: string,
1192
- issues: ModelValidationIssue[],
1193
- ): void {
1194
- const record = asPlainObject(value, path, issues);
1195
- if (!record) {
1196
- return;
1197
- }
1198
-
1199
- for (const [styleName, definition] of Object.entries(record)) {
1200
- const definitionRecord = asPlainObject(definition, `${path}.${styleName}`, issues);
1201
- if (!definitionRecord) {
1202
- continue;
1203
- }
1204
- if (definitionRecord.name !== styleName) {
1205
- issues.push({
1206
- path: `${path}.${styleName}.name`,
1207
- message: "name must match the map key.",
1208
- });
1209
- }
1210
- if (definitionRecord.uiPriority !== undefined && typeof definitionRecord.uiPriority !== "number") {
1211
- issues.push({
1212
- path: `${path}.${styleName}.uiPriority`,
1213
- message: "uiPriority must be a number.",
1214
- });
1215
- }
1216
- }
1217
- }
1218
-
1219
- function validateNumberingCatalog(
1220
- value: unknown,
1221
- path: string,
1222
- issues: ModelValidationIssue[],
1223
- ): void {
1224
- const record = asPlainObject(value, path, issues);
1225
- if (!record) {
1226
- return;
1227
- }
1228
-
1229
- const abstractDefinitions = asPlainObject(
1230
- record.abstractDefinitions,
1231
- `${path}.abstractDefinitions`,
1232
- issues,
1233
- );
1234
- if (abstractDefinitions) {
1235
- for (const [abstractId, definition] of Object.entries(abstractDefinitions)) {
1236
- expectDomainString(
1237
- abstractId,
1238
- "abstractNumberingId",
1239
- `${path}.abstractDefinitions.${abstractId}`,
1240
- issues,
1241
- );
1242
- const definitionRecord = asPlainObject(
1243
- definition,
1244
- `${path}.abstractDefinitions.${abstractId}`,
1245
- issues,
1246
- );
1247
- if (definitionRecord && definitionRecord.abstractNumberingId !== abstractId) {
1248
- issues.push({
1249
- path: `${path}.abstractDefinitions.${abstractId}.abstractNumberingId`,
1250
- message: "abstractNumberingId must match the map key.",
1251
- });
1252
- }
1253
- }
1254
- }
1255
-
1256
- const instances = asPlainObject(record.instances, `${path}.instances`, issues);
1257
- if (instances) {
1258
- for (const [instanceId, instance] of Object.entries(instances)) {
1259
- expectDomainString(
1260
- instanceId,
1261
- "numberingInstanceId",
1262
- `${path}.instances.${instanceId}`,
1263
- issues,
1264
- );
1265
- const instanceRecord = asPlainObject(
1266
- instance,
1267
- `${path}.instances.${instanceId}`,
1268
- issues,
1269
- );
1270
- if (!instanceRecord) {
1271
- continue;
1272
- }
1273
- if (instanceRecord.numberingInstanceId !== instanceId) {
1274
- issues.push({
1275
- path: `${path}.instances.${instanceId}.numberingInstanceId`,
1276
- message: "numberingInstanceId must match the map key.",
1277
- });
1278
- }
1279
- expectDomainString(
1280
- instanceRecord.abstractNumberingId,
1281
- "abstractNumberingId",
1282
- `${path}.instances.${instanceId}.abstractNumberingId`,
1283
- issues,
1284
- );
1285
- }
1286
- }
1287
- }
1288
-
1289
- function validateMediaCatalog(
1290
- value: unknown,
1291
- path: string,
1292
- issues: ModelValidationIssue[],
1293
- ): void {
1294
- const record = asPlainObject(value, path, issues);
1295
- if (!record) {
1296
- return;
1297
- }
1298
-
1299
- const items = asPlainObject(record.items, `${path}.items`, issues);
1300
- if (!items) {
1301
- return;
1302
- }
1303
-
1304
- for (const [mediaId, item] of Object.entries(items)) {
1305
- expectDomainString(mediaId, "mediaId", `${path}.items.${mediaId}`, issues);
1306
- const itemRecord = asPlainObject(item, `${path}.items.${mediaId}`, issues);
1307
- if (!itemRecord) {
1308
- continue;
1309
- }
1310
- if (itemRecord.mediaId !== mediaId) {
1311
- issues.push({
1312
- path: `${path}.items.${mediaId}.mediaId`,
1313
- message: "mediaId must match the map key.",
1314
- });
1315
- }
1316
- expectDomainString(
1317
- itemRecord.packagePartName,
1318
- "packagePartName",
1319
- `${path}.items.${mediaId}.packagePartName`,
1320
- issues,
1321
- );
1322
- if (itemRecord.relationshipId !== undefined) {
1323
- expectDomainString(
1324
- itemRecord.relationshipId,
1325
- "relationshipId",
1326
- `${path}.items.${mediaId}.relationshipId`,
1327
- issues,
1328
- );
1329
- }
1330
- }
1331
- }
1332
-
1333
- function validateDocumentNode(
1334
- value: unknown,
1335
- path: string,
1336
- issues: ModelValidationIssue[],
1337
- ): void {
1338
- const record = asPlainObject(value, path, issues);
1339
- if (!record) {
1340
- return;
1341
- }
1342
-
1343
- const type = expectString(record.type, `${path}.type`, issues);
1344
- if (!type) {
1345
- return;
1346
- }
1347
-
1348
- switch (type) {
1349
- case "doc":
1350
- case "paragraph":
1351
- case "sdt":
1352
- case "custom_xml":
1353
- case "hyperlink":
1354
- if (!Array.isArray(record.children)) {
1355
- issues.push({ path: `${path}.children`, message: "children must be an array." });
1356
- } else {
1357
- record.children.forEach((child, index) =>
1358
- validateDocumentNode(child, `${path}.children[${index}]`, issues),
1359
- );
1360
- }
1361
- if (type === "paragraph" && record.styleId !== undefined) {
1362
- expectDomainString(record.styleId, "styleId", `${path}.styleId`, issues);
1363
- }
1364
- if (type === "paragraph" && record.numbering !== undefined) {
1365
- const numbering = asPlainObject(record.numbering, `${path}.numbering`, issues);
1366
- if (numbering) {
1367
- expectDomainString(
1368
- numbering.numberingInstanceId,
1369
- "numberingInstanceId",
1370
- `${path}.numbering.numberingInstanceId`,
1371
- issues,
1372
- );
1373
- if (typeof numbering.level !== "number") {
1374
- issues.push({
1375
- path: `${path}.numbering.level`,
1376
- message: "level must be a number.",
1377
- });
1378
- }
1379
- }
1380
- }
1381
- if (type === "hyperlink") {
1382
- expectString(record.href, `${path}.href`, issues);
1383
- }
1384
- return;
1385
- case "alt_chunk":
1386
- expectDomainString(record.relationshipId, "relationshipId", `${path}.relationshipId`, issues);
1387
- return;
1388
- case "image":
1389
- expectDomainString(record.mediaId, "mediaId", `${path}.mediaId`, issues);
1390
- if (record.placementXml !== undefined) {
1391
- expectString(record.placementXml, `${path}.placementXml`, issues);
1392
- }
1393
- if (record.display !== undefined) {
1394
- const display = expectString(record.display, `${path}.display`, issues);
1395
- if (display && display !== "inline" && display !== "floating") {
1396
- issues.push({
1397
- path: `${path}.display`,
1398
- message: "display must be 'inline' or 'floating'.",
1399
- });
1400
- }
1401
- }
1402
- if (record.floating !== undefined) {
1403
- validateFloatingImageProperties(record.floating, `${path}.floating`, issues);
1404
- }
1405
- return;
1406
- case "opaque_inline":
1407
- case "opaque_block":
1408
- expectDomainString(record.fragmentId, "fragmentId", `${path}.fragmentId`, issues);
1409
- expectDomainString(record.warningId, "warningId", `${path}.warningId`, issues);
1410
- if (record.rawXml !== undefined) {
1411
- expectString(record.rawXml, `${path}.rawXml`, issues);
1412
- }
1413
- return;
1414
- case "table":
1415
- if (!Array.isArray(record.gridColumns)) {
1416
- issues.push({ path: `${path}.gridColumns`, message: "gridColumns must be an array." });
1417
- }
1418
- if (!Array.isArray(record.rows)) {
1419
- issues.push({ path: `${path}.rows`, message: "rows must be an array." });
1420
- } else {
1421
- record.rows.forEach((row, rowIndex) =>
1422
- validateDocumentNode(row, `${path}.rows[${rowIndex}]`, issues),
1423
- );
1424
- }
1425
- return;
1426
- case "table_row":
1427
- if (!Array.isArray(record.cells)) {
1428
- issues.push({ path: `${path}.cells`, message: "cells must be an array." });
1429
- } else {
1430
- record.cells.forEach((cell, cellIndex) =>
1431
- validateDocumentNode(cell, `${path}.cells[${cellIndex}]`, issues),
1432
- );
1433
- }
1434
- return;
1435
- case "table_cell":
1436
- if (!Array.isArray(record.children)) {
1437
- issues.push({ path: `${path}.children`, message: "children must be an array." });
1438
- } else {
1439
- record.children.forEach((child, childIndex) =>
1440
- validateDocumentNode(child, `${path}.children[${childIndex}]`, issues),
1441
- );
1442
- }
1443
- return;
1444
- case "field":
1445
- expectString(record.fieldType, `${path}.fieldType`, issues);
1446
- expectString(record.instruction, `${path}.instruction`, issues);
1447
- if (!Array.isArray(record.children)) {
1448
- issues.push({ path: `${path}.children`, message: "children must be an array." });
1449
- } else {
1450
- record.children.forEach((child, index) =>
1451
- validateDocumentNode(child, `${path}.children[${index}]`, issues),
1452
- );
1453
- }
1454
- return;
1455
- case "bookmark_start":
1456
- expectString(record.bookmarkId, `${path}.bookmarkId`, issues);
1457
- expectString(record.name, `${path}.name`, issues);
1458
- return;
1459
- case "bookmark_end":
1460
- expectString(record.bookmarkId, `${path}.bookmarkId`, issues);
1461
- return;
1462
- case "section_break":
1463
- return;
1464
- case "text":
1465
- if (typeof record.text !== "string") {
1466
- issues.push({
1467
- path: `${path}.text`,
1468
- message: "text must be a string.",
1469
- });
1470
- }
1471
- return;
1472
- case "hard_break":
1473
- case "column_break":
1474
- case "tab":
1475
- return;
1476
- case "symbol":
1477
- expectString(record.char, `${path}.char`, issues);
1478
- if (record.font !== undefined) {
1479
- expectString(record.font, `${path}.font`, issues);
1480
- }
1481
- return;
1482
- case "footnote_ref":
1483
- expectString(record.noteId, `${path}.noteId`, issues);
1484
- if (record.noteKind !== "footnote" && record.noteKind !== "endnote") {
1485
- issues.push({
1486
- path: `${path}.noteKind`,
1487
- message: "noteKind must be 'footnote' or 'endnote'.",
1488
- });
1489
- }
1490
- return;
1491
- case "chart_preview":
1492
- case "smartart_preview":
1493
- case "shape":
1494
- case "wordart":
1495
- case "vml_shape":
1496
- expectString(record.rawXml, `${path}.rawXml`, issues);
1497
- return;
1498
- default:
1499
- issues.push({
1500
- path: `${path}.type`,
1501
- message: `Unsupported node type ${JSON.stringify(type)}.`,
1502
- });
1503
- }
1504
- }
1505
-
1506
- function validateFloatingImageProperties(
1507
- value: unknown,
1508
- path: string,
1509
- issues: ModelValidationIssue[],
1510
- ): void {
1511
- const record = asPlainObject(value, path, issues);
1512
- if (!record) {
1513
- return;
1514
- }
1515
-
1516
- if (record.horizontalPosition !== undefined) {
1517
- validateFloatingAxisPosition(record.horizontalPosition, `${path}.horizontalPosition`, issues);
1518
- }
1519
- if (record.verticalPosition !== undefined) {
1520
- validateFloatingAxisPosition(record.verticalPosition, `${path}.verticalPosition`, issues);
1521
- }
1522
- if (record.wrap !== undefined) {
1523
- const wrap = expectString(record.wrap, `${path}.wrap`, issues);
1524
- if (
1525
- wrap &&
1526
- wrap !== "none" &&
1527
- wrap !== "square" &&
1528
- wrap !== "tight" &&
1529
- wrap !== "through" &&
1530
- wrap !== "topAndBottom"
1531
- ) {
1532
- issues.push({
1533
- path: `${path}.wrap`,
1534
- message: "wrap must be one of none, square, tight, through, or topAndBottom.",
1535
- });
1536
- }
1537
- }
1538
- }
1539
-
1540
- function validateFloatingAxisPosition(
1541
- value: unknown,
1542
- path: string,
1543
- issues: ModelValidationIssue[],
1544
- ): void {
1545
- const record = asPlainObject(value, path, issues);
1546
- if (!record) {
1547
- return;
1548
- }
1549
-
1550
- if (record.relativeFrom !== undefined) {
1551
- expectString(record.relativeFrom, `${path}.relativeFrom`, issues);
1552
- }
1553
- if (record.align !== undefined) {
1554
- expectString(record.align, `${path}.align`, issues);
1555
- }
1556
- if (record.offset !== undefined && typeof record.offset !== "number") {
1557
- issues.push({
1558
- path: `${path}.offset`,
1559
- message: "offset must be a number.",
1560
- });
1561
- }
1562
- }
1563
-
1564
- function validateReviewStore(
1565
- value: unknown,
1566
- path: string,
1567
- issues: ModelValidationIssue[],
1568
- ): void {
1569
- const record = asPlainObject(value, path, issues);
1570
- if (!record) {
1571
- return;
1572
- }
1573
-
1574
- const comments = asPlainObject(record.comments, `${path}.comments`, issues);
1575
- if (comments) {
1576
- for (const [commentId, thread] of Object.entries(comments)) {
1577
- expectDomainString(commentId, "commentId", `${path}.comments.${commentId}`, issues);
1578
- const threadRecord = asPlainObject(thread, `${path}.comments.${commentId}`, issues);
1579
- if (!threadRecord) {
1580
- continue;
1581
- }
1582
- if (threadRecord.commentId !== commentId) {
1583
- issues.push({
1584
- path: `${path}.comments.${commentId}.commentId`,
1585
- message: "commentId must match the map key.",
1586
- });
1587
- }
1588
- validateAnchor(threadRecord.anchor, `${path}.comments.${commentId}.anchor`, issues);
1589
- expectIso8601UtcTimestamp(
1590
- threadRecord.createdAt,
1591
- `${path}.comments.${commentId}.createdAt`,
1592
- issues,
1593
- );
1594
- validateCommentStatus(threadRecord.status, `${path}.comments.${commentId}.status`, issues);
1595
- if (threadRecord.createdBy !== undefined) {
1596
- expectString(
1597
- threadRecord.createdBy,
1598
- `${path}.comments.${commentId}.createdBy`,
1599
- issues,
1600
- );
1601
- }
1602
- if (threadRecord.authorId !== undefined) {
1603
- expectString(
1604
- threadRecord.authorId,
1605
- `${path}.comments.${commentId}.authorId`,
1606
- issues,
1607
- );
1608
- }
1609
- if (threadRecord.body !== undefined) {
1610
- expectStringAllowEmpty(
1611
- threadRecord.body,
1612
- `${path}.comments.${commentId}.body`,
1613
- issues,
1614
- );
1615
- }
1616
- if (!Array.isArray(threadRecord.warningIds)) {
1617
- issues.push({
1618
- path: `${path}.comments.${commentId}.warningIds`,
1619
- message: "warningIds must be an array.",
1620
- });
1621
- } else {
1622
- threadRecord.warningIds.forEach((warningId, index) =>
1623
- expectDomainString(
1624
- warningId,
1625
- "warningId",
1626
- `${path}.comments.${commentId}.warningIds[${index}]`,
1627
- issues,
1628
- ),
1629
- );
1630
- }
1631
- if (threadRecord.entries !== undefined) {
1632
- validateCommentEntries(
1633
- threadRecord.entries,
1634
- `${path}.comments.${commentId}.entries`,
1635
- issues,
1636
- );
1637
- }
1638
- if (threadRecord.resolution !== undefined) {
1639
- validateCommentResolution(
1640
- threadRecord.resolution,
1641
- `${path}.comments.${commentId}.resolution`,
1642
- issues,
1643
- );
1644
- }
1645
- if (threadRecord.resolvedAt !== undefined) {
1646
- expectIso8601UtcTimestamp(
1647
- threadRecord.resolvedAt,
1648
- `${path}.comments.${commentId}.resolvedAt`,
1649
- issues,
1650
- );
1651
- }
1652
- if (
1653
- threadRecord.isResolved !== undefined &&
1654
- typeof threadRecord.isResolved !== "boolean"
1655
- ) {
1656
- issues.push({
1657
- path: `${path}.comments.${commentId}.isResolved`,
1658
- message: "isResolved must be a boolean.",
1659
- });
1660
- }
1661
- if (threadRecord.metadata !== undefined) {
1662
- validateCommentThreadMetadata(
1663
- threadRecord.metadata,
1664
- `${path}.comments.${commentId}.metadata`,
1665
- issues,
1666
- );
1667
- }
1668
- }
1669
- }
1670
-
1671
- const revisions = asPlainObject(record.revisions, `${path}.revisions`, issues);
1672
- if (revisions) {
1673
- for (const [revisionId, revision] of Object.entries(revisions)) {
1674
- expectDomainString(revisionId, "revisionId", `${path}.revisions.${revisionId}`, issues);
1675
- const revisionRecord = asPlainObject(
1676
- revision,
1677
- `${path}.revisions.${revisionId}`,
1678
- issues,
1679
- );
1680
- if (!revisionRecord) {
1681
- continue;
1682
- }
1683
- if (revisionRecord.changeId !== revisionId) {
1684
- issues.push({
1685
- path: `${path}.revisions.${revisionId}.changeId`,
1686
- message: "changeId must match the map key.",
1687
- });
1688
- }
1689
- validateAnchor(revisionRecord.anchor, `${path}.revisions.${revisionId}.anchor`, issues);
1690
- validateRevisionKind(revisionRecord.kind, `${path}.revisions.${revisionId}.kind`, issues);
1691
- validateRevisionStatus(
1692
- revisionRecord.status,
1693
- `${path}.revisions.${revisionId}.status`,
1694
- issues,
1695
- );
1696
- expectIso8601UtcTimestamp(
1697
- revisionRecord.createdAt,
1698
- `${path}.revisions.${revisionId}.createdAt`,
1699
- issues,
1700
- );
1701
- if (revisionRecord.authorId !== undefined) {
1702
- expectString(
1703
- revisionRecord.authorId,
1704
- `${path}.revisions.${revisionId}.authorId`,
1705
- issues,
1706
- );
1707
- }
1708
- if (revisionRecord.warningIds !== undefined) {
1709
- validateWarningIds(
1710
- revisionRecord.warningIds,
1711
- `${path}.revisions.${revisionId}.warningIds`,
1712
- issues,
1713
- );
1714
- }
1715
- if (revisionRecord.metadata !== undefined) {
1716
- validateRevisionMetadata(
1717
- revisionRecord.metadata,
1718
- `${path}.revisions.${revisionId}.metadata`,
1719
- issues,
1720
- );
1721
- }
1722
- }
1723
- }
1724
- }
1725
-
1726
- function validateCommentStatus(
1727
- value: unknown,
1728
- path: string,
1729
- issues: ModelValidationIssue[],
1730
- ): void {
1731
- if (value === "open" || value === "resolved" || value === "detached") {
1732
- return;
1733
- }
1734
- issues.push({
1735
- path,
1736
- message: "status must be one of open, resolved, or detached.",
1737
- });
1738
- }
1739
-
1740
- function validateCommentEntries(
1741
- value: unknown,
1742
- path: string,
1743
- issues: ModelValidationIssue[],
1744
- ): void {
1745
- if (!Array.isArray(value)) {
1746
- issues.push({
1747
- path,
1748
- message: "entries must be an array.",
1749
- });
1750
- return;
1751
- }
1752
-
1753
- value.forEach((entry, index) => {
1754
- const record = asPlainObject(entry, `${path}[${index}]`, issues);
1755
- if (!record) {
1756
- return;
1757
- }
1758
- expectString(record.entryId, `${path}[${index}].entryId`, issues);
1759
- expectString(record.authorId, `${path}[${index}].authorId`, issues);
1760
- expectStringAllowEmpty(record.body, `${path}[${index}].body`, issues);
1761
- expectIso8601UtcTimestamp(record.createdAt, `${path}[${index}].createdAt`, issues);
1762
- if (record.metadata !== undefined) {
1763
- validateCommentEntryMetadata(record.metadata, `${path}[${index}].metadata`, issues);
1764
- }
1765
- });
1766
- }
1767
-
1768
- function validateCommentEntryMetadata(
1769
- value: unknown,
1770
- path: string,
1771
- issues: ModelValidationIssue[],
1772
- ): void {
1773
- const record = asPlainObject(value, path, issues);
1774
- if (!record) {
1775
- return;
1776
- }
1777
-
1778
- for (const field of [
1779
- "ooxmlCommentId",
1780
- "paraId",
1781
- "parentParaId",
1782
- "durableId",
1783
- "initials",
1784
- ] as const) {
1785
- if (record[field] !== undefined) {
1786
- expectString(record[field], `${path}.${field}`, issues);
1787
- }
1788
- }
1789
- }
1790
-
1791
- function validateCommentResolution(
1792
- value: unknown,
1793
- path: string,
1794
- issues: ModelValidationIssue[],
1795
- ): void {
1796
- const record = asPlainObject(value, path, issues);
1797
- if (!record) {
1798
- return;
1799
- }
1800
-
1801
- expectIso8601UtcTimestamp(record.resolvedAt, `${path}.resolvedAt`, issues);
1802
- expectString(record.resolvedBy, `${path}.resolvedBy`, issues);
1803
- }
1804
-
1805
- function validateCommentThreadMetadata(
1806
- value: unknown,
1807
- path: string,
1808
- issues: ModelValidationIssue[],
1809
- ): void {
1810
- const record = asPlainObject(value, path, issues);
1811
- if (!record) {
1812
- return;
1813
- }
1814
-
1815
- if (
1816
- record.source !== undefined &&
1817
- record.source !== "runtime" &&
1818
- record.source !== "import"
1819
- ) {
1820
- issues.push({
1821
- path: `${path}.source`,
1822
- message: "source must be either runtime or import.",
1823
- });
1824
- }
1825
- if (record.rootOoxmlCommentId !== undefined) {
1826
- expectString(record.rootOoxmlCommentId, `${path}.rootOoxmlCommentId`, issues);
1827
- }
1828
- if (record.rootParaId !== undefined) {
1829
- expectString(record.rootParaId, `${path}.rootParaId`, issues);
1830
- }
1831
- }
1832
-
1833
- function validateRevisionKind(
1834
- value: unknown,
1835
- path: string,
1836
- issues: ModelValidationIssue[],
1837
- ): void {
1838
- if (
1839
- value === "insertion" ||
1840
- value === "deletion" ||
1841
- value === "formatting" ||
1842
- value === "move" ||
1843
- value === "property-change"
1844
- ) {
1845
- return;
1846
- }
1847
- issues.push({
1848
- path,
1849
- message: "kind must be insertion, deletion, formatting, move, or property-change.",
1850
- });
1851
- }
1852
-
1853
- function validateRevisionStatus(
1854
- value: unknown,
1855
- path: string,
1856
- issues: ModelValidationIssue[],
1857
- ): void {
1858
- if (
1859
- value === "open" ||
1860
- value === "accepted" ||
1861
- value === "rejected" ||
1862
- value === "detached"
1863
- ) {
1864
- return;
1865
- }
1866
- issues.push({
1867
- path,
1868
- message: "status must be one of open, accepted, rejected, or detached.",
1869
- });
1870
- }
1871
-
1872
- function validateWarningIds(
1873
- value: unknown,
1874
- path: string,
1875
- issues: ModelValidationIssue[],
1876
- ): void {
1877
- if (!Array.isArray(value)) {
1878
- issues.push({
1879
- path,
1880
- message: "warningIds must be an array.",
1881
- });
1882
- return;
1883
- }
1884
-
1885
- value.forEach((warningId, index) =>
1886
- expectDomainString(warningId, "warningId", `${path}[${index}]`, issues),
1887
- );
1888
- }
1889
-
1890
- function validateRevisionMetadata(
1891
- value: unknown,
1892
- path: string,
1893
- issues: ModelValidationIssue[],
1894
- ): void {
1895
- const record = asPlainObject(value, path, issues);
1896
- if (!record) {
1897
- return;
1898
- }
1899
-
1900
- if (
1901
- record.source !== undefined &&
1902
- record.source !== "runtime" &&
1903
- record.source !== "import"
1904
- ) {
1905
- issues.push({
1906
- path: `${path}.source`,
1907
- message: "source must be either runtime or import.",
1908
- });
1909
- }
1910
- for (const field of [
1911
- "preserveOnlyReason",
1912
- "importedRevisionForm",
1913
- "originalRevisionType",
1914
- "ooxmlRevisionId",
1915
- ] as const) {
1916
- if (record[field] !== undefined) {
1917
- expectString(record[field], `${path}.${field}`, issues);
1918
- }
1919
- }
1920
-
1921
- if (record.propertyChangeData !== undefined) {
1922
- const pcd = asPlainObject(record.propertyChangeData, `${path}.propertyChangeData`, issues);
1923
- if (pcd) {
1924
- expectString(pcd.xmlTag, `${path}.propertyChangeData.xmlTag`, issues);
1925
- expectString(pcd.beforeXml, `${path}.propertyChangeData.beforeXml`, issues);
1926
- }
1927
- }
1928
-
1929
- if (record.moveData !== undefined) {
1930
- const md = asPlainObject(record.moveData, `${path}.moveData`, issues);
1931
- if (md) {
1932
- expectString(md.moveId, `${path}.moveData.moveId`, issues);
1933
- expectString(md.direction, `${path}.moveData.direction`, issues);
1934
- }
1935
- }
1936
- }
1937
-
1938
- function validatePreservationStore(
1939
- value: unknown,
1940
- path: string,
1941
- issues: ModelValidationIssue[],
1942
- ): void {
1943
- const record = asPlainObject(value, path, issues);
1944
- if (!record) {
1945
- return;
1946
- }
1947
-
1948
- const opaqueFragments = asPlainObject(
1949
- record.opaqueFragments,
1950
- `${path}.opaqueFragments`,
1951
- issues,
1952
- );
1953
- if (opaqueFragments) {
1954
- for (const [fragmentId, fragment] of Object.entries(opaqueFragments)) {
1955
- expectDomainString(fragmentId, "fragmentId", `${path}.opaqueFragments.${fragmentId}`, issues);
1956
- const fragmentRecord = asPlainObject(
1957
- fragment,
1958
- `${path}.opaqueFragments.${fragmentId}`,
1959
- issues,
1960
- );
1961
- if (!fragmentRecord) {
1962
- continue;
1963
- }
1964
- if (fragmentRecord.fragmentId !== fragmentId) {
1965
- issues.push({
1966
- path: `${path}.opaqueFragments.${fragmentId}.fragmentId`,
1967
- message: "fragmentId must match the map key.",
1968
- });
1969
- }
1970
- validateRange(
1971
- fragmentRecord.lastKnownRange,
1972
- `${path}.opaqueFragments.${fragmentId}.lastKnownRange`,
1973
- issues,
1974
- );
1975
- expectDomainString(
1976
- fragmentRecord.warningId,
1977
- "warningId",
1978
- `${path}.opaqueFragments.${fragmentId}.warningId`,
1979
- issues,
1980
- );
1981
- if (fragmentRecord.packagePartName !== undefined) {
1982
- expectDomainString(
1983
- fragmentRecord.packagePartName,
1984
- "packagePartName",
1985
- `${path}.opaqueFragments.${fragmentId}.packagePartName`,
1986
- issues,
1987
- );
1988
- }
1989
- if (fragmentRecord.relationshipId !== undefined) {
1990
- expectDomainString(
1991
- fragmentRecord.relationshipId,
1992
- "relationshipId",
1993
- `${path}.opaqueFragments.${fragmentId}.relationshipId`,
1994
- issues,
1995
- );
1996
- }
1997
- }
1998
- }
1999
-
2000
- const packageParts = asPlainObject(record.packageParts, `${path}.packageParts`, issues);
2001
- if (packageParts) {
2002
- for (const [packagePartName, packagePart] of Object.entries(packageParts)) {
2003
- expectDomainString(
2004
- packagePartName,
2005
- "packagePartName",
2006
- `${path}.packageParts.${packagePartName}`,
2007
- issues,
2008
- );
2009
- const packagePartRecord = asPlainObject(
2010
- packagePart,
2011
- `${path}.packageParts.${packagePartName}`,
2012
- issues,
2013
- );
2014
- if (!packagePartRecord) {
2015
- continue;
2016
- }
2017
- if (packagePartRecord.packagePartName !== packagePartName) {
2018
- issues.push({
2019
- path: `${path}.packageParts.${packagePartName}.packagePartName`,
2020
- message: "packagePartName must match the map key.",
2021
- });
2022
- }
2023
- if (Array.isArray(packagePartRecord.relationshipIds)) {
2024
- packagePartRecord.relationshipIds.forEach((relationshipId, index) =>
2025
- expectDomainString(
2026
- relationshipId,
2027
- "relationshipId",
2028
- `${path}.packageParts.${packagePartName}.relationshipIds[${index}]`,
2029
- issues,
2030
- ),
2031
- );
2032
- }
2033
- }
2034
- }
2035
- }
2036
-
2037
- function validateDiagnosticStore(
2038
- value: unknown,
2039
- path: string,
2040
- issues: ModelValidationIssue[],
2041
- ): void {
2042
- const record = asPlainObject(value, path, issues);
2043
- if (!record) {
2044
- return;
2045
- }
2046
-
2047
- if (Array.isArray(record.warnings)) {
2048
- record.warnings.forEach((warning, index) => {
2049
- const warningRecord = asPlainObject(warning, `${path}.warnings[${index}]`, issues);
2050
- if (!warningRecord) {
2051
- return;
2052
- }
2053
- expectDomainString(
2054
- warningRecord.diagnosticId,
2055
- "diagnosticId",
2056
- `${path}.warnings[${index}].diagnosticId`,
2057
- issues,
2058
- );
2059
- expectDomainString(
2060
- warningRecord.warningId,
2061
- "warningId",
2062
- `${path}.warnings[${index}].warningId`,
2063
- issues,
2064
- );
2065
- });
2066
- }
2067
-
2068
- if (Array.isArray(record.errors)) {
2069
- record.errors.forEach((error, index) => {
2070
- const errorRecord = asPlainObject(error, `${path}.errors[${index}]`, issues);
2071
- if (!errorRecord) {
2072
- return;
2073
- }
2074
- expectDomainString(
2075
- errorRecord.diagnosticId,
2076
- "diagnosticId",
2077
- `${path}.errors[${index}].diagnosticId`,
2078
- issues,
2079
- );
2080
- });
2081
- }
2082
- }
2083
-
2084
- function validateAnchor(
2085
- value: unknown,
2086
- path: string,
2087
- issues: ModelValidationIssue[],
2088
- ): void {
2089
- const record = asPlainObject(value, path, issues);
2090
- if (!record) {
2091
- return;
2092
- }
2093
-
2094
- const kind = expectString(record.kind, `${path}.kind`, issues);
2095
- if (!kind) {
2096
- return;
2097
- }
2098
-
2099
- if (kind === "range") {
2100
- validateRange(record.range, `${path}.range`, issues);
2101
- validateBoundaryAssoc(record.assoc, `${path}.assoc`, issues);
2102
- } else if (kind === "node") {
2103
- if (typeof record.at !== "number") {
2104
- issues.push({
2105
- path: `${path}.at`,
2106
- message: "node anchors must contain a numeric at value.",
2107
- });
2108
- }
2109
- if (record.assoc !== -1 && record.assoc !== 1) {
2110
- issues.push({
2111
- path: `${path}.assoc`,
2112
- message: "node anchor assoc must be -1 or 1.",
2113
- });
2114
- }
2115
- } else if (kind === "detached") {
2116
- validateRange(record.lastKnownRange, `${path}.lastKnownRange`, issues);
2117
- if (
2118
- record.reason !== "deleted" &&
2119
- record.reason !== "invalidatedByStructureChange" &&
2120
- record.reason !== "importAmbiguity"
2121
- ) {
2122
- issues.push({
2123
- path: `${path}.reason`,
2124
- message: "detached anchor reason must be deleted, invalidatedByStructureChange, or importAmbiguity.",
2125
- });
2126
- }
2127
- } else {
2128
- issues.push({
2129
- path: `${path}.kind`,
2130
- message: "anchor kind must be range, node, or detached.",
2131
- });
2132
- }
2133
- }
2134
-
2135
- function validateBoundaryAssoc(
2136
- value: unknown,
2137
- path: string,
2138
- issues: ModelValidationIssue[],
2139
- ): void {
2140
- const record = asPlainObject(value, path, issues);
2141
- if (!record) {
2142
- return;
2143
- }
2144
-
2145
- if (record.start !== -1 && record.start !== 1) {
2146
- issues.push({
2147
- path: `${path}.start`,
2148
- message: "assoc.start must be -1 or 1.",
2149
- });
2150
- }
2151
- if (record.end !== -1 && record.end !== 1) {
2152
- issues.push({
2153
- path: `${path}.end`,
2154
- message: "assoc.end must be -1 or 1.",
2155
- });
2156
- }
2157
- }
2158
-
2159
- function validateDocumentReferences(
2160
- record: Record<string, unknown>,
2161
- issues: ModelValidationIssue[],
2162
- ): void {
2163
- const paragraphStyleIds = new Set(
2164
- Object.keys(asPlainObject(asPlainObject(record.styles, "$.styles", [])?.paragraphs, "$.styles.paragraphs", []) ?? {}),
2165
- );
2166
- const numberingInstanceIds = new Set(
2167
- Object.keys(asPlainObject(asPlainObject(record.numbering, "$.numbering", [])?.instances, "$.numbering.instances", []) ?? {}),
2168
- );
2169
- const abstractNumberingIds = new Set(
2170
- Object.keys(asPlainObject(asPlainObject(record.numbering, "$.numbering", [])?.abstractDefinitions, "$.numbering.abstractDefinitions", []) ?? {}),
2171
- );
2172
- const mediaIds = new Set(
2173
- Object.keys(asPlainObject(asPlainObject(record.media, "$.media", [])?.items, "$.media.items", []) ?? {}),
2174
- );
2175
- const warningIds = new Set(
2176
- (Array.isArray(asPlainObject(record.diagnostics, "$.diagnostics", [])?.warnings)
2177
- ? (asPlainObject(record.diagnostics, "$.diagnostics", [])?.warnings as Array<Record<string, unknown>>)
2178
- : []
2179
- ).flatMap((warning) => typeof warning.warningId === "string" ? [warning.warningId] : []),
2180
- );
2181
- const fragmentIds = new Set(
2182
- Object.keys(asPlainObject(asPlainObject(record.preservation, "$.preservation", [])?.opaqueFragments, "$.preservation.opaqueFragments", []) ?? {}),
2183
- );
2184
- const noteIds = collectNoteIds(record);
2185
-
2186
- const numberingInstances = asPlainObject(
2187
- asPlainObject(record.numbering, "$.numbering", [])?.instances,
2188
- "$.numbering.instances",
2189
- [],
2190
- );
2191
- if (numberingInstances) {
2192
- for (const [instanceId, instance] of Object.entries(numberingInstances)) {
2193
- const instanceRecord = asPlainObject(instance, `$.numbering.instances.${instanceId}`, []);
2194
- if (
2195
- instanceRecord &&
2196
- typeof instanceRecord.abstractNumberingId === "string" &&
2197
- !abstractNumberingIds.has(instanceRecord.abstractNumberingId)
2198
- ) {
2199
- issues.push({
2200
- path: `$.numbering.instances.${instanceId}.abstractNumberingId`,
2201
- message: "abstractNumberingId must reference an existing abstract numbering definition.",
2202
- });
2203
- }
2204
- }
2205
- }
2206
-
2207
- validateDocumentNodeReferences(record.content, "$.content", issues, {
2208
- paragraphStyleIds,
2209
- numberingInstanceIds,
2210
- mediaIds,
2211
- warningIds,
2212
- fragmentIds,
2213
- noteIds,
2214
- });
2215
- validateSubPartReferences(record.subParts, "$.subParts", issues, {
2216
- paragraphStyleIds,
2217
- numberingInstanceIds,
2218
- mediaIds,
2219
- warningIds,
2220
- fragmentIds,
2221
- noteIds,
2222
- });
2223
- validateReviewReferences(record.review, "$.review", issues, warningIds);
2224
- }
2225
-
2226
- function validateDocumentNodeReferences(
2227
- value: unknown,
2228
- path: string,
2229
- issues: ModelValidationIssue[],
2230
- references: {
2231
- paragraphStyleIds: ReadonlySet<string>;
2232
- numberingInstanceIds: ReadonlySet<string>;
2233
- mediaIds: ReadonlySet<string>;
2234
- warningIds: ReadonlySet<string>;
2235
- fragmentIds: ReadonlySet<string>;
2236
- noteIds: ReadonlySet<string>;
2237
- },
2238
- ): void {
2239
- const record = asPlainObject(value, path, issues);
2240
- if (!record) {
2241
- return;
2242
- }
2243
-
2244
- const type = typeof record.type === "string" ? record.type : undefined;
2245
- if (type === "paragraph") {
2246
- if (
2247
- typeof record.styleId === "string" &&
2248
- !references.paragraphStyleIds.has(record.styleId)
2249
- ) {
2250
- issues.push({
2251
- path: `${path}.styleId`,
2252
- message: "styleId must reference an existing paragraph style definition.",
2253
- });
2254
- }
2255
- const numbering = asPlainObject(record.numbering, `${path}.numbering`, []);
2256
- if (
2257
- numbering &&
2258
- typeof numbering.numberingInstanceId === "string" &&
2259
- !references.numberingInstanceIds.has(numbering.numberingInstanceId)
2260
- ) {
2261
- issues.push({
2262
- path: `${path}.numbering.numberingInstanceId`,
2263
- message: "numberingInstanceId must reference an existing numbering instance.",
2264
- });
2265
- }
2266
- } else if (type === "image") {
2267
- if (typeof record.mediaId === "string" && !references.mediaIds.has(record.mediaId)) {
2268
- issues.push({
2269
- path: `${path}.mediaId`,
2270
- message: "mediaId must reference an existing media catalog item.",
2271
- });
2272
- }
2273
- } else if (type === "opaque_inline" || type === "opaque_block") {
2274
- if (typeof record.fragmentId === "string" && !references.fragmentIds.has(record.fragmentId)) {
2275
- issues.push({
2276
- path: `${path}.fragmentId`,
2277
- message: "fragmentId must reference an existing opaque fragment.",
2278
- });
2279
- }
2280
- if (typeof record.warningId === "string" && !references.warningIds.has(record.warningId)) {
2281
- issues.push({
2282
- path: `${path}.warningId`,
2283
- message: "warningId must reference an existing diagnostic warning.",
2284
- });
2285
- }
2286
- } else if (type === "footnote_ref") {
2287
- if (typeof record.noteId === "string" && !references.noteIds.has(`${record.noteKind}:${record.noteId}`)) {
2288
- issues.push({
2289
- path: `${path}.noteId`,
2290
- message: "noteId must reference an existing footnote or endnote definition.",
2291
- });
2292
- }
2293
- }
2294
-
2295
- if (Array.isArray(record.children)) {
2296
- record.children.forEach((child, index) =>
2297
- validateDocumentNodeReferences(child, `${path}.children[${index}]`, issues, references),
2298
- );
2299
- }
2300
- if (Array.isArray(record.rows)) {
2301
- record.rows.forEach((row, index) =>
2302
- validateDocumentNodeReferences(row, `${path}.rows[${index}]`, issues, references),
2303
- );
2304
- }
2305
- if (Array.isArray(record.cells)) {
2306
- record.cells.forEach((cell, index) =>
2307
- validateDocumentNodeReferences(cell, `${path}.cells[${index}]`, issues, references),
2308
- );
2309
- }
2310
- }
2311
-
2312
- function validateReviewReferences(
2313
- value: unknown,
2314
- path: string,
2315
- issues: ModelValidationIssue[],
2316
- warningIds: ReadonlySet<string>,
2317
- ): void {
2318
- const record = asPlainObject(value, path, issues);
2319
- if (!record) {
2320
- return;
2321
- }
2322
-
2323
- const comments = asPlainObject(record.comments, `${path}.comments`, []);
2324
- if (comments) {
2325
- for (const [commentId, thread] of Object.entries(comments)) {
2326
- const threadRecord = asPlainObject(thread, `${path}.comments.${commentId}`, []);
2327
- if (!threadRecord || !Array.isArray(threadRecord.warningIds)) {
2328
- continue;
2329
- }
2330
- threadRecord.warningIds.forEach((warningId, index) => {
2331
- if (typeof warningId === "string" && !warningIds.has(warningId)) {
2332
- issues.push({
2333
- path: `${path}.comments.${commentId}.warningIds[${index}]`,
2334
- message: "warningIds must reference existing diagnostic warnings.",
2335
- });
2336
- }
2337
- });
2338
- }
2339
- }
2340
-
2341
- const revisions = asPlainObject(record.revisions, `${path}.revisions`, []);
2342
- if (revisions) {
2343
- for (const [revisionId, revision] of Object.entries(revisions)) {
2344
- const revisionRecord = asPlainObject(revision, `${path}.revisions.${revisionId}`, []);
2345
- if (!revisionRecord || !Array.isArray(revisionRecord.warningIds)) {
2346
- continue;
2347
- }
2348
- revisionRecord.warningIds.forEach((warningId, index) => {
2349
- if (typeof warningId === "string" && !warningIds.has(warningId)) {
2350
- issues.push({
2351
- path: `${path}.revisions.${revisionId}.warningIds[${index}]`,
2352
- message: "warningIds must reference existing diagnostic warnings.",
2353
- });
2354
- }
2355
- });
2356
- }
2357
- }
2358
- }
2359
-
2360
- function validateSubPartReferences(
2361
- value: unknown,
2362
- path: string,
2363
- issues: ModelValidationIssue[],
2364
- references: {
2365
- paragraphStyleIds: ReadonlySet<string>;
2366
- numberingInstanceIds: ReadonlySet<string>;
2367
- mediaIds: ReadonlySet<string>;
2368
- warningIds: ReadonlySet<string>;
2369
- fragmentIds: ReadonlySet<string>;
2370
- noteIds: ReadonlySet<string>;
2371
- },
2372
- ): void {
2373
- if (value === undefined) {
2374
- return;
2375
- }
2376
- const record = asPlainObject(value, path, issues);
2377
- if (!record) {
2378
- return;
2379
- }
2380
-
2381
- const headers = Array.isArray(record.headers) ? record.headers : [];
2382
- headers.forEach((header, index) => {
2383
- const headerRecord = asPlainObject(header, `${path}.headers[${index}]`, []);
2384
- if (!headerRecord || !Array.isArray(headerRecord.blocks)) {
2385
- return;
2386
- }
2387
- headerRecord.blocks.forEach((block, blockIndex) =>
2388
- validateDocumentNodeReferences(block, `${path}.headers[${index}].blocks[${blockIndex}]`, issues, references),
2389
- );
2390
- });
2391
-
2392
- const footers = Array.isArray(record.footers) ? record.footers : [];
2393
- footers.forEach((footer, index) => {
2394
- const footerRecord = asPlainObject(footer, `${path}.footers[${index}]`, []);
2395
- if (!footerRecord || !Array.isArray(footerRecord.blocks)) {
2396
- return;
2397
- }
2398
- footerRecord.blocks.forEach((block, blockIndex) =>
2399
- validateDocumentNodeReferences(block, `${path}.footers[${index}].blocks[${blockIndex}]`, issues, references),
2400
- );
2401
- });
2402
-
2403
- if (record.footnoteCollection !== undefined) {
2404
- const footnoteCollection = asPlainObject(record.footnoteCollection, `${path}.footnoteCollection`, []);
2405
- const footnotes = asPlainObject(footnoteCollection?.footnotes, `${path}.footnoteCollection.footnotes`, []);
2406
- if (footnotes) {
2407
- for (const [noteId, note] of Object.entries(footnotes)) {
2408
- const noteRecord = asPlainObject(note, `${path}.footnoteCollection.footnotes.${noteId}`, []);
2409
- if (!noteRecord || !Array.isArray(noteRecord.blocks)) {
2410
- continue;
2411
- }
2412
- noteRecord.blocks.forEach((block, blockIndex) =>
2413
- validateDocumentNodeReferences(
2414
- block,
2415
- `${path}.footnoteCollection.footnotes.${noteId}.blocks[${blockIndex}]`,
2416
- issues,
2417
- references,
2418
- ),
2419
- );
2420
- }
2421
- }
2422
-
2423
- const endnotes = asPlainObject(footnoteCollection?.endnotes, `${path}.footnoteCollection.endnotes`, []);
2424
- if (endnotes) {
2425
- for (const [noteId, note] of Object.entries(endnotes)) {
2426
- const noteRecord = asPlainObject(note, `${path}.footnoteCollection.endnotes.${noteId}`, []);
2427
- if (!noteRecord || !Array.isArray(noteRecord.blocks)) {
2428
- continue;
2429
- }
2430
- noteRecord.blocks.forEach((block, blockIndex) =>
2431
- validateDocumentNodeReferences(
2432
- block,
2433
- `${path}.footnoteCollection.endnotes.${noteId}.blocks[${blockIndex}]`,
2434
- issues,
2435
- references,
2436
- ),
2437
- );
2438
- }
2439
- }
2440
- }
2441
- }
2442
-
2443
- function collectNoteIds(record: Record<string, unknown>): Set<string> {
2444
- const noteIds = new Set<string>();
2445
- const subParts = asPlainObject(record.subParts, "$.subParts", []);
2446
- const footnoteCollection = asPlainObject(subParts?.footnoteCollection, "$.subParts.footnoteCollection", []);
2447
- const footnotes = asPlainObject(footnoteCollection?.footnotes, "$.subParts.footnoteCollection.footnotes", []);
2448
- if (footnotes) {
2449
- Object.keys(footnotes).forEach((noteId) => noteIds.add(`footnote:${noteId}`));
2450
- }
2451
- const endnotes = asPlainObject(footnoteCollection?.endnotes, "$.subParts.footnoteCollection.endnotes", []);
2452
- if (endnotes) {
2453
- Object.keys(endnotes).forEach((noteId) => noteIds.add(`endnote:${noteId}`));
2454
- }
2455
- return noteIds;
2456
- }
2457
-
2458
- function validateRange(
2459
- value: unknown,
2460
- path: string,
2461
- issues: ModelValidationIssue[],
2462
- ): void {
2463
- const record = asPlainObject(value, path, issues);
2464
- if (!record) {
2465
- return;
2466
- }
2467
-
2468
- if (typeof record.from !== "number" || typeof record.to !== "number") {
2469
- issues.push({
2470
- path,
2471
- message: "Range must contain numeric from and to values.",
2472
- });
2473
- }
2474
- }
2475
-
2476
- function validateExactObjectKeys(
2477
- record: Record<string, unknown>,
2478
- expectedKeys: readonly string[],
2479
- path: string,
2480
- issues: ModelValidationIssue[],
2481
- allowedOptionalKeys?: readonly string[],
2482
- ): void {
2483
- const actualKeys = new Set(Object.keys(record));
2484
- const expected = new Set(expectedKeys);
2485
- const optional = new Set(allowedOptionalKeys ?? []);
2486
-
2487
- for (const expectedKey of expectedKeys) {
2488
- if (!actualKeys.has(expectedKey)) {
2489
- issues.push({
2490
- path,
2491
- message: `Missing required key ${JSON.stringify(expectedKey)}.`,
2492
- });
2493
- }
2494
- }
2495
-
2496
- for (const actualKey of actualKeys) {
2497
- if (!expected.has(actualKey) && !optional.has(actualKey)) {
2498
- issues.push({
2499
- path: `${path}.${actualKey}`,
2500
- message:
2501
- "Unexpected canonical document key. Render/UI state is not part of the canonical envelope.",
2502
- });
2503
- }
2504
- }
2505
- }
2506
-
2507
- function validateSubPartsCatalog(
2508
- value: unknown,
2509
- path: string,
2510
- issues: ModelValidationIssue[],
2511
- ): void {
2512
- if (value === undefined) {
2513
- return;
2514
- }
2515
- const record = asPlainObject(value, path, issues);
2516
- if (!record) {
2517
- return;
2518
- }
2519
-
2520
- if (record.headers !== undefined) {
2521
- if (!Array.isArray(record.headers)) {
2522
- issues.push({ path: `${path}.headers`, message: "headers must be an array." });
2523
- } else {
2524
- record.headers.forEach((header, index) => {
2525
- const headerRecord = asPlainObject(header, `${path}.headers[${index}]`, issues);
2526
- if (headerRecord) {
2527
- expectString(headerRecord.variant, `${path}.headers[${index}].variant`, issues);
2528
- expectString(headerRecord.partPath, `${path}.headers[${index}].partPath`, issues);
2529
- expectString(headerRecord.relationshipId, `${path}.headers[${index}].relationshipId`, issues);
2530
- if (!Array.isArray(headerRecord.blocks)) {
2531
- issues.push({ path: `${path}.headers[${index}].blocks`, message: "blocks must be an array." });
2532
- } else {
2533
- headerRecord.blocks.forEach((block, blockIndex) =>
2534
- validateDocumentNode(block, `${path}.headers[${index}].blocks[${blockIndex}]`, issues),
2535
- );
2536
- }
2537
- }
2538
- });
2539
- }
2540
- }
2541
-
2542
- if (record.footers !== undefined) {
2543
- if (!Array.isArray(record.footers)) {
2544
- issues.push({ path: `${path}.footers`, message: "footers must be an array." });
2545
- } else {
2546
- record.footers.forEach((footer, index) => {
2547
- const footerRecord = asPlainObject(footer, `${path}.footers[${index}]`, issues);
2548
- if (footerRecord) {
2549
- expectString(footerRecord.variant, `${path}.footers[${index}].variant`, issues);
2550
- expectString(footerRecord.partPath, `${path}.footers[${index}].partPath`, issues);
2551
- expectString(footerRecord.relationshipId, `${path}.footers[${index}].relationshipId`, issues);
2552
- if (!Array.isArray(footerRecord.blocks)) {
2553
- issues.push({ path: `${path}.footers[${index}].blocks`, message: "blocks must be an array." });
2554
- } else {
2555
- footerRecord.blocks.forEach((block, blockIndex) =>
2556
- validateDocumentNode(block, `${path}.footers[${index}].blocks[${blockIndex}]`, issues),
2557
- );
2558
- }
2559
- }
2560
- });
2561
- }
2562
- }
2563
-
2564
- if (record.footnoteCollection !== undefined) {
2565
- const footnoteCollection = asPlainObject(record.footnoteCollection, `${path}.footnoteCollection`, issues);
2566
- if (footnoteCollection) {
2567
- validateSubPartNoteMap(footnoteCollection.footnotes, "footnote", `${path}.footnoteCollection.footnotes`, issues);
2568
- validateSubPartNoteMap(footnoteCollection.endnotes, "endnote", `${path}.footnoteCollection.endnotes`, issues);
2569
- }
2570
- }
2571
- }
2572
-
2573
- function validateSubPartNoteMap(
2574
- value: unknown,
2575
- expectedKind: "footnote" | "endnote",
2576
- path: string,
2577
- issues: ModelValidationIssue[],
2578
- ): void {
2579
- const record = asPlainObject(value, path, issues);
2580
- if (!record) {
2581
- return;
2582
- }
2583
-
2584
- for (const [noteId, note] of Object.entries(record)) {
2585
- const noteRecord = asPlainObject(note, `${path}.${noteId}`, issues);
2586
- if (!noteRecord) {
2587
- continue;
2588
- }
2589
- expectString(noteRecord.noteId, `${path}.${noteId}.noteId`, issues);
2590
- if (noteRecord.noteId !== noteId) {
2591
- issues.push({
2592
- path: `${path}.${noteId}.noteId`,
2593
- message: "noteId must match the map key.",
2594
- });
2595
- }
2596
- if (noteRecord.kind !== expectedKind) {
2597
- issues.push({
2598
- path: `${path}.${noteId}.kind`,
2599
- message: `kind must be ${expectedKind}.`,
2600
- });
2601
- }
2602
- if (!Array.isArray(noteRecord.blocks)) {
2603
- issues.push({ path: `${path}.${noteId}.blocks`, message: "blocks must be an array." });
2604
- } else {
2605
- noteRecord.blocks.forEach((block, blockIndex) =>
2606
- validateDocumentNode(block, `${path}.${noteId}.blocks[${blockIndex}]`, issues),
2607
- );
2608
- }
2609
- }
2610
- }
2611
-
2612
- function expectDomainString(
2613
- value: unknown,
2614
- domain: StableIdDomain,
2615
- path: string,
2616
- issues: ModelValidationIssue[],
2617
- ): string | null {
2618
- const stableId = expectString(value, path, issues);
2619
- if (!stableId) {
2620
- return null;
2621
- }
2622
-
2623
- if (!ID_PATTERNS[domain].test(stableId)) {
2624
- issues.push({
2625
- path,
2626
- message: `Expected a valid ${domain}.`,
2627
- });
2628
- return null;
2629
- }
2630
-
2631
- return stableId;
2632
- }