@stll/folio-core 0.7.0 → 0.9.0

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 (193) hide show
  1. package/README.md +39 -0
  2. package/dist/ai-edits/apply.js +18 -4
  3. package/dist/ai-edits/blockRange.d.ts +13 -2
  4. package/dist/ai-edits/blockRange.js +23 -2
  5. package/dist/ai-edits/headless.d.ts +31 -4
  6. package/dist/ai-edits/headless.js +148 -35
  7. package/dist/ai-edits/index.d.ts +6 -4
  8. package/dist/ai-edits/index.js +4 -1
  9. package/dist/ai-edits/scoped-reading.d.ts +9 -0
  10. package/dist/ai-edits/scoped-reading.js +60 -0
  11. package/dist/ai-edits/snapshot.js +53 -7
  12. package/dist/ai-edits/types.d.ts +46 -3
  13. package/dist/compat/eigenpal.d.ts +8 -3
  14. package/dist/compat/eigenpal.js +7 -2
  15. package/dist/controller/fontReadiness.d.ts +29 -0
  16. package/dist/controller/fontReadiness.js +139 -0
  17. package/dist/controller/latestRequestGate.d.ts +13 -0
  18. package/dist/controller/latestRequestGate.js +16 -0
  19. package/dist/controller/layoutPipeline.js +88 -10
  20. package/dist/document-operations.d.ts +10 -3
  21. package/dist/document-operations.js +29 -14
  22. package/dist/docx/blockContentParser.js +53 -10
  23. package/dist/docx/commentParser.js +1 -1
  24. package/dist/docx/compatibility.d.ts +30 -2
  25. package/dist/docx/compatibility.js +118 -39
  26. package/dist/docx/conformance.d.ts +6 -0
  27. package/dist/docx/conformance.js +18 -0
  28. package/dist/docx/encryption/agileDecryption.js +1 -1
  29. package/dist/docx/encryption/encryptionInfo.js +1 -1
  30. package/dist/docx/encryption/openEncryptedDocx.js +1 -1
  31. package/dist/docx/ensureParaIds.d.ts +29 -0
  32. package/dist/docx/ensureParaIds.js +423 -0
  33. package/dist/docx/fontTableParser.d.ts +6 -0
  34. package/dist/docx/fontTableParser.js +72 -0
  35. package/dist/docx/groupDrawingParser.js +7 -2
  36. package/dist/docx/headerFooterParser.js +1 -1
  37. package/dist/docx/normalizeBaseDirection.js +1 -1
  38. package/dist/docx/numberingParser.js +2 -0
  39. package/dist/docx/paragraphParser.js +21 -1
  40. package/dist/docx/paragraphTextBoxEnrichment.js +141 -2
  41. package/dist/docx/parser.d.ts +1 -1
  42. package/dist/docx/parser.js +14 -9
  43. package/dist/docx/rezip.js +70 -5
  44. package/dist/docx/runConsolidator.js +4 -0
  45. package/dist/docx/runParser.js +19 -1
  46. package/dist/docx/selectiveSave.js +3 -3
  47. package/dist/docx/selectiveXmlPatch.d.ts +10 -1
  48. package/dist/docx/selectiveXmlPatch.js +1 -1
  49. package/dist/docx/serializer/fontTableSerializer.d.ts +6 -0
  50. package/dist/docx/serializer/fontTableSerializer.js +26 -0
  51. package/dist/docx/serializer/headerFooterSerializer.js +1 -1
  52. package/dist/docx/serializer/paragraphSerializer.js +8 -0
  53. package/dist/docx/serializer/runSerializer.js +14 -2
  54. package/dist/docx/serializer/settingsSerializer.d.ts +6 -0
  55. package/dist/docx/serializer/settingsSerializer.js +16 -0
  56. package/dist/docx/serializer/stylesSerializer.d.ts +6 -0
  57. package/dist/docx/serializer/stylesSerializer.js +56 -0
  58. package/dist/docx/serializer/tableSerializer.js +29 -26
  59. package/dist/docx/serializer/themeSerializer.d.ts +6 -0
  60. package/dist/docx/serializer/themeSerializer.js +36 -0
  61. package/dist/docx/settingsParser.js +46 -0
  62. package/dist/docx/styleParser.js +26 -1
  63. package/dist/docx/tableParser.js +19 -1
  64. package/dist/docx/textBoxParser.js +6 -1
  65. package/dist/docx/vmlImageParser.js +146 -1
  66. package/dist/i18n/messages/catalogs.gen.d.ts +5916 -5916
  67. package/dist/i18n/messages/catalogs.gen.js +5910 -5910
  68. package/dist/index.d.ts +8 -3
  69. package/dist/index.js +7 -2
  70. package/dist/layout-bridge/convert/footnoteLayout.d.ts +3 -0
  71. package/dist/layout-bridge/convert/footnoteLayout.js +3 -1
  72. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +3 -0
  73. package/dist/layout-bridge/convert/headerFooterLayout.js +22 -3
  74. package/dist/layout-bridge/convert/paragraphFrames.d.ts +7 -0
  75. package/dist/layout-bridge/convert/paragraphFrames.js +136 -0
  76. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +14 -2
  77. package/dist/layout-bridge/convert/toFlowBlocks.js +93 -17
  78. package/dist/layout-bridge/engine/hitTest.js +2 -1
  79. package/dist/layout-bridge/engine/measuring/index.d.ts +2 -1
  80. package/dist/layout-bridge/engine/measuring/index.js +2 -1
  81. package/dist/layout-bridge/engine/selectionRects.js +2 -1
  82. package/dist/layout-engine/headerFooterRefs.d.ts +7 -0
  83. package/dist/layout-engine/headerFooterRefs.js +40 -0
  84. package/dist/layout-engine/index.d.ts +3 -2
  85. package/dist/layout-engine/index.js +68 -106
  86. package/dist/layout-engine/measure/cache.js +11 -3
  87. package/dist/layout-engine/measure/font-metrics.worker.js +1 -0
  88. package/dist/layout-engine/measure/index.d.ts +2 -1
  89. package/dist/layout-engine/measure/index.js +2 -1
  90. package/dist/layout-engine/measure/lineBreakProvider.d.ts +33 -0
  91. package/dist/layout-engine/measure/lineBreakProvider.js +252 -0
  92. package/dist/layout-engine/measure/lineBreaks.d.ts +7 -2
  93. package/dist/layout-engine/measure/lineBreaks.js +11 -18
  94. package/dist/layout-engine/measure/listMarkerWidth.d.ts +4 -1
  95. package/dist/layout-engine/measure/listMarkerWidth.js +33 -8
  96. package/dist/layout-engine/measure/measureBlocks.d.ts +4 -2
  97. package/dist/layout-engine/measure/measureBlocks.js +138 -86
  98. package/dist/layout-engine/measure/measureContainer.js +32 -9
  99. package/dist/layout-engine/measure/measureHelpers.js +5 -2
  100. package/dist/layout-engine/measure/measureParagraph.js +346 -74
  101. package/dist/layout-engine/measure/measureTypes.d.ts +2 -1
  102. package/dist/layout-engine/measure/measureWorker.d.ts +2 -2
  103. package/dist/layout-engine/measure/measureWorker.js +8 -19
  104. package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +3 -14
  105. package/dist/layout-engine/measure/tableCellFloating.d.ts +10 -3
  106. package/dist/layout-engine/measure/tableCellFloating.js +37 -29
  107. package/dist/layout-engine/measure/tableCellFlow.d.ts +27 -0
  108. package/dist/layout-engine/measure/tableCellFlow.js +73 -0
  109. package/dist/layout-engine/measure/tableCellGrid.d.ts +15 -0
  110. package/dist/layout-engine/measure/tableCellGrid.js +62 -0
  111. package/dist/layout-engine/measure/textBoxParagraphLayout.d.ts +18 -0
  112. package/dist/layout-engine/measure/textBoxParagraphLayout.js +32 -0
  113. package/dist/layout-engine/measure/textMeasurementPolicy.d.ts +18 -0
  114. package/dist/layout-engine/measure/textMeasurementPolicy.js +24 -0
  115. package/dist/layout-engine/paginator.d.ts +2 -1
  116. package/dist/layout-engine/paginator.js +7 -6
  117. package/dist/layout-engine/paragraphFrame.d.ts +22 -0
  118. package/dist/layout-engine/paragraphFrame.js +17 -0
  119. package/dist/layout-engine/paragraphSequence.d.ts +7 -0
  120. package/dist/layout-engine/paragraphSequence.js +25 -0
  121. package/dist/layout-engine/paragraphSpacing.d.ts +17 -0
  122. package/dist/layout-engine/paragraphSpacing.js +30 -0
  123. package/dist/layout-engine/renderedBreakReconciliation.d.ts +63 -0
  124. package/dist/layout-engine/renderedBreakReconciliation.js +86 -0
  125. package/dist/layout-engine/tableRowBreak.d.ts +5 -2
  126. package/dist/layout-engine/tableRowBreak.js +62 -29
  127. package/dist/layout-engine/types.d.ts +54 -12
  128. package/dist/layout-engine/types.js +15 -2
  129. package/dist/layout-painter/anchoredImagePosition.d.ts +23 -0
  130. package/dist/layout-painter/anchoredImagePosition.js +95 -0
  131. package/dist/layout-painter/borderStroke.d.ts +23 -0
  132. package/dist/layout-painter/borderStroke.js +39 -0
  133. package/dist/layout-painter/renderImage.d.ts +5 -4
  134. package/dist/layout-painter/renderImage.js +18 -4
  135. package/dist/layout-painter/renderPage.d.ts +2 -27
  136. package/dist/layout-painter/renderPage.js +15 -101
  137. package/dist/layout-painter/renderParagraph.js +45 -26
  138. package/dist/layout-painter/renderTable.d.ts +2 -0
  139. package/dist/layout-painter/renderTable.js +257 -47
  140. package/dist/layout-painter/renderTextBox.js +17 -7
  141. package/dist/managers/DocumentLoaderManager.d.ts +1 -1
  142. package/dist/managers/DocumentLoaderManager.js +2 -2
  143. package/dist/paged-layout/sectionBlockWidths.d.ts +3 -0
  144. package/dist/paged-layout/sectionBlockWidths.js +9 -0
  145. package/dist/paged-layout/sectionGeometry.js +1 -1
  146. package/dist/prosemirror/attrs/index.d.ts +4 -2
  147. package/dist/prosemirror/attrs/index.js +32 -2
  148. package/dist/prosemirror/commands/propertyChangeScope.js +1 -1
  149. package/dist/prosemirror/conversion/fromProseDoc.js +35 -18
  150. package/dist/prosemirror/conversion/toProseDoc.js +73 -37
  151. package/dist/prosemirror/extensions/StarterKit.js +2 -0
  152. package/dist/prosemirror/extensions/core/ParagraphExtension.js +8 -2
  153. package/dist/prosemirror/extensions/features/AutoBidiDetectionExtension.js +1 -1
  154. package/dist/prosemirror/extensions/features/ListExtension.js +2 -0
  155. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +68 -16
  156. package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +1 -0
  157. package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
  158. package/dist/prosemirror/extensions/marks/LanguageExtension.d.ts +7 -0
  159. package/dist/prosemirror/extensions/marks/LanguageExtension.js +30 -0
  160. package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
  161. package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
  162. package/dist/prosemirror/extensions/marks/TrackedChangeExtensions.js +1 -1
  163. package/dist/prosemirror/extensions/marks/markUtils.js +12 -0
  164. package/dist/prosemirror/extensions/nodes/ImageExtension.js +2 -1
  165. package/dist/prosemirror/extensions/nodes/TableExtension.js +4 -2
  166. package/dist/prosemirror/extensions/nodes/TextBoxExtension.d.ts +2 -1
  167. package/dist/prosemirror/extensions/nodes/TextBoxExtension.js +7 -0
  168. package/dist/prosemirror/schema/index.d.ts +2 -2
  169. package/dist/prosemirror/schema/marks.d.ts +6 -1
  170. package/dist/prosemirror/schema/nodes.d.ts +16 -12
  171. package/dist/prosemirror/styles/resolvedStyleAttrs.js +2 -0
  172. package/dist/prosemirror/utils/tabCalculator.d.ts +3 -1
  173. package/dist/prosemirror/utils/tabCalculator.js +12 -10
  174. package/dist/prosemirror/validation.js +4 -1
  175. package/dist/redline.js +13 -3
  176. package/dist/server.d.ts +9 -4
  177. package/dist/server.js +7 -2
  178. package/dist/style-sets/extract.d.ts +35 -0
  179. package/dist/style-sets/extract.js +123 -0
  180. package/dist/style-sets/stellaStyle.d.ts +13 -0
  181. package/dist/style-sets/stellaStyle.js +516 -0
  182. package/dist/style-sets/types.d.ts +31 -0
  183. package/dist/style-sets/types.js +5 -0
  184. package/dist/types/index.d.ts +2 -1
  185. package/dist/utils/createDocument.d.ts +12 -2
  186. package/dist/utils/createDocument.js +55 -36
  187. package/dist/utils/fontResolver.js +6 -6
  188. package/dist/utils/formatToStyle.js +1 -1
  189. package/dist/utils/hexId.d.ts +8 -1
  190. package/dist/utils/hexId.js +11 -3
  191. package/dist/utils/paragraphFormattingMerge.d.ts +14 -3
  192. package/dist/utils/paragraphFormattingMerge.js +14 -4
  193. package/package.json +5 -1
@@ -67,19 +67,24 @@ type FolioDocumentOperationIssue = {
67
67
  retryable: boolean;
68
68
  recovery: FolioDocumentOperationRecovery;
69
69
  };
70
+ type FolioDocumentOperationStory = "main" | {
71
+ type: "header" | "footer";
72
+ relationshipId: string;
73
+ };
70
74
  /** One typed target affected by a successfully applied document operation. */
71
75
  type FolioDocumentOperationAffectedTarget = {
72
76
  type: "block";
73
- story: "main";
77
+ story: FolioDocumentOperationStory;
74
78
  blockId: string;
75
79
  effect: "updated" | "deleted" | "commented";
76
80
  } | {
77
81
  type: "textRange";
78
82
  range: FolioAITextRangeHandle;
79
83
  effect: "formatted" | "commented";
84
+ story?: Exclude<FolioDocumentOperationStory, "main">;
80
85
  } | {
81
86
  type: "insertion";
82
- story: "main";
87
+ story: FolioDocumentOperationStory;
83
88
  anchorBlockId: string;
84
89
  position: "before" | "after";
85
90
  content: "block" | "signatureTable";
@@ -123,6 +128,7 @@ type ApplyFolioDocumentOperationsOptions = {
123
128
  view: FolioAIEditView;
124
129
  snapshot: FolioAIEditSnapshot;
125
130
  batch: FolioDocumentOperationBatch;
131
+ story?: FolioDocumentOperationStory;
126
132
  author?: string;
127
133
  createCommentId?: (text: string) => number;
128
134
  createUndoHandle?: () => FolioDocumentOperationUndoHandle;
@@ -131,9 +137,10 @@ declare const applyFolioDocumentOperations: ({
131
137
  view,
132
138
  snapshot,
133
139
  batch,
140
+ story,
134
141
  author,
135
142
  createCommentId,
136
143
  createUndoHandle
137
144
  }: ApplyFolioDocumentOperationsOptions) => FolioDocumentOperationResult;
138
145
  //#endregion
139
- export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
146
+ export { ApplyFolioDocumentOperationsOptions, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FolioDocumentOperation, FolioDocumentOperationAffectedTarget, FolioDocumentOperationBatch, FolioDocumentOperationCapabilities, FolioDocumentOperationIssue, FolioDocumentOperationMode, FolioDocumentOperationPrecondition, FolioDocumentOperationReceipt, FolioDocumentOperationRecovery, FolioDocumentOperationResult, FolioDocumentOperationStatus, FolioDocumentOperationStory, FolioDocumentOperationType, FolioDocumentOperationUndoFailureReason, FolioDocumentOperationUndoHandle, FolioDocumentOperationUndoResult, InvalidFolioDocumentOperationBatchError, UnsupportedFolioDocumentOperationVersionError, applyFolioDocumentOperations, assertSupportedFolioDocumentOperationVersion, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, isFolioDocumentOperationModeSupported, isSupportedFolioDocumentOperationVersion, parseFolioDocumentOperationBatch };
@@ -438,54 +438,56 @@ const getFolioDocumentOperationIssues = (operations, skipped) => {
438
438
  };
439
439
  });
440
440
  };
441
- const getPrimaryAffectedTarget = (operation) => {
441
+ const getPrimaryAffectedTarget = (operation, story) => {
442
442
  switch (operation.type) {
443
443
  case "replaceInBlock":
444
444
  case "replaceBlock": return {
445
445
  type: "block",
446
- story: "main",
446
+ story,
447
447
  blockId: operation.blockId,
448
448
  effect: "updated"
449
449
  };
450
450
  case "replaceRange": return {
451
451
  type: "block",
452
- story: "main",
452
+ story,
453
453
  blockId: operation.range.blockId,
454
454
  effect: "updated"
455
455
  };
456
456
  case "commentOnRange": return {
457
457
  type: "textRange",
458
458
  range: operation.range,
459
- effect: "commented"
459
+ effect: "commented",
460
+ ...story !== "main" && { story }
460
461
  };
461
462
  case "formatRange": return {
462
463
  type: "textRange",
463
464
  range: operation.range,
464
- effect: "formatted"
465
+ effect: "formatted",
466
+ ...story !== "main" && { story }
465
467
  };
466
468
  case "insertAfterBlock":
467
469
  case "insertBeforeBlock": return {
468
470
  type: "insertion",
469
- story: "main",
471
+ story,
470
472
  anchorBlockId: operation.blockId,
471
473
  position: operation.type === "insertBeforeBlock" ? "before" : "after",
472
474
  content: "block"
473
475
  };
474
476
  case "deleteBlock": return {
475
477
  type: "block",
476
- story: "main",
478
+ story,
477
479
  blockId: operation.blockId,
478
480
  effect: "deleted"
479
481
  };
480
482
  case "commentOnBlock": return {
481
483
  type: "block",
482
- story: "main",
484
+ story,
483
485
  blockId: operation.blockId,
484
486
  effect: "commented"
485
487
  };
486
488
  case "insertSignatureTable": return {
487
489
  type: "insertion",
488
- story: "main",
490
+ story,
489
491
  anchorBlockId: operation.blockId,
490
492
  position: operation.position ?? "after",
491
493
  content: "signatureTable"
@@ -493,7 +495,12 @@ const getPrimaryAffectedTarget = (operation) => {
493
495
  }
494
496
  };
495
497
  /** Build deterministic affected-target receipts from operations and their applied entries. */
496
- const getFolioDocumentOperationReceipts = (operations, applied) => {
498
+ const getFolioDocumentOperationReceipts = (operations, applied) => getFolioDocumentOperationReceiptsForStory({
499
+ operations,
500
+ applied,
501
+ story: "main"
502
+ });
503
+ const getFolioDocumentOperationReceiptsForStory = ({ operations, applied, story }) => {
497
504
  const appliedById = /* @__PURE__ */ new Map();
498
505
  applied.forEach((operation) => {
499
506
  appliedById.set(operation.id, operation);
@@ -502,7 +509,7 @@ const getFolioDocumentOperationReceipts = (operations, applied) => {
502
509
  operations.forEach((operation, operationIndex) => {
503
510
  const appliedOperation = appliedById.get(operation.id);
504
511
  if (!appliedOperation) return;
505
- const affected = [getPrimaryAffectedTarget(operation)];
512
+ const affected = [getPrimaryAffectedTarget(operation, story)];
506
513
  if (appliedOperation.commentId !== void 0) affected.push({
507
514
  type: "comment",
508
515
  commentId: appliedOperation.commentId
@@ -515,7 +522,7 @@ const getFolioDocumentOperationReceipts = (operations, applied) => {
515
522
  });
516
523
  return receipts;
517
524
  };
518
- const applyFolioDocumentOperations = ({ view, snapshot, batch, author, createCommentId, createUndoHandle }) => {
525
+ const applyFolioDocumentOperations = ({ view, snapshot, batch, story = "main", author, createCommentId, createUndoHandle }) => {
519
526
  const parsedBatch = parseFolioDocumentOperationBatch(batch);
520
527
  const apply = ({ targetView, targetCreateCommentId = createCommentId, preview = false }) => {
521
528
  return (preview ? previewFolioAIEditOperations : applyFolioAIEditOperations)({
@@ -556,7 +563,11 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, author, createCom
556
563
  applied: previewResult.applied.map(({ id }) => ({ id })),
557
564
  skipped: previewResult.skipped,
558
565
  issues: getFolioDocumentOperationIssues(parsedBatch.operations, previewResult.skipped),
559
- receipts: getFolioDocumentOperationReceipts(parsedBatch.operations, previewResult.applied),
566
+ receipts: getFolioDocumentOperationReceiptsForStory({
567
+ operations: parsedBatch.operations,
568
+ applied: previewResult.applied,
569
+ story
570
+ }),
560
571
  undoHandle: null
561
572
  };
562
573
  }
@@ -570,7 +581,11 @@ const applyFolioDocumentOperations = ({ view, snapshot, batch, author, createCom
570
581
  status: "committed",
571
582
  ...result,
572
583
  issues: getFolioDocumentOperationIssues(parsedBatch.operations, result.skipped),
573
- receipts: getFolioDocumentOperationReceipts(parsedBatch.operations, result.applied),
584
+ receipts: getFolioDocumentOperationReceiptsForStory({
585
+ operations: parsedBatch.operations,
586
+ applied: result.applied,
587
+ story
588
+ }),
574
589
  undoHandle: result.applied.length > 0 && createUndoHandle !== void 0 ? createUndoHandle() : null
575
590
  };
576
591
  };
@@ -3,10 +3,10 @@ import { parseBookmarkEnd, parseBookmarkStart } from "./bookmarkParser.js";
3
3
  import { parseSdtProperties } from "./sdtProperties.js";
4
4
  import { parseParagraph } from "./paragraphParser.js";
5
5
  import { appendBookmarkMarkerToLastParagraphInBlocks, prependBookmarkMarkersToFirstParagraphInBlocks } from "./bookmarkPlacement.js";
6
+ import { convertBulletToUnicode } from "./bulletMarkers.js";
7
+ import { padDecimal } from "./numberingParser.js";
6
8
  import { enrichParagraphTextBoxes } from "./paragraphTextBoxEnrichment.js";
7
9
  import { parseTable } from "./tableParser.js";
8
- import { padDecimal } from "./numberingParser.js";
9
- import { convertBulletToUnicode } from "./bulletMarkers.js";
10
10
  //#region src/docx/blockContentParser.ts
11
11
  const toRoman = (numParam) => {
12
12
  let num = numParam;
@@ -49,23 +49,42 @@ const formatNumber = (value, numFmt) => {
49
49
  case "lowerRoman": return toRoman(value).toLowerCase();
50
50
  case "upperRoman": return toRoman(value);
51
51
  case "bullet": return "•";
52
+ case "none": return "";
52
53
  default: return String(value);
53
54
  }
54
55
  };
55
- const computeListMarker = (paragraph, numbering, listCounters, abstractCounters) => {
56
+ const computeListMarker = (paragraph, { numbering, listCounters, abstractCounters, restartedNumIds, previousList }) => {
56
57
  const listRendering = paragraph.listRendering;
57
- if (!listRendering || !numbering) return;
58
+ if (!listRendering || !numbering) {
59
+ previousList.abstractNumId = null;
60
+ previousList.fromStyle = false;
61
+ previousList.numId = null;
62
+ return;
63
+ }
58
64
  const { numId, level } = listRendering;
59
- if (numId === 0) return;
60
- if (!listCounters.has(numId)) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
65
+ if (numId === 0) {
66
+ previousList.abstractNumId = null;
67
+ previousList.fromStyle = false;
68
+ previousList.numId = null;
69
+ return;
70
+ }
71
+ const firstEncounter = !listCounters.has(numId);
72
+ if (firstEncounter) listCounters.set(numId, Array.from({ length: 9 }).fill(NaN));
61
73
  const counters = listCounters.get(numId);
62
74
  if (!counters) return;
63
75
  const abstractNumId = numbering.getAbstractNumId(numId);
64
76
  const styleNumbering = paragraph.formatting?.numPrFromStyle;
65
- if (abstractNumId !== null && styleNumbering) {
77
+ let resumedAbstractCounters;
78
+ const resumesRestartedInstance = firstEncounter && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.numId !== null && previousList.numId !== numId && restartedNumIds.has(previousList.numId);
79
+ const resumesStyleInstance = firstEncounter && !styleNumbering && listRendering.startOverride === void 0 && abstractNumId !== null && previousList.abstractNumId === abstractNumId && previousList.fromStyle === true;
80
+ if (abstractNumId !== null && (styleNumbering || resumesRestartedInstance || resumesStyleInstance)) {
66
81
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
67
- if (latestAbstractCounters) for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
82
+ if (latestAbstractCounters) {
83
+ for (let i = 0; i < counters.length; i += 1) counters[i] = latestAbstractCounters[i] ?? NaN;
84
+ resumedAbstractCounters = latestAbstractCounters;
85
+ }
68
86
  }
87
+ if (listRendering.startOverride !== void 0 || resumesRestartedInstance || previousList.numId === numId && restartedNumIds.has(numId)) restartedNumIds.add(numId);
69
88
  if (abstractNumId !== null && level > 0) {
70
89
  const latestAbstractCounters = abstractCounters.get(abstractNumId);
71
90
  if (counters.slice(0, level).every(Number.isNaN)) for (let i = 0; i < level; i += 1) {
@@ -81,10 +100,22 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
81
100
  const childCounter = counters[level + 1];
82
101
  counters[level + 1] = (childCounter === void 0 || Number.isNaN(childCounter) ? 0 : childCounter) + childAdvances;
83
102
  }
84
- if (abstractNumId !== null) abstractCounters.set(abstractNumId, [...counters]);
103
+ if (abstractNumId !== null) {
104
+ if (resumedAbstractCounters) for (const [otherNumId, otherCounters] of listCounters) {
105
+ if (otherNumId === numId || numbering.getAbstractNumId(otherNumId) !== abstractNumId || !otherCounters.every((value, index) => Object.is(value, resumedAbstractCounters[index]))) continue;
106
+ for (let i = 0; i < otherCounters.length; i += 1) otherCounters[i] = counters[i] ?? NaN;
107
+ }
108
+ abstractCounters.set(abstractNumId, [...counters]);
109
+ }
110
+ previousList.abstractNumId = abstractNumId;
111
+ previousList.fromStyle = Boolean(styleNumbering);
112
+ previousList.numId = numId;
85
113
  const pattern = listRendering.marker;
86
114
  if (listRendering.isBullet) {
87
115
  listRendering.marker = convertBulletToUnicode(pattern || "");
116
+ previousList.abstractNumId = null;
117
+ previousList.fromStyle = false;
118
+ previousList.numId = null;
88
119
  return;
89
120
  }
90
121
  let computedMarker = pattern;
@@ -103,6 +134,12 @@ const computeListMarker = (paragraph, numbering, listCounters, abstractCounters)
103
134
  const parseBlockContent = (parent, styles, theme, numbering, rels, media, options) => parseBlockContentWithState(parent, styles, theme, numbering, rels, media, {
104
135
  listCounters: /* @__PURE__ */ new Map(),
105
136
  abstractCounters: /* @__PURE__ */ new Map(),
137
+ restartedNumIds: /* @__PURE__ */ new Set(),
138
+ previousList: {
139
+ abstractNumId: null,
140
+ fromStyle: false,
141
+ numId: null
142
+ },
106
143
  options: {
107
144
  ...options,
108
145
  rootXmlns: mergeXmlnsDeclarations(options?.rootXmlns ?? {}, parent)
@@ -118,7 +155,13 @@ const parseBlockContentWithState = (parent, styles, theme, numbering, rels, medi
118
155
  const paragraph = parseParagraph(child, styles, theme, numbering, rels, media, state.options);
119
156
  prependPendingBookmarkMarkers(paragraph, pendingBookmarkMarkers);
120
157
  enrichParagraphTextBoxes(paragraph, child, styles, theme, numbering, rels, media);
121
- computeListMarker(paragraph, numbering, state.listCounters, state.abstractCounters);
158
+ computeListMarker(paragraph, {
159
+ numbering,
160
+ listCounters: state.listCounters,
161
+ abstractCounters: state.abstractCounters,
162
+ restartedNumIds: state.restartedNumIds,
163
+ previousList: state.previousList
164
+ });
122
165
  content.push(paragraph);
123
166
  continue;
124
167
  }
@@ -75,7 +75,7 @@ function parseComments(commentsXml, styles, theme, rels, media, commentsExtensib
75
75
  for (const child of children) {
76
76
  if ((child.name?.replace(/^.*:/u, "") ?? "") !== "comment") continue;
77
77
  const id = Number.parseInt(getAttribute(child, "w", "id") ?? "0", 10);
78
- const author = getAttribute(child, "w", "author") ?? "Unknown";
78
+ const author = getAttribute(child, "w", "author")?.trim() || "Unknown";
79
79
  const rawInitials = getAttribute(child, "w", "initials");
80
80
  const initials = rawInitials !== null ? String(rawInitials) : void 0;
81
81
  const rawDate = getAttribute(child, "w", "date");
@@ -2,11 +2,39 @@ import { document_d_exports } from "../types/document.js";
2
2
 
3
3
  //#region src/docx/compatibility.d.ts
4
4
  type DocxCompatibilityReason = "opaqueDrawing";
5
+ type FolioDocxCompatibilityHost = "browser" | "server" | "unknown";
6
+ type FolioDocxCompatibilityProfile = document_d_exports.DocxConformanceClass;
7
+ type DocxCompatibilityContext = {
8
+ host: FolioDocxCompatibilityHost;
9
+ profile: FolioDocxCompatibilityProfile;
10
+ };
11
+ type InspectDocxCompatibilityOptions = Partial<DocxCompatibilityContext>;
12
+ type DocxCompatibilityPart = {
13
+ type: "document";
14
+ } | {
15
+ type: "header" | "footer";
16
+ relationshipId: string;
17
+ } | {
18
+ type: "footnote" | "endnote";
19
+ id: number;
20
+ };
21
+ type DocxCompatibilityLocation = {
22
+ part: DocxCompatibilityPart;
23
+ path: string;
24
+ blockId?: string;
25
+ };
26
+ type DocxCompatibilityIssue = {
27
+ code: DocxCompatibilityReason;
28
+ location: DocxCompatibilityLocation;
29
+ };
5
30
  type DocxCompatibility = {
31
+ schemaVersion: 1;
32
+ context: DocxCompatibilityContext;
6
33
  canSafelyEdit: boolean;
34
+ issues: DocxCompatibilityIssue[];
7
35
  reasons: DocxCompatibilityReason[];
8
36
  unsupportedContentCount: number;
9
37
  };
10
- declare function inspectDocxCompatibility(doc: document_d_exports.Document): DocxCompatibility;
38
+ declare const inspectDocxCompatibility: (doc: document_d_exports.Document, options?: InspectDocxCompatibilityOptions) => DocxCompatibility;
11
39
  //#endregion
12
- export { DocxCompatibility, DocxCompatibilityReason, inspectDocxCompatibility };
40
+ export { DocxCompatibility, DocxCompatibilityContext, DocxCompatibilityIssue, DocxCompatibilityLocation, DocxCompatibilityPart, DocxCompatibilityReason, FolioDocxCompatibilityHost, FolioDocxCompatibilityProfile, InspectDocxCompatibilityOptions, inspectDocxCompatibility };
@@ -1,77 +1,156 @@
1
+ import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
1
2
  //#region src/docx/compatibility.ts
2
- const COMPATIBLE_DOCX = {
3
- canSafelyEdit: true,
4
- reasons: [],
5
- unsupportedContentCount: 0
6
- };
7
- function inspectDocxCompatibility(doc) {
3
+ const resolveCompatibilityContext = (doc, options) => ({
4
+ host: options.host ?? "unknown",
5
+ profile: options.profile ?? doc.package.conformanceClass ?? DOCX_CONFORMANCE_CLASSES.UNKNOWN
6
+ });
7
+ const inspectDocxCompatibility = (doc, options = {}) => {
8
+ const context = resolveCompatibilityContext(doc, options);
8
9
  const reasons = /* @__PURE__ */ new Set();
9
- let unsupportedContentCount = 0;
10
- const record = (reason) => {
11
- reasons.add(reason);
12
- unsupportedContentCount += 1;
10
+ const issues = [];
11
+ const record = (location) => {
12
+ const code = "opaqueDrawing";
13
+ reasons.add(code);
14
+ issues.push({
15
+ code,
16
+ location
17
+ });
13
18
  };
14
- inspectBlocks(doc.package.document.content, record);
15
- for (const header of doc.package.headers?.values() ?? []) inspectHeaderFooter(header, record);
16
- for (const footer of doc.package.footers?.values() ?? []) inspectHeaderFooter(footer, record);
17
- for (const footnote of doc.package.footnotes ?? []) inspectBlocks(footnote.content, record);
18
- for (const endnote of doc.package.endnotes ?? []) inspectBlocks(endnote.content, record);
19
- if (unsupportedContentCount === 0) return COMPATIBLE_DOCX;
19
+ inspectBlocks(doc.package.document.content, {
20
+ part: { type: "document" },
21
+ path: "package.document.content",
22
+ record
23
+ });
24
+ for (const [relationshipId, header] of doc.package.headers?.entries() ?? []) inspectHeaderFooter(header, {
25
+ part: {
26
+ type: "header",
27
+ relationshipId
28
+ },
29
+ path: `package.headers.get(${JSON.stringify(relationshipId)}).content`,
30
+ record
31
+ });
32
+ for (const [relationshipId, footer] of doc.package.footers?.entries() ?? []) inspectHeaderFooter(footer, {
33
+ part: {
34
+ type: "footer",
35
+ relationshipId
36
+ },
37
+ path: `package.footers.get(${JSON.stringify(relationshipId)}).content`,
38
+ record
39
+ });
40
+ for (const footnote of doc.package.footnotes ?? []) inspectBlocks(footnote.content, {
41
+ part: {
42
+ type: "footnote",
43
+ id: footnote.id
44
+ },
45
+ path: `package.footnotes[id=${footnote.id}].content`,
46
+ record
47
+ });
48
+ for (const endnote of doc.package.endnotes ?? []) inspectBlocks(endnote.content, {
49
+ part: {
50
+ type: "endnote",
51
+ id: endnote.id
52
+ },
53
+ path: `package.endnotes[id=${endnote.id}].content`,
54
+ record
55
+ });
20
56
  return {
21
- canSafelyEdit: false,
57
+ schemaVersion: 1,
58
+ context,
59
+ canSafelyEdit: issues.length === 0,
60
+ issues,
22
61
  reasons: Array.from(reasons),
23
- unsupportedContentCount
62
+ unsupportedContentCount: issues.length
24
63
  };
25
- }
26
- function inspectBlocks(blocks, record) {
27
- for (const block of blocks) {
64
+ };
65
+ function inspectBlocks(blocks, context) {
66
+ for (const [blockIndex, block] of blocks.entries()) {
67
+ const blockPath = `${context.path}[${blockIndex}]`;
28
68
  if (block.type === "paragraph") {
29
- inspectParagraphContent(block.content, record);
69
+ inspectParagraphContent(block.content, {
70
+ ...block.paraId === void 0 ? {} : { blockId: block.paraId },
71
+ part: context.part,
72
+ path: `${blockPath}.content`,
73
+ record: context.record
74
+ });
30
75
  continue;
31
76
  }
32
77
  if (block.type === "table") {
33
- for (const row of block.rows) for (const cell of row.cells) inspectBlocks(cell.content, record);
78
+ for (const [rowIndex, row] of block.rows.entries()) for (const [cellIndex, cell] of row.cells.entries()) inspectBlocks(cell.content, {
79
+ part: context.part,
80
+ path: `${blockPath}.rows[${rowIndex}].cells[${cellIndex}].content`,
81
+ record: context.record
82
+ });
34
83
  continue;
35
84
  }
36
- inspectBlocks(block.content, record);
85
+ inspectBlocks(block.content, {
86
+ part: context.part,
87
+ path: `${blockPath}.content`,
88
+ record: context.record
89
+ });
37
90
  }
38
91
  }
39
- function inspectHeaderFooter(headerFooter, record) {
40
- inspectBlocks(headerFooter.content, record);
92
+ function inspectHeaderFooter(headerFooter, context) {
93
+ inspectBlocks(headerFooter.content, context);
41
94
  }
42
- function inspectParagraphContent(content, record) {
43
- for (const item of content) {
95
+ function inspectParagraphContent(content, context) {
96
+ for (const [itemIndex, item] of content.entries()) {
97
+ const itemContext = {
98
+ ...context,
99
+ path: `${context.path}[${itemIndex}]`
100
+ };
44
101
  if (item.type === "run") {
45
- inspectRun(item, record);
102
+ inspectRun(item, itemContext);
46
103
  continue;
47
104
  }
48
105
  if (item.type === "hyperlink") {
49
- inspectHyperlink(item, record);
106
+ inspectHyperlink(item, itemContext);
50
107
  continue;
51
108
  }
52
109
  if (item.type === "inlineSdt") {
53
- inspectParagraphContent(item.content, record);
110
+ inspectParagraphContent(item.content, {
111
+ ...itemContext,
112
+ path: `${itemContext.path}.content`
113
+ });
54
114
  continue;
55
115
  }
56
116
  if (item.type === "insertion" || item.type === "deletion" || item.type === "moveFrom" || item.type === "moveTo") {
57
- inspectParagraphContent(item.content, record);
117
+ inspectParagraphContent(item.content, {
118
+ ...itemContext,
119
+ path: `${itemContext.path}.content`
120
+ });
58
121
  continue;
59
122
  }
60
123
  if (item.type === "simpleField") {
61
- inspectParagraphContent(item.content, record);
124
+ inspectParagraphContent(item.content, {
125
+ ...itemContext,
126
+ path: `${itemContext.path}.content`
127
+ });
62
128
  continue;
63
129
  }
64
130
  if (item.type === "complexField") {
65
- for (const run of item.fieldCode) inspectRun(run, record);
66
- for (const run of item.fieldResult) inspectRun(run, record);
131
+ for (const [runIndex, run] of item.fieldCode.entries()) inspectRun(run, {
132
+ ...itemContext,
133
+ path: `${itemContext.path}.fieldCode[${runIndex}]`
134
+ });
135
+ for (const [runIndex, run] of item.fieldResult.entries()) inspectRun(run, {
136
+ ...itemContext,
137
+ path: `${itemContext.path}.fieldResult[${runIndex}]`
138
+ });
67
139
  }
68
140
  }
69
141
  }
70
- function inspectHyperlink(hyperlink, record) {
71
- for (const child of hyperlink.children) if (child.type === "run") inspectRun(child, record);
142
+ function inspectHyperlink(hyperlink, context) {
143
+ for (const [childIndex, child] of hyperlink.children.entries()) if (child.type === "run") inspectRun(child, {
144
+ ...context,
145
+ path: `${context.path}.children[${childIndex}]`
146
+ });
72
147
  }
73
- function inspectRun(run, record) {
74
- for (const content of run.content) if (content.type === "drawing" && content.rawXml) record("opaqueDrawing");
148
+ function inspectRun(run, context) {
149
+ for (const [contentIndex, content] of run.content.entries()) if (content.type === "drawing" && content.rawXml) context.record({
150
+ ...context.blockId === void 0 ? {} : { blockId: context.blockId },
151
+ part: context.part,
152
+ path: `${context.path}.content[${contentIndex}]`
153
+ });
75
154
  }
76
155
  //#endregion
77
156
  export { inspectDocxCompatibility };
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+
3
+ //#region src/docx/conformance.d.ts
4
+ declare const detectDocxConformanceClass: (documentXml: string | null) => document_d_exports.DocxConformanceClass;
5
+ //#endregion
6
+ export { detectDocxConformanceClass };
@@ -0,0 +1,18 @@
1
+ import { getLocalName, getNamespacePrefix, parseXmlDocument } from "./xmlParser.js";
2
+ import { DOCX_CONFORMANCE_CLASSES } from "@stll/docx-core/model";
3
+ //#region src/docx/conformance.ts
4
+ const STRICT_MAIN_NAMESPACE = "http://purl.oclc.org/ooxml/wordprocessingml/main";
5
+ const TRANSITIONAL_MAIN_NAMESPACE = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
6
+ const detectDocxConformanceClass = (documentXml) => {
7
+ if (documentXml === null) return DOCX_CONFORMANCE_CLASSES.UNKNOWN;
8
+ const root = parseXmlDocument(documentXml);
9
+ if (root === null || getLocalName(root.name ?? "") !== "document") return DOCX_CONFORMANCE_CLASSES.UNKNOWN;
10
+ const prefix = getNamespacePrefix(root.name ?? "");
11
+ const namespaceAttribute = prefix === null ? "xmlns" : `xmlns:${prefix}`;
12
+ const namespace = root.attributes?.[namespaceAttribute];
13
+ if (namespace === STRICT_MAIN_NAMESPACE) return DOCX_CONFORMANCE_CLASSES.STRICT;
14
+ if (namespace === TRANSITIONAL_MAIN_NAMESPACE) return DOCX_CONFORMANCE_CLASSES.TRANSITIONAL;
15
+ return DOCX_CONFORMANCE_CLASSES.UNKNOWN;
16
+ };
17
+ //#endregion
18
+ export { detectDocxConformanceClass };
@@ -1,5 +1,5 @@
1
- import { bytesEqual, joinBytes, padToBlock, passwordToUtf16Le, toArrayBuffer, writeUint32Le } from "./cryptoBytes.js";
2
1
  import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
2
+ import { bytesEqual, joinBytes, padToBlock, passwordToUtf16Le, toArrayBuffer, writeUint32Le } from "./cryptoBytes.js";
3
3
  //#region src/docx/encryption/agileDecryption.ts
4
4
  /**
5
5
  * Agile Encryption decryption (Office 2010+) using Web Crypto.
@@ -1,5 +1,5 @@
1
- import { decodeBase64 } from "./cryptoBytes.js";
2
1
  import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
2
+ import { decodeBase64 } from "./cryptoBytes.js";
3
3
  //#region src/docx/encryption/encryptionInfo.ts
4
4
  /**
5
5
  * Parse the EncryptionInfo stream for Agile Encryption (Office 2010+).
@@ -1,5 +1,5 @@
1
- import { toArrayBuffer } from "./cryptoBytes.js";
2
1
  import { DOCX_ENCRYPTION_ERROR_CODES, DocxEncryptionError } from "./errors.js";
2
+ import { toArrayBuffer } from "./cryptoBytes.js";
3
3
  import { decryptAgileEncryptedPackage } from "./agileDecryption.js";
4
4
  import { readEncryptedPackageStreams } from "./compoundFile.js";
5
5
  import { DOCX_CONTAINER_TYPES, detectDocxContainerType } from "./containerFormat.js";
@@ -0,0 +1,29 @@
1
+ //#region src/docx/ensureParaIds.d.ts
2
+ declare const EnsureParaIdsError_base: import("better-result").TaggedErrorClass<"EnsureParaIdsError", {
3
+ message: string;
4
+ cause?: unknown;
5
+ }>;
6
+ /** A malformed or unsupported package prevented paragraph-ID normalization. */
7
+ declare class EnsureParaIdsError extends EnsureParaIdsError_base {}
8
+ /** Counts and normalized bytes returned by {@link ensureParaIds}. */
9
+ type EnsureParaIdsResult = {
10
+ /** The normalized `.docx`; the input bytes verbatim when `alreadyComplete`. */docx: Uint8Array; /** Paragraphs that received a paraId (missing or all-zero before). */
11
+ assigned: number; /** Duplicate paraIds reassigned (the first occurrence keeps the id). */
12
+ deduplicated: number; /** True when the input already had full, unique coverage. */
13
+ alreadyComplete: boolean;
14
+ };
15
+ /** Controls mutation of package metadata that has security implications. */
16
+ type EnsureParaIdsOptions = {
17
+ /**
18
+ * Allow normalization to invalidate existing OPC digital signatures.
19
+ * Callers must warn the user before opting in.
20
+ */
21
+ allowSignedPackageMutation?: boolean;
22
+ };
23
+ /**
24
+ * Backfill `w14:paraId` on every paragraph of a `.docx` buffer. All package,
25
+ * XML, and compression failures are surfaced as {@link EnsureParaIdsError}.
26
+ */
27
+ declare const ensureParaIds: (docx: Uint8Array | ArrayBuffer, options?: EnsureParaIdsOptions) => Promise<EnsureParaIdsResult>;
28
+ //#endregion
29
+ export { EnsureParaIdsError, EnsureParaIdsOptions, EnsureParaIdsResult, ensureParaIds };