@beyondwork/docx-react-component 1.0.27 → 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 (356) 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 +64 -54
  156. package/src/README.md +0 -85
  157. package/src/api/README.md +0 -26
  158. package/src/api/public-types.ts +0 -1418
  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/collab-review-sync.ts +0 -254
  270. package/src/runtime/document-layout.ts +0 -332
  271. package/src/runtime/document-navigation.ts +0 -603
  272. package/src/runtime/document-runtime.ts +0 -3159
  273. package/src/runtime/document-search.ts +0 -145
  274. package/src/runtime/numbering-prefix.ts +0 -216
  275. package/src/runtime/page-layout-estimation.ts +0 -212
  276. package/src/runtime/read-only-diagnostics-runtime.ts +0 -241
  277. package/src/runtime/review-runtime.ts +0 -44
  278. package/src/runtime/revision-runtime.ts +0 -107
  279. package/src/runtime/session-capabilities.ts +0 -192
  280. package/src/runtime/story-context.ts +0 -164
  281. package/src/runtime/story-targeting.ts +0 -162
  282. package/src/runtime/surface-projection.ts +0 -1357
  283. package/src/runtime/table-commands.ts +0 -173
  284. package/src/runtime/table-schema.ts +0 -309
  285. package/src/runtime/view-state.ts +0 -477
  286. package/src/runtime/virtualized-rendering.ts +0 -258
  287. package/src/runtime/workflow-markup.ts +0 -353
  288. package/src/ui/README.md +0 -30
  289. package/src/ui/WordReviewEditor.tsx +0 -4097
  290. package/src/ui/browser-export.ts +0 -52
  291. package/src/ui/comments/README.md +0 -3
  292. package/src/ui/compatibility/README.md +0 -3
  293. package/src/ui/editor-command-bag.ts +0 -120
  294. package/src/ui/editor-runtime-boundary.ts +0 -1457
  295. package/src/ui/editor-shell-view.tsx +0 -142
  296. package/src/ui/editor-surface/README.md +0 -3
  297. package/src/ui/editor-surface-controller.tsx +0 -63
  298. package/src/ui/headless/comment-decoration-model.ts +0 -124
  299. package/src/ui/headless/preserve-editor-selection.ts +0 -5
  300. package/src/ui/headless/revision-decoration-model.ts +0 -128
  301. package/src/ui/headless/selection-helpers.ts +0 -54
  302. package/src/ui/headless/selection-toolbar-model.ts +0 -34
  303. package/src/ui/headless/use-editor-keyboard.ts +0 -103
  304. package/src/ui/review/README.md +0 -3
  305. package/src/ui/runtime-snapshot-selectors.ts +0 -197
  306. package/src/ui/shared/revision-filters.ts +0 -31
  307. package/src/ui/status/README.md +0 -3
  308. package/src/ui/theme/README.md +0 -3
  309. package/src/ui/toolbar/README.md +0 -3
  310. package/src/ui/workflow-surface-blocked-rails.ts +0 -94
  311. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +0 -64
  312. package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +0 -129
  313. package/src/ui-tailwind/chrome/tw-layout-panel.tsx +0 -114
  314. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +0 -34
  315. package/src/ui-tailwind/chrome/tw-page-ruler.tsx +0 -386
  316. package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +0 -186
  317. package/src/ui-tailwind/chrome/tw-suggestion-card.tsx +0 -139
  318. package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +0 -128
  319. package/src/ui-tailwind/chrome/tw-unsaved-modal.tsx +0 -58
  320. package/src/ui-tailwind/chrome/use-before-unload.ts +0 -20
  321. package/src/ui-tailwind/editor-surface/perf-probe.ts +0 -179
  322. package/src/ui-tailwind/editor-surface/pm-collab-plugins.ts +0 -40
  323. package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +0 -178
  324. package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +0 -31
  325. package/src/ui-tailwind/editor-surface/pm-decorations.ts +0 -427
  326. package/src/ui-tailwind/editor-surface/pm-position-map.ts +0 -123
  327. package/src/ui-tailwind/editor-surface/pm-schema.ts +0 -876
  328. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +0 -504
  329. package/src/ui-tailwind/editor-surface/search-plugin.ts +0 -168
  330. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +0 -61
  331. package/src/ui-tailwind/editor-surface/tw-caret.tsx +0 -12
  332. package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +0 -150
  333. package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +0 -129
  334. package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +0 -58
  335. package/src/ui-tailwind/editor-surface/tw-paragraph-block.tsx +0 -151
  336. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +0 -973
  337. package/src/ui-tailwind/editor-surface/tw-segment-view.tsx +0 -111
  338. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -436
  339. package/src/ui-tailwind/index.ts +0 -62
  340. package/src/ui-tailwind/page-chrome-model.ts +0 -27
  341. package/src/ui-tailwind/review/tw-comment-sidebar.tsx +0 -406
  342. package/src/ui-tailwind/review/tw-health-panel.tsx +0 -149
  343. package/src/ui-tailwind/review/tw-review-rail.tsx +0 -120
  344. package/src/ui-tailwind/review/tw-revision-sidebar.tsx +0 -164
  345. package/src/ui-tailwind/status/tw-status-bar.tsx +0 -61
  346. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +0 -52
  347. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +0 -1064
  348. package/src/ui-tailwind/tw-review-workspace.tsx +0 -1417
  349. package/src/validation/README.md +0 -3
  350. package/src/validation/compatibility-engine.ts +0 -634
  351. package/src/validation/compatibility-report.ts +0 -161
  352. package/src/validation/diagnostics.ts +0 -204
  353. package/src/validation/docx-comment-proof.ts +0 -707
  354. package/src/validation/import-diagnostics.ts +0 -128
  355. package/src/validation/low-priority-word-surfaces.ts +0 -373
  356. /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
- }