@docx-editor.dev/core 2.20.0 → 2.21.1

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 (115) hide show
  1. package/README.md +38 -59
  2. package/dist/automation.cjs +1 -1
  3. package/dist/automation.d.ts +11 -10
  4. package/dist/automation.js +1 -1
  5. package/dist/binding.cjs +1 -1
  6. package/dist/binding.d.ts +9 -9
  7. package/dist/binding.js +1 -1
  8. package/dist/chunk-25OQBAKA.js +2 -0
  9. package/dist/chunk-3B3DQMSL.js +11 -0
  10. package/dist/chunk-3P3DNJYM.js +5 -0
  11. package/dist/chunk-3PA64KPK.js +5 -0
  12. package/dist/chunk-4TYA4RLP.cjs +1 -0
  13. package/dist/chunk-7N77EN2Z.cjs +2 -0
  14. package/dist/{chunk-GSEUM6AI.cjs → chunk-AGWWNVIJ.cjs} +1 -1
  15. package/dist/chunk-AJCCS53D.js +1 -0
  16. package/dist/chunk-BF2TOG2V.js +1 -0
  17. package/dist/chunk-DFTVN25X.cjs +5 -0
  18. package/dist/chunk-DGONGMAV.js +2 -0
  19. package/dist/chunk-DZYKU2HG.js +1 -0
  20. package/dist/chunk-E5OUYRNM.cjs +5 -0
  21. package/dist/chunk-F4H3LC6M.cjs +2 -0
  22. package/dist/{chunk-CAIWEQLC.js → chunk-FPDIJ4RF.js} +1 -1
  23. package/dist/chunk-IUTTOBQW.cjs +20 -0
  24. package/dist/{chunk-OA6LFCTG.cjs → chunk-IYCXQAFT.cjs} +3 -3
  25. package/dist/chunk-JL3SI5SZ.js +18 -0
  26. package/dist/chunk-OFWUHH52.js +20 -0
  27. package/dist/chunk-OKY6JRJL.js +4 -0
  28. package/dist/{chunk-PIBYJZIH.cjs → chunk-P24FHMQW.cjs} +1 -1
  29. package/dist/chunk-QBKAOSVF.cjs +18 -0
  30. package/dist/{chunk-2HQXLL5L.cjs → chunk-RA2TDTEQ.cjs} +1 -1
  31. package/dist/chunk-S6J4O5XP.js +1 -0
  32. package/dist/{chunk-WTILOUPG.cjs → chunk-T5B4NMKF.cjs} +1 -1
  33. package/dist/chunk-TKQH7BNE.cjs +1 -0
  34. package/dist/{chunk-OHVJYJOA.js → chunk-U5GUJKTS.js} +1 -1
  35. package/dist/chunk-UKVVUHZL.cjs +1 -0
  36. package/dist/chunk-XRYSPKKC.js +1 -0
  37. package/dist/chunk-ZZRCVDQU.cjs +11 -0
  38. package/dist/collaboration/replication.cjs +1 -1
  39. package/dist/collaboration/replication.d.ts +13 -4
  40. package/dist/collaboration/replication.js +1 -1
  41. package/dist/collaboration.d.ts +4 -3
  42. package/dist/contracts/document.d.ts +3 -2
  43. package/dist/contracts/editor.cjs +1 -1
  44. package/dist/contracts/editor.d.ts +9 -9
  45. package/dist/contracts/editor.js +1 -1
  46. package/dist/contracts/interaction.d.ts +2 -2
  47. package/dist/contracts/modules.cjs +1 -1
  48. package/dist/contracts/modules.d.ts +9 -7
  49. package/dist/contracts/modules.js +1 -1
  50. package/dist/contracts/types.d.ts +2 -1
  51. package/dist/{custom-node-writes-piokwVR5.d.ts → custom-node-writes-CncR6KfC.d.ts} +2 -2
  52. package/dist/{document-protection-C6RvCHx-.d.ts → document-protection-DM6mA8Rf.d.ts} +1 -1
  53. package/dist/{editor-BgtcHulQ.d.ts → editor-BRh7GuZF.d.ts} +33 -30
  54. package/dist/editor-scope-OHPwVQ5l.d.ts +84 -0
  55. package/dist/editor.cjs +1 -1
  56. package/dist/editor.css +1 -1
  57. package/dist/editor.d.ts +130 -27
  58. package/dist/editor.js +1 -1
  59. package/dist/export.cjs +2 -2
  60. package/dist/export.d.ts +13 -13
  61. package/dist/export.js +1 -1
  62. package/dist/{font-resolver-Bv2Z25-J.d.ts → font-resolver-DwuQYj3P.d.ts} +1 -1
  63. package/dist/{headless-document-view-DHKMN_3Y.d.ts → headless-document-view-C3fc_jV0.d.ts} +2 -2
  64. package/dist/{image-resources-C51LuAAu.d.ts → image-resources-DxF-SN_8.d.ts} +1 -1
  65. package/dist/{index-3lr8xXF8.d.ts → index-3pNnG4z4.d.ts} +122 -30
  66. package/dist/index.cjs +1 -1
  67. package/dist/index.d.ts +18 -18
  68. package/dist/index.js +1 -1
  69. package/dist/{layout-shaping-CKBUSaS7.d.ts → layout-shaping-_GGjhhVc.d.ts} +1 -1
  70. package/dist/layout.cjs +1 -1
  71. package/dist/layout.d.ts +43 -18
  72. package/dist/layout.js +1 -1
  73. package/dist/{note-properties-CcXUgqr8.d.ts → note-properties-CjT31AuP.d.ts} +1 -1
  74. package/dist/{omml-equation-C5P6Ym3v.d.ts → omml-equation-Bk3OP1fv.d.ts} +1 -1
  75. package/dist/output.cjs +1 -1
  76. package/dist/output.d.ts +8 -7
  77. package/dist/output.js +1 -1
  78. package/dist/{protocol-CsawNZkt.d.ts → protocol-CrbTIW2Z.d.ts} +2 -2
  79. package/dist/{review-paragraph-order-BCIqjopY.d.ts → review-paragraph-order-D3KZohIL.d.ts} +1 -1
  80. package/dist/{revision-projection-BcRnC4Yj.d.ts → revision-projection-kVd7p6Wd.d.ts} +16 -2
  81. package/dist/{semantic-cell-selection-R9wrenwR.d.ts → semantic-cell-selection-CkSsZHDL.d.ts} +11 -4
  82. package/dist/{semantic-interaction-B7beVwH7.d.ts → semantic-interaction-Z2Xa68U5.d.ts} +1 -1
  83. package/dist/{semantic-paint-CLvHwf8y.d.ts → semantic-paint-BHKprI9P.d.ts} +18 -2
  84. package/dist/{semantic-records-CbXwnS80.d.ts → semantic-records-StH-AnHK.d.ts} +153 -10
  85. package/dist/store.cjs +1 -1
  86. package/dist/store.d.ts +468 -439
  87. package/dist/store.js +1 -1
  88. package/dist/{note-lifecycle-D4AOFCAt.d.ts → tree-package-fragment-CpEV1gl3.d.ts} +183 -143
  89. package/dist/{tree-session-contract-DTQxw2zL.d.ts → tree-session-contract-CTT9nM7Z.d.ts} +13 -8
  90. package/dist/{types-barrel-CEOy-Jv0.d.ts → types-barrel-PwcfEkaX.d.ts} +1481 -1433
  91. package/package.json +2 -2
  92. package/dist/chunk-2H4ABRMB.cjs +0 -20
  93. package/dist/chunk-3EZO3O6I.js +0 -4
  94. package/dist/chunk-44SIVFJT.cjs +0 -5
  95. package/dist/chunk-6UCDHGXI.js +0 -1
  96. package/dist/chunk-7VMVDRRU.js +0 -2
  97. package/dist/chunk-CEXC2VHI.cjs +0 -1
  98. package/dist/chunk-CF3JR35B.js +0 -11
  99. package/dist/chunk-D2V4YBWJ.cjs +0 -2
  100. package/dist/chunk-D5EHDHYV.js +0 -1
  101. package/dist/chunk-DGSJZMAE.cjs +0 -11
  102. package/dist/chunk-EHBPYFRE.js +0 -20
  103. package/dist/chunk-GOEBQ26R.js +0 -1
  104. package/dist/chunk-H6WDEAOJ.js +0 -18
  105. package/dist/chunk-I6LSEDUL.cjs +0 -18
  106. package/dist/chunk-IW35GWJN.js +0 -1
  107. package/dist/chunk-JRBGCIR2.js +0 -5
  108. package/dist/chunk-K5HIOS6J.cjs +0 -3
  109. package/dist/chunk-KLQXP27K.js +0 -3
  110. package/dist/chunk-MZFFU66H.cjs +0 -2
  111. package/dist/chunk-MZPNLIP3.js +0 -2
  112. package/dist/chunk-OHJQ7GPZ.cjs +0 -1
  113. package/dist/chunk-P4TGGDK3.js +0 -1
  114. package/dist/chunk-ZKWRCLT2.cjs +0 -1
  115. package/dist/editor-scope-DZJT4ukT.d.ts +0 -53
@@ -1,225 +1,4 @@
1
- /**
2
- * Why a part or relationship name was refused.
3
- *
4
- * Path traversal is the reason this exists: a name with `..` or a leading `/` is refused rather
5
- * than normalized, because normalizing is how a crafted package reaches outside itself.
6
- */
7
- type NameRejection = 'empty' | 'control-char' | 'backslash' | 'drive-or-unc' | 'encoded-separator' | 'encoded-dot' | 'bad-encoding' | 'empty-segment' | 'dot-segment' | 'unsafe-key' | 'segment-trailing-dot' | 'traversal-escape' | 'not-absolute-uri' | 'unsafe-scheme';
8
- /** A validated OPC name, or the typed reason it was refused. */
9
- type NameResult = {
10
- readonly ok: true;
11
- readonly partName: string;
12
- } | {
13
- readonly ok: false;
14
- readonly reason: NameRejection;
15
- };
16
- /**
17
- * Normalize a ZIP entry name or OPC part name into a canonical part name
18
- * (`/word/document.xml`). Accepts leading-slash and no-leading-slash inputs;
19
- * rejects traversal, dot segments, and the attack surface. Duplicate detection
20
- * folds ASCII case (OPC part-name equivalence).
21
- */
22
- declare function normalizePartName(raw: string): NameResult;
23
- /**
24
- * ASCII-only lowercase fold. OPC part-name/extension equivalence is US-ASCII
25
- * case-insensitive; a locale `toLowerCase()` mis-folds e.g. Turkish `I`/`İ` and
26
- * could let two "equivalent" part names evade duplicate detection.
27
- */
28
- declare function asciiFold(s: string): string;
29
- /** Case-folded key for OPC part-name equivalence / duplicate detection. */
30
- declare function partNameKey(partName: string): string;
31
- /**
32
- * Detect archive entries that collide after normalization (before inflation).
33
- * Returns the case-folded keys that more than one raw name maps to.
34
- */
35
- declare function detectDuplicateNames(rawNames: readonly string[]): {
36
- readonly duplicates: readonly string[];
37
- readonly rejected: readonly {
38
- raw: string;
39
- reason: NameRejection;
40
- }[];
41
- };
42
- /**
43
- * Resolve an internal relationship target against its owner part, without
44
- * escaping the package root. `ownerPartName` is a canonical part name; the base
45
- * is its containing folder. A leading "/" target is package-absolute. `.`/`..`
46
- * segments resolve on a stack; popping above root is `traversal-escape`.
47
- */
48
- declare function resolveInternalTarget(ownerPartName: string, rawTarget: string): NameResult;
49
- /**
50
- * Validate an external-mode relationship target. It MUST be an absolute URI, is
51
- * retained verbatim by the caller, and is NEVER owner-resolved or fetched here.
52
- * Unsafe schemes (javascript/vbscript/data/file) are rejected for runtime sinks;
53
- * the raw lexical form is still preserved separately by the authored record.
54
- */
55
- declare function validateExternalTarget(raw: string): NameResult;
56
-
57
- /** One `<Default>`: a file extension mapped to a content type. */
58
- interface DefaultRecord {
59
- readonly extension: string;
60
- readonly contentType: string;
61
- readonly order: number;
62
- }
63
- /** One `<Override>`: a specific part name mapped to a content type. Beats any Default. */
64
- interface OverrideRecord {
65
- readonly partName: string;
66
- readonly contentType: string;
67
- readonly order: number;
68
- }
69
- /**
70
- * The authored `[Content_Types].xml` records, in significant order.
71
- *
72
- * Retained rather than collapsed into a lookup, because the file's lexical form and ordering are
73
- * part of what a lossless save re-emits.
74
- */
75
- interface ContentTypeRecords {
76
- readonly defaults: readonly DefaultRecord[];
77
- readonly overrides: readonly OverrideRecord[];
78
- }
79
- /**
80
- * Why content-type records could not be indexed.
81
- *
82
- * All fail CLOSED: conflicting Defaults on one extension, duplicate normalized Override names, or
83
- * invalid MIME syntax are refused rather than resolved by picking one.
84
- */
85
- type ContentTypeError = {
86
- readonly code: 'invalid-mime';
87
- readonly value: string;
88
- } | {
89
- readonly code: 'conflicting-default';
90
- readonly extension: string;
91
- } | {
92
- readonly code: 'duplicate-override';
93
- readonly partName: string;
94
- } | {
95
- readonly code: 'invalid-override-name';
96
- readonly partName: string;
97
- } | {
98
- readonly code: 'too-many-records';
99
- readonly limit: number;
100
- };
101
- /** Whether a string is syntactically a MIME type. Syntax only — no registry lookup. */
102
- declare function isValidMime(value: string): boolean;
103
- /** ASCII-case-insensitive extension key (leading dot removed; ASCII-only fold). */
104
- declare function extensionKey(extension: string): string;
105
- /** The resolved lookup: Override by case-folded part name, Default by case-insensitive extension. */
106
- interface ContentTypeIndex {
107
- /** ext key -> single MIME (identical duplicates collapsed). */
108
- readonly defaults: ReadonlyMap<string, string>;
109
- /** case-folded part name -> MIME. */
110
- readonly overrides: ReadonlyMap<string, string>;
111
- }
112
- /** The built index, or the conflict that made it impossible. */
113
- type IndexResult = {
114
- readonly ok: true;
115
- readonly index: ContentTypeIndex;
116
- } | {
117
- readonly ok: false;
118
- readonly error: ContentTypeError;
119
- };
120
- /**
121
- * Build a resolved content-type index, failing closed on conflict/duplicate/MIME
122
- * errors. `maxRecords` bounds the combined record count (N/N+1 gate).
123
- */
124
- declare function buildContentTypeIndex(records: ContentTypeRecords, maxRecords?: number): IndexResult;
125
- /**
126
- * A part's content type, or why it has none.
127
- *
128
- * An orphan record never determines a part's type — a Default with no matching part is preserved
129
- * inertly rather than applied.
130
- */
131
- type ResolveResult = {
132
- readonly ok: true;
133
- readonly contentType: string;
134
- readonly source: 'override' | 'default';
135
- } | {
136
- readonly ok: false;
137
- readonly reason: 'unknown';
138
- };
139
- /** Resolve a part's content type: Override wins over Default; else unknown. */
140
- declare function resolveContentType(index: ContentTypeIndex, partName: string): ResolveResult;
141
-
142
- /** OOXML image relationship type for embedded or linked media parts. */
143
- declare const IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
144
- /**
145
- * Whether a relationship points inside the package or out of it.
146
- *
147
- * The security-relevant distinction: an `External` target is retained VERBATIM and never
148
- * owner-resolved or fetched, because auto-loading a file-supplied remote target is a zero-click
149
- * external fetch.
150
- */
151
- type TargetMode = 'Internal' | 'External';
152
- /**
153
- * One authored relationship, with nothing materialized away: owner part, id, type, raw target
154
- * lexical form, mode, and position.
155
- */
156
- interface RelationshipRecord {
157
- readonly ownerPart: string;
158
- readonly id: string;
159
- readonly type: string;
160
- readonly rawTarget: string;
161
- readonly targetMode: TargetMode;
162
- readonly order: number;
163
- }
164
- /** Why a relationship set is invalid. Duplicate ids within one owner fail closed. */
165
- type RelationshipError = {
166
- readonly code: 'duplicate-id';
167
- readonly ownerPart: string;
168
- readonly id: string;
169
- };
170
- /** The validated relationship set, or the conflict that rejected it. */
171
- type RelationshipSetResult = {
172
- readonly ok: true;
173
- readonly byOwner: ReadonlyMap<string, readonly RelationshipRecord[]>;
174
- } | {
175
- readonly ok: false;
176
- readonly error: RelationshipError;
177
- };
178
- /** Group relationships by owner in authored order; reject duplicate ids per owner. */
179
- declare function buildRelationshipSet(records: readonly RelationshipRecord[]): RelationshipSetResult;
180
- /**
181
- * A relationship resolved to a part, or the reason it could not be.
182
- *
183
- * External targets resolve to a refusal by design: they are never followed from a file.
184
- */
185
- type ResolvedRelationship = {
186
- readonly mode: 'Internal';
187
- readonly target: NameResult;
188
- readonly raw: string;
189
- } | {
190
- readonly mode: 'External';
191
- readonly sinkSafe: NameResult;
192
- readonly raw: string;
193
- };
194
- /**
195
- * Resolve a relationship to a runtime projection while the raw target stays
196
- * authored. Internal -> owner-relative part name; External -> sink-safe
197
- * validation only (never owner-resolved, never fetched). `raw` is always the
198
- * verbatim authored target.
199
- */
200
- declare function resolveRelationship(rec: RelationshipRecord): ResolvedRelationship;
201
- /**
202
- * An image relationship resolved to package bytes, or why it was refused.
203
- *
204
- * External-mode image rels are refused rather than fetched — the no-zero-click-external-fetch
205
- * rule applies to images exactly as it does to links.
206
- */
207
- type ImageRelationshipResolution = {
208
- readonly mode: 'internal';
209
- readonly partName: string;
210
- readonly raw: string;
211
- } | {
212
- readonly mode: 'external';
213
- readonly sinkSafe: boolean;
214
- readonly raw: string;
215
- } | {
216
- readonly mode: 'missing';
217
- };
218
- /**
219
- * Resolve an image relationship id from an owner part. Internal targets resolve
220
- * owner-relative; external targets are never fetched; a missing id is `missing`.
221
- */
222
- declare function resolveImageRelationship(records: readonly RelationshipRecord[] | undefined, ownerPart: string, relationshipId: string): ImageRelationshipResolution;
1
+ import { c as HistoryGroupOutcome } from './editor-scope-OHPwVQ5l.js';
223
2
 
224
3
  /**
225
4
  * One parsed XML node, preserving significant order, whitespace and raw lexical values.
@@ -1092,38 +871,433 @@ type OoxmlInvariantResult = {
1092
871
  */
1093
872
  declare function readOoxmlPart(xml: string, metadata: OoxmlPartMetadata, limits?: XmlLimits): OoxmlReadResult;
1094
873
 
1095
- /**
1096
- * Why a zip was refused at the trust boundary.
1097
- *
1098
- * `too-large` and `too-many-entries` are the zip-bomb guards; `bad-name` catches path traversal —
1099
- * an entry with `..` or a leading `/` is rejected rather than normalized.
1100
- */
1101
- type ZipRejection = 'too-many-entries' | 'too-large' | 'bad-name' | 'inflate-error';
1102
- /** Archive caps: entry count, total decompressed bytes, and the decompression ratio. */
1103
- interface ZipLimits {
1104
- readonly maxEntries: number;
1105
- /** Max total UNCOMPRESSED bytes across the archive. */
1106
- readonly maxTotalBytes: number;
1107
- /** Max per-entry uncompressed:compressed ratio (zip-bomb guard). */
1108
- readonly maxRatio?: number;
1109
- }
1110
- /** The archive caps in force when a host configures none. Conservative and finite. */
1111
- declare const DEFAULT_ZIP_LIMITS: ZipLimits;
1112
- /** The read entries, or the typed refusal. Never throws — the bytes are untrusted. */
1113
- type ZipReadResult = {
1114
- readonly ok: true;
1115
- readonly entries: ReadonlyMap<string, Uint8Array>;
874
+ type AutomationTableMutation = {
875
+ readonly kind: 'values';
876
+ readonly values: readonly (readonly string[])[];
1116
877
  } | {
1117
- readonly ok: false;
1118
- readonly reason: ZipRejection;
1119
- readonly detail?: string;
1120
- readonly limit?: 'zip.maxTotalBytes' | 'zip.maxRatio';
1121
- };
1122
- /**
1123
- * Inflate a ZIP archive with bounds + OPC name normalization. Entry name, count,
1124
- * compression-ratio, and total-uncompressed-size limits are enforced BEFORE each
1125
- * entry is decompressed (via fflate's pre-inflation filter), so a zip bomb or a
1126
- * traversal name is rejected without ever being inflated. Keys are canonical part
878
+ readonly kind: 'addRows' | 'addColumns';
879
+ readonly location: 'start' | 'end';
880
+ readonly count: number;
881
+ readonly values?: readonly (readonly string[])[];
882
+ } | {
883
+ readonly kind: 'deleteRows' | 'deleteColumns';
884
+ readonly index: number;
885
+ readonly count: number;
886
+ } | {
887
+ readonly kind: 'delete';
888
+ } | {
889
+ readonly kind: 'properties';
890
+ readonly styleId?: string;
891
+ readonly headerRowCount?: number;
892
+ } | {
893
+ readonly kind: 'cell';
894
+ readonly cellId: string;
895
+ readonly value?: string;
896
+ readonly columnWidth?: number;
897
+ readonly shadingColor?: string;
898
+ readonly verticalAlignment?: 'Top' | 'Center' | 'Bottom';
899
+ };
900
+
901
+ /** Select one declared entry of a legacy Word dropdown form field. */
902
+ interface SetLegacyDropdownOp {
903
+ readonly op: 'setLegacyDropdown';
904
+ readonly paragraphId: string;
905
+ readonly fieldNodeId: string;
906
+ readonly selectedIndex: number;
907
+ }
908
+
909
+ interface SetFieldCodeOp {
910
+ readonly op: 'setFieldCode';
911
+ readonly paragraphId: string;
912
+ readonly fieldNodeId: string;
913
+ readonly code: string;
914
+ }
915
+
916
+ type InsertTextOp = {
917
+ readonly op: 'insertText';
918
+ readonly paragraphId: string;
919
+ /** Legacy text form identity for protected filling; never an inline wrapper owner. */
920
+ readonly textFormFieldId?: string;
921
+ readonly offset: number;
922
+ readonly text: string;
923
+ /**
924
+ * Write this as a TRACKED insertion, attributed here.
925
+ *
926
+ * On the op rather than on the store, so suggesting stays a decision the surface makes
927
+ * per edit and the write vocabulary stays explicit — a global "everything is tracked
928
+ * now" flag is exactly what `DocEdits` refuses, because it makes the meaning of an op
929
+ * depend on state the op does not carry.
930
+ */
931
+ readonly revision?: RevisionAttributionInput;
932
+ /**
933
+ * When set, the text belongs INSIDE this content control, whatever sits at the offset.
934
+ *
935
+ * A boundary offset is owned by the run that starts there, which at a control's trailing
936
+ * edge is the run after the control — so an offset alone cannot say "append to this field",
937
+ * the way it cannot say which run of a field result to format (see `targetRunIds`). A
938
+ * caller that names the control gets the text in the control; one that does not gets the
939
+ * plain offset rule, which is what a keystroke beside a field means.
940
+ */
941
+ readonly inside?: string;
942
+ /**
943
+ * Which side of a run BOUNDARY the text joins. Default `'left'` — Word's typing rule:
944
+ * the next character takes the formatting of the character before the caret.
945
+ *
946
+ * `'right'` is for a caller that is not typing but placing text inside the run that
947
+ * STARTS at the offset — the hyperlink editor rewriting a link's display text, where
948
+ * landing left of the boundary would put the new text outside the link. Ignored when
949
+ * the offset falls strictly inside a run, which has no boundary to choose.
950
+ */
951
+ readonly bias?: 'left' | 'right';
952
+ };
953
+ type DeleteTextOp = {
954
+ readonly op: 'deleteText';
955
+ readonly paragraphId: string;
956
+ /** Legacy text form identity for protected filling; never an inline wrapper owner. */
957
+ readonly textFormFieldId?: string;
958
+ readonly start: number;
959
+ readonly end: number;
960
+ /** Write this as a TRACKED deletion — the characters stay, wrapped in `w:del`. */
961
+ readonly revision?: RevisionAttributionInput;
962
+ };
963
+
964
+ /** Editable legacy text input types. Other authored types remain inert. */
965
+ type TextFormFieldType = 'regular' | 'number' | 'date';
966
+ /** Options stored in the legacy field definition. Zero means unlimited length. */
967
+ interface TextFormFieldOptions {
968
+ readonly type: TextFormFieldType;
969
+ readonly maxLength: number;
970
+ readonly format: string;
971
+ readonly enabled: boolean;
972
+ }
973
+ /** Supported format choices for legacy text input fields. @public */
974
+ declare const TEXT_FORM_FORMATS: {
975
+ readonly regular: readonly ["", "Uppercase", "Lowercase", "First capital", "Title case"];
976
+ readonly number: readonly ["", "0", "0.00", "#,##0", "#,##0.00", "0%", "0.00%"];
977
+ readonly date: readonly ["", "M/d/yyyy", "MM/dd/yyyy", "d/M/yyyy", "dd/MM/yyyy", "yyyy-MM-dd", "d MMMM yyyy", "MMMM d, yyyy"];
978
+ };
979
+
980
+ /** A legacy text input in the shared paragraph offset space. */
981
+ interface TextFormFieldRange {
982
+ readonly fieldNodeId: string;
983
+ readonly start: number;
984
+ readonly end: number;
985
+ readonly enabled: boolean;
986
+ readonly defaultText: string;
987
+ readonly type: string;
988
+ readonly maxLength: number;
989
+ readonly format: string;
990
+ readonly chromeIds: readonly string[];
991
+ }
992
+ /** Uses the field parser and offset index; never introduces another inline walk. */
993
+ declare function textFormFieldsOf(paragraph: OoxmlParagraphNode): readonly TextFormFieldRange[];
994
+ /** Options edit for an existing legacy text form. */
995
+ interface SetTextFormFieldDefaultOp {
996
+ readonly op: 'setTextFormFieldDefault';
997
+ /** Regional conventions for new date input; defaults to en-US. */
998
+ readonly locale?: string;
999
+ readonly paragraphId: string;
1000
+ readonly fieldNodeId: string;
1001
+ readonly text: string;
1002
+ readonly options?: TextFormFieldOptions;
1003
+ }
1004
+ /** Validate and format the existing result when a user finishes filling a field. */
1005
+ interface CommitTextFormFieldOp {
1006
+ readonly op: 'commitTextFormField';
1007
+ /** Regional conventions for new date input; defaults to en-US. */
1008
+ readonly locale?: string;
1009
+ readonly paragraphId: string;
1010
+ readonly fieldNodeId: string;
1011
+ }
1012
+
1013
+ /** One FORMCHECKBOX in the shared paragraph offset space. */
1014
+ interface LegacyCheckboxFieldRange {
1015
+ /** The begin `w:fldChar` node: what the op addresses. */
1016
+ readonly fieldNodeId: string;
1017
+ readonly start: number;
1018
+ readonly end: number;
1019
+ readonly checked: boolean;
1020
+ /** `w:enabled` under `w:ffData`; a disabled field renders but refuses the toggle. */
1021
+ readonly enabled: boolean;
1022
+ }
1023
+ /** Flip a FORMCHECKBOX's state; the only write into `w:ffData` a checkbox permits. */
1024
+ interface SetLegacyCheckboxOp {
1025
+ readonly op: 'setLegacyCheckbox';
1026
+ readonly paragraphId: string;
1027
+ readonly fieldNodeId: string;
1028
+ readonly checked: boolean;
1029
+ }
1030
+ /** Every FORMCHECKBOX in a paragraph, in document order; uses the shared field parser. */
1031
+ declare function legacyCheckboxFieldsOf(paragraph: OoxmlParagraphNode): readonly LegacyCheckboxFieldRange[];
1032
+
1033
+ /** Replace a building block gallery control's content with one block's body. */
1034
+ interface InsertBuildingBlockOp {
1035
+ readonly op: 'insertBuildingBlock';
1036
+ readonly controlId: string;
1037
+ /** `w:docPartPr/w:name` of the picked block, for the journal and the host. */
1038
+ readonly name: string;
1039
+ /**
1040
+ * Self-contained `w:docPartBody` children, cloned with fresh ids at apply.
1041
+ * Resource, style, field, bookmark, note and revision references return `unsupported`.
1042
+ */
1043
+ readonly blocks: readonly OoxmlNode[];
1044
+ }
1045
+
1046
+ /**
1047
+ * Why a part or relationship name was refused.
1048
+ *
1049
+ * Path traversal is the reason this exists: a name with `..` or a leading `/` is refused rather
1050
+ * than normalized, because normalizing is how a crafted package reaches outside itself.
1051
+ */
1052
+ type NameRejection = 'empty' | 'control-char' | 'backslash' | 'drive-or-unc' | 'encoded-separator' | 'encoded-dot' | 'bad-encoding' | 'empty-segment' | 'dot-segment' | 'unsafe-key' | 'segment-trailing-dot' | 'traversal-escape' | 'not-absolute-uri' | 'unsafe-scheme';
1053
+ /** A validated OPC name, or the typed reason it was refused. */
1054
+ type NameResult = {
1055
+ readonly ok: true;
1056
+ readonly partName: string;
1057
+ } | {
1058
+ readonly ok: false;
1059
+ readonly reason: NameRejection;
1060
+ };
1061
+ /**
1062
+ * Normalize a ZIP entry name or OPC part name into a canonical part name
1063
+ * (`/word/document.xml`). Accepts leading-slash and no-leading-slash inputs;
1064
+ * rejects traversal, dot segments, and the attack surface. Duplicate detection
1065
+ * folds ASCII case (OPC part-name equivalence).
1066
+ */
1067
+ declare function normalizePartName(raw: string): NameResult;
1068
+ /**
1069
+ * ASCII-only lowercase fold. OPC part-name/extension equivalence is US-ASCII
1070
+ * case-insensitive; a locale `toLowerCase()` mis-folds e.g. Turkish `I`/`İ` and
1071
+ * could let two "equivalent" part names evade duplicate detection.
1072
+ */
1073
+ declare function asciiFold(s: string): string;
1074
+ /** Case-folded key for OPC part-name equivalence / duplicate detection. */
1075
+ declare function partNameKey(partName: string): string;
1076
+ /**
1077
+ * Detect archive entries that collide after normalization (before inflation).
1078
+ * Returns the case-folded keys that more than one raw name maps to.
1079
+ */
1080
+ declare function detectDuplicateNames(rawNames: readonly string[]): {
1081
+ readonly duplicates: readonly string[];
1082
+ readonly rejected: readonly {
1083
+ raw: string;
1084
+ reason: NameRejection;
1085
+ }[];
1086
+ };
1087
+ /**
1088
+ * Resolve an internal relationship target against its owner part, without
1089
+ * escaping the package root. `ownerPartName` is a canonical part name; the base
1090
+ * is its containing folder. A leading "/" target is package-absolute. `.`/`..`
1091
+ * segments resolve on a stack; popping above root is `traversal-escape`.
1092
+ */
1093
+ declare function resolveInternalTarget(ownerPartName: string, rawTarget: string): NameResult;
1094
+ /**
1095
+ * Validate an external-mode relationship target. It MUST be an absolute URI, is
1096
+ * retained verbatim by the caller, and is NEVER owner-resolved or fetched here.
1097
+ * Unsafe schemes (javascript/vbscript/data/file) are rejected for runtime sinks;
1098
+ * the raw lexical form is still preserved separately by the authored record.
1099
+ */
1100
+ declare function validateExternalTarget(raw: string): NameResult;
1101
+
1102
+ /** One `<Default>`: a file extension mapped to a content type. */
1103
+ interface DefaultRecord {
1104
+ readonly extension: string;
1105
+ readonly contentType: string;
1106
+ readonly order: number;
1107
+ }
1108
+ /** One `<Override>`: a specific part name mapped to a content type. Beats any Default. */
1109
+ interface OverrideRecord {
1110
+ readonly partName: string;
1111
+ readonly contentType: string;
1112
+ readonly order: number;
1113
+ }
1114
+ /**
1115
+ * The authored `[Content_Types].xml` records, in significant order.
1116
+ *
1117
+ * Retained rather than collapsed into a lookup, because the file's lexical form and ordering are
1118
+ * part of what a lossless save re-emits.
1119
+ */
1120
+ interface ContentTypeRecords {
1121
+ readonly defaults: readonly DefaultRecord[];
1122
+ readonly overrides: readonly OverrideRecord[];
1123
+ }
1124
+ /**
1125
+ * Why content-type records could not be indexed.
1126
+ *
1127
+ * All fail CLOSED: conflicting Defaults on one extension, duplicate normalized Override names, or
1128
+ * invalid MIME syntax are refused rather than resolved by picking one.
1129
+ */
1130
+ type ContentTypeError = {
1131
+ readonly code: 'invalid-mime';
1132
+ readonly value: string;
1133
+ } | {
1134
+ readonly code: 'conflicting-default';
1135
+ readonly extension: string;
1136
+ } | {
1137
+ readonly code: 'duplicate-override';
1138
+ readonly partName: string;
1139
+ } | {
1140
+ readonly code: 'invalid-override-name';
1141
+ readonly partName: string;
1142
+ } | {
1143
+ readonly code: 'too-many-records';
1144
+ readonly limit: number;
1145
+ };
1146
+ /** Whether a string is syntactically a MIME type. Syntax only — no registry lookup. */
1147
+ declare function isValidMime(value: string): boolean;
1148
+ /** ASCII-case-insensitive extension key (leading dot removed; ASCII-only fold). */
1149
+ declare function extensionKey(extension: string): string;
1150
+ /** The resolved lookup: Override by case-folded part name, Default by case-insensitive extension. */
1151
+ interface ContentTypeIndex {
1152
+ /** ext key -> single MIME (identical duplicates collapsed). */
1153
+ readonly defaults: ReadonlyMap<string, string>;
1154
+ /** case-folded part name -> MIME. */
1155
+ readonly overrides: ReadonlyMap<string, string>;
1156
+ }
1157
+ /** The built index, or the conflict that made it impossible. */
1158
+ type IndexResult = {
1159
+ readonly ok: true;
1160
+ readonly index: ContentTypeIndex;
1161
+ } | {
1162
+ readonly ok: false;
1163
+ readonly error: ContentTypeError;
1164
+ };
1165
+ /**
1166
+ * Build a resolved content-type index, failing closed on conflict/duplicate/MIME
1167
+ * errors. `maxRecords` bounds the combined record count (N/N+1 gate).
1168
+ */
1169
+ declare function buildContentTypeIndex(records: ContentTypeRecords, maxRecords?: number): IndexResult;
1170
+ /**
1171
+ * A part's content type, or why it has none.
1172
+ *
1173
+ * An orphan record never determines a part's type — a Default with no matching part is preserved
1174
+ * inertly rather than applied.
1175
+ */
1176
+ type ResolveResult = {
1177
+ readonly ok: true;
1178
+ readonly contentType: string;
1179
+ readonly source: 'override' | 'default';
1180
+ } | {
1181
+ readonly ok: false;
1182
+ readonly reason: 'unknown';
1183
+ };
1184
+ /** Resolve a part's content type: Override wins over Default; else unknown. */
1185
+ declare function resolveContentType(index: ContentTypeIndex, partName: string): ResolveResult;
1186
+
1187
+ /** OOXML image relationship type for embedded or linked media parts. */
1188
+ declare const IMAGE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
1189
+ /**
1190
+ * Whether a relationship points inside the package or out of it.
1191
+ *
1192
+ * The security-relevant distinction: an `External` target is retained VERBATIM and never
1193
+ * owner-resolved or fetched, because auto-loading a file-supplied remote target is a zero-click
1194
+ * external fetch.
1195
+ */
1196
+ type TargetMode = 'Internal' | 'External';
1197
+ /**
1198
+ * One authored relationship, with nothing materialized away: owner part, id, type, raw target
1199
+ * lexical form, mode, and position.
1200
+ */
1201
+ interface RelationshipRecord {
1202
+ readonly ownerPart: string;
1203
+ readonly id: string;
1204
+ readonly type: string;
1205
+ readonly rawTarget: string;
1206
+ readonly targetMode: TargetMode;
1207
+ readonly order: number;
1208
+ }
1209
+ /** Why a relationship set is invalid. Duplicate ids within one owner fail closed. */
1210
+ type RelationshipError = {
1211
+ readonly code: 'duplicate-id';
1212
+ readonly ownerPart: string;
1213
+ readonly id: string;
1214
+ };
1215
+ /** The validated relationship set, or the conflict that rejected it. */
1216
+ type RelationshipSetResult = {
1217
+ readonly ok: true;
1218
+ readonly byOwner: ReadonlyMap<string, readonly RelationshipRecord[]>;
1219
+ } | {
1220
+ readonly ok: false;
1221
+ readonly error: RelationshipError;
1222
+ };
1223
+ /** Group relationships by owner in authored order; reject duplicate ids per owner. */
1224
+ declare function buildRelationshipSet(records: readonly RelationshipRecord[]): RelationshipSetResult;
1225
+ /**
1226
+ * A relationship resolved to a part, or the reason it could not be.
1227
+ *
1228
+ * External targets resolve to a refusal by design: they are never followed from a file.
1229
+ */
1230
+ type ResolvedRelationship = {
1231
+ readonly mode: 'Internal';
1232
+ readonly target: NameResult;
1233
+ readonly raw: string;
1234
+ } | {
1235
+ readonly mode: 'External';
1236
+ readonly sinkSafe: NameResult;
1237
+ readonly raw: string;
1238
+ };
1239
+ /**
1240
+ * Resolve a relationship to a runtime projection while the raw target stays
1241
+ * authored. Internal -> owner-relative part name; External -> sink-safe
1242
+ * validation only (never owner-resolved, never fetched). `raw` is always the
1243
+ * verbatim authored target.
1244
+ */
1245
+ declare function resolveRelationship(rec: RelationshipRecord): ResolvedRelationship;
1246
+ /**
1247
+ * An image relationship resolved to package bytes, or why it was refused.
1248
+ *
1249
+ * External-mode image rels are refused rather than fetched — the no-zero-click-external-fetch
1250
+ * rule applies to images exactly as it does to links.
1251
+ */
1252
+ type ImageRelationshipResolution = {
1253
+ readonly mode: 'internal';
1254
+ readonly partName: string;
1255
+ readonly raw: string;
1256
+ } | {
1257
+ readonly mode: 'external';
1258
+ readonly sinkSafe: boolean;
1259
+ readonly raw: string;
1260
+ } | {
1261
+ readonly mode: 'missing';
1262
+ };
1263
+ /**
1264
+ * Resolve an image relationship id from an owner part. Internal targets resolve
1265
+ * owner-relative; external targets are never fetched; a missing id is `missing`.
1266
+ */
1267
+ declare function resolveImageRelationship(records: readonly RelationshipRecord[] | undefined, ownerPart: string, relationshipId: string): ImageRelationshipResolution;
1268
+
1269
+ /**
1270
+ * Why a zip was refused at the trust boundary.
1271
+ *
1272
+ * `too-large` and `too-many-entries` are the zip-bomb guards; `bad-name` catches path traversal —
1273
+ * an entry with `..` or a leading `/` is rejected rather than normalized.
1274
+ */
1275
+ type ZipRejection = 'too-many-entries' | 'too-large' | 'bad-name' | 'inflate-error';
1276
+ /** Archive caps: entry count, total decompressed bytes, and the decompression ratio. */
1277
+ interface ZipLimits {
1278
+ readonly maxEntries: number;
1279
+ /** Max total UNCOMPRESSED bytes across the archive. */
1280
+ readonly maxTotalBytes: number;
1281
+ /** Max per-entry uncompressed:compressed ratio (zip-bomb guard). */
1282
+ readonly maxRatio?: number;
1283
+ }
1284
+ /** The archive caps in force when a host configures none. Conservative and finite. */
1285
+ declare const DEFAULT_ZIP_LIMITS: ZipLimits;
1286
+ /** The read entries, or the typed refusal. Never throws — the bytes are untrusted. */
1287
+ type ZipReadResult = {
1288
+ readonly ok: true;
1289
+ readonly entries: ReadonlyMap<string, Uint8Array>;
1290
+ } | {
1291
+ readonly ok: false;
1292
+ readonly reason: ZipRejection;
1293
+ readonly detail?: string;
1294
+ readonly limit?: 'zip.maxTotalBytes' | 'zip.maxRatio';
1295
+ };
1296
+ /**
1297
+ * Inflate a ZIP archive with bounds + OPC name normalization. Entry name, count,
1298
+ * compression-ratio, and total-uncompressed-size limits are enforced BEFORE each
1299
+ * entry is decompressed (via fflate's pre-inflation filter), so a zip bomb or a
1300
+ * traversal name is rejected without ever being inflated. Keys are canonical part
1127
1301
  * names.
1128
1302
  */
1129
1303
  declare function readZip(bytes: Uint8Array, limits?: ZipLimits): ZipReadResult;
@@ -1509,218 +1683,87 @@ interface PictureProjection {
1509
1683
  */
1510
1684
  declare function projectDrawing(drawing: OoxmlDrawingNode, context: Readonly<{
1511
1685
  ownerPartName: string;
1512
- supportedMcRequires: ReadonlySet<string>;
1513
- limits: DrawingProjectionLimits;
1514
- namespaceScope?: ReadonlyMap<string, string>;
1515
- resolveRelationship?: RelationshipTargetResolver;
1516
- resolveSchemeColor?: ShapeSchemeColorResolver;
1517
- resolveStyleMatrixReference?: ShapeStyleMatrixResolver;
1518
- }>): DrawingProjection | null;
1519
-
1520
- /** Caps applied while loading a package: zip, XML, part count and relationship count. */
1521
- interface OoxmlPackageLimits {
1522
- readonly zip?: ZipLimits;
1523
- readonly xml?: XmlLimits;
1524
- /** Cap on parts converted into canonical trees (N/N+1 gate, not a soft target). */
1525
- readonly maxXmlParts?: number;
1526
- /** Cap on relationship records across every rels part. */
1527
- readonly maxRelationships?: number;
1528
- }
1529
- /** The limits in force when a host configures none. Conservative and finite. */
1530
- declare const DEFAULT_OOXML_PACKAGE_LIMITS: Required<Pick<OoxmlPackageLimits, 'maxXmlParts' | 'maxRelationships'>>;
1531
- /**
1532
- * An external relationship target. Retained verbatim as authored evidence, with the
1533
- * sink-safety verdict alongside it. Never resolved against the package, never fetched.
1534
- */
1535
- interface OoxmlExternalTarget {
1536
- readonly ownerPart: string;
1537
- readonly id: string;
1538
- readonly type: string;
1539
- readonly rawTarget: string;
1540
- /** False when the target is not a safe sink (javascript:, file:, ...). Still not fetched. */
1541
- readonly sinkSafe: boolean;
1542
- }
1543
- /**
1544
- * A loaded package: every XML part as a canonical tree, plus the non-XML parts kept verbatim.
1545
- *
1546
- * The preservation model in one value. Modelled parts re-emit normalized; everything else is
1547
- * byte-identical, which is why an unrecognized part never costs a document anything.
1548
- */
1549
- interface OoxmlPackage {
1550
- /** Canonical trees, keyed by canonical part name. Non-XML parts are absent by design. */
1551
- readonly parts: ReadonlyMap<string, OoxmlPart>;
1552
- /** Raw bytes of every entry, including the non-XML parts that have no tree. */
1553
- readonly partBytes: ReadonlyMap<string, Uint8Array>;
1554
- /** Internal relationships by owner part, in authored order. */
1555
- readonly relationships: ReadonlyMap<string, readonly RelationshipRecord[]>;
1556
- readonly externalTargets: readonly OoxmlExternalTarget[];
1557
- readonly contentTypes: ContentTypeIndex;
1558
- /** Canonical name of the part the root `officeDocument` relationship points at. */
1559
- readonly mainDocumentPart: string;
1560
- }
1561
- /** Why a package could not be loaded. Every code describes the FILE, not the caller. */
1562
- type OoxmlPackageRejection = ZipRejection | OoxmlReadRejection | 'no-content-types' | 'bad-content-types' | 'no-main-document' | 'bad-relationship-target' | 'duplicate-relationship-id' | 'too-many-relationships' | 'too-many-xml-parts';
1563
- /** A loaded package, or a typed refusal. Never throws. */
1564
- type OoxmlPackageResult = {
1565
- readonly ok: true;
1566
- readonly package: OoxmlPackage;
1567
- } | {
1568
- readonly ok: false;
1569
- readonly reason: OoxmlPackageRejection;
1570
- readonly detail?: string;
1571
- readonly limit?: 'zip.maxTotalBytes' | 'zip.maxRatio';
1572
- };
1573
- declare function readOoxmlPackage(bytes: Uint8Array, limits?: OoxmlPackageLimits): OoxmlPackageResult;
1574
- /**
1575
- * Serialize a canonical package back to DOCX bytes.
1576
- *
1577
- * Starts from the ORIGINAL entry bytes and overwrites only the parts held as trees. A part
1578
- * the loader did not model — media, fonts, an XML part outside the modeled set — passes
1579
- * through untouched, so round-tripping a document cannot lose a part the engine never
1580
- * claimed to understand.
1581
- *
1582
- * The modeled parts are re-emitted NORMALIZED from the tree rather than patched as text.
1583
- * That is the whole point of the canonical tree: correctness is judged by the two D9
1584
- * oracles (the namespace-aware fingerprint and the save/reopen semantic digest), not by
1585
- * byte equality, so a different-but-equivalent spelling is not a defect.
1586
- *
1587
- * `writeZip` re-validates every part name, so a name that became unsafe between load and
1588
- * save cannot be smuggled into the archive.
1589
- */
1590
- declare function writeOoxmlPackage(pkg: OoxmlPackage): Uint8Array;
1591
- /** Replace one part's tree, returning a new package. Pure, like the tree edits themselves. */
1592
- declare function withPart(pkg: OoxmlPackage, part: OoxmlPart): OoxmlPackage;
1593
-
1594
- type AutomationTableMutation = {
1595
- readonly kind: 'values';
1596
- readonly values: readonly (readonly string[])[];
1597
- } | {
1598
- readonly kind: 'addRows' | 'addColumns';
1599
- readonly location: 'start' | 'end';
1600
- readonly count: number;
1601
- readonly values?: readonly (readonly string[])[];
1602
- } | {
1603
- readonly kind: 'deleteRows' | 'deleteColumns';
1604
- readonly index: number;
1605
- readonly count: number;
1606
- } | {
1607
- readonly kind: 'delete';
1608
- } | {
1609
- readonly kind: 'properties';
1610
- readonly styleId?: string;
1611
- readonly headerRowCount?: number;
1612
- } | {
1613
- readonly kind: 'cell';
1614
- readonly cellId: string;
1615
- readonly value?: string;
1616
- readonly columnWidth?: number;
1617
- readonly shadingColor?: string;
1618
- readonly verticalAlignment?: 'Top' | 'Center' | 'Bottom';
1619
- };
1620
-
1621
- interface SetFieldCodeOp {
1622
- readonly op: 'setFieldCode';
1623
- readonly paragraphId: string;
1624
- readonly fieldNodeId: string;
1625
- readonly code: string;
1626
- }
1627
-
1628
- type InsertTextOp = {
1629
- readonly op: 'insertText';
1630
- readonly paragraphId: string;
1631
- /** Legacy text form identity for protected filling; never an inline wrapper owner. */
1632
- readonly textFormFieldId?: string;
1633
- readonly offset: number;
1634
- readonly text: string;
1635
- /**
1636
- * Write this as a TRACKED insertion, attributed here.
1637
- *
1638
- * On the op rather than on the store, so suggesting stays a decision the surface makes
1639
- * per edit and the write vocabulary stays explicit — a global "everything is tracked
1640
- * now" flag is exactly what `DocEdits` refuses, because it makes the meaning of an op
1641
- * depend on state the op does not carry.
1642
- */
1643
- readonly revision?: RevisionAttributionInput;
1644
- /**
1645
- * When set, the text belongs INSIDE this content control, whatever sits at the offset.
1646
- *
1647
- * A boundary offset is owned by the run that starts there, which at a control's trailing
1648
- * edge is the run after the control — so an offset alone cannot say "append to this field",
1649
- * the way it cannot say which run of a field result to format (see `targetRunIds`). A
1650
- * caller that names the control gets the text in the control; one that does not gets the
1651
- * plain offset rule, which is what a keystroke beside a field means.
1652
- */
1653
- readonly inside?: string;
1654
- /**
1655
- * Which side of a run BOUNDARY the text joins. Default `'left'` — Word's typing rule:
1656
- * the next character takes the formatting of the character before the caret.
1657
- *
1658
- * `'right'` is for a caller that is not typing but placing text inside the run that
1659
- * STARTS at the offset — the hyperlink editor rewriting a link's display text, where
1660
- * landing left of the boundary would put the new text outside the link. Ignored when
1661
- * the offset falls strictly inside a run, which has no boundary to choose.
1662
- */
1663
- readonly bias?: 'left' | 'right';
1664
- };
1665
- type DeleteTextOp = {
1666
- readonly op: 'deleteText';
1667
- readonly paragraphId: string;
1668
- /** Legacy text form identity for protected filling; never an inline wrapper owner. */
1669
- readonly textFormFieldId?: string;
1670
- readonly start: number;
1671
- readonly end: number;
1672
- /** Write this as a TRACKED deletion — the characters stay, wrapped in `w:del`. */
1673
- readonly revision?: RevisionAttributionInput;
1674
- };
1686
+ supportedMcRequires: ReadonlySet<string>;
1687
+ limits: DrawingProjectionLimits;
1688
+ namespaceScope?: ReadonlyMap<string, string>;
1689
+ resolveRelationship?: RelationshipTargetResolver;
1690
+ resolveSchemeColor?: ShapeSchemeColorResolver;
1691
+ resolveStyleMatrixReference?: ShapeStyleMatrixResolver;
1692
+ }>): DrawingProjection | null;
1675
1693
 
1676
- /** Editable legacy text input types. Other authored types remain inert. */
1677
- type TextFormFieldType = 'regular' | 'number' | 'date';
1678
- /** Options stored in the legacy field definition. Zero means unlimited length. */
1679
- interface TextFormFieldOptions {
1680
- readonly type: TextFormFieldType;
1681
- readonly maxLength: number;
1682
- readonly format: string;
1683
- readonly enabled: boolean;
1694
+ /** Caps applied while loading a package: zip, XML, part count and relationship count. */
1695
+ interface OoxmlPackageLimits {
1696
+ readonly zip?: ZipLimits;
1697
+ readonly xml?: XmlLimits;
1698
+ /** Cap on parts converted into canonical trees (N/N+1 gate, not a soft target). */
1699
+ readonly maxXmlParts?: number;
1700
+ /** Cap on relationship records across every rels part. */
1701
+ readonly maxRelationships?: number;
1684
1702
  }
1685
- /** Supported format choices for legacy text input fields. @public */
1686
- declare const TEXT_FORM_FORMATS: {
1687
- readonly regular: readonly ["", "Uppercase", "Lowercase", "First capital", "Title case"];
1688
- readonly number: readonly ["", "0", "0.00", "#,##0", "#,##0.00", "0%", "0.00%"];
1689
- readonly date: readonly ["", "M/d/yyyy", "MM/dd/yyyy", "d/M/yyyy", "dd/MM/yyyy", "yyyy-MM-dd", "d MMMM yyyy", "MMMM d, yyyy"];
1690
- };
1691
-
1692
- /** A legacy text input in the shared paragraph offset space. */
1693
- interface TextFormFieldRange {
1694
- readonly fieldNodeId: string;
1695
- readonly start: number;
1696
- readonly end: number;
1697
- readonly enabled: boolean;
1698
- readonly defaultText: string;
1703
+ /** The limits in force when a host configures none. Conservative and finite. */
1704
+ declare const DEFAULT_OOXML_PACKAGE_LIMITS: Required<Pick<OoxmlPackageLimits, 'maxXmlParts' | 'maxRelationships'>>;
1705
+ /**
1706
+ * An external relationship target. Retained verbatim as authored evidence, with the
1707
+ * sink-safety verdict alongside it. Never resolved against the package, never fetched.
1708
+ */
1709
+ interface OoxmlExternalTarget {
1710
+ readonly ownerPart: string;
1711
+ readonly id: string;
1699
1712
  readonly type: string;
1700
- readonly maxLength: number;
1701
- readonly format: string;
1702
- readonly chromeIds: readonly string[];
1703
- }
1704
- /** Uses the field parser and offset index; never introduces another inline walk. */
1705
- declare function textFormFieldsOf(paragraph: OoxmlParagraphNode): readonly TextFormFieldRange[];
1706
- /** Options edit for an existing legacy text form. */
1707
- interface SetTextFormFieldDefaultOp {
1708
- readonly op: 'setTextFormFieldDefault';
1709
- /** Regional conventions for new date input; defaults to en-US. */
1710
- readonly locale?: string;
1711
- readonly paragraphId: string;
1712
- readonly fieldNodeId: string;
1713
- readonly text: string;
1714
- readonly options?: TextFormFieldOptions;
1713
+ readonly rawTarget: string;
1714
+ /** False when the target is not a safe sink (javascript:, file:, ...). Still not fetched. */
1715
+ readonly sinkSafe: boolean;
1715
1716
  }
1716
- /** Validate and format the existing result when a user finishes filling a field. */
1717
- interface CommitTextFormFieldOp {
1718
- readonly op: 'commitTextFormField';
1719
- /** Regional conventions for new date input; defaults to en-US. */
1720
- readonly locale?: string;
1721
- readonly paragraphId: string;
1722
- readonly fieldNodeId: string;
1717
+ /**
1718
+ * A loaded package: every XML part as a canonical tree, plus the non-XML parts kept verbatim.
1719
+ *
1720
+ * The preservation model in one value. Modelled parts re-emit normalized; everything else is
1721
+ * byte-identical, which is why an unrecognized part never costs a document anything.
1722
+ */
1723
+ interface OoxmlPackage {
1724
+ /** Canonical trees, keyed by canonical part name. Non-XML parts are absent by design. */
1725
+ readonly parts: ReadonlyMap<string, OoxmlPart>;
1726
+ /** Raw bytes of every entry, including the non-XML parts that have no tree. */
1727
+ readonly partBytes: ReadonlyMap<string, Uint8Array>;
1728
+ /** Internal relationships by owner part, in authored order. */
1729
+ readonly relationships: ReadonlyMap<string, readonly RelationshipRecord[]>;
1730
+ readonly externalTargets: readonly OoxmlExternalTarget[];
1731
+ readonly contentTypes: ContentTypeIndex;
1732
+ /** Canonical name of the part the root `officeDocument` relationship points at. */
1733
+ readonly mainDocumentPart: string;
1723
1734
  }
1735
+ /** Why a package could not be loaded. Every code describes the FILE, not the caller. */
1736
+ type OoxmlPackageRejection = ZipRejection | OoxmlReadRejection | 'no-content-types' | 'bad-content-types' | 'no-main-document' | 'bad-relationship-target' | 'duplicate-relationship-id' | 'too-many-relationships' | 'too-many-xml-parts';
1737
+ /** A loaded package, or a typed refusal. Never throws. */
1738
+ type OoxmlPackageResult = {
1739
+ readonly ok: true;
1740
+ readonly package: OoxmlPackage;
1741
+ } | {
1742
+ readonly ok: false;
1743
+ readonly reason: OoxmlPackageRejection;
1744
+ readonly detail?: string;
1745
+ readonly limit?: 'zip.maxTotalBytes' | 'zip.maxRatio';
1746
+ };
1747
+ declare function readOoxmlPackage(bytes: Uint8Array, limits?: OoxmlPackageLimits): OoxmlPackageResult;
1748
+ /**
1749
+ * Serialize a canonical package back to DOCX bytes.
1750
+ *
1751
+ * Starts from the ORIGINAL entry bytes and overwrites only the parts held as trees. A part
1752
+ * the loader did not model — media, fonts, an XML part outside the modeled set — passes
1753
+ * through untouched, so round-tripping a document cannot lose a part the engine never
1754
+ * claimed to understand.
1755
+ *
1756
+ * The modeled parts are re-emitted NORMALIZED from the tree rather than patched as text.
1757
+ * That is the whole point of the canonical tree: correctness is judged by the two D9
1758
+ * oracles (the namespace-aware fingerprint and the save/reopen semantic digest), not by
1759
+ * byte equality, so a different-but-equivalent spelling is not a defect.
1760
+ *
1761
+ * `writeZip` re-validates every part name, so a name that became unsafe between load and
1762
+ * save cannot be smuggled into the archive.
1763
+ */
1764
+ declare function writeOoxmlPackage(pkg: OoxmlPackage): Uint8Array;
1765
+ /** Replace one part's tree, returning a new package. Pure, like the tree edits themselves. */
1766
+ declare function withPart(pkg: OoxmlPackage, part: OoxmlPart): OoxmlPackage;
1724
1767
 
1725
1768
  /** The `w15` namespace: `commentsExtended.xml` — thread parent and resolved state. */
1726
1769
  declare const W15_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2012/wordml";
@@ -1799,1146 +1842,1151 @@ declare function threadStateOfPart(part: OoxmlPart): Map<string, CommentThreadSt
1799
1842
  */
1800
1843
  declare function hasAnyComment(pkg: OoxmlPackage): boolean;
1801
1844
 
1802
- /** A position in the model offset space of one story. */
1803
- interface ReviewPosition {
1804
- readonly paragraphId: string;
1805
- readonly offset: number;
1806
- }
1807
- /** Where an item is anchored: a range in one story. */
1808
- interface ReviewRange {
1809
- readonly partName: string;
1810
- readonly start: ReviewPosition;
1811
- readonly end: ReviewPosition;
1812
- }
1813
- /**
1814
- * What kind of decision a revision card represents.
1815
- *
1816
- * Wider than the four content wrappers, because a reviewer has to be shown every pending
1817
- * decision, including the ones that decorate no characters. A card the surface cannot show is
1818
- * a change the reviewer never learns about — and `acceptAllRevisions` refuses if ANY revision
1819
- * in the document is one the engine cannot resolve, so an invisible one makes Accept All fail
1820
- * for a reason nothing on screen explains.
1821
- */
1822
- type ReviewRevisionKind = 'insert' | 'delete'
1845
+ /** How deep controls may nest before a walk stops descending. Shared by every lane. */
1846
+ declare const MAX_CONTENT_CONTROL_NESTING = 32;
1847
+ /** Cap on controls one walk reports, so a hostile file cannot make a read unbounded. */
1848
+ declare const MAX_CONTENT_CONTROLS_PER_PART = 10000;
1849
+ /** `CT_SdtPr/w:id` is `ST_DecimalNumber`; Word treats it as a signed 32-bit integer. */
1850
+ declare const CONTENT_CONTROL_ID_MAX = 2147483647;
1851
+ /** `ST_Lock` (§17.5.2.24). An absent `w:lock` is `unlocked`. */
1823
1852
  /**
1824
- * A deletion and an insertion that are one edit: text typed over a selection.
1853
+ * `w:lock` — what a control refuses.
1825
1854
  *
1826
- * Word shows these as a single `Replaced "x" with "y"` card, and resolving one half
1827
- * without the other is never what the reviewer meant — accepting the deletion alone
1828
- * leaves the replacement text unproposed, rejecting it alone leaves both.
1855
+ * `sdtLocked` protects the wrapper, `contentLocked` the text inside it, and `sdtContentLocked`
1856
+ * both. A control inside a locked one is locked in effect regardless of its own value.
1829
1857
  */
1830
- | 'replace' | 'moveFrom' | 'moveTo'
1831
- /** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
1832
- | 'format'
1833
- /** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
1834
- | 'paragraphMark'
1835
- /** A row, cell, section or grid revision. Supported row revisions are resolvable. */
1836
- | 'structural';
1858
+ type ContentControlLock = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked';
1837
1859
  /**
1838
- * One tracked change as a review card.
1860
+ * What kind of control this is.
1839
1861
  *
1840
- * Keyed per DECISION rather than per site: a revision spanning three ranges is one card, because
1841
- * accepting it accepts all three.
1862
+ * `checkbox` is the Microsoft `w14:checkbox` extension rather than an ECMA-376 type
1863
+ * element, and the two are not mutually exclusive with "declares no type": four controls
1864
+ * in the comprehensive fixture declare the extension and no `w:`-prefixed type, so a scan
1865
+ * that read only the ECMA-376 choice reported them untyped. `untyped` means the control
1866
+ * declares neither — a rich-text container, which is what Word treats it as.
1842
1867
  */
1843
- interface ReviewRevisionItem {
1844
- readonly kind: 'revision';
1845
- /** Stable across renders and unique per DECISION, not per site. */
1846
- readonly id: string;
1847
- /** The payload `acceptRevision` / `rejectRevision` take. */
1848
- readonly address: RevisionAddress;
1849
- /**
1850
- * EVERY address this decision covers, `address` first.
1851
- *
1852
- * More than one only for a replacement, whose halves a foreign editor may have written
1853
- * as two independent revisions. Accept and reject walk all of them in one transaction:
1854
- * resolving one half and leaving the other is a state no reviewer asked for.
1855
- */
1856
- readonly addresses: readonly RevisionAddress[];
1857
- /** The words a replacement removes. Empty for every other kind. */
1858
- readonly replacedText: string;
1859
- readonly revisionKind: ReviewRevisionKind;
1860
- /** OOXML property-change element, used to resolve independently numbered formatting kinds. */
1861
- readonly formattingKind?: string;
1862
- /** Changed direct formatting values, ready for localized review summaries. */
1863
- readonly formattingChanges?: readonly {
1864
- readonly property: 'bold' | 'italic' | 'underline' | 'strike' | 'fontFamily' | 'fontSize' | 'color' | 'alignment' | 'leftIndent' | 'rightIndent' | 'firstLineIndent' | 'hangingIndent' | 'spaceBefore' | 'spaceAfter';
1865
- /** New direct value; null means inherit. Sizes and distances are in points. */
1866
- readonly value: string | null;
1867
- }[];
1868
- /** Newly applied proofing language codes in a tracked formatting change. */
1869
- readonly formattingLanguages?: readonly string[];
1870
- /**
1871
- * WHICH decision a `paragraphMark` records, absent for every other kind.
1872
- *
1873
- * `EG_ParaRPrTrackChanges` is `ins? del? moveFrom? moveTo?`, and the four say opposite
1874
- * things about one break. Without this a card called a deleted break an inserted one, which
1875
- * is the reverse of what Accept on that card does.
1876
- */
1877
- readonly markDirection?: 'insert' | 'delete' | 'moveFrom' | 'moveTo';
1878
- readonly author: string;
1879
- readonly date?: string;
1880
- /** Text the revision covers, for the card summary. Empty for changes with no characters. */
1881
- readonly text: string;
1882
- /** Every site this decision touches, in document order. */
1883
- readonly ranges: readonly ReviewRange[];
1884
- /**
1885
- * How deeply this change is NESTED inside other changes, 0 for an unenclosed one.
1886
- *
1887
- * `w:ins` wrapping `w:del` is content one reviewer added and another struck. Both stay
1888
- * pending, so both are cards, over one identical range — and a range then cannot say which
1889
- * change a caret is in. Word treats the innermost as the operative one, so it wins the caret
1890
- * and the change enclosing it stays listed and reachable.
1891
- */
1892
- readonly nesting: number;
1893
- /**
1894
- * How many leading `ranges` are the STRUCK half of a replacement.
1895
- *
1896
- * A replacement's card is one decision but its ranges are two colours — red over what is
1897
- * going, green over what takes its place. ABSENT when the halves do not split at a single
1898
- * point, which is what a file recording both under one revision id can produce; a surface
1899
- * then has no basis for two colours and should paint one neutral band rather than guess.
1900
- */
1901
- readonly replacedRangeCount?: number;
1902
- /**
1903
- * True when the engine cannot resolve this kind, so accept and reject must not be offered.
1904
- *
1905
- * Derived by the DERIVATION rather than from a caller-supplied predicate: the refusal list
1906
- * is internal, and a surface asked to compute it would have to guess. A card that offers a
1907
- * button the engine will refuse is worse than one that explains why it cannot.
1908
- */
1909
- readonly readOnly: boolean;
1910
- /** The other half of a move, or the other side of a delete/insert replacement. */
1911
- readonly pairedWith?: string;
1912
- /**
1913
- * Comments answering this change, in document order.
1914
- *
1915
- * A reply to a tracked change IS a comment: `w:ins` and `w:del` carry no body, so the text
1916
- * is written as a comment over the revision's own range and the range is what links them.
1917
- */
1918
- readonly replyIds: readonly string[];
1868
+ /** Which kind of control a `w:sdt` is, and therefore what a written value must look like. */
1869
+ type ContentControlKind = 'richText' | 'plainText' | 'checkbox' | 'dropDownList' | 'comboBox' | 'date' | 'picture' | 'docPartObj' | 'docPartList' | 'group' | 'citation' | 'bibliography' | 'equation' | 'untyped';
1870
+ /** One declared option of a dropdown or combo box: its display text and its stored value. */
1871
+ interface ContentControlListItem {
1872
+ readonly displayText: string;
1873
+ readonly value: string;
1874
+ }
1875
+ /** A date picker's authored format and locale, for rendering and for parsing what it stores. */
1876
+ interface ContentControlDateFormat {
1877
+ readonly fullDate?: string;
1878
+ readonly dateFormat?: string;
1879
+ readonly lid?: string;
1880
+ readonly storeMappedDataAs?: string;
1881
+ readonly calendar?: string;
1919
1882
  }
1920
- /**
1921
- * One comment as a review card. A reply carries `parentId`; OOXML gives replies no separate
1922
- * element, so threads are reconstructed from that link.
1923
- */
1924
- interface ReviewCommentItem {
1925
- readonly kind: 'comment';
1926
- readonly id: string;
1927
- readonly comment: CommentRecord;
1928
- readonly range: ReviewRange | null;
1929
- readonly resolved: boolean;
1930
- /** The comment this replies to, absent for a top-level comment. */
1931
- readonly parentId?: string;
1932
- /** The REVISION this comment answers, when it covers exactly that change's characters. */
1933
- readonly parentRevisionId?: string;
1934
- /** Replies to this comment, in document order. Empty for a reply or a childless comment. */
1935
- readonly replyIds: readonly string[];
1936
- /** True when the file gave this comment no usable range. */
1937
- readonly orphaned: boolean;
1883
+ /** One of a checkbox's two glyphs: the font and code point it is drawn with. */
1884
+ interface ContentControlCheckboxState {
1885
+ /** The `w14:val` hex code point of the glyph, exactly as the control declares it. */
1886
+ readonly value: string;
1887
+ readonly font?: string;
1938
1888
  }
1939
1889
  /**
1940
- * A card contributed by a recognized custom node (`defineCustomNode` with a `reviewCard`
1941
- * hook), anchored at the node's range.
1890
+ * A checkbox's state and the two glyphs it chooses between.
1942
1891
  *
1943
- * Informational, never resolvable: there is nothing to accept or reject, so the engine
1944
- * refuses those verbs on it. `title` and `detail` are HOST-authored (the definition's hook
1945
- * produced them), but `attrs` and `text` originate in a file an attacker controls — a
1946
- * surface renders every one of these as text, never markup.
1892
+ * Both glyphs matter on a write: setting the value has to update `w14:checked` AND the run's
1893
+ * character, or the document's glyph and its recorded state disagree.
1947
1894
  */
1948
- interface ReviewCustomItem {
1949
- readonly kind: 'custom';
1950
- /** The SDT node's stable id in the canonical tree. */
1951
- readonly id: string;
1952
- /** The definition's `name`. */
1953
- readonly name: string;
1954
- /** The raw `w:tag` the node was recognized from. Untrusted input. */
1955
- readonly tag: string;
1956
- /** Attrs decoded from the tag, after the definition's recognition hook. Untrusted input. */
1957
- readonly attrs: Readonly<Record<string, string>>;
1958
- /** The SDT's literal content text. Untrusted input. */
1959
- readonly text: string;
1960
- /**
1961
- * The payload the node's control binds to, after the definition validated it.
1962
- *
1963
- * Undefined when the node carries none or the payload did not match — see the pro package's
1964
- * `RecognizedCustomNode.data`, which this is carried from.
1965
- */
1966
- readonly data?: unknown;
1967
- /**
1968
- * Whether this node asked for a sidebar card.
1969
- *
1970
- * False for a definition with no `reviewCard`: the item exists so the chip's own surfaces can
1971
- * read `attrs`, `text` and `data` off it, and the rail leaves it out. A surface listing cards
1972
- * filters on this rather than on an empty `title`.
1973
- */
1974
- readonly carded: boolean;
1975
- /** Card title, from the definition's `reviewCard` hook. Empty when `carded` is false. */
1976
- readonly title: string;
1977
- /** Card body, from the definition's `reviewCard` hook. */
1978
- readonly detail?: string;
1979
- /**
1980
- * Glyph for this node in the collapsed rail, as an SVG path in a `0 -960 960 960` viewBox.
1981
- *
1982
- * A PATH rather than a rendered node because this item crosses core, which is DOM-free and
1983
- * React-free — the same reason `title` and `detail` are strings. A host that wants arbitrary
1984
- * markup overrides the `Markers` part's `icon` instead, which lives in the adapter where JSX
1985
- * belongs. Absent falls back to the generic custom-node glyph.
1986
- *
1987
- * HOST-AUTHORED, never file data: it is interpolated into an SVG `d` attribute, and a path
1988
- * taken from a document would be attacker-controlled markup.
1989
- */
1990
- readonly icon?: string;
1991
- readonly range: ReviewRange | null;
1895
+ interface ContentControlCheckbox {
1896
+ readonly checked: boolean;
1897
+ readonly checkedState?: ContentControlCheckboxState;
1898
+ readonly uncheckedState?: ContentControlCheckboxState;
1899
+ }
1900
+ /** `CT_DataBinding` — preserved metadata. Never resolved, never fetched. */
1901
+ /** `w:dataBinding` — the custom-XML part and XPath a control's value is bound to. */
1902
+ interface ContentControlDataBinding {
1903
+ readonly xpath?: string;
1904
+ readonly storeItemID?: string;
1905
+ readonly prefixMappings?: string;
1992
1906
  }
1993
1907
  /**
1994
- * One pending decision in the review queue: a tracked change, a comment thread, or a pro
1995
- * custom-node card. Discriminate on `kind`.
1908
+ * A control's whole `w:sdtPr`, projected into one typed shape.
1996
1909
  *
1997
- * The store's own derivation (`collectReviewItems`) only ever produces the first two; the
1998
- * `kind: 'custom'` card is contributed by the pro review module, which recognizes the node.
1910
+ * What every other lane reads instead of walking `localName` strings — which also means a Word
1911
+ * re-save that demotes the properties node to generic does not break the projection.
1999
1912
  */
2000
- type ReviewItem = ReviewRevisionItem | ReviewCommentItem | ReviewCustomItem;
2001
- /** What the review queue derivation reads: one story part plus its comment parts. */
2002
- interface ReviewModelInput {
2003
- /** The story the ranges live in — the main document, a header, a note. */
2004
- readonly storyPart: OoxmlPart;
2005
- /**
2006
- * Header/footer story parts, in section order. Their revisions and comment anchors join
2007
- * the queue: a tracked change in a header is a pending decision like any other, and a
2008
- * queue that only walked the body silently hid it from the rail AND from Accept All.
2009
- */
2010
- readonly furnitureParts?: readonly OoxmlPart[] | undefined;
2011
- /** `word/comments.xml`, absent when the package has none. */
2012
- readonly commentsPart?: OoxmlPart | undefined;
2013
- /** `word/commentsExtended.xml`, absent when the package has none. */
2014
- readonly commentsExtendedPart?: OoxmlPart | undefined;
2015
- /**
2016
- * Custom node definitions from the module registry, forwarded OPAQUELY.
2017
- *
2018
- * Core carries them the way the registry does — as unknowns — so the definition shape
2019
- * stays a capability-package concern. The pro derivation narrows them and contributes
2020
- * `kind: 'custom'` cards for definitions that opted in.
2021
- */
2022
- readonly customNodes?: readonly unknown[] | undefined;
2023
- /**
2024
- * Where a capability package reports a node it could not read. Supplied per editor, so a page
2025
- * with two of them keeps their diagnostics apart.
2026
- */
2027
- readonly reportCustomNodeDiagnostic?: ((diagnostic: unknown) => void) | undefined;
2028
- /**
2029
- * The payload each of the story's controls binds to, keyed by the control's node id.
2030
- *
2031
- * Resolved by the ENGINE and handed over, because a payload lives in a customXml data part
2032
- * and a derivation that only receives story parts has no way to reach one. Untrusted file
2033
- * input on both members; a capability package validates it against whatever shape it
2034
- * declared before handing it to a host.
2035
- */
2036
- readonly customNodePayloads?: ReadonlyMap<string, {
2037
- readonly nodeId: string;
2038
- readonly label: string;
2039
- readonly data: string;
2040
- }> | undefined;
1913
+ interface ContentControlProperties {
1914
+ readonly type: ContentControlKind;
1915
+ readonly alias?: string;
1916
+ readonly tag?: string;
1917
+ /** `@w:val` of `w:id` when the file declares a parseable one. Never fabricated. */
1918
+ readonly id?: number;
1919
+ readonly lock: ContentControlLock;
1920
+ /** The glossary entry named by `w:placeholder/w:docPart`. Preserved, never loaded. */
1921
+ readonly placeholderDocPart?: string;
1922
+ readonly temporary: boolean;
1923
+ readonly showingPlaceholder: boolean;
1924
+ readonly dataBinding?: ContentControlDataBinding;
1925
+ readonly label?: string;
1926
+ readonly tabIndex?: number;
1927
+ /** Items a dropdown or combo box offers. Empty for every other type. */
1928
+ readonly listItems: readonly ContentControlListItem[];
1929
+ readonly lastValue?: string;
1930
+ readonly date?: ContentControlDateFormat;
1931
+ readonly multiLine?: boolean;
1932
+ readonly checkbox?: ContentControlCheckbox;
2041
1933
  }
2042
- /** The stable key a surface uses for the active item and for a React list. */
2043
- declare function reviewItemKey(item: ReviewItem): string;
2044
- /** Every range a decision touches. One card can cover several, in different paragraphs. */
2045
- declare function reviewItemRanges(item: ReviewItem): readonly ReviewRange[];
2046
- /** The first range of an item, in authored order, or null when it has none. */
2047
- declare function firstReviewRange(item: ReviewItem): ReviewRange | null;
1934
+ /** Whether a node is a `w:sdt` wrapper. */
1935
+ declare function isContentControlNode(node: OoxmlNode): node is OoxmlContentControlNode;
1936
+ /** Whether a node is a `w:sdtContent` — the container holding a control's actual content. */
1937
+ declare function isContentControlContentNode(node: OoxmlNode): node is OoxmlContentControlContentNode;
2048
1938
  /**
2049
- * A single comparable number for document order.
1939
+ * A `w:sdt` wrapper, TYPED or DEMOTED.
2050
1940
  *
2051
- * Paragraph index dominates offset, so a revision spanning paragraphs still sorts by where it
2052
- * STARTS. An item with no resolvable range sorts last rather than to position zero, which is
2053
- * where an orphan used to land — tearing an orphaned reply out of its own thread.
1941
+ * Flattening predates typing and must not stop at a control the reader refused to type: a
1942
+ * demoted wrapper still renders its content in place in Word, so a walk that only saw the
1943
+ * typed kind would drop every paragraph inside a malformed control out of the story.
2054
1944
  */
2055
- declare function reviewItemPositionRank(item: ReviewItem, order: ReadonlyMap<string, number>): number;
1945
+ declare function isContentControlWrapper(node: OoxmlNode): boolean;
2056
1946
  /**
2057
- * Every item covering a position, innermost first.
1947
+ * The children of a wrapper's `w:sdtContent`, typed or demoted, in order.
2058
1948
  *
2059
- * Returning the whole stack rather than one winner is what lets a surface offer cycling, a
2060
- * stacked card, or a "1 of 3" affordance. A comment wrapping a revision used to be unreachable
2061
- * because only the tightest range was ever returned.
1949
+ * The one place that knows a control's content is reached through an intermediate element,
1950
+ * so the four flattening walks (story blocks, layout, header/footer references, list
1951
+ * resolution) ask the same question rather than each re-deriving it.
2062
1952
  */
2063
- declare function reviewItemsAt(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem[];
1953
+ declare function contentControlContentChildren(wrapper: OoxmlNode): readonly OoxmlNode[];
2064
1954
  /**
2065
- * The item the caret is in, or null.
1955
+ * A container's children with every content-control wrapper flattened away, bounded in depth.
2066
1956
  *
2067
- * A resolved comment never activates: a settled thread must not reopen itself as the reviewer
2068
- * types near it.
1957
+ * ONE unwrap rule, for the places where a control sits between a container and the children that
1958
+ * container is defined in terms of. `CT_SdtRow` puts it between `w:tbl` and `w:tr`, `CT_SdtCell`
1959
+ * between `w:tr` and `w:tc`, and a walk that filtered on the child's kind dropped the row or cell
1960
+ * entirely: not measured, not painted, not addressable. Flattening at the point of the filter
1961
+ * means a controlled row is the same row to the grid pass, the pagination pass and the story walk.
2069
1962
  *
2070
- * A REPLY resolves to the thread it belongs to. A reply is anchored over its parent's range,
2071
- * so both cover the caret — and the reply, being newer, wins the innermost test. It is not a
2072
- * card of its own (it renders inside its parent's), so the thread would have gone active with
2073
- * nothing on screen showing it: the reply box vanished from a comment the moment somebody
2074
- * replied to it.
1963
+ * Returns the SAME array identity when there is nothing to unwrap, so the tables that carry no
1964
+ * controls — nearly all of them — allocate nothing and the incremental layout cache still sees
1965
+ * its own inputs.
2075
1966
  */
2076
- declare function activeReviewItem(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem | null;
1967
+ declare function flattenContentControls(children: readonly OoxmlNode[], maxDepth?: number): readonly OoxmlNode[];
1968
+ /** A control's `w:sdtPr`, or null. Matches structurally, so a Word-demoted node still resolves. */
1969
+ declare function contentControlPropertiesNodeOf(control: OoxmlNode): OoxmlContentControlPropertiesNode | undefined;
1970
+ /** A control's `w:sdtEndPr` — the run properties applied to its closing marker. */
1971
+ declare function contentControlEndPropertiesNodeOf(control: OoxmlNode): OoxmlContentControlEndPropertiesNode | undefined;
1972
+ /** A control's `w:sdtContent`, or null when the wrapper has none. */
1973
+ declare function contentControlContentNodeOf(control: OoxmlNode): OoxmlContentControlContentNode | undefined;
1974
+ /** A signed 32-bit `ST_DecimalNumber`, or undefined when the file wrote something else. */
1975
+ declare function parseContentControlId(raw: string | undefined): number | undefined;
2077
1976
  /**
2078
- * Walk a reply up to the card that heads its thread. Guarded against a cyclic file.
2079
- *
2080
- * The head is not always a comment. A reply to a tracked change renders inside the REVISION's
2081
- * card, so resolving it to itself opened an item nothing on screen was drawing — the reply box
2082
- * vanished the moment somebody answered a change.
1977
+ * Read a control's properties.
2083
1978
  *
2084
- * EXPORTED because the paginated surface answers "which card is open" itself, against its own
2085
- * dismissed-key state, rather than through {@link activeReviewItem}. Two copies of the
2086
- * innermost-wins rule was survivable while a reply could only be a comment — the parent came
2087
- * first in `comments.xml` order and won the tie by accident. It stopped being survivable the
2088
- * moment a reply could answer a revision, which outranks it outright.
2089
- */
2090
- declare function reviewThreadRootOf(items: readonly ReviewItem[], comment: ReviewCommentItem): ReviewItem;
2091
-
2092
- /** A canonical review decision. Keys are valid only in the open document. @public */
2093
- interface RevisionBatchEntry {
2094
- readonly key: string;
2095
- readonly author: string;
2096
- readonly partName: string;
2097
- readonly revisionKind: ReviewRevisionItem['revisionKind'];
2098
- }
2099
- /** Why a requested decision remains pending. @public */
2100
- type RevisionBatchSkipReason = 'unsupported-revision' | 'incomplete-group' | 'unknown-revision';
2101
- /** Outcome of one selected-set decision. Counts refer to review decisions, not XML markers. @public */
2102
- interface RevisionBatchResult {
2103
- readonly resolved: readonly RevisionBatchEntry[];
2104
- readonly skipped: readonly {
2105
- readonly key: string;
2106
- readonly reason: RevisionBatchSkipReason;
2107
- readonly revision?: RevisionBatchEntry;
2108
- }[];
2109
- /** All pending decisions in the addressed scope, including excluded and unsupported changes. */
2110
- readonly remaining: number;
2111
- }
2112
- /** @internal Pure preflight; callers commit its operations through their normal write guards. */
2113
- declare function planRevisionBatch(part: OoxmlPart, action: 'accept' | 'reject', keys?: readonly string[], scopeRoot?: OoxmlNode): {
2114
- ops: readonly TreeDocOp[];
2115
- result: RevisionBatchResult;
2116
- };
2117
-
1979
+ * Total: a control with no `w:sdtPr` at all answers the same shape with the defaults an
1980
+ * absent property means, so no caller has to branch on the container's existence.
1981
+ */
1982
+ declare function contentControlPropertiesOf(control: OoxmlNode): ContentControlProperties;
1983
+ /** Where a control sits, read off what its content holds. */
1984
+ type ContentControlLevel = 'block' | 'inline' | 'row' | 'cell' | 'empty';
2118
1985
  /**
2119
- * Shared types for the `@docx-editor.dev/core` contract. Type-only, zero runtime.
1986
+ * How deeply a control is nested inside other controls.
2120
1987
  *
2121
- * `@docx-editor.dev/core/contracts/types` re-exports this module verbatim, through
2122
- * `types-barrel.ts`. There is no `@docx-editor.dev/core/types` subpath.
1988
+ * Bounded by {@link MAX_CONTENT_CONTROL_NESTING}: nesting depth comes from a file and is a
1989
+ * recursion bound.
2123
1990
  */
2124
- /** Which paragraph-level reads the selection disagrees about. @public */
2125
- interface ParagraphDisagreements {
2126
- readonly alignment: boolean;
2127
- readonly spaceBeforePt: boolean;
2128
- readonly spaceAfterPt: boolean;
2129
- readonly lineSpacing: boolean;
2130
- readonly tabStops: boolean;
1991
+ declare function contentControlLevelOf(control: OoxmlNode): ContentControlLevel;
1992
+ /** One control, plus how deeply it is nested and the controls that enclose it. */
1993
+ interface ContentControlEntry {
1994
+ readonly node: OoxmlContentControlNode;
1995
+ /** 0 for a top-level control; the nesting depth otherwise. */
1996
+ readonly depth: number;
1997
+ /** Enclosing controls, outermost first. Empty at depth 0. */
1998
+ readonly ancestors: readonly OoxmlContentControlNode[];
2131
1999
  }
2132
2000
  /**
2133
- * The five paragraph flags a Paragraph dialog shows as checkboxes.
2134
- *
2135
- * `contextualSpacing` is Word's "Don't add space between paragraphs of the same style";
2136
- * the rest are its Pagination block. `null` means the selection disagrees.
2001
+ * Every control under a node, in document order, bounded in depth and in count.
2137
2002
  *
2138
- * @public
2003
+ * ONE walk, shared. The nesting bound is the same one layout flattens with, so a control
2004
+ * a lane can address is a control every lane can address — and a file that nests past it
2005
+ * keeps its content in the tree (the serializer never stops) while no walk recurses
2006
+ * further.
2139
2007
  */
2140
- interface ParagraphFlags {
2141
- readonly contextualSpacing: boolean | null;
2142
- readonly keepNext: boolean | null;
2143
- readonly keepLines: boolean | null;
2144
- readonly widowControl: boolean | null;
2145
- readonly pageBreakBefore: boolean | null;
2146
- }
2008
+ declare function contentControlsIn(root: OoxmlNode, options?: {
2009
+ readonly maxDepth?: number;
2010
+ readonly limit?: number;
2011
+ }): readonly ContentControlEntry[];
2012
+ /** Find one control by canonical node id, with the same bounded walk. */
2013
+ declare function findContentControl(root: OoxmlNode, nodeId: string): ContentControlEntry | null;
2147
2014
  /**
2148
- * Every field of the Paragraph dialog, as `setParagraphFormat` takes it.
2149
- *
2150
- * An omitted field is left as authored. `null` REMOVES a setting so the style supplies it
2151
- * again — not the same as a zero, which blocks the cascade.
2015
+ * The plain text a control's content holds, in reading order.
2152
2016
  *
2153
- * @public
2017
+ * `w:delText` is excluded: struck text is not the control's value, and a dropdown whose
2018
+ * old item is still present as a tracked deletion would otherwise report both.
2154
2019
  */
2155
- interface ParagraphFormatCommand {
2156
- alignment?: 'left' | 'center' | 'right' | 'justify';
2157
- spaceBeforePt?: number | null;
2158
- spaceAfterPt?: number | null;
2159
- lineSpacing?: {
2160
- rule: 'multiple' | 'exact' | 'atLeast';
2161
- value: number;
2162
- } | null;
2163
- indentLeftTwips?: number | null;
2164
- indentRightTwips?: number | null;
2165
- /** ONE signed first-line offset: negative is a hanging indent. */
2166
- indentFirstLineTwips?: number | null;
2167
- contextualSpacing?: boolean;
2168
- keepNext?: boolean;
2169
- keepLines?: boolean;
2170
- widowControl?: boolean;
2171
- pageBreakBefore?: boolean;
2172
- /**
2173
- * Replace the paragraph's custom tab stops. An empty list CLEARS them, which is what
2174
- * "Clear All" does; omit the field to leave them as authored.
2175
- */
2176
- tabStops?: readonly ParagraphTabStop[];
2177
- }
2178
- /** One custom tab stop, as a command states it. @public */
2179
- interface ParagraphTabStop {
2180
- readonly positionTwips: number;
2181
- readonly alignment: 'left' | 'center' | 'right' | 'decimal' | 'bar';
2182
- readonly leader?: 'none' | 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot';
2183
- }
2184
- /** Ownership token for one retained selection highlight. @public */
2185
- type SelectionPin = symbol;
2020
+ declare function contentControlTextOf(control: OoxmlNode): string;
2186
2021
  /**
2187
- * The LLM- and JSON-facing address for a piece of a document.
2022
+ * Resolve the lock a position inherits from the controls enclosing it.
2188
2023
  *
2189
- * `paraId` is the 8-hex `w14:paraId`, matched case-insensitively. `search` is a
2190
- * phrase that must match EXACTLY ONCE inside that paragraph; ambiguous or
2191
- * missing matches fail with `'ambiguous'` / `'notFound'` rather than falling
2192
- * back to first-match.
2024
+ * CONSERVATIVE, because a template that says a field cannot be edited and sits inside a
2025
+ * section that says it cannot be removed means both. Each half of `ST_Lock` is taken
2026
+ * independently and the strongest wins, so an unlocked control inside a `contentLocked`
2027
+ * one is still content-locked — a nested control cannot grant a permission its parent
2028
+ * withheld, which is the only reading under which a lock is a lock.
2029
+ */
2030
+ declare function resolveContentControlLock(chain: readonly ContentControlLock[]): ContentControlLock;
2031
+ /** Whether a resolved lock forbids editing the content it covers. */
2032
+ declare function lockForbidsEdit(lock: ContentControlLock): boolean;
2033
+ /** Whether a resolved lock forbids removing the control itself. */
2034
+ declare function lockForbidsRemoval(lock: ContentControlLock): boolean;
2035
+ /**
2036
+ * `CT_SdtPr`'s declared child order (§17.5.2.38), followed by the type choice.
2193
2037
  *
2194
- * Offset-based addressing was tried and abandoned: an agent cannot compute a
2195
- * character offset it has not seen, and offsets do not survive concurrent
2196
- * edits. Do not reintroduce `{ blockId, offset }`.
2038
+ * Serialization re-emits children in tree order, so the ONLY way a write keeps the
2039
+ * sequence valid is to place the child it authors at its schema position — which is what
2040
+ * {@link orderedContentControlProperties} does.
2197
2041
  */
2198
- interface DocAnchor {
2199
- paraId: string;
2200
- search?: string;
2201
- /** Opt-in disambiguation. Omit to require uniqueness. */
2202
- occurrence?: number;
2203
- }
2204
- /** Structural addressing for content the paraId map cannot reach. */
2205
- interface DocLocation {
2206
- container: ContainerRef;
2207
- /** Block indices, descending into tables and content controls. */
2208
- path: number[];
2209
- offset?: number;
2210
- }
2042
+ declare const CONTENT_CONTROL_PROPERTY_ORDER: readonly string[];
2211
2043
  /**
2212
- * Which STORY a location belongs to.
2044
+ * Rebuild a `w:sdtPr`'s children in schema order.
2213
2045
  *
2214
- * The body is one container; every header, footer and note is another. A path alone is
2215
- * ambiguous without it, because block index 0 exists in every story a document has.
2046
+ * Modelled children sort to their declared position and the type element follows them,
2047
+ * as the sequence requires. Everything else — a `w15:repeatingSection`, a vendor
2048
+ * extension, the `w14:checkbox` that is not part of the ECMA-376 choice — keeps the order
2049
+ * it was authored in and follows the modelled block, so a write that touches one property
2050
+ * neither drops nor reorders anything it does not name.
2216
2051
  */
2217
- type ContainerRef = {
2218
- part: 'body';
2219
- } | {
2220
- part: 'header' | 'footer';
2221
- rId: string;
2222
- } | {
2223
- part: 'footnote' | 'endnote';
2224
- noteId: number;
2225
- };
2052
+ declare function orderedContentControlProperties(children: readonly OoxmlNode[]): readonly OoxmlNode[];
2226
2053
  /**
2227
- * Anything an operation can be pointed at: a paragraph-relative {@link DocAnchor}, a structural
2228
- * {@link DocLocation}, or a {@link DocRange} spanning two of them.
2054
+ * The next `w:id` to write.
2055
+ *
2056
+ * Solo: one past the document's own maximum. Never a clock, a timestamp, a random source
2057
+ * or a hash: those collide with ids already in the file and produce values Word rejects.
2058
+ * Null when the document already reaches the signed 32-bit bound, so the caller refuses
2059
+ * rather than wrapping into a negative id. Hostile or out-of-range existing values are
2060
+ * ignored for seeding rather than counted past.
2061
+ *
2062
+ * When a collaboration actor is bound (explicit argument or the open store transaction),
2063
+ * the next id is the next unused value in that actor's stripe. Two peers inserting a
2064
+ * generic content control — including a custom node authored as `w:sdt` — from the same
2065
+ * snapshot then cannot share a `w:id`.
2229
2066
  */
2230
- type DocTarget = DocAnchor | DocLocation | DocRange;
2231
- /** A span between two positions. The endpoints may be addressed either way, independently. */
2232
- interface DocRange {
2233
- from: DocAnchor | DocLocation;
2234
- to: DocAnchor | DocLocation;
2067
+ declare function allocateContentControlId(root: OoxmlNode, actorId?: string): number | null;
2068
+
2069
+ /** A position in the model offset space of one story. */
2070
+ interface ReviewPosition {
2071
+ readonly paragraphId: string;
2072
+ readonly offset: number;
2073
+ }
2074
+ /** Where an item is anchored: a range in one story. */
2075
+ interface ReviewRange {
2076
+ readonly partName: string;
2077
+ readonly start: ReviewPosition;
2078
+ readonly end: ReviewPosition;
2235
2079
  }
2236
2080
  /**
2237
- * Every write returns this rather than `boolean`.
2081
+ * What kind of decision a revision card represents.
2238
2082
  *
2239
- * A bare boolean cannot distinguish "no-op" from "target not found" from
2240
- * "content control is locked", and the editor layer already throws eight
2241
- * distinct ContentControl error classes that a boolean would flatten.
2083
+ * Wider than the four content wrappers, because a reviewer has to be shown every pending
2084
+ * decision, including the ones that decorate no characters. A card the surface cannot show is
2085
+ * a change the reviewer never learns about — and `acceptAllRevisions` refuses if ANY revision
2086
+ * in the document is one the engine cannot resolve, so an invisible one makes Accept All fail
2087
+ * for a reason nothing on screen explains.
2242
2088
  */
2243
- type ExecResult = {
2244
- ok: true;
2245
- changed: boolean;
2246
- revisions?: RevisionBatchResult;
2247
- } | {
2248
- ok: false;
2249
- code: ExecErrorCode;
2250
- reason: string;
2251
- target?: DocTarget;
2252
- revisions?: RevisionBatchResult;
2253
- };
2089
+ type ReviewRevisionKind = 'insert' | 'delete'
2254
2090
  /**
2255
- * Why a write was refused, as a value to branch on.
2091
+ * A deletion and an insertion that are one edit: text typed over a selection.
2256
2092
  *
2257
- * Deliberately finer-grained than a boolean: "no-op", "target not found" and "content control is
2258
- * locked" are different outcomes, and a caller retrying the first should not retry the third.
2093
+ * Word shows these as a single `Replaced "x" with "y"` card, and resolving one half
2094
+ * without the other is never what the reviewer meant — accepting the deletion alone
2095
+ * leaves the replacement text unproposed, rejecting it alone leaves both.
2259
2096
  */
2260
- type ExecErrorCode = 'notFound' | 'ambiguous' | 'locked' | 'bound' | 'typeMismatch' | 'kindMismatch' | 'outOfBounds' | 'unsupported' | 'invalidArgs';
2097
+ | 'replace' | 'moveFrom' | 'moveTo'
2098
+ /** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
2099
+ | 'format'
2100
+ /** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
2101
+ | 'paragraphMark'
2102
+ /** A row, cell, section or grid revision. Supported row revisions are resolvable. */
2103
+ | 'structural';
2261
2104
  /**
2262
- * A parsed .docx.
2105
+ * One tracked change as a review card.
2263
2106
  *
2264
- * NOT JSON-round-trippable: it holds `Map`s and `Date`s, plus an internal
2265
- * side-table of verbatim XML used for lossless round-tripping. Use `toJSON` /
2266
- * `fromJSON` before sending it over JSON-RPC or handing it to a model.
2107
+ * Keyed per DECISION rather than per site: a revision spanning three ranges is one card, because
2108
+ * accepting it accepts all three.
2267
2109
  */
2268
- interface DocxDocument {
2269
- readonly body: DocumentBody;
2270
- readonly styles: StyleDefinitions;
2271
- readonly theme?: Theme;
2272
- readonly comments: readonly DocComment[];
2273
- readonly revisions: readonly Revision[];
2274
- }
2275
- /** The main story: its blocks in reading order, plus the sections derived from them. */
2276
- interface DocumentBody {
2277
- readonly content: readonly Block[];
2110
+ interface ReviewRevisionItem {
2111
+ readonly kind: 'revision';
2112
+ /** Stable across renders and unique per DECISION, not per site. */
2113
+ readonly id: string;
2114
+ /** The payload `acceptRevision` / `rejectRevision` take. */
2115
+ readonly address: RevisionAddress;
2116
+ /**
2117
+ * EVERY address this decision covers, `address` first.
2118
+ *
2119
+ * Replacements, table fragments, and adjacent formatting changes can combine multiple
2120
+ * original revisions into one decision. Review commands resolve the complete decision
2121
+ * in one transaction while preserving independent changes outside the group.
2122
+ */
2123
+ readonly addresses: readonly RevisionAddress[];
2124
+ /** The words a replacement removes. Empty for every other kind. */
2125
+ readonly replacedText: string;
2126
+ readonly revisionKind: ReviewRevisionKind;
2127
+ /** OOXML property-change element, used to resolve independently numbered formatting kinds. */
2128
+ readonly formattingKind?: string;
2129
+ /** Distinct structural operations covered by this decision, in document order. */
2130
+ readonly structuralChanges?: readonly ('rowInsert' | 'rowDelete' | 'cellInsert' | 'cellDelete' | 'cellMerge' | 'numberingInsert')[];
2131
+ /** Changed direct formatting values, ready for localized review summaries. */
2132
+ readonly formattingChanges?: readonly {
2133
+ readonly property: 'bold' | 'italic' | 'underline' | 'strike' | 'fontFamily' | 'fontSize' | 'color' | 'alignment' | 'leftIndent' | 'rightIndent' | 'firstLineIndent' | 'hangingIndent' | 'spaceBefore' | 'spaceAfter';
2134
+ /** New direct value; null means inherit. Sizes and distances are in points. */
2135
+ readonly value: string | null;
2136
+ }[];
2137
+ /** Newly applied proofing language codes in a tracked formatting change. */
2138
+ readonly formattingLanguages?: readonly string[];
2139
+ /**
2140
+ * WHICH decision a `paragraphMark` records, absent for every other kind.
2141
+ *
2142
+ * `EG_ParaRPrTrackChanges` is `ins? del? moveFrom? moveTo?`, and the four say opposite
2143
+ * things about one break. Without this a card called a deleted break an inserted one, which
2144
+ * is the reverse of what Accept on that card does.
2145
+ */
2146
+ readonly markDirection?: 'insert' | 'delete' | 'moveFrom' | 'moveTo';
2147
+ readonly author: string;
2148
+ readonly date?: string;
2149
+ /** Text the revision covers, for the card summary. Empty for changes with no characters. */
2150
+ readonly text: string;
2151
+ /** Every site this decision touches, in document order. */
2152
+ readonly ranges: readonly ReviewRange[];
2278
2153
  /**
2279
- * Derived, not stored: recomputed on read from section-break markers and
2280
- * section inheritance. Never treat it as a spreadable field.
2154
+ * How deeply this change is NESTED inside other changes, 0 for an unenclosed one.
2155
+ *
2156
+ * `w:ins` wrapping `w:del` is content one reviewer added and another struck. Both stay
2157
+ * pending, so both are cards, over one identical range — and a range then cannot say which
2158
+ * change a caret is in. Word treats the innermost as the operative one, so it wins the caret
2159
+ * and the change enclosing it stays listed and reachable.
2281
2160
  */
2282
- readonly sections: readonly Section[];
2283
- }
2284
- /** The JSON-safe projection of a document. */
2285
- interface DocxDocumentJSON {
2286
- readonly [key: string]: unknown;
2287
- }
2288
- /** One section: the page it lays out on, and the header/footer stories it declares. */
2289
- interface Section {
2290
- readonly properties: SectionProperties;
2291
- readonly headers: HeaderFooterSet;
2292
- readonly footers: HeaderFooterSet;
2161
+ readonly nesting: number;
2162
+ /**
2163
+ * How many leading `ranges` are the STRUCK half of a replacement.
2164
+ *
2165
+ * A replacement's card is one decision but its ranges are two colours — red over what is
2166
+ * going, green over what takes its place. ABSENT when the halves do not split at a single
2167
+ * point, which is what a file recording both under one revision id can produce; a surface
2168
+ * then has no basis for two colours and should paint one neutral band rather than guess.
2169
+ */
2170
+ readonly replacedRangeCount?: number;
2171
+ /**
2172
+ * True when the engine cannot resolve this kind, so accept and reject must not be offered.
2173
+ *
2174
+ * Derived by the DERIVATION rather than from a caller-supplied predicate: the refusal list
2175
+ * is internal, and a surface asked to compute it would have to guess. A card that offers a
2176
+ * button the engine will refuse is worse than one that explains why it cannot.
2177
+ */
2178
+ readonly readOnly: boolean;
2179
+ /** The other half of a move, or the other side of a delete/insert replacement. */
2180
+ readonly pairedWith?: string;
2181
+ /**
2182
+ * Comments answering this change, in document order.
2183
+ *
2184
+ * A reply to a tracked change IS a comment: `w:ins` and `w:del` carry no body, so the text
2185
+ * is written as a comment over the revision's own range and the range is what links them.
2186
+ */
2187
+ readonly replyIds: readonly string[];
2293
2188
  }
2294
2189
  /**
2295
- * A section's three header (or footer) variants, each a relationship id.
2296
- *
2297
- * All optional: a variant a section does not declare inherits the previous section's, and one
2298
- * absent everywhere means the document simply has none.
2190
+ * One comment as a review card. A reply carries `parentId`; OOXML gives replies no separate
2191
+ * element, so threads are reconstructed from that link.
2299
2192
  */
2300
- interface HeaderFooterSet {
2301
- readonly default?: string;
2302
- readonly first?: string;
2303
- readonly even?: string;
2304
- }
2305
- /** Anything that can sit at block level in a story. Discriminate on `kind`. */
2306
- type Block = Paragraph | Table | ContentControl;
2307
- /** One paragraph: its runs, the style it names, and its list membership. */
2308
- interface Paragraph {
2309
- readonly kind: 'paragraph';
2310
- /** `w14:paraId`. The stable handle `DocAnchor` addresses. */
2311
- readonly paraId?: string;
2312
- readonly runs: readonly Run[];
2313
- readonly styleId?: string;
2314
- readonly numbering?: NumberingRef;
2315
- }
2316
- /** A stretch of text sharing one set of character properties. */
2317
- interface Run {
2318
- readonly text: string;
2319
- readonly formatting?: RunFormatting;
2320
- /** Set when the run carries a tracked change. Unique only within its part. */
2321
- readonly revisionId?: number;
2193
+ interface ReviewCommentItem {
2194
+ readonly kind: 'comment';
2195
+ readonly id: string;
2196
+ readonly comment: CommentRecord;
2197
+ readonly range: ReviewRange | null;
2198
+ readonly resolved: boolean;
2199
+ /** The comment this replies to, absent for a top-level comment. */
2200
+ readonly parentId?: string;
2201
+ /** The REVISION this comment answers, when it covers exactly that change's characters. */
2202
+ readonly parentRevisionId?: string;
2203
+ /** Replies to this comment, in document order. Empty for a reply or a childless comment. */
2204
+ readonly replyIds: readonly string[];
2205
+ /** True when the file gave this comment no usable range. */
2206
+ readonly orphaned: boolean;
2322
2207
  }
2323
2208
  /**
2324
- * Character formatting, and — at selection level — the paragraph properties a toolbar reads
2325
- * alongside it.
2209
+ * A card contributed by a recognized custom node (`defineCustomNode` with a `reviewCard`
2210
+ * hook), anchored at the node's range.
2326
2211
  *
2327
- * One type serves both roles so a toolbar reads alignment, style and script state from the same
2328
- * object as bold and italic. On a {@link Run} the selection-level fields stay absent: a run has
2329
- * no alignment or paragraph style of its own.
2212
+ * Informational, never resolvable: there is nothing to accept or reject, so the engine
2213
+ * refuses those verbs on it. `title` and `detail` are HOST-authored (the definition's hook
2214
+ * produced them), but `attrs` and `text` originate in a file an attacker controls — a
2215
+ * surface renders every one of these as text, never markup.
2330
2216
  */
2331
- interface RunFormatting {
2332
- readonly bold?: boolean;
2333
- readonly italic?: boolean;
2334
- readonly underline?: boolean;
2335
- readonly strike?: boolean;
2336
- readonly color?: ColorValue;
2337
- readonly highlight?: string;
2338
- readonly fontFamily?: string;
2339
- readonly fontSizePt?: number;
2340
- readonly superscript?: boolean;
2341
- readonly subscript?: boolean;
2342
- /** Paragraph alignment at the selection. `both` is OOXML's spelling of justify. */
2343
- readonly alignment?: 'left' | 'center' | 'right' | 'both';
2344
- /** Paragraph style id (`w:pStyle`) at the selection. */
2345
- readonly styleId?: string;
2217
+ interface ReviewCustomItem {
2218
+ readonly kind: 'custom';
2219
+ /** The SDT node's stable id in the canonical tree. */
2220
+ readonly id: string;
2221
+ /** The definition's `name`. */
2222
+ readonly name: string;
2223
+ /** The raw `w:tag` the node was recognized from. Untrusted input. */
2224
+ readonly tag: string;
2225
+ /** Attrs decoded from the tag, after the definition's recognition hook. Untrusted input. */
2226
+ readonly attrs: Readonly<Record<string, string>>;
2227
+ /** The SDT's literal content text. Untrusted input. */
2228
+ readonly text: string;
2346
2229
  /**
2347
- * Line spacing at the selection, in the unit its rule implies — LINES for `multiple`,
2348
- * points for `exact` and `atLeast`. The same vocabulary `setLineSpacing` takes, so a
2349
- * control can show what it reads and send back what it shows. Absent when the
2350
- * selection's paragraphs disagree or state no line spacing.
2230
+ * The payload the node's control binds to, after the definition validated it.
2231
+ *
2232
+ * Undefined when the node carries none or the payload did not match — see the pro package's
2233
+ * `RecognizedCustomNode.data`, which this is carried from.
2351
2234
  */
2352
- readonly lineSpacing?: {
2353
- readonly rule: 'multiple' | 'exact' | 'atLeast';
2354
- readonly value: number;
2355
- };
2235
+ readonly data?: unknown;
2356
2236
  /**
2357
- * Space above and below the paragraph at the selection, in points.
2237
+ * Whether this node asked for a sidebar card.
2358
2238
  *
2359
- * The measurement the cascade STATES — `w:docDefaults`, the style chain and direct
2360
- * formatting resolved together — not the gap the page finally draws. The two differ for a
2361
- * paragraph carrying `w:beforeAutospacing`, where Word substitutes its own value and
2362
- * ignores the measurement, and again where two paragraphs collapse their adjacent spacing.
2363
- * Absent when the selection's paragraphs disagree or no level states it.
2239
+ * False for a definition with no `reviewCard`: the item exists so the chip's own surfaces can
2240
+ * read `attrs`, `text` and `data` off it, and the rail leaves it out. A surface listing cards
2241
+ * filters on this rather than on an empty `title`.
2364
2242
  */
2365
- readonly spaceBeforePt?: number;
2366
- readonly spaceAfterPt?: number;
2243
+ readonly carded: boolean;
2244
+ /** Card title, from the definition's `reviewCard` hook. Empty when `carded` is false. */
2245
+ readonly title: string;
2246
+ /** Card body, from the definition's `reviewCard` hook. */
2247
+ readonly detail?: string;
2367
2248
  /**
2368
- * The paragraph flags the Paragraph dialog shows as checkboxes.
2249
+ * Glyph for this node in the collapsed rail, as an SVG path in a `0 -960 960 960` viewBox.
2369
2250
  *
2370
- * Each is `true`, `false`, or `null` when the selection's paragraphs disagree — a
2371
- * checkbox needs all three. Read through the cascade, so a flag a STYLE sets reads as on,
2372
- * which is what the box has to show over a paragraph the page is visibly keeping with the
2373
- * next. Absent on a `Run`, like every other selection-level field here.
2251
+ * A PATH rather than a rendered node because this item crosses core, which is DOM-free and
2252
+ * React-free — the same reason `title` and `detail` are strings. A host that wants arbitrary
2253
+ * markup overrides the `Markers` part's `icon` instead, which lives in the adapter where JSX
2254
+ * belongs. Absent falls back to the generic custom-node glyph.
2255
+ *
2256
+ * HOST-AUTHORED, never file data: it is interpolated into an SVG `d` attribute, and a path
2257
+ * taken from a document would be attacker-controlled markup.
2374
2258
  */
2375
- readonly paragraphFlags?: ParagraphFlags;
2259
+ readonly icon?: string;
2260
+ readonly range: ReviewRange | null;
2261
+ }
2262
+ /**
2263
+ * One pending decision in the review queue: a tracked change, a comment thread, or a pro
2264
+ * custom-node card. Discriminate on `kind`.
2265
+ *
2266
+ * The store's own derivation (`collectReviewItems`) only ever produces the first two; the
2267
+ * `kind: 'custom'` card is contributed by the pro review module, which recognizes the node.
2268
+ */
2269
+ type ReviewItem = ReviewRevisionItem | ReviewCommentItem | ReviewCustomItem;
2270
+ /** What the review queue derivation reads: one story part plus its comment parts. */
2271
+ interface ReviewModelInput {
2272
+ /** The story the ranges live in — the main document, a header, a note. */
2273
+ readonly storyPart: OoxmlPart;
2376
2274
  /**
2377
- * Which paragraph-level fields above are absent because the selection DISAGREES, as
2378
- * opposed to because nothing states them.
2275
+ * Header/footer story parts, in section order. Their revisions and comment anchors join
2276
+ * the queue: a tracked change in a header is a pending decision like any other, and a
2277
+ * queue that only walked the body silently hid it from the rail AND from Accept All.
2278
+ */
2279
+ readonly furnitureParts?: readonly OoxmlPart[] | undefined;
2280
+ /** Shared style definitions. Their revisions have no paragraph range. */
2281
+ readonly stylesPart?: OoxmlPart | undefined;
2282
+ /** `word/comments.xml`, absent when the package has none. */
2283
+ readonly commentsPart?: OoxmlPart | undefined;
2284
+ /** `word/commentsExtended.xml`, absent when the package has none. */
2285
+ readonly commentsExtendedPart?: OoxmlPart | undefined;
2286
+ /**
2287
+ * Custom node definitions from the module registry, forwarded OPAQUELY.
2379
2288
  *
2380
- * A `null`/absent value alone cannot tell those apart, and both readings have shipped as
2381
- * bugs: a disagreement rendered as a concrete value is uncorrectable, because the value
2382
- * that would fix it is the one already on screen; an absent value rendered as "mixed"
2383
- * tells a single paragraph it disagrees with itself.
2289
+ * Core carries them the way the registry does — as unknowns — so the definition shape
2290
+ * stays a capability-package concern. The pro derivation narrows them and contributes
2291
+ * `kind: 'custom'` cards for definitions that opted in.
2384
2292
  */
2385
- readonly disagrees?: ParagraphDisagreements;
2293
+ readonly customNodes?: readonly unknown[] | undefined;
2386
2294
  /**
2387
- * The paragraph's custom tab stops at the selection, cascade included. Absent when the
2388
- * selection disagrees or the paragraphs have none.
2295
+ * Where a capability package reports a node it could not read. Supplied per editor, so a page
2296
+ * with two of them keeps their diagnostics apart.
2389
2297
  */
2390
- readonly tabStops?: readonly ParagraphTabStop[];
2298
+ readonly reportCustomNodeDiagnostic?: ((diagnostic: unknown) => void) | undefined;
2391
2299
  /**
2392
- * The EFFECTIVE paragraph indent at the selection — cascade and numbering merge
2393
- * included, so a numbered item that authors no `w:ind` reports the indent its list
2394
- * definition gives it. Absent when nothing is loaded, or when the selection is inside a
2395
- * table (see {@link IndentFormatting}).
2300
+ * The payload each of the story's controls binds to, keyed by the control's node id.
2301
+ *
2302
+ * Resolved by the ENGINE and handed over, because a payload lives in a customXml data part
2303
+ * and a derivation that only receives story parts has no way to reach one. Untrusted file
2304
+ * input on both members; a capability package validates it against whatever shape it
2305
+ * declared before handing it to a host.
2396
2306
  */
2397
- readonly indent?: IndentFormatting;
2307
+ readonly customNodePayloads?: ReadonlyMap<string, {
2308
+ readonly nodeId: string;
2309
+ readonly label: string;
2310
+ readonly data: string;
2311
+ }> | undefined;
2398
2312
  }
2313
+ /** The stable key a surface uses for the active item and for a React list. */
2314
+ declare function reviewItemKey(item: ReviewItem): string;
2315
+ /** Every range a decision touches. One card can cover several, in different paragraphs. */
2316
+ declare function reviewItemRanges(item: ReviewItem): readonly ReviewRange[];
2317
+ /** The first range of an item, in authored order, or null when it has none. */
2318
+ declare function firstReviewRange(item: ReviewItem): ReviewRange | null;
2319
+ /**
2320
+ * A single comparable number for document order.
2321
+ *
2322
+ * Paragraph index dominates offset, so a revision spanning paragraphs still sorts by where it
2323
+ * STARTS. An item with no resolvable range sorts last rather than to position zero, which is
2324
+ * where an orphan used to land — tearing an orphaned reply out of its own thread.
2325
+ */
2326
+ declare function reviewItemPositionRank(item: ReviewItem, order: ReadonlyMap<string, number>): number;
2399
2327
  /**
2400
- * Indent at the selection, in twips.
2328
+ * Every item covering a position, innermost first.
2401
2329
  *
2402
- * Unlike every other field here, this does NOT go absent when the selection's paragraphs
2403
- * disagree. A ruler has to draw its handles somewhere, and Word draws them at the FIRST
2404
- * selected paragraph's values — Select All is the commonest indent gesture, and hiding the
2405
- * handles for it would be worse than showing one paragraph's truth. The values are
2406
- * therefore always the first touched paragraph's, and {@link mixed} records per field
2407
- * whether the rest agree.
2330
+ * Returning the whole stack rather than one winner is what lets a surface offer cycling, a
2331
+ * stacked card, or a "1 of 3" affordance. A comment wrapping a revision used to be unreachable
2332
+ * because only the tightest range was ever returned.
2333
+ */
2334
+ declare function reviewItemsAt(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem[];
2335
+ /**
2336
+ * The item the caret is in, or null.
2408
2337
  *
2409
- * `firstLine` is ONE SIGNED offset: negative is a hanging indent. OOXML spells it as two
2410
- * mutually exclusive attributes and this collapses them hanging-wins (§17.3.1.12), which
2411
- * is the model Word itself keeps.
2338
+ * A resolved comment never activates: a settled thread must not reopen itself as the reviewer
2339
+ * types near it.
2412
2340
  *
2413
- * Absent inside a table. The value would be correct there, but it is measured from the
2414
- * cell's content edge while a ruler is drawn against the page's margin, and the ruler does
2415
- * not know the cell.
2341
+ * A REPLY resolves to the thread it belongs to. A reply is anchored over its parent's range,
2342
+ * so both cover the caret — and the reply, being newer, wins the innermost test. It is not a
2343
+ * card of its own (it renders inside its parent's), so the thread would have gone active with
2344
+ * nothing on screen showing it: the reply box vanished from a comment the moment somebody
2345
+ * replied to it.
2416
2346
  */
2417
- interface IndentFormatting {
2418
- /** Left indent, signed. Negative pulls text into the margin, as Word allows. */
2419
- readonly left: number;
2420
- /** Right indent, signed. */
2421
- readonly right: number;
2422
- /** First-line offset from {@link left}, signed. Negative is a hanging indent. */
2423
- readonly firstLine: number;
2424
- /** Per field, whether the selection's paragraphs disagree about it. */
2425
- readonly mixed: {
2426
- readonly left: boolean;
2427
- readonly right: boolean;
2428
- readonly firstLine: boolean;
2429
- };
2347
+ declare function activeReviewItem(items: readonly ReviewItem[], position: ReviewPosition, order: ReadonlyMap<string, number>): ReviewItem | null;
2348
+ /**
2349
+ * Walk a reply up to the card that heads its thread. Guarded against a cyclic file.
2350
+ *
2351
+ * The head is not always a comment. A reply to a tracked change renders inside the REVISION's
2352
+ * card, so resolving it to itself opened an item nothing on screen was drawing — the reply box
2353
+ * vanished the moment somebody answered a change.
2354
+ *
2355
+ * EXPORTED because the paginated surface answers "which card is open" itself, against its own
2356
+ * dismissed-key state, rather than through {@link activeReviewItem}. Two copies of the
2357
+ * innermost-wins rule was survivable while a reply could only be a comment — the parent came
2358
+ * first in `comments.xml` order and won the tie by accident. It stopped being survivable the
2359
+ * moment a reply could answer a revision, which outranks it outright.
2360
+ */
2361
+ declare function reviewThreadRootOf(items: readonly ReviewItem[], comment: ReviewCommentItem): ReviewItem;
2362
+
2363
+ /** The value a control accepts, by what kind of control it is. */
2364
+ type ContentControlValueInput = {
2365
+ readonly kind: 'text';
2366
+ readonly text: string;
2367
+ } | {
2368
+ readonly kind: 'listItem';
2369
+ readonly value: string;
2370
+ } | {
2371
+ readonly kind: 'checkbox';
2372
+ readonly checked: boolean;
2430
2373
  }
2431
- /** A table: its rows, and the table style they resolve through. */
2432
- interface Table {
2433
- readonly kind: 'table';
2434
- readonly rows: readonly TableRow[];
2435
- readonly styleId?: string;
2374
+ /** A calendar date, `YYYY-MM-DD` or a full ISO-8601 instant. */
2375
+ | {
2376
+ readonly kind: 'date';
2377
+ readonly iso: string;
2378
+ };
2379
+ /** The control types an insertion may author. Picture and repeating section are deferred. */
2380
+ declare const INSERTABLE_CONTENT_CONTROL_TYPES: readonly ["richText", "plainText", "dropDownList", "comboBox", "date"];
2381
+ /**
2382
+ * The OOXML spellings the store writes.
2383
+ *
2384
+ * NOT the contract's `InsertableContentControlType`, which is the same set of kinds in the
2385
+ * READ vocabulary (`dropdown`) plus this lane's spelling of it (`dropDownList`). The editor
2386
+ * maps between the two; the names differ so a reader can tell which vocabulary a value is in.
2387
+ */
2388
+ type InsertableContentControlKind = (typeof INSERTABLE_CONTENT_CONTROL_TYPES)[number];
2389
+
2390
+ /** A canonical review decision. Keys are valid only in the open document. @public */
2391
+ interface RevisionBatchEntry {
2392
+ readonly key: string;
2393
+ readonly author: string;
2394
+ readonly partName: string;
2395
+ readonly revisionKind: ReviewRevisionItem['revisionKind'];
2436
2396
  }
2437
- /** One table row. Cell count may vary between rows: `colSpan` and vertical merges reshape it. */
2438
- interface TableRow {
2439
- readonly cells: readonly TableCell[];
2397
+ /** Why a requested decision remains pending. @public */
2398
+ type RevisionBatchSkipReason = 'unsupported-revision' | 'incomplete-group' | 'retained-structure' | 'unknown-revision';
2399
+ /** Outcome of one selected-set decision. Counts refer to review decisions, not XML markers. @public */
2400
+ interface RevisionBatchResult {
2401
+ readonly resolved: readonly RevisionBatchEntry[];
2402
+ readonly skipped: readonly {
2403
+ readonly key: string;
2404
+ readonly reason: RevisionBatchSkipReason;
2405
+ readonly revision?: RevisionBatchEntry;
2406
+ }[];
2407
+ /** All pending decisions in the addressed scope, including excluded and unsupported changes. */
2408
+ readonly remaining: number;
2440
2409
  }
2410
+ /** @internal Pure preflight; callers commit its operations through their normal write guards. */
2411
+ declare function planRevisionBatch(part: OoxmlPart, action: 'accept' | 'reject', keys?: readonly string[], scopeRoot?: OoxmlNode): {
2412
+ ops: readonly TreeDocOp[];
2413
+ result: RevisionBatchResult;
2414
+ };
2415
+
2416
+ /** Allowlisted OOXML table border line styles — store/layout authority. */
2417
+ declare const TABLE_BORDER_STYLES: readonly ["single", "dashed", "dotted", "double", "triple", "thick"];
2441
2418
  /**
2442
- * One cell. Its `content` is ordinary blocks, so a cell may hold paragraphs, nested tables and
2443
- * content controls alike.
2419
+ * One of the border line styles this engine draws.
2420
+ *
2421
+ * An ALLOWLIST, not the full `ST_Border` enumeration: a file may name any of the seventy-odd
2422
+ * OOXML border styles, and anything outside this set falls back rather than being passed through
2423
+ * to paint.
2444
2424
  */
2445
- interface TableCell {
2446
- readonly content: readonly Block[];
2447
- /** Vertical merge span. Absent means 1. */
2448
- readonly rowSpan?: number;
2449
- /** `w:gridSpan`. Absent means 1. */
2450
- readonly colSpan?: number;
2425
+ type TableBorderStyle = (typeof TABLE_BORDER_STYLES)[number];
2426
+
2427
+ /**
2428
+ * Shared types for the `@docx-editor.dev/core` contract. Type-only, zero runtime.
2429
+ *
2430
+ * `@docx-editor.dev/core/contracts/types` re-exports this module verbatim, through
2431
+ * `types-barrel.ts`. There is no `@docx-editor.dev/core/types` subpath.
2432
+ */
2433
+ /** Which paragraph-level reads the selection disagrees about. @public */
2434
+ interface ParagraphDisagreements {
2435
+ readonly alignment: boolean;
2436
+ readonly spaceBeforePt: boolean;
2437
+ readonly spaceAfterPt: boolean;
2438
+ readonly lineSpacing: boolean;
2439
+ readonly tabStops: boolean;
2451
2440
  }
2452
- /** Structured document tag (`w:sdt`). */
2453
- interface ContentControl {
2454
- readonly kind: 'contentControl';
2455
- readonly id: string;
2456
- readonly tag?: string;
2457
- readonly alias?: string;
2458
- readonly controlType: ContentControlType;
2459
- readonly locked?: boolean;
2460
- readonly content: readonly Block[];
2441
+ /**
2442
+ * The five paragraph flags a Paragraph dialog shows as checkboxes.
2443
+ *
2444
+ * `contextualSpacing` is Word's "Don't add space between paragraphs of the same style";
2445
+ * the rest are its Pagination block. `null` means the selection disagrees.
2446
+ *
2447
+ * @public
2448
+ */
2449
+ interface ParagraphFlags {
2450
+ readonly contextualSpacing: boolean | null;
2451
+ readonly keepNext: boolean | null;
2452
+ readonly keepLines: boolean | null;
2453
+ readonly widowControl: boolean | null;
2454
+ readonly pageBreakBefore: boolean | null;
2461
2455
  }
2462
- /** Which kind of control a `w:sdt` is, and therefore what a value written into it must be. */
2463
- type ContentControlType = 'richText' | 'plainText' | 'checkbox' | 'dropdown' | 'comboBox' | 'date' | 'picture' | 'repeatingSection';
2464
2456
  /**
2465
- * The control kinds an insertion can author.
2457
+ * Every field of the Paragraph dialog, as `setParagraphFormat` takes it.
2466
2458
  *
2467
- * A NARROWING of {@link ContentControlType}, not a separate vocabulary: a kind read off an
2468
- * existing control can be handed straight back to `insertContentControl`. Three kinds are
2469
- * absent because an insertion would have to invent what they carry — a checkbox needs its two
2470
- * declared glyph states, a picture needs an image part, and a repeating section is a container
2471
- * whose items have their own verbs. Reading still answers the wider type; a document may hold
2472
- * kinds this cannot create.
2459
+ * An omitted field is left as authored. `null` REMOVES a setting so the style supplies it
2460
+ * again — not the same as a zero, which blocks the cascade.
2473
2461
  *
2474
- * `dropDownList` is the same kind as `dropdown`, spelt as OOXML spells it. Both are accepted,
2475
- * so a type read off a control can be handed back without translating it — and the OOXML
2476
- * spelling is the one the automation protocol takes, so a caller who uses both surfaces can
2477
- * write that one everywhere.
2462
+ * @public
2478
2463
  */
2479
- type InsertableContentControlType = Extract<ContentControlType, 'richText' | 'plainText' | 'dropdown' | 'comboBox' | 'date'> | 'dropDownList';
2464
+ interface ParagraphFormatCommand {
2465
+ alignment?: 'left' | 'center' | 'right' | 'justify';
2466
+ spaceBeforePt?: number | null;
2467
+ spaceAfterPt?: number | null;
2468
+ lineSpacing?: {
2469
+ rule: 'multiple' | 'exact' | 'atLeast';
2470
+ value: number;
2471
+ } | null;
2472
+ indentLeftTwips?: number | null;
2473
+ indentRightTwips?: number | null;
2474
+ /** ONE signed first-line offset: negative is a hanging indent. */
2475
+ indentFirstLineTwips?: number | null;
2476
+ contextualSpacing?: boolean;
2477
+ keepNext?: boolean;
2478
+ keepLines?: boolean;
2479
+ widowControl?: boolean;
2480
+ pageBreakBefore?: boolean;
2481
+ /**
2482
+ * Replace the paragraph's custom tab stops. An empty list CLEARS them, which is what
2483
+ * "Clear All" does; omit the field to leave them as authored.
2484
+ */
2485
+ tabStops?: readonly ParagraphTabStop[];
2486
+ }
2487
+ /** One custom tab stop, as a command states it. @public */
2488
+ interface ParagraphTabStop {
2489
+ readonly positionTwips: number;
2490
+ readonly alignment: 'left' | 'center' | 'right' | 'decimal' | 'bar';
2491
+ readonly leader?: 'none' | 'dot' | 'hyphen' | 'underscore' | 'heavy' | 'middleDot';
2492
+ }
2493
+ /** Ownership token for one retained selection highlight. @public */
2494
+ type SelectionPin = symbol;
2480
2495
  /**
2481
- * Narrows a content-control query. Fields combine with AND; an empty filter matches every
2482
- * control.
2496
+ * The LLM- and JSON-facing address for a piece of a document.
2497
+ *
2498
+ * `paraId` is the 8-hex `w14:paraId`, matched case-insensitively. `search` is a
2499
+ * phrase that must match EXACTLY ONCE inside that paragraph; ambiguous or
2500
+ * missing matches fail with `'ambiguous'` / `'notFound'` rather than falling
2501
+ * back to first-match.
2502
+ *
2503
+ * Offset-based addressing was tried and abandoned: an agent cannot compute a
2504
+ * character offset it has not seen, and offsets do not survive concurrent
2505
+ * edits. Do not reintroduce `{ blockId, offset }`.
2483
2506
  */
2484
- interface ContentControlFilter {
2485
- readonly tag?: string;
2486
- readonly alias?: string;
2487
- readonly controlType?: ContentControlType;
2507
+ interface DocAnchor {
2508
+ paraId: string;
2509
+ search?: string;
2510
+ /** Opt-in disambiguation. Omit to require uniqueness. */
2511
+ occurrence?: number;
2512
+ }
2513
+ /** Structural addressing for content the paraId map cannot reach. */
2514
+ interface DocLocation {
2515
+ container: ContainerRef;
2516
+ /** Block indices, descending into tables and content controls. */
2517
+ path: number[];
2518
+ offset?: number;
2488
2519
  }
2489
2520
  /**
2490
- * A position in one story: a paragraph and a UTF-16 offset inside it.
2521
+ * Which STORY a location belongs to.
2491
2522
  *
2492
- * The same offset space the ops and the caret use, so an anchor read here can be handed
2493
- * straight back to a selection without re-deriving anything.
2523
+ * The body is one container; every header, footer and note is another. A path alone is
2524
+ * ambiguous without it, because block index 0 exists in every story a document has.
2525
+ */
2526
+ type ContainerRef = {
2527
+ part: 'body';
2528
+ } | {
2529
+ part: 'header' | 'footer';
2530
+ rId: string;
2531
+ } | {
2532
+ part: 'footnote' | 'endnote';
2533
+ noteId: number;
2534
+ };
2535
+ /**
2536
+ * Anything an operation can be pointed at: a paragraph-relative {@link DocAnchor}, a structural
2537
+ * {@link DocLocation}, or a {@link DocRange} spanning two of them.
2494
2538
  */
2495
- interface DocAnchorRange {
2496
- /** Canonical part name of the story the range lives in, e.g. `/word/document.xml`. */
2497
- readonly part: string;
2498
- readonly startParagraphId: string;
2499
- readonly startOffset: number;
2500
- /** May sit in a later paragraph: the range markers are independent elements. */
2501
- readonly endParagraphId: string;
2502
- readonly endOffset: number;
2539
+ type DocTarget = DocAnchor | DocLocation | DocRange;
2540
+ /** A span between two positions. The endpoints may be addressed either way, independently. */
2541
+ interface DocRange {
2542
+ from: DocAnchor | DocLocation;
2543
+ to: DocAnchor | DocLocation;
2503
2544
  }
2504
2545
  /**
2505
- * One comment or reply, as `comments.xml` records it.
2546
+ * Every write returns this rather than `boolean`.
2506
2547
  *
2507
- * A reply is a comment with a `parentId`; OOXML gives replies no separate element, so the thread
2508
- * is reconstructed from that link rather than from nesting.
2548
+ * A bare boolean cannot distinguish "no-op" from "target not found" from
2549
+ * "content control is locked", and the editor layer already throws eight
2550
+ * distinct ContentControl error classes that a boolean would flatten.
2509
2551
  */
2510
- interface DocComment {
2511
- readonly id: string;
2512
- readonly author: string;
2513
- /**
2514
- * OPTIONAL, because `CT_Comment` makes `@w:date` optional and files omit it. A comment
2515
- * with no date is a comment, not a defect, and fabricating one is a content change.
2516
- */
2517
- readonly date?: string;
2518
- readonly text: string;
2519
- readonly parentId?: string;
2520
- readonly resolved?: boolean;
2521
- /**
2522
- * Where the comment is anchored, absent when the file gave it no usable range.
2523
- *
2524
- * A comment lives in `comments.xml` and is placed by markers in a STORY, so the story is
2525
- * part of the address: a comment anchored in a header belongs to a part the body never saw.
2526
- */
2527
- readonly anchor?: DocAnchorRange;
2528
- /**
2529
- * True when the file gave this comment no usable range — a reference with no markers, or a
2530
- * start with no end. Reported rather than dropped: a reviewer's remark vanishing silently
2531
- * is worse than one that says it lost its text.
2532
- */
2533
- readonly orphaned?: boolean;
2534
- }
2552
+ type ExecResult = {
2553
+ ok: true;
2554
+ changed: boolean;
2555
+ revisions?: RevisionBatchResult;
2556
+ history?: HistoryGroupOutcome;
2557
+ } | {
2558
+ ok: false;
2559
+ code: ExecErrorCode;
2560
+ reason: string;
2561
+ target?: DocTarget;
2562
+ revisions?: RevisionBatchResult;
2563
+ };
2535
2564
  /**
2536
- * What kind of decision a revision represents.
2565
+ * Why a write was refused, as a value to branch on.
2537
2566
  *
2538
- * Wider than insert/delete/format, and it has to be. `w:moveFrom`/`w:moveTo` are not a
2539
- * deletion and an insertion — resolving one half alone duplicates or loses the content;
2540
- * `w:pPr/w:rPr/w:ins|w:del` decorates no characters at all and merges paragraphs when
2541
- * resolved; a row or cell revision is structural. A reviewer shown only three kinds is a
2542
- * reviewer who never learns about the rest.
2567
+ * Deliberately finer-grained than a boolean: "no-op", "target not found" and "content control is
2568
+ * locked" are different outcomes, and a caller retrying the first should not retry the third.
2543
2569
  */
2544
- type RevisionType = 'insert' | 'delete'
2545
- /** A deletion and an insertion that are one edit: text typed over a selection. */
2546
- | 'replace' | 'moveFrom' | 'moveTo'
2547
- /** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
2548
- | 'format'
2549
- /** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
2550
- | 'paragraphMark'
2551
- /** A row, cell, section or grid revision. */
2552
- | 'structural';
2570
+ type ExecErrorCode = 'notFound' | 'ambiguous' | 'locked' | 'bound' | 'typeMismatch' | 'kindMismatch' | 'outOfBounds' | 'unsupported' | 'invalidArgs';
2553
2571
  /**
2554
- * One tracked change.
2572
+ * A parsed .docx.
2555
2573
  *
2556
- * Addressing needs BOTH `id` and `part`: `@w:id` is unique only within a part, so an id alone
2557
- * names two revisions in any package that has a header or a comments part.
2574
+ * NOT JSON-round-trippable: it holds `Map`s and `Date`s, plus an internal
2575
+ * side-table of verbatim XML used for lossless round-tripping. Use `toJSON` /
2576
+ * `fromJSON` before sending it over JSON-RPC or handing it to a model.
2558
2577
  */
2559
- interface Revision {
2560
- /** Numeric, and unique only WITHIN a part. Pair with `part` to address one. */
2561
- readonly id: number;
2562
- readonly type: RevisionType;
2563
- readonly author: string;
2564
- /**
2565
- * OPTIONAL. `CT_TrackChange` requires `@w:id` and `@w:author` and makes `@w:date`
2566
- * optional, and producers that omit it are ordinary. Requiring it here forced either a
2567
- * fabricated date — a content change — or dropping the revision from the list.
2568
- */
2569
- readonly date?: string;
2578
+ interface DocxDocument {
2579
+ readonly body: DocumentBody;
2580
+ readonly styles: StyleDefinitions;
2581
+ readonly theme?: Theme;
2582
+ readonly comments: readonly DocComment[];
2583
+ readonly revisions: readonly Revision[];
2584
+ }
2585
+ /** The main story: its blocks in reading order, plus the sections derived from them. */
2586
+ interface DocumentBody {
2587
+ readonly content: readonly Block[];
2570
2588
  /**
2571
- * REQUIRED, and a canonical PART NAME rather than a three-value enum.
2572
- *
2573
- * `@w:id` is unique only within a part, so an address without one names two revisions in a
2574
- * package that has both. The enum could not express the parts revisions actually occur in:
2575
- * `header3.xml`, `footer1.xml`, `comments.xml`, and `styles.xml`, which carries
2576
- * `w:pPrChange` inside style definitions with ids that collide with `document.xml`.
2589
+ * Derived, not stored: recomputed on read from section-break markers and
2590
+ * section inheritance. Never treat it as a spreadable field.
2577
2591
  */
2578
- readonly part: string;
2592
+ readonly sections: readonly Section[];
2579
2593
  }
2580
- /** `w:sectPr`: the page a section lays out on. Twips throughout, as the file stores them. */
2581
- interface SectionProperties {
2582
- readonly pageSize: {
2583
- widthTwips: number;
2584
- heightTwips: number;
2585
- };
2586
- readonly margins: PageMargins;
2587
- readonly columns?: {
2588
- count: number;
2589
- gapTwips: number;
2590
- };
2591
- /** `w:titlePg` — whether the section's first page takes the `first` header/footer variant. */
2592
- readonly titlePage?: boolean;
2594
+ /** The JSON-safe projection of a document. */
2595
+ interface DocxDocumentJSON {
2596
+ readonly [key: string]: unknown;
2593
2597
  }
2594
- /** Page margins in twips. */
2595
- interface PageMargins {
2596
- readonly topTwips: number;
2597
- readonly rightTwips: number;
2598
- readonly bottomTwips: number;
2599
- readonly leftTwips: number;
2598
+ /** One section: the page it lays out on, and the header/footer stories it declares. */
2599
+ interface Section {
2600
+ readonly properties: SectionProperties;
2601
+ readonly headers: HeaderFooterSet;
2602
+ readonly footers: HeaderFooterSet;
2600
2603
  }
2601
2604
  /**
2602
- * `styles.xml`, split by the three style families that address separately.
2605
+ * A section's three header (or footer) variants, each a relationship id.
2603
2606
  *
2604
- * Keyed by style ID rather than by the name a reader sees, because the ID is what a paragraph or
2605
- * run actually references.
2607
+ * All optional: a variant a section does not declare inherits the previous section's, and one
2608
+ * absent everywhere means the document simply has none.
2606
2609
  */
2607
- interface StyleDefinitions {
2608
- readonly paragraph: ReadonlyMap<string, StyleDefinition>;
2609
- readonly character: ReadonlyMap<string, StyleDefinition>;
2610
- readonly table: ReadonlyMap<string, StyleDefinition>;
2610
+ interface HeaderFooterSet {
2611
+ readonly default?: string;
2612
+ readonly first?: string;
2613
+ readonly even?: string;
2611
2614
  }
2612
- /** One style: the ID content references, the name a reader sees, and its inheritance link. */
2613
- interface StyleDefinition {
2614
- readonly id: string;
2615
- readonly name: string;
2616
- /** `w:basedOn` — the style this one inherits from. Absent at the root of a chain. */
2617
- readonly basedOn?: string;
2615
+ /** Anything that can sit at block level in a story. Discriminate on `kind`. */
2616
+ type Block = Paragraph | Table | ContentControl;
2617
+ /** One paragraph: its runs, the style it names, and its list membership. */
2618
+ interface Paragraph {
2619
+ readonly kind: 'paragraph';
2620
+ /** `w14:paraId`. The stable handle `DocAnchor` addresses. */
2621
+ readonly paraId?: string;
2622
+ readonly runs: readonly Run[];
2623
+ readonly styleId?: string;
2624
+ readonly numbering?: NumberingRef;
2618
2625
  }
2619
- /** A paragraph's list membership: which `numbering.xml` definition, and at which level. */
2620
- interface NumberingRef {
2621
- readonly numId: string;
2622
- /** Zero-based. OOXML numbering has nine levels, 0 through 8. */
2623
- readonly level: number;
2626
+ /** A stretch of text sharing one set of character properties. */
2627
+ interface Run {
2628
+ readonly text: string;
2629
+ readonly formatting?: RunFormatting;
2630
+ /** Set when the run carries a tracked change. Unique only within its part. */
2631
+ readonly revisionId?: number;
2624
2632
  }
2625
2633
  /**
2626
- * A colour as the FILE expresses it, not as a resolved RGB string.
2634
+ * Character formatting, and — at selection level — the paragraph properties a toolbar reads
2635
+ * alongside it.
2627
2636
  *
2628
- * A theme colour stays a theme reference — slot plus tint or shade — so that changing the theme
2629
- * repaints the document the way Word does. Flattening to hex at read time would freeze the
2630
- * resolved value and break that link. `auto` is Word's "let the renderer decide", usually black
2631
- * on white.
2637
+ * One type serves both roles so a toolbar reads alignment, style and script state from the same
2638
+ * object as bold and italic. On a {@link Run} the selection-level fields stay absent: a run has
2639
+ * no alignment or paragraph style of its own.
2632
2640
  */
2633
- type ColorValue = {
2634
- readonly kind: 'hex';
2635
- readonly value: string;
2636
- } | {
2637
- readonly kind: 'theme';
2638
- readonly slot: string;
2639
- readonly tint?: number;
2640
- readonly shade?: number;
2641
- } | {
2642
- readonly kind: 'auto';
2643
- };
2644
- /** `theme1.xml` — what a {@link ColorValue} of kind `theme` resolves against. */
2645
- interface Theme {
2646
- readonly colorScheme: ThemeColorScheme;
2647
- readonly fontScheme?: Record<string, string>;
2648
- }
2649
- /** Theme colour slots (`accent1`, `dk1`, `lt2`, …) to hex. */
2650
- type ThemeColorScheme = Readonly<Record<string, string>>;
2651
- /** A watermark, which OOXML expresses as either text or an image — never both meaningfully. */
2652
- interface Watermark {
2653
- readonly text?: string;
2654
- readonly imageData?: Uint8Array;
2655
- }
2656
- /** A size in EMUs, the unit DrawingML stores extents in. 914400 EMU = 1 inch. */
2657
- interface Extent {
2658
- readonly widthEmu: number;
2659
- readonly heightEmu: number;
2660
- }
2661
- /** A font the document names, and whether its bytes travel inside the package. */
2662
- interface FontDefinition {
2663
- readonly family: string;
2664
- readonly embedded: boolean;
2665
- }
2666
- /** An axis-aligned rectangle in points, the unit layout works in throughout. */
2667
- interface Rect {
2668
- readonly x: number;
2669
- readonly y: number;
2670
- readonly width: number;
2671
- readonly height: number;
2672
- }
2673
- /** A position in points. */
2674
- interface Point {
2675
- readonly x: number;
2676
- readonly y: number;
2641
+ interface RunFormatting {
2642
+ readonly bold?: boolean;
2643
+ readonly italic?: boolean;
2644
+ readonly underline?: boolean;
2645
+ readonly strike?: boolean;
2646
+ readonly color?: ColorValue;
2647
+ readonly highlight?: string;
2648
+ readonly fontFamily?: string;
2649
+ readonly fontSizePt?: number;
2650
+ readonly superscript?: boolean;
2651
+ readonly subscript?: boolean;
2652
+ /** Paragraph alignment at the selection. `both` is OOXML's spelling of justify. */
2653
+ readonly alignment?: 'left' | 'center' | 'right' | 'both';
2654
+ /** Paragraph style id (`w:pStyle`) at the selection. */
2655
+ readonly styleId?: string;
2656
+ /**
2657
+ * Line spacing at the selection, in the unit its rule implies — LINES for `multiple`,
2658
+ * points for `exact` and `atLeast`. The same vocabulary `setLineSpacing` takes, so a
2659
+ * control can show what it reads and send back what it shows. Absent when the
2660
+ * selection's paragraphs disagree or state no line spacing.
2661
+ */
2662
+ readonly lineSpacing?: {
2663
+ readonly rule: 'multiple' | 'exact' | 'atLeast';
2664
+ readonly value: number;
2665
+ };
2666
+ /**
2667
+ * Space above and below the paragraph at the selection, in points.
2668
+ *
2669
+ * The measurement the cascade STATES — `w:docDefaults`, the style chain and direct
2670
+ * formatting resolved together — not the gap the page finally draws. The two differ for a
2671
+ * paragraph carrying `w:beforeAutospacing`, where Word substitutes its own value and
2672
+ * ignores the measurement, and again where two paragraphs collapse their adjacent spacing.
2673
+ * Absent when the selection's paragraphs disagree or no level states it.
2674
+ */
2675
+ readonly spaceBeforePt?: number;
2676
+ readonly spaceAfterPt?: number;
2677
+ /**
2678
+ * The paragraph flags the Paragraph dialog shows as checkboxes.
2679
+ *
2680
+ * Each is `true`, `false`, or `null` when the selection's paragraphs disagree — a
2681
+ * checkbox needs all three. Read through the cascade, so a flag a STYLE sets reads as on,
2682
+ * which is what the box has to show over a paragraph the page is visibly keeping with the
2683
+ * next. Absent on a `Run`, like every other selection-level field here.
2684
+ */
2685
+ readonly paragraphFlags?: ParagraphFlags;
2686
+ /**
2687
+ * Which paragraph-level fields above are absent because the selection DISAGREES, as
2688
+ * opposed to because nothing states them.
2689
+ *
2690
+ * A `null`/absent value alone cannot tell those apart, and both readings have shipped as
2691
+ * bugs: a disagreement rendered as a concrete value is uncorrectable, because the value
2692
+ * that would fix it is the one already on screen; an absent value rendered as "mixed"
2693
+ * tells a single paragraph it disagrees with itself.
2694
+ */
2695
+ readonly disagrees?: ParagraphDisagreements;
2696
+ /**
2697
+ * The paragraph's custom tab stops at the selection, cascade included. Absent when the
2698
+ * selection disagrees or the paragraphs have none.
2699
+ */
2700
+ readonly tabStops?: readonly ParagraphTabStop[];
2701
+ /**
2702
+ * The EFFECTIVE paragraph indent at the selection — cascade and numbering merge
2703
+ * included, so a numbered item that authors no `w:ind` reports the indent its list
2704
+ * definition gives it. Absent when nothing is loaded, or when the selection is inside a
2705
+ * table (see {@link IndentFormatting}).
2706
+ */
2707
+ readonly indent?: IndentFormatting;
2677
2708
  }
2678
- /** What every subscription returns. Calling it twice is safe. */
2679
- type Unsubscribe = () => void;
2680
-
2681
- /** How deep controls may nest before a walk stops descending. Shared by every lane. */
2682
- declare const MAX_CONTENT_CONTROL_NESTING = 32;
2683
- /** Cap on controls one walk reports, so a hostile file cannot make a read unbounded. */
2684
- declare const MAX_CONTENT_CONTROLS_PER_PART = 10000;
2685
- /** `CT_SdtPr/w:id` is `ST_DecimalNumber`; Word treats it as a signed 32-bit integer. */
2686
- declare const CONTENT_CONTROL_ID_MAX = 2147483647;
2687
- /** `ST_Lock` (§17.5.2.24). An absent `w:lock` is `unlocked`. */
2688
2709
  /**
2689
- * `w:lock` — what a control refuses.
2710
+ * Indent at the selection, in twips.
2690
2711
  *
2691
- * `sdtLocked` protects the wrapper, `contentLocked` the text inside it, and `sdtContentLocked`
2692
- * both. A control inside a locked one is locked in effect regardless of its own value.
2693
- */
2694
- type ContentControlLock = 'unlocked' | 'sdtLocked' | 'contentLocked' | 'sdtContentLocked';
2695
- /**
2696
- * What kind of control this is.
2712
+ * Unlike every other field here, this does NOT go absent when the selection's paragraphs
2713
+ * disagree. A ruler has to draw its handles somewhere, and Word draws them at the FIRST
2714
+ * selected paragraph's values — Select All is the commonest indent gesture, and hiding the
2715
+ * handles for it would be worse than showing one paragraph's truth. The values are
2716
+ * therefore always the first touched paragraph's, and {@link mixed} records per field
2717
+ * whether the rest agree.
2697
2718
  *
2698
- * `checkbox` is the Microsoft `w14:checkbox` extension rather than an ECMA-376 type
2699
- * element, and the two are not mutually exclusive with "declares no type": four controls
2700
- * in the comprehensive fixture declare the extension and no `w:`-prefixed type, so a scan
2701
- * that read only the ECMA-376 choice reported them untyped. `untyped` means the control
2702
- * declares neither — a rich-text container, which is what Word treats it as.
2719
+ * `firstLine` is ONE SIGNED offset: negative is a hanging indent. OOXML spells it as two
2720
+ * mutually exclusive attributes and this collapses them hanging-wins (§17.3.1.12), which
2721
+ * is the model Word itself keeps.
2722
+ *
2723
+ * Absent inside a table. The value would be correct there, but it is measured from the
2724
+ * cell's content edge while a ruler is drawn against the page's margin, and the ruler does
2725
+ * not know the cell.
2703
2726
  */
2704
- /** Which kind of control a `w:sdt` is, and therefore what a written value must look like. */
2705
- type ContentControlKind = 'richText' | 'plainText' | 'checkbox' | 'dropDownList' | 'comboBox' | 'date' | 'picture' | 'docPartObj' | 'docPartList' | 'group' | 'citation' | 'bibliography' | 'equation' | 'untyped';
2706
- /** One declared option of a dropdown or combo box: its display text and its stored value. */
2707
- interface ContentControlListItem {
2708
- readonly displayText: string;
2709
- readonly value: string;
2727
+ interface IndentFormatting {
2728
+ /** Left indent, signed. Negative pulls text into the margin, as Word allows. */
2729
+ readonly left: number;
2730
+ /** Right indent, signed. */
2731
+ readonly right: number;
2732
+ /** First-line offset from {@link left}, signed. Negative is a hanging indent. */
2733
+ readonly firstLine: number;
2734
+ /** Per field, whether the selection's paragraphs disagree about it. */
2735
+ readonly mixed: {
2736
+ readonly left: boolean;
2737
+ readonly right: boolean;
2738
+ readonly firstLine: boolean;
2739
+ };
2710
2740
  }
2711
- /** A date picker's authored format and locale, for rendering and for parsing what it stores. */
2712
- interface ContentControlDateFormat {
2713
- readonly fullDate?: string;
2714
- readonly dateFormat?: string;
2715
- readonly lid?: string;
2716
- readonly storeMappedDataAs?: string;
2717
- readonly calendar?: string;
2741
+ /** A table: its rows, and the table style they resolve through. */
2742
+ interface Table {
2743
+ readonly kind: 'table';
2744
+ readonly rows: readonly TableRow[];
2745
+ readonly styleId?: string;
2718
2746
  }
2719
- /** One of a checkbox's two glyphs: the font and code point it is drawn with. */
2720
- interface ContentControlCheckboxState {
2721
- /** The `w14:val` hex code point of the glyph, exactly as the control declares it. */
2722
- readonly value: string;
2723
- readonly font?: string;
2747
+ /** One table row. Cell count may vary between rows: `colSpan` and vertical merges reshape it. */
2748
+ interface TableRow {
2749
+ readonly cells: readonly TableCell[];
2724
2750
  }
2725
2751
  /**
2726
- * A checkbox's state and the two glyphs it chooses between.
2727
- *
2728
- * Both glyphs matter on a write: setting the value has to update `w14:checked` AND the run's
2729
- * character, or the document's glyph and its recorded state disagree.
2752
+ * One cell. Its `content` is ordinary blocks, so a cell may hold paragraphs, nested tables and
2753
+ * content controls alike.
2730
2754
  */
2731
- interface ContentControlCheckbox {
2732
- readonly checked: boolean;
2733
- readonly checkedState?: ContentControlCheckboxState;
2734
- readonly uncheckedState?: ContentControlCheckboxState;
2735
- }
2736
- /** `CT_DataBinding` — preserved metadata. Never resolved, never fetched. */
2737
- /** `w:dataBinding` — the custom-XML part and XPath a control's value is bound to. */
2738
- interface ContentControlDataBinding {
2739
- readonly xpath?: string;
2740
- readonly storeItemID?: string;
2741
- readonly prefixMappings?: string;
2755
+ interface TableCell {
2756
+ readonly content: readonly Block[];
2757
+ /** Vertical merge span. Absent means 1. */
2758
+ readonly rowSpan?: number;
2759
+ /** `w:gridSpan`. Absent means 1. */
2760
+ readonly colSpan?: number;
2742
2761
  }
2743
- /**
2744
- * A control's whole `w:sdtPr`, projected into one typed shape.
2745
- *
2746
- * What every other lane reads instead of walking `localName` strings — which also means a Word
2747
- * re-save that demotes the properties node to generic does not break the projection.
2748
- */
2749
- interface ContentControlProperties {
2750
- readonly type: ContentControlKind;
2751
- readonly alias?: string;
2762
+ /** Structured document tag (`w:sdt`). */
2763
+ interface ContentControl {
2764
+ readonly kind: 'contentControl';
2765
+ readonly id: string;
2752
2766
  readonly tag?: string;
2753
- /** `@w:val` of `w:id` when the file declares a parseable one. Never fabricated. */
2754
- readonly id?: number;
2755
- readonly lock: ContentControlLock;
2756
- /** The glossary entry named by `w:placeholder/w:docPart`. Preserved, never loaded. */
2757
- readonly placeholderDocPart?: string;
2758
- readonly temporary: boolean;
2759
- readonly showingPlaceholder: boolean;
2760
- readonly dataBinding?: ContentControlDataBinding;
2761
- readonly label?: string;
2762
- readonly tabIndex?: number;
2763
- /** Items a dropdown or combo box offers. Empty for every other type. */
2764
- readonly listItems: readonly ContentControlListItem[];
2765
- readonly lastValue?: string;
2766
- readonly date?: ContentControlDateFormat;
2767
- readonly multiLine?: boolean;
2768
- readonly checkbox?: ContentControlCheckbox;
2767
+ readonly alias?: string;
2768
+ readonly controlType: ContentControlType;
2769
+ readonly locked?: boolean;
2770
+ readonly content: readonly Block[];
2769
2771
  }
2770
- /** Whether a node is a `w:sdt` wrapper. */
2771
- declare function isContentControlNode(node: OoxmlNode): node is OoxmlContentControlNode;
2772
- /** Whether a node is a `w:sdtContent` — the container holding a control's actual content. */
2773
- declare function isContentControlContentNode(node: OoxmlNode): node is OoxmlContentControlContentNode;
2774
- /**
2775
- * A `w:sdt` wrapper, TYPED or DEMOTED.
2776
- *
2777
- * Flattening predates typing and must not stop at a control the reader refused to type: a
2778
- * demoted wrapper still renders its content in place in Word, so a walk that only saw the
2779
- * typed kind would drop every paragraph inside a malformed control out of the story.
2780
- */
2781
- declare function isContentControlWrapper(node: OoxmlNode): boolean;
2782
- /**
2783
- * The children of a wrapper's `w:sdtContent`, typed or demoted, in order.
2784
- *
2785
- * The one place that knows a control's content is reached through an intermediate element,
2786
- * so the four flattening walks (story blocks, layout, header/footer references, list
2787
- * resolution) ask the same question rather than each re-deriving it.
2788
- */
2789
- declare function contentControlContentChildren(wrapper: OoxmlNode): readonly OoxmlNode[];
2772
+ /** Which kind of control a `w:sdt` is, and therefore what a value written into it must be. */
2773
+ type ContentControlType = 'richText' | 'plainText' | 'checkbox' | 'dropdown' | 'comboBox' | 'date' | 'picture' | 'buildingBlockGallery' | 'repeatingSection';
2790
2774
  /**
2791
- * A container's children with every content-control wrapper flattened away, bounded in depth.
2792
- *
2793
- * ONE unwrap rule, for the places where a control sits between a container and the children that
2794
- * container is defined in terms of. `CT_SdtRow` puts it between `w:tbl` and `w:tr`, `CT_SdtCell`
2795
- * between `w:tr` and `w:tc`, and a walk that filtered on the child's kind dropped the row or cell
2796
- * entirely: not measured, not painted, not addressable. Flattening at the point of the filter
2797
- * means a controlled row is the same row to the grid pass, the pagination pass and the story walk.
2775
+ * The control kinds an insertion can author.
2798
2776
  *
2799
- * Returns the SAME array identity when there is nothing to unwrap, so the tables that carry no
2800
- * controls — nearly all of them — allocate nothing and the incremental layout cache still sees
2801
- * its own inputs.
2802
- */
2803
- declare function flattenContentControls(children: readonly OoxmlNode[], maxDepth?: number): readonly OoxmlNode[];
2804
- /** A control's `w:sdtPr`, or null. Matches structurally, so a Word-demoted node still resolves. */
2805
- declare function contentControlPropertiesNodeOf(control: OoxmlNode): OoxmlContentControlPropertiesNode | undefined;
2806
- /** A control's `w:sdtEndPr` — the run properties applied to its closing marker. */
2807
- declare function contentControlEndPropertiesNodeOf(control: OoxmlNode): OoxmlContentControlEndPropertiesNode | undefined;
2808
- /** A control's `w:sdtContent`, or null when the wrapper has none. */
2809
- declare function contentControlContentNodeOf(control: OoxmlNode): OoxmlContentControlContentNode | undefined;
2810
- /** A signed 32-bit `ST_DecimalNumber`, or undefined when the file wrote something else. */
2811
- declare function parseContentControlId(raw: string | undefined): number | undefined;
2812
- /**
2813
- * Read a control's properties.
2777
+ * A NARROWING of {@link ContentControlType}, not a separate vocabulary: a kind read off an
2778
+ * existing control can be handed straight back to `insertContentControl`. Three kinds are
2779
+ * absent because an insertion would have to invent what they carry — a checkbox needs its two
2780
+ * declared glyph states, a picture needs an image part, a building block gallery needs a
2781
+ * glossary to list, and a repeating section is a container whose items have their own verbs. Reading still answers the wider type; a document may hold
2782
+ * kinds this cannot create.
2814
2783
  *
2815
- * Total: a control with no `w:sdtPr` at all answers the same shape with the defaults an
2816
- * absent property means, so no caller has to branch on the container's existence.
2784
+ * `dropDownList` is the same kind as `dropdown`, spelt as OOXML spells it. Both are accepted,
2785
+ * so a type read off a control can be handed back without translating it — and the OOXML
2786
+ * spelling is the one the automation protocol takes, so a caller who uses both surfaces can
2787
+ * write that one everywhere.
2817
2788
  */
2818
- declare function contentControlPropertiesOf(control: OoxmlNode): ContentControlProperties;
2819
- /** Where a control sits, read off what its content holds. */
2820
- type ContentControlLevel = 'block' | 'inline' | 'row' | 'cell' | 'empty';
2789
+ type InsertableContentControlType = Extract<ContentControlType, 'richText' | 'plainText' | 'dropdown' | 'comboBox' | 'date'> | 'dropDownList';
2821
2790
  /**
2822
- * How deeply a control is nested inside other controls.
2823
- *
2824
- * Bounded by {@link MAX_CONTENT_CONTROL_NESTING}: nesting depth comes from a file and is a
2825
- * recursion bound.
2791
+ * Narrows a content-control query. Fields combine with AND; an empty filter matches every
2792
+ * control.
2826
2793
  */
2827
- declare function contentControlLevelOf(control: OoxmlNode): ContentControlLevel;
2828
- /** One control, plus how deeply it is nested and the controls that enclose it. */
2829
- interface ContentControlEntry {
2830
- readonly node: OoxmlContentControlNode;
2831
- /** 0 for a top-level control; the nesting depth otherwise. */
2832
- readonly depth: number;
2833
- /** Enclosing controls, outermost first. Empty at depth 0. */
2834
- readonly ancestors: readonly OoxmlContentControlNode[];
2794
+ interface ContentControlFilter {
2795
+ readonly tag?: string;
2796
+ readonly alias?: string;
2797
+ readonly controlType?: ContentControlType;
2835
2798
  }
2836
2799
  /**
2837
- * Every control under a node, in document order, bounded in depth and in count.
2800
+ * A position in one story: a paragraph and a UTF-16 offset inside it.
2838
2801
  *
2839
- * ONE walk, shared. The nesting bound is the same one layout flattens with, so a control
2840
- * a lane can address is a control every lane can address — and a file that nests past it
2841
- * keeps its content in the tree (the serializer never stops) while no walk recurses
2842
- * further.
2802
+ * The same offset space the ops and the caret use, so an anchor read here can be handed
2803
+ * straight back to a selection without re-deriving anything.
2843
2804
  */
2844
- declare function contentControlsIn(root: OoxmlNode, options?: {
2845
- readonly maxDepth?: number;
2846
- readonly limit?: number;
2847
- }): readonly ContentControlEntry[];
2848
- /** Find one control by canonical node id, with the same bounded walk. */
2849
- declare function findContentControl(root: OoxmlNode, nodeId: string): ContentControlEntry | null;
2805
+ interface DocAnchorRange {
2806
+ /** Canonical part name of the story the range lives in, e.g. `/word/document.xml`. */
2807
+ readonly part: string;
2808
+ readonly startParagraphId: string;
2809
+ readonly startOffset: number;
2810
+ /** May sit in a later paragraph: the range markers are independent elements. */
2811
+ readonly endParagraphId: string;
2812
+ readonly endOffset: number;
2813
+ }
2850
2814
  /**
2851
- * The plain text a control's content holds, in reading order.
2815
+ * One comment or reply, as `comments.xml` records it.
2852
2816
  *
2853
- * `w:delText` is excluded: struck text is not the control's value, and a dropdown whose
2854
- * old item is still present as a tracked deletion would otherwise report both.
2817
+ * A reply is a comment with a `parentId`; OOXML gives replies no separate element, so the thread
2818
+ * is reconstructed from that link rather than from nesting.
2855
2819
  */
2856
- declare function contentControlTextOf(control: OoxmlNode): string;
2820
+ interface DocComment {
2821
+ readonly id: string;
2822
+ readonly author: string;
2823
+ /**
2824
+ * OPTIONAL, because `CT_Comment` makes `@w:date` optional and files omit it. A comment
2825
+ * with no date is a comment, not a defect, and fabricating one is a content change.
2826
+ */
2827
+ readonly date?: string;
2828
+ readonly text: string;
2829
+ readonly parentId?: string;
2830
+ readonly resolved?: boolean;
2831
+ /**
2832
+ * Where the comment is anchored, absent when the file gave it no usable range.
2833
+ *
2834
+ * A comment lives in `comments.xml` and is placed by markers in a STORY, so the story is
2835
+ * part of the address: a comment anchored in a header belongs to a part the body never saw.
2836
+ */
2837
+ readonly anchor?: DocAnchorRange;
2838
+ /**
2839
+ * True when the file gave this comment no usable range — a reference with no markers, or a
2840
+ * start with no end. Reported rather than dropped: a reviewer's remark vanishing silently
2841
+ * is worse than one that says it lost its text.
2842
+ */
2843
+ readonly orphaned?: boolean;
2844
+ }
2857
2845
  /**
2858
- * Resolve the lock a position inherits from the controls enclosing it.
2846
+ * What kind of decision a revision represents.
2859
2847
  *
2860
- * CONSERVATIVE, because a template that says a field cannot be edited and sits inside a
2861
- * section that says it cannot be removed means both. Each half of `ST_Lock` is taken
2862
- * independently and the strongest wins, so an unlocked control inside a `contentLocked`
2863
- * one is still content-locked — a nested control cannot grant a permission its parent
2864
- * withheld, which is the only reading under which a lock is a lock.
2848
+ * Wider than insert/delete/format, and it has to be. `w:moveFrom`/`w:moveTo` are not a
2849
+ * deletion and an insertion — resolving one half alone duplicates or loses the content;
2850
+ * `w:pPr/w:rPr/w:ins|w:del` decorates no characters at all and merges paragraphs when
2851
+ * resolved; a row or cell revision is structural. A reviewer shown only three kinds is a
2852
+ * reviewer who never learns about the rest.
2865
2853
  */
2866
- declare function resolveContentControlLock(chain: readonly ContentControlLock[]): ContentControlLock;
2867
- /** Whether a resolved lock forbids editing the content it covers. */
2868
- declare function lockForbidsEdit(lock: ContentControlLock): boolean;
2869
- /** Whether a resolved lock forbids removing the control itself. */
2870
- declare function lockForbidsRemoval(lock: ContentControlLock): boolean;
2854
+ type RevisionType = 'insert' | 'delete'
2855
+ /** A deletion and an insertion that are one edit: text typed over a selection. */
2856
+ | 'replace' | 'moveFrom' | 'moveTo'
2857
+ /** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
2858
+ | 'format'
2859
+ /** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
2860
+ | 'paragraphMark'
2861
+ /** A row, cell, section or grid revision. */
2862
+ | 'structural';
2871
2863
  /**
2872
- * `CT_SdtPr`'s declared child order (§17.5.2.38), followed by the type choice.
2864
+ * One tracked change.
2873
2865
  *
2874
- * Serialization re-emits children in tree order, so the ONLY way a write keeps the
2875
- * sequence valid is to place the child it authors at its schema position — which is what
2876
- * {@link orderedContentControlProperties} does.
2866
+ * Addressing needs BOTH `id` and `part`: `@w:id` is unique only within a part, so an id alone
2867
+ * names two revisions in any package that has a header or a comments part.
2877
2868
  */
2878
- declare const CONTENT_CONTROL_PROPERTY_ORDER: readonly string[];
2869
+ interface Revision {
2870
+ /** Numeric, and unique only WITHIN a part. Pair with `part` to address one. */
2871
+ readonly id: number;
2872
+ readonly type: RevisionType;
2873
+ readonly author: string;
2874
+ /**
2875
+ * OPTIONAL. `CT_TrackChange` requires `@w:id` and `@w:author` and makes `@w:date`
2876
+ * optional, and producers that omit it are ordinary. Requiring it here forced either a
2877
+ * fabricated date — a content change — or dropping the revision from the list.
2878
+ */
2879
+ readonly date?: string;
2880
+ /**
2881
+ * REQUIRED, and a canonical PART NAME rather than a three-value enum.
2882
+ *
2883
+ * `@w:id` is unique only within a part, so an address without one names two revisions in a
2884
+ * package that has both. The enum could not express the parts revisions actually occur in:
2885
+ * `header3.xml`, `footer1.xml`, `comments.xml`, and `styles.xml`, which carries
2886
+ * `w:pPrChange` inside style definitions with ids that collide with `document.xml`.
2887
+ */
2888
+ readonly part: string;
2889
+ }
2890
+ /** `w:sectPr`: the page a section lays out on. Twips throughout, as the file stores them. */
2891
+ interface SectionProperties {
2892
+ readonly pageSize: {
2893
+ widthTwips: number;
2894
+ heightTwips: number;
2895
+ };
2896
+ readonly margins: PageMargins;
2897
+ readonly columns?: {
2898
+ count: number;
2899
+ gapTwips: number;
2900
+ };
2901
+ /** `w:titlePg` — whether the section's first page takes the `first` header/footer variant. */
2902
+ readonly titlePage?: boolean;
2903
+ }
2904
+ /** Page margins in twips. */
2905
+ interface PageMargins {
2906
+ readonly topTwips: number;
2907
+ readonly rightTwips: number;
2908
+ readonly bottomTwips: number;
2909
+ readonly leftTwips: number;
2910
+ }
2879
2911
  /**
2880
- * Rebuild a `w:sdtPr`'s children in schema order.
2912
+ * `styles.xml`, split by the three style families that address separately.
2881
2913
  *
2882
- * Modelled children sort to their declared position and the type element follows them,
2883
- * as the sequence requires. Everything else — a `w15:repeatingSection`, a vendor
2884
- * extension, the `w14:checkbox` that is not part of the ECMA-376 choice — keeps the order
2885
- * it was authored in and follows the modelled block, so a write that touches one property
2886
- * neither drops nor reorders anything it does not name.
2914
+ * Keyed by style ID rather than by the name a reader sees, because the ID is what a paragraph or
2915
+ * run actually references.
2887
2916
  */
2888
- declare function orderedContentControlProperties(children: readonly OoxmlNode[]): readonly OoxmlNode[];
2917
+ interface StyleDefinitions {
2918
+ readonly paragraph: ReadonlyMap<string, StyleDefinition>;
2919
+ readonly character: ReadonlyMap<string, StyleDefinition>;
2920
+ readonly table: ReadonlyMap<string, StyleDefinition>;
2921
+ }
2922
+ /** One style: the ID content references, the name a reader sees, and its inheritance link. */
2923
+ interface StyleDefinition {
2924
+ readonly id: string;
2925
+ readonly name: string;
2926
+ /** `w:basedOn` — the style this one inherits from. Absent at the root of a chain. */
2927
+ readonly basedOn?: string;
2928
+ }
2929
+ /** A paragraph's list membership: which `numbering.xml` definition, and at which level. */
2930
+ interface NumberingRef {
2931
+ readonly numId: string;
2932
+ /** Zero-based. OOXML numbering has nine levels, 0 through 8. */
2933
+ readonly level: number;
2934
+ }
2889
2935
  /**
2890
- * The next `w:id` to write.
2891
- *
2892
- * Solo: one past the document's own maximum. Never a clock, a timestamp, a random source
2893
- * or a hash: those collide with ids already in the file and produce values Word rejects.
2894
- * Null when the document already reaches the signed 32-bit bound, so the caller refuses
2895
- * rather than wrapping into a negative id. Hostile or out-of-range existing values are
2896
- * ignored for seeding rather than counted past.
2936
+ * A colour as the FILE expresses it, not as a resolved RGB string.
2897
2937
  *
2898
- * When a collaboration actor is bound (explicit argument or the open store transaction),
2899
- * the next id is the next unused value in that actor's stripe. Two peers inserting a
2900
- * generic content control — including a custom node authored as `w:sdt` — from the same
2901
- * snapshot then cannot share a `w:id`.
2938
+ * A theme colour stays a theme reference — slot plus tint or shade — so that changing the theme
2939
+ * repaints the document the way Word does. Flattening to hex at read time would freeze the
2940
+ * resolved value and break that link. `auto` is Word's "let the renderer decide", usually black
2941
+ * on white.
2902
2942
  */
2903
- declare function allocateContentControlId(root: OoxmlNode, actorId?: string): number | null;
2904
-
2905
- /** The value a control accepts, by what kind of control it is. */
2906
- type ContentControlValueInput = {
2907
- readonly kind: 'text';
2908
- readonly text: string;
2909
- } | {
2910
- readonly kind: 'listItem';
2943
+ type ColorValue = {
2944
+ readonly kind: 'hex';
2911
2945
  readonly value: string;
2912
2946
  } | {
2913
- readonly kind: 'checkbox';
2914
- readonly checked: boolean;
2915
- }
2916
- /** A calendar date, `YYYY-MM-DD` or a full ISO-8601 instant. */
2917
- | {
2918
- readonly kind: 'date';
2919
- readonly iso: string;
2947
+ readonly kind: 'theme';
2948
+ readonly slot: string;
2949
+ readonly tint?: number;
2950
+ readonly shade?: number;
2951
+ } | {
2952
+ readonly kind: 'auto';
2920
2953
  };
2921
- /** The control types an insertion may author. Picture and repeating section are deferred. */
2922
- declare const INSERTABLE_CONTENT_CONTROL_TYPES: readonly ["richText", "plainText", "dropDownList", "comboBox", "date"];
2923
- /**
2924
- * The OOXML spellings the store writes.
2925
- *
2926
- * NOT the contract's `InsertableContentControlType`, which is the same set of kinds in the
2927
- * READ vocabulary (`dropdown`) plus this lane's spelling of it (`dropDownList`). The editor
2928
- * maps between the two; the names differ so a reader can tell which vocabulary a value is in.
2929
- */
2930
- type InsertableContentControlKind = (typeof INSERTABLE_CONTENT_CONTROL_TYPES)[number];
2931
-
2932
- /** Allowlisted OOXML table border line styles — store/layout authority. */
2933
- declare const TABLE_BORDER_STYLES: readonly ["single", "dashed", "dotted", "double", "triple", "thick"];
2934
- /**
2935
- * One of the border line styles this engine draws.
2936
- *
2937
- * An ALLOWLIST, not the full `ST_Border` enumeration: a file may name any of the seventy-odd
2938
- * OOXML border styles, and anything outside this set falls back rather than being passed through
2939
- * to paint.
2940
- */
2941
- type TableBorderStyle = (typeof TABLE_BORDER_STYLES)[number];
2954
+ /** `theme1.xml` — what a {@link ColorValue} of kind `theme` resolves against. */
2955
+ interface Theme {
2956
+ readonly colorScheme: ThemeColorScheme;
2957
+ readonly fontScheme?: Record<string, string>;
2958
+ }
2959
+ /** Theme colour slots (`accent1`, `dk1`, `lt2`, …) to hex. */
2960
+ type ThemeColorScheme = Readonly<Record<string, string>>;
2961
+ /** A watermark, which OOXML expresses as either text or an image — never both meaningfully. */
2962
+ interface Watermark {
2963
+ readonly text?: string;
2964
+ readonly imageData?: Uint8Array;
2965
+ }
2966
+ /** A size in EMUs, the unit DrawingML stores extents in. 914400 EMU = 1 inch. */
2967
+ interface Extent {
2968
+ readonly widthEmu: number;
2969
+ readonly heightEmu: number;
2970
+ }
2971
+ /** A font the document names, and whether its bytes travel inside the package. */
2972
+ interface FontDefinition {
2973
+ readonly family: string;
2974
+ readonly embedded: boolean;
2975
+ }
2976
+ /** An axis-aligned rectangle in points, the unit layout works in throughout. */
2977
+ interface Rect {
2978
+ readonly x: number;
2979
+ readonly y: number;
2980
+ readonly width: number;
2981
+ readonly height: number;
2982
+ }
2983
+ /** A position in points. */
2984
+ interface Point {
2985
+ readonly x: number;
2986
+ readonly y: number;
2987
+ }
2988
+ /** What every subscription returns. Calling it twice is safe. */
2989
+ type Unsubscribe = () => void;
2942
2990
 
2943
2991
  /**
2944
2992
  * How a tracked change is addressed: `@w:id`, `@w:author` and `@w:date` together, within a
@@ -3849,7 +3897,7 @@ type TreeDocOp = SetFieldCodeOp | {
3849
3897
  readonly paragraphId: string;
3850
3898
  readonly pageNumberText: string;
3851
3899
  }[];
3852
- } | SetTextFormFieldDefaultOp | CommitTextFormFieldOp | {
3900
+ } | SetTextFormFieldDefaultOp | CommitTextFormFieldOp | SetLegacyCheckboxOp | SetLegacyDropdownOp | InsertBuildingBlockOp | {
3853
3901
  /**
3854
3902
  * Rewrite recognized fields' cached RESULT runs in place — between the `separate` and
3855
3903
  * `end` fldChars, or a `w:fldSimple`'s content. The instruction is never modified; a
@@ -4013,4 +4061,4 @@ type TreeOpResult = {
4013
4061
  readonly detail?: string;
4014
4062
  };
4015
4063
 
4016
- export { type RevisionBatchSkipReason as $, type IndentFormatting as A, type Block as B, type CommentRecord as C, type DocxDocument as D, type ExecResult as E, type ParagraphFlags as F, type ParagraphTabStop as G, type HeaderFooterSet as H, type InsertableContentControlType as I, type RevisionType as J, type Section as K, type SectionProperties as L, type StyleDefinition as M, type Theme as N, type OoxmlPart as O, type Paragraph as P, type ThemeColorScheme as Q, type ReviewModelInput as R, type StyleDefinitions as S, type Table as T, type OoxmlNodeId as U, type OoxmlGenericElementNode as V, type TreeDocOp as W, type OoxmlPackageLimits as X, type OoxmlPackageRejection as Y, type RevisionBatchEntry as Z, type RevisionBatchResult as _, type ReviewItem as a, type CommentPosition as a$, type OoxmlProperty as a0, type OoxmlElement as a1, type OoxmlContentControlNode as a2, type OoxmlContentControlContentNode as a3, type DrawingProjection as a4, type TreeOpRejection as a5, type AutomationTableMutation as a6, type SourceCrop as a7, type DrawingKind as a8, type ImageWrapTarget as a9, type OoxmlSeparatorNode as aA, type DrawingTreeDocOp as aB, type ImpactClass as aC, type TreeDocColorValue as aD, IMAGE_WRAP_TARGETS as aE, TEXT_FORM_FORMATS as aF, type TableBorderStyle as aG, type DrawingAccessibility as aH, type DrawingImageEffects as aI, type DrawingTransform as aJ, type VectorShapeProjection as aK, type OoxmlDrawingNode as aL, type OoxmlInvariantResult as aM, type RelationshipRecord as aN, type OoxmlInvariantIssue as aO, type OoxmlHardBreakNode as aP, type OoxmlFldCharNode as aQ, type OoxmlFldSimpleNode as aR, type OoxmlInstrTextNode as aS, type OoxmlAttribute as aT, type TreeOpResult as aU, type CommentThreadState as aV, ACCEPTED_PARAGRAPH_PROPERTIES as aW, ACCEPTED_RUN_PROPERTIES as aX, CONTENT_CONTROL_ID_MAX as aY, CONTENT_CONTROL_PROPERTY_ORDER as aZ, type CommentAnchor as a_, type DrawingPositionInput as aa, type DrawingLocks as ab, type ParagraphFormatCommand as ac, type Watermark as ad, type Rect as ae, type SelectionPin as af, type Unsubscribe as ag, type DocxDocumentJSON as ah, type DrawingHorizontalReferenceFrame as ai, type DrawingVerticalReferenceFrame as aj, type FontDefinition as ak, type NumberingRef as al, type PageMargins as am, type ParagraphDisagreements as an, type Point as ao, type Run as ap, type TableCell as aq, type TableRow as ar, type RevisionAttributionInput as as, type OoxmlParagraphNode as at, type OoxmlNoteNode as au, type OoxmlContinuationSeparatorNode as av, type OoxmlEndnotesNode as aw, type OoxmlFootnotesNode as ax, type OoxmlNoteRefNode as ay, type OoxmlNoteReferenceNode as az, type ReviewRevisionItem as b, type OoxmlRunNode as b$, type CommitTextFormFieldOp as b0, type ContentControlCheckbox as b1, type ContentControlCheckboxState as b2, type ContentControlDataBinding as b3, type ContentControlDateFormat as b4, type ContentControlEntry as b5, type ContentControlKind as b6, type ContentControlLevel as b7, type ContentControlListItem as b8, type ContentControlLock as b9, type OoxmlContentControlCheckedNode as bA, type OoxmlContentControlCheckedStateNode as bB, type OoxmlContentControlComboBoxNode as bC, type OoxmlContentControlDataBindingNode as bD, type OoxmlContentControlDateFormatNode as bE, type OoxmlContentControlDateNode as bF, type OoxmlContentControlDropDownListNode as bG, type OoxmlContentControlEndPropertiesNode as bH, type OoxmlContentControlLidNode as bI, type OoxmlContentControlListItemNode as bJ, type OoxmlContentControlPropertiesNode as bK, type OoxmlContentControlStoreMappedDataAsNode as bL, type OoxmlContentControlTextNode as bM, type OoxmlContentControlUncheckedStateNode as bN, type OoxmlDocumentNode as bO, type OoxmlExternalTarget as bP, type OoxmlGenericExtensionAttribute as bQ, type OoxmlHyperlinkNode as bR, type OoxmlInvariantIssueCode as bS, type OoxmlKnownNodeAttribute as bT, type OoxmlNamespaceBinding as bU, type OoxmlNodeIdentityRules as bV, type OoxmlPackageResult as bW, type OoxmlParagraphPropertiesNode as bX, type OoxmlPartMetadata as bY, type OoxmlReadRejection as bZ, type OoxmlReadResult as b_, type ContentControlProperties as ba, type ContentTypeError as bb, type ContentTypeIndex as bc, type ContentTypeRecords as bd, DEFAULT_OOXML_PACKAGE_LIMITS as be, DEFAULT_ZIP_LIMITS as bf, type DefaultRecord as bg, type DeleteTextOp as bh, HYPERLINK_RELATIONSHIP_TYPE as bi, type HyperlinkKind as bj, type HyperlinkTarget as bk, IMAGE_RELATIONSHIP_TYPE as bl, type ImageRelationshipResolution as bm, type IndexResult as bn, type InsertTextOp as bo, MAX_CONTENT_CONTROLS_PER_PART as bp, MAX_CONTENT_CONTROL_NESTING as bq, type NameRejection as br, type NameResult as bs, OFFICE_RELATIONSHIP_NAMESPACE_URI as bt, OOXML_NODE_IDENTITY_RULES as bu, type OoxmlBodyNode as bv, type OoxmlBookmarkEndNode as bw, type OoxmlBookmarkStartNode as bx, type OoxmlContentControlCalendarNode as by, type OoxmlContentControlCheckboxNode as bz, type ReviewCommentItem as c, isValidMime as c$, type OoxmlRunPropertiesNode as c0, type OoxmlTabNode as c1, type OoxmlTextElementNode as c2, type OoxmlTextNode as c3, type OoxmlWmlValAttribute as c4, type OoxmlXmlSpaceAttribute as c5, type OverrideRecord as c6, type RelationshipError as c7, type RelationshipSetResult as c8, type RelationshipTargetResolver as c9, childElements as cA, commentAnchorsOfStory as cB, commentsOfPart as cC, contentControlContentChildren as cD, contentControlContentNodeOf as cE, contentControlEndPropertiesNodeOf as cF, contentControlLevelOf as cG, contentControlPropertiesNodeOf as cH, contentControlPropertiesOf as cI, contentControlTextOf as cJ, contentControlsIn as cK, detectDuplicateNames as cL, extensionKey as cM, findContentControl as cN, findElement as cO, firstReviewRange as cP, flattenContentControls as cQ, hasAnyComment as cR, hyperlinkAnchorOf as cS, hyperlinkRelationshipIdOf as cT, hyperlinkTargetOf as cU, isContentControlContentNode as cV, isContentControlNode as cW, isContentControlWrapper as cX, isContentRevisionKind as cY, isHyperlinkNode as cZ, isRangeMarkerKind as c_, type ResolveResult as ca, type ResolvedRelationship as cb, type SetTextFormFieldDefaultOp as cc, TABLE_BORDER_STYLES as cd, type TabStopWrite as ce, type TargetMode as cf, type TextFormFieldOptions as cg, type TextFormFieldRange as ch, type TextFormFieldType as ci, type TreeDocOpKind as cj, type TreeOpEffect as ck, W15_NAMESPACE_URI as cl, WML_NAMESPACE_URI as cm, XMLNS_NAMESPACE_URI as cn, XML_NAMESPACE_URI as co, type XmlLimits as cp, type XmlNode as cq, type XmlRejection as cr, type XmlResult as cs, type ZipLimits as ct, type ZipReadResult as cu, type ZipRejection as cv, allocateContentControlId as cw, asciiFold as cx, buildContentTypeIndex as cy, buildRelationshipSet as cz, type ReviewCustomItem as d, lockForbidsEdit as d0, lockForbidsRemoval as d1, normalizePartName as d2, orderedContentControlProperties as d3, parseContentControlId as d4, partNameKey as d5, planRevisionBatch as d6, projectDrawing as d7, readOnOffChild as d8, readOoxmlPackage as d9, readOoxmlPart as da, readXml as db, readZip as dc, resolveContentControlLock as dd, resolveContentType as de, resolveImageRelationship as df, resolveInternalTarget as dg, resolveRelationship as dh, reviewItemKey as di, reviewItemRanges as dj, textContent as dk, textFormFieldsOf as dl, threadStateOfPart as dm, validateExternalTarget as dn, withPart as dp, writeOoxmlPackage as dq, writeZip as dr, type VectorShapeComponent as ds, activeReviewItem as dt, reviewItemPositionRank as du, reviewItemsAt as dv, reviewThreadRootOf as dw, type ReviewPosition as e, type ReviewRange as f, type ReviewRevisionKind as g, type RevisionAddress as h, type OoxmlNode as i, type OoxmlPackage as j, type ContentControlType as k, type DocTarget as l, type RunFormatting as m, type Extent as n, type ContainerRef as o, type ContentControlFilter as p, type DocRange as q, type Revision as r, type DocComment as s, type ColorValue as t, type ContentControl as u, type DocAnchor as v, type DocAnchorRange as w, type DocLocation as x, type DocumentBody as y, type ExecErrorCode as z };
4064
+ export { type RevisionBatchSkipReason as $, type IndentFormatting as A, type Block as B, type CommentRecord as C, type DocxDocument as D, type ExecResult as E, type ParagraphFlags as F, type ParagraphTabStop as G, type HeaderFooterSet as H, type InsertableContentControlType as I, type RevisionType as J, type Section as K, type SectionProperties as L, type StyleDefinition as M, type Theme as N, type OoxmlPart as O, type Paragraph as P, type ThemeColorScheme as Q, type ReviewModelInput as R, type StyleDefinitions as S, type Table as T, type OoxmlNodeId as U, type OoxmlGenericElementNode as V, type TreeDocOp as W, type OoxmlPackageLimits as X, type OoxmlPackageRejection as Y, type RevisionBatchEntry as Z, type RevisionBatchResult as _, type ReviewItem as a, type CommentPosition as a$, type OoxmlProperty as a0, type OoxmlElement as a1, type OoxmlContentControlNode as a2, type OoxmlContentControlContentNode as a3, type DrawingProjection as a4, type TreeOpRejection as a5, type AutomationTableMutation as a6, type SourceCrop as a7, type DrawingKind as a8, type ImageWrapTarget as a9, type OoxmlSeparatorNode as aA, type DrawingTreeDocOp as aB, type ImpactClass as aC, type TreeDocColorValue as aD, IMAGE_WRAP_TARGETS as aE, TEXT_FORM_FORMATS as aF, type TableBorderStyle as aG, type DrawingAccessibility as aH, type DrawingImageEffects as aI, type DrawingTransform as aJ, type VectorShapeProjection as aK, type OoxmlDrawingNode as aL, type OoxmlInvariantResult as aM, type OoxmlFldCharNode as aN, type OoxmlFldSimpleNode as aO, type OoxmlInstrTextNode as aP, type OoxmlInvariantIssue as aQ, type TreeOpResult as aR, type RelationshipRecord as aS, type OoxmlHardBreakNode as aT, type OoxmlAttribute as aU, type CommentThreadState as aV, ACCEPTED_PARAGRAPH_PROPERTIES as aW, ACCEPTED_RUN_PROPERTIES as aX, CONTENT_CONTROL_ID_MAX as aY, CONTENT_CONTROL_PROPERTY_ORDER as aZ, type CommentAnchor as a_, type DrawingPositionInput as aa, type DrawingLocks as ab, type ParagraphFormatCommand as ac, type Watermark as ad, type Rect as ae, type SelectionPin as af, type Unsubscribe as ag, type DocxDocumentJSON as ah, type DrawingHorizontalReferenceFrame as ai, type DrawingVerticalReferenceFrame as aj, type FontDefinition as ak, type NumberingRef as al, type PageMargins as am, type ParagraphDisagreements as an, type Point as ao, type Run as ap, type TableCell as aq, type TableRow as ar, type RevisionAttributionInput as as, type OoxmlParagraphNode as at, type OoxmlNoteNode as au, type OoxmlContinuationSeparatorNode as av, type OoxmlEndnotesNode as aw, type OoxmlFootnotesNode as ax, type OoxmlNoteRefNode as ay, type OoxmlNoteReferenceNode as az, type ReviewRevisionItem as b, type OoxmlReadRejection as b$, type CommitTextFormFieldOp as b0, type ContentControlCheckbox as b1, type ContentControlCheckboxState as b2, type ContentControlDataBinding as b3, type ContentControlDateFormat as b4, type ContentControlEntry as b5, type ContentControlKind as b6, type ContentControlLevel as b7, type ContentControlListItem as b8, type ContentControlLock as b9, type OoxmlContentControlCalendarNode as bA, type OoxmlContentControlCheckboxNode as bB, type OoxmlContentControlCheckedNode as bC, type OoxmlContentControlCheckedStateNode as bD, type OoxmlContentControlComboBoxNode as bE, type OoxmlContentControlDataBindingNode as bF, type OoxmlContentControlDateFormatNode as bG, type OoxmlContentControlDateNode as bH, type OoxmlContentControlDropDownListNode as bI, type OoxmlContentControlEndPropertiesNode as bJ, type OoxmlContentControlLidNode as bK, type OoxmlContentControlListItemNode as bL, type OoxmlContentControlPropertiesNode as bM, type OoxmlContentControlStoreMappedDataAsNode as bN, type OoxmlContentControlTextNode as bO, type OoxmlContentControlUncheckedStateNode as bP, type OoxmlDocumentNode as bQ, type OoxmlExternalTarget as bR, type OoxmlGenericExtensionAttribute as bS, type OoxmlHyperlinkNode as bT, type OoxmlInvariantIssueCode as bU, type OoxmlKnownNodeAttribute as bV, type OoxmlNamespaceBinding as bW, type OoxmlNodeIdentityRules as bX, type OoxmlPackageResult as bY, type OoxmlParagraphPropertiesNode as bZ, type OoxmlPartMetadata as b_, type ContentControlProperties as ba, type ContentTypeError as bb, type ContentTypeIndex as bc, type ContentTypeRecords as bd, DEFAULT_OOXML_PACKAGE_LIMITS as be, DEFAULT_ZIP_LIMITS as bf, type DefaultRecord as bg, type DeleteTextOp as bh, HYPERLINK_RELATIONSHIP_TYPE as bi, type HyperlinkKind as bj, type HyperlinkTarget as bk, IMAGE_RELATIONSHIP_TYPE as bl, type ImageRelationshipResolution as bm, type IndexResult as bn, type InsertBuildingBlockOp as bo, type InsertTextOp as bp, type LegacyCheckboxFieldRange as bq, MAX_CONTENT_CONTROLS_PER_PART as br, MAX_CONTENT_CONTROL_NESTING as bs, type NameRejection as bt, type NameResult as bu, OFFICE_RELATIONSHIP_NAMESPACE_URI as bv, OOXML_NODE_IDENTITY_RULES as bw, type OoxmlBodyNode as bx, type OoxmlBookmarkEndNode as by, type OoxmlBookmarkStartNode as bz, type ReviewCommentItem as c, isContentControlWrapper as c$, type OoxmlReadResult as c0, type OoxmlRunNode as c1, type OoxmlRunPropertiesNode as c2, type OoxmlTabNode as c3, type OoxmlTextElementNode as c4, type OoxmlTextNode as c5, type OoxmlWmlValAttribute as c6, type OoxmlXmlSpaceAttribute as c7, type OverrideRecord as c8, type RelationshipError as c9, allocateContentControlId as cA, asciiFold as cB, buildContentTypeIndex as cC, buildRelationshipSet as cD, childElements as cE, commentAnchorsOfStory as cF, commentsOfPart as cG, contentControlContentChildren as cH, contentControlContentNodeOf as cI, contentControlEndPropertiesNodeOf as cJ, contentControlLevelOf as cK, contentControlPropertiesNodeOf as cL, contentControlPropertiesOf as cM, contentControlTextOf as cN, contentControlsIn as cO, detectDuplicateNames as cP, extensionKey as cQ, findContentControl as cR, findElement as cS, firstReviewRange as cT, flattenContentControls as cU, hasAnyComment as cV, hyperlinkAnchorOf as cW, hyperlinkRelationshipIdOf as cX, hyperlinkTargetOf as cY, isContentControlContentNode as cZ, isContentControlNode as c_, type RelationshipSetResult as ca, type RelationshipTargetResolver as cb, type ResolveResult as cc, type ResolvedRelationship as cd, type SetLegacyCheckboxOp as ce, type SetLegacyDropdownOp as cf, type SetTextFormFieldDefaultOp as cg, TABLE_BORDER_STYLES as ch, type TabStopWrite as ci, type TargetMode as cj, type TextFormFieldOptions as ck, type TextFormFieldRange as cl, type TextFormFieldType as cm, type TreeDocOpKind as cn, type TreeOpEffect as co, W15_NAMESPACE_URI as cp, WML_NAMESPACE_URI as cq, XMLNS_NAMESPACE_URI as cr, XML_NAMESPACE_URI as cs, type XmlLimits as ct, type XmlNode as cu, type XmlRejection as cv, type XmlResult as cw, type ZipLimits as cx, type ZipReadResult as cy, type ZipRejection as cz, type ReviewCustomItem as d, isContentRevisionKind as d0, isHyperlinkNode as d1, isRangeMarkerKind as d2, isValidMime as d3, legacyCheckboxFieldsOf as d4, lockForbidsEdit as d5, lockForbidsRemoval as d6, normalizePartName as d7, orderedContentControlProperties as d8, parseContentControlId as d9, reviewItemsAt as dA, reviewThreadRootOf as dB, partNameKey as da, planRevisionBatch as db, projectDrawing as dc, readOnOffChild as dd, readOoxmlPackage as de, readOoxmlPart as df, readXml as dg, readZip as dh, resolveContentControlLock as di, resolveContentType as dj, resolveImageRelationship as dk, resolveInternalTarget as dl, resolveRelationship as dm, reviewItemKey as dn, reviewItemRanges as dp, textContent as dq, textFormFieldsOf as dr, threadStateOfPart as ds, validateExternalTarget as dt, withPart as du, writeOoxmlPackage as dv, writeZip as dw, type VectorShapeComponent as dx, activeReviewItem as dy, reviewItemPositionRank as dz, type ReviewPosition as e, type ReviewRange as f, type ReviewRevisionKind as g, type RevisionAddress as h, type OoxmlNode as i, type OoxmlPackage as j, type ContentControlType as k, type DocTarget as l, type RunFormatting as m, type Extent as n, type ContainerRef as o, type ContentControlFilter as p, type DocRange as q, type Revision as r, type DocComment as s, type ColorValue as t, type ContentControl as u, type DocAnchor as v, type DocAnchorRange as w, type DocLocation as x, type DocumentBody as y, type ExecErrorCode as z };