@beyondwork/docx-react-component 1.0.101 → 1.0.103

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 (41) hide show
  1. package/package.json +1 -1
  2. package/src/core/commands/formatting-commands.ts +8 -7
  3. package/src/core/commands/paragraph-layout-commands.ts +11 -10
  4. package/src/core/commands/section-layout-commands.ts +7 -6
  5. package/src/core/commands/style-commands.ts +3 -2
  6. package/src/io/export/build-app-properties-xml.ts +24 -0
  7. package/src/io/normalize/normalize-text.ts +6 -5
  8. package/src/io/ooxml/docprops.ts +298 -0
  9. package/src/io/ooxml/parse-anchor.ts +15 -15
  10. package/src/io/ooxml/parse-drawing.ts +5 -5
  11. package/src/io/ooxml/parse-fields.ts +16 -15
  12. package/src/io/ooxml/parse-font-table.ts +2 -1
  13. package/src/io/ooxml/parse-footnotes.ts +3 -2
  14. package/src/io/ooxml/parse-headers-footers.ts +7 -6
  15. package/src/io/ooxml/parse-main-document.ts +41 -40
  16. package/src/io/ooxml/parse-numbering.ts +3 -2
  17. package/src/io/ooxml/parse-object.ts +6 -6
  18. package/src/io/ooxml/parse-paragraph-formatting.ts +12 -11
  19. package/src/io/ooxml/parse-picture.ts +16 -16
  20. package/src/io/ooxml/parse-run-formatting.ts +11 -10
  21. package/src/io/ooxml/parse-settings.ts +2 -1
  22. package/src/io/ooxml/parse-shapes.ts +18 -17
  23. package/src/io/ooxml/parse-styles.ts +16 -16
  24. package/src/io/ooxml/parse-theme.ts +5 -4
  25. package/src/model/canonical-document.ts +920 -815
  26. package/src/runtime/formatting/document-lookup.ts +3 -2
  27. package/src/runtime/formatting/formatting-context.ts +66 -25
  28. package/src/runtime/formatting/index.ts +18 -0
  29. package/src/runtime/formatting/layout-inputs.ts +256 -0
  30. package/src/runtime/formatting/numbering/geometry.ts +13 -12
  31. package/src/runtime/formatting/style-cascade.ts +2 -1
  32. package/src/runtime/formatting/table-style-resolver.ts +8 -7
  33. package/src/runtime/surface-projection.ts +31 -36
  34. package/src/session/export/stateful-export-pipeline.ts +9 -4
  35. package/src/session/export/stateful-export.ts +22 -6
  36. package/src/session/import/canonical-assembly.ts +2 -3
  37. package/src/session/import/loader-types.ts +3 -1
  38. package/src/session/import/loader.ts +12 -0
  39. package/src/session/import/normalize.ts +2 -1
  40. package/src/session/import/source-package-evidence.ts +1016 -0
  41. package/src/session/shared/session-utils.ts +9 -0
@@ -41,6 +41,7 @@ import type {
41
41
  ShapeContent,
42
42
  VmlShapeNode,
43
43
  WordArtNode,
44
+ Mutable,
44
45
  } from "../model/canonical-document.ts";
45
46
  import {
46
47
  describeOpaqueFragment,
@@ -575,7 +576,7 @@ function createSurfaceBlock(
575
576
 
576
577
  function createTableBlock(
577
578
  tableIndex: number,
578
- table: TableNode,
579
+ table: Mutable<TableNode>,
579
580
  document: CanonicalDocumentEnvelope,
580
581
  cursor: number,
581
582
  counters: {
@@ -769,7 +770,7 @@ function createTableBlock(
769
770
  };
770
771
  }
771
772
 
772
- function computeTableRowSpans(table: TableNode): Map<string, number> {
773
+ function computeTableRowSpans(table: Mutable<TableNode>): Map<string, number> {
773
774
  const positionedRows = table.rows.map((row) => {
774
775
  let startColumn = 0;
775
776
 
@@ -933,7 +934,7 @@ function toSurfaceFrameProperties(
933
934
  }
934
935
 
935
936
  function resolveSurfaceParagraphFormatting(
936
- formatting: CanonicalParagraphFormatting,
937
+ formatting: Mutable<CanonicalParagraphFormatting>,
937
938
  themeResolver: ThemeColorResolver | undefined,
938
939
  ): CanonicalParagraphFormatting {
939
940
  if (!formatting.shading) {
@@ -1065,7 +1066,7 @@ function resolveCellBorderStyles(
1065
1066
 
1066
1067
  function createSdtBlock(
1067
1068
  sdtIndex: number,
1068
- block: SdtNode,
1069
+ block: Mutable<SdtNode>,
1069
1070
  document: CanonicalDocumentEnvelope,
1070
1071
  cursor: number,
1071
1072
  counters: {
@@ -1123,7 +1124,7 @@ function createSdtBlock(
1123
1124
 
1124
1125
  function createParagraphBlock(
1125
1126
  paragraphIndex: number,
1126
- paragraph: ParagraphNode,
1127
+ paragraph: Mutable<ParagraphNode>,
1127
1128
  document: CanonicalDocumentEnvelope,
1128
1129
  start: number,
1129
1130
  formattingContext: FormattingContext,
@@ -1757,13 +1758,7 @@ function appendInlineSegments(
1757
1758
  const fieldLabel =
1758
1759
  node.fieldFamily === "TOC"
1759
1760
  ? "Table of Contents"
1760
- : node.fieldFamily === "PAGE"
1761
- ? "Current page number"
1762
- : node.fieldFamily === "NUMPAGES"
1763
- ? "Total pages"
1764
- : node.fieldFamily === "SECTIONPAGES"
1765
- ? "Section pages"
1766
- : `${node.fieldFamily ?? "Field"}: ${node.fieldTarget ?? node.instruction.trim()}`;
1761
+ : `${node.fieldFamily ?? "Field"}: ${node.fieldTarget ?? node.instruction.trim()}`;
1767
1762
  paragraph.segments.push({
1768
1763
  segmentId: `${paragraph.blockId}-segment-${paragraph.segments.length}`,
1769
1764
  kind: "field_ref",
@@ -1804,7 +1799,7 @@ function appendInlineSegments(
1804
1799
  }
1805
1800
 
1806
1801
  function registerParsedChartPreview(
1807
- node: ChartPreviewNode,
1802
+ node: Mutable<ChartPreviewNode>,
1808
1803
  document: CanonicalDocumentEnvelope,
1809
1804
  ): string | undefined {
1810
1805
  if (!node.parsedData) return undefined;
@@ -1946,7 +1941,7 @@ function surfaceAnchorFromGeometry(
1946
1941
  * fast-path image rendering.
1947
1942
  */
1948
1943
  function surfacePictureEffectsFromContent(
1949
- content: PictureContent,
1944
+ content: Mutable<PictureContent>,
1950
1945
  themeResolver?: ThemeColorResolver,
1951
1946
  ): SurfacePictureEffects | undefined {
1952
1947
  const outerShadow = resolveSurfacePictureShadow(content.outerShadow, themeResolver);
@@ -2159,7 +2154,7 @@ function appendTextBoxSegment(
2159
2154
  return { nextCursor: start + 1, lockedFragmentIds: [] };
2160
2155
  }
2161
2156
 
2162
- function shouldRenderSecondaryStoryVmlTextBox(node: VmlShapeNode): boolean {
2157
+ function shouldRenderSecondaryStoryVmlTextBox(node: Mutable<VmlShapeNode>): boolean {
2163
2158
  return Boolean(node.text) && (!node.shapeType || /_x0000_t202$/iu.test(node.shapeType));
2164
2159
  }
2165
2160
 
@@ -2174,7 +2169,7 @@ function isMicrosoftSensitivityLabelShape(
2174
2169
  /"Placement"\s*:\s*"Footer"/iu.test(node.rawXml);
2175
2170
  }
2176
2171
 
2177
- function createChartDetail(node: ChartPreviewNode): string {
2172
+ function createChartDetail(node: Mutable<ChartPreviewNode>): string {
2178
2173
  const parts = ["Embedded chart."];
2179
2174
  if (node.previewMediaId) {
2180
2175
  parts.push(`Preview available via fallback image (${node.previewMediaId}).`);
@@ -2185,7 +2180,7 @@ function createChartDetail(node: ChartPreviewNode): string {
2185
2180
  return parts.join(" ");
2186
2181
  }
2187
2182
 
2188
- function createSmartArtDetail(node: SmartArtPreviewNode): string {
2183
+ function createSmartArtDetail(node: Mutable<SmartArtPreviewNode>): string {
2189
2184
  const parts = ["SmartArt diagram."];
2190
2185
  if (node.previewMediaId) {
2191
2186
  parts.push(`Preview available via fallback image (${node.previewMediaId}).`);
@@ -2196,7 +2191,7 @@ function createSmartArtDetail(node: SmartArtPreviewNode): string {
2196
2191
  return parts.join(" ");
2197
2192
  }
2198
2193
 
2199
- function createShapeDetail(node: ShapeNode): string {
2194
+ function createShapeDetail(node: Mutable<ShapeNode>): string {
2200
2195
  if (node.isTextBox) {
2201
2196
  const parts = ["Text box."];
2202
2197
  if (node.text) parts.push(`Content: "${node.text}".`);
@@ -2210,7 +2205,7 @@ function createShapeDetail(node: ShapeNode): string {
2210
2205
  return parts.join(" ");
2211
2206
  }
2212
2207
 
2213
- function createWordArtDetail(node: WordArtNode): string {
2208
+ function createWordArtDetail(node: Mutable<WordArtNode>): string {
2214
2209
  const parts = ["WordArt decorative text."];
2215
2210
  if (node.text) parts.push(`Text: "${node.text}".`);
2216
2211
  if (node.geometry) parts.push(`Effect: ${node.geometry}.`);
@@ -2218,7 +2213,7 @@ function createWordArtDetail(node: WordArtNode): string {
2218
2213
  return parts.join(" ");
2219
2214
  }
2220
2215
 
2221
- function createVmlDetail(node: VmlShapeNode): string {
2216
+ function createVmlDetail(node: Mutable<VmlShapeNode>): string {
2222
2217
  const parts = ["Legacy VML drawing."];
2223
2218
  if (node.shapeType) parts.push(`Type: ${node.shapeType}.`);
2224
2219
  if (node.text) parts.push(`Text content: "${node.text}".`);
@@ -2587,22 +2582,22 @@ function toSurfaceTabStop(
2587
2582
  }
2588
2583
 
2589
2584
  function buildDirectParagraphFormattingFromNode(
2590
- paragraph: ParagraphNode,
2585
+ paragraph: Mutable<ParagraphNode>,
2591
2586
  ): CanonicalParagraphFormatting | undefined {
2592
- const direct: CanonicalParagraphFormatting = {};
2593
- if (paragraph.spacing) direct.spacing = paragraph.spacing;
2594
- if (paragraph.indentation) direct.indentation = paragraph.indentation;
2595
- if (paragraph.alignment) direct.alignment = paragraph.alignment;
2596
- if (paragraph.borders) direct.borders = paragraph.borders;
2597
- if (paragraph.shading) direct.shading = paragraph.shading;
2598
- if (paragraph.tabStops && paragraph.tabStops.length > 0) direct.tabStops = [...paragraph.tabStops];
2599
- if (paragraph.contextualSpacing !== undefined) direct.contextualSpacing = paragraph.contextualSpacing;
2600
- if (paragraph.keepNext !== undefined) direct.keepNext = paragraph.keepNext;
2601
- if (paragraph.keepLines !== undefined) direct.keepLines = paragraph.keepLines;
2602
- if (paragraph.widowControl !== undefined) direct.widowControl = paragraph.widowControl;
2603
- if (paragraph.pageBreakBefore !== undefined) direct.pageBreakBefore = paragraph.pageBreakBefore;
2604
- if (paragraph.outlineLevel !== undefined) direct.outlineLevel = paragraph.outlineLevel;
2605
- if (paragraph.bidi !== undefined) direct.bidi = paragraph.bidi;
2587
+ const direct: Mutable<CanonicalParagraphFormatting> = {};
2588
+ if (paragraph.spacing) (direct as Mutable<typeof direct>).spacing = paragraph.spacing;
2589
+ if (paragraph.indentation) (direct as Mutable<typeof direct>).indentation = paragraph.indentation;
2590
+ if (paragraph.alignment) (direct as Mutable<typeof direct>).alignment = paragraph.alignment;
2591
+ if (paragraph.borders) (direct as Mutable<typeof direct>).borders = paragraph.borders;
2592
+ if (paragraph.shading) (direct as Mutable<typeof direct>).shading = paragraph.shading;
2593
+ if (paragraph.tabStops && paragraph.tabStops.length > 0) (direct as Mutable<typeof direct>).tabStops = [...paragraph.tabStops];
2594
+ if (paragraph.contextualSpacing !== undefined) (direct as Mutable<typeof direct>).contextualSpacing = paragraph.contextualSpacing;
2595
+ if (paragraph.keepNext !== undefined) (direct as Mutable<typeof direct>).keepNext = paragraph.keepNext;
2596
+ if (paragraph.keepLines !== undefined) (direct as Mutable<typeof direct>).keepLines = paragraph.keepLines;
2597
+ if (paragraph.widowControl !== undefined) (direct as Mutable<typeof direct>).widowControl = paragraph.widowControl;
2598
+ if (paragraph.pageBreakBefore !== undefined) (direct as Mutable<typeof direct>).pageBreakBefore = paragraph.pageBreakBefore;
2599
+ if (paragraph.outlineLevel !== undefined) (direct as Mutable<typeof direct>).outlineLevel = paragraph.outlineLevel;
2600
+ if (paragraph.bidi !== undefined) (direct as Mutable<typeof direct>).bidi = paragraph.bidi;
2606
2601
  if (paragraph.suppressLineNumbers !== undefined) direct.suppressLineNumbers = paragraph.suppressLineNumbers;
2607
2602
  return Object.keys(direct).length > 0 ? direct : undefined;
2608
2603
  }
@@ -2947,7 +2942,7 @@ function createFloatingImageDetail(
2947
2942
  return parts.join(" ");
2948
2943
  }
2949
2944
 
2950
- function hasMediaItem(media: MediaCatalog, mediaId: string): boolean {
2945
+ function hasMediaItem(media: Mutable<MediaCatalog>, mediaId: string): boolean {
2951
2946
  return mediaId in media.items;
2952
2947
  }
2953
2948
 
@@ -136,7 +136,7 @@ export function ensureHostMetadataParts(
136
136
  if (!corePropertiesPart || corePropertiesPart.contentType !== CORE_PROPERTIES_CONTENT_TYPE) {
137
137
  exportSession.replaceOwnedPart({
138
138
  path: CORE_PROPERTIES_PART_PATH,
139
- bytes: corePropertiesPart?.bytes ?? new TextEncoder().encode(buildCorePropertiesXml(document)),
139
+ bytes: new TextEncoder().encode(buildCorePropertiesXml(document)),
140
140
  contentType: CORE_PROPERTIES_CONTENT_TYPE,
141
141
  compression: corePropertiesPart?.compression,
142
142
  });
@@ -146,7 +146,7 @@ export function ensureHostMetadataParts(
146
146
  if (!appPropertiesPart || appPropertiesPart.contentType !== APP_PROPERTIES_CONTENT_TYPE) {
147
147
  exportSession.replaceOwnedPart({
148
148
  path: APP_PROPERTIES_PART_PATH,
149
- bytes: appPropertiesPart?.bytes ?? new TextEncoder().encode(buildAppPropertiesXml()),
149
+ bytes: new TextEncoder().encode(buildAppPropertiesXml(document.metadata.appProperties)),
150
150
  contentType: APP_PROPERTIES_CONTENT_TYPE,
151
151
  compression: appPropertiesPart?.compression,
152
152
  });
@@ -305,11 +305,16 @@ export function buildCorePropertiesXml(document: CanonicalDocumentEnvelope): str
305
305
  xmlNode("dc:title", metadata.title),
306
306
  xmlNode("dc:subject", metadata.subject),
307
307
  xmlNode("dc:description", metadata.description),
308
+ xmlNode("dc:creator", metadata.creator),
308
309
  xmlNode("dc:language", metadata.language),
309
310
  xmlNode("cp:keywords", keywords),
310
311
  xmlNode("cp:category", metadata.category),
311
- xmlNode('dcterms:created xsi:type="dcterms:W3CDTF"', document.createdAt),
312
- xmlNode('dcterms:modified xsi:type="dcterms:W3CDTF"', document.updatedAt),
312
+ xmlNode("cp:lastModifiedBy", metadata.lastModifiedBy),
313
+ xmlNode("cp:contentStatus", metadata.contentStatus),
314
+ xmlNode("cp:revision", metadata.revision),
315
+ xmlNode("cp:version", metadata.version),
316
+ xmlNode('dcterms:created xsi:type="dcterms:W3CDTF"', metadata.createdUtc ?? document.createdAt),
317
+ xmlNode('dcterms:modified xsi:type="dcterms:W3CDTF"', metadata.modifiedUtc ?? document.updatedAt),
313
318
  ].filter((line): line is string => Boolean(line));
314
319
 
315
320
  return [
@@ -83,6 +83,7 @@ import {
83
83
  import { toEditorSessionState } from "../import/canonical-assembly.ts";
84
84
  import {
85
85
  serializeCanonicalDocumentForExport,
86
+ serializeNumberingCatalogForExport,
86
87
  } from "../shared/session-utils.ts";
87
88
  import { withDocumentRelatedParts } from "../import/package-parts.ts";
88
89
  import {
@@ -290,6 +291,14 @@ export async function runStatefulExport(
290
291
  state.sourcePeoplePartPath ?? PEOPLE_PART_PATH;
291
292
  const numberingPartPath =
292
293
  state.sourceNumberingPartPath ?? NUMBERING_PART_PATH;
294
+ const sourceNumberingPart = state.sourceNumberingPartPath
295
+ ? state.sourcePackage.parts.get(numberingPartPath)
296
+ : undefined;
297
+ const numberingSignatureMatch =
298
+ serializeNumberingCatalogForExport(currentDocument.numbering as NumberingCatalog) ===
299
+ state.initialNumberingSignature;
300
+ const canReuseSourceNumberingPart =
301
+ numberingSignatureMatch && sourceNumberingPart !== undefined;
293
302
  const serializedNumberingXml = hasSerializableNumberingEntries(
294
303
  currentDocument.numbering as NumberingCatalog,
295
304
  )
@@ -303,6 +312,7 @@ export async function runStatefulExport(
303
312
  partPath: numberingPartPath,
304
313
  existingRelationshipId: state.sourceNumberingRelationshipId,
305
314
  include:
315
+ canReuseSourceNumberingPart ||
306
316
  Boolean(serializedNumberingXml) ||
307
317
  Boolean(state.sourceNumberingPartPath),
308
318
  },
@@ -423,16 +433,22 @@ export async function runStatefulExport(
423
433
  relationships: nextRelationships,
424
434
  });
425
435
 
426
- if (serializedNumberingXml || state.sourceNumberingPartPath) {
436
+ if (canReuseSourceNumberingPart || serializedNumberingXml || state.sourceNumberingPartPath) {
437
+ const numberingBytes =
438
+ canReuseSourceNumberingPart && sourceNumberingPart
439
+ ? sourceNumberingPart.bytes
440
+ : new TextEncoder().encode(
441
+ serializedNumberingXml ??
442
+ `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"></w:numbering>`,
443
+ );
444
+
427
445
  exportSession.replaceOwnedPart({
428
446
  path: numberingPartPath,
429
- bytes: new TextEncoder().encode(
430
- serializedNumberingXml ??
431
- `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"></w:numbering>`,
432
- ),
447
+ bytes: numberingBytes,
433
448
  contentType:
434
- state.sourcePackage.parts.get(numberingPartPath)?.contentType ??
449
+ sourceNumberingPart?.contentType ??
435
450
  WORD_NUMBERING_CONTENT_TYPE,
451
+ compression: sourceNumberingPart?.compression,
436
452
  });
437
453
  }
438
454
 
@@ -133,6 +133,7 @@ export function createImportedCanonicalDocument(input: {
133
133
  subParts?: SubPartsCatalog;
134
134
  parsedStyles?: ParseStylesResult;
135
135
  fontTable?: CanonicalDocument["fontTable"];
136
+ metadata?: CanonicalDocument["metadata"];
136
137
  preservation: CanonicalDocument["preservation"];
137
138
  diagnostics: CanonicalDocument["diagnostics"];
138
139
  review: CanonicalDocument["review"];
@@ -166,9 +167,7 @@ export function createImportedCanonicalDocument(input: {
166
167
  docId: createCanonicalDocumentId(input.documentId),
167
168
  createdAt: input.timestamp,
168
169
  updatedAt: input.timestamp,
169
- metadata: {
170
- customProperties: {},
171
- },
170
+ metadata: input.metadata ?? { customProperties: {} },
172
171
  styles,
173
172
  numbering,
174
173
  media: input.media,
@@ -252,7 +252,8 @@ export interface LoadedDocxEditorSession {
252
252
  * State captured at import time that the export pipeline consumes to
253
253
  * round-trip the package. Carries source bytes, the OPC package
254
254
  * snapshot, per-part relationships, preserved-part manifest, and the
255
- * initial canonical signature used for fast-path byte reuse.
255
+ * initial canonical signatures used for fast-path byte reuse and
256
+ * preserve-first per-part export decisions.
256
257
  *
257
258
  * Load path builds this; `exportDocxEditorSession` consumes it via
258
259
  * the closure captured at open time.
@@ -299,6 +300,7 @@ export interface ImportedDocxState {
299
300
  preservedCommentDefinitions: readonly ImportedCommentDefinition[];
300
301
  blockingCommentDiagnostics: readonly CommentImportDiagnostic[];
301
302
  initialCanonicalSignature: string;
303
+ initialNumberingSignature: string;
302
304
  sourceSubPartPaths: {
303
305
  headers: Array<{ partPath: string; relationshipId: string }>;
304
306
  footers: Array<{ partPath: string; relationshipId: string }>;
@@ -97,6 +97,7 @@ import {
97
97
  } from "../../io/ooxml/parse-revisions.ts";
98
98
  import { parseCommentsFromOoxml } from "../../io/ooxml/parse-comments.ts";
99
99
  import { parseNumberingXml } from "../../io/ooxml/parse-numbering.ts";
100
+ import { parseDocumentMetadataFromOpcParts } from "../../io/ooxml/docprops.ts";
100
101
  import {
101
102
  parseHeaderFooterReferences,
102
103
  parseHeaderXml,
@@ -131,6 +132,7 @@ import {
131
132
  decodeUtf8,
132
133
  extractDocumentRootAttributes,
133
134
  serializeCanonicalDocumentForExport,
135
+ serializeNumberingCatalogForExport,
134
136
  toUint8Array,
135
137
  } from "../shared/session-utils.ts";
136
138
  import {
@@ -285,6 +287,7 @@ export async function loadDocxSessionAsync(
285
287
  }),
286
288
  );
287
289
  }
290
+ const packageMetadata = parseDocumentMetadataFromOpcParts(sourcePackage.parts);
288
291
  stages.emit("opc");
289
292
  await scheduler.yield();
290
293
  const embeddedWorkflowPayload = parseWorkflowPayloadEnvelopeFromPackage(sourcePackage);
@@ -1018,6 +1021,7 @@ export async function loadDocxSessionAsync(
1018
1021
  subParts,
1019
1022
  parsedStyles,
1020
1023
  fontTable: parsedFontTable,
1024
+ metadata: packageMetadata,
1021
1025
  preservation: buildImportPreservation(normalizedDocument.preservation, sourcePackage, [
1022
1026
  mainDocumentPath,
1023
1027
  numberingPartPath,
@@ -1152,6 +1156,9 @@ export async function loadDocxSessionAsync(
1152
1156
  BLOCKING_COMMENT_DIAGNOSTIC_CODES.has(diagnostic.code),
1153
1157
  ),
1154
1158
  initialCanonicalSignature: serializeCanonicalDocumentForExport(document),
1159
+ initialNumberingSignature: serializeNumberingCatalogForExport(
1160
+ document.numbering as _NumberingCatalog,
1161
+ ),
1155
1162
  sourceSubPartPaths: {
1156
1163
  headers: sourceHeaderPaths,
1157
1164
  footers: sourceFooterPaths,
@@ -1229,6 +1236,7 @@ export function loadDocxSessionSync(
1229
1236
  }),
1230
1237
  );
1231
1238
  }
1239
+ const packageMetadata = parseDocumentMetadataFromOpcParts(sourcePackage.parts);
1232
1240
  stages.emit("opc");
1233
1241
  const embeddedWorkflowPayload = parseWorkflowPayloadEnvelopeFromPackage(sourcePackage);
1234
1242
  const embeddedWorkflowMetadata = embeddedWorkflowPayload?.workflowMetadata;
@@ -1697,6 +1705,7 @@ export function loadDocxSessionSync(
1697
1705
  subParts,
1698
1706
  parsedStyles,
1699
1707
  fontTable: parsedFontTable,
1708
+ metadata: packageMetadata,
1700
1709
  preservation: buildImportPreservation(normalizedDocument.preservation, sourcePackage, [
1701
1710
  mainDocumentPath,
1702
1711
  numberingPartPath,
@@ -1808,6 +1817,9 @@ export function loadDocxSessionSync(
1808
1817
  BLOCKING_COMMENT_DIAGNOSTIC_CODES.has(diagnostic.code),
1809
1818
  ),
1810
1819
  initialCanonicalSignature: serializeCanonicalDocumentForExport(document),
1820
+ initialNumberingSignature: serializeNumberingCatalogForExport(
1821
+ document.numbering as _NumberingCatalog,
1822
+ ),
1811
1823
  sourceSubPartPaths: {
1812
1824
  headers: sourceHeaderPaths,
1813
1825
  footers: sourceFooterPaths,
@@ -22,6 +22,7 @@ import type {
22
22
  CanonicalDocument,
23
23
  FootnoteCollection,
24
24
  OpaqueFragmentRecord,
25
+ Mutable,
25
26
  } from "../../model/canonical-document.ts";
26
27
 
27
28
  /** Warning shape carried on `CanonicalDocument.diagnostics.warnings`. */
@@ -130,7 +131,7 @@ export function normalizeFootnoteCollectionOpaqueBlocks(
130
131
  if (!collection) return;
131
132
  const notes = kind === "footnote" ? collection.footnotes : collection.endnotes;
132
133
  for (const definition of Object.values(notes)) {
133
- definition.blocks = normalizeSubPartOpaqueBlocks(
134
+ (definition as Mutable<typeof definition>).blocks = normalizeSubPartOpaqueBlocks(
134
135
  definition.blocks,
135
136
  opaqueFragments,
136
137
  warnings,