@beyondwork/docx-react-component 1.0.102 → 1.0.104

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 (57) hide show
  1. package/package.json +1 -1
  2. package/src/api/public-types.ts +63 -1
  3. package/src/api/v3/_runtime-handle.ts +2 -0
  4. package/src/api/v3/ai/outline.ts +2 -7
  5. package/src/api/v3/runtime/geometry.ts +79 -0
  6. package/src/core/commands/formatting-commands.ts +8 -7
  7. package/src/core/commands/paragraph-layout-commands.ts +11 -10
  8. package/src/core/commands/section-layout-commands.ts +7 -6
  9. package/src/core/commands/style-commands.ts +3 -2
  10. package/src/io/normalize/normalize-text.ts +6 -5
  11. package/src/io/ooxml/parse-anchor.ts +15 -15
  12. package/src/io/ooxml/parse-drawing.ts +103 -5
  13. package/src/io/ooxml/parse-fields.ts +43 -21
  14. package/src/io/ooxml/parse-font-table.ts +2 -1
  15. package/src/io/ooxml/parse-footnotes.ts +3 -2
  16. package/src/io/ooxml/parse-headers-footers.ts +7 -6
  17. package/src/io/ooxml/parse-main-document.ts +41 -40
  18. package/src/io/ooxml/parse-numbering.ts +3 -2
  19. package/src/io/ooxml/parse-object.ts +6 -6
  20. package/src/io/ooxml/parse-paragraph-formatting.ts +12 -11
  21. package/src/io/ooxml/parse-picture.ts +16 -16
  22. package/src/io/ooxml/parse-run-formatting.ts +11 -10
  23. package/src/io/ooxml/parse-settings.ts +2 -1
  24. package/src/io/ooxml/parse-shapes.ts +148 -17
  25. package/src/io/ooxml/parse-styles.ts +16 -16
  26. package/src/io/ooxml/parse-theme.ts +5 -4
  27. package/src/model/canonical-document.ts +869 -836
  28. package/src/model/canonical-layout-inputs.ts +979 -0
  29. package/src/model/layout/index.ts +6 -0
  30. package/src/model/layout/page-graph-types.ts +61 -0
  31. package/src/model/layout/runtime-page-graph-types.ts +10 -0
  32. package/src/runtime/collab/runtime-collab-sync.ts +3 -3
  33. package/src/runtime/debug/build-debug-inspector-snapshot.ts +17 -4
  34. package/src/runtime/document-runtime.ts +30 -14
  35. package/src/runtime/event-refresh-hints.ts +3 -0
  36. package/src/runtime/formatting/document-lookup.ts +3 -2
  37. package/src/runtime/formatting/formatting-context.ts +176 -34
  38. package/src/runtime/formatting/index.ts +20 -0
  39. package/src/runtime/formatting/layout-inputs.ts +320 -0
  40. package/src/runtime/formatting/numbering/geometry.ts +13 -12
  41. package/src/runtime/formatting/style-cascade.ts +2 -1
  42. package/src/runtime/formatting/table-style-resolver.ts +8 -7
  43. package/src/runtime/geometry/caret-geometry.ts +82 -10
  44. package/src/runtime/geometry/geometry-facet.ts +36 -0
  45. package/src/runtime/geometry/geometry-index.ts +891 -0
  46. package/src/runtime/geometry/geometry-types.ts +221 -1
  47. package/src/runtime/geometry/index.ts +26 -0
  48. package/src/runtime/geometry/inert-geometry-facet.ts +3 -0
  49. package/src/runtime/geometry/replacement-envelope.ts +41 -2
  50. package/src/runtime/layout/layout-engine-version.ts +16 -1
  51. package/src/runtime/layout/page-graph.ts +191 -1
  52. package/src/runtime/prerender/graph-canonicalize.ts +30 -0
  53. package/src/runtime/surface-projection.ts +74 -39
  54. package/src/runtime/workflow/coordinator.ts +57 -11
  55. package/src/session/import/normalize.ts +2 -1
  56. package/src/session/import/source-package-evidence.ts +612 -1
  57. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +3 -0
@@ -105,7 +105,7 @@ export interface CanonicalDocument {
105
105
  */
106
106
  export interface CanonicalDocumentFragment {
107
107
  /** Block-level children of the fragment, in document order. */
108
- readonly blocks: ReadonlyArray<BlockNode>;
108
+ readonly blocks: BlockNode[];
109
109
  }
110
110
 
111
111
  /**
@@ -138,18 +138,20 @@ export type MutableCanonicalDocumentFragment = Mutable<CanonicalDocumentFragment
138
138
  * Never cast a value received as a parameter. Only cast values you just
139
139
  * created via object literal, spread, `structuredClone`, or `createCanonicalDocument`.
140
140
  *
141
- * **Shallow only.** `Mutable<T>` strips `readonly` at the top level of `T`;
142
- * nested `ReadonlyArray<…>`, `Readonly<…>`, and inner `readonly` modifiers
143
- * declared inside referenced types are preserved. For example,
144
- * `Mutable<CanonicalDocument>` does NOT make `draft.content.children`
145
- * mutable `content` is `DocumentRootNode`, whose `children` field is
146
- * declared as it is in the source type. If you need to mutate nested
147
- * structure, build the nested value first (also as a `Mutable<…>` of the
148
- * nested type) and assign it onto the draft as a complete replacement.
149
- * Deep-readonly propagation across nested catalogs/stores/nodes is a
150
- * deferred D1 follow-up (see `docs/plans/refactor/02-canonical-document-deferred.md`).
141
+ * `Mutable<T>` strips readonly recursively, including `…[]`,
142
+ * `Readonly<…>`, and inner readonly modifiers declared inside referenced
143
+ * types. That is intentional for D1: parser/runtime producers can build a
144
+ * fully local draft with normal assignment and array operations, then return
145
+ * it widened to the readonly canonical type at the function boundary.
151
146
  */
152
- export type Mutable<T> = { -readonly [K in keyof T]: T[K] };
147
+ export type Mutable<T> =
148
+ T extends (...args: infer Args) => infer Return
149
+ ? (...args: Args) => Return
150
+ : T extends (infer Item)[]
151
+ ? Mutable<Item>[]
152
+ : T extends object
153
+ ? { -readonly [K in keyof T]: Mutable<T[K]> }
154
+ : T;
153
155
 
154
156
  /**
155
157
  * Draft-only mutable view of `CanonicalDocument`. Equivalent to
@@ -186,92 +188,92 @@ export type MutableCanonicalFontTable = Mutable<CanonicalFontTable>;
186
188
 
187
189
 
188
190
  export interface DocumentMetadata {
189
- title?: string;
190
- subject?: string;
191
- description?: string;
192
- creator?: string;
193
- language?: string;
194
- keywords?: string[];
195
- category?: string;
196
- lastModifiedBy?: string;
197
- contentStatus?: string;
198
- revision?: string;
199
- version?: string;
200
- createdUtc?: string;
201
- modifiedUtc?: string;
202
- importMode?: string;
203
- appProperties?: DocumentAppProperties;
204
- customProperties: Record<string, string>;
191
+ readonly title?: string;
192
+ readonly subject?: string;
193
+ readonly description?: string;
194
+ readonly creator?: string;
195
+ readonly language?: string;
196
+ readonly keywords?: string[];
197
+ readonly category?: string;
198
+ readonly lastModifiedBy?: string;
199
+ readonly contentStatus?: string;
200
+ readonly revision?: string;
201
+ readonly version?: string;
202
+ readonly createdUtc?: string;
203
+ readonly modifiedUtc?: string;
204
+ readonly importMode?: string;
205
+ readonly appProperties?: DocumentAppProperties;
206
+ readonly customProperties: Record<string, string>;
205
207
  }
206
208
 
207
209
  export interface DocumentAppProperties {
208
- application?: string;
209
- appVersion?: string;
210
- template?: string;
211
- pages?: number;
212
- words?: number;
213
- characters?: number;
214
- charactersWithSpaces?: number;
215
- totalTime?: number;
216
- company?: string;
217
- manager?: string;
218
- docSecurity?: number;
210
+ readonly application?: string;
211
+ readonly appVersion?: string;
212
+ readonly template?: string;
213
+ readonly pages?: number;
214
+ readonly words?: number;
215
+ readonly characters?: number;
216
+ readonly charactersWithSpaces?: number;
217
+ readonly totalTime?: number;
218
+ readonly company?: string;
219
+ readonly manager?: string;
220
+ readonly docSecurity?: number;
219
221
  }
220
222
 
221
223
  export interface StylesCatalog {
222
- paragraphs: Record<string, ParagraphStyleDefinition>;
223
- characters: Record<string, CharacterStyleDefinition>;
224
- tables: Record<string, TableStyleDefinition>;
224
+ readonly paragraphs: Record<string, ParagraphStyleDefinition>;
225
+ readonly characters: Record<string, CharacterStyleDefinition>;
226
+ readonly tables: Record<string, TableStyleDefinition>;
225
227
  /**
226
228
  * `w:type="numbering"` styles — preserve-only. Word emits these for
227
229
  * style-linked list families (List Bullet, List Number). Canonical
228
230
  * consumers do not apply numbering via these styles; they survive
229
231
  * only for round-trip fidelity.
230
232
  */
231
- numberingStyles?: Record<string, NumberingStyleDefinition>;
232
- latentStyles?: Record<string, LatentStyleDefinition>;
233
- fromPackage?: boolean;
234
- docDefaults?: DocumentDefaults;
233
+ readonly numberingStyles?: Record<string, NumberingStyleDefinition>;
234
+ readonly latentStyles?: Record<string, LatentStyleDefinition>;
235
+ readonly fromPackage?: boolean;
236
+ readonly docDefaults?: DocumentDefaults;
235
237
  }
236
238
 
237
239
  /** A `<w:style w:type="numbering">` entry. Preserve-only. */
238
240
  export interface NumberingStyleDefinition {
239
- styleId: string;
240
- displayName: string;
241
- kind: "numbering";
242
- isDefault: boolean;
243
- basedOn?: string;
244
- aliases?: string[];
241
+ readonly styleId: string;
242
+ readonly displayName: string;
243
+ readonly kind: "numbering";
244
+ readonly isDefault: boolean;
245
+ readonly basedOn?: string;
246
+ readonly aliases?: string[];
245
247
  /**
246
248
  * The `<w:numId w:val="..."/>` inside the `<w:pPr><w:numPr>` subtree,
247
249
  * when present. Consumers that later resolve numbering-type styles to
248
250
  * numbering instances (Lane 3a) will use this.
249
251
  */
250
- numberingInstanceId?: string;
252
+ readonly numberingInstanceId?: string;
251
253
  }
252
254
 
253
255
  export interface ParagraphStyleDefinition {
254
- styleId: string;
255
- basedOn?: string;
256
- nextStyle?: string;
257
- outlineLevel?: number;
258
- numbering?: ParagraphStyleNumberingReference;
259
- displayName: string;
256
+ readonly styleId: string;
257
+ readonly basedOn?: string;
258
+ readonly nextStyle?: string;
259
+ readonly outlineLevel?: number;
260
+ readonly numbering?: ParagraphStyleNumberingReference;
261
+ readonly displayName: string;
260
262
  /**
261
263
  * Alternate display names parsed from `<w:aliases w:val="A,B"/>`
262
264
  * (ECMA-376 §17.7.4.2). Comma-separated in source; stored as array.
263
265
  */
264
- aliases?: string[];
266
+ readonly aliases?: string[];
265
267
  /**
266
268
  * `<w:autoRedefine/>` — when true, Word re-records paragraph direct
267
269
  * formatting into the style definition as the user types (ECMA-376
268
270
  * §17.7.4.3). Round-trip preservation only; no render effect.
269
271
  */
270
- autoRedefine?: boolean;
271
- kind: "paragraph";
272
- isDefault: boolean;
273
- paragraphProperties?: CanonicalParagraphFormatting;
274
- runProperties?: CanonicalRunFormatting;
272
+ readonly autoRedefine?: boolean;
273
+ readonly kind: "paragraph";
274
+ readonly isDefault: boolean;
275
+ readonly paragraphProperties?: CanonicalParagraphFormatting;
276
+ readonly runProperties?: CanonicalRunFormatting;
275
277
  /**
276
278
  * Style ID of the linked character style (from `<w:link w:val="..."/>`).
277
279
  * Populated during parse; the second-pass resolver in `parse-styles.ts`
@@ -279,55 +281,55 @@ export interface ParagraphStyleDefinition {
279
281
  * declares the relationship on one side. Mirrors LibreOffice's
280
282
  * `StyleSheetTable.cxx:535` second pass.
281
283
  */
282
- linkedStyleId?: string;
284
+ readonly linkedStyleId?: string;
283
285
  }
284
286
 
285
287
  export interface ParagraphStyleNumberingReference {
286
- numberingInstanceId: string;
287
- level?: number;
288
+ readonly numberingInstanceId: string;
289
+ readonly level?: number;
288
290
  }
289
291
 
290
292
  export interface CharacterStyleDefinition {
291
- styleId: string;
292
- basedOn?: string;
293
- displayName: string;
293
+ readonly styleId: string;
294
+ readonly basedOn?: string;
295
+ readonly displayName: string;
294
296
  /** See `ParagraphStyleDefinition.aliases`. */
295
- aliases?: string[];
296
- kind: "character";
297
- isDefault: boolean;
298
- runProperties?: CanonicalRunFormatting;
297
+ readonly aliases?: string[];
298
+ readonly kind: "character";
299
+ readonly isDefault: boolean;
300
+ readonly runProperties?: CanonicalRunFormatting;
299
301
  /**
300
302
  * Style ID of the linked paragraph style (from `<w:link w:val="..."/>`).
301
303
  * See `ParagraphStyleDefinition.linkedStyleId` for the second-pass
302
304
  * reciprocal-resolution contract.
303
305
  */
304
- linkedStyleId?: string;
306
+ readonly linkedStyleId?: string;
305
307
  }
306
308
 
307
309
  export interface TableStyleDefinition {
308
- styleId: string;
309
- basedOn?: string;
310
- displayName: string;
310
+ readonly styleId: string;
311
+ readonly basedOn?: string;
312
+ readonly displayName: string;
311
313
  /** See `ParagraphStyleDefinition.aliases`. */
312
- aliases?: string[];
313
- kind: "table";
314
- isDefault: boolean;
315
- formatting?: TableStyleFormatting;
316
- conditionalFormatting?: Partial<Record<TableStyleConditionalRegion, TableStyleFormatting>>;
314
+ readonly aliases?: string[];
315
+ readonly kind: "table";
316
+ readonly isDefault: boolean;
317
+ readonly formatting?: TableStyleFormatting;
318
+ readonly conditionalFormatting?: Partial<Record<TableStyleConditionalRegion, TableStyleFormatting>>;
317
319
  }
318
320
 
319
321
  export interface LatentStyleDefinition {
320
- name: string;
321
- locked?: boolean;
322
- semiHidden?: boolean;
323
- unhideWhenUsed?: boolean;
324
- qFormat?: boolean;
325
- uiPriority?: number;
322
+ readonly name: string;
323
+ readonly locked?: boolean;
324
+ readonly semiHidden?: boolean;
325
+ readonly unhideWhenUsed?: boolean;
326
+ readonly qFormat?: boolean;
327
+ readonly uiPriority?: number;
326
328
  }
327
329
 
328
330
  export interface NumberingCatalog {
329
- abstractDefinitions: Record<string, AbstractNumberingDefinition>;
330
- instances: Record<string, NumberingInstance>;
331
+ readonly abstractDefinitions: Record<string, AbstractNumberingDefinition>;
332
+ readonly instances: Record<string, NumberingInstance>;
331
333
  /**
332
334
  * `<w:numPicBullet>` media-catalog entries keyed by `w:numPicBulletId`.
333
335
  * Each entry preserves the raw drawing/pic XML plus a resolved media
@@ -335,7 +337,7 @@ export interface NumberingCatalog {
335
337
  * Consumed by numbering-prefix when `NumberingLevelDefinition.picBulletId`
336
338
  * is set (ECMA-376 §17.9.19). Rendering is Lane 6 territory.
337
339
  */
338
- numPicBullets?: Record<string, NumPicBullet>;
340
+ readonly numPicBullets?: Record<string, NumPicBullet>;
339
341
  }
340
342
 
341
343
  /**
@@ -348,101 +350,101 @@ export interface NumberingCatalog {
348
350
  */
349
351
  export interface NumPicBullet {
350
352
  /** `w:numPicBulletId` attribute value — the lookup key. */
351
- numPicBulletId: string;
353
+ readonly numPicBulletId: string;
352
354
  /** Verbatim XML for the `<w:numPicBullet>` element. */
353
- rawXml: string;
355
+ readonly rawXml: string;
354
356
  /**
355
357
  * Resolved media-catalog media id when the bullet's inner image references
356
358
  * a relationship we could resolve at parse time. Undefined when the
357
359
  * bullet uses an unresolved blipRef or a VML path.
358
360
  */
359
- mediaId?: string;
361
+ readonly mediaId?: string;
360
362
  /**
361
363
  * Bullet cell extent in EMU (English Metric Units) when parsed from the
362
364
  * drawing's `wp:extent` — lets the renderer size the bullet image without
363
365
  * round-tripping through a new measurement pass. Undefined for VML paths.
364
366
  */
365
- widthEmu?: number;
366
- heightEmu?: number;
367
+ readonly widthEmu?: number;
368
+ readonly heightEmu?: number;
367
369
  }
368
370
 
369
371
  export interface AbstractNumberingDefinition {
370
- abstractNumberingId: string;
371
- levels: NumberingLevelDefinition[];
372
- nsid?: string;
373
- multiLevelType?: "singleLevel" | "multilevel" | "hybridMultilevel";
372
+ readonly abstractNumberingId: string;
373
+ readonly levels: NumberingLevelDefinition[];
374
+ readonly nsid?: string;
375
+ readonly multiLevelType?: "singleLevel" | "multilevel" | "hybridMultilevel";
374
376
  /** ECMA-376 17.9.26 `<w:tmpl>` — template code identifying the abstractNum's origin template (ST_LongHexNumber). Preserved for round-trip. */
375
- tplc?: string;
376
- styleLink?: string;
377
- numStyleLink?: string;
377
+ readonly tplc?: string;
378
+ readonly styleLink?: string;
379
+ readonly numStyleLink?: string;
378
380
  }
379
381
 
380
382
  export interface NumberingLevelParagraphGeometry {
381
- justification?: "left" | "center" | "right" | "both" | "distribute";
382
- spacing?: ParagraphSpacing;
383
- indentation?: ParagraphIndentation;
384
- tabStops?: TabStop[];
383
+ readonly justification?: "left" | "center" | "right" | "both" | "distribute";
384
+ readonly spacing?: ParagraphSpacing;
385
+ readonly indentation?: ParagraphIndentation;
386
+ readonly tabStops?: TabStop[];
385
387
  }
386
388
 
387
389
  export interface NumberingLevelDefinition {
388
- level: number;
389
- format: string;
390
- text: string;
391
- startAt?: number;
392
- paragraphStyleId?: string;
393
- isLegalNumbering?: boolean;
394
- suffix?: "tab" | "space" | "nothing";
395
- paragraphGeometry?: NumberingLevelParagraphGeometry;
396
- runProperties?: CanonicalRunFormatting;
397
- restartAfterLevel?: number;
390
+ readonly level: number;
391
+ readonly format: string;
392
+ readonly text: string;
393
+ readonly startAt?: number;
394
+ readonly paragraphStyleId?: string;
395
+ readonly isLegalNumbering?: boolean;
396
+ readonly suffix?: "tab" | "space" | "nothing";
397
+ readonly paragraphGeometry?: NumberingLevelParagraphGeometry;
398
+ readonly runProperties?: CanonicalRunFormatting;
399
+ readonly restartAfterLevel?: number;
398
400
  /**
399
401
  * `<w:lvlPicBulletId w:val="N"/>` reference — picks the matching entry
400
402
  * from `NumberingCatalog.numPicBullets`. When set, the marker renders as
401
403
  * an inline image instead of a text glyph. ECMA-376 §17.9.12.
402
404
  */
403
- picBulletId?: string;
405
+ readonly picBulletId?: string;
404
406
  }
405
407
 
406
408
  export interface NumberingLevelOverrideDefinition {
407
- level: number;
408
- format?: string;
409
- text?: string;
410
- startAt?: number;
411
- paragraphStyleId?: string;
412
- isLegalNumbering?: boolean;
413
- suffix?: "tab" | "space" | "nothing";
414
- paragraphGeometry?: NumberingLevelParagraphGeometry;
415
- runProperties?: CanonicalRunFormatting;
416
- restartAfterLevel?: number;
417
- picBulletId?: string;
409
+ readonly level: number;
410
+ readonly format?: string;
411
+ readonly text?: string;
412
+ readonly startAt?: number;
413
+ readonly paragraphStyleId?: string;
414
+ readonly isLegalNumbering?: boolean;
415
+ readonly suffix?: "tab" | "space" | "nothing";
416
+ readonly paragraphGeometry?: NumberingLevelParagraphGeometry;
417
+ readonly runProperties?: CanonicalRunFormatting;
418
+ readonly restartAfterLevel?: number;
419
+ readonly picBulletId?: string;
418
420
  }
419
421
 
420
422
  export interface NumberingInstance {
421
- numberingInstanceId: string;
422
- abstractNumberingId: string;
423
- overrides: NumberingLevelOverride[];
423
+ readonly numberingInstanceId: string;
424
+ readonly abstractNumberingId: string;
425
+ readonly overrides: NumberingLevelOverride[];
424
426
  }
425
427
 
426
428
  export interface NumberingLevelOverride {
427
- level: number;
428
- startAt?: number;
429
- levelDefinition?: NumberingLevelOverrideDefinition;
429
+ readonly level: number;
430
+ readonly startAt?: number;
431
+ readonly levelDefinition?: NumberingLevelOverrideDefinition;
430
432
  }
431
433
 
432
434
  export interface MediaCatalog {
433
- items: Record<string, MediaItem>;
435
+ readonly items: Record<string, MediaItem>;
434
436
  }
435
437
 
436
438
  export interface MediaItem {
437
- mediaId: string;
438
- contentType: string;
439
- filename: string;
440
- relationshipId?: string;
441
- packagePartName: string;
442
- altText?: string;
443
- display?: "inline" | "floating";
444
- widthEmu?: number;
445
- heightEmu?: number;
439
+ readonly mediaId: string;
440
+ readonly contentType: string;
441
+ readonly filename: string;
442
+ readonly relationshipId?: string;
443
+ readonly packagePartName: string;
444
+ readonly altText?: string;
445
+ readonly display?: "inline" | "floating";
446
+ readonly widthEmu?: number;
447
+ readonly heightEmu?: number;
446
448
  }
447
449
 
448
450
  // ---- Sub-part canonical types ----
@@ -450,25 +452,25 @@ export interface MediaItem {
450
452
  export type HeaderFooterVariant = "default" | "first" | "even";
451
453
 
452
454
  export interface HeaderDocument {
453
- variant: HeaderFooterVariant;
454
- partPath: string;
455
- relationshipId: string;
456
- blocks: BlockNode[];
457
- sectionIndex?: number;
455
+ readonly variant: HeaderFooterVariant;
456
+ readonly partPath: string;
457
+ readonly relationshipId: string;
458
+ readonly blocks: BlockNode[];
459
+ readonly sectionIndex?: number;
458
460
  }
459
461
 
460
462
  export interface FooterDocument {
461
- variant: HeaderFooterVariant;
462
- partPath: string;
463
- relationshipId: string;
464
- blocks: BlockNode[];
465
- sectionIndex?: number;
463
+ readonly variant: HeaderFooterVariant;
464
+ readonly partPath: string;
465
+ readonly relationshipId: string;
466
+ readonly blocks: BlockNode[];
467
+ readonly sectionIndex?: number;
466
468
  }
467
469
 
468
470
  export interface FootnoteDefinition {
469
- noteId: string;
470
- kind: "footnote" | "endnote";
471
- blocks: BlockNode[];
471
+ readonly noteId: string;
472
+ readonly kind: "footnote" | "endnote";
473
+ readonly blocks: BlockNode[];
472
474
  }
473
475
 
474
476
  /**
@@ -483,33 +485,33 @@ export interface FootnoteDefinition {
483
485
  */
484
486
  export interface FootnoteSeparators {
485
487
  /** Raw XML of the <w:r> children inside the separator paragraph. */
486
- separatorContent?: string;
488
+ readonly separatorContent?: string;
487
489
  /** Full XML of the first separator paragraph. */
488
- separatorParagraphXml?: string;
490
+ readonly separatorParagraphXml?: string;
489
491
  /** Raw XML of the <w:r> children inside the continuationSeparator paragraph. */
490
- continuationSeparatorContent?: string;
492
+ readonly continuationSeparatorContent?: string;
491
493
  /** Full XML of the first continuation-separator paragraph. */
492
- continuationSeparatorParagraphXml?: string;
494
+ readonly continuationSeparatorParagraphXml?: string;
493
495
  }
494
496
 
495
497
  export interface FootnoteCollection {
496
- footnotes: Record<string, FootnoteDefinition>;
497
- endnotes: Record<string, FootnoteDefinition>;
498
+ readonly footnotes: Record<string, FootnoteDefinition>;
499
+ readonly endnotes: Record<string, FootnoteDefinition>;
498
500
  /** Separator content parsed from footnotes.xml special entries. */
499
- footnoteSeparators?: FootnoteSeparators;
501
+ readonly footnoteSeparators?: FootnoteSeparators;
500
502
  /** Separator content parsed from endnotes.xml special entries. */
501
- endnoteSeparators?: FootnoteSeparators;
503
+ readonly endnoteSeparators?: FootnoteSeparators;
502
504
  }
503
505
 
504
506
  export interface ThemeColorScheme {
505
- name: string;
506
- colors: Record<string, string>;
507
+ readonly name: string;
508
+ readonly colors: Record<string, string>;
507
509
  }
508
510
 
509
511
  export interface ThemeFontScheme {
510
- name: string;
511
- majorFont?: string;
512
- minorFont?: string;
512
+ readonly name: string;
513
+ readonly majorFont?: string;
514
+ readonly minorFont?: string;
513
515
  }
514
516
 
515
517
  /**
@@ -541,9 +543,9 @@ export type ThemeFontSlot =
541
543
  | "minorBidi";
542
544
 
543
545
  export interface ThemeDefinition {
544
- name?: string;
545
- colorScheme?: ThemeColorScheme;
546
- fontScheme?: ThemeFontScheme;
546
+ readonly name?: string;
547
+ readonly colorScheme?: ThemeColorScheme;
548
+ readonly fontScheme?: ThemeFontScheme;
547
549
  }
548
550
 
549
551
  /**
@@ -555,49 +557,49 @@ export interface ThemeDefinition {
555
557
  * future serializer can re-emit byte-stable diffs.
556
558
  */
557
559
  export interface CompatSetting {
558
- name: string;
559
- uri: string;
560
- value: string;
560
+ readonly name: string;
561
+ readonly uri: string;
562
+ readonly value: string;
561
563
  }
562
564
 
563
565
  export interface DocumentSettings {
564
- evenAndOddHeaders?: boolean;
565
- zoomLevel?: "pageWidth" | "onePage" | number;
566
+ readonly evenAndOddHeaders?: boolean;
567
+ readonly zoomLevel?: "pageWidth" | "onePage" | number;
566
568
  /**
567
569
  * Document-wide default tab stop interval from `<w:defaultTabStop w:val>`.
568
570
  * Value is stored in twips and feeds layout measurement whenever a paragraph
569
571
  * does not declare an explicit next tab stop.
570
572
  */
571
- defaultTabStop?: number;
573
+ readonly defaultTabStop?: number;
572
574
  /**
573
575
  * Settings-level default footnote configuration from `<w:footnotePr>`.
574
576
  * Section-level `SectionProperties.footnotePr` overrides this when present.
575
577
  */
576
- footnotePr?: FootnoteProperties;
578
+ readonly footnotePr?: FootnoteProperties;
577
579
  /**
578
580
  * Settings-level default endnote configuration from `<w:endnotePr>`.
579
581
  * Section-level `SectionProperties.endnotePr` overrides this when present.
580
582
  */
581
- endnotePr?: EndnoteProperties;
583
+ readonly endnotePr?: EndnoteProperties;
582
584
  /**
583
585
  * Ordered list of <w:compatSetting> entries inside <w:compat>. Insertion
584
586
  * order is preserved for serializer diff stability.
585
587
  */
586
- compatSettings?: CompatSetting[];
588
+ readonly compatSettings?: CompatSetting[];
587
589
  /**
588
590
  * Boolean flag children of <w:compat> that are NOT <w:compatSetting>
589
591
  * (e.g. <w:spaceForUL/>, <w:doNotExpandShiftReturn/>). Keyed by local
590
592
  * element name. The value reflects ST_OnOff semantics: missing `w:val` is
591
593
  * true; explicit `w:val="0"`/`"false"` is false.
592
594
  */
593
- compatFlags?: Record<string, boolean>;
595
+ readonly compatFlags?: Record<string, boolean>;
594
596
  /**
595
597
  * Settings-level (NOT inside <w:compat>) compat-adjacent boolean flags
596
598
  * such as <w:doNotEmbedSmartTags/>. Kept in a separate field from
597
599
  * compatFlags because the OOXML location differs and the future
598
600
  * serializer must re-emit them at root, not inside <w:compat>.
599
601
  */
600
- rootCompatFlags?: Record<string, boolean>;
602
+ readonly rootCompatFlags?: Record<string, boolean>;
601
603
  /**
602
604
  * <w:themeFontLang> attribute bag, captured verbatim so the future
603
605
  * serializer can re-emit unknown attributes Word may add.
@@ -608,7 +610,7 @@ export interface DocumentSettings {
608
610
  * - { "w:val": "en-US", … }: attributes preserved with their qualified
609
611
  * names so the serializer round-trips namespace prefixes intact.
610
612
  */
611
- themeFontLang?: Record<string, string>;
613
+ readonly themeFontLang?: Record<string, string>;
612
614
  /**
613
615
  * Local names of every direct child of <w:settings> that this parser does
614
616
  * not model individually. Insertion order preserved; duplicates retained.
@@ -618,35 +620,35 @@ export interface DocumentSettings {
618
620
  * use this list as a validator assertion that every preserved child still
619
621
  * appears in the re-emitted output.
620
622
  */
621
- unmodelledSettingsChildren?: string[];
623
+ readonly unmodelledSettingsChildren?: string[];
622
624
  /**
623
625
  * Parsed from `<w:clrSchemeMapping>` in settings.xml. Maps document-level
624
626
  * color style slot names to physical clrScheme slots. Absent when the
625
627
  * element was not present in the document (identity mapping applies).
626
628
  */
627
- clrSchemeMapping?: ClrSchemeMapping;
629
+ readonly clrSchemeMapping?: ClrSchemeMapping;
628
630
  }
629
631
 
630
632
  export interface SubPartsCatalog {
631
- headers: HeaderDocument[];
632
- footers: FooterDocument[];
633
- footnoteCollection?: FootnoteCollection;
634
- theme?: ThemeDefinition;
635
- finalSectionProperties?: SectionProperties;
636
- resolvedTheme?: ResolvedTheme;
637
- settings?: DocumentSettings;
633
+ readonly headers: HeaderDocument[];
634
+ readonly footers: FooterDocument[];
635
+ readonly footnoteCollection?: FootnoteCollection;
636
+ readonly theme?: ThemeDefinition;
637
+ readonly finalSectionProperties?: SectionProperties;
638
+ readonly resolvedTheme?: ResolvedTheme;
639
+ readonly settings?: DocumentSettings;
638
640
  /**
639
641
  * Fully materialized theme for runtime resolution — combines theme1.xml
640
642
  * color/font scheme with the clrSchemeMapping from settings.xml.
641
643
  * Available after CO1 load wiring lands in docx-session.ts.
642
644
  */
643
- canonicalTheme?: CanonicalTheme;
645
+ readonly canonicalTheme?: CanonicalTheme;
644
646
  }
645
647
 
646
648
  export interface ResolvedTheme {
647
- colors: Record<string, string>;
648
- majorFont?: string;
649
- minorFont?: string;
649
+ readonly colors: Record<string, string>;
650
+ readonly majorFont?: string;
651
+ readonly minorFont?: string;
650
652
  }
651
653
 
652
654
  /**
@@ -688,22 +690,22 @@ export type ClrSchemeMapping = Partial<Record<ClrSchemeMappingSlot, ThemeColorSl
688
690
  * documents with no theme part.
689
691
  */
690
692
  export interface CanonicalTheme {
691
- clrScheme: ThemeColorScheme;
692
- fontScheme?: ThemeFontScheme;
693
+ readonly clrScheme: ThemeColorScheme;
694
+ readonly fontScheme?: ThemeFontScheme;
693
695
  /** Resolved clrSchemeMapping: styleSlot → clrScheme slot name. Empty = identity. */
694
- clrMap: ClrSchemeMapping;
696
+ readonly clrMap: ClrSchemeMapping;
695
697
  /** Stable content hash of clrScheme.colors (sorted key:value pairs). */
696
- themeHash: string;
698
+ readonly themeHash: string;
697
699
  /** Stable content hash of clrMap (sorted key:value pairs). */
698
- clrMapHash: string;
700
+ readonly clrMapHash: string;
699
701
  }
700
702
 
701
703
  // ---- Inline footnote reference node ----
702
704
 
703
705
  export interface FootnoteRefNode {
704
- type: "footnote_ref";
705
- noteId: string;
706
- noteKind: "footnote" | "endnote";
706
+ readonly type: "footnote_ref";
707
+ readonly noteId: string;
708
+ readonly noteKind: "footnote" | "endnote";
707
709
  }
708
710
 
709
711
  export type DocumentNode =
@@ -741,8 +743,8 @@ export type DocumentNode =
741
743
  | OleEmbedNode;
742
744
 
743
745
  export interface DocumentRootNode {
744
- type: "doc";
745
- children: BlockNode[];
746
+ readonly type: "doc";
747
+ readonly children: BlockNode[];
746
748
  }
747
749
 
748
750
  export type BlockNode =
@@ -755,74 +757,74 @@ export type BlockNode =
755
757
  | OpaqueBlockNode;
756
758
 
757
759
  export interface ParagraphSpacing {
758
- before?: number;
759
- after?: number;
760
- line?: number;
761
- lineRule?: "auto" | "exact" | "atLeast";
760
+ readonly before?: number;
761
+ readonly after?: number;
762
+ readonly line?: number;
763
+ readonly lineRule?: "auto" | "exact" | "atLeast";
762
764
  }
763
765
 
764
766
  export interface ParagraphIndentation {
765
- left?: number;
766
- right?: number;
767
- firstLine?: number;
768
- hanging?: number;
767
+ readonly left?: number;
768
+ readonly right?: number;
769
+ readonly firstLine?: number;
770
+ readonly hanging?: number;
769
771
  }
770
772
 
771
773
  export interface TabStop {
772
- position: number;
773
- align: "left" | "center" | "right" | "decimal" | "num" | "bar" | "clear";
774
- leader?: "none" | "dot" | "hyphen" | "underscore" | "heavy" | "middleDot";
774
+ readonly position: number;
775
+ readonly align: "left" | "center" | "right" | "decimal" | "num" | "bar" | "clear";
776
+ readonly leader?: "none" | "dot" | "hyphen" | "underscore" | "heavy" | "middleDot";
775
777
  }
776
778
 
777
779
  export interface ParagraphBorders {
778
- top?: BorderSpec;
779
- left?: BorderSpec;
780
- bottom?: BorderSpec;
781
- right?: BorderSpec;
782
- bar?: BorderSpec;
783
- between?: BorderSpec;
780
+ readonly top?: BorderSpec;
781
+ readonly left?: BorderSpec;
782
+ readonly bottom?: BorderSpec;
783
+ readonly right?: BorderSpec;
784
+ readonly bar?: BorderSpec;
785
+ readonly between?: BorderSpec;
784
786
  }
785
787
 
786
788
  export interface ParagraphShading {
787
- fill?: string;
788
- color?: string;
789
- val?: string;
789
+ readonly fill?: string;
790
+ readonly color?: string;
791
+ readonly val?: string;
790
792
  /**
791
793
  * Theme shading references (§17.3.5). When `themeFill` is set and `fill`
792
794
  * is absent or `"auto"`, render-time shading resolves through the theme
793
795
  * color resolver. The raw theme attrs remain on the canonical model so
794
796
  * export can round-trip the original `<w:shd>` byte-for-byte.
795
797
  */
796
- themeFill?: string;
797
- themeFillTint?: string;
798
- themeFillShade?: string;
799
- themeColor?: string;
800
- themeColorTint?: string;
801
- themeColorShade?: string;
798
+ readonly themeFill?: string;
799
+ readonly themeFillTint?: string;
800
+ readonly themeFillShade?: string;
801
+ readonly themeColor?: string;
802
+ readonly themeColorTint?: string;
803
+ readonly themeColorShade?: string;
802
804
  }
803
805
 
804
806
  /** Body of an OOXML `<w:rPr>` (run properties). All fields optional; absence = "not specified at this level". */
805
807
  export interface CanonicalRunFormatting {
806
- bold?: boolean;
807
- italic?: boolean;
808
- underline?: "single" | "double" | "thick" | "dotted" | "dash" | "wave" | "none";
809
- strikethrough?: boolean;
810
- doubleStrikethrough?: boolean;
811
- vanish?: boolean;
812
- allCaps?: boolean;
813
- smallCaps?: boolean;
814
- verticalAlign?: "baseline" | "superscript" | "subscript";
808
+ readonly bold?: boolean;
809
+ readonly italic?: boolean;
810
+ readonly underline?: "single" | "double" | "thick" | "dotted" | "dash" | "wave" | "none";
811
+ readonly strikethrough?: boolean;
812
+ readonly doubleStrikethrough?: boolean;
813
+ readonly vanish?: boolean;
814
+ readonly allCaps?: boolean;
815
+ readonly smallCaps?: boolean;
816
+ readonly verticalAlign?: "baseline" | "superscript" | "subscript";
815
817
  /**
816
818
  * Convenience alias for the primary font family — the first non-empty of
817
819
  * `fontFamilyAscii` → `fontFamilyHAnsi` → `fontFamilyEastAsia` → `fontFamilyCs`.
818
820
  * Script-aware consumers should read the specific `fontFamily{Ascii,HAnsi,EastAsia,Cs}`
819
821
  * fields directly.
820
822
  */
821
- fontFamily?: string;
822
- fontFamilyAscii?: string;
823
- fontFamilyHAnsi?: string;
824
- fontFamilyEastAsia?: string;
825
- fontFamilyCs?: string;
823
+ readonly fontFamily?: string;
824
+ readonly fontFamilyAscii?: string;
825
+ readonly fontFamilyHAnsi?: string;
826
+ readonly fontFamilyEastAsia?: string;
827
+ readonly fontFamilyCs?: string;
826
828
  /**
827
829
  * ECMA-376 §17.3.2.26 theme-slot bindings on `<w:rFonts>`
828
830
  * (`w:asciiTheme`, `w:hAnsiTheme`, `w:eastAsiaTheme`, `w:cstheme`).
@@ -837,18 +839,18 @@ export interface CanonicalRunFormatting {
837
839
  * concrete name wins. Keep the theme slot on the canonical so the
838
840
  * export path can re-emit it byte-identically.
839
841
  */
840
- asciiTheme?: ThemeFontSlot;
841
- hAnsiTheme?: ThemeFontSlot;
842
- eastAsiaTheme?: ThemeFontSlot;
843
- csTheme?: ThemeFontSlot;
844
- fontSizeHalfPoints?: number;
845
- fontSizeCsHalfPoints?: number;
842
+ readonly asciiTheme?: ThemeFontSlot;
843
+ readonly hAnsiTheme?: ThemeFontSlot;
844
+ readonly eastAsiaTheme?: ThemeFontSlot;
845
+ readonly csTheme?: ThemeFontSlot;
846
+ readonly fontSizeHalfPoints?: number;
847
+ readonly fontSizeCsHalfPoints?: number;
846
848
  /**
847
849
  * Color value from `<w:color w:val>`. Either an OOXML hex (e.g., `"2E74B5"`)
848
850
  * or the sentinel `"auto"` (which serializers must round-trip verbatim).
849
851
  */
850
- colorHex?: string;
851
- colorThemeSlot?: string;
852
+ readonly colorHex?: string;
853
+ readonly colorThemeSlot?: string;
852
854
  /**
853
855
  * `w:themeTint` hex byte (0x00–0xFF) read from `<w:color>`. ECMA-376
854
856
  * §17.18.85. Applied at render/cascade time against `colorThemeSlot`:
@@ -857,17 +859,17 @@ export interface CanonicalRunFormatting {
857
859
  * for byte-stable round-trip; resolution math lives in the runtime
858
860
  * theme-color resolver.
859
861
  */
860
- colorThemeTint?: string;
862
+ readonly colorThemeTint?: string;
861
863
  /**
862
864
  * `w:themeShade` hex byte (0x00–0xFF) from `<w:color>`. ECMA-376
863
865
  * §17.18.83. Applied at render/cascade time: luminance mod =
864
866
  * shade/255 * L (darkens toward black; 0xFF means no modulation).
865
867
  */
866
- colorThemeShade?: string;
867
- highlight?: string;
868
- characterSpacingTwips?: number;
869
- characterStyleId?: string;
870
- languageCode?: string;
868
+ readonly colorThemeShade?: string;
869
+ readonly highlight?: string;
870
+ readonly characterSpacingTwips?: number;
871
+ readonly characterStyleId?: string;
872
+ readonly languageCode?: string;
871
873
  /**
872
874
  * Unmodelled direct children of `<w:rPr>` captured verbatim for round-trip.
873
875
  * See `src/io/ooxml/property-grab-bag.ts` and
@@ -876,27 +878,27 @@ export interface CanonicalRunFormatting {
876
878
  * `<w:em>`, `<w:kern>` through parse→serialize round-trip even though the
877
879
  * runtime does not model them.
878
880
  */
879
- unknownPropertyChildren?: UnknownPropertyChild[];
881
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
880
882
  }
881
883
 
882
884
  /** Body of an OOXML `<w:pPr>` (paragraph properties). All fields optional; absence = "not specified at this level". */
883
885
  export interface CanonicalParagraphFormatting {
884
- spacing?: ParagraphSpacing;
885
- indentation?: ParagraphIndentation;
886
- alignment?: "left" | "center" | "right" | "both" | "distribute" | "start" | "end";
887
- borders?: ParagraphBorders;
888
- shading?: ParagraphShading;
889
- tabStops?: TabStop[];
890
- contextualSpacing?: boolean;
891
- keepNext?: boolean;
892
- keepLines?: boolean;
893
- widowControl?: boolean;
894
- pageBreakBefore?: boolean;
895
- outlineLevel?: number;
896
- bidi?: boolean;
897
- suppressLineNumbers?: boolean;
898
- suppressAutoHyphens?: boolean;
899
- paragraphMarkRunProperties?: CanonicalRunFormatting;
886
+ readonly spacing?: ParagraphSpacing;
887
+ readonly indentation?: ParagraphIndentation;
888
+ readonly alignment?: "left" | "center" | "right" | "both" | "distribute" | "start" | "end";
889
+ readonly borders?: ParagraphBorders;
890
+ readonly shading?: ParagraphShading;
891
+ readonly tabStops?: TabStop[];
892
+ readonly contextualSpacing?: boolean;
893
+ readonly keepNext?: boolean;
894
+ readonly keepLines?: boolean;
895
+ readonly widowControl?: boolean;
896
+ readonly pageBreakBefore?: boolean;
897
+ readonly outlineLevel?: number;
898
+ readonly bidi?: boolean;
899
+ readonly suppressLineNumbers?: boolean;
900
+ readonly suppressAutoHyphens?: boolean;
901
+ readonly paragraphMarkRunProperties?: CanonicalRunFormatting;
900
902
  /**
901
903
  * `<w:framePr>` — paragraph text-frame properties (ECMA-376 §17.3.1.11).
902
904
  * Absent on ordinary paragraphs. When present, the paragraph renders
@@ -904,13 +906,13 @@ export interface CanonicalParagraphFormatting {
904
906
  * templates for side-by-side "instructional column + body column"
905
907
  * paragraphs and for drop-caps.
906
908
  *
907
- * Added 2026-04-23 per `docs/KNOWN-ISSUES-VISUAL.md §2.3 "w:framePr
908
- * 2-column inset text frames"`. Before this type existed, L01's parser
909
+ * Added 2026-04-23 after the visual issue-register finding on `w:framePr`
910
+ * 2-column inset text frames. Before this type existed, L01's parser
909
911
  * fell through to `OpaqueBlockNode` whenever `<w:pPr>` contained
910
912
  * `<w:framePr>`, which hid the paragraph from L04 pagination and L11
911
913
  * render. Canonical representation unblocks downstream adoption.
912
914
  */
913
- frameProperties?: FrameProperties;
915
+ readonly frameProperties?: FrameProperties;
914
916
  /**
915
917
  * Unmodelled direct children of `<w:pPr>` captured verbatim for round-trip.
916
918
  * See `src/io/ooxml/property-grab-bag.ts` for the mechanism and Lane 3 O2
@@ -922,7 +924,7 @@ export interface CanonicalParagraphFormatting {
922
924
  * or `<w:kinsoku>` survive a parse→serialize round-trip even though the
923
925
  * runtime doesn't understand them.
924
926
  */
925
- unknownPropertyChildren?: UnknownPropertyChild[];
927
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
926
928
  }
927
929
 
928
930
  /**
@@ -937,57 +939,57 @@ export interface CanonicalParagraphFormatting {
937
939
  */
938
940
  export interface FrameProperties {
939
941
  /** `w:w` — frame width in twips. Absence = auto-width from content. */
940
- widthTwips?: number;
942
+ readonly widthTwips?: number;
941
943
  /** `w:h` — frame height in twips. Interpretation depends on `hRule`. */
942
- heightTwips?: number;
944
+ readonly heightTwips?: number;
943
945
  /**
944
946
  * `w:hRule` — height rule.
945
947
  * - `"auto"` (default): height follows content.
946
948
  * - `"atLeast"`: content grows the frame; `heightTwips` is the floor.
947
949
  * - `"exact"`: content is clipped to `heightTwips`.
948
950
  */
949
- hRule?: "auto" | "atLeast" | "exact";
951
+ readonly hRule?: "auto" | "atLeast" | "exact";
950
952
  /** `w:x` — horizontal absolute position in twips from `hAnchor`. */
951
- xTwips?: number;
953
+ readonly xTwips?: number;
952
954
  /** `w:y` — vertical absolute position in twips from `vAnchor`. */
953
- yTwips?: number;
955
+ readonly yTwips?: number;
954
956
  /**
955
957
  * `w:xAlign` — horizontal alignment keyword; overrides `xTwips` if
956
958
  * both are set (Word behavior). `"inside"` / `"outside"` are
957
959
  * mirror-aware for even/odd pages.
958
960
  */
959
- xAlign?: "left" | "center" | "right" | "inside" | "outside";
961
+ readonly xAlign?: "left" | "center" | "right" | "inside" | "outside";
960
962
  /**
961
963
  * `w:yAlign` — vertical alignment keyword; overrides `yTwips` if
962
964
  * both are set. `"inline"` places the frame in the text flow.
963
965
  */
964
- yAlign?: "top" | "center" | "bottom" | "inside" | "outside" | "inline";
966
+ readonly yAlign?: "top" | "center" | "bottom" | "inside" | "outside" | "inline";
965
967
  /** `w:hAnchor` — what `x` / `xAlign` is measured from. */
966
- hAnchor?: "text" | "margin" | "page";
968
+ readonly hAnchor?: "text" | "margin" | "page";
967
969
  /** `w:vAnchor` — what `y` / `yAlign` is measured from. */
968
- vAnchor?: "text" | "margin" | "page";
970
+ readonly vAnchor?: "text" | "margin" | "page";
969
971
  /**
970
972
  * `w:wrap` — how surrounding text flows around the frame.
971
973
  * `"around"`: text wraps on both sides; `"notBeside"`: text stops
972
974
  * above/below the frame; `"none"`: no text on the frame line;
973
975
  * `"auto"` / `"tight"` / `"through"`: extended wrap modes.
974
976
  */
975
- wrap?: "around" | "auto" | "none" | "notBeside" | "tight" | "through";
977
+ readonly wrap?: "around" | "auto" | "none" | "notBeside" | "tight" | "through";
976
978
  /** `w:hSpace` — horizontal clear-space around the frame, twips. */
977
- hSpaceTwips?: number;
979
+ readonly hSpaceTwips?: number;
978
980
  /** `w:vSpace` — vertical clear-space around the frame, twips. */
979
- vSpaceTwips?: number;
981
+ readonly vSpaceTwips?: number;
980
982
  /**
981
983
  * `w:dropCap` — drop-cap semantics when the frame holds an initial
982
984
  * letter. `"none"` (default): not a drop-cap frame.
983
985
  * `"drop"`: character spans `lines` lines, text wraps around it.
984
986
  * `"margin"`: character hangs in the margin.
985
987
  */
986
- dropCap?: "none" | "drop" | "margin";
988
+ readonly dropCap?: "none" | "drop" | "margin";
987
989
  /** `w:lines` — number of lines the drop-cap spans. Only meaningful when `dropCap !== "none"`. */
988
- lines?: number;
990
+ readonly lines?: number;
989
991
  /** `w:anchorLock` — prevents the frame's anchor paragraph from moving between pages during reflow. */
990
- anchorLock?: boolean;
992
+ readonly anchorLock?: boolean;
991
993
  /**
992
994
  * Verbatim source XML of the `<w:framePr>` element. Populated by
993
995
  * the parser for lossless round-trip of extension attributes
@@ -996,7 +998,7 @@ export interface FrameProperties {
996
998
  * modeled fields and merge in any extension attributes from
997
999
  * `rawXml` that aren't covered by the modeled set.
998
1000
  */
999
- rawXml?: string;
1001
+ readonly rawXml?: string;
1000
1002
  }
1001
1003
 
1002
1004
  /**
@@ -1008,65 +1010,65 @@ export interface FrameProperties {
1008
1010
  */
1009
1011
  export interface UnknownPropertyChild {
1010
1012
  /** Qualified element name as it appeared in source, e.g. "w15:collapsed". */
1011
- elementName: string;
1013
+ readonly elementName: string;
1012
1014
  /** Verbatim source XML for the child element, including closing/self-closing form. */
1013
- rawXml: string;
1015
+ readonly rawXml: string;
1014
1016
  }
1015
1017
 
1016
1018
  /** Body of an OOXML `<w:docDefaults>` — baseline formatting applied before style chain. */
1017
1019
  export interface DocumentDefaults {
1018
- paragraph?: CanonicalParagraphFormatting;
1019
- run?: CanonicalRunFormatting;
1020
+ readonly paragraph?: CanonicalParagraphFormatting;
1021
+ readonly run?: CanonicalRunFormatting;
1020
1022
  }
1021
1023
 
1022
1024
  /** Materialized `fontTable.xml` — one entry per `<w:font>` element. */
1023
1025
  export interface CanonicalFontTable {
1024
- fonts: Record<string, CanonicalFontEntry>;
1026
+ readonly fonts: Record<string, CanonicalFontEntry>;
1025
1027
  }
1026
1028
 
1027
1029
  /** Metadata for a single `<w:font>` element from fontTable.xml. */
1028
1030
  export interface CanonicalFontEntry {
1029
1031
  /** `w:name` attribute — the key used in rFonts references. */
1030
- name: string;
1032
+ readonly name: string;
1031
1033
  /**
1032
1034
  * `w:family` value. Qualitative category per ECMA-376 §17.8.3.10.
1033
1035
  * "roman" = proportional serif, "swiss" = proportional sans-serif,
1034
1036
  * "modern" = fixed-pitch, "script" = handwriting, "decorative" = display.
1035
1037
  */
1036
- family?: "roman" | "swiss" | "modern" | "script" | "decorative";
1038
+ readonly family?: "roman" | "swiss" | "modern" | "script" | "decorative";
1037
1039
  /** `w:pitch` value — "fixed" maps to monospace metrics. */
1038
- pitch?: "fixed" | "variable" | "default";
1040
+ readonly pitch?: "fixed" | "variable" | "default";
1039
1041
  /**
1040
1042
  * Windows charset code from `w:charset w:val`.
1041
1043
  * 2 = Symbol font (charset override required for correct glyph mapping).
1042
1044
  */
1043
- charset?: number;
1045
+ readonly charset?: number;
1044
1046
  /** `w:altName` — substitution name when the primary font is unavailable. */
1045
- altName?: string;
1047
+ readonly altName?: string;
1046
1048
  }
1047
1049
 
1048
1050
  export interface ParagraphNode {
1049
- type: "paragraph";
1050
- styleId?: string;
1051
+ readonly type: "paragraph";
1052
+ readonly styleId?: string;
1051
1053
  numbering?: {
1052
- numberingInstanceId: string;
1053
- level: number;
1054
+ readonly numberingInstanceId: string;
1055
+ readonly level: number;
1054
1056
  };
1055
- alignment?: "left" | "center" | "right" | "both" | "distribute";
1056
- spacing?: ParagraphSpacing;
1057
- contextualSpacing?: boolean;
1058
- indentation?: ParagraphIndentation;
1059
- tabStops?: TabStop[];
1060
- keepNext?: boolean;
1061
- keepLines?: boolean;
1062
- outlineLevel?: number;
1063
- pageBreakBefore?: boolean;
1064
- widowControl?: boolean;
1065
- borders?: ParagraphBorders;
1066
- shading?: ParagraphShading;
1067
- bidi?: boolean;
1068
- suppressLineNumbers?: boolean;
1069
- cnfStyle?: string;
1057
+ readonly alignment?: "left" | "center" | "right" | "both" | "distribute";
1058
+ readonly spacing?: ParagraphSpacing;
1059
+ readonly contextualSpacing?: boolean;
1060
+ readonly indentation?: ParagraphIndentation;
1061
+ readonly tabStops?: TabStop[];
1062
+ readonly keepNext?: boolean;
1063
+ readonly keepLines?: boolean;
1064
+ readonly outlineLevel?: number;
1065
+ readonly pageBreakBefore?: boolean;
1066
+ readonly widowControl?: boolean;
1067
+ readonly borders?: ParagraphBorders;
1068
+ readonly shading?: ParagraphShading;
1069
+ readonly bidi?: boolean;
1070
+ readonly suppressLineNumbers?: boolean;
1071
+ readonly cnfStyle?: string;
1070
1072
  /**
1071
1073
  * `<w:framePr>` — inline paragraph text-frame properties set
1072
1074
  * directly on the paragraph (not through the style cascade).
@@ -1084,7 +1086,7 @@ export interface ParagraphNode {
1084
1086
  * to `OpaqueBlockNode` because the parser had no canonical target
1085
1087
  * at the node level.
1086
1088
  */
1087
- frameProperties?: FrameProperties;
1089
+ readonly frameProperties?: FrameProperties;
1088
1090
  /**
1089
1091
  * Preserved w14 extension identifiers for this paragraph.
1090
1092
  * Round-trip (§2 A.7) requires these to survive import → export so the
@@ -1093,65 +1095,65 @@ export interface ParagraphNode {
1093
1095
  * 8-hex uppercase strings per ECMA-376 Part 1 Appendix A.
1094
1096
  */
1095
1097
  wordExtensionIds?: {
1096
- paraId?: string;
1097
- textId?: string;
1098
+ readonly paraId?: string;
1099
+ readonly textId?: string;
1098
1100
  };
1099
- children: InlineNode[];
1101
+ readonly children: InlineNode[];
1100
1102
  }
1101
1103
 
1102
1104
  export interface BorderSpec {
1103
- value?: string;
1104
- size?: number;
1105
- space?: number;
1106
- color?: string;
1105
+ readonly value?: string;
1106
+ readonly size?: number;
1107
+ readonly space?: number;
1108
+ readonly color?: string;
1107
1109
  }
1108
1110
 
1109
1111
  export interface TableBorders {
1110
- top?: BorderSpec;
1111
- left?: BorderSpec;
1112
- bottom?: BorderSpec;
1113
- right?: BorderSpec;
1114
- insideH?: BorderSpec;
1115
- insideV?: BorderSpec;
1112
+ readonly top?: BorderSpec;
1113
+ readonly left?: BorderSpec;
1114
+ readonly bottom?: BorderSpec;
1115
+ readonly right?: BorderSpec;
1116
+ readonly insideH?: BorderSpec;
1117
+ readonly insideV?: BorderSpec;
1116
1118
  }
1117
1119
 
1118
1120
  export interface TableCellBorders {
1119
- top?: BorderSpec;
1120
- left?: BorderSpec;
1121
- bottom?: BorderSpec;
1122
- right?: BorderSpec;
1123
- insideH?: BorderSpec;
1124
- insideV?: BorderSpec;
1121
+ readonly top?: BorderSpec;
1122
+ readonly left?: BorderSpec;
1123
+ readonly bottom?: BorderSpec;
1124
+ readonly right?: BorderSpec;
1125
+ readonly insideH?: BorderSpec;
1126
+ readonly insideV?: BorderSpec;
1125
1127
  }
1126
1128
 
1127
1129
  export interface TableWidth {
1128
- value: number;
1129
- type: "dxa" | "auto" | "pct" | "nil";
1130
+ readonly value: number;
1131
+ readonly type: "dxa" | "auto" | "pct" | "nil";
1130
1132
  }
1131
1133
 
1132
1134
  export interface TableIndent {
1133
- value: number;
1134
- type: "dxa" | "auto" | "pct" | "nil";
1135
+ readonly value: number;
1136
+ readonly type: "dxa" | "auto" | "pct" | "nil";
1135
1137
  }
1136
1138
 
1137
1139
  export interface TableFloatingProperties {
1138
- horizontalAnchor?: "margin" | "page" | "text";
1139
- verticalAnchor?: "margin" | "page" | "text";
1140
- horizontalAlign?: "left" | "center" | "right" | "inside" | "outside";
1141
- horizontalOffset?: number;
1142
- verticalAlign?: "top" | "center" | "bottom" | "inside" | "outside";
1143
- verticalOffset?: number;
1144
- leftFromText?: number;
1145
- rightFromText?: number;
1146
- topFromText?: number;
1147
- bottomFromText?: number;
1148
- overlap?: boolean;
1140
+ readonly horizontalAnchor?: "margin" | "page" | "text";
1141
+ readonly verticalAnchor?: "margin" | "page" | "text";
1142
+ readonly horizontalAlign?: "left" | "center" | "right" | "inside" | "outside";
1143
+ readonly horizontalOffset?: number;
1144
+ readonly verticalAlign?: "top" | "center" | "bottom" | "inside" | "outside";
1145
+ readonly verticalOffset?: number;
1146
+ readonly leftFromText?: number;
1147
+ readonly rightFromText?: number;
1148
+ readonly topFromText?: number;
1149
+ readonly bottomFromText?: number;
1150
+ readonly overlap?: boolean;
1149
1151
  }
1150
1152
 
1151
1153
  export interface CellShading {
1152
- fill?: string;
1153
- color?: string;
1154
- val?: string;
1154
+ readonly fill?: string;
1155
+ readonly color?: string;
1156
+ readonly val?: string;
1155
1157
  /**
1156
1158
  * SOW gap G3 — theme-shading references (§17.3.5). When `themeFill` is set
1157
1159
  * and `fill` is absent or "auto", the runtime resolves the shading via
@@ -1159,29 +1161,29 @@ export interface CellShading {
1159
1161
  * themeFillShade)`. Fields are kept verbatim so export can round-trip the
1160
1162
  * original `w:shd` element without re-encoding the resolved hex.
1161
1163
  */
1162
- themeFill?: string;
1163
- themeFillTint?: string;
1164
- themeFillShade?: string;
1165
- themeColor?: string;
1166
- themeColorTint?: string;
1167
- themeColorShade?: string;
1164
+ readonly themeFill?: string;
1165
+ readonly themeFillTint?: string;
1166
+ readonly themeFillShade?: string;
1167
+ readonly themeColor?: string;
1168
+ readonly themeColorTint?: string;
1169
+ readonly themeColorShade?: string;
1168
1170
  }
1169
1171
 
1170
1172
  export interface TableCellMargins {
1171
- top?: number;
1172
- left?: number;
1173
- bottom?: number;
1174
- right?: number;
1173
+ readonly top?: number;
1174
+ readonly left?: number;
1175
+ readonly bottom?: number;
1176
+ readonly right?: number;
1175
1177
  }
1176
1178
 
1177
1179
  export interface TableLook {
1178
- val?: string;
1179
- firstRow?: boolean;
1180
- lastRow?: boolean;
1181
- firstColumn?: boolean;
1182
- lastColumn?: boolean;
1183
- noHBand?: boolean;
1184
- noVBand?: boolean;
1180
+ readonly val?: string;
1181
+ readonly firstRow?: boolean;
1182
+ readonly lastRow?: boolean;
1183
+ readonly firstColumn?: boolean;
1184
+ readonly lastColumn?: boolean;
1185
+ readonly noHBand?: boolean;
1186
+ readonly noVBand?: boolean;
1185
1187
  }
1186
1188
 
1187
1189
  export type TableStyleConditionalRegion =
@@ -1196,140 +1198,140 @@ export type TableStyleConditionalRegion =
1196
1198
 
1197
1199
  export interface TableStyleFormatting {
1198
1200
  table?: {
1199
- width?: TableWidth;
1200
- alignment?: "left" | "center" | "right";
1201
- borders?: TableBorders;
1202
- cellMargins?: TableCellMargins;
1203
- tblLook?: TableLook;
1201
+ readonly width?: TableWidth;
1202
+ readonly alignment?: "left" | "center" | "right";
1203
+ readonly borders?: TableBorders;
1204
+ readonly cellMargins?: TableCellMargins;
1205
+ readonly tblLook?: TableLook;
1204
1206
  };
1205
1207
  row?: {
1206
- height?: number;
1207
- heightRule?: "auto" | "atLeast" | "exact";
1208
- isHeader?: boolean;
1208
+ readonly height?: number;
1209
+ readonly heightRule?: "auto" | "atLeast" | "exact";
1210
+ readonly isHeader?: boolean;
1209
1211
  };
1210
1212
  cell?: {
1211
- width?: TableWidth;
1212
- borders?: TableCellBorders;
1213
- shading?: CellShading;
1214
- verticalAlign?: "top" | "center" | "bottom";
1213
+ readonly width?: TableWidth;
1214
+ readonly borders?: TableCellBorders;
1215
+ readonly shading?: CellShading;
1216
+ readonly verticalAlign?: "top" | "center" | "bottom";
1215
1217
  };
1216
1218
  /**
1217
1219
  * `<w:pPr>` directly inside the table style body — applies to every
1218
1220
  * cell paragraph by default. Conditional-region pPr (inside
1219
1221
  * `<w:tblStylePr>`) is not captured here; see Lane 3a follow-up.
1220
1222
  */
1221
- paragraphProperties?: CanonicalParagraphFormatting;
1223
+ readonly paragraphProperties?: CanonicalParagraphFormatting;
1222
1224
  /**
1223
1225
  * `<w:rPr>` directly inside the table style body — applies to every
1224
1226
  * run in every cell paragraph by default.
1225
1227
  */
1226
- runProperties?: CanonicalRunFormatting;
1228
+ readonly runProperties?: CanonicalRunFormatting;
1227
1229
  }
1228
1230
 
1229
1231
  export interface TableNode {
1230
- type: "table";
1231
- styleId?: string;
1232
+ readonly type: "table";
1233
+ readonly styleId?: string;
1232
1234
  /** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
1233
- propertiesXml?: string;
1234
- unknownPropertyChildren?: UnknownPropertyChild[];
1235
- gridColumns: number[];
1236
- rows: TableRowNode[];
1237
- width?: TableWidth;
1238
- alignment?: "left" | "center" | "right";
1239
- borders?: TableBorders;
1240
- cellMargins?: TableCellMargins;
1241
- tblLook?: TableLook;
1242
- indent?: TableIndent;
1243
- layoutMode?: "fixed" | "autofit";
1244
- cellSpacing?: TableWidth;
1245
- caption?: string;
1246
- description?: string;
1247
- bidiVisual?: boolean;
1248
- floating?: TableFloatingProperties;
1235
+ readonly propertiesXml?: string;
1236
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
1237
+ readonly gridColumns: number[];
1238
+ readonly rows: TableRowNode[];
1239
+ readonly width?: TableWidth;
1240
+ readonly alignment?: "left" | "center" | "right";
1241
+ readonly borders?: TableBorders;
1242
+ readonly cellMargins?: TableCellMargins;
1243
+ readonly tblLook?: TableLook;
1244
+ readonly indent?: TableIndent;
1245
+ readonly layoutMode?: "fixed" | "autofit";
1246
+ readonly cellSpacing?: TableWidth;
1247
+ readonly caption?: string;
1248
+ readonly description?: string;
1249
+ readonly bidiVisual?: boolean;
1250
+ readonly floating?: TableFloatingProperties;
1249
1251
  }
1250
1252
 
1251
1253
  export interface TableRowNode {
1252
- type: "table_row";
1254
+ readonly type: "table_row";
1253
1255
  /** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
1254
- propertiesXml?: string;
1255
- unknownPropertyChildren?: UnknownPropertyChild[];
1256
- cells: TableCellNode[];
1257
- gridBefore?: number;
1258
- widthBefore?: TableWidth;
1259
- gridAfter?: number;
1260
- widthAfter?: TableWidth;
1261
- height?: number;
1262
- heightRule?: "auto" | "atLeast" | "exact";
1263
- isHeader?: boolean;
1264
- cantSplit?: boolean;
1265
- horizontalAlignment?: "left" | "center" | "right";
1266
- cnfStyle?: string;
1256
+ readonly propertiesXml?: string;
1257
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
1258
+ readonly cells: TableCellNode[];
1259
+ readonly gridBefore?: number;
1260
+ readonly widthBefore?: TableWidth;
1261
+ readonly gridAfter?: number;
1262
+ readonly widthAfter?: TableWidth;
1263
+ readonly height?: number;
1264
+ readonly heightRule?: "auto" | "atLeast" | "exact";
1265
+ readonly isHeader?: boolean;
1266
+ readonly cantSplit?: boolean;
1267
+ readonly horizontalAlignment?: "left" | "center" | "right";
1268
+ readonly cnfStyle?: string;
1267
1269
  }
1268
1270
 
1269
1271
  export interface TableCellNode {
1270
- type: "table_cell";
1272
+ readonly type: "table_cell";
1271
1273
  /** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
1272
- propertiesXml?: string;
1273
- unknownPropertyChildren?: UnknownPropertyChild[];
1274
- gridSpan?: number;
1275
- verticalMerge?: "restart" | "continue";
1276
- children: BlockNode[];
1277
- width?: TableWidth;
1278
- borders?: TableCellBorders;
1279
- shading?: CellShading;
1280
- verticalAlign?: "top" | "center" | "bottom";
1281
- textDirection?: "lrTb" | "tbRl" | "btLr";
1282
- noWrap?: boolean;
1283
- fitText?: boolean;
1284
- margins?: TableCellMargins;
1285
- cnfStyle?: string;
1274
+ readonly propertiesXml?: string;
1275
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
1276
+ readonly gridSpan?: number;
1277
+ readonly verticalMerge?: "restart" | "continue";
1278
+ readonly children: BlockNode[];
1279
+ readonly width?: TableWidth;
1280
+ readonly borders?: TableCellBorders;
1281
+ readonly shading?: CellShading;
1282
+ readonly verticalAlign?: "top" | "center" | "bottom";
1283
+ readonly textDirection?: "lrTb" | "tbRl" | "btLr";
1284
+ readonly noWrap?: boolean;
1285
+ readonly fitText?: boolean;
1286
+ readonly margins?: TableCellMargins;
1287
+ readonly cnfStyle?: string;
1286
1288
  }
1287
1289
 
1288
1290
  export interface SdtCheckboxState {
1289
- checked: boolean;
1290
- checkedChar?: string;
1291
- uncheckedChar?: string;
1291
+ readonly checked: boolean;
1292
+ readonly checkedChar?: string;
1293
+ readonly uncheckedChar?: string;
1292
1294
  }
1293
1295
 
1294
1296
  export interface SdtDatePickerState {
1295
- fullDate?: string;
1296
- dateFormat?: string;
1297
- lid?: string;
1297
+ readonly fullDate?: string;
1298
+ readonly dateFormat?: string;
1299
+ readonly lid?: string;
1298
1300
  }
1299
1301
 
1300
1302
  export interface SdtDropdownListItem {
1301
- displayText?: string;
1302
- value: string;
1303
+ readonly displayText?: string;
1304
+ readonly value: string;
1303
1305
  }
1304
1306
 
1305
1307
  export interface SdtNode {
1306
- type: "sdt";
1308
+ readonly type: "sdt";
1307
1309
  properties: {
1308
- sdtType?: string;
1309
- alias?: string;
1310
- tag?: string;
1311
- lock?: string;
1312
- propertiesXml?: string;
1313
- checkbox?: SdtCheckboxState;
1314
- datePicker?: SdtDatePickerState;
1315
- dropdownList?: SdtDropdownListItem[];
1316
- comboBox?: SdtDropdownListItem[];
1317
- showingPlcHdr?: boolean;
1310
+ readonly sdtType?: string;
1311
+ readonly alias?: string;
1312
+ readonly tag?: string;
1313
+ readonly lock?: string;
1314
+ readonly propertiesXml?: string;
1315
+ readonly checkbox?: SdtCheckboxState;
1316
+ readonly datePicker?: SdtDatePickerState;
1317
+ readonly dropdownList?: SdtDropdownListItem[];
1318
+ readonly comboBox?: SdtDropdownListItem[];
1319
+ readonly showingPlcHdr?: boolean;
1318
1320
  };
1319
- children: BlockNode[];
1321
+ readonly children: BlockNode[];
1320
1322
  }
1321
1323
 
1322
1324
  export interface CustomXmlNode {
1323
- type: "custom_xml";
1324
- uri?: string;
1325
- element?: string;
1326
- rawXml?: string;
1327
- children: BlockNode[];
1325
+ readonly type: "custom_xml";
1326
+ readonly uri?: string;
1327
+ readonly element?: string;
1328
+ readonly rawXml?: string;
1329
+ readonly children: BlockNode[];
1328
1330
  }
1329
1331
 
1330
1332
  export interface AltChunkNode {
1331
- type: "alt_chunk";
1332
- relationshipId: string;
1333
+ readonly type: "alt_chunk";
1334
+ readonly relationshipId: string;
1333
1335
  }
1334
1336
 
1335
1337
  /**
@@ -1377,53 +1379,53 @@ export type FieldRefreshStatus =
1377
1379
  | "preserve-only";
1378
1380
 
1379
1381
  export interface FieldNode {
1380
- type: "field";
1381
- fieldType: "simple" | "complex";
1382
- instruction: string;
1383
- children: InlineNode[];
1382
+ readonly type: "field";
1383
+ readonly fieldType: "simple" | "complex";
1384
+ readonly instruction: string;
1385
+ readonly children: InlineNode[];
1384
1386
  /** Classified field family. Undefined for legacy snapshots. */
1385
- fieldFamily?: FieldFamily;
1387
+ readonly fieldFamily?: FieldFamily;
1386
1388
  /** Target bookmark name for REF/PAGEREF/NOTEREF fields. */
1387
- fieldTarget?: string;
1389
+ readonly fieldTarget?: string;
1388
1390
  /** Runtime refresh status. Undefined for legacy or preserve-only fields. */
1389
- refreshStatus?: FieldRefreshStatus;
1391
+ readonly refreshStatus?: FieldRefreshStatus;
1390
1392
  /** Parsed field switches. Present only when the instruction contains recognized switches. */
1391
1393
  switches?: {
1392
- hyperlink?: boolean;
1393
- relativePosition?: boolean;
1394
- numericRef?: boolean;
1395
- recursive?: boolean;
1396
- suppressNonDelimiter?: boolean;
1397
- includeNumbering?: boolean;
1398
- includeLevel?: boolean;
1394
+ readonly hyperlink?: boolean;
1395
+ readonly relativePosition?: boolean;
1396
+ readonly numericRef?: boolean;
1397
+ readonly recursive?: boolean;
1398
+ readonly suppressNonDelimiter?: boolean;
1399
+ readonly includeNumbering?: boolean;
1400
+ readonly includeLevel?: boolean;
1399
1401
  };
1400
1402
  /** Legacy form-field metadata from w:ffData (textInput, checkBox, ddList). Round-trip only; no UI for MVP. */
1401
- legacyFormField?: LegacyFormFieldNode;
1403
+ readonly legacyFormField?: LegacyFormFieldNode;
1402
1404
  }
1403
1405
 
1404
1406
  export type LegacyFormFieldKind = "textInput" | "checkBox" | "ddList";
1405
1407
 
1406
1408
  export interface LegacyFormFieldNode {
1407
- kind: LegacyFormFieldKind;
1408
- name?: string;
1409
- enabled?: boolean;
1410
- calcOnExit?: boolean;
1409
+ readonly kind: LegacyFormFieldKind;
1410
+ readonly name?: string;
1411
+ readonly enabled?: boolean;
1412
+ readonly calcOnExit?: boolean;
1411
1413
  textInput?: {
1412
- default?: string;
1413
- maxLength?: number;
1414
- format?: string;
1414
+ readonly default?: string;
1415
+ readonly maxLength?: number;
1416
+ readonly format?: string;
1415
1417
  };
1416
1418
  checkBox?: {
1417
- size?: number;
1418
- default?: boolean;
1419
- checked?: boolean;
1419
+ readonly size?: number;
1420
+ readonly default?: boolean;
1421
+ readonly checked?: boolean;
1420
1422
  };
1421
1423
  ddList?: {
1422
- default?: number;
1423
- listEntry?: string[];
1424
+ readonly default?: number;
1425
+ readonly listEntry?: string[];
1424
1426
  };
1425
1427
  /** Verbatim <w:ffData>…</w:ffData> XML for lossless round-trip. */
1426
- rawXml: string;
1428
+ readonly rawXml: string;
1427
1429
  /**
1428
1430
  * Phase V.a: set to `true` when typed fields have been mutated via
1429
1431
  * `setLegacyFormFieldValue` since parse. On serialize, the `rawXml` is
@@ -1431,7 +1433,7 @@ export interface LegacyFormFieldNode {
1431
1433
  * Un-mutated nodes emit the preserved `rawXml` verbatim for lossless
1432
1434
  * round-trip.
1433
1435
  */
1434
- mutated?: boolean;
1436
+ readonly mutated?: boolean;
1435
1437
  }
1436
1438
 
1437
1439
  // ─── Field registry ─────────────────────────────────────────────────────────
@@ -1446,34 +1448,36 @@ export interface LegacyFormFieldNode {
1446
1448
  */
1447
1449
  export interface FieldRegistry {
1448
1450
  /** Supported field instances that participate in refresh. */
1449
- supported: FieldRegistryEntry[];
1451
+ readonly supported: FieldRegistryEntry[];
1450
1452
  /** Preserve-only field instances cataloged for round-trip safety. */
1451
- preserveOnly: FieldRegistryEntry[];
1453
+ readonly preserveOnly: FieldRegistryEntry[];
1452
1454
  /** First-class TOC regions, including cached field-result rows and generated heading rows. */
1453
- tocRegions?: TocRegion[];
1455
+ readonly tocRegions?: TocRegion[];
1454
1456
  /** Generated TOC structure extracted from heading-driven TOC fields. */
1455
- tocStructure?: TocStructure;
1457
+ readonly tocStructure?: TocStructure;
1456
1458
  }
1457
1459
 
1458
1460
  export interface FieldRegistryEntry {
1459
1461
  /** Stable document-order index of this field (0-based). */
1460
- fieldIndex: number;
1462
+ readonly fieldIndex: number;
1461
1463
  /** Classified field family. */
1462
- fieldFamily: FieldFamily;
1464
+ readonly fieldFamily: FieldFamily;
1463
1465
  /** Whether the field is in the supported refresh slice. */
1464
- supported: boolean;
1466
+ readonly supported: boolean;
1465
1467
  /** Field instruction text. */
1466
- instruction: string;
1468
+ readonly instruction: string;
1467
1469
  /** Target bookmark name for REF/PAGEREF/NOTEREF fields. */
1468
- fieldTarget?: string;
1470
+ readonly fieldTarget?: string;
1469
1471
  /** Current display text extracted from field content. */
1470
- displayText: string;
1472
+ readonly displayText: string;
1471
1473
  /** Paragraph index in document order where this field appears. */
1472
- paragraphIndex: number;
1474
+ readonly paragraphIndex: number;
1475
+ /** Stable story key for the story that owns this field instance. */
1476
+ readonly storyKey?: string;
1473
1477
  /** Runtime refresh status. */
1474
- refreshStatus: FieldRefreshStatus;
1478
+ readonly refreshStatus: FieldRefreshStatus;
1475
1479
  /** Parsed field switches carried from FieldNode.switches. Present only for REF/PAGEREF/NOTEREF/TOC entries with recognized switches. */
1476
- switches?: FieldNode["switches"];
1480
+ readonly switches?: FieldNode["switches"];
1477
1481
  }
1478
1482
 
1479
1483
  /**
@@ -1482,138 +1486,138 @@ export interface FieldRegistryEntry {
1482
1486
  */
1483
1487
  export interface TocStructure {
1484
1488
  /** The raw TOC field instruction (e.g. "TOC \\o \"1-3\" \\h"). */
1485
- instruction: string;
1489
+ readonly instruction: string;
1486
1490
  /** Heading level range the TOC covers. */
1487
- levelRange: { from: number; to: number };
1491
+ readonly levelRange: { from: number; to: number };
1488
1492
  /** Ordered TOC entries derived from heading paragraphs. */
1489
- entries: TocEntry[];
1493
+ readonly entries: TocEntry[];
1490
1494
  /** Whether the TOC content is current with the heading structure. */
1491
- status: "current" | "stale";
1495
+ readonly status: "current" | "stale";
1492
1496
  }
1493
1497
 
1494
1498
  export interface TocInstructionModel {
1495
1499
  /** The raw TOC field instruction (e.g. "TOC \\o \"1-3\" \\h"). */
1496
- raw: string;
1500
+ readonly raw: string;
1497
1501
  /** Heading level range selected by \\o, or defaulted to 1-9. */
1498
- outlineRange: { from: number; to: number };
1502
+ readonly outlineRange: { from: number; to: number };
1499
1503
  /** \\h — result entries should hyperlink to their anchors. */
1500
- hyperlink?: boolean;
1504
+ readonly hyperlink?: boolean;
1501
1505
  /** \\z — hide tab leader and page numbers in web layout. */
1502
- hidePageNumbersInWeb?: boolean;
1506
+ readonly hidePageNumbersInWeb?: boolean;
1503
1507
  /** \\u — include paragraphs with outline levels. */
1504
- useOutlineLevels?: boolean;
1508
+ readonly useOutlineLevels?: boolean;
1505
1509
  /** \\t "Style,Level,..." — style-driven TOC mapping. */
1506
- styleMap?: TocStyleMapEntry[];
1510
+ readonly styleMap?: TocStyleMapEntry[];
1507
1511
  /** \\b bookmark — bound source content to a bookmark. */
1508
- bookmarkName?: string;
1512
+ readonly bookmarkName?: string;
1509
1513
  /** \\c identifier — table-of-figures / sequence identifier. */
1510
- sequenceIdentifier?: string;
1514
+ readonly sequenceIdentifier?: string;
1511
1515
  /** \\f identifier — TC-entry identifier. */
1512
- tcIdentifier?: string;
1516
+ readonly tcIdentifier?: string;
1513
1517
  /** \\p separator — custom entry/page separator. */
1514
- entryPageSeparator?: string;
1518
+ readonly entryPageSeparator?: string;
1515
1519
  /** \\d separator — custom sequence separator. */
1516
- sequenceSeparator?: string;
1520
+ readonly sequenceSeparator?: string;
1517
1521
  /** \\n optional range — omit page numbers. */
1518
- omitPageNumbers?: true | { from: number; to: number };
1522
+ readonly omitPageNumbers?: true | { from: number; to: number };
1519
1523
  /** Switches parsed but not yet semantically implemented. */
1520
- unsupportedSwitches?: string[];
1524
+ readonly unsupportedSwitches?: string[];
1521
1525
  }
1522
1526
 
1523
1527
  export interface TocStyleMapEntry {
1524
- styleName: string;
1525
- level: number;
1528
+ readonly styleName: string;
1529
+ readonly level: number;
1526
1530
  }
1527
1531
 
1528
1532
  export interface TocCachedEntry {
1529
1533
  /** Text extracted from the cached field-result row before the page-number tab. */
1530
- text: string;
1534
+ readonly text: string;
1531
1535
  /** TOC level inferred from TOC1/TOC2/etc. paragraph style. */
1532
- level: number;
1536
+ readonly level: number;
1533
1537
  /** Paragraph index of the cached result row in document order. */
1534
- paragraphIndex: number;
1538
+ readonly paragraphIndex: number;
1535
1539
  /** Style ID of the cached result paragraph, if available. */
1536
- styleId?: string;
1540
+ readonly styleId?: string;
1537
1541
  /** Bookmark/hyperlink anchor used by the cached result row, if present. */
1538
- bookmarkName?: string;
1542
+ readonly bookmarkName?: string;
1539
1543
  /** Page label extracted from the cached field result. */
1540
- pageText?: string;
1544
+ readonly pageText?: string;
1541
1545
  /** Full visible row text, including tabs/page labels. */
1542
- displayText: string;
1546
+ readonly displayText: string;
1543
1547
  }
1544
1548
 
1545
1549
  export interface TocRegion {
1546
1550
  /** Stable document-order id for this imported TOC region. */
1547
- tocId: string;
1551
+ readonly tocId: string;
1548
1552
  /** Source TOC field index in FieldRegistry.supported. */
1549
- sourceFieldIndex: number;
1553
+ readonly sourceFieldIndex: number;
1550
1554
  /** Parsed instruction metadata and switches. */
1551
- instruction: TocInstructionModel;
1555
+ readonly instruction: TocInstructionModel;
1552
1556
  /** Inclusive paragraph range containing the cached/generated result rows. */
1553
- resultRange: { fromParagraphIndex: number; toParagraphIndex: number };
1557
+ readonly resultRange: { fromParagraphIndex: number; toParagraphIndex: number };
1554
1558
  /** Parent container kind where the region was found. */
1555
- parentKind: "body" | "sdt" | "custom_xml" | "table_cell";
1559
+ readonly parentKind: "body" | "sdt" | "custom_xml" | "table_cell";
1556
1560
  /** Structural path to the parent block sequence, for diagnostics. */
1557
- sourcePath: string;
1561
+ readonly sourcePath: string;
1558
1562
  /** Cached rows imported from the DOCX field result. */
1559
- cachedEntries: TocCachedEntry[];
1563
+ readonly cachedEntries: TocCachedEntry[];
1560
1564
  /** Heading-derived rows generated from the canonical document. */
1561
- generatedEntries: TocEntry[];
1565
+ readonly generatedEntries: TocEntry[];
1562
1566
  /** Whether cached entries match the generated heading model. */
1563
- status: "current" | "stale";
1567
+ readonly status: "current" | "stale";
1564
1568
  }
1565
1569
 
1566
1570
  export interface TocEntry {
1567
1571
  /** Heading text. */
1568
- text: string;
1572
+ readonly text: string;
1569
1573
  /** Heading outline level (1-9). */
1570
- level: number;
1574
+ readonly level: number;
1571
1575
  /** Paragraph index of the heading in document order. */
1572
- paragraphIndex: number;
1576
+ readonly paragraphIndex: number;
1573
1577
  /** Style ID of the heading paragraph, if available. */
1574
- styleId?: string;
1578
+ readonly styleId?: string;
1575
1579
  /** Bookmark name anchoring this heading, if present. */
1576
- bookmarkName?: string;
1580
+ readonly bookmarkName?: string;
1577
1581
  }
1578
1582
 
1579
1583
  export interface BookmarkStartNode {
1580
- type: "bookmark_start";
1581
- bookmarkId: string;
1582
- name: string;
1584
+ readonly type: "bookmark_start";
1585
+ readonly bookmarkId: string;
1586
+ readonly name: string;
1583
1587
  }
1584
1588
 
1585
1589
  export interface BookmarkEndNode {
1586
- type: "bookmark_end";
1587
- bookmarkId: string;
1590
+ readonly type: "bookmark_end";
1591
+ readonly bookmarkId: string;
1588
1592
  }
1589
1593
 
1590
1594
  export interface SectionBreakNode {
1591
- type: "section_break";
1592
- sectionPropertiesXml?: string;
1595
+ readonly type: "section_break";
1596
+ readonly sectionPropertiesXml?: string;
1593
1597
  /**
1594
1598
  * @deprecated Legacy field from older snapshots. New exports should use
1595
1599
  * sectionPropertiesXml and only contain raw <w:sectPr> content.
1596
1600
  */
1597
- propertiesXml?: string;
1598
- sectionProperties?: SectionProperties;
1601
+ readonly propertiesXml?: string;
1602
+ readonly sectionProperties?: SectionProperties;
1599
1603
  }
1600
1604
 
1601
1605
  export interface SectionProperties {
1602
- pageSize?: PageSize;
1603
- pageMargins?: PageMargins;
1604
- columns?: ColumnProperties;
1605
- pageNumbering?: PageNumbering;
1606
- lineNumbering?: SectionLineNumbering;
1607
- pageBorders?: SectionPageBorders;
1608
- documentGrid?: SectionDocumentGrid;
1609
- headerReferences?: HeaderFooterReference[];
1610
- footerReferences?: HeaderFooterReference[];
1611
- sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
1612
- titlePage?: boolean;
1606
+ readonly pageSize?: PageSize;
1607
+ readonly pageMargins?: PageMargins;
1608
+ readonly columns?: ColumnProperties;
1609
+ readonly pageNumbering?: PageNumbering;
1610
+ readonly lineNumbering?: SectionLineNumbering;
1611
+ readonly pageBorders?: SectionPageBorders;
1612
+ readonly documentGrid?: SectionDocumentGrid;
1613
+ readonly headerReferences?: HeaderFooterReference[];
1614
+ readonly footerReferences?: HeaderFooterReference[];
1615
+ readonly sectionType?: "continuous" | "nextPage" | "evenPage" | "oddPage" | "nextColumn";
1616
+ readonly titlePage?: boolean;
1613
1617
  /** Per-section footnote configuration (ECMA-376 §17.11.12). Overrides file-level defaults. */
1614
- footnotePr?: FootnoteProperties;
1618
+ readonly footnotePr?: FootnoteProperties;
1615
1619
  /** Per-section endnote configuration (ECMA-376 §17.11.4). Overrides file-level defaults. */
1616
- endnotePr?: EndnoteProperties;
1620
+ readonly endnotePr?: EndnoteProperties;
1617
1621
  /**
1618
1622
  * Unmodelled direct children of `<w:sectPr>` captured verbatim for
1619
1623
  * round-trip. Mirrors `CanonicalParagraphFormatting.unknownPropertyChildren`
@@ -1622,7 +1626,7 @@ export interface SectionProperties {
1622
1626
  * `<w15:footnoteColumns>` and Word-internal section knobs through a
1623
1627
  * parse→serialize round-trip when the runtime mutates section properties.
1624
1628
  */
1625
- unknownPropertyChildren?: UnknownPropertyChild[];
1629
+ readonly unknownPropertyChildren?: UnknownPropertyChild[];
1626
1630
  }
1627
1631
 
1628
1632
  /**
@@ -1641,13 +1645,13 @@ export interface SectionProperties {
1641
1645
  * counterpart; `EndnoteProperties` aliases this shape.
1642
1646
  */
1643
1647
  export interface FootnoteProperties {
1644
- pos?: "pageBottom" | "beneathText" | "sectEnd" | "docEnd";
1648
+ readonly pos?: "pageBottom" | "beneathText" | "sectEnd" | "docEnd";
1645
1649
  numFmt?: "decimal" | "upperRoman" | "lowerRoman" | "upperLetter" | "lowerLetter"
1646
1650
  | "ordinal" | "cardinalText" | "ordinalText" | "hex" | "chicago" | "bullet"
1647
1651
  | "ideographDigital" | "japaneseCounting" | "arabicAbjad" | "arabicAlpha"
1648
1652
  | "none";
1649
- numStart?: number;
1650
- numRestart?: "continuous" | "eachSect" | "eachPage";
1653
+ readonly numStart?: number;
1654
+ readonly numRestart?: "continuous" | "eachSect" | "eachPage";
1651
1655
  }
1652
1656
 
1653
1657
  /**
@@ -1658,62 +1662,62 @@ export interface FootnoteProperties {
1658
1662
  export type EndnoteProperties = FootnoteProperties;
1659
1663
 
1660
1664
  export interface PageSize {
1661
- width: number;
1662
- height: number;
1663
- orientation?: "portrait" | "landscape";
1665
+ readonly width: number;
1666
+ readonly height: number;
1667
+ readonly orientation?: "portrait" | "landscape";
1664
1668
  }
1665
1669
 
1666
1670
  export interface PageMargins {
1667
- top: number;
1668
- right: number;
1669
- bottom: number;
1670
- left: number;
1671
- header?: number;
1672
- footer?: number;
1673
- gutter?: number;
1671
+ readonly top: number;
1672
+ readonly right: number;
1673
+ readonly bottom: number;
1674
+ readonly left: number;
1675
+ readonly header?: number;
1676
+ readonly footer?: number;
1677
+ readonly gutter?: number;
1674
1678
  }
1675
1679
 
1676
1680
  export interface ColumnProperties {
1677
- count?: number;
1678
- space?: number;
1679
- equalWidth?: boolean;
1680
- columns?: Array<{ width: number; space?: number }>;
1681
- separator?: boolean;
1681
+ readonly count?: number;
1682
+ readonly space?: number;
1683
+ readonly equalWidth?: boolean;
1684
+ readonly columns?: Array<{ width: number; space?: number }>;
1685
+ readonly separator?: boolean;
1682
1686
  }
1683
1687
 
1684
1688
  export interface PageNumbering {
1685
- format?: string;
1686
- start?: number;
1687
- chapStyle?: string;
1688
- chapSep?: string;
1689
+ readonly format?: string;
1690
+ readonly start?: number;
1691
+ readonly chapStyle?: string;
1692
+ readonly chapSep?: string;
1689
1693
  }
1690
1694
 
1691
1695
  export interface SectionLineNumbering {
1692
- countBy?: number;
1693
- start?: number;
1694
- distance?: number;
1695
- restart?: "newPage" | "newSection" | "continuous";
1696
+ readonly countBy?: number;
1697
+ readonly start?: number;
1698
+ readonly distance?: number;
1699
+ readonly restart?: "newPage" | "newSection" | "continuous";
1696
1700
  }
1697
1701
 
1698
1702
  export interface SectionPageBorders {
1699
- top?: BorderSpec;
1700
- left?: BorderSpec;
1701
- bottom?: BorderSpec;
1702
- right?: BorderSpec;
1703
- offsetFrom?: "page" | "text";
1704
- display?: "allPages" | "firstPage" | "notFirstPage";
1705
- zOrder?: "front" | "back";
1703
+ readonly top?: BorderSpec;
1704
+ readonly left?: BorderSpec;
1705
+ readonly bottom?: BorderSpec;
1706
+ readonly right?: BorderSpec;
1707
+ readonly offsetFrom?: "page" | "text";
1708
+ readonly display?: "allPages" | "firstPage" | "notFirstPage";
1709
+ readonly zOrder?: "front" | "back";
1706
1710
  }
1707
1711
 
1708
1712
  export interface SectionDocumentGrid {
1709
- type?: "default" | "lines" | "linesAndChars" | "snapToChars";
1710
- linePitch?: number;
1711
- charSpace?: number;
1713
+ readonly type?: "default" | "lines" | "linesAndChars" | "snapToChars";
1714
+ readonly linePitch?: number;
1715
+ readonly charSpace?: number;
1712
1716
  }
1713
1717
 
1714
1718
  export interface HeaderFooterReference {
1715
- variant: HeaderFooterVariant;
1716
- relationshipId: string;
1719
+ readonly variant: HeaderFooterVariant;
1720
+ readonly relationshipId: string;
1717
1721
  }
1718
1722
 
1719
1723
  export type InlineNode =
@@ -1741,9 +1745,9 @@ export type InlineNode =
1741
1745
  | OleEmbedNode;
1742
1746
 
1743
1747
  export interface TextNode {
1744
- type: "text";
1745
- text: string;
1746
- marks?: TextMark[];
1748
+ readonly type: "text";
1749
+ readonly text: string;
1750
+ readonly marks?: TextMark[];
1747
1751
  }
1748
1752
 
1749
1753
  export type TextMark =
@@ -1770,11 +1774,11 @@ export type TextMark =
1770
1774
  | { type: "allCaps" };
1771
1775
 
1772
1776
  export interface HardBreakNode {
1773
- type: "hard_break";
1777
+ readonly type: "hard_break";
1774
1778
  }
1775
1779
 
1776
1780
  export interface ColumnBreakNode {
1777
- type: "column_break";
1781
+ readonly type: "column_break";
1778
1782
  }
1779
1783
 
1780
1784
  /**
@@ -1792,66 +1796,85 @@ export interface ColumnBreakNode {
1792
1796
  * section properties; `PageBreakNode` is inline inside a run.
1793
1797
  */
1794
1798
  export interface PageBreakNode {
1795
- type: "page_break";
1799
+ readonly type: "page_break";
1796
1800
  }
1797
1801
 
1798
1802
  export interface TabNode {
1799
- type: "tab";
1803
+ readonly type: "tab";
1800
1804
  }
1801
1805
 
1802
1806
  export interface SymbolNode {
1803
- type: "symbol";
1804
- char: string;
1805
- font?: string;
1806
- marks?: TextMark[];
1807
+ readonly type: "symbol";
1808
+ readonly char: string;
1809
+ readonly font?: string;
1810
+ readonly marks?: TextMark[];
1807
1811
  }
1808
1812
 
1809
1813
  export interface HyperlinkNode {
1810
- type: "hyperlink";
1811
- href: string;
1812
- children: Array<TextNode | HardBreakNode | ColumnBreakNode | PageBreakNode | TabNode | SymbolNode>;
1814
+ readonly type: "hyperlink";
1815
+ readonly href: string;
1816
+ readonly children: Array<TextNode | HardBreakNode | ColumnBreakNode | PageBreakNode | TabNode | SymbolNode>;
1813
1817
  }
1814
1818
 
1815
1819
  export interface ImageNode {
1816
- type: "image";
1817
- mediaId: string;
1818
- altText?: string;
1819
- placementXml?: string;
1820
- display?: "inline" | "floating";
1821
- floating?: FloatingImageProperties;
1820
+ readonly type: "image";
1821
+ readonly mediaId: string;
1822
+ readonly altText?: string;
1823
+ readonly placementXml?: string;
1824
+ readonly display?: "inline" | "floating";
1825
+ readonly floating?: FloatingImageProperties;
1822
1826
  }
1823
1827
 
1824
1828
  export interface FloatingImageProperties {
1825
- horizontalPosition?: FloatingAxisPosition;
1826
- verticalPosition?: FloatingAxisPosition;
1827
- wrap?: "none" | "square" | "tight" | "through" | "topAndBottom";
1828
- behindDoc?: boolean;
1829
- layoutInCell?: boolean;
1830
- allowOverlap?: boolean;
1829
+ readonly horizontalPosition?: FloatingAxisPosition;
1830
+ readonly verticalPosition?: FloatingAxisPosition;
1831
+ readonly wrap?: "none" | "square" | "tight" | "through" | "topAndBottom";
1832
+ readonly behindDoc?: boolean;
1833
+ readonly layoutInCell?: boolean;
1834
+ readonly allowOverlap?: boolean;
1831
1835
  }
1832
1836
 
1833
1837
  export interface FloatingAxisPosition {
1834
- relativeFrom?: string;
1835
- align?: string;
1836
- offset?: number;
1838
+ readonly relativeFrom?: string;
1839
+ readonly align?: string;
1840
+ readonly offset?: number;
1837
1841
  }
1838
1842
 
1839
1843
  export interface OpaqueInlineNode {
1840
- type: "opaque_inline";
1841
- fragmentId: string;
1842
- warningId: string;
1844
+ readonly type: "opaque_inline";
1845
+ readonly fragmentId: string;
1846
+ readonly warningId: string;
1843
1847
  }
1844
1848
 
1845
1849
  // ---- Complex rendering inline nodes (read-only previews) ----
1846
1850
 
1851
+ export type PreserveOnlyObjectFallbackHint =
1852
+ | "drawing-inline"
1853
+ | "drawing-floating"
1854
+ | "chart"
1855
+ | "smartart"
1856
+ | "shape"
1857
+ | "wordart"
1858
+ | "vml-shape"
1859
+ | "ole-object"
1860
+ | "opaque-object";
1861
+
1862
+ export interface PreserveOnlyObjectSizing {
1863
+ readonly sourceId?: string;
1864
+ readonly display: "inline" | "floating" | "unknown";
1865
+ readonly extentEmu?: { widthEmu: number; heightEmu: number };
1866
+ readonly fallbackHint: PreserveOnlyObjectFallbackHint;
1867
+ readonly relationshipIds?: string[];
1868
+ }
1869
+
1847
1870
  /**
1848
1871
  * Read-only preview of a chart (c:chart). The original drawing XML is stored in
1849
1872
  * rawXml for lossless round-trip export. If a fallback image was present in
1850
1873
  * mc:AlternateContent it is referenced by previewMediaId.
1851
1874
  */
1852
1875
  export interface ChartPreviewNode {
1853
- type: "chart_preview";
1854
- previewMediaId?: string;
1876
+ readonly type: "chart_preview";
1877
+ readonly previewMediaId?: string;
1855
1878
  /**
1856
1879
  * Typed chart data model parsed from the `c:chartSpace` part, when
1857
1880
  * available. Populated at import time by the Stage 1 chart parser
@@ -1869,7 +1892,8 @@ export interface ChartPreviewNode {
1869
1892
  * `parsedData` in place.
1870
1893
  */
1871
1894
  readonly parsedData?: ChartModel;
1872
- rawXml: string;
1895
+ readonly rawXml: string;
1896
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
1873
1897
  }
1874
1898
 
1875
1899
  /**
@@ -1877,9 +1901,10 @@ export interface ChartPreviewNode {
1877
1901
  * stored in rawXml for lossless round-trip export.
1878
1902
  */
1879
1903
  export interface SmartArtPreviewNode {
1880
- type: "smartart_preview";
1881
- previewMediaId?: string;
1882
- rawXml: string;
1904
+ readonly type: "smartart_preview";
1905
+ readonly previewMediaId?: string;
1906
+ readonly rawXml: string;
1907
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
1883
1908
  }
1884
1909
 
1885
1910
  /**
@@ -1894,14 +1919,14 @@ export interface SmartArtPreviewNode {
1894
1919
  * runtime contract was confirmed canonical).
1895
1920
  */
1896
1921
  export interface ShapeNode {
1897
- type: "shape";
1898
- text?: string;
1899
- geometry?: string;
1900
- isTextBox?: boolean;
1922
+ readonly type: "shape";
1923
+ readonly text?: string;
1924
+ readonly geometry?: string;
1925
+ readonly isTextBox?: boolean;
1901
1926
  /** Text box body layout from `wps:bodyPr` / `a:bodyPr`. */
1902
- textBoxBody?: TextBoxBodyProperties;
1927
+ readonly textBoxBody?: TextBoxBodyProperties;
1903
1928
  /** Raw `<w:txbxContent>` XML, preserved for serialization + round-trip. */
1904
- txbxContentXml?: string;
1929
+ readonly txbxContentXml?: string;
1905
1930
  /**
1906
1931
  * Parsed canonical block-level structure from `<w:txbxContent>`,
1907
1932
  * populated when the parse path supplies a `blockParser` callback
@@ -1909,8 +1934,9 @@ export interface ShapeNode {
1909
1934
  * body via `src/io/ooxml/parse-main-document.ts`). Shape + semantics
1910
1935
  * identical to `ShapeContent.txbxBlocks` on the drawing-frame path.
1911
1936
  */
1912
- txbxBlocks?: ReadonlyArray<BlockNode>;
1913
- rawXml: string;
1937
+ readonly txbxBlocks?: BlockNode[];
1938
+ readonly rawXml: string;
1939
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
1914
1940
  }
1915
1941
 
1916
1942
  /**
@@ -1918,10 +1944,11 @@ export interface ShapeNode {
1918
1944
  * Text is extracted for display. The original drawing XML is preserved in rawXml.
1919
1945
  */
1920
1946
  export interface WordArtNode {
1921
- type: "wordart";
1922
- text: string;
1923
- geometry?: string;
1924
- rawXml: string;
1947
+ readonly type: "wordart";
1948
+ readonly text: string;
1949
+ readonly geometry?: string;
1950
+ readonly rawXml: string;
1951
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
1925
1952
  }
1926
1953
 
1927
1954
  /**
@@ -1930,10 +1957,11 @@ export interface WordArtNode {
1930
1957
  * in rawXml for lossless round-trip export.
1931
1958
  */
1932
1959
  export interface VmlShapeNode {
1933
- type: "vml_shape";
1934
- text?: string;
1935
- shapeType?: string;
1936
- rawXml: string;
1960
+ readonly type: "vml_shape";
1961
+ readonly text?: string;
1962
+ readonly shapeType?: string;
1963
+ readonly rawXml: string;
1964
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
1937
1965
  }
1938
1966
 
1939
1967
  /**
@@ -1956,111 +1984,111 @@ export interface VmlShapeNode {
1956
1984
  * through when `<o:OLEObject>` is absent.
1957
1985
  */
1958
1986
  export interface OleEmbedNode {
1959
- type: "ole_embed";
1960
- id: string;
1961
- progId?: string;
1962
- embedType: "oleObject";
1963
- relationshipId: string;
1987
+ readonly type: "ole_embed";
1988
+ readonly id: string;
1989
+ readonly progId?: string;
1990
+ readonly embedType: "oleObject";
1991
+ readonly relationshipId: string;
1964
1992
  metadata: {
1965
- originalFilename?: string;
1966
- classId?: string;
1967
- shapeId?: string;
1993
+ readonly originalFilename?: string;
1994
+ readonly classId?: string;
1995
+ readonly shapeId?: string;
1968
1996
  };
1969
- rawXml: string;
1997
+ readonly rawXml: string;
1970
1998
  }
1971
1999
 
1972
2000
  export interface AnchorGeometry {
1973
- display: "inline" | "floating";
1974
- extent: { widthEmu: number; heightEmu: number };
1975
- wrapMode: "none" | "square" | "tight" | "through" | "topAndBottom";
2001
+ readonly display: "inline" | "floating";
2002
+ readonly extent: { widthEmu: number; heightEmu: number };
2003
+ readonly wrapMode: "none" | "square" | "tight" | "through" | "topAndBottom";
1976
2004
  /**
1977
2005
  * Polygon coords (in OOXML's 21600ths-of-image units) when wrapMode is
1978
2006
  * "tight" or "through" and `wp:wrapPolygon` is present. First point from
1979
2007
  * `wp:start`; subsequent points from sequential `wp:lineTo`. Consumed by
1980
2008
  * Lane 6 P7 float-wrap for proper polygon text flow.
1981
2009
  */
1982
- wrapPolygon?: Array<{ x: number; y: number }>;
1983
- positionH?: { relativeFrom: string; align?: string; offset?: number };
1984
- positionV?: { relativeFrom: string; align?: string; offset?: number };
1985
- distMargins?: { top?: number; bottom?: number; left?: number; right?: number };
1986
- relativeHeight?: number;
1987
- behindDoc?: boolean;
1988
- layoutInCell?: boolean;
1989
- allowOverlap?: boolean;
1990
- simplePos?: boolean;
2010
+ readonly wrapPolygon?: Array<{ x: number; y: number }>;
2011
+ readonly positionH?: { relativeFrom: string; align?: string; offset?: number };
2012
+ readonly positionV?: { relativeFrom: string; align?: string; offset?: number };
2013
+ readonly distMargins?: { top?: number; bottom?: number; left?: number; right?: number };
2014
+ readonly relativeHeight?: number;
2015
+ readonly behindDoc?: boolean;
2016
+ readonly layoutInCell?: boolean;
2017
+ readonly allowOverlap?: boolean;
2018
+ readonly simplePos?: boolean;
1991
2019
  docPr?: {
1992
- id: string;
1993
- name?: string;
1994
- descr?: string;
2020
+ readonly id: string;
2021
+ readonly name?: string;
2022
+ readonly descr?: string;
1995
2023
  /** `wp:docPr hidden="1"` — Word UI hides the frame from the user. */
1996
- hidden?: boolean;
2024
+ readonly hidden?: boolean;
1997
2025
  };
1998
2026
  /**
1999
2027
  * `wp:cNvGraphicFramePr/a:graphicFrameLocks` — object-manipulation locks
2000
2028
  * consumed by Lane 6 object chrome (disable resize handles / move, etc.).
2001
2029
  */
2002
2030
  frameLocks?: {
2003
- noChangeAspect?: boolean;
2004
- noResize?: boolean;
2005
- noMove?: boolean;
2006
- noRot?: boolean;
2007
- noSelect?: boolean;
2008
- noGrp?: boolean;
2031
+ readonly noChangeAspect?: boolean;
2032
+ readonly noResize?: boolean;
2033
+ readonly noMove?: boolean;
2034
+ readonly noRot?: boolean;
2035
+ readonly noSelect?: boolean;
2036
+ readonly noGrp?: boolean;
2009
2037
  };
2010
2038
  }
2011
2039
 
2012
2040
  export interface PictureContent {
2013
- type: "picture";
2014
- blipRef: string;
2041
+ readonly type: "picture";
2042
+ readonly blipRef: string;
2015
2043
  /**
2016
2044
  * Phase 4.4 G4 — true when the image references an external URL via
2017
2045
  * `a:blip r:link` (bytes not in the .docx package) rather than embedded
2018
2046
  * via `r:embed`. External-linked images can't resolve to mediaId;
2019
2047
  * `state: "missing"` in surface projection.
2020
2048
  */
2021
- isLinked?: boolean;
2049
+ readonly isLinked?: boolean;
2022
2050
  /** Resolved media catalog ID (e.g. "media:word/media/image1.png"), populated by parse-drawing. */
2023
- mediaId?: string;
2051
+ readonly mediaId?: string;
2024
2052
  /** Absolute package path for media catalog lookup. */
2025
- packagePartName?: string;
2053
+ readonly packagePartName?: string;
2026
2054
  /** MIME resolved from the OPC media part, when known. */
2027
- contentType?: string;
2055
+ readonly contentType?: string;
2028
2056
  /** DrawingML a:lum brightness/contrast adjustments on the blip. Values are OOXML fixed percentages. */
2029
- lum?: { bright?: number; contrast?: number };
2030
- srcRect?: { top: number; bottom: number; left: number; right: number };
2031
- stretch?: boolean;
2057
+ readonly lum?: { bright?: number; contrast?: number };
2058
+ readonly srcRect?: { top: number; bottom: number; left: number; right: number };
2059
+ readonly stretch?: boolean;
2032
2060
  /**
2033
2061
  * Phase 4.5 G7 — `pic:blipFill/a:tile` (tiled image repeat). Mutually
2034
2062
  * exclusive with `stretch`. tx/ty = offset (EMUs), sx/sy = scale ×1000,
2035
2063
  * flip = "x"|"y"|"xy", algn = alignment token.
2036
2064
  */
2037
2065
  tile?: {
2038
- tx?: number;
2039
- ty?: number;
2040
- sx?: number;
2041
- sy?: number;
2042
- flip?: "x" | "y" | "xy";
2043
- algn?: string;
2066
+ readonly tx?: number;
2067
+ readonly ty?: number;
2068
+ readonly sx?: number;
2069
+ readonly sy?: number;
2070
+ readonly flip?: "x" | "y" | "xy";
2071
+ readonly algn?: string;
2044
2072
  };
2045
- rotation?: number;
2046
- flipH?: boolean;
2047
- flipV?: boolean;
2048
- presetGeom?: string;
2073
+ readonly rotation?: number;
2074
+ readonly flipH?: boolean;
2075
+ readonly flipV?: boolean;
2076
+ readonly presetGeom?: string;
2049
2077
  /** N11.b — DrawingML a:softEdge feather radius in EMU. */
2050
- softEdgeRadius?: number;
2078
+ readonly softEdgeRadius?: number;
2051
2079
  /** N11.b — DrawingML a:outerShdw attributes. `dir` is in 60000ths of a degree; `blurRad`/`dist` in EMU. */
2052
- outerShadow?: { blurRad: number; dist: number; dir: number; color: string; colorType: "srgbClr" | "schemeClr" };
2080
+ readonly outerShadow?: { blurRad: number; dist: number; dir: number; color: string; colorType: "srgbClr" | "schemeClr" };
2053
2081
  /** N11.b — DrawingML a:glow radius in EMU + color. */
2054
- glow?: { radius: number; color: string; colorType: "srgbClr" | "schemeClr" };
2082
+ readonly glow?: { radius: number; color: string; colorType: "srgbClr" | "schemeClr" };
2055
2083
  /** Original w:drawing XML slice, preserved for lossless round-trip serialization. */
2056
- rawXml?: string;
2084
+ readonly rawXml?: string;
2057
2085
  }
2058
2086
 
2059
2087
  export interface ShapeContent {
2060
- type: "shape";
2088
+ readonly type: "shape";
2061
2089
  /** Plain-text fallback extracted from `w:txbxContent` when present. */
2062
- text?: string;
2063
- geometry?: string;
2090
+ readonly text?: string;
2091
+ readonly geometry?: string;
2064
2092
  /**
2065
2093
  * Shape fill — solid, gradient, pattern, or none. srgbClr values on solid +
2066
2094
  * gradient stops + pattern fg/bg are normalized to uppercase hex. schemeClr
@@ -2073,26 +2101,26 @@ export interface ShapeContent {
2073
2101
  | { kind: "solid"; color: string; colorType: "srgbClr" | "schemeClr" }
2074
2102
  | { kind: "none" }
2075
2103
  | {
2076
- kind: "gradient";
2077
- stops: Array<{ pos: number; color: string; colorType: "srgbClr" | "schemeClr" }>;
2104
+ readonly kind: "gradient";
2105
+ readonly stops: Array<{ pos: number; color: string; colorType: "srgbClr" | "schemeClr" }>;
2078
2106
  direction:
2079
2107
  | { kind: "linear"; angle: number; scaled?: boolean }
2080
2108
  | { kind: "path"; path: "circle" | "rect" | "shape" };
2081
- rotWithShape?: boolean;
2109
+ readonly rotWithShape?: boolean;
2082
2110
  }
2083
2111
  | {
2084
- kind: "pattern";
2085
- preset: string;
2086
- fg?: { color: string; colorType: "srgbClr" | "schemeClr" };
2087
- bg?: { color: string; colorType: "srgbClr" | "schemeClr" };
2112
+ readonly kind: "pattern";
2113
+ readonly preset: string;
2114
+ readonly fg?: { color: string; colorType: "srgbClr" | "schemeClr" };
2115
+ readonly bg?: { color: string; colorType: "srgbClr" | "schemeClr" };
2088
2116
  };
2089
- line?: { color?: string; widthEmu?: number; noLine?: boolean };
2117
+ readonly line?: { color?: string; widthEmu?: number; noLine?: boolean };
2090
2118
  /** True when the shape's geometry + txbxContent presence make it a text box. */
2091
- isTextBox?: boolean;
2119
+ readonly isTextBox?: boolean;
2092
2120
  /** Text box body layout from `wps:bodyPr` / `a:bodyPr`. */
2093
- textBoxBody?: TextBoxBodyProperties;
2121
+ readonly textBoxBody?: TextBoxBodyProperties;
2094
2122
  /** Raw w:txbxContent XML, preserved for serialization + lossless round-trip. */
2095
- txbxContentXml?: string;
2123
+ readonly txbxContentXml?: string;
2096
2124
  /**
2097
2125
  * Parsed block-level structure from `w:txbxContent`, populated when a
2098
2126
  * `blockParser` callback is supplied during parse (CO4 F3.3).
@@ -2106,45 +2134,46 @@ export interface ShapeContent {
2106
2134
  * contract — unblocks L03 cascade + L11 render walking `txbxBlocks`
2107
2135
  * without `as unknown as BlockNode[]` casts at the consumer site.
2108
2136
  */
2109
- txbxBlocks?: ReadonlyArray<BlockNode>;
2110
- rawXml: string;
2137
+ readonly txbxBlocks?: BlockNode[];
2138
+ readonly rawXml: string;
2139
+ readonly preserveOnlyObject?: PreserveOnlyObjectSizing;
2111
2140
  }
2112
2141
 
2113
2142
  export interface TextBoxBodyProperties {
2114
2143
  /** OOXML bodyPr anchor: top, center, or bottom. */
2115
- anchor?: "t" | "ctr" | "b";
2116
- insetLeftEmu?: number;
2117
- insetTopEmu?: number;
2118
- insetRightEmu?: number;
2119
- insetBottomEmu?: number;
2144
+ readonly anchor?: "t" | "ctr" | "b";
2145
+ readonly insetLeftEmu?: number;
2146
+ readonly insetTopEmu?: number;
2147
+ readonly insetRightEmu?: number;
2148
+ readonly insetBottomEmu?: number;
2120
2149
  }
2121
2150
 
2122
2151
  export interface DrawingFrameNode {
2123
- type: "drawing_frame";
2124
- anchor: AnchorGeometry;
2152
+ readonly type: "drawing_frame";
2153
+ readonly anchor: AnchorGeometry;
2125
2154
  content:
2126
2155
  | PictureContent
2127
2156
  | ShapeContent
2128
2157
  | ChartPreviewNode
2129
2158
  | SmartArtPreviewNode
2130
- | { type: "opaque"; rawXml: string };
2159
+ | { type: "opaque"; rawXml: string; preserveOnlyObject?: PreserveOnlyObjectSizing };
2131
2160
  }
2132
2161
 
2133
2162
  export interface OpaqueBlockNode {
2134
- type: "opaque_block";
2135
- fragmentId: string;
2136
- warningId: string;
2137
- rawXml?: string;
2163
+ readonly type: "opaque_block";
2164
+ readonly fragmentId: string;
2165
+ readonly warningId: string;
2166
+ readonly rawXml?: string;
2138
2167
  }
2139
2168
 
2140
2169
  export interface DocRange {
2141
- from: number;
2142
- to: number;
2170
+ readonly from: number;
2171
+ readonly to: number;
2143
2172
  }
2144
2173
 
2145
2174
  export interface BoundaryAssoc {
2146
- start: -1 | 1;
2147
- end: -1 | 1;
2175
+ readonly start: -1 | 1;
2176
+ readonly end: -1 | 1;
2148
2177
  }
2149
2178
 
2150
2179
  export type CanonicalAnchor =
@@ -2166,70 +2195,70 @@ export type CanonicalAnchor =
2166
2195
  };
2167
2196
 
2168
2197
  export interface ReviewStore {
2169
- comments: Record<string, CommentThread>;
2170
- revisions: Record<string, RevisionRecord>;
2198
+ readonly comments: Record<string, CommentThread>;
2199
+ readonly revisions: Record<string, RevisionRecord>;
2171
2200
  }
2172
2201
 
2173
2202
  export interface CommentThread {
2174
- commentId: string;
2175
- status: "open" | "resolved" | "detached";
2176
- anchor: CanonicalAnchor;
2177
- createdAt: ISO8601DateTime;
2178
- createdBy?: string;
2179
- authorId?: string;
2180
- body?: string;
2181
- entries?: CommentEntry[];
2182
- resolution?: CommentResolution;
2183
- resolvedAt?: ISO8601DateTime;
2184
- warningIds: string[];
2185
- isResolved?: boolean;
2186
- metadata?: CommentThreadMetadata;
2203
+ readonly commentId: string;
2204
+ readonly status: "open" | "resolved" | "detached";
2205
+ readonly anchor: CanonicalAnchor;
2206
+ readonly createdAt: ISO8601DateTime;
2207
+ readonly createdBy?: string;
2208
+ readonly authorId?: string;
2209
+ readonly body?: string;
2210
+ readonly entries?: CommentEntry[];
2211
+ readonly resolution?: CommentResolution;
2212
+ readonly resolvedAt?: ISO8601DateTime;
2213
+ readonly warningIds: string[];
2214
+ readonly isResolved?: boolean;
2215
+ readonly metadata?: CommentThreadMetadata;
2187
2216
  }
2188
2217
 
2189
2218
  export interface CommentEntry {
2190
- entryId: string;
2191
- authorId: string;
2192
- createdAt: ISO8601DateTime;
2193
- body: string;
2194
- metadata?: CommentEntryMetadata;
2219
+ readonly entryId: string;
2220
+ readonly authorId: string;
2221
+ readonly createdAt: ISO8601DateTime;
2222
+ readonly body: string;
2223
+ readonly metadata?: CommentEntryMetadata;
2195
2224
  }
2196
2225
 
2197
2226
  export interface CommentEntryMetadata {
2198
- ooxmlCommentId?: string;
2199
- paraId?: string;
2200
- parentParaId?: string;
2201
- durableId?: string;
2202
- initials?: string;
2227
+ readonly ooxmlCommentId?: string;
2228
+ readonly paraId?: string;
2229
+ readonly parentParaId?: string;
2230
+ readonly durableId?: string;
2231
+ readonly initials?: string;
2203
2232
  }
2204
2233
 
2205
2234
  export interface CommentResolution {
2206
- resolvedAt: ISO8601DateTime;
2207
- resolvedBy: string;
2235
+ readonly resolvedAt: ISO8601DateTime;
2236
+ readonly resolvedBy: string;
2208
2237
  }
2209
2238
 
2210
2239
  export interface CommentThreadMetadata {
2211
- source?: "runtime" | "import";
2212
- rootOoxmlCommentId?: string;
2213
- rootParaId?: string;
2240
+ readonly source?: "runtime" | "import";
2241
+ readonly rootOoxmlCommentId?: string;
2242
+ readonly rootParaId?: string;
2214
2243
  /**
2215
2244
  * Runtime-authored discussion thread for a tracked change. The comment
2216
2245
  * anchor points at the revision range; this metadata keeps the review
2217
2246
  * relation stable after remaps and lets suggestion cards surface replies.
2218
2247
  */
2219
- linkedRevisionId?: string;
2220
- detachedReason?: "incomplete-markers" | "multi-paragraph" | "opaque-region" | "revision-overlap";
2221
- actionabilityNote?: string;
2248
+ readonly linkedRevisionId?: string;
2249
+ readonly detachedReason?: "incomplete-markers" | "multi-paragraph" | "opaque-region" | "revision-overlap";
2250
+ readonly actionabilityNote?: string;
2222
2251
  }
2223
2252
 
2224
2253
  export interface RevisionPropertyChangeData {
2225
- xmlTag: "pPrChange" | "sectPrChange" | "tblPrChange" | "rPrChange";
2226
- beforeXml: string;
2254
+ readonly xmlTag: "pPrChange" | "sectPrChange" | "tblPrChange" | "rPrChange";
2255
+ readonly beforeXml: string;
2227
2256
  }
2228
2257
 
2229
2258
  export interface RevisionMoveData {
2230
- moveId: string;
2231
- direction: "from" | "to";
2232
- linkedRevisionId?: string;
2259
+ readonly moveId: string;
2260
+ readonly direction: "from" | "to";
2261
+ readonly linkedRevisionId?: string;
2233
2262
  }
2234
2263
 
2235
2264
  export type RevisionStoryTargetRecord =
@@ -2250,21 +2279,21 @@ export type RevisionStoryTargetRecord =
2250
2279
  | { kind: "endnote"; noteId: string };
2251
2280
 
2252
2281
  export interface RevisionRecord {
2253
- changeId: string;
2254
- kind: "insertion" | "deletion" | "formatting" | "move" | "property-change";
2255
- anchor: CanonicalAnchor;
2256
- authorId?: string;
2257
- createdAt: ISO8601DateTime;
2258
- warningIds?: string[];
2259
- metadata?: RevisionMetadataRecord;
2260
- status: "open" | "accepted" | "rejected" | "detached";
2282
+ readonly changeId: string;
2283
+ readonly kind: "insertion" | "deletion" | "formatting" | "move" | "property-change";
2284
+ readonly anchor: CanonicalAnchor;
2285
+ readonly authorId?: string;
2286
+ readonly createdAt: ISO8601DateTime;
2287
+ readonly warningIds?: string[];
2288
+ readonly metadata?: RevisionMetadataRecord;
2289
+ readonly status: "open" | "accepted" | "rejected" | "detached";
2261
2290
  }
2262
2291
 
2263
2292
  export interface RevisionMetadataRecord {
2264
- source?: "runtime" | "import";
2265
- storyTarget?: RevisionStoryTargetRecord;
2266
- preserveOnlyReason?: string;
2267
- suggestionId?: string;
2293
+ readonly source?: "runtime" | "import";
2294
+ readonly storyTarget?: RevisionStoryTargetRecord;
2295
+ readonly preserveOnlyReason?: string;
2296
+ readonly suggestionId?: string;
2268
2297
  semanticKind?:
2269
2298
  | "insertion"
2270
2299
  | "deletion"
@@ -2273,49 +2302,49 @@ export interface RevisionMetadataRecord {
2273
2302
  | "paragraph-property-change"
2274
2303
  | "structural-change"
2275
2304
  | "object-change";
2276
- linkedRevisionIds?: string[];
2277
- predecessorSuggestionId?: string;
2305
+ readonly linkedRevisionIds?: string[];
2306
+ readonly predecessorSuggestionId?: string;
2278
2307
  importedRevisionForm?:
2279
2308
  | "run-insertion"
2280
2309
  | "run-deletion"
2281
2310
  | "paragraph-insertion"
2282
2311
  | "paragraph-deletion";
2283
- originalRevisionType?: string;
2284
- ooxmlRevisionId?: string;
2285
- propertyChangeData?: RevisionPropertyChangeData;
2286
- moveData?: RevisionMoveData;
2312
+ readonly originalRevisionType?: string;
2313
+ readonly ooxmlRevisionId?: string;
2314
+ readonly propertyChangeData?: RevisionPropertyChangeData;
2315
+ readonly moveData?: RevisionMoveData;
2287
2316
  }
2288
2317
 
2289
2318
  export interface PreservationStore {
2290
- opaqueFragments: Record<string, OpaqueFragmentRecord>;
2291
- packageParts: Record<string, PreservedPackagePart>;
2319
+ readonly opaqueFragments: Record<string, OpaqueFragmentRecord>;
2320
+ readonly packageParts: Record<string, PreservedPackagePart>;
2292
2321
  }
2293
2322
 
2294
2323
  export interface OpaqueFragmentRecord {
2295
- fragmentId: string;
2296
- payloadKind: "xml-subtree" | "package-part";
2297
- payloadReference: string;
2298
- featureClass: "preserve-only";
2299
- lastKnownRange: DocRange;
2300
- warningId: string;
2301
- packagePartName?: string;
2302
- relationshipId?: string;
2324
+ readonly fragmentId: string;
2325
+ readonly payloadKind: "xml-subtree" | "package-part";
2326
+ readonly payloadReference: string;
2327
+ readonly featureClass: "preserve-only";
2328
+ readonly lastKnownRange: DocRange;
2329
+ readonly warningId: string;
2330
+ readonly packagePartName?: string;
2331
+ readonly relationshipId?: string;
2303
2332
  }
2304
2333
 
2305
2334
  export interface PreservedPackagePart {
2306
- packagePartName: string;
2307
- contentType: string;
2308
- relationshipIds: string[];
2335
+ readonly packagePartName: string;
2336
+ readonly contentType: string;
2337
+ readonly relationshipIds: string[];
2309
2338
  }
2310
2339
 
2311
2340
  export interface DiagnosticStore {
2312
- warnings: DiagnosticWarningEntry[];
2313
- errors: DiagnosticErrorEntry[];
2341
+ readonly warnings: DiagnosticWarningEntry[];
2342
+ readonly errors: DiagnosticErrorEntry[];
2314
2343
  }
2315
2344
 
2316
2345
  export interface DiagnosticWarningEntry {
2317
- diagnosticId: string;
2318
- warningId: string;
2346
+ readonly diagnosticId: string;
2347
+ readonly warningId: string;
2319
2348
  source:
2320
2349
  | "import"
2321
2350
  | "runtime"
@@ -2323,11 +2352,11 @@ export interface DiagnosticWarningEntry {
2323
2352
  | "preservation"
2324
2353
  | "validation"
2325
2354
  | "export";
2326
- message: string;
2355
+ readonly message: string;
2327
2356
  }
2328
2357
 
2329
2358
  export interface DiagnosticErrorEntry {
2330
- diagnosticId: string;
2359
+ readonly diagnosticId: string;
2331
2360
  code:
2332
2361
  | "load_failed"
2333
2362
  | "import_failed"
@@ -2336,10 +2365,10 @@ export interface DiagnosticErrorEntry {
2336
2365
  | "validation_failed"
2337
2366
  | "datastore_failed"
2338
2367
  | "internal_invariant";
2339
- message: string;
2340
- isFatal: boolean;
2341
- source: "import" | "runtime" | "validation" | "datastore" | "host" | "export";
2342
- details?: unknown;
2368
+ readonly message: string;
2369
+ readonly isFatal: boolean;
2370
+ readonly source: "import" | "runtime" | "validation" | "datastore" | "host" | "export";
2371
+ readonly details?: unknown;
2343
2372
  }
2344
2373
 
2345
2374
  export function createCanonicalDocument(
@@ -4386,6 +4415,10 @@ function validateFieldRegistryEntry(
4386
4415
  });
4387
4416
  }
4388
4417
 
4418
+ if (record.storyKey !== undefined) {
4419
+ expectString(record.storyKey, `${path}.storyKey`, issues);
4420
+ }
4421
+
4389
4422
  if (
4390
4423
  typeof record.refreshStatus !== "string" ||
4391
4424
  !FIELD_REFRESH_STATUSES.has(record.refreshStatus as FieldRefreshStatus)