@docx-editor.dev/core 2.35.0-canary.20261104032723.sha-43c2012 → 2.35.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 (115) hide show
  1. package/README.md +9 -14
  2. package/dist/automation.cjs +1 -1
  3. package/dist/automation.d.ts +16 -11
  4. package/dist/automation.js +1 -1
  5. package/dist/binding.cjs +1 -1
  6. package/dist/binding.d.ts +12 -12
  7. package/dist/binding.js +1 -1
  8. package/dist/{chrome-print-CK3v5NhC.d.ts → chrome-print-H0uSxdg3.d.ts} +70 -17
  9. package/dist/{chunk-AMOFXSEO.cjs → chunk-2C75IWUN.cjs} +1 -1
  10. package/dist/chunk-2NFQ7OXY.cjs +1 -0
  11. package/dist/chunk-2V3V57SC.js +3 -0
  12. package/dist/chunk-32YOHLEK.cjs +32 -0
  13. package/dist/chunk-4NISR27M.cjs +31 -0
  14. package/dist/chunk-5ZE7ROGP.js +31 -0
  15. package/dist/{chunk-LZ64O473.js → chunk-66ZAVRGB.js} +1 -1
  16. package/dist/{chunk-A5GMOQWD.js → chunk-6767S7UF.js} +1 -1
  17. package/dist/chunk-6BZINLEN.cjs +1 -0
  18. package/dist/{chunk-EBIMIIC3.js → chunk-AVGSD7FB.js} +5 -5
  19. package/dist/{chunk-TZTKEPCC.cjs → chunk-CBJMRC4L.cjs} +1 -1
  20. package/dist/chunk-DCILO4XD.cjs +236 -0
  21. package/dist/{chunk-FBGZOGLU.cjs → chunk-DSIDCA3V.cjs} +4 -4
  22. package/dist/chunk-FLEFHRJG.cjs +5 -0
  23. package/dist/chunk-FMSXJ22B.js +2 -0
  24. package/dist/{chunk-7ACXKFKO.js → chunk-I4OZW2NO.js} +1 -1
  25. package/dist/chunk-JGE2O6IF.cjs +3 -0
  26. package/dist/chunk-K2BBJDM7.js +32 -0
  27. package/dist/chunk-LMASPHYS.js +1 -0
  28. package/dist/chunk-NYZP5NM3.js +4 -0
  29. package/dist/chunk-P3XUE72N.js +236 -0
  30. package/dist/{chunk-JZ4LO3ZZ.js → chunk-Q5YVLUGG.js} +1 -1
  31. package/dist/chunk-QALWAAPL.cjs +4 -0
  32. package/dist/{chunk-ELR6HXOO.js → chunk-QDUMZYTX.js} +1 -1
  33. package/dist/chunk-QLANEOTJ.cjs +7 -0
  34. package/dist/{chunk-4NPNKO5U.cjs → chunk-SJV6WCTP.cjs} +1 -1
  35. package/dist/{chunk-I4WSXFY6.js → chunk-TMCMXIBT.js} +4 -4
  36. package/dist/{chunk-GTICLPKS.cjs → chunk-TY5CE2JR.cjs} +1 -1
  37. package/dist/chunk-UAUB5YV6.js +7 -0
  38. package/dist/{chunk-Q4MUV5KX.cjs → chunk-WWZWT26M.cjs} +1 -1
  39. package/dist/chunk-Y3JSN52G.cjs +2 -0
  40. package/dist/chunk-YD3KSZJM.js +1 -0
  41. package/dist/collaboration/replication.cjs +1 -1
  42. package/dist/collaboration/replication.d.ts +3 -3
  43. package/dist/collaboration/replication.js +1 -1
  44. package/dist/collaboration.d.ts +2 -2
  45. package/dist/contracts/document.d.ts +2 -2
  46. package/dist/contracts/editor.d.ts +8 -8
  47. package/dist/contracts/modules.cjs +1 -1
  48. package/dist/contracts/modules.d.ts +34 -13
  49. package/dist/contracts/modules.js +1 -1
  50. package/dist/contracts/types.d.ts +3 -3
  51. package/dist/{custom-node-writes-BC3z8NwO.d.ts → custom-node-writes-Cj0itq58.d.ts} +2 -2
  52. package/dist/{document-protection-8hXPdn5U.d.ts → document-protection-CDLoYQI3.d.ts} +1 -1
  53. package/dist/{drawing-graphic-kind-_5u2E4ee.d.ts → drawing-graphic-kind-8zt6pugF.d.ts} +2 -2
  54. package/dist/{drawing-preview-package-JyAqL6OV.d.ts → drawing-preview-package-8o9LSfzP.d.ts} +1 -1
  55. package/dist/{editor-BGCdE3tB.d.ts → editor-CujQcn1z.d.ts} +20 -10
  56. package/dist/editor.cjs +1 -1
  57. package/dist/editor.css +3 -3
  58. package/dist/editor.d.ts +52 -29
  59. package/dist/editor.js +1 -1
  60. package/dist/export.cjs +2 -2
  61. package/dist/export.d.ts +6 -6
  62. package/dist/export.js +1 -1
  63. package/dist/index.cjs +1 -1
  64. package/dist/index.d.ts +18 -18
  65. package/dist/index.js +1 -1
  66. package/dist/{layout-shaping-D2qNes9-.d.ts → layout-shaping-ZjLRz_qa.d.ts} +1 -1
  67. package/dist/layout.cjs +1 -1
  68. package/dist/layout.d.ts +25 -21
  69. package/dist/layout.js +1 -1
  70. package/dist/{note-properties-DGPzKUuA.d.ts → note-properties-BRpskBeh.d.ts} +1 -1
  71. package/dist/output.cjs +1 -1
  72. package/dist/output.d.ts +10 -10
  73. package/dist/output.js +1 -1
  74. package/dist/{protocol-0y3iaYj9.d.ts → protocol-GVVQQwCX.d.ts} +13 -2
  75. package/dist/{review-paragraph-order-DeQp2_iu.d.ts → review-paragraph-order-aEHnQTt7.d.ts} +5 -2
  76. package/dist/{revision-projection-CrAv-HP6.d.ts → revision-projection-vU_UiTLx.d.ts} +1 -1
  77. package/dist/{semantic-cell-selection-CeG_qqDV.d.ts → semantic-cell-selection-CaRIISu-.d.ts} +2 -2
  78. package/dist/{semantic-interaction-DcE_ZsiD.d.ts → semantic-interaction-LJtUJJS5.d.ts} +1 -1
  79. package/dist/{semantic-paint-DfeN6UQF.d.ts → semantic-paint-2LBYlQcn.d.ts} +11 -4
  80. package/dist/{semantic-record-queries-qwd9Ict6.d.ts → semantic-record-queries-C_YK2gCz.d.ts} +1 -1
  81. package/dist/{semantic-records-thL1rcmG.d.ts → semantic-records-AU2pIYRp.d.ts} +3 -3
  82. package/dist/store.cjs +1 -1
  83. package/dist/store.d.ts +1664 -1664
  84. package/dist/store.js +1 -1
  85. package/dist/{tree-op-types-CGlwCQXg.d.ts → tree-op-types-DmrePCMo.d.ts} +320 -11
  86. package/dist/{tree-package-store-D0_BOm-r.d.ts → tree-package-fragment-DXZ2B7x0.d.ts} +702 -691
  87. package/dist/{tree-session-contract-DkThhHjy.d.ts → tree-session-contract-D3e-5YNq.d.ts} +19 -4
  88. package/dist/{unsupported-elements-qj0sXi2s.d.ts → unsupported-elements-9Pz4I_F6.d.ts} +1 -1
  89. package/docs/accept-reject-without-scrolling.md +31 -48
  90. package/docs/comment-on-found-text.md +47 -23
  91. package/docs/custom-review-cards.md +50 -34
  92. package/docs/highlight-ranges.md +26 -6
  93. package/docs/index.md +21 -21
  94. package/docs/propose-rewrite.md +87 -138
  95. package/docs/tracked-replacement.md +41 -20
  96. package/package.json +2 -2
  97. package/dist/chunk-4C7JUJDX.cjs +0 -1
  98. package/dist/chunk-4HENIAFG.js +0 -1
  99. package/dist/chunk-6IYKYYWF.js +0 -32
  100. package/dist/chunk-CDAFCPT6.cjs +0 -3
  101. package/dist/chunk-CU6QPXKD.js +0 -2
  102. package/dist/chunk-EMITIPLN.js +0 -4
  103. package/dist/chunk-IGB66DIX.cjs +0 -30
  104. package/dist/chunk-JQPTNBLT.js +0 -236
  105. package/dist/chunk-JVWRILO3.cjs +0 -4
  106. package/dist/chunk-KOYEBRLV.js +0 -1
  107. package/dist/chunk-KZZPOZOF.cjs +0 -2
  108. package/dist/chunk-NPWMZIPA.cjs +0 -32
  109. package/dist/chunk-NS2QCOAS.js +0 -7
  110. package/dist/chunk-O2PGRL5D.js +0 -3
  111. package/dist/chunk-TFJUIP4X.cjs +0 -236
  112. package/dist/chunk-V3N4SOAK.cjs +0 -1
  113. package/dist/chunk-V6FOPP2H.js +0 -30
  114. package/dist/chunk-XFEIUKOS.cjs +0 -7
  115. package/dist/chunk-ZF4N3N3L.cjs +0 -5
package/dist/store.d.ts CHANGED
@@ -12,18 +12,18 @@
12
12
  * @packageDocumentation
13
13
  * @public
14
14
  */
15
- import { bd as OoxmlAttribute, O as OoxmlPart, a4 as OoxmlNode, be as OoxmlInvariantResult, av as OoxmlElement, bf as LegacyTextArtProjection, aW as VectorShapeComponent, bg as ChartPartResolver, bh as ChartexPartResolver, bi as ShapeSchemeColorResolver, bj as ShapeEffectList, b0 as PictureFrameEffects, a$ as VectorShapeProjection, bk as GroupTextboxProjection, bl as MetafileImageMime, aL as OoxmlParagraphNode, bm as OoxmlFldCharNode, bn as OoxmlFldSimpleNode, bo as OoxmlInstrTextNode, au as TreeDocOp, aF as TreeOpRejection, bp as TreeOpResult, a5 as OoxmlPackage, bq as RelationshipRecord, ax as OoxmlGenericElementNode, br as OoxmlHardBreakNode, bs as ShapeColor, bt as ResourceLimits, aC as OoxmlProperty, V as RevisionSiteRefusal, f as ReviewRange, R as ReviewModelInput, a as ReviewItem, C as CommentRecord, e as ReviewPosition, bu as CommentThreadState, c as ReviewCommentItem, b as ReviewRevisionItem, aJ as DrawingTreeDocOp, aK as ImpactClass, ag as ImageWrapTarget } from './tree-op-types-CGlwCQXg.js';
16
- export { bv as ACCEPTED_PARAGRAPH_PROPERTIES, bw as ACCEPTED_RUN_PROPERTIES, bx as BookmarkOpPosition, by as CHARTEX_GRAPHIC_DATA_URI, bz as CHARTEX_NAMESPACE_URI, bA as CHART_GRAPHIC_DATA_URI, bB as CHART_NAMESPACE_URI, bC as CHART_STYLE_NAMESPACE_URI, bD as CONTENT_CONTROL_ID_MAX, bE as CONTENT_CONTROL_PROPERTY_ORDER, bF as ChartPart, bG as ChartThemeFaces, bH as ChartexPart, bI as CommentAnchor, bJ as CommentPosition, bK as CommitTextFormFieldOp, bL as ContentControlCheckbox, bM as ContentControlCheckboxState, bN as ContentControlDataBinding, bO as ContentControlDateFormat, bP as ContentControlEntry, bQ as ContentControlKind, bR as ContentControlLevel, bS as ContentControlListItem, bT as ContentControlLock, bU as ContentControlProperties, bV as ContentTypeError, bW as ContentTypeIndex, bX as ContentTypeRecords, bY as CreateImageResourceCacheOptions, b6 as DEFAULT_IMAGE_RESOURCE_LIMITS, bZ as DEFAULT_LIMITS, b_ as DEFAULT_OOXML_PACKAGE_LIMITS, b$ as DEFAULT_ZIP_LIMITS, c0 as DIAGRAM_NAMESPACE_URI, c1 as DRAWINGML_MAIN_NAMESPACE_URI, c2 as DefaultRecord, c3 as DeleteBookmarkOp, c4 as DeleteTextOp, c5 as DiagramPartResolver, c6 as DiagramParts, aY as DrawingImageEffects, a8 as DrawingKind, a9 as DrawingLocks, aa as DrawingPositionInput, c7 as HARD_CEILINGS, c8 as HYPERLINK_RELATIONSHIP_TYPE, c9 as HyperlinkKind, ca as HyperlinkTarget, cb as IMAGE_RELATIONSHIP_TYPE, cc as IMAGE_RESOURCE_HARD_CEILINGS, b7 as IMAGE_WRAP_TARGETS, ad as ImageDecodePort, cd as ImageRelationshipResolution, ae as ImageResourceLimits, ce as ImageResourceLookup, af as ImageResourceState, cf as IndexResult, cg as InsertBookmarkOp, ch as InsertBuildingBlockOp, ci as InsertComplexFieldOp, cj as InsertEquationOp, ck as InsertPageFieldOp, cl as InsertParagraphBesideTableOp, cm as InsertTableOp, cn as InsertTextOp, co as LegacyCheckboxFieldRange, cp as MAX_CONTENT_CONTROLS_PER_PART, cq as MAX_CONTENT_CONTROL_NESTING, cr as MAX_SVG_SNIFF_BYTES, cs as MoveTableOp, ct as NameRejection, cu as NameResult, cv as OFFICE_RELATIONSHIP_NAMESPACE_URI, cw as OOXML_NODE_IDENTITY_RULES, cx as OoxmlBodyNode, cy as OoxmlBookmarkEndNode, cz as OoxmlBookmarkStartNode, cA as OoxmlContentControlCalendarNode, cB as OoxmlContentControlCheckboxNode, cC as OoxmlContentControlCheckedNode, cD as OoxmlContentControlCheckedStateNode, cE as OoxmlContentControlComboBoxNode, ay as OoxmlContentControlContentNode, cF as OoxmlContentControlDataBindingNode, cG as OoxmlContentControlDateFormatNode, cH as OoxmlContentControlDateNode, cI as OoxmlContentControlDropDownListNode, cJ as OoxmlContentControlEndPropertiesNode, cK as OoxmlContentControlLidNode, cL as OoxmlContentControlListItemNode, aw as OoxmlContentControlNode, cM as OoxmlContentControlPropertiesNode, cN as OoxmlContentControlStoreMappedDataAsNode, cO as OoxmlContentControlTextNode, cP as OoxmlContentControlUncheckedStateNode, aN as OoxmlContinuationSeparatorNode, cQ as OoxmlDocumentNode, aO as OoxmlEndnotesNode, cR as OoxmlExternalTarget, aP as OoxmlFootnotesNode, cS as OoxmlGenericExtensionAttribute, cT as OoxmlHyperlinkNode, aI as OoxmlInvariantIssue, cU as OoxmlInvariantIssueCode, cV as OoxmlKnownNodeAttribute, cW as OoxmlNamespaceBinding, aG as OoxmlNodeId, cX as OoxmlNodeIdentityRules, aM as OoxmlNoteNode, aQ as OoxmlNoteRefNode, aR as OoxmlNoteReferenceNode, az as OoxmlPackageLimits, aA as OoxmlPackageRejection, aD as OoxmlPackageResult, cY as OoxmlParagraphPropertiesNode, cZ as OoxmlPartMetadata, c_ as OoxmlReadRejection, c$ as OoxmlReadResult, d0 as OoxmlRunNode, d1 as OoxmlRunPropertiesNode, aS as OoxmlSeparatorNode, d2 as OoxmlTabNode, d3 as OoxmlTextElementNode, d4 as OoxmlTextNode, d5 as OoxmlWmlValAttribute, d6 as OoxmlXmlSpaceAttribute, d7 as OverrideRecord, aj as PreservedImageMime, d8 as RelationshipError, d9 as RelationshipSetResult, da as RelationshipTargetResolver, al as RenderableImageMime, db as ResolveResult, dc as ResolvedRelationship, d as ReviewCustomItem, g as ReviewRevisionKind, h as RevisionAddress, J as RevisionAuthorEntry, K as RevisionAuthorResult, L as RevisionAuthorSkipReason, M as RevisionBatchEntry, Q as RevisionBatchResult, T as RevisionBatchSkipDetail, U as RevisionBatchSkipReason, dd as SetFieldResultOp, de as SetLegacyCheckboxOp, df as SetLegacyDropdownOp, dg as SetTextFormFieldDefaultOp, dh as ShapeReflection, di as ShapeShadow, aT as SourceCrop, an as SupportedImageMime, dj as TABLE_BORDER_STYLES, dk as TabStopWrite, aU as TableBorderStyle, dl as TargetMode, b4 as TextFormFieldOptions, b3 as TextFormFieldRange, dm as TextFormFieldType, dn as TextboxStoryProjection, dp as TreeDocOpKind, dq as TreeOpEffect, ap as ValidatedImageBytesHandle, dr as ValidatedRasterHeader, ds as VectorGradient, dt as VectorPattern, du as W15_NAMESPACE_URI, dv as WML_NAMESPACE_URI, dw as XMLNS_NAMESPACE_URI, dx as XML_NAMESPACE_URI, dy as XmlLimits, dz as XmlNode, dA as XmlRejection, dB as XmlResult, dC as ZipLimits, dD as ZipReadResult, dE as ZipRejection, dF as allocateContentControlId, dG as asciiFold, dH as buildContentTypeIndex, dI as buildRelationshipSet, dJ as childElements, dK as commentAnchorsOfStory, dL as commentsOfPart, dM as contentControlContentChildren, dN as contentControlContentNodeOf, dO as contentControlEndPropertiesNodeOf, dP as contentControlLevelOf, dQ as contentControlPropertiesNodeOf, dR as contentControlPropertiesOf, dS as contentControlTextOf, dT as contentControlsIn, dU as createImageResourceCache, dV as detectDuplicateNames, dW as directFill, dX as enclosedBookmarkDeletions, dY as extensionKey, dZ as findContentControl, d_ as findElement, d$ as firstReviewRange, e0 as flattenContentControls, e1 as hasAnyComment, e2 as hasBoundedSvgRoot, e3 as hyperlinkAnchorOf, e4 as hyperlinkRelationshipIdOf, e5 as hyperlinkTargetOf, e6 as imageResourceLookupFor, e7 as isContentControlContentNode, e8 as isContentControlNode, e9 as isContentControlWrapper, ea as isContentRevisionKind, eb as isElement, ec as isHyperlinkNode, ed as isRangeMarkerKind, ee as isValidMime, ef as legacyCheckboxFieldsOf, eg as liveDrawingReferenceCount, eh as lockForbidsEdit, ei as lockForbidsRemoval, ej as normalizePartName, ek as orderedContentControlProperties, el as parseContentControlId, em as partNameKey, en as planRevisionAuthorChange, eo as planRevisionBatch, ep as projectDrawing, eq as readOnOffChild, er as readOoxmlPackage, es as readOoxmlPart, et as readXml, eu as readZip, ev as resolveColorNode, ew as resolveContentControlLock, ex as resolveContentType, ey as resolveImageRelationship, b9 as resolveImageResourceLimits, ez as resolveInternalTarget, eA as resolveLimits, eB as resolveRelationship, eC as reviewItemKey, eD as reviewItemRanges, bb as sniffImageMime, eE as textContent, eF as textFormFieldsOf, eG as threadStateOfPart, eH as validateExternalTarget, eI as validateGifHeader, eJ as validateJpegHeader, eK as validatePngHeader, bc as validateRasterHeader, eL as withPart, eM as withoutBookmarkInPart, eN as writeOoxmlPackage, eO as writeOoxmlPackageKeepingRevisionIds, eP as writeZip } from './tree-op-types-CGlwCQXg.js';
17
- import { F as FieldResultsMode, E as EditOptions, c as TreeModelChange, d as TreeDocumentStore, T as TreePackageStore, S as StoryScope, b as HistoryGroup } from './tree-package-store-D0_BOm-r.js';
18
- export { l as AtomicNoteSpan, m as CascadeDeletedNoteReferencesOptions, C as ContentLockPolicy, D as DEFAULT_MAX_EDITABLE_STORY_PARTS, n as DrawingPropertyIdResult, j as FragmentPasteInput, k as FragmentPasteResult, o as HeaderFooterKind, p as HeaderFooterLifecycleImpact, q as HeaderFooterLifecycleOp, s as HeaderFooterLifecycleRejection, t as HeaderFooterLifecycleResult, e as HeaderFooterParts, f as HeaderFooterSectionResolution, u as HeaderFooterSlotMeta, H as HeaderFooterVariant, v as HistoryCaptureKind, M as MAX_FRAGMENT_DECODED_BYTES, w as MAX_NOTES_PER_PART, x as MAX_NOTE_REFERENCE_PARTS, y as MAX_NOTE_REFERENCE_SCAN, z as NOTE_ATOM_CHAR, B as NOTE_CONTINUATION_SEPARATOR_ID, G as NOTE_ID_MAX, J as NOTE_ID_MIN, K as NOTE_SEPARATOR_ID, L as NoteDiagnostic, O as NoteDiagnosticCode, N as NoteKind, P as NoteLifecycleImpact, Q as NoteLifecycleOp, U as NoteLifecycleOptions, V as NoteLifecycleRejection, W as NoteLifecycleResult, X as NoteReferenceHit, Y as NoteReferenceScanBudget, Z as NoteType, _ as OoxmlEditResult, $ as PackageTransactResult, a0 as RemotePackageAttribution, g as SelectionMark, a1 as StoryResolveResult, a as StoryTargetRejection, a2 as TransactionRevisionIds, a3 as TreeDocumentStoreOptions, a4 as TreePackageStoreOptions, a5 as TreeStoryRef, h as TreeTransactOptions, a6 as TreeTransactResult, a7 as TreeTransactionContext, a8 as allocateDrawingPropertyId, a9 as allocateNoteId, aa as applyEdits, ab as applyFragmentPaste, ac as applyHeaderFooterLifecycleOp, ad as applyNoteLifecycleOp, ae as atomicNoteSpansOf, af as cascadeDeletedNoteReferences, ag as collectNodeIds, ah as collectNoteReferences, ai as collectPackageNoteReferences, aj as collectSectionPropertyNodes, ak as createNodeIdAllocator, al as createNoteReferenceScanBudget, am as customMarkFollows, an as diagnoseNoteReferences, ao as findNode, ap as findNoteById, aq as formatNoteScopeId, ar as hasNode, as as insertChildren, at as isContinuationSeparatorNode, au as isEndnotesNode, av as isFootnotesNode, aw as isHeaderFooterLifecycleOp, ax as isNormalNote, ay as isNoteAtomNode, az as isNoteLifecycleOp, aA as isNoteNode, aB as isNoteRefNode, aC as isNoteReferenceNode, aD as isSeparatorNode, aE as normalNoteIds, aF as noteAtomText, aG as noteIdOf, aH as noteKindOf, aI as noteRefKindOf, aJ as noteReferenceKindOf, aK as noteTypeOf, aL as notesOf, aM as notesPartHasId, aN as observeHistoryGroup, aO as parentNodeOf, aP as parseNoteId, aQ as parseNoteScopeId, aR as removeNode, aS as replaceChildren, aT as replaceNode, r as reportHistoryGroup, aU as resolveHeaderFooterParts, aV as resolveHeaderFooterPartsBySection, aW as resolveHeaderFooterResolutionBySection, aX as resolveNotesPart, aY as withBinaryPart, aZ as withEmbeddedImage, a_ as withoutUnreferencedImagePart } from './tree-package-store-D0_BOm-r.js';
19
- import { O as OmmlEquationProjection, a as DrawingSupport, D as DrawingGraphicKind, b as DrawingSupportIssue } from './drawing-graphic-kind-_5u2E4ee.js';
20
- export { e as DEFAULT_OMML_LIMITS, c as DocumentProperties, E as EnsuredHyperlinkRelationship, f as EquationBoxEdges, g as EquationBoxStrikes, h as EquationControlSource, i as EquationExpression, d as HeadlessDocumentRejection, H as HeadlessDocumentView, j as HeadlessThemeFonts, L as LinearMathOmmlResult, k as LinearMathParseResult, l as LinearMathRejection, m as OFFICE_MATH_NAMESPACE_URI, n as OmmlLimits, o as OpenHeadlessDocumentResult, p as authorableHyperlinkTarget, q as ensureHyperlinkRelationship, r as equationExpressionToLinearMath, s as headlessViewOfStore, t as linearMathToOmml, u as openHeadlessDocument, v as parseLinearMath, w as projectOmmlEquation, x as relationshipTargetIn } from './drawing-graphic-kind-_5u2E4ee.js';
21
- import { I as InsertCustomNodeWrite, C as CustomNodeWriteResult } from './custom-node-writes-BC3z8NwO.js';
22
- export { c as BookmarkAnchor, B as BookmarkIndex, d as CustomNodePayloadRead, e as CustomNodePayloadWrite, a as CustomNodeSweepOutcome, f as CustomNodeSweepResult, g as CustomNodeWriteRejection, D as DocumentTrackingSettings, E as EmbeddedFont, F as FontStyleKey, b as ListDefinitionGeometry, L as ListKind, h as ListLevelGeometry, M as MAX_CUSTOM_NODE_LABEL_LENGTH, i as MAX_CUSTOM_NODE_PAYLOAD_LENGTH, N as NO_TRACKING_SETTINGS, R as ReadEmbeddedFontsOptions, j as buildBookmarkIndex, k as customNodePayloadsByControl, l as customNodePayloadsOf, m as deobfuscateFont, n as ensureListDefinition, o as ensureNumberingLevel, p as insertCustomNodeWrite, r as readEmbeddedFonts, q as readTrackingSettings, s as removeCustomNodeWrite, t as sweepCustomNodePayloads } from './custom-node-writes-BC3z8NwO.js';
23
- export { B as BookmarkNameProblem, D as DrawingPreviewPackageLimits, a as DrawingPreviewPackageResult, b as DrawingPreviewTarget, M as MAX_AUTHORED_BOOKMARK_NAME_LENGTH, c as bookmarkNameKey, d as bookmarkNameProblem, e as createDrawingPreviewPackage, i as isHiddenBookmarkName } from './drawing-preview-package-JyAqL6OV.js';
24
- export { c as collectFlowBlocks, a as commentBodyText, b as commentInitials, d as contentControlContentOf, e as deepParagraphOrderOfPart, i as isContentControl, f as isContentControlContent, p as paragraphOrderOfPart, w as walkAllStoryParagraphs, g as walkParagraphInline, h as walkStoryBlocks } from './review-paragraph-order-DeQp2_iu.js';
25
- export { A as AuthoredNoteProperties, D as DEFAULT_ENDNOTE_PROPERTIES, b as DEFAULT_FOOTNOTE_PROPERTIES, E as EndnotePosition, F as FootnotePosition, N as NoteNumRestart, a as ResolvedEndnoteProperties, R as ResolvedFootnoteProperties, c as authoredDocumentEndnoteProperties, d as authoredDocumentFootnoteProperties, e as authoredEndnotePropertiesFromSectPr, f as authoredFootnotePropertiesFromSectPr, i as isLegalEndnotePosition, g as isLegalFootnotePosition, h as isLegalNumRestart, p as parseAuthoredNoteProperties, r as resolveEndnoteProperties, j as resolveFootnoteProperties, s as settingsPartOf } from './note-properties-DGPzKUuA.js';
26
- export { D as DocumentProtectionEdit, a as DocumentProtectionState, N as NO_DOCUMENT_PROTECTION, r as readDocumentProtection } from './document-protection-8hXPdn5U.js';
15
+ import { bx as OoxmlAttribute, O as OoxmlPart, am as OoxmlNode, by as OoxmlInvariantResult, aU as OoxmlElement, bz as LegacyTextArtProjection, be as VectorShapeComponent, bA as ChartPartResolver, bB as ChartexPartResolver, bC as ShapeSchemeColorResolver, bD as ShapeEffectList, bk as PictureFrameEffects, bj as VectorShapeProjection, bE as GroupTextboxProjection, bF as MetafileImageMime, b1 as OoxmlParagraphNode, bG as OoxmlFldCharNode, bH as OoxmlFldSimpleNode, bI as OoxmlInstrTextNode, aO as TreeDocOp, aZ as TreeOpRejection, bJ as TreeOpResult, bK as ResourceLimits, r as OoxmlPackage, s as RelationshipRecord, aW as OoxmlGenericElementNode, bL as OoxmlHardBreakNode, bM as ShapeColor, aS as OoxmlProperty, ab as RevisionSiteRefusal, w as ReviewRange, R as ReviewModelInput, a as ReviewItem, e as CommentRecord, v as ReviewPosition, bN as CommentThreadState, t as ReviewCommentItem, d as ReviewRevisionItem, b9 as DrawingTreeDocOp, ba as ImpactClass, az as ImageWrapTarget } from './tree-op-types-DmrePCMo.js';
16
+ export { bO as ACCEPTED_PARAGRAPH_PROPERTIES, bP as ACCEPTED_RUN_PROPERTIES, bQ as BookmarkOpPosition, bR as CHARTEX_GRAPHIC_DATA_URI, bS as CHARTEX_NAMESPACE_URI, bT as CHART_GRAPHIC_DATA_URI, bU as CHART_NAMESPACE_URI, bV as CHART_STYLE_NAMESPACE_URI, bW as CONTENT_CONTROL_ID_MAX, bX as CONTENT_CONTROL_PROPERTY_ORDER, bY as ChartPart, bZ as ChartThemeFaces, b_ as ChartexPart, b$ as CommentAnchor, c0 as CommentPosition, c1 as CommitTextFormFieldOp, c2 as ContentControlCheckbox, c3 as ContentControlCheckboxState, c4 as ContentControlDataBinding, c5 as ContentControlDateFormat, c6 as ContentControlEntry, c7 as ContentControlKind, c8 as ContentControlLevel, c9 as ContentControlListItem, ca as ContentControlLock, cb as ContentControlProperties, cc as ContentTypeError, p as ContentTypeIndex, cd as ContentTypeRecords, ce as CreateImageResourceCacheOptions, bq as DEFAULT_IMAGE_RESOURCE_LIMITS, cf as DEFAULT_LIMITS, cg as DEFAULT_OOXML_PACKAGE_LIMITS, ch as DEFAULT_ZIP_LIMITS, ci as DIAGRAM_NAMESPACE_URI, cj as DRAWINGML_MAIN_NAMESPACE_URI, ck as DefaultRecord, cl as DeleteBookmarkOp, cm as DeleteTextOp, cn as DiagramPartResolver, co as DiagramParts, bg as DrawingImageEffects, ar as DrawingKind, as as DrawingLocks, at as DrawingPositionInput, cp as HARD_CEILINGS, cq as HYPERLINK_RELATIONSHIP_TYPE, cr as HyperlinkKind, cs as HyperlinkTarget, ct as IMAGE_RELATIONSHIP_TYPE, cu as IMAGE_RESOURCE_HARD_CEILINGS, br as IMAGE_WRAP_TARGETS, aw as ImageDecodePort, cv as ImageRelationshipResolution, ax as ImageResourceLimits, cw as ImageResourceLookup, ay as ImageResourceState, cx as IndexResult, cy as InsertBookmarkOp, cz as InsertBuildingBlockOp, cA as InsertComplexFieldOp, cB as InsertEquationOp, cC as InsertPageFieldOp, cD as InsertParagraphBesideTableOp, cE as InsertTableOp, cF as InsertTextOp, cG as LegacyCheckboxFieldRange, cH as MAX_CONTENT_CONTROLS_PER_PART, cI as MAX_CONTENT_CONTROL_NESTING, cJ as MAX_SVG_SNIFF_BYTES, cK as MoveTableOp, cL as NameRejection, cM as NameResult, cN as OFFICE_RELATIONSHIP_NAMESPACE_URI, cO as OOXML_NODE_IDENTITY_RULES, cP as OoxmlBodyNode, cQ as OoxmlBookmarkEndNode, cR as OoxmlBookmarkStartNode, cS as OoxmlContentControlCalendarNode, cT as OoxmlContentControlCheckboxNode, cU as OoxmlContentControlCheckedNode, cV as OoxmlContentControlCheckedStateNode, cW as OoxmlContentControlComboBoxNode, aX as OoxmlContentControlContentNode, cX as OoxmlContentControlDataBindingNode, cY as OoxmlContentControlDateFormatNode, cZ as OoxmlContentControlDateNode, c_ as OoxmlContentControlDropDownListNode, c$ as OoxmlContentControlEndPropertiesNode, d0 as OoxmlContentControlLidNode, d1 as OoxmlContentControlListItemNode, aV as OoxmlContentControlNode, d2 as OoxmlContentControlPropertiesNode, d3 as OoxmlContentControlStoreMappedDataAsNode, d4 as OoxmlContentControlTextNode, d5 as OoxmlContentControlUncheckedStateNode, b3 as OoxmlContinuationSeparatorNode, d6 as OoxmlDocumentNode, b4 as OoxmlEndnotesNode, q as OoxmlExternalTarget, b5 as OoxmlFootnotesNode, d7 as OoxmlGenericExtensionAttribute, d8 as OoxmlHyperlinkNode, b0 as OoxmlInvariantIssue, d9 as OoxmlInvariantIssueCode, da as OoxmlKnownNodeAttribute, db as OoxmlNamespaceBinding, a_ as OoxmlNodeId, dc as OoxmlNodeIdentityRules, b2 as OoxmlNoteNode, b6 as OoxmlNoteRefNode, b7 as OoxmlNoteReferenceNode, aP as OoxmlPackageLimits, aQ as OoxmlPackageRejection, aT as OoxmlPackageResult, dd as OoxmlParagraphPropertiesNode, de as OoxmlPartMetadata, df as OoxmlReadRejection, dg as OoxmlReadResult, dh as OoxmlRunNode, di as OoxmlRunPropertiesNode, b8 as OoxmlSeparatorNode, dj as OoxmlTabNode, dk as OoxmlTextElementNode, dl as OoxmlTextNode, dm as OoxmlWmlValAttribute, dn as OoxmlXmlSpaceAttribute, dp as OverrideRecord, aC as PreservedImageMime, dq as RelationshipError, dr as RelationshipSetResult, ds as RelationshipTargetResolver, aE as RenderableImageMime, dt as ResolveResult, du as ResolvedRelationship, u as ReviewCustomItem, x as ReviewRevisionKind, y as RevisionAddress, a4 as RevisionAuthorEntry, a5 as RevisionAuthorResult, a6 as RevisionAuthorSkipReason, a7 as RevisionBatchEntry, a8 as RevisionBatchResult, a9 as RevisionBatchSkipDetail, aa as RevisionBatchSkipReason, dv as SetFieldResultOp, dw as SetLegacyCheckboxOp, dx as SetLegacyDropdownOp, dy as SetTextFormFieldDefaultOp, dz as ShapeReflection, dA as ShapeShadow, bb as SourceCrop, aG as SupportedImageMime, dB as TABLE_BORDER_STYLES, dC as TabStopWrite, bc as TableBorderStyle, T as TargetMode, bo as TextFormFieldOptions, bn as TextFormFieldRange, dD as TextFormFieldType, dE as TextboxStoryProjection, dF as TreeDocOpKind, dG as TreeOpEffect, aI as ValidatedImageBytesHandle, dH as ValidatedRasterHeader, dI as VectorGradient, dJ as VectorPattern, dK as W15_NAMESPACE_URI, dL as WML_NAMESPACE_URI, dM as XMLNS_NAMESPACE_URI, dN as XML_NAMESPACE_URI, dO as XmlLimits, dP as XmlNode, dQ as XmlRejection, dR as XmlResult, dS as ZipLimits, dT as ZipReadResult, dU as ZipRejection, dV as allocateContentControlId, dW as asciiFold, dX as buildContentTypeIndex, dY as buildRelationshipSet, dZ as childElements, d_ as commentAnchorsOfStory, d$ as commentsOfPart, e0 as contentControlContentChildren, e1 as contentControlContentNodeOf, e2 as contentControlEndPropertiesNodeOf, e3 as contentControlLevelOf, e4 as contentControlPropertiesNodeOf, e5 as contentControlPropertiesOf, e6 as contentControlTextOf, e7 as contentControlsIn, e8 as createImageResourceCache, e9 as detectDuplicateNames, ea as directFill, eb as enclosedBookmarkDeletions, ec as extensionKey, ed as findContentControl, ee as findElement, ef as firstReviewRange, eg as flattenContentControls, eh as hasAnyComment, ei as hasBoundedSvgRoot, ej as hyperlinkAnchorOf, ek as hyperlinkRelationshipIdOf, el as hyperlinkTargetOf, em as imageResourceLookupFor, en as isContentControlContentNode, eo as isContentControlNode, ep as isContentControlWrapper, eq as isContentRevisionKind, er as isElement, es as isHyperlinkNode, et as isRangeMarkerKind, eu as isValidMime, ev as legacyCheckboxFieldsOf, ew as liveDrawingReferenceCount, ex as lockForbidsEdit, ey as lockForbidsRemoval, ez as normalizePartName, eA as orderedContentControlProperties, eB as parseContentControlId, eC as partNameKey, eD as planRevisionAuthorChange, eE as planRevisionBatch, eF as projectDrawing, eG as readOnOffChild, eH as readOoxmlPackage, eI as readOoxmlPart, eJ as readXml, eK as readZip, eL as resolveColorNode, eM as resolveContentControlLock, eN as resolveContentType, eO as resolveImageRelationship, bt as resolveImageResourceLimits, eP as resolveInternalTarget, eQ as resolveLimits, eR as resolveRelationship, eS as reviewItemKey, eT as reviewItemRanges, bv as sniffImageMime, eU as textContent, eV as textFormFieldsOf, eW as threadStateOfPart, eX as validateExternalTarget, eY as validateGifHeader, eZ as validateJpegHeader, e_ as validatePngHeader, bw as validateRasterHeader, e$ as withPart, f0 as withoutBookmarkInPart, f1 as writeOoxmlPackage, f2 as writeOoxmlPackageKeepingRevisionIds, f3 as writeZip } from './tree-op-types-DmrePCMo.js';
17
+ export { c as collectFlowBlocks, a as commentBodyText, b as commentInitials, d as contentControlContentOf, e as deepParagraphOrderOfPart, i as isContentControl, f as isContentControlContent, p as paragraphOrderOfPart, s as sha256Bytes, w as walkAllStoryParagraphs, g as walkParagraphInline, h as walkStoryBlocks } from './review-paragraph-order-aEHnQTt7.js';
18
+ import { F as FieldResultsMode, E as EditOptions, c as TreeModelChange, d as TreeDocumentStore, T as TreePackageStore, S as StoryScope, b as HistoryGroup } from './tree-package-fragment-DXZ2B7x0.js';
19
+ export { l as AtomicNoteSpan, m as CascadeDeletedNoteReferencesOptions, C as ContentLockPolicy, D as DEFAULT_MAX_EDITABLE_STORY_PARTS, n as DrawingPropertyIdResult, j as FragmentPasteInput, k as FragmentPasteResult, o as HeaderFooterKind, p as HeaderFooterLifecycleImpact, q as HeaderFooterLifecycleOp, s as HeaderFooterLifecycleRejection, t as HeaderFooterLifecycleResult, e as HeaderFooterParts, f as HeaderFooterSectionResolution, u as HeaderFooterSlotMeta, H as HeaderFooterVariant, v as HistoryCaptureKind, M as MAX_FRAGMENT_DECODED_BYTES, w as MAX_NOTES_PER_PART, x as MAX_NOTE_REFERENCE_PARTS, y as MAX_NOTE_REFERENCE_SCAN, z as NOTE_ATOM_CHAR, B as NOTE_CONTINUATION_SEPARATOR_ID, G as NOTE_ID_MAX, J as NOTE_ID_MIN, K as NOTE_SEPARATOR_ID, L as NoteDiagnostic, O as NoteDiagnosticCode, N as NoteKind, P as NoteLifecycleImpact, Q as NoteLifecycleOp, U as NoteLifecycleOptions, V as NoteLifecycleRejection, W as NoteLifecycleResult, X as NoteReferenceHit, Y as NoteReferenceScanBudget, Z as NoteType, _ as OoxmlEditResult, $ as PackageTransactResult, a0 as RemotePackageAttribution, g as SelectionMark, a1 as StoryResolveResult, a as StoryTargetRejection, a2 as TransactionRevisionIds, a3 as TreeDocumentStoreOptions, a4 as TreePackageStoreOptions, a5 as TreeStoryRef, h as TreeTransactOptions, a6 as TreeTransactResult, a7 as TreeTransactionContext, a8 as allocateDrawingPropertyId, a9 as allocateNoteId, aa as applyEdits, ab as applyFragmentPaste, ac as applyHeaderFooterLifecycleOp, ad as applyNoteLifecycleOp, ae as atomicNoteSpansOf, af as cascadeDeletedNoteReferences, ag as collectNodeIds, ah as collectNoteReferences, ai as collectPackageNoteReferences, aj as collectSectionPropertyNodes, ak as createNodeIdAllocator, al as createNoteReferenceScanBudget, am as customMarkFollows, an as diagnoseNoteReferences, ao as findNode, ap as findNoteById, aq as formatNoteScopeId, ar as hasNode, as as insertChildren, at as isContinuationSeparatorNode, au as isEndnotesNode, av as isFootnotesNode, aw as isHeaderFooterLifecycleOp, ax as isNormalNote, ay as isNoteAtomNode, az as isNoteLifecycleOp, aA as isNoteNode, aB as isNoteRefNode, aC as isNoteReferenceNode, aD as isSeparatorNode, aE as normalNoteIds, aF as noteAtomText, aG as noteIdOf, aH as noteKindOf, aI as noteRefKindOf, aJ as noteReferenceKindOf, aK as noteTypeOf, aL as notesOf, aM as notesPartHasId, aN as observeHistoryGroup, aO as parentNodeOf, aP as parseNoteId, aQ as parseNoteScopeId, aR as removeNode, aS as replaceChildren, aT as replaceNode, r as reportHistoryGroup, aU as resolveHeaderFooterParts, aV as resolveHeaderFooterPartsBySection, aW as resolveHeaderFooterResolutionBySection, aX as resolveNotesPart, aY as withBinaryPart, aZ as withEmbeddedImage, a_ as withoutUnreferencedImagePart } from './tree-package-fragment-DXZ2B7x0.js';
20
+ import { O as OmmlEquationProjection, a as DrawingSupport, D as DrawingGraphicKind, b as DrawingSupportIssue } from './drawing-graphic-kind-8zt6pugF.js';
21
+ export { e as DEFAULT_OMML_LIMITS, c as DocumentProperties, E as EnsuredHyperlinkRelationship, f as EquationBoxEdges, g as EquationBoxStrikes, h as EquationControlSource, i as EquationExpression, d as HeadlessDocumentRejection, H as HeadlessDocumentView, j as HeadlessThemeFonts, L as LinearMathOmmlResult, k as LinearMathParseResult, l as LinearMathRejection, m as OFFICE_MATH_NAMESPACE_URI, n as OmmlLimits, o as OpenHeadlessDocumentResult, p as authorableHyperlinkTarget, q as ensureHyperlinkRelationship, r as equationExpressionToLinearMath, s as headlessViewOfStore, t as linearMathToOmml, u as openHeadlessDocument, v as parseLinearMath, w as projectOmmlEquation, x as relationshipTargetIn } from './drawing-graphic-kind-8zt6pugF.js';
22
+ import { I as InsertCustomNodeWrite, C as CustomNodeWriteResult } from './custom-node-writes-Cj0itq58.js';
23
+ export { c as BookmarkAnchor, B as BookmarkIndex, d as CustomNodePayloadRead, e as CustomNodePayloadWrite, a as CustomNodeSweepOutcome, f as CustomNodeSweepResult, g as CustomNodeWriteRejection, D as DocumentTrackingSettings, E as EmbeddedFont, F as FontStyleKey, b as ListDefinitionGeometry, L as ListKind, h as ListLevelGeometry, M as MAX_CUSTOM_NODE_LABEL_LENGTH, i as MAX_CUSTOM_NODE_PAYLOAD_LENGTH, N as NO_TRACKING_SETTINGS, R as ReadEmbeddedFontsOptions, j as buildBookmarkIndex, k as customNodePayloadsByControl, l as customNodePayloadsOf, m as deobfuscateFont, n as ensureListDefinition, o as ensureNumberingLevel, p as insertCustomNodeWrite, r as readEmbeddedFonts, q as readTrackingSettings, s as removeCustomNodeWrite, t as sweepCustomNodePayloads } from './custom-node-writes-Cj0itq58.js';
24
+ export { B as BookmarkNameProblem, D as DrawingPreviewPackageLimits, a as DrawingPreviewPackageResult, b as DrawingPreviewTarget, M as MAX_AUTHORED_BOOKMARK_NAME_LENGTH, c as bookmarkNameKey, d as bookmarkNameProblem, e as createDrawingPreviewPackage, i as isHiddenBookmarkName } from './drawing-preview-package-8o9LSfzP.js';
25
+ export { A as AuthoredNoteProperties, D as DEFAULT_ENDNOTE_PROPERTIES, b as DEFAULT_FOOTNOTE_PROPERTIES, E as EndnotePosition, F as FootnotePosition, N as NoteNumRestart, a as ResolvedEndnoteProperties, R as ResolvedFootnoteProperties, c as authoredDocumentEndnoteProperties, d as authoredDocumentFootnoteProperties, e as authoredEndnotePropertiesFromSectPr, f as authoredFootnotePropertiesFromSectPr, i as isLegalEndnotePosition, g as isLegalFootnotePosition, h as isLegalNumRestart, p as parseAuthoredNoteProperties, r as resolveEndnoteProperties, j as resolveFootnoteProperties, s as settingsPartOf } from './note-properties-BRpskBeh.js';
26
+ export { D as DocumentProtectionEdit, a as DocumentProtectionState, N as NO_DOCUMENT_PROTECTION, r as readDocumentProtection } from './document-protection-CDLoYQI3.js';
27
27
  import { h as MetafileRenderOptions, a as MetafileReplay } from './metafile-drawing-Ce6TuWLm.js';
28
28
  export { b as MetafileCommand, c as MetafileDrawing, M as MetafileFont, d as MetafileMatrix, e as MetafilePaint, f as MetafilePathCommand, g as MetafileRaster } from './metafile-drawing-Ce6TuWLm.js';
29
29
  import './editor-scope-CA6egMaK.js';
@@ -399,6 +399,23 @@ declare function atomicFieldSpansOf(paragraph: OoxmlParagraphNode, options?: {
399
399
  /** Whether `fldCharType` is a legal ST_FldCharType value (used by tests / guards). */
400
400
  declare function isLegalFldCharType(value: string): boolean;
401
401
 
402
+ /** One inline content control's identity and the UTF-16 span its content covers. */
403
+ interface InlineControlSpan {
404
+ readonly controlId: string;
405
+ readonly start: number;
406
+ readonly end: number;
407
+ }
408
+ /**
409
+ * The innermost inline content control whose content ends exactly at `offset` — the caret
410
+ * at its right outer edge. What Backspace consults to delete the node as ONE unit
411
+ * (pro-review-and-custom-nodes 4.6): deleting its last character from outside would either
412
+ * strip one letter from a content-locked label (refused, so the key looks dead) or leave a
413
+ * half-deleted chip whose tag still claims the full payload.
414
+ */
415
+ declare function inlineControlEndingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
416
+ /** The forward-delete mirror: the control whose content STARTS exactly at `offset`. */
417
+ declare function inlineControlStartingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
418
+
402
419
  /** One addressable unit of paragraph text: text, tab, hard break, or atomic field. */
403
420
  interface Segment {
404
421
  readonly runId: string;
@@ -417,25 +434,11 @@ interface Segment {
417
434
  readonly formatRunIds?: readonly string[];
418
435
  }
419
436
  /**
420
- * Flatten a paragraph into UTF-16 addressable segments, in document order.
421
- *
422
- * A HYPERLINK's runs are addressed too. `w:hyperlink` is a run container, not a leaf, and
423
- * the characters inside a link are ordinary paragraph text: the user selects them, types
424
- * over them and deletes them like any other. Skipping the container — which is what
425
- * iterating only direct `w:r` children did — left every link's text with no offsets at all,
426
- * so `paragraphTextOf` read "Visit or ." for a sentence that says "Visit Example.com or
427
- * Anthropic's website." and layout, selection and the ops all agreed on the wrong string.
428
- *
429
- * Inline CONTENT CONTROLS are the same class of wrapper: their `w:sdtContent` runs join the
430
- * paragraph's offset stream with no break opportunity at the boundary. Nesting is bounded
431
- * (`MAX_INLINE_CONTAINER_DEPTH`); beyond the bound the wrapper is opaque so recursion
432
- * cannot exhaust the stack.
433
- *
434
- * `runId` stays the id of the run the content actually lives in, at whatever depth: the
435
- * appliers resolve it with `findNode` and rebuild that run's children, so nesting costs them
436
- * nothing.
437
+ * A paragraph's segments as caller-owned objects: {@link segmentsOf} for the public entry
438
+ * point. Internal readers share one frozen array; a host gets its own copies, as it did when
439
+ * every call walked the paragraph again.
437
440
  */
438
- declare function segmentsOf(paragraph: OoxmlParagraphNode): Segment[];
441
+ declare function segmentsOfCopy(paragraph: OoxmlParagraphNode): Segment[];
439
442
  /** Half-open `[start, end)` of one node in its paragraph's model offset space. */
440
443
  interface OffsetSpan {
441
444
  readonly start: number;
@@ -474,22 +477,6 @@ interface ParagraphOffsetIndex {
474
477
  * second implementation that disagreed would place edits inside content that cannot be split.
475
478
  */
476
479
  declare function paragraphOffsetIndex(paragraph: OoxmlParagraphNode): ParagraphOffsetIndex;
477
- /** One inline content control's identity and the UTF-16 span its content covers. */
478
- interface InlineControlSpan {
479
- readonly controlId: string;
480
- readonly start: number;
481
- readonly end: number;
482
- }
483
- /**
484
- * The innermost inline content control whose content ends exactly at `offset` — the caret
485
- * at its right outer edge. What Backspace consults to delete the node as ONE unit
486
- * (pro-review-and-custom-nodes 4.6): deleting its last character from outside would either
487
- * strip one letter from a content-locked label (refused, so the key looks dead) or leave a
488
- * half-deleted chip whose tag still claims the full payload.
489
- */
490
- declare function inlineControlEndingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
491
- /** The forward-delete mirror: the control whose content STARTS exactly at `offset`. */
492
- declare function inlineControlStartingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
493
480
 
494
481
  /** Structural validation, run before any tree work so a rejection changes nothing. */
495
482
  declare function validateTreeOp(part: OoxmlPart, op: TreeDocOp, options?: Pick<EditOptions, 'contentLockPolicy'>): TreeOpRejection | null;
@@ -513,1913 +500,1926 @@ declare function paragraphTextOf(part: OoxmlPart, paragraphId: string, options?:
513
500
  declare function applyTreeOp(part: OoxmlPart, op: TreeDocOp, options?: EditOptions): TreeOpResult;
514
501
 
515
502
  /**
516
- * Keys that must never be assigned from file data.
503
+ * Every kind of thing the registry gives a stable identity to.
517
504
  *
518
- * XML attribute names become object keys, and a document controls those names — assigning
519
- * `__proto__` into an ordinary object is the prototype-pollution hazard this engine audits for.
520
- */
521
- declare const DANGEROUS_KEYS: readonly string[];
522
- /** A file-derived key that would pollute a prototype. Refused, never sanitized-and-accepted. */
523
- declare class DangerousKeyError extends Error {
524
- readonly key: string;
525
- readonly path: string;
526
- constructor(key: string, path: string);
527
- }
528
- /** Whether a key is one of {@link DANGEROUS_KEYS}. */
529
- declare function isDangerousKey(key: string): boolean;
530
- /** A fresh null-prototype record — the only object shape parser intermediates use. */
531
- declare function nullRecord<T = unknown>(): Record<string, T>;
532
- /**
533
- * Recursively convert `value` into null-prototype records, rejecting any
534
- * dangerous object key. Arrays and primitives pass through (arrays rebuilt so no
535
- * inherited prototype pollution survives). Throws DangerousKeyError on the first
536
- * unsafe key, naming its path. Cyclic inputs are rejected.
505
+ * Identity is `(kind, id)` plus version and NEVER registration order, so two bundles contributing
506
+ * the same command resolve deterministically regardless of which loaded first.
537
507
  */
538
- declare function toSafeRecord(value: unknown, path?: string): unknown;
539
-
540
- /** Locate `[Content_Types].xml` bytes regardless of zip key spelling. */
541
- declare function contentTypesPartBytes(pkg: OoxmlPackage): {
542
- readonly storageKey: string;
543
- readonly bytes: Uint8Array;
544
- } | null;
508
+ declare const ID_KINDS: readonly ['extension', 'capability', 'command', 'query', 'schema', 'dependencyKey', 'runtimePort', 'result', 'origin'];
509
+ /** One of {@link ID_KINDS}. */
510
+ /** One of {@link ID_KINDS} — which sort of thing an identifier names. */
511
+ type IdKind = (typeof ID_KINDS)[number];
545
512
  /**
546
- * What a package-level invariant walk found wrong.
513
+ * Whether a string is a well-formed identifier in either accepted grammar.
547
514
  *
548
- * Package invariants are cross-PART: a relationship pointing at a part that does not exist, or a
549
- * part no content-type record covers. Neither is visible from inside a single part's tree.
515
+ * Reverse-domain (`dev.docx-editor.core.command.insert-text`) or package-owned
516
+ * (`@docx-editor.dev/engine-core#command/insert-text`). Ids are opaque once validated.
550
517
  */
551
- type PackageInvariantCode = 'dangling-relationship' | 'missing-content-type'
552
- /** A part occupies a name OPC reserves for package infrastructure. */
553
- | 'reserved-part-name'
554
- /** Two parts whose names differ only by case, which OPC treats as one part. */
555
- | 'duplicate-part-name'
556
- /** A part name the OPC screens refuse; `writeZip` would throw on save. */
557
- | 'unsafe-part-name';
558
- /** One package invariant violation, located by part name and relationship id. */
559
- interface PackageInvariantIssue {
560
- readonly code: PackageInvariantCode;
561
- /** The part the issue is about: the missing target, or the part with no type. */
562
- readonly partName: string;
563
- /** For a dangling relationship, the owner that points at nothing. */
564
- readonly ownerPart?: string;
565
- }
566
- /** Whether a package satisfies its cross-part invariants, listing every violation otherwise. */
567
- type PackageInvariantResult = {
568
- readonly ok: true;
569
- } | {
570
- readonly ok: false;
571
- readonly issues: readonly PackageInvariantIssue[];
572
- };
573
- /** Relationship records owned by one part, in authored order. */
574
- declare function relationshipsOf(pkg: OoxmlPackage, ownerPart: string): readonly RelationshipRecord[];
575
- /** The content type that resolves for a part name, or null when nothing declares one. */
576
- declare function resolveContentTypeOf(pkg: OoxmlPackage, partName: string): string | null;
577
- /** The `.rels` part that owns a part's relationships, by OPC convention. */
578
- declare function relsPartNameFor(partName: string): string;
518
+ declare function isValidId(id: string): boolean;
579
519
  /**
580
- * Declare a content type for a part, by upserting an `<Override>` in the content-types tree.
520
+ * Validate an identifier, throwing when it is malformed.
581
521
  *
582
- * A no-op when the part already resolves to the same type, so repeating a write does not append
583
- * a duplicate entry. Resolving to a DIFFERENT type replaces the existing Override in place.
584
- * `forceOverride` writes an explicit Override even when a Default already matches.
522
+ * Throws rather than returning: a bad id is an author mistake at registration time, not file
523
+ * input, and accepting it would produce a registry whose identities silently collide.
585
524
  */
586
- declare function withContentTypeOverride(pkg: OoxmlPackage, partName: string, contentType: string, options?: {
587
- readonly forceOverride?: boolean;
588
- }): OoxmlPackage;
589
- /** What {@link withoutPart} answers: `ok: false` leaves the package exactly as it was. */
590
- interface WithoutPartResult {
591
- readonly pkg: OoxmlPackage;
592
- readonly ok: boolean;
525
+ declare function assertValidId(id: string, kind?: IdKind): void;
526
+ /** A validated (kind, id, version) triple — the registry's unit of identity. */
527
+ interface CapabilityId {
528
+ readonly kind: IdKind;
529
+ readonly id: string;
530
+ readonly version: string;
531
+ }
532
+
533
+ /** A parsed semantic version. Only the three numeric components — no pre-release or build. */
534
+ interface SemVer {
535
+ readonly major: number;
536
+ readonly minor: number;
537
+ readonly patch: number;
593
538
  }
594
- /** Add or replace one relationship on a part, returning a new package. */
595
- declare function withRelationship(pkg: OoxmlPackage, ownerPart: string, type: string, rawTarget: string): {
596
- readonly pkg: OoxmlPackage;
597
- readonly relationshipId: string;
598
- readonly ok: boolean;
599
- };
600
539
  /**
601
- * Add a part that does not exist yet, with the content-type override it needs to be openable.
540
+ * Parse a semantic version, rejecting anything the registry's rules do not need.
602
541
  *
603
- * Deliberately does NOT create a relationship: a part is reachable because something points at
604
- * it, and which part points at it is the caller's decision. Creating one here would guess.
542
+ * Deliberately narrow: an unsupported range form is refused rather than accepted, so a malformed
543
+ * range can never masquerade as "compatible".
605
544
  */
606
- declare function withNewPart(pkg: OoxmlPackage, partName: string, root: OoxmlElement, contentType: string): OoxmlPackage;
545
+ declare function parseSemVer(input: string): SemVer;
546
+ /** Order two versions: negative, zero or positive, by major then minor then patch. */
547
+ declare function compareSemVer(a: SemVer, b: SemVer): -1 | 0 | 1;
607
548
  /**
608
- * The two invariants a package must satisfy before it is published.
609
- *
610
- * Both describe the half-written state that splitting a multi-part write across transactions
611
- * produces, and both make a package Word refuses to open:
612
- *
613
- * - a relationship pointing at a part nobody created;
614
- * - a part with no content type, which is unopenable even though the XML is well formed.
615
- *
616
- * Checked at the commit boundary rather than inside each primitive, for the same reason part
617
- * validation moved there: a transaction is allowed to pass through an inconsistent intermediate
618
- * as long as nothing can observe it.
549
+ * Whether `version` satisfies `range`. Supported range grammar:
550
+ * - `*` any version
551
+ * - `1.2.3` exact
552
+ * - `^1.2.3` caret: same major, and >= the floor (major 0 pins the minor)
553
+ * - `>=1.2.3 <2.0.0` a single lower (inclusive) + upper (exclusive) bound pair
554
+ * Any other shape throws — an unparseable range is a registration error, not a pass.
619
555
  */
620
- declare function validatePackageInvariants(pkg: OoxmlPackage): PackageInvariantResult;
556
+ declare function satisfies(version: string, range: string): boolean;
621
557
 
622
558
  /**
623
- * Whether a string is a valid XML NCName in the strict ASCII profile — a name with no colon.
559
+ * Why registry resolution failed.
624
560
  *
625
- * This is the rule for names the engine itself writes or accepts from a caller. Names read
626
- * from a document use {@link isXmlNCName}, which admits every XML 1.0 name.
561
+ * Every code names a declaration conflict a bundle author can fix — a missing dependency, an
562
+ * unsatisfied version range, a duplicate contribution with no replacement policy.
627
563
  */
628
- declare function isValidNCName(name: string): boolean;
629
- /** A QName is an optional `prefix:` (both NCNames) — never attacker-derived. */
630
- declare function isValidQName(name: string): boolean;
564
+ type RegistryErrorCode = 'invalid-id' | 'invalid-version' | 'duplicate-extension' | 'id-collision' | 'replacement-target-missing' | 'unauthorized-replacement' | 'replacement-version-mismatch' | 'ambiguous-replacement' | 'missing-dependency' | 'dependency-cycle' | 'conflict' | 'missing-port';
631
565
  /**
632
- * Validate a qualified name, throwing when it is malformed.
566
+ * A resolution failure naming every responsible party.
633
567
  *
634
- * Guards the serializer: an invalid QName written into XML produces a file Word cannot open, so
635
- * it fails here rather than at save.
636
- */
637
- declare function assertValidQName(name: string): void;
638
- /**
639
- * Controlled namespace-prefix allocation: deterministic, collision-free prefixes
640
- * for namespace URIs. A known URI always yields the same registered prefix; new
641
- * URIs get a generated `ns{n}` prefix, never one derived from file content.
568
+ * Thrown rather than returned: an unresolvable registry is a build-time composition mistake, and
569
+ * an engine running with a half-resolved registry would fail later in ways that do not point back
570
+ * to the bundle that caused it.
642
571
  */
643
- declare class PrefixAllocator {
644
- private readonly byUri;
645
- private readonly usedPrefixes;
646
- private counter;
647
- constructor(known?: Readonly<Record<string, string>>);
648
- prefixFor(namespaceUri: string): string;
649
- /** The declared bindings, for emitting xmlns declarations. */
650
- bindings(): {
651
- prefix: string;
652
- uri: string;
653
- }[];
572
+ declare class RegistryError extends Error {
573
+ readonly code: RegistryErrorCode;
574
+ /** Stable identities responsible for the failure (extensions, ids). */
575
+ readonly responsible: readonly string[];
576
+ constructor(code: RegistryErrorCode, message: string,
577
+ /** Stable identities responsible for the failure (extensions, ids). */
578
+ responsible?: readonly string[]);
654
579
  }
655
-
656
- /**
657
- * A hyperlink target projected for a RUNTIME sink, or the reason it was withheld.
658
- *
659
- * The authored target stays authored — the record layer keeps it verbatim and escapes it into
660
- * owned OOXML, which is not a runtime sink. Only this allowlist-sanitized projection reaches DOM,
661
- * CSS, navigation or fetch, so `javascript:`, `data:` and `vbscript:` never leave the boundary.
662
- */
663
- type HrefProjection = {
664
- readonly ok: true;
665
- readonly href: string;
580
+ /** How a base contribution permits replacement by other bundles. */
581
+ type ReplacementPolicy = {
582
+ readonly kind: 'none';
666
583
  } | {
667
- readonly ok: false;
668
- readonly inert: true;
584
+ readonly kind: 'single';
585
+ } | {
586
+ readonly kind: 'priority';
669
587
  };
588
+ /** One thing a bundle contributes: a command, a query, a schema, a port, keyed by `(kind, id)`. */
589
+ interface Contribution {
590
+ readonly kind: Exclude<IdKind, 'extension' | 'origin' | 'result'>;
591
+ readonly id: string;
592
+ readonly version: string;
593
+ /** If set, this contribution replaces an existing base contribution. */
594
+ readonly replaces?: {
595
+ readonly targetId: string;
596
+ readonly targetRange: string;
597
+ readonly priority?: number;
598
+ };
599
+ /** Policy this contribution exposes to would-be replacers (default `none`). */
600
+ readonly replaceable?: ReplacementPolicy;
601
+ readonly payload?: unknown;
602
+ }
670
603
  /**
671
- * Project a file-derived URL for a DOM/navigation sink. Strips embedded
672
- * tab/LF/CR (used to smuggle `java\nscript:`), allows relative URLs and the
673
- * scheme allowlist, and renders everything else inert. Never fetches.
604
+ * A unit of engine functionality: its identity, version, dependencies, conflicts, required ports,
605
+ * and contributions.
606
+ *
607
+ * The registry's whole input. Everything a bundle needs and everything it offers is DECLARED, so
608
+ * resolution can be deterministic and order-independent.
674
609
  */
675
- declare function sanitizeHref(raw: string): HrefProjection;
676
- /** XML-escape a value for validated serialization back into owned OOXML. */
677
- declare function escapeXml(value: string): string;
678
- /** XML-escape a value bound for an ATTRIBUTE, preserving CR/LF/TAB across a re-parse. @internal */
679
- declare function escapeXmlAttribute(value: string): string;
680
- /**
681
- * Validate that a string contains only characters legal in XML 1.0 text/attributes. escapeXml
682
- * handles the markup-significant characters but leaves control chars, U+FFFE/U+FFFF, and unpaired
683
- * surrogates — which would emit malformed XML (or be silently mangled during UTF-8 encoding). Returns
684
- * false so a caller can reject fail-closed. Valid supplementary characters (correct surrogate pairs)
685
- * pass.
686
- *
687
- * @internal
688
- */
689
- declare function isValidXmlText(value: string): boolean;
690
- /**
691
- * CSS string-escape a file-derived value (e.g. an `@font-face` family name or an
692
- * inline style value). Emits `\<hex> ` escapes for quotes, backslash, and
693
- * controls so the value cannot break out of its CSS string.
694
- */
695
- declare function escapeCssString(value: string): string;
696
- /** Whether a file-derived CSS fragment contains a `url()` or `@import` (must be rejected). */
697
- declare function containsCssFetch(value: string): boolean;
698
- /**
699
- * Content that must never execute or auto-resolve: OLE objects, macros, DDE and `INCLUDE*` field
700
- * instructions.
701
- *
702
- * Rendered INERT by default rather than stripped — removing it would be a lossless-preservation
703
- * failure, so it is carried and never acted on.
704
- */
705
- declare const INERT_EXECUTABLE_KINDS: readonly ['field-dde', 'field-include', 'macro', 'activex', 'ole', 'embedded-object', 'executable-relationship'];
706
- /** One of {@link INERT_EXECUTABLE_KINDS}. */
707
- /** One of {@link INERT_EXECUTABLE_KINDS} — content carried but never executed. */
708
- type InertExecutableKind = (typeof INERT_EXECUTABLE_KINDS)[number];
709
- /** Whether a content kind is one this engine refuses to execute or auto-resolve. */
710
- declare function isInertExecutable(kind: string): boolean;
711
- /** Whether a field instruction's leading keyword is safe to evaluate. */
712
- declare function isEvaluableField(instruction: string): boolean;
713
- /** One item a scrub pass considers: its kind, and where in the package it sits. */
714
- interface ContentItem {
610
+ interface FeatureBundle {
715
611
  readonly id: string;
716
- readonly kind: string;
612
+ readonly version: string;
613
+ readonly dependencies?: readonly string[];
614
+ readonly conflicts?: readonly string[];
615
+ readonly requiredPorts?: readonly string[];
616
+ readonly contributions: readonly Contribution[];
617
+ }
618
+ /** The resolved result: every contribution selected, indexed by `(kind, id)`. */
619
+ interface ResolvedRegistry {
620
+ readonly extensions: ReadonlyMap<string, FeatureBundle>;
621
+ readonly contributions: ReadonlyMap<string, Contribution>;
622
+ get(kind: Contribution['kind'], id: string): Contribution | undefined;
623
+ }
624
+ /** How resolution treats optional bundles and replacement policies. */
625
+ interface ResolveOptions {
626
+ /** Runtime port ids available in this environment (design D9 / task 0.3). */
627
+ readonly availablePorts?: readonly string[];
717
628
  }
718
629
  /**
719
- * What an explicit scrub removed.
630
+ * Resolve a set of bundles into one registry, or throw.
720
631
  *
721
- * A scrub DECLARES ITSELF non-lossless: removing executable content changes the file, which is a
722
- * choice a caller makes deliberately rather than a default the engine applies.
632
+ * Deterministic and registration-order independent: selection is by `(kind, id)` plus version,
633
+ * and ties break only on declared policy. Array order never decides anything.
634
+ *
635
+ * @throws RegistryError naming every bundle responsible for the failure.
723
636
  */
724
- interface ScrubResult {
725
- readonly kept: readonly ContentItem[];
726
- readonly removed: readonly ContentItem[];
727
- /** A scrub that removes anything is non-lossless by definition. */
728
- readonly nonLossless: boolean;
729
- }
730
- /** Explicit scrub export: remove inert executable classes, report removals. */
731
- declare function scrubExport(items: readonly ContentItem[]): ScrubResult;
637
+ declare function resolve(bundles: readonly FeatureBundle[], options?: ResolveOptions): ResolvedRegistry;
732
638
 
733
- /** True for an `m:oMathPara` display equation. */
734
- declare function isOmmlDisplay(node: OoxmlNode): node is OoxmlGenericElementNode;
735
- /** True for a paragraph-level equation atom: `m:oMath` or `m:oMathPara`. */
736
- declare function isOmmlEquationAtom(node: OoxmlNode): node is OoxmlGenericElementNode;
737
- /** The `m:oMath` equations of a display, or the equation itself for an inline `m:oMath`. */
738
- declare function equationsOfAtom(node: OoxmlNode): readonly OoxmlGenericElementNode[];
739
639
  /**
740
- * Project a display equation as one layout atom. A display with one equation keeps that
741
- * equation's projection, so the painted atom names the editable `m:oMath`. Several equations
742
- * share one row, separated by an em space.
640
+ * Typed-origin domains (design D5 / ADR-S5). `mutation.*` are the canonical
641
+ * write origins; `projection` and `awareness` MUST NOT enter history, audit,
642
+ * snapshots, or replication.
743
643
  */
744
- declare function projectOmmlDisplay(node: OoxmlNode): OmmlEquationProjection | null;
644
+ declare const ORIGIN_IDS: {
645
+ readonly mutationHuman: "dev.docx-editor.core.origin.mutation.human";
646
+ readonly mutationAgent: "dev.docx-editor.core.origin.mutation.agent";
647
+ readonly mutationRemote: "dev.docx-editor.core.origin.mutation.remote";
648
+ readonly mutationUndo: "dev.docx-editor.core.origin.mutation.undo";
649
+ readonly mutationRedo: "dev.docx-editor.core.origin.mutation.redo";
650
+ readonly mutationMigration: "dev.docx-editor.core.origin.mutation.migration";
651
+ readonly mutationRepair: "dev.docx-editor.core.origin.mutation.repair";
652
+ readonly mutationServer: "dev.docx-editor.core.origin.mutation.server";
653
+ readonly projection: "dev.docx-editor.core.origin.projection";
654
+ readonly awareness: "dev.docx-editor.core.origin.awareness";
655
+ };
656
+ /** Canonical writes carry a mutation origin; these are the undoable-eligibility inputs. */
657
+ declare const CANONICAL_MUTATION_ORIGINS: readonly string[];
658
+ /** Origins that never enter authored state / history / audit / snapshot / replication. */
659
+ declare const NON_CANONICAL_ORIGINS: readonly string[];
745
660
  /**
746
- * The physical justification of a paragraph whose only visible content is display math.
747
- * `m:oMathParaPr/m:jc` sets it; the default `centerGroup` centers the display. Returns null
748
- * when the paragraph also holds text or other content, which keeps the paragraph `w:jc`.
661
+ * Result taxonomy (design D8 / task 7.8). A transport/protocol failure that
662
+ * prevents a valid envelope is a typed exception, not a member here.
749
663
  */
750
- declare function displayMathAlignment(paragraph: {
751
- readonly children: readonly OoxmlNode[];
752
- }): 'left' | 'center' | 'right' | null;
753
- /** Project a paragraph-level equation atom: inline `m:oMath` or display `m:oMathPara`. */
754
- declare function projectOmmlAtom(node: OoxmlNode): OmmlEquationProjection | null;
755
-
756
- /** UTF-16 placeholder for a page break in paragraph text projections. */
757
- declare const PAGE_BREAK_CHAR = "\f";
758
- /** What a `w:br` breaks. `other` covers values this engine does not model, kept losslessly. */
759
- type HardBreakKind = 'line' | 'page' | 'column' | 'other';
760
- /** Read the semantic break kind from a typed `w:br` node. */
761
- declare function hardBreakKind(node: OoxmlHardBreakNode): HardBreakKind;
762
- /** Whether a node is a `w:br` with `w:type="page"`. */
763
- declare function isPageBreakNode(node: OoxmlNode): node is OoxmlHardBreakNode;
764
- /** Text projection for a `w:br` node — one UTF-16 code unit per break. */
765
- declare function hardBreakText(node: OoxmlHardBreakNode): string;
766
- /** Attributes for a newly authored `w:br`. */
767
- declare function hardBreakAttributes(kind: 'line' | 'page'): OoxmlHardBreakNode['attributes'];
664
+ declare const RESULT_IDS: {
665
+ readonly applied: "dev.docx-editor.core.result.applied";
666
+ readonly validation: "dev.docx-editor.core.result.validation";
667
+ readonly conflict: "dev.docx-editor.core.result.conflict";
668
+ readonly resource: "dev.docx-editor.core.result.resource";
669
+ readonly authorization: "dev.docx-editor.core.result.authorization";
670
+ readonly aborted: "dev.docx-editor.core.result.aborted";
671
+ };
672
+ /** Runtime ports (extensions-and-runtime-ports spec; design D9). */
673
+ declare const RUNTIME_PORT_IDS: {
674
+ readonly fonts: "dev.docx-editor.core.port.fonts";
675
+ readonly shaping: "dev.docx-editor.core.port.shaping";
676
+ readonly images: "dev.docx-editor.core.port.images";
677
+ readonly clock: "dev.docx-editor.core.port.clock";
678
+ readonly identity: "dev.docx-editor.core.port.identity";
679
+ readonly persistence: "dev.docx-editor.core.port.persistence";
680
+ readonly transport: "dev.docx-editor.core.port.transport";
681
+ readonly scheduling: "dev.docx-editor.core.port.scheduling";
682
+ readonly audit: "dev.docx-editor.core.port.audit";
683
+ readonly authorization: "dev.docx-editor.core.port.authorization";
684
+ readonly resourceAccounting: "dev.docx-editor.core.port.resource-accounting";
685
+ readonly cancellation: "dev.docx-editor.core.port.cancellation";
686
+ readonly externalResourceConsent: "dev.docx-editor.core.port.external-resource-consent";
687
+ };
688
+ /** Layout dependency keys (design D6 / task 8.2). */
689
+ declare const DEPENDENCY_KEY_IDS: {
690
+ readonly style: "dev.docx-editor.core.dep.style";
691
+ readonly numbering: "dev.docx-editor.core.dep.numbering";
692
+ readonly section: "dev.docx-editor.core.dep.section";
693
+ readonly story: "dev.docx-editor.core.dep.story";
694
+ readonly font: "dev.docx-editor.core.dep.font";
695
+ readonly image: "dev.docx-editor.core.dep.image";
696
+ readonly table: "dev.docx-editor.core.dep.table";
697
+ readonly field: "dev.docx-editor.core.dep.field";
698
+ readonly note: "dev.docx-editor.core.dep.note";
699
+ readonly headerFooter: "dev.docx-editor.core.dep.header-footer";
700
+ readonly annotation: "dev.docx-editor.core.dep.annotation";
701
+ };
702
+ /** Every frozen id as a flat list, for the immutability / uniqueness gate. */
703
+ declare const ALL_FROZEN_IDS: readonly string[];
768
704
 
769
- /** Parsed TOC instruction. Unknown switches are ignored for generation but left on the wire. */
770
- interface TocInstruction {
771
- readonly keyword: 'TOC';
772
- /** Include hyperlinks (`\\h`). */
773
- readonly hyperlink: boolean;
774
- /** Omit page numbers (`\\n`). */
775
- readonly omitPageNumbers: boolean;
776
- /** Inclusive 1-based outline levels from `\\o "n-m"`. Defaults 1–9. */
777
- readonly outlineStart: number;
778
- readonly outlineEnd: number;
779
- readonly raw: string;
780
- }
781
- /** Longest TOC field instruction read. Instructions come from a file; the parse is bounded. */
782
- declare const TOC_MAX_INSTRUCTION_CHARS = 256;
783
- /** Most entries one generated table of contents may hold. */
784
- declare const TOC_MAX_ENTRIES = 512;
785
- /** Most bookmarks minted during one TOC refresh. */
786
- declare const TOC_MAX_BOOKMARKS_PER_REFRESH = 512;
787
- /** Deepest nested field instruction followed. Caps recursion on file-supplied structure. */
788
- declare const TOC_MAX_FIELD_NESTING = 4;
789
705
  /**
790
- * Most layout passes a TOC refresh runs before settling.
706
+ * A counter's limit was reached. Carries the label, the limit, and what was attempted.
791
707
  *
792
- * Page numbers change the TOC's own height, which changes page numbers — bounded so a
793
- * non-converging document stops rather than looping.
708
+ * Thrown at the N → N+1 boundary, so the increment that would have crossed the limit never
709
+ * happens rather than being detected afterwards.
794
710
  */
795
- declare const TOC_MAX_PAGE_PASSES = 3;
711
+ declare class LimitExceededError extends Error {
712
+ readonly label: string;
713
+ readonly limit: number;
714
+ readonly attempted: number;
715
+ constructor(label: string, limit: number, attempted: number);
716
+ }
796
717
  /**
797
- * Parse a TOC field instruction string.
718
+ * Overflow-safe counting against a fixed limit.
798
719
  *
799
- * Returns null when the leading keyword is not TOC, the string is over-long, or the
800
- * outline range is hostile. Does not evaluate or fetch anything.
720
+ * Never silently wraps: it rejects the increment that would cross its limit AND any arithmetic
721
+ * that would exceed `Number.MAX_SAFE_INTEGER`. That second guard is the point — a file-supplied
722
+ * count must never be trusted into an allocation, and a wrapped counter reads as small.
801
723
  */
802
- declare function parseTocInstruction(raw: string): TocInstruction | null;
803
-
804
- /** A table of contents found in a document, whether wrapped in an SDT or a bare field. */
805
- interface DetectedToc {
806
- /** Enclosing control id when it identifies one TOC, otherwise the begin fldChar id. */
807
- readonly id: string;
808
- readonly beginNodeId: string;
809
- readonly beginParagraphId: string;
810
- readonly endParagraphId: string;
811
- readonly resultParagraphIds: readonly string[];
812
- /** Direct parent whose paragraph children delimit the cached result. */
813
- readonly containerId: string;
814
- readonly contentControlId?: string;
815
- readonly instruction: TocInstruction;
724
+ declare class BoundedCounter {
725
+ readonly label: string;
726
+ readonly limit: number;
727
+ private value;
728
+ constructor(label: string, limit: number);
729
+ get current(): number;
730
+ /** Remaining headroom before the limit. */
731
+ get remaining(): number;
732
+ /**
733
+ * Add `n` (default 1). Throws LimitExceededError if the result would exceed the
734
+ * limit (so `limit` itself is reachable but `limit + 1` is not) or overflow the
735
+ * safe-integer range. On rejection the counter is unchanged.
736
+ */
737
+ add(n?: number): number;
738
+ /** Whether adding `n` would be accepted, without mutating. */
739
+ canAdd(n?: number): boolean;
740
+ release(n: number): void;
816
741
  }
817
- /** Discover refreshable body TOCs without evaluating any field instruction. */
818
- declare function detectBodyTocs(part: OoxmlPart): readonly DetectedToc[];
819
- /** Locate the table of contents containing a position, or null. */
820
- declare function findDetectedToc(tocs: readonly DetectedToc[], tocId: string): DetectedToc | null;
821
742
 
822
- /** One heading symbol: its `w:font` and `w:char`, as the file wrote them. */
823
- interface TocSymbol {
824
- readonly font: string | null;
825
- readonly char: string;
743
+ /** A budget was misused: over-carved, over-reserved, or disposed with children outstanding. */
744
+ declare class BudgetError extends Error {
745
+ constructor(message: string);
826
746
  }
827
-
828
- declare const TWIPS: unique symbol;
829
- declare const POINTS: unique symbol;
830
747
  /**
831
- * A length in twentieths of a point — OOXML's `ST_TwipsMeasure` / `ST_SignedTwipsMeasure`.
832
- *
833
- * The brand is intersection-typed onto `number`, so a `Twips` value still decays to `number`
834
- * wherever arithmetic consumes it; only the reverse direction — passing a raw number where
835
- * `Twips` is declared — is refused.
748
+ * Capacity claimed BEFORE it is allocated.
836
749
  *
837
- * @public
750
+ * Reserve-then-allocate rather than allocate-then-check: discovering the overrun after the
751
+ * allocation has already happened defeats the point of having a budget.
838
752
  */
839
- type Twips = number & {
840
- readonly [TWIPS]: true;
841
- };
753
+ interface Reservation {
754
+ readonly amount: number;
755
+ readonly released: boolean;
756
+ release(): void;
757
+ }
842
758
  /**
843
- * A length in typographic points, the unit layout and paint compute in.
759
+ * One node in the hierarchical resource budget tree.
844
760
  *
845
- * @public
846
- */
847
- type Points = number & {
848
- readonly [POINTS]: true;
849
- };
850
- /**
851
- * Twentieths of a point per point.
761
+ * An operation owns a root budget; parsers, extensions, workers, layout, transport and output
762
+ * carve children from it, so no subsystem can consume more than the operation as a whole allows.
852
763
  *
853
- * @public
764
+ * `dispose()` REFUSES while children or reservations are outstanding, then runs registered
765
+ * cleanups LIFO — spill files, worker termination — even if one throws. A leaked child budget is a
766
+ * leaked worker, so the refusal is the diagnostic.
854
767
  */
855
- declare const TWIPS_PER_POINT = 20;
768
+ declare class Budget {
769
+ readonly label: string;
770
+ readonly capacity: number;
771
+ private readonly used;
772
+ private readonly children;
773
+ private activeReservations;
774
+ private readonly cleanups;
775
+ private disposed;
776
+ private carveAmount;
777
+ private parent?;
778
+ constructor(label: string, capacity: number);
779
+ get inUse(): number;
780
+ get available(): number;
781
+ get isDisposed(): boolean;
782
+ /** Reserve `amount` from this budget. Throws if it would exceed capacity. */
783
+ reserve(amount: number): Reservation;
784
+ /** Carve a child budget of `capacity` out of this budget's headroom. */
785
+ child(label: string, capacity: number): Budget;
786
+ /** Register cleanup for a spill file, worker, or queue tied to this budget. */
787
+ onRelease(cleanup: () => void): void;
788
+ /**
789
+ * Release this budget. Refuses while any child budget or reservation is
790
+ * outstanding (children must finish first). Runs cleanups LIFO, returns the
791
+ * carve-out to the parent, and marks disposed. Cleanup errors are collected and
792
+ * rethrown after every cleanup has run.
793
+ */
794
+ dispose(): void;
795
+ private assertLive;
796
+ }
797
+
856
798
  /**
857
- * Assert that a number is a twips measurement.
858
- *
859
- * A compile-time cast, not a validator: bounds, integrality and finiteness stay at the parse
860
- * boundary that produced the number, exactly where they are enforced today. The cast is the
861
- * caller's claim about the UNIT, nothing more.
799
+ * Which side of the point of no return an operation is on.
862
800
  *
863
- * @public
801
+ * Canonical publication is that point. Cancelling BEFORE it rolls the whole operation back;
802
+ * cancelling after it leaves the commit standing and cancels only derived work — layout, export,
803
+ * caches.
864
804
  */
865
- declare const twips: (value: number) => Twips;
805
+ type CancellationPhase = 'pre-publication' | 'post-publication';
806
+ /** Thrown from a `checkpoint()` when the operation has been cancelled. */
807
+ declare class CancellationError extends Error {
808
+ /** True when the commit is already published and only derived work is cancelled. */
809
+ readonly derivedOnly: boolean;
810
+ constructor(
811
+ /** True when the commit is already published and only derived work is cancelled. */
812
+ derivedOnly: boolean, reason?: string);
813
+ }
866
814
  /**
867
- * Assert that a number is a points measurement. A compile-time cast; see {@link twips}.
815
+ * The read side of cancellation, handed to cooperative work.
868
816
  *
869
- * @public
817
+ * Cooperative rather than pre-emptive: long work calls `checkpoint()` at declared intervals, which
818
+ * is what lets an operation unwind promptly without the engine having to interrupt it mid-mutation.
870
819
  */
871
- declare const points$1: (value: number) => Points;
820
+ interface CancellationToken {
821
+ readonly isCancelled: boolean;
822
+ readonly phase: CancellationPhase;
823
+ /** True only when cancelled AND the commit is already published. */
824
+ readonly derivedOnly: boolean;
825
+ /** Throws CancellationError if cancelled; call at declared checkpoint intervals. */
826
+ checkpoint(): void;
827
+ }
872
828
  /**
873
- * The one twips-to-points conversion. Exact division; twips are the finer unit.
829
+ * The write side of cancellation, and the owner of the phase.
874
830
  *
875
- * @public
831
+ * Whoever starts an operation holds this; everything it calls gets only the token.
876
832
  */
877
- declare const twipsToPoints: (value: Twips) => Points;
833
+ declare class CancellationController {
834
+ private _cancelled;
835
+ private _phase;
836
+ private _reason?;
837
+ cancel(reason?: string): void;
838
+ /** Mark canonical publication — the point of no return. */
839
+ markPublished(): void;
840
+ get isCancelled(): boolean;
841
+ get phase(): CancellationPhase;
842
+ get isPublished(): boolean;
843
+ /** Cancelled after publication -> full rollback is no longer possible. */
844
+ get derivedOnly(): boolean;
845
+ get token(): CancellationToken;
846
+ }
847
+
848
+ /** Injectable time source — the engine never calls Date.now directly (determinism). */
849
+ interface ClockPort {
850
+ now(): number;
851
+ }
852
+ /** Stable id minting for sessions, commits, and allocator seeds. */
853
+ interface IdentityPort {
854
+ newId(): string;
855
+ }
856
+ /** Cooperative scheduling of deferred work. */
857
+ interface SchedulingPort {
858
+ schedule(task: () => void): void;
859
+ }
860
+ /** Redacted observability sink (raw text never enters here — design D5). */
861
+ interface AuditPort {
862
+ record(entry: {
863
+ readonly kind: string;
864
+ readonly at: number;
865
+ readonly meta?: Record<string, unknown>;
866
+ }): void;
867
+ }
868
+ /** Read/write/export authorization decisions. */
869
+ interface AuthorizationPort {
870
+ authorize(action: string, subject?: string): boolean;
871
+ }
872
+ /** Explicit consent gate for any remote/external resource (no zero-click fetch). */
873
+ interface ExternalResourceConsentPort {
874
+ requestConsent(target: string): boolean;
875
+ }
876
+ /** Cancellation source for the current operation. */
877
+ interface CancellationPort {
878
+ readonly token: CancellationToken;
879
+ }
880
+ /** Where documents are stored and retrieved. Opaque marker until its milestone. */
881
+ interface PersistencePort {
882
+ readonly kind: 'persistence';
883
+ }
884
+ /** How the engine talks to a remote peer. Opaque marker until its milestone. */
885
+ interface TransportPort {
886
+ readonly kind: 'transport';
887
+ }
888
+ /** Where font bytes come from. Opaque marker until its milestone. */
889
+ interface FontPort {
890
+ readonly kind: 'font';
891
+ }
892
+ /** Which shaping backend measures text. Opaque marker until its milestone. */
893
+ interface ShapingPort {
894
+ readonly kind: 'shaping';
895
+ }
896
+ /** How image bytes are decoded. Opaque marker until its milestone. */
897
+ interface ImagePort {
898
+ readonly kind: 'image';
899
+ }
900
+ /** Where resource consumption is reported. Opaque marker until its milestone. */
901
+ interface ResourceAccountingPort {
902
+ readonly kind: 'resource-accounting';
903
+ }
878
904
  /**
879
- * The one points-to-twips conversion.
880
- *
881
- * Rounds to the nearest twip: every OOXML twips measurement is integral, and emitting a
882
- * fractional twip would write a value no consumer of the file can represent.
905
+ * A required port the runtime did not provide.
883
906
  *
884
- * @public
907
+ * Thrown rather than falling back: silently reaching for a browser global when a worker or server
908
+ * adapter did not supply a port is exactly the coupling ports exist to prevent.
885
909
  */
886
- declare const pointsToTwips: (value: Points) => Twips;
910
+ declare class PortResolutionError extends Error {
911
+ readonly portId: string;
912
+ constructor(portId: string);
913
+ }
887
914
  /**
888
- * Read a twips attribute (`ST_TwipsMeasure`, `ST_SignedTwipsMeasure`, or a `dxa` table width)
889
- * as a whole, signed number of twips, or `null` when the value is not a measurement.
890
- *
891
- * The schema allows an integer or a universal measure, but files also carry decimal twips
892
- * (`4743.74`, `708.0`, `707.9999999999998`). The accepted forms read as follows:
893
- *
894
- * - A decimal truncates toward zero: `1443.74` is 1443 and `-720.6` is -720. A leading `+`,
895
- * leading zeros, a bare fraction (`.5`) and a trailing point (`1440.`) are accepted.
896
- * - A universal measure (`mm`, `cm`, `in`, `pt`, `pc`, `pi`) converts exactly and then
897
- * truncates: `1.27cm` is 720 and `0.333in` is 479. Its sign is ignored, so `-0.5in` is 720.
898
- * - Whitespace, exponents, hexadecimal, uppercase units, and an integer part longer than nine
899
- * digits are not measurements.
900
- *
901
- * The result is NOT bounded. Callers apply their own sign rule and range clamp, because each
902
- * attribute has a different valid range and a different fallback.
915
+ * The environment-dependent services an engine instance may reach.
903
916
  *
904
- * @internal
917
+ * Everything outside pure computation — the clock, identity minting, scheduling, fonts, images —
918
+ * is reached only through here, which is what lets browser, worker and server adapters supply
919
+ * only what their runtime actually offers.
905
920
  */
906
- declare function readTwipsMeasure(raw: string | undefined): number | null;
907
-
908
- /** Outline heading shape consumed by TOC planning (mirrors DocumentOutlineEntry). */
909
- interface TocOutlineHeading {
910
- readonly text: string;
911
- readonly level: number;
912
- readonly blockId: string;
921
+ declare class PortRegistry {
922
+ private readonly ports;
923
+ provide(portId: string, port: unknown): this;
924
+ has(portId: string): boolean;
925
+ /** Resolve a port by id or throw. */
926
+ resolve<T>(portId: string): T;
927
+ /** Assert every id in `portIds` is present; returns the missing ones (empty if all present). */
928
+ missing(portIds: readonly string[]): string[];
929
+ /** The provided port ids (for bridging to registry.resolve availablePorts). */
930
+ availablePorts(): string[];
931
+ clock(): ClockPort;
932
+ identity(): IdentityPort;
933
+ }
934
+ /** Deterministic clock: starts at `start` and advances by `step` each call. */
935
+ declare class DeterministicClock implements ClockPort {
936
+ private readonly step;
937
+ private t;
938
+ constructor(start?: number, step?: number);
939
+ now(): number;
940
+ }
941
+ /** Sequential identity for tests and deterministic fixtures. */
942
+ declare class SequentialIdentity implements IdentityPort {
943
+ private readonly prefix;
944
+ private n;
945
+ constructor(prefix?: string);
946
+ newId(): string;
913
947
  }
914
948
 
915
- /** Left-indent step between TOC levels, in twips (matches `scripts/demo-doc/toc-block.xml`). */
916
- declare const TOC_LEVEL_INDENT_TWIPS: Twips;
917
- /** Bounded left-indent twips for a TOC entry level (0-based heading depth). */
918
- declare function tocLeftIndentTwips(level: number): Twips;
919
- /** One planned TOC entry: its level, its text, and the heading it points at. */
920
- interface TocEntryPlan {
921
- readonly level: number;
922
- readonly text: string;
923
- readonly headingParagraphId: string;
924
- readonly bookmarkName: string;
925
- readonly pageNumberText: string;
926
- /** The heading's symbols, one per {@link TOC_SYMBOL_MARK} in `text`, in order. */
927
- readonly symbols?: readonly TocSymbol[];
949
+ /** What one operation is started with. Only `ports` is required. */
950
+ interface OperationInit {
951
+ readonly ports: PortRegistry;
952
+ readonly limits?: Partial<ResourceLimits>;
953
+ readonly config?: Readonly<Record<string, unknown>>;
954
+ /** Root budget capacity in abstract units (default: the byte decompression limit). */
955
+ readonly capacity?: number;
956
+ /** Operation identity (from IdentityPort in production); defaulted for tests. */
957
+ readonly id?: string;
928
958
  }
929
- /** Build one TOC entry paragraph node. */
930
- declare function buildTocEntryParagraph(mint: () => string, entry: TocEntryPlan, instruction: TocInstruction, paragraphPropertiesTemplate?: OoxmlNode): OoxmlNode;
931
- /** Build an SDT-wrapped complex TOC field with an already planned cached result. */
932
- declare function buildTocContentControl(mint: () => string, entries: readonly TocEntryPlan[], instruction: TocInstruction, alias: string): OoxmlNode;
933
959
  /**
934
- * Word flattens manual line/tab breaks from a heading into spaces in its TOC cache.
935
- * Carrying them verbatim makes a short title wrap even when the row has ample room, and the
936
- * same normalization is what lets a cached row be matched back to the heading it came from.
937
- */
938
- declare function tocEntryText(text: string): string;
939
- /**
940
- * Plan TOC entries from the outline and existing bookmarks.
941
- *
942
- * `actorId` is explicit because this planner runs BEFORE the store transaction, so the
943
- * ambient transaction actor is not bound. Callers that already have the collaboration
944
- * identity pass it here. `resolveAllocationActor` still honours a
945
- * `runWithTransactionActor` wrap when the caller prefers that seam.
946
- */
947
- declare function planTocEntries(part: OoxmlPart, outline: readonly TocOutlineHeading[], instruction: TocInstruction, pageNumberByParagraphId: ReadonlyMap<string, string>, excludeParagraphIds: ReadonlySet<string>, actorId?: string): {
948
- readonly entries: readonly TocEntryPlan[];
949
- readonly bookmarksToCreate: readonly {
950
- paragraphId: string;
951
- name: string;
952
- }[];
953
- };
954
- /** Create bookmarkStart/End pair nodes for insertion at the start of a paragraph. */
955
- declare function bookmarkPairNodes(mint: () => string, name: string, id: string): {
956
- readonly start: OoxmlNode;
957
- readonly end: OoxmlNode;
958
- };
959
-
960
- /**
961
- * The heading each cached result row stands for, aligned with `toc.resultParagraphIds`.
960
+ * The frozen environment of one operation: its limits, config, ports, root budget and
961
+ * cancellation controller.
962
962
  *
963
- * `null` for a row that names no heading this document still has — a stale row, or the
964
- * chrome/blank paragraphs a cached result can carry. Callers leave those alone rather than
965
- * writing another row's number into them.
963
+ * IMMUTABLE for the operation's lifetime, which is what lets cache reuse and replica agreement
964
+ * key on it. A new operation always gets a fresh snapshot rather than observing a mutated one.
966
965
  */
967
- declare function resolveTocRowHeadings(part: OoxmlPart, toc: DetectedToc, outline: readonly TocOutlineHeading[], excludeParagraphIds: ReadonlySet<string>): readonly (string | null)[];
968
-
969
- /** One paragraph in the index: its node id, its story, and its position. */
970
- interface ParagraphIndexEntry {
971
- /** Canonical tree node id — the stable identity operations address. */
972
- readonly nodeId: string;
973
- /** Position among the story's paragraphs, in document order. */
974
- readonly ordinal: number;
975
- /** Concatenated text content, with tabs and breaks mapped as the model reads them. */
976
- readonly text: string;
977
- /** Node ids of the paragraph's runs, in order. */
978
- readonly runIds: readonly string[];
979
- }
980
- /** One story root — body, a header/footer variant, or a note part. */
981
- interface StoryIndexEntry {
982
- /** The part the story lives in. */
983
- readonly partName: string;
984
- /** The `w:body` (or story root) node id. */
985
- readonly rootId: string;
986
- readonly paragraphs: readonly ParagraphIndexEntry[];
987
- }
988
- /** One style definition, indexed by the id content references it under. */
989
- interface StyleIndexEntry {
990
- readonly styleId: string;
991
- readonly type: string;
992
- readonly nodeId: string;
993
- readonly name: string | null;
994
- readonly basedOn: string | null;
995
- /** Whether the part marks this the default style for its type (`w:default="1"`). */
996
- readonly isDefault: boolean;
966
+ interface OperationContext {
967
+ readonly id: string;
968
+ readonly limits: ResourceLimits;
969
+ readonly config: Readonly<Record<string, unknown>>;
970
+ readonly ports: PortRegistry;
971
+ readonly budget: Budget;
972
+ readonly cancellation: CancellationController;
997
973
  }
998
974
  /**
999
- * The derived lookups over a part: paragraphs, stories and styles by id.
975
+ * Begin an operation, capturing its immutable environment snapshot.
1000
976
  *
1001
- * Diff-patched on commit rather than rebuilt — rebuilding every index per keystroke is what made
1002
- * typing scale with document length.
977
+ * Resolves and freezes limits and configuration, carves the root budget, and creates the
978
+ * cancellation controller — everything downstream work needs, fixed for the operation's duration.
1003
979
  */
1004
- interface OoxmlIndexes {
1005
- /** The tree revision these projections were derived from. */
1006
- readonly revision: number;
1007
- /** Body story first, then any other story parts, keyed by part name. */
1008
- readonly stories: ReadonlyMap<string, StoryIndexEntry>;
1009
- /** Every paragraph across every story, keyed by node id. */
1010
- readonly paragraphs: ReadonlyMap<string, ParagraphIndexEntry>;
1011
- readonly relationships: ReadonlyMap<string, readonly RelationshipRecord[]>;
1012
- readonly styles: ReadonlyMap<string, StyleIndexEntry>;
1013
- }
1014
- /** Style definitions, read out of the generic `w:styles` tree. */
1015
- declare function indexStyles(part: OoxmlPart | undefined): Map<string, StyleIndexEntry>;
1016
- /** The package's style definitions part, if it has one. */
1017
- declare function stylesPartOf(pkg: OoxmlPackage): OoxmlPart | undefined;
980
+ declare function beginOperation(init: OperationInit): OperationContext;
981
+ /** Release the operation's root budget (requires every child/reservation released). */
982
+ declare function endOperation(ctx: OperationContext): void;
983
+
984
+ /** What a limit counts. Typed so a byte cap and a depth cap cannot be compared by accident. */
985
+ type LimitUnit = 'bytes' | 'count' | 'depth' | 'ratio' | 'passes';
1018
986
  /**
1019
- * Derive every index from one canonical package revision.
987
+ * Where a limit is enforced.
1020
988
  *
1021
- * Pure: the same package and revision always produce the same index, which is what makes
1022
- * "rebuild rather than mutate" a safe invalidation strategy.
989
+ * Declared per limit so enforcement happens at the boundary that can still refuse cheaply — a zip
990
+ * cap checked during layout has already let the bomb decompress.
1023
991
  */
1024
- declare function deriveOoxmlIndexes(pkg: OoxmlPackage, revision: number): OoxmlIndexes;
1025
-
1026
- /** One paragraph's meaning, independent of how it was spelled in XML. */
1027
- interface ParagraphDigest {
1028
- /** Ordinal identity within its story. Node ids are NOT used: a reopened package
1029
- * legitimately re-derives them, and requiring them to match would test the id scheme
1030
- * rather than the content. */
1031
- readonly ordinal: number;
1032
- /**
1033
- * Where the paragraph SITS: the element path from the story root, so a paragraph that
1034
- * moves out of the table cell it was written in is a difference rather than a paragraph
1035
- * with the same ordinal and the same text.
1036
- */
1037
- readonly path: string;
1038
- /**
1039
- * `w14:paraId`, uppercase-normalized (matching is case-insensitive), or null. This is
1040
- * MANAGED identity — the agent contract anchors on it and comment threading references
1041
- * it — so a serializer silently dropping it must be a digest difference. Other paragraph
1042
- * attributes (`w:rsidR` …) stay deliberately undigested: they are revision noise.
1043
- */
1044
- readonly paraId: string | null;
1045
- /** Text content, including tabs and hard breaks as their characters. */
1046
- readonly text: string;
1047
- /** Accepted paragraph properties, as sorted tokens including nested children. */
1048
- readonly paragraphProperties: readonly string[];
1049
- /** Per-run accepted properties, in run order. */
1050
- readonly runProperties: readonly (readonly string[])[];
1051
- /** Fingerprints of every generic (unknown) subtree, in document order. */
1052
- readonly genericStructure: readonly string[];
1053
- }
1054
- /** One story reduced to its semantic content, ignoring everything a normalized re-emit changes. */
1055
- interface StoryDigest {
1056
- readonly partName: string;
1057
- readonly paragraphs: readonly ParagraphDigest[];
1058
- /**
1059
- * Every block-level element of the part OUTSIDE a paragraph, in document order, as
1060
- * `path name(attributes)` — the tables, rows, cells, sections, content controls and
1061
- * their property children, each at the position that says what contains it.
1062
- *
1063
- * A paragraph's own internals are not here (they are its `ParagraphDigest`), which keeps
1064
- * this list proportional to a document's STRUCTURE rather than its text.
1065
- */
1066
- readonly structure: readonly string[];
992
+ type EnforcementPhase = 'package-read' | 'xml-parse' | 'layout' | 'output';
993
+ /** One limit's unit and enforcement phase — the metadata that makes limits testable uniformly. */
994
+ interface LimitSpec {
995
+ readonly unit: LimitUnit;
996
+ readonly phase: EnforcementPhase;
1067
997
  }
998
+ /** Unit + enforcement phase for every resource limit. */
999
+ declare const LIMIT_SPECS: Readonly<Record<keyof ResourceLimits, LimitSpec>>;
1000
+ /** Every limit name, for iterating the specs and asserting each has a unit and phase. */
1001
+ declare const LIMIT_KEYS: (keyof ResourceLimits)[];
1002
+ /** A phase-scoped overflow-safe counter for one limit. */
1003
+ declare function makeLimitCounter(limits: ResourceLimits, key: keyof ResourceLimits): BoundedCounter;
1068
1004
  /**
1069
- * The whole document's semantic content — one of the D9 losslessness oracles.
1070
- *
1071
- * What a save/reopen round-trip is compared on. Byte identity applies only to non-XML parts;
1072
- * modelled parts re-emit normalized, so equality is asserted HERE rather than on bytes.
1005
+ * Deterministic memory substitute. Tracks current and peak "allocated" units
1006
+ * against a hard byte ceiling; `allocate` fails closed (LimitExceededError) past
1007
+ * the limit. Used in place of process RSS so memory-limit tests are reproducible
1008
+ * and cancellation/cleanup can be asserted without a real allocator.
1073
1009
  */
1074
- interface SemanticDigest {
1075
- readonly stories: readonly StoryDigest[];
1010
+ declare class DeterministicMemoryMeter {
1011
+ private readonly counter;
1012
+ private current;
1013
+ private peak;
1014
+ constructor(limitBytes: number);
1015
+ allocate(bytes: number): void;
1016
+ free(bytes: number): void;
1017
+ get currentBytes(): number;
1018
+ get peakBytes(): number;
1019
+ /** Release everything (cancellation/cleanup path). */
1020
+ reset(): void;
1076
1021
  }
1077
- /** Where two digests diverge — the diagnostic when a round-trip loses something. */
1078
- type DigestDifference = {
1079
- readonly path: string;
1080
- readonly before: string;
1081
- readonly after: string;
1022
+ /** Assert the finite-default / hard-ceiling invariant holds for every limit. */
1023
+ declare function assertLimitInvariants(): void;
1024
+
1025
+ /** Any JSON value. The domain canonicalization and stable hashing operate over. */
1026
+ type Json = null | boolean | number | string | Json[] | {
1027
+ [k: string]: Json;
1082
1028
  };
1083
- /** Digest one part: its paragraphs, and the structure that holds them. */
1084
- declare function digestPart(part: OoxmlPart): StoryDigest | null;
1085
- /** Digest every part, in the given order. */
1086
- declare function semanticDigest(parts: Iterable<OoxmlPart>): SemanticDigest;
1087
1029
  /**
1088
- * Every way two digests differ, as readable paths.
1089
- *
1090
- * Returns the differences rather than a boolean because "the round trip lost something" is
1091
- * useless without saying what — the failure this oracle exists to catch is a silent drop,
1092
- * and a bare `false` reproduces the silence.
1030
+ * Produce a canonical string for `value`, dropping any object key whose name is
1031
+ * in `ephemera` at any depth. Numbers are emitted losslessly; -0 normalizes to 0;
1032
+ * non-finite numbers are rejected (they must never enter a comparator input).
1093
1033
  */
1094
- declare function diffSemanticDigests(before: SemanticDigest, after: SemanticDigest): DigestDifference[];
1034
+ declare function canonicalize(value: unknown, ephemera?: ReadonlySet<string>): string;
1035
+ /** 64-bit FNV-1a over the canonical form, returned as a 16-char hex string. */
1036
+ declare function stableHash(value: unknown, ephemera?: ReadonlySet<string>): string;
1095
1037
 
1096
- /** Valid per MS-DOCX: 8 hex digits, non-zero, below 0x80000000. */
1097
- declare function isValidParaId(value: string): boolean;
1098
- /** The authored `w14:paraId` value of an element, verbatim, or null. */
1099
- declare function paraIdOf(node: OoxmlNode): string | null;
1100
- /** FNV-1a over UTF-16 code units — the deterministic mint every id derivation shares. */
1101
- declare function fnv1a32(value: string): number;
1102
- /**
1103
- * Deterministic 8-hex mint in (0x00000000, 0x80000000), collision-free against
1104
- * `used` (uppercase hex). Same seed + same used-set → same value, which is what
1105
- * keeps `splitParagraphMany` byte-identical to its equivalent single splits and
1106
- * a reopened save identical to the session that produced it.
1107
- */
1108
- declare function mintParaId(seed: string, used: ReadonlySet<string>): string;
1109
- /** Every valid `w14:paraId` in the tree, uppercased. Memoized per root object. */
1110
- declare function usedParaIds(root: OoxmlElement): ReadonlySet<string>;
1111
- /**
1112
- * The non-empty prefix the part ROOT binds to the w14 namespace, or null.
1113
- *
1114
- * Minted attributes require an in-scope binding (the invariant validator reports
1115
- * `invalid-qname` otherwise, and the serializer would allocate an `nsN` alias).
1116
- * Only the root binding counts: a binding authored on some descendant does not
1117
- * cover paragraphs elsewhere in the tree.
1118
- */
1119
- declare function w14RootPrefix(root: OoxmlElement): string | null;
1120
- /** The minted `[w14:paraId, w14:textId]` pair. `textId` mirrors `paraId`: it has no uniqueness requirement, no consumer reads it, and one allocator is simpler than two — but Word writes both, so we write both. */
1121
- declare function mintedParagraphIdentityAttributes(prefix: string, value: string): readonly OoxmlAttribute[];
1122
1038
  /**
1123
- * Load-time paragraph-identity normalization for a session's main part.
1124
- *
1125
- * Keeps every valid, first-seen paraId verbatim; mints (deterministically, seeded
1126
- * by the paragraph's structural node id) for paragraphs whose id is missing,
1127
- * malformed, zero, out of range, or a duplicate. Adds the root `xmlns` binding
1128
- * the minted attributes need. Returns the INPUT PART REFERENCE when there is
1129
- * nothing to do — a document Word saved yesterday re-serializes byte-identical.
1130
- *
1131
- * Attribute-only rebuilds move no child, so every node keeps its structural-path
1132
- * id; the copy respreads only the ancestors of changed paragraphs.
1039
+ * How one artifact class is compared.
1133
1040
  *
1134
- * Fail-open: if the rebuilt part does not validate (a bug, or a pathology the
1135
- * prefix choice could not defuse), the original part is returned — a document
1136
- * must never become unopenable over an identity enhancement.
1041
+ * Frozen per artifact: nothing here invents a tolerance for an artifact the spec requires to
1042
+ * match exactly.
1137
1043
  */
1138
- declare function normalizeParagraphIdentity(part: OoxmlPart): OoxmlPart;
1139
-
1140
- /** A text position: a paragraph's node id and a UTF-16 offset in its text. */
1141
- interface BookmarkPosition {
1142
- readonly paragraphId: string;
1143
- readonly offset: number;
1144
- }
1145
- /** One bookmark, both ends resolved. */
1146
- interface BookmarkRecord {
1147
- /** The name as the document writes it. */
1148
- readonly name: string;
1149
- /** Names starting with an underscore are hidden. */
1150
- readonly hidden: boolean;
1151
- /** The marker pair's `@w:id`. */
1152
- readonly markerId: string;
1153
- readonly start: BookmarkPosition;
1154
- readonly end: BookmarkPosition;
1155
- /** Node ids of the two markers. */
1156
- readonly startNodeId: string;
1157
- readonly endNodeId: string;
1158
- /**
1159
- * How many marker pairs in the part carry this name, compared without case: 1, or more
1160
- * where concurrent edits left copies. The record is the first; the rest are not listed.
1161
- */
1162
- readonly copies: number;
1163
- }
1164
- /** List each name once, using its first marker pair in document order. @public */
1165
- declare function listBookmarks(part: OoxmlPart): readonly BookmarkRecord[];
1166
-
1167
- /** One `w:docPart` of the glossary, with the blocks its body holds. */
1168
- interface BuildingBlock {
1169
- /** `w:docPartPr/w:name`, the identity a control's placeholder and a pick refer to. */
1170
- readonly name: string;
1171
- /** `w:category/w:gallery`, an `ST_DocPartGallery` value such as `docParts`. */
1172
- readonly gallery: string;
1173
- /** `w:category/w:name`; `General` when the block declares none. */
1174
- readonly category: string;
1175
- /** The `w:docPartBody` children: paragraphs, tables and block controls, in order. */
1176
- readonly blocks: readonly OoxmlNode[];
1044
+ type ComparatorMode = 'canonical-exact' | 'exact' | 'tolerance' | 'sync-optimization-only';
1045
+ /** One artifact class's comparison mode and its declared ephemera — fields excluded from equality. */
1046
+ interface ComparatorDescriptor {
1047
+ readonly id: string;
1048
+ readonly mode: ComparatorMode;
1049
+ /** Object keys excluded from canonical-exact comparison (declared ephemera). */
1050
+ readonly ephemera: readonly string[];
1051
+ readonly note: string;
1177
1052
  }
1178
- /** The gallery and category a `w:docPartList` control lists. */
1179
- interface BuildingBlockGalleryFilter {
1180
- readonly gallery: string;
1181
- readonly category: string | null;
1053
+ /** The frozen artifact comparator set. */
1054
+ declare const COMPARATORS: {
1055
+ readonly authoredState: {
1056
+ readonly id: "dev.docx-editor.core.comparator.authored-state";
1057
+ readonly mode: 'canonical-exact';
1058
+ readonly ephemera: readonly ["revision", "provenance", "producedAt", "commitId"];
1059
+ readonly note: 'canonical normalized authored records; ephemera excluded';
1060
+ };
1061
+ readonly anchor: {
1062
+ readonly id: "dev.docx-editor.core.comparator.anchor";
1063
+ readonly mode: 'exact';
1064
+ readonly ephemera: readonly [];
1065
+ readonly note: 'internal anchor identity/affinity compares exactly';
1066
+ };
1067
+ readonly yjsStateVector: {
1068
+ readonly id: "dev.docx-editor.core.comparator.yjs-state-vector";
1069
+ readonly mode: 'sync-optimization-only';
1070
+ readonly ephemera: readonly [];
1071
+ readonly note: 'exchange optimization only; never proves update or delete-set coverage';
1072
+ };
1073
+ readonly shapedRun: {
1074
+ readonly id: "dev.docx-editor.core.comparator.shaped-run";
1075
+ readonly mode: 'exact';
1076
+ readonly ephemera: readonly [];
1077
+ readonly note: 'glyph ids, clusters, and fixed-point advances compare exactly';
1078
+ };
1079
+ readonly paginationFingerprint: {
1080
+ readonly id: "dev.docx-editor.core.comparator.pagination-fingerprint";
1081
+ readonly mode: 'exact';
1082
+ readonly ephemera: readonly [];
1083
+ readonly note: 'page/column boundaries, break causes, fixed-point geometry compare exactly';
1084
+ };
1085
+ readonly semanticTree: {
1086
+ readonly id: "dev.docx-editor.core.comparator.semantic-tree";
1087
+ readonly mode: 'exact';
1088
+ readonly ephemera: readonly [];
1089
+ readonly note: 'reading order, roles, headings, alt text compare exactly';
1090
+ };
1091
+ readonly hitTest: {
1092
+ readonly id: "dev.docx-editor.core.comparator.hit-test";
1093
+ readonly mode: 'exact';
1094
+ readonly ephemera: readonly [];
1095
+ readonly note: 'resolved hit target and cluster affinity compare exactly';
1096
+ };
1097
+ readonly pdfSemantics: {
1098
+ readonly id: "dev.docx-editor.core.comparator.pdf-semantics";
1099
+ readonly mode: 'canonical-exact';
1100
+ readonly ephemera: readonly ["objectNumber", "producer", "creationDate", "modDate", "subsetTag"];
1101
+ readonly note: 'canonical semantic PDF objects; container ephemera excluded';
1102
+ };
1103
+ readonly rasterCheckpoint: {
1104
+ readonly id: "dev.docx-editor.core.comparator.raster-checkpoint";
1105
+ readonly mode: 'tolerance';
1106
+ readonly ephemera: readonly [];
1107
+ readonly note: 'documented unavoidable raster comparison; explicit tolerance only';
1108
+ };
1109
+ readonly benchmarkEvidence: {
1110
+ readonly id: "dev.docx-editor.core.comparator.benchmark-evidence";
1111
+ readonly mode: 'sync-optimization-only';
1112
+ readonly ephemera: readonly [];
1113
+ readonly note: 'diagnostic evidence, not an equivalence basis';
1114
+ };
1115
+ };
1116
+ /** Which frozen comparator to use. */
1117
+ type ComparatorName = keyof typeof COMPARATORS;
1118
+ /** Whether two artifacts matched, and where they diverged when they did not. */
1119
+ interface ComparisonResult {
1120
+ readonly equal: boolean;
1121
+ /** Canonical forms when unequal (diagnostic). */
1122
+ readonly left?: string;
1123
+ readonly right?: string;
1182
1124
  }
1183
- /** The glossary part, through the main part's relationship or the conventional name. */
1184
- declare function glossaryPartOf(pkg: OoxmlPackage): OoxmlPart | null;
1185
- /** Every named building block of the glossary, in authored order. Empty without a glossary. */
1186
- declare function buildingBlocksOf(pkg: OoxmlPackage): readonly BuildingBlock[];
1187
- /** The gallery a `w:docPartList` control lists, or null for any other control. */
1188
- declare function buildingBlockGalleryOf(control: OoxmlNode): BuildingBlockGalleryFilter | null;
1189
1125
  /**
1190
- * The blocks a gallery control offers: same gallery, and the control's category when it names
1191
- * one. Sorted by category, then name, as Word groups its list.
1126
+ * Compare two artifacts under a named comparator. `canonical-exact` and `exact`
1127
+ * compare canonical forms (exact drops no ephemera). `tolerance` requires an
1128
+ * epsilon and compares finite numbers structurally. `sync-optimization-only`
1129
+ * throws — such artifacts are never an equivalence basis.
1192
1130
  */
1193
- declare function buildingBlocksForControl(pkg: OoxmlPackage, control: OoxmlNode): readonly BuildingBlock[];
1131
+ declare function compareArtifacts(name: ComparatorName, left: unknown, right: unknown, opts?: {
1132
+ epsilon?: number;
1133
+ }): ComparisonResult;
1134
+ /** Fingerprint an artifact under a named comparator's canonicalization policy. */
1135
+ declare function fingerprint(name: ComparatorName, value: unknown): string;
1194
1136
 
1137
+ /** Whether a recorded conformance step was expected to commit or be refused. */
1138
+ type FixtureOutcome = 'applied' | 'aborted' | 'validation' | 'conflict' | 'resource' | 'authorization';
1139
+ /** The change one step produced, summarized to what a fixture can compare across runtimes. */
1140
+ interface ModelChangeSummary {
1141
+ readonly fromRevision: number;
1142
+ readonly toRevision: number;
1143
+ readonly origin: string;
1144
+ readonly dirty: readonly string[];
1145
+ readonly dependencyKeys: readonly string[];
1146
+ }
1147
+ /** Where anchors sat after a step — how a fixture proves positions survived an edit. */
1148
+ interface AnchorSnapshot {
1149
+ readonly anchorId: string;
1150
+ readonly story: string;
1151
+ readonly block: string;
1152
+ readonly affinity: 'before' | 'after';
1153
+ }
1154
+ /** What one step must produce: its outcome, its change summary, and its authored-state hash. */
1155
+ interface FixtureExpectation {
1156
+ readonly outcome: FixtureOutcome;
1157
+ /** Present iff outcome === 'applied'; MUST be > baseRevision. */
1158
+ readonly committedRevision?: number;
1159
+ readonly modelChange?: ModelChangeSummary;
1160
+ /** 16-hex authored-state fingerprint (comparator 0.4). */
1161
+ readonly authoredStateHash?: string;
1162
+ readonly outputHash?: string;
1163
+ readonly anchors?: readonly AnchorSnapshot[];
1164
+ }
1165
+ /** One recorded operation and what it was expected to do. */
1166
+ interface FixtureStep {
1167
+ readonly baseRevision: number;
1168
+ readonly origin: string;
1169
+ /** Opaque DocOp payloads (schema owned by section 4). */
1170
+ readonly ops: readonly unknown[];
1171
+ readonly expect: FixtureExpectation;
1172
+ }
1173
+ /** Opaque encoded backend bytes (snapshot or replication update). */
1174
+ interface EncodedEnvelope {
1175
+ readonly kind: 'snapshot' | 'update';
1176
+ readonly protocolVersion: number;
1177
+ readonly schemaVersion: number;
1178
+ readonly documentId: string;
1179
+ readonly byteLength: number;
1180
+ /** Hex-encoded opaque bytes; length MUST equal byteLength. */
1181
+ readonly bytesHex: string;
1182
+ }
1183
+ /** Which implementation recorded a fixture — local store, Yjs, or a binding. */
1184
+ type FixtureSource = {
1185
+ readonly kind: 'create';
1186
+ } | {
1187
+ readonly kind: 'docx';
1188
+ readonly sha256: string;
1189
+ readonly bytesRef: string;
1190
+ };
1195
1191
  /**
1196
- * Which story a comment deletion or resolution may read markers from.
1192
+ * The frozen conformance container: revisions, origins, operations, changes, snapshots and
1193
+ * hashes.
1197
1194
  *
1198
- * `w:id` is not unique across stories: a header and the body can both hold comment 1.
1195
+ * The CONTAINER is what is frozen — field names, origin membership, revision monotonicity, hash
1196
+ * format. The op and change payloads travel opaquely, so their schemas can evolve without
1197
+ * invalidating every recorded fixture.
1199
1198
  */
1200
- interface CommentDeletionOwner {
1201
- /** Canonical name of the part holding the story's markers. */
1202
- readonly storyPartName: string;
1203
- /**
1204
- * When the story is one note inside a shared notes part, that note's `w:id`.
1205
- * Absent means the whole part is the story (body, header, footer).
1206
- */
1207
- readonly noteId?: number;
1199
+ interface ConformanceFixture {
1200
+ readonly formatVersion: 1;
1201
+ readonly documentId: string;
1202
+ readonly source: FixtureSource;
1203
+ readonly steps: readonly FixtureStep[];
1204
+ readonly snapshots?: readonly EncodedEnvelope[];
1205
+ readonly updates?: readonly EncodedEnvelope[];
1206
+ }
1207
+ /** Whether a fixture is well-formed, listing every structural violation. */
1208
+ interface ValidationResult {
1209
+ readonly valid: boolean;
1210
+ readonly errors: readonly string[];
1208
1211
  }
1212
+ /** Structurally validate a fixture against the frozen format rules. */
1213
+ declare function validateFixture(fixture: ConformanceFixture): ValidationResult;
1209
1214
 
1215
+ /** The outcome a store reports for one replayed step. */
1216
+ interface ReplayOutcome {
1217
+ readonly outcome: FixtureOutcome;
1218
+ readonly committedRevision?: number;
1219
+ /** Canonical authored-state value AFTER the step (hashed via comparator 0.4). */
1220
+ readonly authoredState?: unknown;
1221
+ }
1222
+ /** The interface a conformance runtime implements so one fixture can drive every backend. */
1223
+ interface ReplayStore {
1224
+ /** Initialize from the fixture source; return the initial revision (0 for create). */
1225
+ init(fixture: ConformanceFixture): number;
1226
+ /** Apply one step and report what happened. */
1227
+ applyStep(step: FixtureStep): ReplayOutcome;
1228
+ }
1229
+ /** What replaying a fixture produced: per-step outcomes and where they diverged. */
1230
+ interface ReplayReport {
1231
+ readonly ok: boolean;
1232
+ readonly mismatches: readonly string[];
1233
+ }
1210
1234
  /**
1211
- * Delete a comment thread: that story's markers, and metadata no other story still names.
1212
- *
1213
- * `owner` names the story whose markers may be stripped. Omitted, it is the main document
1214
- * body — never every XML part. A header or note that reused the same `w:id` is a different
1215
- * remark; pass its part (and `noteId` when the story is one note) to delete that one.
1216
- *
1217
- * Returns the package unchanged when the id names no comment, and null when a removal was
1218
- * refused — a caller inside a transaction rolls back rather than committing a package whose
1219
- * comment is half gone.
1235
+ * Replay a fixture against a store and check every step's outcome, committed
1236
+ * revision, and authored-state fingerprint against the fixture's expectations.
1220
1237
  */
1221
- declare function deleteCommentThread(pkg: OoxmlPackage, commentId: string, owner?: CommentDeletionOwner): OoxmlPackage | null;
1238
+ declare function replayFixture(fixture: ConformanceFixture, store: ReplayStore): ReplayReport;
1239
+ /** Hash a store's authored state, so two runtimes replaying one fixture can be compared. */
1240
+ declare function hashAuthored(authoredState: unknown): string;
1241
+
1222
1242
  /**
1223
- * Delete every comment the edit between `before` and `after` emptied in `owner`'s story.
1224
- *
1225
- * `owner` omitted is the main document body, matching {@link deleteCommentThread}.
1243
+ * Keys that must never be assigned from file data.
1226
1244
  *
1227
- * THE TEST, exactly, and per story. A comment dies when the words it covered in that story are
1228
- * gone and nothing in that story still places it. A neighbour that reused the same `w:id` is
1229
- * judged under its own owner. Notes that vanished (reference cascade) are reaped as their own
1230
- * stories so a unique id still loses its `comments.xml` body. Remaining-marker overflow
1231
- * preserves comments/commentEx/commentId metadata rather than deleting records the scan could
1232
- * not prove unused. Null is reserved for a removal that actually failed.
1245
+ * XML attribute names become object keys, and a document controls those names — assigning
1246
+ * `__proto__` into an ordinary object is the prototype-pollution hazard this engine audits for.
1233
1247
  */
1234
- declare function cascadeEmptiedComments(before: OoxmlPackage, after: OoxmlPackage, owner?: CommentDeletionOwner): OoxmlPackage | null;
1235
-
1236
- /** Presentation settings the document itself states. Absence is the answer, never an error. */
1237
- interface DocumentViewSettings {
1238
- /**
1239
- * `w:doNotShadeFormData` (§17.15.1.49) — do NOT shade legacy form fields.
1240
- *
1241
- * Word draws a grey block behind every `w:ffData` field so a reader can find the blanks in a
1242
- * form. It is on by DEFAULT, and this element is how a document turns it off, so the absent
1243
- * case means shading. Reading the name as "shade form data" inverts it and blanks the shading
1244
- * on exactly the documents that asked for it loudest.
1245
- *
1246
- * Never printed, by Word or by us: it marks where to type, and a printed form has no blanks
1247
- * to find.
1248
- */
1249
- readonly doNotShadeFormData: boolean;
1248
+ declare const DANGEROUS_KEYS: readonly string[];
1249
+ /** A file-derived key that would pollute a prototype. Refused, never sanitized-and-accepted. */
1250
+ declare class DangerousKeyError extends Error {
1251
+ readonly key: string;
1252
+ readonly path: string;
1253
+ constructor(key: string, path: string);
1250
1254
  }
1251
- /** The defaults a document with no `settings.xml` gets: everything Word would shade, shaded. */
1252
- declare const DEFAULT_VIEW_SETTINGS: DocumentViewSettings;
1253
- /** Read the view settings from a `settings.xml` root, or the defaults when it has none. */
1254
- declare function readViewSettings(settingsRoot: OoxmlNode | null | undefined): DocumentViewSettings;
1255
+ /** Whether a key is one of {@link DANGEROUS_KEYS}. */
1256
+ declare function isDangerousKey(key: string): boolean;
1257
+ /** A fresh null-prototype record — the only object shape parser intermediates use. */
1258
+ declare function nullRecord<T = unknown>(): Record<string, T>;
1259
+ /**
1260
+ * Recursively convert `value` into null-prototype records, rejecting any
1261
+ * dangerous object key. Arrays and primitives pass through (arrays rebuilt so no
1262
+ * inherited prototype pollution survives). Throws DangerousKeyError on the first
1263
+ * unsafe key, naming its path. Cyclic inputs are rejected.
1264
+ */
1265
+ declare function toSafeRecord(value: unknown, path?: string): unknown;
1255
1266
 
1256
- /** How deep transparent block wrappers may nest before the walk stops descending. */
1257
- declare const MAX_STORY_SDT_NESTING = 32;
1258
- /** Which kind of story a root is. Layout walks all four the same way. */
1259
- type OoxmlStoryKind = 'body' | 'header' | 'footer' | 'note';
1260
- /** One story root and the blocks under it, with transparent block wrappers already flattened. */
1261
- interface OoxmlStoryRoot {
1262
- readonly kind: OoxmlStoryKind;
1263
- /** The `w:body` / `w:hdr` / `w:ftr` / `w:footnote` element that holds the blocks. */
1264
- readonly root: OoxmlNode;
1265
- }
1267
+ /** Locate `[Content_Types].xml` bytes regardless of zip key spelling. */
1268
+ declare function contentTypesPartBytes(pkg: OoxmlPackage): {
1269
+ readonly storageKey: string;
1270
+ readonly bytes: Uint8Array;
1271
+ } | null;
1266
1272
  /**
1267
- * Every story root in a part, in document order.
1273
+ * What a package-level invariant walk found wrong.
1268
1274
  *
1269
- * Does not descend INTO a story: a story's blocks are the walk below, and a story root never
1270
- * contains another story root.
1275
+ * Package invariants are cross-PART: a relationship pointing at a part that does not exist, or a
1276
+ * part no content-type record covers. Neither is visible from inside a single part's tree.
1271
1277
  */
1272
- declare function storyRootsOf(part: OoxmlPart): readonly OoxmlStoryRoot[];
1278
+ type PackageInvariantCode = 'dangling-relationship' | 'missing-content-type'
1279
+ /** A part occupies a name OPC reserves for package infrastructure. */
1280
+ | 'reserved-part-name'
1281
+ /** Two parts whose names differ only by case, which OPC treats as one part. */
1282
+ | 'duplicate-part-name'
1283
+ /** A part name the OPC screens refuse; `writeZip` would throw on save. */
1284
+ | 'unsafe-part-name';
1285
+ /** One package invariant violation, located by part name and relationship id. */
1286
+ interface PackageInvariantIssue {
1287
+ readonly code: PackageInvariantCode;
1288
+ /** The part the issue is about: the missing target, or the part with no type. */
1289
+ readonly partName: string;
1290
+ /** For a dangling relationship, the owner that points at nothing. */
1291
+ readonly ownerPart?: string;
1292
+ }
1293
+ /** Whether a package satisfies its cross-part invariants, listing every violation otherwise. */
1294
+ type PackageInvariantResult = {
1295
+ readonly ok: true;
1296
+ } | {
1297
+ readonly ok: false;
1298
+ readonly issues: readonly PackageInvariantIssue[];
1299
+ };
1300
+ /** Relationship records owned by one part, in authored order. */
1301
+ declare function relationshipsOf(pkg: OoxmlPackage, ownerPart: string): readonly RelationshipRecord[];
1302
+ /** The content type that resolves for a part name, or null when nothing declares one. */
1303
+ declare function resolveContentTypeOf(pkg: OoxmlPackage, partName: string): string | null;
1304
+ /** The `.rels` part that owns a part's relationships, by OPC convention. */
1305
+ declare function relsPartNameFor(partName: string): string;
1273
1306
  /**
1274
- * Whether a part is SHAPED to hold stories — a body, a header, a footer, or a notes part.
1307
+ * Declare a content type for a part, by upserting an `<Override>` in the content-types tree.
1275
1308
  *
1276
- * A shape test, deliberately not `storyRootsOf(part).length > 0`. That counts EDITABLE stories,
1277
- * and a notes part holding nothing but `w:separator` and `w:continuationSeparator` has none —
1278
- * which is the footnotes part Word writes into every document that ever held a footnote. A
1279
- * caller asking "should I walk this part at all" that used the editable count skipped it
1280
- * entirely, and the two callers that ask are a payload sweep and an export strip: both DELETE
1281
- * what they conclude nothing names.
1309
+ * A no-op when the part already resolves to the same type, so repeating a write does not append
1310
+ * a duplicate entry. Resolving to a DIFFERENT type replaces the existing Override in place.
1311
+ * `forceOverride` writes an explicit Override even when a Default already matches.
1282
1312
  */
1283
- declare function isStoryPart(part: OoxmlPart): boolean;
1284
- /** The main body story of a part, or null when the part holds none. */
1285
- declare function bodyStoryRoot(part: OoxmlPart): OoxmlNode | null;
1313
+ declare function withContentTypeOverride(pkg: OoxmlPackage, partName: string, contentType: string, options?: {
1314
+ readonly forceOverride?: boolean;
1315
+ }): OoxmlPackage;
1316
+ /** What {@link withoutPart} answers: `ok: false` leaves the package exactly as it was. */
1317
+ interface WithoutPartResult {
1318
+ readonly pkg: OoxmlPackage;
1319
+ readonly ok: boolean;
1320
+ }
1321
+ /** Add or replace one relationship on a part, returning a new package. */
1322
+ declare function withRelationship(pkg: OoxmlPackage, ownerPart: string, type: string, rawTarget: string): {
1323
+ readonly pkg: OoxmlPackage;
1324
+ readonly relationshipId: string;
1325
+ readonly ok: boolean;
1326
+ };
1286
1327
  /**
1287
- * Every paragraph of one story, in reading order.
1328
+ * Add a part that does not exist yet, with the content-type override it needs to be openable.
1288
1329
  *
1289
- * Descends through tables (rows, cells, nested tables), block controls and block custom XML.
1290
- * The returned nodes are paragraph elements; a caller addresses them by `id`.
1330
+ * Deliberately does NOT create a relationship: a part is reachable because something points at
1331
+ * it, and which part points at it is the caller's decision. Creating one here would guess.
1291
1332
  */
1292
- declare function storyParagraphs(root: OoxmlNode): readonly OoxmlNode[];
1333
+ declare function withNewPart(pkg: OoxmlPackage, partName: string, root: OoxmlElement, contentType: string): OoxmlPackage;
1293
1334
  /**
1294
- * The block walk itself, appending into `out`.
1335
+ * The two invariants a package must satisfy before it is published.
1295
1336
  *
1296
- * Exported so `allParagraphs` in the binding lane is the same traversal rather than a second
1297
- * copy of it.
1337
+ * Both describe the half-written state that splitting a multi-part write across transactions
1338
+ * produces, and both make a package Word refuses to open:
1339
+ *
1340
+ * - a relationship pointing at a part nobody created;
1341
+ * - a part with no content type, which is unopenable even though the XML is well formed.
1342
+ *
1343
+ * Checked at the commit boundary rather than inside each primitive, for the same reason part
1344
+ * validation moved there: a transaction is allowed to pass through an inconsistent intermediate
1345
+ * as long as nothing can observe it.
1298
1346
  */
1299
- declare function collectStoryParagraphs(children: readonly OoxmlNode[], out: OoxmlNode[], sdtDepth: number): void;
1347
+ declare function validatePackageInvariants(pkg: OoxmlPackage): PackageInvariantResult;
1300
1348
 
1301
- /** One searchable text-box story and the drawing location that can reveal it. */
1302
- interface TextboxStoryRoot {
1303
- /** The `w:txbxContent` story root. */
1304
- readonly root: OoxmlElement;
1305
- /** Layout atom id used to select the drawing, including an MC wrapper when present. */
1306
- readonly drawingNodeId: string;
1307
- /** Canonical node id of the paragraph that anchors the drawing. */
1308
- readonly hostParagraphId: string;
1309
- }
1310
1349
  /**
1311
- * List the text-box stories in one part, in document order.
1350
+ * Whether a string is a valid XML NCName in the strict ASCII profile — a name with no colon.
1312
1351
  *
1313
- * The bounded walk stops at drawings. It does not project pictures, shape geometry, or story
1314
- * content. Results are memoized by immutable part-root identity.
1352
+ * This is the rule for names the engine itself writes or accepts from a caller. Names read
1353
+ * from a document use {@link isXmlNCName}, which admits every XML 1.0 name.
1315
1354
  */
1316
- declare function textboxStoriesInPart(part: OoxmlPart): readonly TextboxStoryRoot[];
1317
-
1318
- /** Relationship from the story to one data part. */
1319
- declare const CUSTOM_XML_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";
1320
- /** Relationship from a data part to the properties that carry its `ds:itemID`. */
1321
- declare const CUSTOM_XML_PROPS_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps";
1322
- /** `itemPropsN.xml` needs an Override; `itemN.xml` rides the package's `xml` default. */
1323
- declare const CUSTOM_XML_PROPS_TYPE = "application/vnd.openxmlformats-officedocument.customXmlProperties+xml";
1324
- /** The datastore namespace `ds:datastoreItem` lives in. */
1325
- declare const DATASTORE_NAMESPACE_URI = "http://schemas.openxmlformats.org/officeDocument/2006/customXml";
1326
- /** A data part located in a package: where it lives, and the GUID an SDT binds to. */
1327
- interface CustomXmlDataPart {
1328
- /** Canonical part name, e.g. `/customXml/item1.xml`. */
1329
- readonly partName: string;
1330
- /** Part name of its properties, e.g. `/customXml/itemProps1.xml`. */
1331
- readonly propsPartName: string;
1332
- /** `ds:itemID`, braced and upper-case, as `w:storeItemID` must spell it. */
1333
- readonly itemId: string;
1334
- /** Namespace URI of the payload root, which is what identifies one store among several. */
1335
- readonly namespaceUri: string;
1355
+ declare function isValidNCName(name: string): boolean;
1356
+ /** A QName is an optional `prefix:` (both NCNames) — never attacker-derived. */
1357
+ declare function isValidQName(name: string): boolean;
1358
+ /**
1359
+ * Validate a qualified name, throwing when it is malformed.
1360
+ *
1361
+ * Guards the serializer: an invalid QName written into XML produces a file Word cannot open, so
1362
+ * it fails here rather than at save.
1363
+ */
1364
+ declare function assertValidQName(name: string): void;
1365
+ /**
1366
+ * Controlled namespace-prefix allocation: deterministic, collision-free prefixes
1367
+ * for namespace URIs. A known URI always yields the same registered prefix; new
1368
+ * URIs get a generated `ns{n}` prefix, never one derived from file content.
1369
+ */
1370
+ declare class PrefixAllocator {
1371
+ private readonly byUri;
1372
+ private readonly usedPrefixes;
1373
+ private counter;
1374
+ constructor(known?: Readonly<Record<string, string>>);
1375
+ prefixFor(namespaceUri: string): string;
1376
+ /** The declared bindings, for emitting xmlns declarations. */
1377
+ bindings(): {
1378
+ prefix: string;
1379
+ uri: string;
1380
+ }[];
1336
1381
  }
1382
+
1337
1383
  /**
1338
- * A `ds:itemID` derived from the seed rather than drawn at random.
1339
- *
1340
- * Exported so a caller that authors a store OUTSIDE a package — a template engine splicing
1341
- * markup it will assemble into a `.docx` later — mints the id the same way this does, rather
1342
- * than inventing a second GUID shape Word has to be tolerant of.
1384
+ * A hyperlink target projected for a RUNTIME sink, or the reason it was withheld.
1343
1385
  *
1344
- * The store is a pure function of what it is asked to write: the same document written twice
1345
- * has to produce the same bytes, or a save/reopen/save round trip stops being a fixed point
1346
- * and every digest taken over saved bytes moves. A GUID's job here is uniqueness within one
1347
- * package, not unguessability, so four FNV-1a passes over a salted seed carry it.
1386
+ * The authored target stays authored — the record layer keeps it verbatim and escapes it into
1387
+ * owned OOXML, which is not a runtime sink. Only this allowlist-sanitized projection reaches DOM,
1388
+ * CSS, navigation or fetch, so `javascript:`, `data:` and `vbscript:` never leave the boundary.
1348
1389
  */
1349
- declare function datastoreItemIdFor(seed: string): string;
1390
+ type HrefProjection = {
1391
+ readonly ok: true;
1392
+ readonly href: string;
1393
+ } | {
1394
+ readonly ok: false;
1395
+ readonly inert: true;
1396
+ };
1350
1397
  /**
1351
- * The data parts a story relates to, in relationship order.
1352
- *
1353
- * Reads the relationships rather than scanning `/customXml/` by name: a part nothing relates
1354
- * to is not part of the document, and a package from a hostile sender can hold as many
1355
- * plausibly-named files as it likes.
1398
+ * Project a file-derived URL for a DOM/navigation sink. Strips embedded
1399
+ * tab/LF/CR (used to smuggle `java\nscript:`), allows relative URLs and the
1400
+ * scheme allowlist, and renders everything else inert. Never fetches.
1356
1401
  */
1357
- declare function customXmlDataParts(pkg: OoxmlPackage, storyPartName: string): CustomXmlDataPart[];
1358
- /** The data part carrying this namespace, or null when the document has none yet. */
1359
- declare function findCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string): CustomXmlDataPart | null;
1360
- /** What {@link withCustomXmlDataPart} answers. */
1361
- interface CustomXmlDataPartResult {
1362
- readonly pkg: OoxmlPackage;
1363
- /** Null when the part could not be authored; the package then comes back unchanged. */
1364
- readonly part: CustomXmlDataPart | null;
1365
- }
1402
+ declare function sanitizeHref(raw: string): HrefProjection;
1403
+ /** XML-escape a value for validated serialization back into owned OOXML. */
1404
+ declare function escapeXml(value: string): string;
1405
+ /** XML-escape a value bound for an ATTRIBUTE, preserving CR/LF/TAB across a re-parse. @internal */
1406
+ declare function escapeXmlAttribute(value: string): string;
1366
1407
  /**
1367
- * Ensure the document carries a data part for this namespace, creating it and everything it
1368
- * needs — properties, both relationships, the properties content type — when it does not.
1369
- *
1370
- * Idempotent: a package that already has one for the namespace comes back untouched, so a
1371
- * second node added to the same store does not author a second store.
1408
+ * Validate that a string contains only characters legal in XML 1.0 text/attributes. escapeXml
1409
+ * handles the markup-significant characters but leaves control chars, U+FFFE/U+FFFF, and unpaired
1410
+ * surrogates — which would emit malformed XML (or be silently mangled during UTF-8 encoding). Returns
1411
+ * false so a caller can reject fail-closed. Valid supplementary characters (correct surrogate pairs)
1412
+ * pass.
1372
1413
  *
1373
- * @param storyPartName - Whose relationships the store hangs off. Word enumerates its data
1374
- * store from the MAIN DOCUMENT part, so a store authored off a header or footer is one Word
1375
- * never sees.
1414
+ * @internal
1376
1415
  */
1377
- declare function withCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string, rootLocalName: string): CustomXmlDataPartResult;
1416
+ declare function isValidXmlText(value: string): boolean;
1378
1417
  /**
1379
- * Remove a store from a document: both parts, both relationships, the Override.
1380
- *
1381
- * PACKAGE ONLY. It does not touch the body, so a `w:sdt` bound to the store keeps its
1382
- * `w:dataBinding`, its `w:storeItemID` and its `w:tag`, and Word then opens a control bound to
1383
- * a store that is not there. Stripping those is the export path's job and is not built; this
1384
- * is the half that removes the payload, which is the half a caller can rely on.
1418
+ * CSS string-escape a file-derived value (e.g. an `@font-face` family name or an
1419
+ * inline style value). Emits `\<hex> ` escapes for quotes, backslash, and
1420
+ * controls so the value cannot break out of its CSS string.
1421
+ */
1422
+ declare function escapeCssString(value: string): string;
1423
+ /** Whether a file-derived CSS fragment contains a `url()` or `@import` (must be rejected). */
1424
+ declare function containsCssFetch(value: string): boolean;
1425
+ /**
1426
+ * Content that must never execute or auto-resolve: OLE objects, macros, DDE and `INCLUDE*` field
1427
+ * instructions.
1385
1428
  *
1386
- * `ok: false` means nothing was removed and the package is unchanged — most often because an
1387
- * owner's `.rels` was never parsed into a tree, which {@link withoutPart} refuses to work
1388
- * around. A caller exporting a document has to treat that as a failure to export rather than
1389
- * as a document with nothing to strip, or it ships the payload it meant to remove.
1429
+ * Rendered INERT by default rather than stripped — removing it would be a lossless-preservation
1430
+ * failure, so it is carried and never acted on.
1390
1431
  */
1391
- declare function withoutCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string): WithoutPartResult;
1392
-
1393
- /** One node in a store: its id, the bound text, and the payload beside it. */
1394
- interface CustomXmlNode {
1432
+ declare const INERT_EXECUTABLE_KINDS: readonly ['field-dde', 'field-include', 'macro', 'activex', 'ole', 'embedded-object', 'executable-relationship'];
1433
+ /** One of {@link INERT_EXECUTABLE_KINDS}. */
1434
+ /** One of {@link INERT_EXECUTABLE_KINDS} — content carried but never executed. */
1435
+ type InertExecutableKind = (typeof INERT_EXECUTABLE_KINDS)[number];
1436
+ /** Whether a content kind is one this engine refuses to execute or auto-resolve. */
1437
+ declare function isInertExecutable(kind: string): boolean;
1438
+ /** Whether a field instruction's leading keyword is safe to evaluate. */
1439
+ declare function isEvaluableField(instruction: string): boolean;
1440
+ /** One item a scrub pass considers: its kind, and where in the package it sits. */
1441
+ interface ContentItem {
1395
1442
  readonly id: string;
1396
- /** Text a `w:dataBinding` resolves to. Untrusted file input. */
1397
- readonly label: string;
1398
- /** Payload as authored, JSON by convention and unparsed here. Untrusted file input. */
1399
- readonly data: string;
1443
+ readonly kind: string;
1400
1444
  }
1401
- /** Every node a store holds, in document order. */
1402
- declare function customXmlNodes(pkg: OoxmlPackage, partName: string): CustomXmlNode[];
1403
- /** One node by id, or null. */
1404
- declare function readCustomXmlNode(pkg: OoxmlPackage, partName: string, nodeId: string): CustomXmlNode | null;
1405
1445
  /**
1406
- * Write a node, replacing one that already has the id.
1446
+ * What an explicit scrub removed.
1407
1447
  *
1408
- * Replace rather than append: a second node with the same id makes the binding xpath ambiguous,
1409
- * and Word resolves an ambiguous xpath to the first match — so an "update" that appended would
1410
- * leave the control showing its old text forever.
1448
+ * A scrub DECLARES ITSELF non-lossless: removing executable content changes the file, which is a
1449
+ * choice a caller makes deliberately rather than a default the engine applies.
1411
1450
  */
1412
- declare function withCustomXmlNode(pkg: OoxmlPackage, partName: string, node: CustomXmlNode): OoxmlPackage;
1413
- /** Drop one node by id. A store that never held it comes back unchanged. */
1414
- declare function withoutCustomXmlNode(pkg: OoxmlPackage, partName: string, nodeId: string): OoxmlPackage;
1451
+ interface ScrubResult {
1452
+ readonly kept: readonly ContentItem[];
1453
+ readonly removed: readonly ContentItem[];
1454
+ /** A scrub that removes anything is non-lossless by definition. */
1455
+ readonly nonLossless: boolean;
1456
+ }
1457
+ /** Explicit scrub export: remove inert executable classes, report removals. */
1458
+ declare function scrubExport(items: readonly ContentItem[]): ScrubResult;
1459
+
1460
+ /** True for an `m:oMathPara` display equation. */
1461
+ declare function isOmmlDisplay(node: OoxmlNode): node is OoxmlGenericElementNode;
1462
+ /** True for a paragraph-level equation atom: `m:oMath` or `m:oMathPara`. */
1463
+ declare function isOmmlEquationAtom(node: OoxmlNode): node is OoxmlGenericElementNode;
1464
+ /** The `m:oMath` equations of a display, or the equation itself for an inline `m:oMath`. */
1465
+ declare function equationsOfAtom(node: OoxmlNode): readonly OoxmlGenericElementNode[];
1415
1466
  /**
1416
- * Drop every node no longer referenced, given the ids the document still binds.
1417
- *
1418
- * This is the whole deletion story. Deleting a control in THIS editor can remove its node
1419
- * directly, but a control deleted in Word leaves the node behind — Word has no lifecycle link
1420
- * between the two and no way to run our code. Reconciling against what the story actually binds
1421
- * collects both, and is the only thing that can collect the second.
1422
- *
1423
- * Takes the referenced ids rather than reading the story itself: the caller already walked it,
1424
- * and a sweep that guessed at which controls exist would delete a payload on a mistake.
1467
+ * Project a display equation as one layout atom. A display with one equation keeps that
1468
+ * equation's projection, so the painted atom names the editable `m:oMath`. Several equations
1469
+ * share one row, separated by an em space.
1425
1470
  */
1426
- declare function withoutOrphanCustomXmlNodes(pkg: OoxmlPackage, partName: string, referencedIds: ReadonlySet<string>): {
1427
- readonly pkg: OoxmlPackage;
1428
- readonly removed: readonly string[];
1429
- };
1471
+ declare function projectOmmlDisplay(node: OoxmlNode): OmmlEquationProjection | null;
1430
1472
  /**
1431
- * The `w:xpath` a binding uses to reach a node's label, or null when the id cannot be addressed.
1432
- *
1433
- * Word needs a prefix for the payload namespace even when the store declares it as a default —
1434
- * an unprefixed step in an XPath means "no namespace", so `/docxEditor/node` would match
1435
- * nothing in a namespaced store. The prefix is declared in `w:prefixMappings` beside it.
1473
+ * The physical justification of a paragraph whose only visible content is display math.
1474
+ * `m:oMathParaPr/m:jc` sets it; the default `centerGroup` centers the display. Returns null
1475
+ * when the paragraph also holds text or other content, which keeps the paragraph `w:jc`.
1436
1476
  */
1437
- declare function customXmlLabelXPath(prefix: string, rootLocalName: string, nodeId: string): string | null;
1438
- /** The `w:prefixMappings` value declaring that prefix, or null when it cannot be written.
1439
- * The namespace sits inside single quotes inside a double-quoted attribute, so a namespace
1440
- * carrying either quote character has no representation here. */
1441
- declare function customXmlPrefixMappings(prefix: string, namespaceUri: string): string | null;
1477
+ declare function displayMathAlignment(paragraph: {
1478
+ readonly children: readonly OoxmlNode[];
1479
+ }): 'left' | 'center' | 'right' | null;
1480
+ /** Project a paragraph-level equation atom: inline `m:oMath` or display `m:oMathPara`. */
1481
+ declare function projectOmmlAtom(node: OoxmlNode): OmmlEquationProjection | null;
1442
1482
 
1443
- /** The prefix every binding this library authors declares and quotes. */
1444
- declare const CUSTOM_NODE_XPATH_PREFIX = "ns0";
1445
- /** The three attributes a `w:dataBinding` carries. */
1446
- interface CustomNodeBinding {
1447
- readonly prefixMappings: string;
1448
- readonly xpath: string;
1449
- readonly storeItemId: string;
1483
+ /** UTF-16 placeholder for a page break in paragraph text projections. */
1484
+ declare const PAGE_BREAK_CHAR = "\f";
1485
+ /** What a `w:br` breaks. `other` covers values this engine does not model, kept losslessly. */
1486
+ type HardBreakKind = 'line' | 'page' | 'column' | 'other';
1487
+ /** Read the semantic break kind from a typed `w:br` node. */
1488
+ declare function hardBreakKind(node: OoxmlHardBreakNode): HardBreakKind;
1489
+ /** Whether a node is a `w:br` with `w:type="page"`. */
1490
+ declare function isPageBreakNode(node: OoxmlNode): node is OoxmlHardBreakNode;
1491
+ /** Text projection for a `w:br` node — one UTF-16 code unit per break. */
1492
+ declare function hardBreakText(node: OoxmlHardBreakNode): string;
1493
+ /** Attributes for a newly authored `w:br`. */
1494
+ declare function hardBreakAttributes(kind: 'line' | 'page'): OoxmlHardBreakNode['attributes'];
1495
+
1496
+ /** Parsed TOC instruction. Unknown switches are ignored for generation but left on the wire. */
1497
+ interface TocInstruction {
1498
+ readonly keyword: 'TOC';
1499
+ /** Include hyperlinks (`\\h`). */
1500
+ readonly hyperlink: boolean;
1501
+ /** Omit page numbers (`\\n`). */
1502
+ readonly omitPageNumbers: boolean;
1503
+ /** Inclusive 1-based outline levels from `\\o "n-m"`. Defaults 1–9. */
1504
+ readonly outlineStart: number;
1505
+ readonly outlineEnd: number;
1506
+ readonly raw: string;
1450
1507
  }
1508
+ /** Longest TOC field instruction read. Instructions come from a file; the parse is bounded. */
1509
+ declare const TOC_MAX_INSTRUCTION_CHARS = 256;
1510
+ /** Most entries one generated table of contents may hold. */
1511
+ declare const TOC_MAX_ENTRIES = 512;
1512
+ /** Most bookmarks minted during one TOC refresh. */
1513
+ declare const TOC_MAX_BOOKMARKS_PER_REFRESH = 512;
1514
+ /** Deepest nested field instruction followed. Caps recursion on file-supplied structure. */
1515
+ declare const TOC_MAX_FIELD_NESTING = 4;
1451
1516
  /**
1452
- * The binding for one node in one store, or null when the id cannot be addressed by an XPath.
1517
+ * Most layout passes a TOC refresh runs before settling.
1453
1518
  *
1454
- * Refuses rather than escapes: XPath 1.0 has no escape for a quote inside a literal, so an id
1455
- * carrying one could close the predicate and append an expression of the sender's choosing.
1456
- * The ids are minted by a host, so refusing is honest — see `ADDRESSABLE_ID`.
1519
+ * Page numbers change the TOC's own height, which changes page numbers — bounded so a
1520
+ * non-converging document stops rather than looping.
1457
1521
  */
1458
- declare function customNodeBinding(part: CustomXmlDataPart, rootLocalName: string, nodeId: string): CustomNodeBinding | null;
1522
+ declare const TOC_MAX_PAGE_PASSES = 3;
1459
1523
  /**
1460
- * Every node id the story's controls bind to in one store, in document order.
1524
+ * Parse a TOC field instruction string.
1461
1525
  *
1462
- * This is the input the orphan sweep takes: a node whose id is not in here is one no control
1463
- * names, whether it was deleted in this editor or in Word. Reading it from the STORY rather than
1464
- * from a host's bookkeeping is what makes the two cases one mechanism.
1526
+ * Returns null when the leading keyword is not TOC, the string is over-long, or the
1527
+ * outline range is hostile. Does not evaluate or fetch anything.
1465
1528
  */
1466
- declare function boundCustomXmlNodeIds(part: OoxmlPart, storeItemId: string): Set<string>;
1529
+ declare function parseTocInstruction(raw: string): TocInstruction | null;
1530
+
1531
+ /** A table of contents found in a document, whether wrapped in an SDT or a bare field. */
1532
+ interface DetectedToc {
1533
+ /** Enclosing control id when it identifies one TOC, otherwise the begin fldChar id. */
1534
+ readonly id: string;
1535
+ readonly beginNodeId: string;
1536
+ readonly beginParagraphId: string;
1537
+ readonly endParagraphId: string;
1538
+ readonly resultParagraphIds: readonly string[];
1539
+ /** Direct parent whose paragraph children delimit the cached result. */
1540
+ readonly containerId: string;
1541
+ readonly contentControlId?: string;
1542
+ readonly instruction: TocInstruction;
1543
+ }
1544
+ /** Discover refreshable body TOCs without evaluating any field instruction. */
1545
+ declare function detectBodyTocs(part: OoxmlPart): readonly DetectedToc[];
1546
+ /** Locate the table of contents containing a position, or null. */
1547
+ declare function findDetectedToc(tocs: readonly DetectedToc[], tocId: string): DetectedToc | null;
1548
+
1549
+ /** One heading symbol: its `w:font` and `w:char`, as the file wrote them. */
1550
+ interface TocSymbol {
1551
+ readonly font: string | null;
1552
+ readonly char: string;
1553
+ }
1554
+
1555
+ declare const TWIPS: unique symbol;
1556
+ declare const POINTS: unique symbol;
1467
1557
  /**
1468
- * Every node id ANY story in the package binds, in one store.
1558
+ * A length in twentieths of a point — OOXML's `ST_TwipsMeasure` / `ST_SignedTwipsMeasure`.
1469
1559
  *
1470
- * THE WHOLE PACKAGE, not one story. A payload is reachable from a header as easily as from the
1471
- * body — Word enumerates its data store from the main part, but nothing stops a control
1472
- * elsewhere quoting the same `w:storeItemID` — and the two callers that decide what is an orphan
1473
- * both destroy data when they are wrong. The sweep would collect a payload a header still paints;
1474
- * the export would strip a store a header still names, which is a document Word offers to repair.
1560
+ * The brand is intersection-typed onto `number`, so a `Twips` value still decays to `number`
1561
+ * wherever arithmetic consumes it; only the reverse direction — passing a raw number where
1562
+ * `Twips` is declared — is refused.
1475
1563
  *
1476
- * Costs one walk per story per store, on open and on export. Neither is a keystroke.
1564
+ * @public
1477
1565
  */
1478
- declare function boundCustomXmlNodeIdsInPackage(pkg: OoxmlPackage, storeItemId: string): Set<string>;
1479
- /** The node id one control binds to in the named store, or null when it binds to nothing there. */
1480
- declare function boundCustomXmlNodeIdOf(control: OoxmlNode, storeItemId: string): string | null;
1481
-
1482
- /** What happens to one control when the document is exported. */
1483
- type CustomNodeExportPolicy = 'keep' | 'text' | 'remove';
1484
- /** How {@link withExportedCustomNodes} decides, and which stores it may tidy afterwards. */
1485
- interface CustomNodeExportRequest {
1486
- /** The story whose controls are policed, and whose relationships the stores hang off. */
1487
- readonly storyPartName: string;
1488
- /**
1489
- * Payload namespaces the caller CLAIMS.
1490
- *
1491
- * The store cleanup runs only over these. Word's own Cover Page Properties store rides in most
1492
- * templates, and an export that tidied every customXml part would be deleting from documents it
1493
- * was only asked to strip its own markup from.
1494
- */
1495
- readonly namespaces: readonly string[];
1496
- /** The fate of a control carrying this `w:tag`. A control with no tag is never touched. */
1497
- readonly decide: (tag: string) => CustomNodeExportPolicy;
1498
- }
1566
+ type Twips = number & {
1567
+ readonly [TWIPS]: true;
1568
+ };
1499
1569
  /**
1500
- * The export, or the reason there is no export.
1570
+ * A length in typographic points, the unit layout and paint compute in.
1501
1571
  *
1502
- * A refusal answers no package on purpose. "Stripping failed, here is the document anyway" is the
1503
- * one outcome that must not be possible: a caller would ship the markup it asked to remove and
1504
- * have been told the export succeeded.
1572
+ * @public
1505
1573
  */
1506
- type CustomNodeExportResult = {
1507
- readonly ok: true;
1508
- readonly pkg: OoxmlPackage;
1509
- /** Controls unwrapped (`text`) and controls removed (`remove`). */
1510
- readonly unwrapped: number;
1511
- readonly removed: number;
1512
- } | {
1513
- readonly ok: false;
1514
- readonly reason: string;
1574
+ type Points = number & {
1575
+ readonly [POINTS]: true;
1515
1576
  };
1516
1577
  /**
1517
- * Apply the policy, then take the payloads and the stores the policy orphaned.
1578
+ * Twentieths of a point per point.
1518
1579
  *
1519
- * Order matters and is the reverse of the write's. The BODY goes first, so the sweep that follows
1520
- * sees the controls that actually survive; the stores go last, once nothing binds them. Doing it
1521
- * the other way would strip a store while a control still quoted its `w:storeItemID`, which is a
1522
- * document Word opens and offers to repair.
1580
+ * @public
1523
1581
  */
1524
- declare function withExportedCustomNodes(pkg: OoxmlPackage, request: CustomNodeExportRequest): CustomNodeExportResult;
1525
-
1526
- /** Named inputs a formula may read besides adjust values and earlier formulas. @internal */
1527
- interface VmlFormulaContext {
1528
- readonly width: number;
1529
- readonly height: number;
1530
- readonly adjust: readonly number[];
1531
- }
1532
- /** The values of `eqns` in order, or null when one cannot be read. @internal */
1533
- declare function vmlFormulaValues(eqns: readonly string[], context: VmlFormulaContext): readonly number[] | null;
1534
-
1535
- /** The VML namespace. @internal */
1536
- declare const VML = "urn:schemas-microsoft-com:vml";
1537
- /** The Office VML extensions namespace (`o:`). @internal */
1538
- declare const OFFICE = "urn:schemas-microsoft-com:office:office";
1539
- /** The Word VML extensions namespace (`w10:`). @internal */
1540
- declare const WORD_VML = "urn:schemas-microsoft-com:office:word";
1541
- /** Whether `node` is the element `ns`:`name`. @internal */
1542
- declare const named: (node: OoxmlNode, ns: string, name: string) => boolean;
1543
- /** The value of attribute `name` in namespace `ns` (none by default). @internal */
1544
- declare const attribute: (node: OoxmlElement, name: string, ns?: string) => string | undefined;
1545
- /** The element children of `node`. @internal */
1546
- declare function children(node: OoxmlElement): OoxmlElement[];
1547
- /** Whether a VML boolean is false (`f`, `false`, `0`). @internal */
1548
- declare const off: (value: string | undefined) => boolean;
1549
- /** A plain decimal number, bounded; NaN when malformed. @internal */
1550
- declare function numeric(value: string | undefined): number;
1551
- /** A VML length in points (`pt`, `in`, `cm`, `mm`, `px`); NaN when malformed. @internal */
1552
- declare function points(value: string | undefined): number;
1582
+ declare const TWIPS_PER_POINT = 20;
1553
1583
  /**
1554
- * VML rotation in degrees (`315`, `-45`, or `fd` sixty-five-thousandths). NaN when malformed.
1584
+ * Assert that a number is a twips measurement.
1555
1585
  *
1556
- * @internal
1586
+ * A compile-time cast, not a validator: bounds, integrality and finiteness stay at the parse
1587
+ * boundary that produced the number, exactly where they are enforced today. The cast is the
1588
+ * caller's claim about the UNIT, nothing more.
1589
+ *
1590
+ * @public
1557
1591
  */
1558
- declare function legacyRotationDegrees(raw: string | undefined): number;
1559
- /** The declarations of a `style` attribute by lower-case name; null when malformed. @internal */
1560
- declare function styleOf(node: OoxmlElement): ReadonlyMap<string, string> | null;
1561
- /** A VML colour as `#rrggbb`, `fallback` when absent; null when malformed. @internal */
1562
- declare function color(value: string | undefined, fallback: string): string | null;
1563
-
1564
- interface GeometryGuide {
1565
- readonly name: string;
1566
- readonly op: string;
1567
- readonly args: readonly string[];
1568
- }
1569
-
1570
- type GeometryFill = 'norm' | 'none' | 'lighten' | 'lightenLess' | 'darken' | 'darkenLess';
1571
- type GeometryCommand = {
1572
- readonly op: 'M' | 'L';
1573
- readonly args: readonly [string, string];
1574
- } | {
1575
- readonly op: 'A';
1576
- readonly args: readonly [string, string, string, string];
1577
- } | {
1578
- readonly op: 'Q';
1579
- readonly args: readonly [string, string, string, string];
1580
- } | {
1581
- readonly op: 'C';
1582
- readonly args: readonly [string, string, string, string, string, string];
1583
- } | {
1584
- readonly op: 'Z';
1585
- readonly args: readonly [];
1586
- };
1587
- interface GeometryPath {
1588
- /** Path coordinate space; 0 means the shape's own EMU space on that axis. */
1589
- readonly width: number;
1590
- readonly height: number;
1591
- readonly fill: GeometryFill;
1592
- readonly stroke: boolean;
1593
- readonly commands: readonly GeometryCommand[];
1594
- }
1595
- /** @internal */
1596
- interface GeometryDefinition {
1597
- readonly avLst: readonly GeometryGuide[];
1598
- readonly gdLst: readonly GeometryGuide[];
1599
- /** Text rectangle guide references l/t/r/b; null means the whole shape. */
1600
- readonly textRect: readonly [string, string, string, string] | null;
1601
- readonly paths: readonly GeometryPath[];
1602
- }
1603
- interface GeometryPoint {
1604
- readonly x: number;
1605
- readonly y: number;
1606
- }
1607
- interface EvaluatedSubpath {
1608
- readonly points: readonly GeometryPoint[];
1609
- readonly closed: boolean;
1610
- }
1611
- interface EvaluatedPath {
1612
- readonly subpaths: readonly EvaluatedSubpath[];
1613
- readonly fill: GeometryFill;
1614
- readonly stroke: boolean;
1615
- }
1616
- /** @internal */
1617
- interface EvaluatedGeometry {
1618
- readonly paths: readonly EvaluatedPath[];
1619
- readonly textRect: Readonly<{
1620
- left: number;
1621
- top: number;
1622
- right: number;
1623
- bottom: number;
1624
- }>;
1625
- }
1626
- /** The ECMA-376 preset geometry named by `a:prstGeom/@prst`, or null for an unknown name. @internal */
1627
- declare function presetGeometryDefinition(name: string): GeometryDefinition | null;
1592
+ declare const twips: (value: number) => Twips;
1628
1593
  /**
1629
- * Evaluate a definition at a shape size. `adjust` replaces avLst values by name, as
1630
- * `a:prstGeom/a:avLst` does. Null when the flattened geometry would exceed
1631
- * MAX_GEOMETRY_POINTS; sizes that are negative or not finite read as 0.
1594
+ * Assert that a number is a points measurement. A compile-time cast; see {@link twips}.
1632
1595
  *
1633
- * @internal
1596
+ * @public
1634
1597
  */
1635
- declare function evaluateGeometry(definition: GeometryDefinition, widthEmu: number, heightEmu: number, adjust?: ReadonlyMap<string, number>): EvaluatedGeometry | null;
1636
-
1637
- /** The text a shape's style supplies: `a:fontRef` names a theme font slot and a colour. @internal */
1638
- interface DrawingTextDefaults {
1639
- readonly fontSlot: 'major' | 'minor' | null;
1640
- readonly color: ShapeColor | null;
1641
- }
1598
+ declare const points$1: (value: number) => Points;
1642
1599
  /**
1643
- * A lowered text body; `'no-text'` when it holds nothing to paint; `'unsupported'` when it
1644
- * holds text the lowering cannot express (a vertical mode other than a quarter turn, several
1645
- * columns, or a body past the bounds), so its shape must not render without it.
1600
+ * The one twips-to-points conversion. Exact division; twips are the finer unit.
1646
1601
  *
1647
- * @internal
1602
+ * @public
1648
1603
  */
1649
- type TextBodyLowering = LoweredDrawingText | 'no-text' | 'unsupported';
1650
- /** @internal */
1651
- interface LoweredDrawingText {
1652
- /** A `w:txbxContent` root holding the lowered paragraphs; node ids are namespaced. */
1653
- readonly content: OoxmlElement;
1654
- readonly insetsEmu: Readonly<{
1655
- top: number;
1656
- right: number;
1657
- bottom: number;
1658
- left: number;
1659
- }>;
1660
- readonly verticalAnchor: 'top' | 'center' | 'bottom';
1661
- readonly noWrap: boolean;
1662
- /** Clockwise turn `a:bodyPr/@rot` adds to the text, in degrees. */
1663
- readonly rotationDegrees: number;
1664
- /**
1665
- * `vert="vert"` (90) or `vert="vert270"` (270): the lines run along the box's long side, so
1666
- * the story lays out in the box turned a quarter. Insets are already in text space.
1667
- */
1668
- readonly quarterTurn: 0 | 90 | 270;
1669
- }
1670
- /** Lower one text body; see {@link TextBodyLowering}. @internal */
1671
- declare function lowerDrawingTextBody(txBody: OoxmlElement, partName: string, defaults: DrawingTextDefaults, resolveSchemeColor?: ShapeSchemeColorResolver): TextBodyLowering;
1672
-
1673
- /** @internal */
1674
- declare function findGraphicData(anchor: OoxmlElement): OoxmlElement | null;
1675
-
1604
+ declare const twipsToPoints: (value: Twips) => Points;
1676
1605
  /**
1677
- * The theme and settings identity behind a scheme colour resolver. Resolvers are rebuilt for
1678
- * every package revision; the token stays the same while the theme and settings parts do.
1606
+ * The one points-to-twips conversion.
1607
+ *
1608
+ * Rounds to the nearest twip: every OOXML twips measurement is integral, and emitting a
1609
+ * fractional twip would write a value no consumer of the file can represent.
1610
+ *
1611
+ * @public
1612
+ */
1613
+ declare const pointsToTwips: (value: Points) => Twips;
1614
+ /**
1615
+ * Read a twips attribute (`ST_TwipsMeasure`, `ST_SignedTwipsMeasure`, or a `dxa` table width)
1616
+ * as a whole, signed number of twips, or `null` when the value is not a measurement.
1617
+ *
1618
+ * The schema allows an integer or a universal measure, but files also carry decimal twips
1619
+ * (`4743.74`, `708.0`, `707.9999999999998`). The accepted forms read as follows:
1620
+ *
1621
+ * - A decimal truncates toward zero: `1443.74` is 1443 and `-720.6` is -720. A leading `+`,
1622
+ * leading zeros, a bare fraction (`.5`) and a trailing point (`1440.`) are accepted.
1623
+ * - A universal measure (`mm`, `cm`, `in`, `pt`, `pc`, `pi`) converts exactly and then
1624
+ * truncates: `1.27cm` is 720 and `0.333in` is 479. Its sign is ignored, so `-0.5in` is 720.
1625
+ * - Whitespace, exponents, hexadecimal, uppercase units, and an integer part longer than nine
1626
+ * digits are not measurements.
1627
+ *
1628
+ * The result is NOT bounded. Callers apply their own sign rule and range clamp, because each
1629
+ * attribute has a different valid range and a different fallback.
1679
1630
  *
1680
1631
  * @internal
1681
1632
  */
1682
- declare function shapeThemeCacheToken(resolveSchemeColor: (token: string) => string | null): string | undefined;
1633
+ declare function readTwipsMeasure(raw: string | undefined): number | null;
1634
+
1635
+ /** Outline heading shape consumed by TOC planning (mirrors DocumentOutlineEntry). */
1636
+ interface TocOutlineHeading {
1637
+ readonly text: string;
1638
+ readonly level: number;
1639
+ readonly blockId: string;
1640
+ }
1683
1641
 
1642
+ /** Left-indent step between TOC levels, in twips (matches `scripts/demo-doc/toc-block.xml`). */
1643
+ declare const TOC_LEVEL_INDENT_TWIPS: Twips;
1644
+ /** Bounded left-indent twips for a TOC entry level (0-based heading depth). */
1645
+ declare function tocLeftIndentTwips(level: number): Twips;
1646
+ /** One planned TOC entry: its level, its text, and the heading it points at. */
1647
+ interface TocEntryPlan {
1648
+ readonly level: number;
1649
+ readonly text: string;
1650
+ readonly headingParagraphId: string;
1651
+ readonly bookmarkName: string;
1652
+ readonly pageNumberText: string;
1653
+ /** The heading's symbols, one per {@link TOC_SYMBOL_MARK} in `text`, in order. */
1654
+ readonly symbols?: readonly TocSymbol[];
1655
+ }
1656
+ /** Build one TOC entry paragraph node. */
1657
+ declare function buildTocEntryParagraph(mint: () => string, entry: TocEntryPlan, instruction: TocInstruction, paragraphPropertiesTemplate?: OoxmlNode): OoxmlNode;
1658
+ /** Build an SDT-wrapped complex TOC field with an already planned cached result. */
1659
+ declare function buildTocContentControl(mint: () => string, entries: readonly TocEntryPlan[], instruction: TocInstruction, alias: string): OoxmlNode;
1684
1660
  /**
1685
- * Every kind of thing the registry gives a stable identity to.
1686
- *
1687
- * Identity is `(kind, id)` plus version and NEVER registration order, so two bundles contributing
1688
- * the same command resolve deterministically regardless of which loaded first.
1661
+ * Word flattens manual line/tab breaks from a heading into spaces in its TOC cache.
1662
+ * Carrying them verbatim makes a short title wrap even when the row has ample room, and the
1663
+ * same normalization is what lets a cached row be matched back to the heading it came from.
1689
1664
  */
1690
- declare const ID_KINDS: readonly ['extension', 'capability', 'command', 'query', 'schema', 'dependencyKey', 'runtimePort', 'result', 'origin'];
1691
- /** One of {@link ID_KINDS}. */
1692
- /** One of {@link ID_KINDS} — which sort of thing an identifier names. */
1693
- type IdKind = (typeof ID_KINDS)[number];
1665
+ declare function tocEntryText(text: string): string;
1694
1666
  /**
1695
- * Whether a string is a well-formed identifier in either accepted grammar.
1667
+ * Plan TOC entries from the outline and existing bookmarks.
1696
1668
  *
1697
- * Reverse-domain (`dev.docx-editor.core.command.insert-text`) or package-owned
1698
- * (`@docx-editor.dev/engine-core#command/insert-text`). Ids are opaque once validated.
1669
+ * `actorId` is explicit because this planner runs BEFORE the store transaction, so the
1670
+ * ambient transaction actor is not bound. Callers that already have the collaboration
1671
+ * identity pass it here. `resolveAllocationActor` still honours a
1672
+ * `runWithTransactionActor` wrap when the caller prefers that seam.
1699
1673
  */
1700
- declare function isValidId(id: string): boolean;
1674
+ declare function planTocEntries(part: OoxmlPart, outline: readonly TocOutlineHeading[], instruction: TocInstruction, pageNumberByParagraphId: ReadonlyMap<string, string>, excludeParagraphIds: ReadonlySet<string>, actorId?: string): {
1675
+ readonly entries: readonly TocEntryPlan[];
1676
+ readonly bookmarksToCreate: readonly {
1677
+ paragraphId: string;
1678
+ name: string;
1679
+ }[];
1680
+ };
1681
+ /** Create bookmarkStart/End pair nodes for insertion at the start of a paragraph. */
1682
+ declare function bookmarkPairNodes(mint: () => string, name: string, id: string): {
1683
+ readonly start: OoxmlNode;
1684
+ readonly end: OoxmlNode;
1685
+ };
1686
+
1701
1687
  /**
1702
- * Validate an identifier, throwing when it is malformed.
1688
+ * The heading each cached result row stands for, aligned with `toc.resultParagraphIds`.
1703
1689
  *
1704
- * Throws rather than returning: a bad id is an author mistake at registration time, not file
1705
- * input, and accepting it would produce a registry whose identities silently collide.
1690
+ * `null` for a row that names no heading this document still has — a stale row, or the
1691
+ * chrome/blank paragraphs a cached result can carry. Callers leave those alone rather than
1692
+ * writing another row's number into them.
1706
1693
  */
1707
- declare function assertValidId(id: string, kind?: IdKind): void;
1708
- /** A validated (kind, id, version) triple — the registry's unit of identity. */
1709
- interface CapabilityId {
1710
- readonly kind: IdKind;
1711
- readonly id: string;
1712
- readonly version: string;
1713
- }
1694
+ declare function resolveTocRowHeadings(part: OoxmlPart, toc: DetectedToc, outline: readonly TocOutlineHeading[], excludeParagraphIds: ReadonlySet<string>): readonly (string | null)[];
1714
1695
 
1715
- /** A parsed semantic version. Only the three numeric components — no pre-release or build. */
1716
- interface SemVer {
1717
- readonly major: number;
1718
- readonly minor: number;
1719
- readonly patch: number;
1696
+ /** One paragraph in the index: its node id, its story, and its position. */
1697
+ interface ParagraphIndexEntry {
1698
+ /** Canonical tree node id — the stable identity operations address. */
1699
+ readonly nodeId: string;
1700
+ /** Position among the story's paragraphs, in document order. */
1701
+ readonly ordinal: number;
1702
+ /** Concatenated text content, with tabs and breaks mapped as the model reads them. */
1703
+ readonly text: string;
1704
+ /** Node ids of the paragraph's runs, in order. */
1705
+ readonly runIds: readonly string[];
1706
+ }
1707
+ /** One story root — body, a header/footer variant, or a note part. */
1708
+ interface StoryIndexEntry {
1709
+ /** The part the story lives in. */
1710
+ readonly partName: string;
1711
+ /** The `w:body` (or story root) node id. */
1712
+ readonly rootId: string;
1713
+ readonly paragraphs: readonly ParagraphIndexEntry[];
1714
+ }
1715
+ /** One style definition, indexed by the id content references it under. */
1716
+ interface StyleIndexEntry {
1717
+ readonly styleId: string;
1718
+ readonly type: string;
1719
+ readonly nodeId: string;
1720
+ readonly name: string | null;
1721
+ readonly basedOn: string | null;
1722
+ /** Whether the part marks this the default style for its type (`w:default="1"`). */
1723
+ readonly isDefault: boolean;
1720
1724
  }
1721
1725
  /**
1722
- * Parse a semantic version, rejecting anything the registry's rules do not need.
1726
+ * The derived lookups over a part: paragraphs, stories and styles by id.
1723
1727
  *
1724
- * Deliberately narrow: an unsupported range form is refused rather than accepted, so a malformed
1725
- * range can never masquerade as "compatible".
1728
+ * Diff-patched on commit rather than rebuilt — rebuilding every index per keystroke is what made
1729
+ * typing scale with document length.
1726
1730
  */
1727
- declare function parseSemVer(input: string): SemVer;
1728
- /** Order two versions: negative, zero or positive, by major then minor then patch. */
1729
- declare function compareSemVer(a: SemVer, b: SemVer): -1 | 0 | 1;
1731
+ interface OoxmlIndexes {
1732
+ /** The tree revision these projections were derived from. */
1733
+ readonly revision: number;
1734
+ /** Body story first, then any other story parts, keyed by part name. */
1735
+ readonly stories: ReadonlyMap<string, StoryIndexEntry>;
1736
+ /** Every paragraph across every story, keyed by node id. */
1737
+ readonly paragraphs: ReadonlyMap<string, ParagraphIndexEntry>;
1738
+ readonly relationships: ReadonlyMap<string, readonly RelationshipRecord[]>;
1739
+ readonly styles: ReadonlyMap<string, StyleIndexEntry>;
1740
+ }
1741
+ /** Style definitions, read out of the generic `w:styles` tree. */
1742
+ declare function indexStyles(part: OoxmlPart | undefined): Map<string, StyleIndexEntry>;
1743
+ /** The package's style definitions part, if it has one. */
1744
+ declare function stylesPartOf(pkg: OoxmlPackage): OoxmlPart | undefined;
1730
1745
  /**
1731
- * Whether `version` satisfies `range`. Supported range grammar:
1732
- * - `*` any version
1733
- * - `1.2.3` exact
1734
- * - `^1.2.3` caret: same major, and >= the floor (major 0 pins the minor)
1735
- * - `>=1.2.3 <2.0.0` a single lower (inclusive) + upper (exclusive) bound pair
1736
- * Any other shape throws — an unparseable range is a registration error, not a pass.
1746
+ * The numbering part's root, chosen as the editor session chooses the one it paints with: the
1747
+ * main document's FIRST numbering relationship, else the default path.
1737
1748
  */
1738
- declare function satisfies(version: string, range: string): boolean;
1739
-
1749
+ declare function numberingRootOf(pkg: OoxmlPackage): OoxmlElement | null;
1740
1750
  /**
1741
- * Why registry resolution failed.
1751
+ * Derive every index from one canonical package revision.
1742
1752
  *
1743
- * Every code names a declaration conflict a bundle author can fix — a missing dependency, an
1744
- * unsatisfied version range, a duplicate contribution with no replacement policy.
1753
+ * Pure: the same package and revision always produce the same index, which is what makes
1754
+ * "rebuild rather than mutate" a safe invalidation strategy.
1745
1755
  */
1746
- type RegistryErrorCode = 'invalid-id' | 'invalid-version' | 'duplicate-extension' | 'id-collision' | 'replacement-target-missing' | 'unauthorized-replacement' | 'replacement-version-mismatch' | 'ambiguous-replacement' | 'missing-dependency' | 'dependency-cycle' | 'conflict' | 'missing-port';
1756
+ declare function deriveOoxmlIndexes(pkg: OoxmlPackage, revision: number): OoxmlIndexes;
1757
+
1758
+ /** One paragraph's meaning, independent of how it was spelled in XML. */
1759
+ interface ParagraphDigest {
1760
+ /** Ordinal identity within its story. Node ids are NOT used: a reopened package
1761
+ * legitimately re-derives them, and requiring them to match would test the id scheme
1762
+ * rather than the content. */
1763
+ readonly ordinal: number;
1764
+ /**
1765
+ * Where the paragraph SITS: the element path from the story root, so a paragraph that
1766
+ * moves out of the table cell it was written in is a difference rather than a paragraph
1767
+ * with the same ordinal and the same text.
1768
+ */
1769
+ readonly path: string;
1770
+ /**
1771
+ * `w14:paraId`, uppercase-normalized (matching is case-insensitive), or null. This is
1772
+ * MANAGED identity — the agent contract anchors on it and comment threading references
1773
+ * it — so a serializer silently dropping it must be a digest difference. Other paragraph
1774
+ * attributes (`w:rsidR` …) stay deliberately undigested: they are revision noise.
1775
+ */
1776
+ readonly paraId: string | null;
1777
+ /** Text content, including tabs and hard breaks as their characters. */
1778
+ readonly text: string;
1779
+ /** Accepted paragraph properties, as sorted tokens including nested children. */
1780
+ readonly paragraphProperties: readonly string[];
1781
+ /** Per-run accepted properties, in run order. */
1782
+ readonly runProperties: readonly (readonly string[])[];
1783
+ /** Fingerprints of every generic (unknown) subtree, in document order. */
1784
+ readonly genericStructure: readonly string[];
1785
+ }
1786
+ /** One story reduced to its semantic content, ignoring everything a normalized re-emit changes. */
1787
+ interface StoryDigest {
1788
+ readonly partName: string;
1789
+ readonly paragraphs: readonly ParagraphDigest[];
1790
+ /**
1791
+ * Every block-level element of the part OUTSIDE a paragraph, in document order, as
1792
+ * `path name(attributes)` — the tables, rows, cells, sections, content controls and
1793
+ * their property children, each at the position that says what contains it.
1794
+ *
1795
+ * A paragraph's own internals are not here (they are its `ParagraphDigest`), which keeps
1796
+ * this list proportional to a document's STRUCTURE rather than its text.
1797
+ */
1798
+ readonly structure: readonly string[];
1799
+ }
1747
1800
  /**
1748
- * A resolution failure naming every responsible party.
1801
+ * The whole document's semantic content — one of the D9 losslessness oracles.
1749
1802
  *
1750
- * Thrown rather than returned: an unresolvable registry is a build-time composition mistake, and
1751
- * an engine running with a half-resolved registry would fail later in ways that do not point back
1752
- * to the bundle that caused it.
1803
+ * What a save/reopen round-trip is compared on. Byte identity applies only to non-XML parts;
1804
+ * modelled parts re-emit normalized, so equality is asserted HERE rather than on bytes.
1753
1805
  */
1754
- declare class RegistryError extends Error {
1755
- readonly code: RegistryErrorCode;
1756
- /** Stable identities responsible for the failure (extensions, ids). */
1757
- readonly responsible: readonly string[];
1758
- constructor(code: RegistryErrorCode, message: string,
1759
- /** Stable identities responsible for the failure (extensions, ids). */
1760
- responsible?: readonly string[]);
1806
+ interface SemanticDigest {
1807
+ readonly stories: readonly StoryDigest[];
1761
1808
  }
1762
- /** How a base contribution permits replacement by other bundles. */
1763
- type ReplacementPolicy = {
1764
- readonly kind: 'none';
1765
- } | {
1766
- readonly kind: 'single';
1767
- } | {
1768
- readonly kind: 'priority';
1809
+ /** Where two digests diverge — the diagnostic when a round-trip loses something. */
1810
+ type DigestDifference = {
1811
+ readonly path: string;
1812
+ readonly before: string;
1813
+ readonly after: string;
1769
1814
  };
1770
- /** One thing a bundle contributes: a command, a query, a schema, a port, keyed by `(kind, id)`. */
1771
- interface Contribution {
1772
- readonly kind: Exclude<IdKind, 'extension' | 'origin' | 'result'>;
1773
- readonly id: string;
1774
- readonly version: string;
1775
- /** If set, this contribution replaces an existing base contribution. */
1776
- readonly replaces?: {
1777
- readonly targetId: string;
1778
- readonly targetRange: string;
1779
- readonly priority?: number;
1780
- };
1781
- /** Policy this contribution exposes to would-be replacers (default `none`). */
1782
- readonly replaceable?: ReplacementPolicy;
1783
- readonly payload?: unknown;
1784
- }
1815
+ /** Digest one part: its paragraphs, and the structure that holds them. */
1816
+ declare function digestPart(part: OoxmlPart): StoryDigest | null;
1817
+ /** Digest every part, in the given order. */
1818
+ declare function semanticDigest(parts: Iterable<OoxmlPart>): SemanticDigest;
1785
1819
  /**
1786
- * A unit of engine functionality: its identity, version, dependencies, conflicts, required ports,
1787
- * and contributions.
1820
+ * Every way two digests differ, as readable paths.
1788
1821
  *
1789
- * The registry's whole input. Everything a bundle needs and everything it offers is DECLARED, so
1790
- * resolution can be deterministic and order-independent.
1822
+ * Returns the differences rather than a boolean because "the round trip lost something" is
1823
+ * useless without saying what — the failure this oracle exists to catch is a silent drop,
1824
+ * and a bare `false` reproduces the silence.
1791
1825
  */
1792
- interface FeatureBundle {
1793
- readonly id: string;
1794
- readonly version: string;
1795
- readonly dependencies?: readonly string[];
1796
- readonly conflicts?: readonly string[];
1797
- readonly requiredPorts?: readonly string[];
1798
- readonly contributions: readonly Contribution[];
1799
- }
1800
- /** The resolved result: every contribution selected, indexed by `(kind, id)`. */
1801
- interface ResolvedRegistry {
1802
- readonly extensions: ReadonlyMap<string, FeatureBundle>;
1803
- readonly contributions: ReadonlyMap<string, Contribution>;
1804
- get(kind: Contribution['kind'], id: string): Contribution | undefined;
1805
- }
1806
- /** How resolution treats optional bundles and replacement policies. */
1807
- interface ResolveOptions {
1808
- /** Runtime port ids available in this environment (design D9 / task 0.3). */
1809
- readonly availablePorts?: readonly string[];
1810
- }
1826
+ declare function diffSemanticDigests(before: SemanticDigest, after: SemanticDigest): DigestDifference[];
1827
+
1828
+ /** Valid per MS-DOCX: 8 hex digits, non-zero, below 0x80000000. */
1829
+ declare function isValidParaId(value: string): boolean;
1830
+ /** The authored `w14:paraId` value of an element, verbatim, or null. */
1831
+ declare function paraIdOf(node: OoxmlNode): string | null;
1832
+ /** FNV-1a over UTF-16 code units — the deterministic mint every id derivation shares. */
1833
+ declare function fnv1a32(value: string): number;
1834
+ /**
1835
+ * Deterministic 8-hex mint in (0x00000000, 0x80000000), collision-free against
1836
+ * `used` (uppercase hex). Same seed + same used-set → same value, which is what
1837
+ * keeps `splitParagraphMany` byte-identical to its equivalent single splits and
1838
+ * a reopened save identical to the session that produced it.
1839
+ */
1840
+ declare function mintParaId(seed: string, used: ReadonlySet<string>): string;
1841
+ /** Every valid `w14:paraId` in the tree, uppercased. Memoized per root object. */
1842
+ declare function usedParaIds(root: OoxmlElement): ReadonlySet<string>;
1811
1843
  /**
1812
- * Resolve a set of bundles into one registry, or throw.
1813
- *
1814
- * Deterministic and registration-order independent: selection is by `(kind, id)` plus version,
1815
- * and ties break only on declared policy. Array order never decides anything.
1844
+ * The non-empty prefix the part ROOT binds to the w14 namespace, or null.
1816
1845
  *
1817
- * @throws RegistryError naming every bundle responsible for the failure.
1846
+ * Minted attributes require an in-scope binding (the invariant validator reports
1847
+ * `invalid-qname` otherwise, and the serializer would allocate an `nsN` alias).
1848
+ * Only the root binding counts: a binding authored on some descendant does not
1849
+ * cover paragraphs elsewhere in the tree.
1818
1850
  */
1819
- declare function resolve(bundles: readonly FeatureBundle[], options?: ResolveOptions): ResolvedRegistry;
1820
-
1851
+ declare function w14RootPrefix(root: OoxmlElement): string | null;
1852
+ /** The minted `[w14:paraId, w14:textId]` pair. `textId` mirrors `paraId`: it has no uniqueness requirement, no consumer reads it, and one allocator is simpler than two — but Word writes both, so we write both. */
1853
+ declare function mintedParagraphIdentityAttributes(prefix: string, value: string): readonly OoxmlAttribute[];
1821
1854
  /**
1822
- * Typed-origin domains (design D5 / ADR-S5). `mutation.*` are the canonical
1823
- * write origins; `projection` and `awareness` MUST NOT enter history, audit,
1824
- * snapshots, or replication.
1855
+ * Load-time paragraph-identity normalization for a session's main part.
1856
+ *
1857
+ * Keeps every valid, first-seen paraId verbatim; mints (deterministically, seeded
1858
+ * by the paragraph's structural node id) for paragraphs whose id is missing,
1859
+ * malformed, zero, out of range, or a duplicate. Adds the root `xmlns` binding
1860
+ * the minted attributes need. Returns the INPUT PART REFERENCE when there is
1861
+ * nothing to do — a document Word saved yesterday re-serializes byte-identical.
1862
+ *
1863
+ * Attribute-only rebuilds move no child, so every node keeps its structural-path
1864
+ * id; the copy respreads only the ancestors of changed paragraphs.
1865
+ *
1866
+ * Fail-open: if the rebuilt part does not validate (a bug, or a pathology the
1867
+ * prefix choice could not defuse), the original part is returned — a document
1868
+ * must never become unopenable over an identity enhancement.
1825
1869
  */
1826
- declare const ORIGIN_IDS: {
1827
- readonly mutationHuman: "dev.docx-editor.core.origin.mutation.human";
1828
- readonly mutationAgent: "dev.docx-editor.core.origin.mutation.agent";
1829
- readonly mutationRemote: "dev.docx-editor.core.origin.mutation.remote";
1830
- readonly mutationUndo: "dev.docx-editor.core.origin.mutation.undo";
1831
- readonly mutationRedo: "dev.docx-editor.core.origin.mutation.redo";
1832
- readonly mutationMigration: "dev.docx-editor.core.origin.mutation.migration";
1833
- readonly mutationRepair: "dev.docx-editor.core.origin.mutation.repair";
1834
- readonly mutationServer: "dev.docx-editor.core.origin.mutation.server";
1835
- readonly projection: "dev.docx-editor.core.origin.projection";
1836
- readonly awareness: "dev.docx-editor.core.origin.awareness";
1837
- };
1838
- /** Canonical writes carry a mutation origin; these are the undoable-eligibility inputs. */
1839
- declare const CANONICAL_MUTATION_ORIGINS: readonly string[];
1840
- /** Origins that never enter authored state / history / audit / snapshot / replication. */
1841
- declare const NON_CANONICAL_ORIGINS: readonly string[];
1870
+ declare function normalizeParagraphIdentity(part: OoxmlPart): OoxmlPart;
1871
+
1872
+ /** A text position: a paragraph's node id and a UTF-16 offset in its text. */
1873
+ interface BookmarkPosition {
1874
+ readonly paragraphId: string;
1875
+ readonly offset: number;
1876
+ }
1877
+ /** One bookmark, both ends resolved. */
1878
+ interface BookmarkRecord {
1879
+ /** The name as the document writes it. */
1880
+ readonly name: string;
1881
+ /** Names starting with an underscore are hidden. */
1882
+ readonly hidden: boolean;
1883
+ /** The marker pair's `@w:id`. */
1884
+ readonly markerId: string;
1885
+ readonly start: BookmarkPosition;
1886
+ readonly end: BookmarkPosition;
1887
+ /** Node ids of the two markers. */
1888
+ readonly startNodeId: string;
1889
+ readonly endNodeId: string;
1890
+ /**
1891
+ * How many marker pairs in the part carry this name, compared without case: 1, or more
1892
+ * where concurrent edits left copies. The record is the first; the rest are not listed.
1893
+ */
1894
+ readonly copies: number;
1895
+ }
1896
+ /** List each name once, using its first marker pair in document order. @public */
1897
+ declare function listBookmarks(part: OoxmlPart): readonly BookmarkRecord[];
1898
+
1899
+ /** One `w:docPart` of the glossary, with the blocks its body holds. */
1900
+ interface BuildingBlock {
1901
+ /** `w:docPartPr/w:name`, the identity a control's placeholder and a pick refer to. */
1902
+ readonly name: string;
1903
+ /** `w:category/w:gallery`, an `ST_DocPartGallery` value such as `docParts`. */
1904
+ readonly gallery: string;
1905
+ /** `w:category/w:name`; `General` when the block declares none. */
1906
+ readonly category: string;
1907
+ /** The `w:docPartBody` children: paragraphs, tables and block controls, in order. */
1908
+ readonly blocks: readonly OoxmlNode[];
1909
+ }
1910
+ /** The gallery and category a `w:docPartList` control lists. */
1911
+ interface BuildingBlockGalleryFilter {
1912
+ readonly gallery: string;
1913
+ readonly category: string | null;
1914
+ }
1915
+ /** The glossary part, through the main part's relationship or the conventional name. */
1916
+ declare function glossaryPartOf(pkg: OoxmlPackage): OoxmlPart | null;
1917
+ /** Every named building block of the glossary, in authored order. Empty without a glossary. */
1918
+ declare function buildingBlocksOf(pkg: OoxmlPackage): readonly BuildingBlock[];
1919
+ /** The gallery a `w:docPartList` control lists, or null for any other control. */
1920
+ declare function buildingBlockGalleryOf(control: OoxmlNode): BuildingBlockGalleryFilter | null;
1842
1921
  /**
1843
- * Result taxonomy (design D8 / task 7.8). A transport/protocol failure that
1844
- * prevents a valid envelope is a typed exception, not a member here.
1922
+ * The blocks a gallery control offers: same gallery, and the control's category when it names
1923
+ * one. Sorted by category, then name, as Word groups its list.
1845
1924
  */
1846
- declare const RESULT_IDS: {
1847
- readonly applied: "dev.docx-editor.core.result.applied";
1848
- readonly validation: "dev.docx-editor.core.result.validation";
1849
- readonly conflict: "dev.docx-editor.core.result.conflict";
1850
- readonly resource: "dev.docx-editor.core.result.resource";
1851
- readonly authorization: "dev.docx-editor.core.result.authorization";
1852
- readonly aborted: "dev.docx-editor.core.result.aborted";
1853
- };
1854
- /** Runtime ports (extensions-and-runtime-ports spec; design D9). */
1855
- declare const RUNTIME_PORT_IDS: {
1856
- readonly fonts: "dev.docx-editor.core.port.fonts";
1857
- readonly shaping: "dev.docx-editor.core.port.shaping";
1858
- readonly images: "dev.docx-editor.core.port.images";
1859
- readonly clock: "dev.docx-editor.core.port.clock";
1860
- readonly identity: "dev.docx-editor.core.port.identity";
1861
- readonly persistence: "dev.docx-editor.core.port.persistence";
1862
- readonly transport: "dev.docx-editor.core.port.transport";
1863
- readonly scheduling: "dev.docx-editor.core.port.scheduling";
1864
- readonly audit: "dev.docx-editor.core.port.audit";
1865
- readonly authorization: "dev.docx-editor.core.port.authorization";
1866
- readonly resourceAccounting: "dev.docx-editor.core.port.resource-accounting";
1867
- readonly cancellation: "dev.docx-editor.core.port.cancellation";
1868
- readonly externalResourceConsent: "dev.docx-editor.core.port.external-resource-consent";
1869
- };
1870
- /** Layout dependency keys (design D6 / task 8.2). */
1871
- declare const DEPENDENCY_KEY_IDS: {
1872
- readonly style: "dev.docx-editor.core.dep.style";
1873
- readonly numbering: "dev.docx-editor.core.dep.numbering";
1874
- readonly section: "dev.docx-editor.core.dep.section";
1875
- readonly story: "dev.docx-editor.core.dep.story";
1876
- readonly font: "dev.docx-editor.core.dep.font";
1877
- readonly image: "dev.docx-editor.core.dep.image";
1878
- readonly table: "dev.docx-editor.core.dep.table";
1879
- readonly field: "dev.docx-editor.core.dep.field";
1880
- readonly note: "dev.docx-editor.core.dep.note";
1881
- readonly headerFooter: "dev.docx-editor.core.dep.header-footer";
1882
- readonly annotation: "dev.docx-editor.core.dep.annotation";
1883
- };
1884
- /** Every frozen id as a flat list, for the immutability / uniqueness gate. */
1885
- declare const ALL_FROZEN_IDS: readonly string[];
1925
+ declare function buildingBlocksForControl(pkg: OoxmlPackage, control: OoxmlNode): readonly BuildingBlock[];
1886
1926
 
1887
1927
  /**
1888
- * A counter's limit was reached. Carries the label, the limit, and what was attempted.
1928
+ * The comments part this story names, when it exists and really is one.
1889
1929
  *
1890
- * Thrown at the N → N+1 boundary, so the increment that would have crossed the limit never
1891
- * happens rather than being detected afterwards.
1930
+ * Conventional `/word/comments.xml` is used only when no usable relationship exists AND the
1931
+ * part's content type is the comments type. A present wrong-typed part is not a comments part.
1892
1932
  */
1893
- declare class LimitExceededError extends Error {
1894
- readonly label: string;
1895
- readonly limit: number;
1896
- readonly attempted: number;
1897
- constructor(label: string, limit: number, attempted: number);
1898
- }
1933
+ declare function commentsPartNameForStory(pkg: OoxmlPackage, storyPartName: string): string | null;
1934
+
1899
1935
  /**
1900
- * Overflow-safe counting against a fixed limit.
1936
+ * Which story a comment deletion or resolution may read markers from.
1901
1937
  *
1902
- * Never silently wraps: it rejects the increment that would cross its limit AND any arithmetic
1903
- * that would exceed `Number.MAX_SAFE_INTEGER`. That second guard is the point — a file-supplied
1904
- * count must never be trusted into an allocation, and a wrapped counter reads as small.
1938
+ * `w:id` is not unique across stories: a header and the body can both hold comment 1.
1905
1939
  */
1906
- declare class BoundedCounter {
1907
- readonly label: string;
1908
- readonly limit: number;
1909
- private value;
1910
- constructor(label: string, limit: number);
1911
- get current(): number;
1912
- /** Remaining headroom before the limit. */
1913
- get remaining(): number;
1940
+ interface CommentDeletionOwner {
1941
+ /** Canonical name of the part holding the story's markers. */
1942
+ readonly storyPartName: string;
1914
1943
  /**
1915
- * Add `n` (default 1). Throws LimitExceededError if the result would exceed the
1916
- * limit (so `limit` itself is reachable but `limit + 1` is not) or overflow the
1917
- * safe-integer range. On rejection the counter is unchanged.
1944
+ * When the story is one note inside a shared notes part, that note's `w:id`.
1945
+ * Absent means the whole part is the story (body, header, footer).
1918
1946
  */
1919
- add(n?: number): number;
1920
- /** Whether adding `n` would be accepted, without mutating. */
1921
- canAdd(n?: number): boolean;
1922
- release(n: number): void;
1947
+ readonly noteId?: number;
1923
1948
  }
1924
1949
 
1925
- /** A budget was misused: over-carved, over-reserved, or disposed with children outstanding. */
1926
- declare class BudgetError extends Error {
1927
- constructor(message: string);
1928
- }
1929
1950
  /**
1930
- * Capacity claimed BEFORE it is allocated.
1951
+ * Delete a comment thread: that story's markers, and metadata no other story still names.
1931
1952
  *
1932
- * Reserve-then-allocate rather than allocate-then-check: discovering the overrun after the
1933
- * allocation has already happened defeats the point of having a budget.
1953
+ * `owner` names the story whose markers may be stripped. Omitted, it is the main document
1954
+ * body — never every XML part. A header or note that reused the same `w:id` is a different
1955
+ * remark; pass its part (and `noteId` when the story is one note) to delete that one.
1956
+ *
1957
+ * Returns the package unchanged when the id names no comment, and null when a removal was
1958
+ * refused — a caller inside a transaction rolls back rather than committing a package whose
1959
+ * comment is half gone.
1934
1960
  */
1935
- interface Reservation {
1936
- readonly amount: number;
1937
- readonly released: boolean;
1938
- release(): void;
1939
- }
1961
+ declare function deleteCommentThread(pkg: OoxmlPackage, commentId: string, owner?: CommentDeletionOwner): OoxmlPackage | null;
1940
1962
  /**
1941
- * One node in the hierarchical resource budget tree.
1963
+ * Delete every comment the edit between `before` and `after` emptied in `owner`'s story.
1942
1964
  *
1943
- * An operation owns a root budget; parsers, extensions, workers, layout, transport and output
1944
- * carve children from it, so no subsystem can consume more than the operation as a whole allows.
1965
+ * `owner` omitted is the main document body, matching {@link deleteCommentThread}.
1945
1966
  *
1946
- * `dispose()` REFUSES while children or reservations are outstanding, then runs registered
1947
- * cleanups LIFO — spill files, worker termination — even if one throws. A leaked child budget is a
1948
- * leaked worker, so the refusal is the diagnostic.
1967
+ * THE TEST, exactly, and per story. A comment dies when the words it covered in that story are
1968
+ * gone and nothing in that story still places it. A neighbour that reused the same `w:id` is
1969
+ * judged under its own owner. Notes that vanished (reference cascade) are reaped as their own
1970
+ * stories so a unique id still loses its `comments.xml` body. Remaining-marker overflow
1971
+ * preserves comments/commentEx/commentId metadata rather than deleting records the scan could
1972
+ * not prove unused. Null is reserved for a removal that actually failed.
1949
1973
  */
1950
- declare class Budget {
1951
- readonly label: string;
1952
- readonly capacity: number;
1953
- private readonly used;
1954
- private readonly children;
1955
- private activeReservations;
1956
- private readonly cleanups;
1957
- private disposed;
1958
- private carveAmount;
1959
- private parent?;
1960
- constructor(label: string, capacity: number);
1961
- get inUse(): number;
1962
- get available(): number;
1963
- get isDisposed(): boolean;
1964
- /** Reserve `amount` from this budget. Throws if it would exceed capacity. */
1965
- reserve(amount: number): Reservation;
1966
- /** Carve a child budget of `capacity` out of this budget's headroom. */
1967
- child(label: string, capacity: number): Budget;
1968
- /** Register cleanup for a spill file, worker, or queue tied to this budget. */
1969
- onRelease(cleanup: () => void): void;
1974
+ declare function cascadeEmptiedComments(before: OoxmlPackage, after: OoxmlPackage, owner?: CommentDeletionOwner): OoxmlPackage | null;
1975
+
1976
+ /** Presentation settings the document itself states. Absence is the answer, never an error. */
1977
+ interface DocumentViewSettings {
1970
1978
  /**
1971
- * Release this budget. Refuses while any child budget or reservation is
1972
- * outstanding (children must finish first). Runs cleanups LIFO, returns the
1973
- * carve-out to the parent, and marks disposed. Cleanup errors are collected and
1974
- * rethrown after every cleanup has run.
1979
+ * `w:doNotShadeFormData` (§17.15.1.49) — do NOT shade legacy form fields.
1980
+ *
1981
+ * Word draws a grey block behind every `w:ffData` field so a reader can find the blanks in a
1982
+ * form. It is on by DEFAULT, and this element is how a document turns it off, so the absent
1983
+ * case means shading. Reading the name as "shade form data" inverts it and blanks the shading
1984
+ * on exactly the documents that asked for it loudest.
1985
+ *
1986
+ * Never printed, by Word or by us: it marks where to type, and a printed form has no blanks
1987
+ * to find.
1975
1988
  */
1976
- dispose(): void;
1977
- private assertLive;
1989
+ readonly doNotShadeFormData: boolean;
1978
1990
  }
1991
+ /** The defaults a document with no `settings.xml` gets: everything Word would shade, shaded. */
1992
+ declare const DEFAULT_VIEW_SETTINGS: DocumentViewSettings;
1993
+ /** Read the view settings from a `settings.xml` root, or the defaults when it has none. */
1994
+ declare function readViewSettings(settingsRoot: OoxmlNode | null | undefined): DocumentViewSettings;
1979
1995
 
1996
+ /** How deep transparent block wrappers may nest before the walk stops descending. */
1997
+ declare const MAX_STORY_SDT_NESTING = 32;
1998
+ /** Which kind of story a root is. Layout walks all four the same way. */
1999
+ type OoxmlStoryKind = 'body' | 'header' | 'footer' | 'note';
2000
+ /** One story root and the blocks under it, with transparent block wrappers already flattened. */
2001
+ interface OoxmlStoryRoot {
2002
+ readonly kind: OoxmlStoryKind;
2003
+ /** The `w:body` / `w:hdr` / `w:ftr` / `w:footnote` element that holds the blocks. */
2004
+ readonly root: OoxmlNode;
2005
+ }
1980
2006
  /**
1981
- * Which side of the point of no return an operation is on.
2007
+ * Every story root in a part, in document order.
1982
2008
  *
1983
- * Canonical publication is that point. Cancelling BEFORE it rolls the whole operation back;
1984
- * cancelling after it leaves the commit standing and cancels only derived work — layout, export,
1985
- * caches.
2009
+ * Does not descend INTO a story: a story's blocks are the walk below, and a story root never
2010
+ * contains another story root.
1986
2011
  */
1987
- type CancellationPhase = 'pre-publication' | 'post-publication';
1988
- /** Thrown from a `checkpoint()` when the operation has been cancelled. */
1989
- declare class CancellationError extends Error {
1990
- /** True when the commit is already published and only derived work is cancelled. */
1991
- readonly derivedOnly: boolean;
1992
- constructor(
1993
- /** True when the commit is already published and only derived work is cancelled. */
1994
- derivedOnly: boolean, reason?: string);
1995
- }
2012
+ declare function storyRootsOf(part: OoxmlPart): readonly OoxmlStoryRoot[];
1996
2013
  /**
1997
- * The read side of cancellation, handed to cooperative work.
2014
+ * Whether a part is SHAPED to hold stories — a body, a header, a footer, or a notes part.
1998
2015
  *
1999
- * Cooperative rather than pre-emptive: long work calls `checkpoint()` at declared intervals, which
2000
- * is what lets an operation unwind promptly without the engine having to interrupt it mid-mutation.
2016
+ * A shape test, deliberately not `storyRootsOf(part).length > 0`. That counts EDITABLE stories,
2017
+ * and a notes part holding nothing but `w:separator` and `w:continuationSeparator` has none —
2018
+ * which is the footnotes part Word writes into every document that ever held a footnote. A
2019
+ * caller asking "should I walk this part at all" that used the editable count skipped it
2020
+ * entirely, and the two callers that ask are a payload sweep and an export strip: both DELETE
2021
+ * what they conclude nothing names.
2001
2022
  */
2002
- interface CancellationToken {
2003
- readonly isCancelled: boolean;
2004
- readonly phase: CancellationPhase;
2005
- /** True only when cancelled AND the commit is already published. */
2006
- readonly derivedOnly: boolean;
2007
- /** Throws CancellationError if cancelled; call at declared checkpoint intervals. */
2008
- checkpoint(): void;
2009
- }
2023
+ declare function isStoryPart(part: OoxmlPart): boolean;
2024
+ /** The main body story of a part, or null when the part holds none. */
2025
+ declare function bodyStoryRoot(part: OoxmlPart): OoxmlNode | null;
2010
2026
  /**
2011
- * The write side of cancellation, and the owner of the phase.
2027
+ * Every paragraph of one story, in reading order.
2012
2028
  *
2013
- * Whoever starts an operation holds this; everything it calls gets only the token.
2029
+ * Descends through tables (rows, cells, nested tables), block controls and block custom XML.
2030
+ * The returned nodes are paragraph elements; a caller addresses them by `id`.
2014
2031
  */
2015
- declare class CancellationController {
2016
- private _cancelled;
2017
- private _phase;
2018
- private _reason?;
2019
- cancel(reason?: string): void;
2020
- /** Mark canonical publication — the point of no return. */
2021
- markPublished(): void;
2022
- get isCancelled(): boolean;
2023
- get phase(): CancellationPhase;
2024
- get isPublished(): boolean;
2025
- /** Cancelled after publication -> full rollback is no longer possible. */
2026
- get derivedOnly(): boolean;
2027
- get token(): CancellationToken;
2028
- }
2032
+ declare function storyParagraphs(root: OoxmlNode): readonly OoxmlNode[];
2033
+ /**
2034
+ * The block walk itself, appending into `out`.
2035
+ *
2036
+ * Exported so `allParagraphs` in the binding lane is the same traversal rather than a second
2037
+ * copy of it.
2038
+ */
2039
+ declare function collectStoryParagraphs(children: readonly OoxmlNode[], out: OoxmlNode[], sdtDepth: number): void;
2029
2040
 
2030
- /** Injectable time source — the engine never calls Date.now directly (determinism). */
2031
- interface ClockPort {
2032
- now(): number;
2033
- }
2034
- /** Stable id minting for sessions, commits, and allocator seeds. */
2035
- interface IdentityPort {
2036
- newId(): string;
2037
- }
2038
- /** Cooperative scheduling of deferred work. */
2039
- interface SchedulingPort {
2040
- schedule(task: () => void): void;
2041
- }
2042
- /** Redacted observability sink (raw text never enters here — design D5). */
2043
- interface AuditPort {
2044
- record(entry: {
2045
- readonly kind: string;
2046
- readonly at: number;
2047
- readonly meta?: Record<string, unknown>;
2048
- }): void;
2049
- }
2050
- /** Read/write/export authorization decisions. */
2051
- interface AuthorizationPort {
2052
- authorize(action: string, subject?: string): boolean;
2053
- }
2054
- /** Explicit consent gate for any remote/external resource (no zero-click fetch). */
2055
- interface ExternalResourceConsentPort {
2056
- requestConsent(target: string): boolean;
2057
- }
2058
- /** Cancellation source for the current operation. */
2059
- interface CancellationPort {
2060
- readonly token: CancellationToken;
2061
- }
2062
- /** Where documents are stored and retrieved. Opaque marker until its milestone. */
2063
- interface PersistencePort {
2064
- readonly kind: 'persistence';
2065
- }
2066
- /** How the engine talks to a remote peer. Opaque marker until its milestone. */
2067
- interface TransportPort {
2068
- readonly kind: 'transport';
2069
- }
2070
- /** Where font bytes come from. Opaque marker until its milestone. */
2071
- interface FontPort {
2072
- readonly kind: 'font';
2073
- }
2074
- /** Which shaping backend measures text. Opaque marker until its milestone. */
2075
- interface ShapingPort {
2076
- readonly kind: 'shaping';
2077
- }
2078
- /** How image bytes are decoded. Opaque marker until its milestone. */
2079
- interface ImagePort {
2080
- readonly kind: 'image';
2081
- }
2082
- /** Where resource consumption is reported. Opaque marker until its milestone. */
2083
- interface ResourceAccountingPort {
2084
- readonly kind: 'resource-accounting';
2041
+ /** One searchable text-box story and the drawing location that can reveal it. */
2042
+ interface TextboxStoryRoot {
2043
+ /** The `w:txbxContent` story root. */
2044
+ readonly root: OoxmlElement;
2045
+ /** Layout atom id used to select the drawing, including an MC wrapper when present. */
2046
+ readonly drawingNodeId: string;
2047
+ /** Canonical node id of the paragraph that anchors the drawing. */
2048
+ readonly hostParagraphId: string;
2085
2049
  }
2086
2050
  /**
2087
- * A required port the runtime did not provide.
2051
+ * List the text-box stories in one part, in document order.
2088
2052
  *
2089
- * Thrown rather than falling back: silently reaching for a browser global when a worker or server
2090
- * adapter did not supply a port is exactly the coupling ports exist to prevent.
2053
+ * The bounded walk stops at drawings. It does not project pictures, shape geometry, or story
2054
+ * content. Results are memoized by immutable part-root identity.
2091
2055
  */
2092
- declare class PortResolutionError extends Error {
2093
- readonly portId: string;
2094
- constructor(portId: string);
2056
+ declare function textboxStoriesInPart(part: OoxmlPart): readonly TextboxStoryRoot[];
2057
+
2058
+ /** Relationship from the story to one data part. */
2059
+ declare const CUSTOM_XML_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml";
2060
+ /** Relationship from a data part to the properties that carry its `ds:itemID`. */
2061
+ declare const CUSTOM_XML_PROPS_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXmlProps";
2062
+ /** `itemPropsN.xml` needs an Override; `itemN.xml` rides the package's `xml` default. */
2063
+ declare const CUSTOM_XML_PROPS_TYPE = "application/vnd.openxmlformats-officedocument.customXmlProperties+xml";
2064
+ /** The datastore namespace `ds:datastoreItem` lives in. */
2065
+ declare const DATASTORE_NAMESPACE_URI = "http://schemas.openxmlformats.org/officeDocument/2006/customXml";
2066
+ /** A data part located in a package: where it lives, and the GUID an SDT binds to. */
2067
+ interface CustomXmlDataPart {
2068
+ /** Canonical part name, e.g. `/customXml/item1.xml`. */
2069
+ readonly partName: string;
2070
+ /** Part name of its properties, e.g. `/customXml/itemProps1.xml`. */
2071
+ readonly propsPartName: string;
2072
+ /** `ds:itemID`, braced and upper-case, as `w:storeItemID` must spell it. */
2073
+ readonly itemId: string;
2074
+ /** Namespace URI of the payload root, which is what identifies one store among several. */
2075
+ readonly namespaceUri: string;
2095
2076
  }
2096
2077
  /**
2097
- * The environment-dependent services an engine instance may reach.
2078
+ * A `ds:itemID` derived from the seed rather than drawn at random.
2098
2079
  *
2099
- * Everything outside pure computation — the clock, identity minting, scheduling, fonts, images —
2100
- * is reached only through here, which is what lets browser, worker and server adapters supply
2101
- * only what their runtime actually offers.
2080
+ * Exported so a caller that authors a store OUTSIDE a package — a template engine splicing
2081
+ * markup it will assemble into a `.docx` later — mints the id the same way this does, rather
2082
+ * than inventing a second GUID shape Word has to be tolerant of.
2083
+ *
2084
+ * The store is a pure function of what it is asked to write: the same document written twice
2085
+ * has to produce the same bytes, or a save/reopen/save round trip stops being a fixed point
2086
+ * and every digest taken over saved bytes moves. A GUID's job here is uniqueness within one
2087
+ * package, not unguessability, so four FNV-1a passes over a salted seed carry it.
2102
2088
  */
2103
- declare class PortRegistry {
2104
- private readonly ports;
2105
- provide(portId: string, port: unknown): this;
2106
- has(portId: string): boolean;
2107
- /** Resolve a port by id or throw. */
2108
- resolve<T>(portId: string): T;
2109
- /** Assert every id in `portIds` is present; returns the missing ones (empty if all present). */
2110
- missing(portIds: readonly string[]): string[];
2111
- /** The provided port ids (for bridging to registry.resolve availablePorts). */
2112
- availablePorts(): string[];
2113
- clock(): ClockPort;
2114
- identity(): IdentityPort;
2115
- }
2116
- /** Deterministic clock: starts at `start` and advances by `step` each call. */
2117
- declare class DeterministicClock implements ClockPort {
2118
- private readonly step;
2119
- private t;
2120
- constructor(start?: number, step?: number);
2121
- now(): number;
2122
- }
2123
- /** Sequential identity for tests and deterministic fixtures. */
2124
- declare class SequentialIdentity implements IdentityPort {
2125
- private readonly prefix;
2126
- private n;
2127
- constructor(prefix?: string);
2128
- newId(): string;
2089
+ declare function datastoreItemIdFor(seed: string): string;
2090
+ /**
2091
+ * The data parts a story relates to, in relationship order.
2092
+ *
2093
+ * Reads the relationships rather than scanning `/customXml/` by name: a part nothing relates
2094
+ * to is not part of the document, and a package from a hostile sender can hold as many
2095
+ * plausibly-named files as it likes.
2096
+ */
2097
+ declare function customXmlDataParts(pkg: OoxmlPackage, storyPartName: string): CustomXmlDataPart[];
2098
+ /** The data part carrying this namespace, or null when the document has none yet. */
2099
+ declare function findCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string): CustomXmlDataPart | null;
2100
+ /** What {@link withCustomXmlDataPart} answers. */
2101
+ interface CustomXmlDataPartResult {
2102
+ readonly pkg: OoxmlPackage;
2103
+ /** Null when the part could not be authored; the package then comes back unchanged. */
2104
+ readonly part: CustomXmlDataPart | null;
2129
2105
  }
2106
+ /**
2107
+ * Ensure the document carries a data part for this namespace, creating it and everything it
2108
+ * needs — properties, both relationships, the properties content type — when it does not.
2109
+ *
2110
+ * Idempotent: a package that already has one for the namespace comes back untouched, so a
2111
+ * second node added to the same store does not author a second store.
2112
+ *
2113
+ * @param storyPartName - Whose relationships the store hangs off. Word enumerates its data
2114
+ * store from the MAIN DOCUMENT part, so a store authored off a header or footer is one Word
2115
+ * never sees.
2116
+ */
2117
+ declare function withCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string, rootLocalName: string): CustomXmlDataPartResult;
2118
+ /**
2119
+ * Remove a store from a document: both parts, both relationships, the Override.
2120
+ *
2121
+ * PACKAGE ONLY. It does not touch the body, so a `w:sdt` bound to the store keeps its
2122
+ * `w:dataBinding`, its `w:storeItemID` and its `w:tag`, and Word then opens a control bound to
2123
+ * a store that is not there. Stripping those is the export path's job and is not built; this
2124
+ * is the half that removes the payload, which is the half a caller can rely on.
2125
+ *
2126
+ * `ok: false` means nothing was removed and the package is unchanged — most often because an
2127
+ * owner's `.rels` was never parsed into a tree, which {@link withoutPart} refuses to work
2128
+ * around. A caller exporting a document has to treat that as a failure to export rather than
2129
+ * as a document with nothing to strip, or it ships the payload it meant to remove.
2130
+ */
2131
+ declare function withoutCustomXmlDataPart(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string): WithoutPartResult;
2130
2132
 
2131
- /** What one operation is started with. Only `ports` is required. */
2132
- interface OperationInit {
2133
- readonly ports: PortRegistry;
2134
- readonly limits?: Partial<ResourceLimits>;
2135
- readonly config?: Readonly<Record<string, unknown>>;
2136
- /** Root budget capacity in abstract units (default: the byte decompression limit). */
2137
- readonly capacity?: number;
2138
- /** Operation identity (from IdentityPort in production); defaulted for tests. */
2139
- readonly id?: string;
2133
+ /** One node in a store: its id, the bound text, and the payload beside it. */
2134
+ interface CustomXmlNode {
2135
+ readonly id: string;
2136
+ /** Text a `w:dataBinding` resolves to. Untrusted file input. */
2137
+ readonly label: string;
2138
+ /** Payload as authored, JSON by convention and unparsed here. Untrusted file input. */
2139
+ readonly data: string;
2140
2140
  }
2141
+ /** Every node a store holds, in document order. */
2142
+ declare function customXmlNodes(pkg: OoxmlPackage, partName: string): CustomXmlNode[];
2143
+ /** One node by id, or null. */
2144
+ declare function readCustomXmlNode(pkg: OoxmlPackage, partName: string, nodeId: string): CustomXmlNode | null;
2141
2145
  /**
2142
- * The frozen environment of one operation: its limits, config, ports, root budget and
2143
- * cancellation controller.
2146
+ * Write a node, replacing one that already has the id.
2144
2147
  *
2145
- * IMMUTABLE for the operation's lifetime, which is what lets cache reuse and replica agreement
2146
- * key on it. A new operation always gets a fresh snapshot rather than observing a mutated one.
2148
+ * Replace rather than append: a second node with the same id makes the binding xpath ambiguous,
2149
+ * and Word resolves an ambiguous xpath to the first match — so an "update" that appended would
2150
+ * leave the control showing its old text forever.
2147
2151
  */
2148
- interface OperationContext {
2149
- readonly id: string;
2150
- readonly limits: ResourceLimits;
2151
- readonly config: Readonly<Record<string, unknown>>;
2152
- readonly ports: PortRegistry;
2153
- readonly budget: Budget;
2154
- readonly cancellation: CancellationController;
2155
- }
2152
+ declare function withCustomXmlNode(pkg: OoxmlPackage, partName: string, node: CustomXmlNode): OoxmlPackage;
2153
+ /** Drop one node by id. A store that never held it comes back unchanged. */
2154
+ declare function withoutCustomXmlNode(pkg: OoxmlPackage, partName: string, nodeId: string): OoxmlPackage;
2156
2155
  /**
2157
- * Begin an operation, capturing its immutable environment snapshot.
2156
+ * Drop every node no longer referenced, given the ids the document still binds.
2158
2157
  *
2159
- * Resolves and freezes limits and configuration, carves the root budget, and creates the
2160
- * cancellation controller — everything downstream work needs, fixed for the operation's duration.
2158
+ * This is the whole deletion story. Deleting a control in THIS editor can remove its node
2159
+ * directly, but a control deleted in Word leaves the node behind — Word has no lifecycle link
2160
+ * between the two and no way to run our code. Reconciling against what the story actually binds
2161
+ * collects both, and is the only thing that can collect the second.
2162
+ *
2163
+ * Takes the referenced ids rather than reading the story itself: the caller already walked it,
2164
+ * and a sweep that guessed at which controls exist would delete a payload on a mistake.
2161
2165
  */
2162
- declare function beginOperation(init: OperationInit): OperationContext;
2163
- /** Release the operation's root budget (requires every child/reservation released). */
2164
- declare function endOperation(ctx: OperationContext): void;
2165
-
2166
- /** What a limit counts. Typed so a byte cap and a depth cap cannot be compared by accident. */
2167
- type LimitUnit = 'bytes' | 'count' | 'depth' | 'ratio' | 'passes';
2166
+ declare function withoutOrphanCustomXmlNodes(pkg: OoxmlPackage, partName: string, referencedIds: ReadonlySet<string>): {
2167
+ readonly pkg: OoxmlPackage;
2168
+ readonly removed: readonly string[];
2169
+ };
2168
2170
  /**
2169
- * Where a limit is enforced.
2171
+ * The `w:xpath` a binding uses to reach a node's label, or null when the id cannot be addressed.
2170
2172
  *
2171
- * Declared per limit so enforcement happens at the boundary that can still refuse cheaply — a zip
2172
- * cap checked during layout has already let the bomb decompress.
2173
+ * Word needs a prefix for the payload namespace even when the store declares it as a default —
2174
+ * an unprefixed step in an XPath means "no namespace", so `/docxEditor/node` would match
2175
+ * nothing in a namespaced store. The prefix is declared in `w:prefixMappings` beside it.
2173
2176
  */
2174
- type EnforcementPhase = 'package-read' | 'xml-parse' | 'layout' | 'output';
2175
- /** One limit's unit and enforcement phase — the metadata that makes limits testable uniformly. */
2176
- interface LimitSpec {
2177
- readonly unit: LimitUnit;
2178
- readonly phase: EnforcementPhase;
2177
+ declare function customXmlLabelXPath(prefix: string, rootLocalName: string, nodeId: string): string | null;
2178
+ /** The `w:prefixMappings` value declaring that prefix, or null when it cannot be written.
2179
+ * The namespace sits inside single quotes inside a double-quoted attribute, so a namespace
2180
+ * carrying either quote character has no representation here. */
2181
+ declare function customXmlPrefixMappings(prefix: string, namespaceUri: string): string | null;
2182
+
2183
+ /** The prefix every binding this library authors declares and quotes. */
2184
+ declare const CUSTOM_NODE_XPATH_PREFIX = "ns0";
2185
+ /** The three attributes a `w:dataBinding` carries. */
2186
+ interface CustomNodeBinding {
2187
+ readonly prefixMappings: string;
2188
+ readonly xpath: string;
2189
+ readonly storeItemId: string;
2179
2190
  }
2180
- /** Unit + enforcement phase for every resource limit. */
2181
- declare const LIMIT_SPECS: Readonly<Record<keyof ResourceLimits, LimitSpec>>;
2182
- /** Every limit name, for iterating the specs and asserting each has a unit and phase. */
2183
- declare const LIMIT_KEYS: (keyof ResourceLimits)[];
2184
- /** A phase-scoped overflow-safe counter for one limit. */
2185
- declare function makeLimitCounter(limits: ResourceLimits, key: keyof ResourceLimits): BoundedCounter;
2186
2191
  /**
2187
- * Deterministic memory substitute. Tracks current and peak "allocated" units
2188
- * against a hard byte ceiling; `allocate` fails closed (LimitExceededError) past
2189
- * the limit. Used in place of process RSS so memory-limit tests are reproducible
2190
- * and cancellation/cleanup can be asserted without a real allocator.
2192
+ * The binding for one node in one store, or null when the id cannot be addressed by an XPath.
2193
+ *
2194
+ * Refuses rather than escapes: XPath 1.0 has no escape for a quote inside a literal, so an id
2195
+ * carrying one could close the predicate and append an expression of the sender's choosing.
2196
+ * The ids are minted by a host, so refusing is honest — see `ADDRESSABLE_ID`.
2191
2197
  */
2192
- declare class DeterministicMemoryMeter {
2193
- private readonly counter;
2194
- private current;
2195
- private peak;
2196
- constructor(limitBytes: number);
2197
- allocate(bytes: number): void;
2198
- free(bytes: number): void;
2199
- get currentBytes(): number;
2200
- get peakBytes(): number;
2201
- /** Release everything (cancellation/cleanup path). */
2202
- reset(): void;
2203
- }
2204
- /** Assert the finite-default / hard-ceiling invariant holds for every limit. */
2205
- declare function assertLimitInvariants(): void;
2206
-
2207
- /** Any JSON value. The domain canonicalization and stable hashing operate over. */
2208
- type Json = null | boolean | number | string | Json[] | {
2209
- [k: string]: Json;
2210
- };
2198
+ declare function customNodeBinding(part: CustomXmlDataPart, rootLocalName: string, nodeId: string): CustomNodeBinding | null;
2211
2199
  /**
2212
- * Produce a canonical string for `value`, dropping any object key whose name is
2213
- * in `ephemera` at any depth. Numbers are emitted losslessly; -0 normalizes to 0;
2214
- * non-finite numbers are rejected (they must never enter a comparator input).
2200
+ * Every node id the story's controls bind to in one store, in document order.
2201
+ *
2202
+ * This is the input the orphan sweep takes: a node whose id is not in here is one no control
2203
+ * names, whether it was deleted in this editor or in Word. Reading it from the STORY rather than
2204
+ * from a host's bookkeeping is what makes the two cases one mechanism.
2215
2205
  */
2216
- declare function canonicalize(value: unknown, ephemera?: ReadonlySet<string>): string;
2217
- /** 64-bit FNV-1a over the canonical form, returned as a 16-char hex string. */
2218
- declare function stableHash(value: unknown, ephemera?: ReadonlySet<string>): string;
2219
-
2206
+ declare function boundCustomXmlNodeIds(part: OoxmlPart, storeItemId: string): Set<string>;
2220
2207
  /**
2221
- * How one artifact class is compared.
2208
+ * Every node id ANY story in the package binds, in one store.
2222
2209
  *
2223
- * Frozen per artifact: nothing here invents a tolerance for an artifact the spec requires to
2224
- * match exactly.
2210
+ * THE WHOLE PACKAGE, not one story. A payload is reachable from a header as easily as from the
2211
+ * body — Word enumerates its data store from the main part, but nothing stops a control
2212
+ * elsewhere quoting the same `w:storeItemID` — and the two callers that decide what is an orphan
2213
+ * both destroy data when they are wrong. The sweep would collect a payload a header still paints;
2214
+ * the export would strip a store a header still names, which is a document Word offers to repair.
2215
+ *
2216
+ * Costs one walk per story per store, on open and on export. Neither is a keystroke.
2225
2217
  */
2226
- type ComparatorMode = 'canonical-exact' | 'exact' | 'tolerance' | 'sync-optimization-only';
2227
- /** One artifact class's comparison mode and its declared ephemera — fields excluded from equality. */
2228
- interface ComparatorDescriptor {
2229
- readonly id: string;
2230
- readonly mode: ComparatorMode;
2231
- /** Object keys excluded from canonical-exact comparison (declared ephemera). */
2232
- readonly ephemera: readonly string[];
2233
- readonly note: string;
2218
+ declare function boundCustomXmlNodeIdsInPackage(pkg: OoxmlPackage, storeItemId: string): Set<string>;
2219
+ /** The node id one control binds to in the named store, or null when it binds to nothing there. */
2220
+ declare function boundCustomXmlNodeIdOf(control: OoxmlNode, storeItemId: string): string | null;
2221
+
2222
+ /** What happens to one control when the document is exported. */
2223
+ type CustomNodeExportPolicy = 'keep' | 'text' | 'remove';
2224
+ /** How {@link withExportedCustomNodes} decides, and which stores it may tidy afterwards. */
2225
+ interface CustomNodeExportRequest {
2226
+ /** The story whose controls are policed, and whose relationships the stores hang off. */
2227
+ readonly storyPartName: string;
2228
+ /**
2229
+ * Payload namespaces the caller CLAIMS.
2230
+ *
2231
+ * The store cleanup runs only over these. Word's own Cover Page Properties store rides in most
2232
+ * templates, and an export that tidied every customXml part would be deleting from documents it
2233
+ * was only asked to strip its own markup from.
2234
+ */
2235
+ readonly namespaces: readonly string[];
2236
+ /** The fate of a control carrying this `w:tag`. A control with no tag is never touched. */
2237
+ readonly decide: (tag: string) => CustomNodeExportPolicy;
2234
2238
  }
2235
- /** The frozen artifact comparator set. */
2236
- declare const COMPARATORS: {
2237
- readonly authoredState: {
2238
- readonly id: "dev.docx-editor.core.comparator.authored-state";
2239
- readonly mode: 'canonical-exact';
2240
- readonly ephemera: readonly ["revision", "provenance", "producedAt", "commitId"];
2241
- readonly note: 'canonical normalized authored records; ephemera excluded';
2242
- };
2243
- readonly anchor: {
2244
- readonly id: "dev.docx-editor.core.comparator.anchor";
2245
- readonly mode: 'exact';
2246
- readonly ephemera: readonly [];
2247
- readonly note: 'internal anchor identity/affinity compares exactly';
2248
- };
2249
- readonly yjsStateVector: {
2250
- readonly id: "dev.docx-editor.core.comparator.yjs-state-vector";
2251
- readonly mode: 'sync-optimization-only';
2252
- readonly ephemera: readonly [];
2253
- readonly note: 'exchange optimization only; never proves update or delete-set coverage';
2254
- };
2255
- readonly shapedRun: {
2256
- readonly id: "dev.docx-editor.core.comparator.shaped-run";
2257
- readonly mode: 'exact';
2258
- readonly ephemera: readonly [];
2259
- readonly note: 'glyph ids, clusters, and fixed-point advances compare exactly';
2260
- };
2261
- readonly paginationFingerprint: {
2262
- readonly id: "dev.docx-editor.core.comparator.pagination-fingerprint";
2263
- readonly mode: 'exact';
2264
- readonly ephemera: readonly [];
2265
- readonly note: 'page/column boundaries, break causes, fixed-point geometry compare exactly';
2266
- };
2267
- readonly semanticTree: {
2268
- readonly id: "dev.docx-editor.core.comparator.semantic-tree";
2269
- readonly mode: 'exact';
2270
- readonly ephemera: readonly [];
2271
- readonly note: 'reading order, roles, headings, alt text compare exactly';
2272
- };
2273
- readonly hitTest: {
2274
- readonly id: "dev.docx-editor.core.comparator.hit-test";
2275
- readonly mode: 'exact';
2276
- readonly ephemera: readonly [];
2277
- readonly note: 'resolved hit target and cluster affinity compare exactly';
2278
- };
2279
- readonly pdfSemantics: {
2280
- readonly id: "dev.docx-editor.core.comparator.pdf-semantics";
2281
- readonly mode: 'canonical-exact';
2282
- readonly ephemera: readonly ["objectNumber", "producer", "creationDate", "modDate", "subsetTag"];
2283
- readonly note: 'canonical semantic PDF objects; container ephemera excluded';
2284
- };
2285
- readonly rasterCheckpoint: {
2286
- readonly id: "dev.docx-editor.core.comparator.raster-checkpoint";
2287
- readonly mode: 'tolerance';
2288
- readonly ephemera: readonly [];
2289
- readonly note: 'documented unavoidable raster comparison; explicit tolerance only';
2290
- };
2291
- readonly benchmarkEvidence: {
2292
- readonly id: "dev.docx-editor.core.comparator.benchmark-evidence";
2293
- readonly mode: 'sync-optimization-only';
2294
- readonly ephemera: readonly [];
2295
- readonly note: 'diagnostic evidence, not an equivalence basis';
2296
- };
2297
- };
2298
- /** Which frozen comparator to use. */
2299
- type ComparatorName = keyof typeof COMPARATORS;
2300
- /** Whether two artifacts matched, and where they diverged when they did not. */
2301
- interface ComparisonResult {
2302
- readonly equal: boolean;
2303
- /** Canonical forms when unequal (diagnostic). */
2304
- readonly left?: string;
2305
- readonly right?: string;
2239
+ /**
2240
+ * The export, or the reason there is no export.
2241
+ *
2242
+ * A refusal answers no package on purpose. "Stripping failed, here is the document anyway" is the
2243
+ * one outcome that must not be possible: a caller would ship the markup it asked to remove and
2244
+ * have been told the export succeeded.
2245
+ */
2246
+ type CustomNodeExportResult = {
2247
+ readonly ok: true;
2248
+ readonly pkg: OoxmlPackage;
2249
+ /** Controls unwrapped (`text`) and controls removed (`remove`). */
2250
+ readonly unwrapped: number;
2251
+ readonly removed: number;
2252
+ } | {
2253
+ readonly ok: false;
2254
+ readonly reason: string;
2255
+ };
2256
+ /**
2257
+ * Apply the policy, then take the payloads and the stores the policy orphaned.
2258
+ *
2259
+ * Order matters and is the reverse of the write's. The BODY goes first, so the sweep that follows
2260
+ * sees the controls that actually survive; the stores go last, once nothing binds them. Doing it
2261
+ * the other way would strip a store while a control still quoted its `w:storeItemID`, which is a
2262
+ * document Word opens and offers to repair.
2263
+ */
2264
+ declare function withExportedCustomNodes(pkg: OoxmlPackage, request: CustomNodeExportRequest): CustomNodeExportResult;
2265
+
2266
+ /** Named inputs a formula may read besides adjust values and earlier formulas. @internal */
2267
+ interface VmlFormulaContext {
2268
+ readonly width: number;
2269
+ readonly height: number;
2270
+ readonly adjust: readonly number[];
2306
2271
  }
2272
+ /** The values of `eqns` in order, or null when one cannot be read. @internal */
2273
+ declare function vmlFormulaValues(eqns: readonly string[], context: VmlFormulaContext): readonly number[] | null;
2274
+
2275
+ /** The VML namespace. @internal */
2276
+ declare const VML = "urn:schemas-microsoft-com:vml";
2277
+ /** The Office VML extensions namespace (`o:`). @internal */
2278
+ declare const OFFICE = "urn:schemas-microsoft-com:office:office";
2279
+ /** The Word VML extensions namespace (`w10:`). @internal */
2280
+ declare const WORD_VML = "urn:schemas-microsoft-com:office:word";
2281
+ /** Whether `node` is the element `ns`:`name`. @internal */
2282
+ declare const named: (node: OoxmlNode, ns: string, name: string) => boolean;
2283
+ /** The value of attribute `name` in namespace `ns` (none by default). @internal */
2284
+ declare const attribute: (node: OoxmlElement, name: string, ns?: string) => string | undefined;
2285
+ /** The element children of `node`. @internal */
2286
+ declare function children(node: OoxmlElement): OoxmlElement[];
2287
+ /** Whether a VML boolean is false (`f`, `false`, `0`). @internal */
2288
+ declare const off: (value: string | undefined) => boolean;
2289
+ /** A plain decimal number, bounded; NaN when malformed. @internal */
2290
+ declare function numeric(value: string | undefined): number;
2291
+ /** A VML length in points (`pt`, `in`, `cm`, `mm`, `px`); NaN when malformed. @internal */
2292
+ declare function points(value: string | undefined): number;
2307
2293
  /**
2308
- * Compare two artifacts under a named comparator. `canonical-exact` and `exact`
2309
- * compare canonical forms (exact drops no ephemera). `tolerance` requires an
2310
- * epsilon and compares finite numbers structurally. `sync-optimization-only`
2311
- * throws — such artifacts are never an equivalence basis.
2294
+ * VML rotation in degrees (`315`, `-45`, or `fd` sixty-five-thousandths). NaN when malformed.
2295
+ *
2296
+ * @internal
2312
2297
  */
2313
- declare function compareArtifacts(name: ComparatorName, left: unknown, right: unknown, opts?: {
2314
- epsilon?: number;
2315
- }): ComparisonResult;
2316
- /** Fingerprint an artifact under a named comparator's canonicalization policy. */
2317
- declare function fingerprint(name: ComparatorName, value: unknown): string;
2298
+ declare function legacyRotationDegrees(raw: string | undefined): number;
2299
+ /** The declarations of a `style` attribute by lower-case name; null when malformed. @internal */
2300
+ declare function styleOf(node: OoxmlElement): ReadonlyMap<string, string> | null;
2301
+ /** A VML colour as `#rrggbb`, `fallback` when absent; null when malformed. @internal */
2302
+ declare function color(value: string | undefined, fallback: string): string | null;
2318
2303
 
2319
- /** Whether a recorded conformance step was expected to commit or be refused. */
2320
- type FixtureOutcome = 'applied' | 'aborted' | 'validation' | 'conflict' | 'resource' | 'authorization';
2321
- /** The change one step produced, summarized to what a fixture can compare across runtimes. */
2322
- interface ModelChangeSummary {
2323
- readonly fromRevision: number;
2324
- readonly toRevision: number;
2325
- readonly origin: string;
2326
- readonly dirty: readonly string[];
2327
- readonly dependencyKeys: readonly string[];
2304
+ interface GeometryGuide {
2305
+ readonly name: string;
2306
+ readonly op: string;
2307
+ readonly args: readonly string[];
2328
2308
  }
2329
- /** Where anchors sat after a step — how a fixture proves positions survived an edit. */
2330
- interface AnchorSnapshot {
2331
- readonly anchorId: string;
2332
- readonly story: string;
2333
- readonly block: string;
2334
- readonly affinity: 'before' | 'after';
2309
+
2310
+ type GeometryFill = 'norm' | 'none' | 'lighten' | 'lightenLess' | 'darken' | 'darkenLess';
2311
+ type GeometryCommand = {
2312
+ readonly op: 'M' | 'L';
2313
+ readonly args: readonly [string, string];
2314
+ } | {
2315
+ readonly op: 'A';
2316
+ readonly args: readonly [string, string, string, string];
2317
+ } | {
2318
+ readonly op: 'Q';
2319
+ readonly args: readonly [string, string, string, string];
2320
+ } | {
2321
+ readonly op: 'C';
2322
+ readonly args: readonly [string, string, string, string, string, string];
2323
+ } | {
2324
+ readonly op: 'Z';
2325
+ readonly args: readonly [];
2326
+ };
2327
+ interface GeometryPath {
2328
+ /** Path coordinate space; 0 means the shape's own EMU space on that axis. */
2329
+ readonly width: number;
2330
+ readonly height: number;
2331
+ readonly fill: GeometryFill;
2332
+ readonly stroke: boolean;
2333
+ readonly commands: readonly GeometryCommand[];
2335
2334
  }
2336
- /** What one step must produce: its outcome, its change summary, and its authored-state hash. */
2337
- interface FixtureExpectation {
2338
- readonly outcome: FixtureOutcome;
2339
- /** Present iff outcome === 'applied'; MUST be > baseRevision. */
2340
- readonly committedRevision?: number;
2341
- readonly modelChange?: ModelChangeSummary;
2342
- /** 16-hex authored-state fingerprint (comparator 0.4). */
2343
- readonly authoredStateHash?: string;
2344
- readonly outputHash?: string;
2345
- readonly anchors?: readonly AnchorSnapshot[];
2335
+ /** @internal */
2336
+ interface GeometryDefinition {
2337
+ readonly avLst: readonly GeometryGuide[];
2338
+ readonly gdLst: readonly GeometryGuide[];
2339
+ /** Text rectangle guide references l/t/r/b; null means the whole shape. */
2340
+ readonly textRect: readonly [string, string, string, string] | null;
2341
+ readonly paths: readonly GeometryPath[];
2346
2342
  }
2347
- /** One recorded operation and what it was expected to do. */
2348
- interface FixtureStep {
2349
- readonly baseRevision: number;
2350
- readonly origin: string;
2351
- /** Opaque DocOp payloads (schema owned by section 4). */
2352
- readonly ops: readonly unknown[];
2353
- readonly expect: FixtureExpectation;
2343
+ interface GeometryPoint {
2344
+ readonly x: number;
2345
+ readonly y: number;
2354
2346
  }
2355
- /** Opaque encoded backend bytes (snapshot or replication update). */
2356
- interface EncodedEnvelope {
2357
- readonly kind: 'snapshot' | 'update';
2358
- readonly protocolVersion: number;
2359
- readonly schemaVersion: number;
2360
- readonly documentId: string;
2361
- readonly byteLength: number;
2362
- /** Hex-encoded opaque bytes; length MUST equal byteLength. */
2363
- readonly bytesHex: string;
2347
+ interface EvaluatedSubpath {
2348
+ readonly points: readonly GeometryPoint[];
2349
+ readonly closed: boolean;
2364
2350
  }
2365
- /** Which implementation recorded a fixture — local store, Yjs, or a binding. */
2366
- type FixtureSource = {
2367
- readonly kind: 'create';
2368
- } | {
2369
- readonly kind: 'docx';
2370
- readonly sha256: string;
2371
- readonly bytesRef: string;
2372
- };
2351
+ interface EvaluatedPath {
2352
+ readonly subpaths: readonly EvaluatedSubpath[];
2353
+ readonly fill: GeometryFill;
2354
+ readonly stroke: boolean;
2355
+ }
2356
+ /** @internal */
2357
+ interface EvaluatedGeometry {
2358
+ readonly paths: readonly EvaluatedPath[];
2359
+ readonly textRect: Readonly<{
2360
+ left: number;
2361
+ top: number;
2362
+ right: number;
2363
+ bottom: number;
2364
+ }>;
2365
+ }
2366
+ /** The ECMA-376 preset geometry named by `a:prstGeom/@prst`, or null for an unknown name. @internal */
2367
+ declare function presetGeometryDefinition(name: string): GeometryDefinition | null;
2373
2368
  /**
2374
- * The frozen conformance container: revisions, origins, operations, changes, snapshots and
2375
- * hashes.
2369
+ * Evaluate a definition at a shape size. `adjust` replaces avLst values by name, as
2370
+ * `a:prstGeom/a:avLst` does. Null when the flattened geometry would exceed
2371
+ * MAX_GEOMETRY_POINTS; sizes that are negative or not finite read as 0.
2376
2372
  *
2377
- * The CONTAINER is what is frozen — field names, origin membership, revision monotonicity, hash
2378
- * format. The op and change payloads travel opaquely, so their schemas can evolve without
2379
- * invalidating every recorded fixture.
2373
+ * @internal
2380
2374
  */
2381
- interface ConformanceFixture {
2382
- readonly formatVersion: 1;
2383
- readonly documentId: string;
2384
- readonly source: FixtureSource;
2385
- readonly steps: readonly FixtureStep[];
2386
- readonly snapshots?: readonly EncodedEnvelope[];
2387
- readonly updates?: readonly EncodedEnvelope[];
2388
- }
2389
- /** Whether a fixture is well-formed, listing every structural violation. */
2390
- interface ValidationResult {
2391
- readonly valid: boolean;
2392
- readonly errors: readonly string[];
2393
- }
2394
- /** Structurally validate a fixture against the frozen format rules. */
2395
- declare function validateFixture(fixture: ConformanceFixture): ValidationResult;
2375
+ declare function evaluateGeometry(definition: GeometryDefinition, widthEmu: number, heightEmu: number, adjust?: ReadonlyMap<string, number>): EvaluatedGeometry | null;
2396
2376
 
2397
- /** The outcome a store reports for one replayed step. */
2398
- interface ReplayOutcome {
2399
- readonly outcome: FixtureOutcome;
2400
- readonly committedRevision?: number;
2401
- /** Canonical authored-state value AFTER the step (hashed via comparator 0.4). */
2402
- readonly authoredState?: unknown;
2403
- }
2404
- /** The interface a conformance runtime implements so one fixture can drive every backend. */
2405
- interface ReplayStore {
2406
- /** Initialize from the fixture source; return the initial revision (0 for create). */
2407
- init(fixture: ConformanceFixture): number;
2408
- /** Apply one step and report what happened. */
2409
- applyStep(step: FixtureStep): ReplayOutcome;
2377
+ /** The text a shape's style supplies: `a:fontRef` names a theme font slot and a colour. @internal */
2378
+ interface DrawingTextDefaults {
2379
+ readonly fontSlot: 'major' | 'minor' | null;
2380
+ readonly color: ShapeColor | null;
2410
2381
  }
2411
- /** What replaying a fixture produced: per-step outcomes and where they diverged. */
2412
- interface ReplayReport {
2413
- readonly ok: boolean;
2414
- readonly mismatches: readonly string[];
2382
+ /**
2383
+ * A lowered text body; `'no-text'` when it holds nothing to paint; `'unsupported'` when it
2384
+ * holds text the lowering cannot express (a vertical mode other than a quarter turn, several
2385
+ * columns, or a body past the bounds), so its shape must not render without it.
2386
+ *
2387
+ * @internal
2388
+ */
2389
+ type TextBodyLowering = LoweredDrawingText | 'no-text' | 'unsupported';
2390
+ /** @internal */
2391
+ interface LoweredDrawingText {
2392
+ /** A `w:txbxContent` root holding the lowered paragraphs; node ids are namespaced. */
2393
+ readonly content: OoxmlElement;
2394
+ readonly insetsEmu: Readonly<{
2395
+ top: number;
2396
+ right: number;
2397
+ bottom: number;
2398
+ left: number;
2399
+ }>;
2400
+ readonly verticalAnchor: 'top' | 'center' | 'bottom';
2401
+ readonly noWrap: boolean;
2402
+ /** Clockwise turn `a:bodyPr/@rot` adds to the text, in degrees. */
2403
+ readonly rotationDegrees: number;
2404
+ /**
2405
+ * `vert="vert"` (90) or `vert="vert270"` (270): the lines run along the box's long side, so
2406
+ * the story lays out in the box turned a quarter. Insets are already in text space.
2407
+ */
2408
+ readonly quarterTurn: 0 | 90 | 270;
2415
2409
  }
2410
+ /** Lower one text body; see {@link TextBodyLowering}. @internal */
2411
+ declare function lowerDrawingTextBody(txBody: OoxmlElement, partName: string, defaults: DrawingTextDefaults, resolveSchemeColor?: ShapeSchemeColorResolver): TextBodyLowering;
2412
+
2413
+ /** @internal */
2414
+ declare function findGraphicData(anchor: OoxmlElement): OoxmlElement | null;
2415
+
2416
2416
  /**
2417
- * Replay a fixture against a store and check every step's outcome, committed
2418
- * revision, and authored-state fingerprint against the fixture's expectations.
2417
+ * The theme and settings identity behind a scheme colour resolver. Resolvers are rebuilt for
2418
+ * every package revision; the token stays the same while the theme and settings parts do.
2419
+ *
2420
+ * @internal
2419
2421
  */
2420
- declare function replayFixture(fixture: ConformanceFixture, store: ReplayStore): ReplayReport;
2421
- /** Hash a store's authored state, so two runtimes replaying one fixture can be compared. */
2422
- declare function hashAuthored(authoredState: unknown): string;
2422
+ declare function shapeThemeCacheToken(resolveSchemeColor: (token: string) => string | null): string | undefined;
2423
2423
 
2424
2424
  /**
2425
2425
  * WHERE a copy reaches, computed by the editing surface against the published layout with
@@ -3123,5 +3123,5 @@ declare function inspectDrawingSupport(pkg: OoxmlPackage): DrawingSupportReport;
3123
3123
  /** Every {@link TreeDocOpKind}, for validation and exhaustiveness checks. */
3124
3124
  declare const TREE_DOC_OP_KINDS: readonly ["replaceStoryBlocks", "insertText", "deleteText", "setParagraphMarkRevision", "proposeParagraphMerge", "insertCommentMarker", "acceptRevision", "rejectRevision", "acceptAllRevisions", "rejectAllRevisions", "setRevisionAttribution", "insertTab", "insertHardBreak", "insertPageBreak", "insertPageField", "insertComplexField", "insertEquation", "setFieldCode", "setListLevel", "setListNumbering", "setParagraphTabStops", "setParagraphMarkProperties", "splitParagraph", "splitParagraphMany", "joinParagraphs", "setRunProperties", "setParagraphProperties", "setSectionProperties", "setSectionMark", "insertHyperlink", "setHyperlinkTarget", "removeHyperlink", "setMathEquation", "removeMathEquation", "setContentControlValue", "removeContentControl", "insertInlineContentControl", "addRepeatingSectionItem", "removeRepeatingSectionItem", "deleteBlock", "insertTable", "moveTable", "insertParagraphBesideTable", "insertTableRow", "deleteTableRow", "insertTableColumn", "deleteTableColumn", "mergeTableCells", "splitTableCell", "setTableColumnWidths", "setTableRightEdgeWidth", "setTableRowHeight", "setTableProperties", "authorTable", "setTableCellBorders", "setTableCellFill", "setTableCellVerticalAlignment", "createHeaderFooter", "deleteHeaderFooter", "linkToPrevious", "unlinkFromPrevious", "setSectionFurnitureOptions", "setDocumentProtection", "setWatermark", "insertNote", "deleteNote", "convertNote", "convertAllNotes", "setNoteProperties", "setContentControlProperties", "insertContentControl", "insertFragment", "insertDrawing", "replaceDrawingResource", "deleteDrawing", "resizeDrawing", "cropDrawing", "positionDrawing", "setDrawingWrap", "setDrawingMetadata", "setDrawingLocks", "transformDrawing", "insertToc", "replaceTocResult", "removeToc", "rewriteTocPageNumbers", "refreshFieldResults", "setFieldResult", "setTextFormFieldDefault", "commitTextFormField", "setLegacyCheckbox", "setLegacyDropdown", "insertBuildingBlock", "insertBookmark", "deleteBookmark"];
3125
3125
 
3126
- export { ALL_FROZEN_IDS, AUTHORABLE_PARAGRAPH_PROPERTIES, AUTHORABLE_RUN_PROPERTIES, BoundedCounter, Budget, BudgetError, CANONICAL_MUTATION_ORIGINS, COMPARATORS, CUSTOM_NODE_XPATH_PREFIX, CUSTOM_XML_PROPS_REL, CUSTOM_XML_PROPS_TYPE, CUSTOM_XML_REL, CancellationController, CancellationError, ChartPartResolver, ChartexPartResolver, CommentRecord, CommentThreadState, CustomNodeWriteResult, DANGEROUS_KEYS, DATASTORE_NAMESPACE_URI, DEFAULT_FORMATTING_DISPLAY_MODE, DEFAULT_VIEW_SETTINGS, DEPENDENCY_KEY_IDS, DangerousKeyError, DeterministicClock, DeterministicMemoryMeter, DrawingGraphicKind, DrawingSupport, DrawingSupportIssue, DrawingTreeDocOp, EditOptions, FIELD_ATOM_CHAR, FieldResultsMode, GroupTextboxProjection, HistoryGroup, ID_KINDS, INERT_EXECUTABLE_KINDS, ImageWrapTarget, ImpactClass, InsertCustomNodeWrite, LIMIT_KEYS, LIMIT_SPECS, LegacyTextArtProjection, LimitExceededError, MAX_STORY_SDT_NESTING, MetafileImageMime, MetafileRenderOptions, MetafileReplay, NON_CANONICAL_ORIGINS, ORIGIN_IDS, OmmlEquationProjection, OoxmlAttribute, OoxmlElement, OoxmlFldCharNode, OoxmlFldSimpleNode, OoxmlGenericElementNode, OoxmlHardBreakNode, OoxmlInstrTextNode, OoxmlInvariantResult, OoxmlNode, OoxmlPackage, OoxmlParagraphNode, OoxmlPart, OoxmlProperty, PAGE_BREAK_CHAR, PictureFrameEffects, PortRegistry, PortResolutionError, PrefixAllocator, RESULT_IDS, RUNTIME_PORT_IDS, RegistryError, RelationshipRecord, ResourceLimits, ReviewCommentItem, ReviewItem, ReviewModelInput, ReviewPosition, ReviewRange, ReviewRevisionItem, RevisionSiteRefusal, SEARCH_MATCH_LIMIT, SEARCH_QUERY_MAX, SequentialIdentity, ShapeColor, ShapeEffectList, ShapeSchemeColorResolver, StoryScope, TOC_LEVEL_INDENT_TWIPS, TOC_MAX_BOOKMARKS_PER_REFRESH, TOC_MAX_ENTRIES, TOC_MAX_FIELD_NESTING, TOC_MAX_INSTRUCTION_CHARS, TOC_MAX_PAGE_PASSES, TREE_DOC_OP_KINDS, TWIPS_PER_POINT, TreeDocOp, TreeDocumentStore, TreeModelChange, TreeOpRejection, TreeOpResult, TreePackageStore, VML as VML_NAMESPACE_URI, OFFICE as VML_OFFICE_NAMESPACE_URI, WORD_VML as VML_WORD_NAMESPACE_URI, VectorShapeComponent, VectorShapeProjection, addComment, addPackageComment, applyTreeOp, assertLimitInvariants, assertValidId, assertValidQName, atomicFieldSpansOf, authoredProperties, beginOperation, bodyStoryRoot, bookmarkPairNodes, boundCustomXmlNodeIdOf, boundCustomXmlNodeIds, boundCustomXmlNodeIdsInPackage, buildTocContentControl, buildTocEntryParagraph, buildingBlockGalleryOf, buildingBlocksForControl, buildingBlocksOf, canonicalOoxmlFingerprint, canonicalize, cascadeEmptiedComments, collectReviewItems, collectRevisionSites, collectStoryParagraphs, commentItemsOf, commentPartNameOf, commentsExtendedPartNameOf, compareArtifacts, compareSemVer, containsCssFetch, contentTypesPartBytes, customNodeBinding, customXmlDataParts, customXmlLabelXPath, customXmlNodes, customXmlPrefixMappings, datastoreItemIdFor, deleteCommentThread, deletePackageComments, deriveOoxmlIndexes, detectBodyTocs, diffSemanticDigests, digestPart, directParagraphMarkProperties, directParagraphProperties, displayMathAlignment, drawingOpImpact, endOperation, equationsOfAtom, escapeCssString, escapeXml, escapeXmlAttribute, evaluateGeometry, extractFragmentPackage, fieldAtomText, fieldOnOffAttribute, findCustomXmlDataPart, findDetectedToc, findDirectChild, findGraphicData, findOccurrences, fingerprint, fldCharType, fldSimpleInstr, fnv1a32, foldCase, formatOwnedRunIds, formattableRanges, formsProtectionEnabled, glossaryPartOf, hardBreakAttributes, hardBreakKind, hardBreakText, hasCommentPart, hasLegacyFormFieldData, hashAuthored, indexStyles, inlineControlEndingAt, inlineControlStartingAt, insertPackageCustomNode, inspectDrawingSupport, installDrawingRenderers, instrTextValue, isAuthorableRunProperty, isDangerousKey, isDrawingTreeDocOp, isEvaluableField, isFieldChrome, isFldChar, isFldCharNode, isFldSimple, isFldSimpleNode, isInertExecutable, isInstrText, isInstrTextNode, isLegalFldCharType, isOmmlDisplay, isOmmlEquationAtom, isPageBreakNode, isSearchableQuery, isStoryPart, isValidId, isValidNCName, isValidParaId, isValidQName, isValidXmlText, isWholeWord, legacyRotationDegrees, linkRevisionReplies, listBookmarks, locateSites, lowerDrawingTextBody, makeLimitCounter, materializeGlossaryPlaceholders, mergeFragmentIntoPackage, mergedFontProperty, mergedMultiSettingProperty, mergedParagraphMarkProperties, mergedProperties, mintParaId, mintedParagraphIdentityAttributes, nextCommentId, normalizeParagraphIdentity, nullRecord, ooxmlTreesEqual, paraIdOf, paragraphOffsetIndex, paragraphTextOf, parseEmu, parseSemVer, parseTocInstruction, planTocEntries, points$1 as points, pointsToTwips, presetGeometryDefinition, projectOmmlAtom, projectOmmlDisplay, propertyContainer, readCustomXmlNode, readTwipsMeasure, readViewSettings, refreshBoundContent, relationshipsOf, relsPartNameFor, removePackageCustomNode, replayFixture, resolve, resolveContentTypeOf, resolveTocRowHeadings, revisionItemsOf, runAddressRanges, runPropertyEdits, runsCovering, sanitizeHref, satisfies, schemaAttributeValue, scrubExport, sectionProtectsForms, segmentsOf, semanticDigest, serializeOoxmlPart, setCommentResolved, setPackageCommentResolved, shapeThemeCacheToken, stableHash, storyParagraphs, storyRootsOf, stylesPartOf, textboxStoriesInPart, toSafeRecord, tocEntryText, tocLeftIndentTwips, twips, twipsToPoints, usedParaIds, validateDrawingOp, validateFixture, validateOoxmlPart, validatePackageInvariants, validateTreeOp, attribute as vmlAttribute, children as vmlChildren, color as vmlColor, vmlFormulaValues, named as vmlNamed, numeric as vmlNumber, off as vmlOff, points as vmlPoints, styleOf as vmlStyle, w14RootPrefix, withContentTypeOverride, withCustomXmlDataPart, withCustomXmlNode, withExportedCustomNodes, withNewPart, withRelationship, withoutCustomXmlDataPart, withoutCustomXmlNode, withoutOrphanCustomXmlNodes, wrapTargetToAnchorSpec };
3126
+ export { ALL_FROZEN_IDS, AUTHORABLE_PARAGRAPH_PROPERTIES, AUTHORABLE_RUN_PROPERTIES, BoundedCounter, Budget, BudgetError, CANONICAL_MUTATION_ORIGINS, COMPARATORS, CUSTOM_NODE_XPATH_PREFIX, CUSTOM_XML_PROPS_REL, CUSTOM_XML_PROPS_TYPE, CUSTOM_XML_REL, CancellationController, CancellationError, ChartPartResolver, ChartexPartResolver, CommentRecord, CommentThreadState, CustomNodeWriteResult, DANGEROUS_KEYS, DATASTORE_NAMESPACE_URI, DEFAULT_FORMATTING_DISPLAY_MODE, DEFAULT_VIEW_SETTINGS, DEPENDENCY_KEY_IDS, DangerousKeyError, DeterministicClock, DeterministicMemoryMeter, DrawingGraphicKind, DrawingSupport, DrawingSupportIssue, DrawingTreeDocOp, EditOptions, FIELD_ATOM_CHAR, FieldResultsMode, GroupTextboxProjection, HistoryGroup, ID_KINDS, INERT_EXECUTABLE_KINDS, ImageWrapTarget, ImpactClass, InsertCustomNodeWrite, LIMIT_KEYS, LIMIT_SPECS, LegacyTextArtProjection, LimitExceededError, MAX_STORY_SDT_NESTING, MetafileImageMime, MetafileRenderOptions, MetafileReplay, NON_CANONICAL_ORIGINS, ORIGIN_IDS, OmmlEquationProjection, OoxmlAttribute, OoxmlElement, OoxmlFldCharNode, OoxmlFldSimpleNode, OoxmlGenericElementNode, OoxmlHardBreakNode, OoxmlInstrTextNode, OoxmlInvariantResult, OoxmlNode, OoxmlPackage, OoxmlParagraphNode, OoxmlPart, OoxmlProperty, PAGE_BREAK_CHAR, PictureFrameEffects, PortRegistry, PortResolutionError, PrefixAllocator, RESULT_IDS, RUNTIME_PORT_IDS, RegistryError, RelationshipRecord, ResourceLimits, ReviewCommentItem, ReviewItem, ReviewModelInput, ReviewPosition, ReviewRange, ReviewRevisionItem, RevisionSiteRefusal, SEARCH_MATCH_LIMIT, SEARCH_QUERY_MAX, SequentialIdentity, ShapeColor, ShapeEffectList, ShapeSchemeColorResolver, StoryScope, TOC_LEVEL_INDENT_TWIPS, TOC_MAX_BOOKMARKS_PER_REFRESH, TOC_MAX_ENTRIES, TOC_MAX_FIELD_NESTING, TOC_MAX_INSTRUCTION_CHARS, TOC_MAX_PAGE_PASSES, TREE_DOC_OP_KINDS, TWIPS_PER_POINT, TreeDocOp, TreeDocumentStore, TreeModelChange, TreeOpRejection, TreeOpResult, TreePackageStore, VML as VML_NAMESPACE_URI, OFFICE as VML_OFFICE_NAMESPACE_URI, WORD_VML as VML_WORD_NAMESPACE_URI, VectorShapeComponent, VectorShapeProjection, addComment, addPackageComment, applyTreeOp, assertLimitInvariants, assertValidId, assertValidQName, atomicFieldSpansOf, authoredProperties, beginOperation, bodyStoryRoot, bookmarkPairNodes, boundCustomXmlNodeIdOf, boundCustomXmlNodeIds, boundCustomXmlNodeIdsInPackage, buildTocContentControl, buildTocEntryParagraph, buildingBlockGalleryOf, buildingBlocksForControl, buildingBlocksOf, canonicalOoxmlFingerprint, canonicalize, cascadeEmptiedComments, collectReviewItems, collectRevisionSites, collectStoryParagraphs, commentItemsOf, commentPartNameOf, commentsExtendedPartNameOf, commentsPartNameForStory, compareArtifacts, compareSemVer, containsCssFetch, contentTypesPartBytes, customNodeBinding, customXmlDataParts, customXmlLabelXPath, customXmlNodes, customXmlPrefixMappings, datastoreItemIdFor, deleteCommentThread, deletePackageComments, deriveOoxmlIndexes, detectBodyTocs, diffSemanticDigests, digestPart, directParagraphMarkProperties, directParagraphProperties, displayMathAlignment, drawingOpImpact, endOperation, equationsOfAtom, escapeCssString, escapeXml, escapeXmlAttribute, evaluateGeometry, extractFragmentPackage, fieldAtomText, fieldOnOffAttribute, findCustomXmlDataPart, findDetectedToc, findDirectChild, findGraphicData, findOccurrences, fingerprint, fldCharType, fldSimpleInstr, fnv1a32, foldCase, formatOwnedRunIds, formattableRanges, formsProtectionEnabled, glossaryPartOf, hardBreakAttributes, hardBreakKind, hardBreakText, hasCommentPart, hasLegacyFormFieldData, hashAuthored, indexStyles, inlineControlEndingAt, inlineControlStartingAt, insertPackageCustomNode, inspectDrawingSupport, installDrawingRenderers, instrTextValue, isAuthorableRunProperty, isDangerousKey, isDrawingTreeDocOp, isEvaluableField, isFieldChrome, isFldChar, isFldCharNode, isFldSimple, isFldSimpleNode, isInertExecutable, isInstrText, isInstrTextNode, isLegalFldCharType, isOmmlDisplay, isOmmlEquationAtom, isPageBreakNode, isSearchableQuery, isStoryPart, isValidId, isValidNCName, isValidParaId, isValidQName, isValidXmlText, isWholeWord, legacyRotationDegrees, linkRevisionReplies, listBookmarks, locateSites, lowerDrawingTextBody, makeLimitCounter, materializeGlossaryPlaceholders, mergeFragmentIntoPackage, mergedFontProperty, mergedMultiSettingProperty, mergedParagraphMarkProperties, mergedProperties, mintParaId, mintedParagraphIdentityAttributes, nextCommentId, normalizeParagraphIdentity, nullRecord, numberingRootOf, ooxmlTreesEqual, paraIdOf, paragraphOffsetIndex, paragraphTextOf, parseEmu, parseSemVer, parseTocInstruction, planTocEntries, points$1 as points, pointsToTwips, presetGeometryDefinition, projectOmmlAtom, projectOmmlDisplay, propertyContainer, readCustomXmlNode, readTwipsMeasure, readViewSettings, refreshBoundContent, relationshipsOf, relsPartNameFor, removePackageCustomNode, replayFixture, resolve, resolveContentTypeOf, resolveTocRowHeadings, revisionItemsOf, runAddressRanges, runPropertyEdits, runsCovering, sanitizeHref, satisfies, schemaAttributeValue, scrubExport, sectionProtectsForms, segmentsOfCopy as segmentsOf, semanticDigest, serializeOoxmlPart, setCommentResolved, setPackageCommentResolved, shapeThemeCacheToken, stableHash, storyParagraphs, storyRootsOf, stylesPartOf, textboxStoriesInPart, toSafeRecord, tocEntryText, tocLeftIndentTwips, twips, twipsToPoints, usedParaIds, validateDrawingOp, validateFixture, validateOoxmlPart, validatePackageInvariants, validateTreeOp, attribute as vmlAttribute, children as vmlChildren, color as vmlColor, vmlFormulaValues, named as vmlNamed, numeric as vmlNumber, off as vmlOff, points as vmlPoints, styleOf as vmlStyle, w14RootPrefix, withContentTypeOverride, withCustomXmlDataPart, withCustomXmlNode, withExportedCustomNodes, withNewPart, withRelationship, withoutCustomXmlDataPart, withoutCustomXmlNode, withoutOrphanCustomXmlNodes, wrapTargetToAnchorSpec };
3127
3127
  export type { AddCommentRequest, AddCommentResult, AnchorSnapshot, AtomicFieldSpan, AuditPort, AuthorizationPort, BookmarkPosition, BookmarkRecord, BuildingBlock, BuildingBlockGalleryFilter, CancellationPhase, CancellationPort, CancellationToken, CapabilityId, ChartRenderContext, ClockPort, CommentAnchorRequest, CommentDeletionOwner, ComparatorDescriptor, ComparatorMode, ComparatorName, ComparisonResult, ConformanceFixture, ContentItem, Contribution, CustomNodeBinding, CustomNodeExportPolicy, CustomNodeExportRequest, CustomNodeExportResult, CustomXmlDataPart, CustomXmlDataPartResult, CustomXmlNode, DetectedToc, DiagramLayoutOptions, DiagramLayoutParts, DigestDifference, DocumentViewSettings, DrawingEffectsRenderer, DrawingGraphicProjection, DrawingRenderers, DrawingSupportEntry, DrawingSupportReport, DrawingTextDefaults, EncodedEnvelope, EnforcementPhase, EvaluatedGeometry, ExternalResourceConsentPort, FeatureBundle, FixtureExpectation, FixtureOutcome, FixtureSource, FixtureStep, FldCharType, FontPort, FormattingDisplayMode, FormattingRevisionAuthorFilter, FragmentCoverage, FragmentExtractRejection, FragmentExtractResult, FragmentMergeRejection, FragmentMergeResult, GeometryDefinition, HardBreakKind, HrefProjection, IdKind, IdentityPort, ImagePort, InertExecutableKind, InlineControlSpan, Json, LegacyTextArtContext, LimitSpec, LimitUnit, LinkableReviewItem, LoweredDrawingText, MetafileRasterImage, MetafileRenderer, ModelChangeSummary, OffsetSpan, OoxmlIndexes, OoxmlStoryKind, OoxmlStoryRoot, OperationContext, OperationInit, PackageCommentDelete, PackageInvariantCode, PackageInvariantIssue, PackageInvariantResult, ParagraphDigest, ParagraphIndexEntry, ParagraphOffsetIndex, PersistencePort, PictureEffectsProjection, Points, RegistryErrorCode, ReplacementPolicy, ReplayOutcome, ReplayReport, ReplayStore, Reservation, ResolveOptions, ResolvedRegistry, ResourceAccountingPort, RunPropertyEdit, SchedulingPort, ScrubResult, Segment, SemVer, SemanticDigest, SetCommentResolvedResult, ShapeEffectsInput, ShapingPort, StoryDigest, StoryIndexEntry, StyleIndexEntry, TextArtLayout, TextArtLayoutInput, TextArtLayoutRenderer, TextArtOutline, TextBodyLowering, TextMatchOptions, TextOccurrence, TextOccurrences, TextboxStoryRoot, TocEntryPlan, TocInstruction, TocOutlineHeading, TransportPort, Twips, ValidationResult, VmlFormulaContext };