@docx-editor.dev/core 0.0.1-placeholder → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +92 -2
- package/THIRD_PARTY_NOTICES.md +18 -0
- package/dist/automation.cjs +1 -0
- package/dist/automation.d.cts +74 -0
- package/dist/automation.d.ts +74 -0
- package/dist/automation.js +1 -0
- package/dist/binding.cjs +1 -0
- package/dist/binding.d.cts +82 -0
- package/dist/binding.d.ts +82 -0
- package/dist/binding.js +1 -0
- package/dist/chunk-5KSCB2OZ.js +2 -0
- package/dist/chunk-BSC44TIT.js +5 -0
- package/dist/chunk-DLPL5DAG.js +12 -0
- package/dist/chunk-DOEIARE2.js +2 -0
- package/dist/chunk-G7YCQBZE.cjs +5 -0
- package/dist/chunk-HBL3NNH5.cjs +1 -0
- package/dist/chunk-HR7K3W6A.js +1 -0
- package/dist/chunk-HVAV2JSN.js +11 -0
- package/dist/chunk-MYH2HCUJ.cjs +2 -0
- package/dist/chunk-NSM75TN6.cjs +1 -0
- package/dist/chunk-PHDVKUWZ.js +1 -0
- package/dist/chunk-QADYGK5L.cjs +12 -0
- package/dist/chunk-REZUWYB7.js +1 -0
- package/dist/chunk-RNBHTPW7.js +10 -0
- package/dist/chunk-UD2ZXLFI.cjs +2 -0
- package/dist/chunk-VYAVK2AH.cjs +1 -0
- package/dist/chunk-YUK46A2N.cjs +11 -0
- package/dist/chunk-ZMIX42J7.cjs +10 -0
- package/dist/chunk-ZT55Q52J.cjs +2 -0
- package/dist/chunk-ZUALAR4M.js +2 -0
- package/dist/content-control-walk-CbTqHq6O.d.cts +56 -0
- package/dist/content-control-walk-D06GijR0.d.ts +56 -0
- package/dist/contracts/document.cjs +1 -0
- package/dist/contracts/document.d.cts +246 -0
- package/dist/contracts/document.d.ts +246 -0
- package/dist/contracts/document.js +0 -0
- package/dist/contracts/editor.cjs +1 -0
- package/dist/contracts/editor.d.cts +11 -0
- package/dist/contracts/editor.d.ts +11 -0
- package/dist/contracts/editor.js +1 -0
- package/dist/contracts/interaction.cjs +1 -0
- package/dist/contracts/interaction.d.cts +11 -0
- package/dist/contracts/interaction.d.ts +11 -0
- package/dist/contracts/interaction.js +0 -0
- package/dist/contracts/modules.cjs +1 -0
- package/dist/contracts/modules.d.cts +90 -0
- package/dist/contracts/modules.d.ts +90 -0
- package/dist/contracts/modules.js +1 -0
- package/dist/contracts/types.cjs +1 -0
- package/dist/contracts/types.d.cts +452 -0
- package/dist/contracts/types.d.ts +452 -0
- package/dist/contracts/types.js +0 -0
- package/dist/editor-C1QRi1v4.d.ts +1671 -0
- package/dist/editor-D0UDock4.d.cts +1671 -0
- package/dist/editor.cjs +1 -0
- package/dist/editor.css +6017 -0
- package/dist/editor.d.cts +271 -0
- package/dist/editor.d.ts +271 -0
- package/dist/editor.js +1 -0
- package/dist/hf-references-BdjsQNE_.d.ts +71 -0
- package/dist/hf-references-CgEOSw3i.d.cts +71 -0
- package/dist/image-resources-BJnTpG66.d.ts +195 -0
- package/dist/image-resources-BpIydXtU.d.cts +195 -0
- package/dist/index-CrW-IxXt.d.cts +2515 -0
- package/dist/index-ZqBEOrTV.d.ts +2515 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +1 -0
- package/dist/layout.cjs +1 -0
- package/dist/layout.d.cts +2316 -0
- package/dist/layout.d.ts +2316 -0
- package/dist/layout.js +1 -0
- package/dist/note-nodes-BPSTVDMr.d.ts +108 -0
- package/dist/note-nodes-C6DLxlvj.d.cts +108 -0
- package/dist/note-properties-CnmCoory.d.cts +68 -0
- package/dist/note-properties-m75hmP70.d.ts +68 -0
- package/dist/ooxml-package-CR5us6ad.d.cts +354 -0
- package/dist/ooxml-package-D_CtywTW.d.ts +354 -0
- package/dist/ooxml-tree-BU0e3DVi.d.cts +871 -0
- package/dist/ooxml-tree-BU0e3DVi.d.ts +871 -0
- package/dist/output.cjs +1 -0
- package/dist/output.d.cts +158 -0
- package/dist/output.d.ts +158 -0
- package/dist/output.js +1 -0
- package/dist/protocol-CAkOBBGE.d.cts +1201 -0
- package/dist/protocol-oxHaWmt4.d.ts +1201 -0
- package/dist/review-support-DS8DYX2a.d.cts +326 -0
- package/dist/review-support-Fy3vdIea.d.ts +326 -0
- package/dist/revision-projection-72ZApe-g.d.cts +84 -0
- package/dist/revision-projection-DHAkcD_L.d.ts +84 -0
- package/dist/semantic-cell-selection-CZjBSmMv.d.cts +427 -0
- package/dist/semantic-cell-selection-DsmHmLJ5.d.ts +427 -0
- package/dist/semantic-interaction-D-Dszxrn.d.cts +187 -0
- package/dist/semantic-interaction-Dy-dCUFF.d.ts +187 -0
- package/dist/semantic-paint-drawings-BA3z9jl8.d.ts +21 -0
- package/dist/semantic-paint-drawings-D7qbB0E2.d.cts +21 -0
- package/dist/semantic-records-CJR3BvHs.d.cts +1432 -0
- package/dist/semantic-records-CWodBJsz.d.ts +1432 -0
- package/dist/shaped-measurer-B-TAtcQU.d.ts +613 -0
- package/dist/shaped-measurer-CNIT_svZ.d.cts +613 -0
- package/dist/store.cjs +1 -0
- package/dist/store.d.cts +2247 -0
- package/dist/store.d.ts +2247 -0
- package/dist/store.js +1 -0
- package/dist/tree-op-types-CwOeiaQ8.d.ts +1431 -0
- package/dist/tree-op-types-DMiW6nIH.d.cts +1431 -0
- package/dist/tree-package-store-D5S7VKs5.d.ts +708 -0
- package/dist/tree-package-store-DOKMtCnh.d.cts +708 -0
- package/dist/tree-session-CiUel5Kz.d.ts +542 -0
- package/dist/tree-session-D47_imvR.d.cts +542 -0
- package/dist/tree-store-CxifqwIv.d.cts +273 -0
- package/dist/tree-store-hLtjeyWE.d.ts +273 -0
- package/package.json +105 -15
- package/index.js +0 -1
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,2247 @@
|
|
|
1
|
+
import { e as ResourceLimits } from './image-resources-BJnTpG66.js';
|
|
2
|
+
export { C as CreateImageResourceCacheOptions, D as DEFAULT_IMAGE_RESOURCE_LIMITS, f as DEFAULT_LIMITS, H as HARD_CEILINGS, g as IMAGE_RESOURCE_HARD_CEILINGS, b as ImageDecodePort, c as ImageResourceLimits, h as ImageResourceLookup, I as ImageResourceState, M as MAX_SVG_SNIFF_BYTES, P as PreservedImageMime, S as SupportedImageMime, i as ValidatedRasterHeader, j as createImageResourceCache, k as hasBoundedSvgRoot, l as imageResourceLookupFor, m as liveDrawingReferenceCount, r as resolveImageResourceLimits, n as resolveLimits, s as sniffImageMime, o as validateGifHeader, p as validateJpegHeader, q as validatePngHeader, v as validateRasterHeader } from './image-resources-BJnTpG66.js';
|
|
3
|
+
import { O as OoxmlPackage, R as RelationshipRecord } from './ooxml-package-D_CtywTW.js';
|
|
4
|
+
export { C as ContentTypeError, c as ContentTypeIndex, d as ContentTypeRecords, D as DEFAULT_OOXML_PACKAGE_LIMITS, e as DEFAULT_ZIP_LIMITS, f as DefaultRecord, I as IMAGE_RELATIONSHIP_TYPE, g as ImageRelationshipResolution, h as IndexResult, N as NameRejection, i as NameResult, j as OoxmlExternalTarget, a as OoxmlPackageLimits, b as OoxmlPackageRejection, k as OoxmlPackageResult, l as OverrideRecord, m as RelationshipError, n as RelationshipSetResult, o as ResolveResult, p as ResolvedRelationship, T as TargetMode, Z as ZipLimits, q as ZipReadResult, r as ZipRejection, s as asciiFold, t as buildContentTypeIndex, u as buildRelationshipSet, v as detectDuplicateNames, w as extensionKey, x as isValidMime, y as normalizePartName, z as partNameKey, A as readOoxmlPackage, B as readZip, E as resolveContentType, F as resolveImageRelationship, G as resolveInternalTarget, H as resolveRelationship, J as validateExternalTarget, K as withPart, L as writeOoxmlPackage, M as writeZip } from './ooxml-package-D_CtywTW.js';
|
|
5
|
+
import { e as OoxmlPart, O as OoxmlNode, r as OoxmlInvariantResult, a as OoxmlElement, s as OoxmlInvariantIssue, t as OoxmlHardBreakNode, f as OoxmlParagraphNode, u as OoxmlFldCharNode, v as OoxmlFldSimpleNode, w as OoxmlInstrTextNode, x as OoxmlAttribute } from './ooxml-tree-BU0e3DVi.js';
|
|
6
|
+
export { y as OOXML_NODE_IDENTITY_RULES, z as OoxmlBodyNode, A as OoxmlBookmarkEndNode, B as OoxmlBookmarkStartNode, C as OoxmlContentControlCalendarNode, D as OoxmlContentControlCheckboxNode, E as OoxmlContentControlCheckedNode, F as OoxmlContentControlCheckedStateNode, G as OoxmlContentControlComboBoxNode, d as OoxmlContentControlContentNode, H as OoxmlContentControlDataBindingNode, I as OoxmlContentControlDateFormatNode, J as OoxmlContentControlDateNode, K as OoxmlContentControlDropDownListNode, p as OoxmlContentControlEndPropertiesNode, L as OoxmlContentControlLidNode, M as OoxmlContentControlListItemNode, b as OoxmlContentControlNode, q as OoxmlContentControlPropertiesNode, N as OoxmlContentControlStoreMappedDataAsNode, P as OoxmlContentControlTextNode, Q as OoxmlContentControlUncheckedStateNode, h as OoxmlContinuationSeparatorNode, R as OoxmlDocumentNode, i as OoxmlEndnotesNode, j as OoxmlFootnotesNode, c as OoxmlGenericElementNode, S as OoxmlGenericExtensionAttribute, T as OoxmlHyperlinkNode, U as OoxmlInvariantIssueCode, V as OoxmlKnownNodeAttribute, W as OoxmlNamespaceBinding, Y as OoxmlNodeId, Z as OoxmlNodeIdentityRules, g as OoxmlNoteNode, k as OoxmlNoteRefNode, l as OoxmlNoteReferenceNode, _ as OoxmlParagraphPropertiesNode, $ as OoxmlPartMetadata, n as OoxmlReadRejection, a0 as OoxmlReadResult, a1 as OoxmlRunNode, a2 as OoxmlRunPropertiesNode, m as OoxmlSeparatorNode, a3 as OoxmlTabNode, a4 as OoxmlTextElementNode, a5 as OoxmlTextNode, a6 as OoxmlWmlValAttribute, a7 as OoxmlXmlSpaceAttribute, a8 as WML_NAMESPACE_URI, a9 as XMLNS_NAMESPACE_URI, aa as XML_NAMESPACE_URI, X as XmlLimits, ab as XmlNode, ac as XmlRejection, ad as XmlResult, ae as childElements, af as findElement, ag as isContentRevisionKind, ah as isRangeMarkerKind, ai as readOnOffChild, aj as readOoxmlPart, ak as readXml, al as textContent } from './ooxml-tree-BU0e3DVi.js';
|
|
7
|
+
export { c as BookmarkAnchor, B as BookmarkIndex, C as CascadeDeletedNoteReferencesOptions, d as DEFAULT_MAX_EDITABLE_STORY_PARTS, D as DocumentTrackingSettings, e as DrawingPropertyIdResult, E as EmbeddedFont, F as FontStyleKey, H as HeaderFooterLifecycleImpact, f as HeaderFooterLifecycleOp, g as HeaderFooterLifecycleRejection, h as HeaderFooterLifecycleResult, L as ListKind, M as MAX_NOTE_REFERENCE_PARTS, i as MAX_NOTE_REFERENCE_SCAN, N as NO_TRACKING_SETTINGS, j as NoteDiagnostic, k as NoteDiagnosticCode, l as NoteLifecycleImpact, m as NoteLifecycleOp, n as NoteLifecycleOptions, o as NoteLifecycleRejection, p as NoteLifecycleResult, q as NoteReferenceHit, r as NoteReferenceScanBudget, P as PackageTransactResult, s as ReadEmbeddedFontsOptions, t as StoryResolveResult, S as StoryScope, a as StoryTargetRejection, T as TreePackageStore, u as TreePackageStoreOptions, v as allocateDrawingPropertyId, w as applyHeaderFooterLifecycleOp, x as applyNoteLifecycleOp, y as buildBookmarkIndex, z as cascadeDeletedNoteReferences, G as collectNoteReferences, J as collectPackageNoteReferences, K as createNoteReferenceScanBudget, O as deobfuscateFont, Q as diagnoseNoteReferences, U as ensureListDefinition, V as ensureNumberingLevel, W as isHeaderFooterLifecycleOp, X as isNoteLifecycleOp, Y as normalNoteIds, Z as notesPartHasId, _ as readEmbeddedFonts, $ as readTrackingSettings, a0 as resolveNotesPart, a1 as withBinaryPart, a2 as withEmbeddedImage, a3 as withoutUnreferencedImagePart } from './tree-package-store-D5S7VKs5.js';
|
|
8
|
+
export { c as HeaderFooterKind, a as HeaderFooterParts, b as HeaderFooterSectionResolution, d as HeaderFooterSlotMeta, H as HeaderFooterVariant, e as collectSectionPropertyNodes, r as resolveHeaderFooterParts, f as resolveHeaderFooterPartsBySection, g as resolveHeaderFooterResolutionBySection } from './hf-references-BdjsQNE_.js';
|
|
9
|
+
export { c as collectFlowBlocks, d as contentControlContentOf, i as isContentControl, b as isContentControlContent, w as walkAllStoryParagraphs, e as walkParagraphInline, f as walkStoryBlocks } from './content-control-walk-D06GijR0.js';
|
|
10
|
+
import { T as TreeDocOp, a as TreeOpRejection, q as TreeOpResult, O as OoxmlProperty, R as RevisionAddress, m as DrawingTreeDocOp, I as ImpactClass, b as ImageWrapTarget } from './tree-op-types-CwOeiaQ8.js';
|
|
11
|
+
export { A as ACCEPTED_PARAGRAPH_PROPERTIES, r as ACCEPTED_RUN_PROPERTIES, s as CONTENT_CONTROL_ID_MAX, t as CONTENT_CONTROL_PROPERTY_ORDER, u as ContentControlCheckbox, v as ContentControlCheckboxState, p as ContentControlDataBinding, w as ContentControlDateFormat, x as ContentControlEntry, C as ContentControlKind, n as ContentControlLevel, y as ContentControlListItem, o as ContentControlLock, z as ContentControlProperties, c as DrawingKind, e as DrawingLocks, d as DrawingPositionInput, H as HYPERLINK_RELATIONSHIP_TYPE, B as HyperlinkKind, E as HyperlinkTarget, i as IMAGE_WRAP_TARGETS, M as MAX_CONTENT_CONTROLS_PER_PART, F as MAX_CONTENT_CONTROL_NESTING, G as OFFICE_RELATIONSHIP_NAMESPACE_URI, J as RelationshipTargetResolver, S as SourceCrop, K as TABLE_BORDER_STYLES, L as TREE_DOC_OP_KINDS, l as TableBorderStyle, N as TreeDocOpKind, P as TreeOpEffect, Q as allocateContentControlId, U as contentControlContentChildren, W as contentControlContentNodeOf, X as contentControlEndPropertiesNodeOf, Y as contentControlLevelOf, Z as contentControlPropertiesNodeOf, _ as contentControlPropertiesOf, $ as contentControlTextOf, a0 as contentControlsIn, a1 as findContentControl, a2 as flattenContentControls, a3 as hyperlinkAnchorOf, a4 as hyperlinkRelationshipIdOf, a5 as hyperlinkTargetOf, a6 as isContentControlContentNode, a7 as isContentControlNode, a8 as isContentControlWrapper, a9 as isHyperlinkNode, aa as lockForbidsEdit, ab as lockForbidsRemoval, ac as orderedContentControlProperties, ad as parseContentControlId, ae as projectDrawing, af as resolveContentControlLock } from './tree-op-types-CwOeiaQ8.js';
|
|
12
|
+
export { A as AtomicNoteSpan, M as MAX_NOTES_PER_PART, a as NOTE_ATOM_CHAR, b as NOTE_CONTINUATION_SEPARATOR_ID, c as NOTE_ID_MAX, d as NOTE_ID_MIN, e as NOTE_SEPARATOR_ID, N as NoteKind, f as NoteType, g as allocateNoteId, h as atomicNoteSpansOf, i as customMarkFollows, j as findNoteById, k as formatNoteScopeId, l as isContinuationSeparatorNode, m as isEndnotesNode, o as isFootnotesNode, p as isNormalNote, q as isNoteAtomNode, r as isNoteNode, s as isNoteRefNode, t as isNoteReferenceNode, u as isSeparatorNode, v as noteAtomText, w as noteIdOf, x as noteKindOf, y as noteRefKindOf, z as noteReferenceKindOf, n as noteTypeOf, B as notesOf, C as parseNoteId, D as parseNoteScopeId } from './note-nodes-BPSTVDMr.js';
|
|
13
|
+
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-m75hmP70.js';
|
|
14
|
+
import { T as TreeModelChange, b as TreeDocumentStore } from './tree-store-hLtjeyWE.js';
|
|
15
|
+
export { S as SelectionMark, f as TreeDocumentStoreOptions, a as TreeStoryRef, d as TreeTransactOptions, g as TreeTransactResult, c as TreeTransactionContext } from './tree-store-hLtjeyWE.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Every kind of thing the registry gives a stable identity to.
|
|
19
|
+
*
|
|
20
|
+
* Identity is `(kind, id)` plus version and NEVER registration order, so two bundles contributing
|
|
21
|
+
* the same command resolve deterministically regardless of which loaded first.
|
|
22
|
+
*/
|
|
23
|
+
declare const ID_KINDS: readonly ["extension", "capability", "command", "query", "schema", "dependencyKey", "runtimePort", "result", "origin"];
|
|
24
|
+
/** One of {@link ID_KINDS}. */
|
|
25
|
+
/** One of {@link ID_KINDS} — which sort of thing an identifier names. */
|
|
26
|
+
type IdKind = (typeof ID_KINDS)[number];
|
|
27
|
+
/**
|
|
28
|
+
* Whether a string is a well-formed identifier in either accepted grammar.
|
|
29
|
+
*
|
|
30
|
+
* Reverse-domain (`dev.docx-editor.core.command.insert-text`) or package-owned
|
|
31
|
+
* (`@docx-editor.dev/engine-core#command/insert-text`). Ids are opaque once validated.
|
|
32
|
+
*/
|
|
33
|
+
declare function isValidId(id: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Validate an identifier, throwing when it is malformed.
|
|
36
|
+
*
|
|
37
|
+
* Throws rather than returning: a bad id is an author mistake at registration time, not file
|
|
38
|
+
* input, and accepting it would produce a registry whose identities silently collide.
|
|
39
|
+
*/
|
|
40
|
+
declare function assertValidId(id: string, kind?: IdKind): void;
|
|
41
|
+
/** A validated (kind, id, version) triple — the registry's unit of identity. */
|
|
42
|
+
interface CapabilityId {
|
|
43
|
+
readonly kind: IdKind;
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly version: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A parsed semantic version. Only the three numeric components — no pre-release or build. */
|
|
49
|
+
interface SemVer {
|
|
50
|
+
readonly major: number;
|
|
51
|
+
readonly minor: number;
|
|
52
|
+
readonly patch: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Parse a semantic version, rejecting anything the registry's rules do not need.
|
|
56
|
+
*
|
|
57
|
+
* Deliberately narrow: an unsupported range form is refused rather than accepted, so a malformed
|
|
58
|
+
* range can never masquerade as "compatible".
|
|
59
|
+
*/
|
|
60
|
+
declare function parseSemVer(input: string): SemVer;
|
|
61
|
+
/** Order two versions: negative, zero or positive, by major then minor then patch. */
|
|
62
|
+
declare function compareSemVer(a: SemVer, b: SemVer): -1 | 0 | 1;
|
|
63
|
+
/**
|
|
64
|
+
* Whether `version` satisfies `range`. Supported range grammar:
|
|
65
|
+
* - `*` any version
|
|
66
|
+
* - `1.2.3` exact
|
|
67
|
+
* - `^1.2.3` caret: same major, and >= the floor (major 0 pins the minor)
|
|
68
|
+
* - `>=1.2.3 <2.0.0` a single lower (inclusive) + upper (exclusive) bound pair
|
|
69
|
+
* Any other shape throws — an unparseable range is a registration error, not a pass.
|
|
70
|
+
*/
|
|
71
|
+
declare function satisfies(version: string, range: string): boolean;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Why registry resolution failed.
|
|
75
|
+
*
|
|
76
|
+
* Every code names a declaration conflict a bundle author can fix — a missing dependency, an
|
|
77
|
+
* unsatisfied version range, a duplicate contribution with no replacement policy.
|
|
78
|
+
*/
|
|
79
|
+
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';
|
|
80
|
+
/**
|
|
81
|
+
* A resolution failure naming every responsible party.
|
|
82
|
+
*
|
|
83
|
+
* Thrown rather than returned: an unresolvable registry is a build-time composition mistake, and
|
|
84
|
+
* an engine running with a half-resolved registry would fail later in ways that do not point back
|
|
85
|
+
* to the bundle that caused it.
|
|
86
|
+
*/
|
|
87
|
+
declare class RegistryError extends Error {
|
|
88
|
+
readonly code: RegistryErrorCode;
|
|
89
|
+
/** Stable identities responsible for the failure (extensions, ids). */
|
|
90
|
+
readonly responsible: readonly string[];
|
|
91
|
+
constructor(code: RegistryErrorCode, message: string,
|
|
92
|
+
/** Stable identities responsible for the failure (extensions, ids). */
|
|
93
|
+
responsible?: readonly string[]);
|
|
94
|
+
}
|
|
95
|
+
/** How a base contribution permits replacement by other bundles. */
|
|
96
|
+
type ReplacementPolicy = {
|
|
97
|
+
readonly kind: 'none';
|
|
98
|
+
} | {
|
|
99
|
+
readonly kind: 'single';
|
|
100
|
+
} | {
|
|
101
|
+
readonly kind: 'priority';
|
|
102
|
+
};
|
|
103
|
+
/** One thing a bundle contributes: a command, a query, a schema, a port, keyed by `(kind, id)`. */
|
|
104
|
+
interface Contribution {
|
|
105
|
+
readonly kind: Exclude<IdKind, 'extension' | 'origin' | 'result'>;
|
|
106
|
+
readonly id: string;
|
|
107
|
+
readonly version: string;
|
|
108
|
+
/** If set, this contribution replaces an existing base contribution. */
|
|
109
|
+
readonly replaces?: {
|
|
110
|
+
readonly targetId: string;
|
|
111
|
+
readonly targetRange: string;
|
|
112
|
+
readonly priority?: number;
|
|
113
|
+
};
|
|
114
|
+
/** Policy this contribution exposes to would-be replacers (default `none`). */
|
|
115
|
+
readonly replaceable?: ReplacementPolicy;
|
|
116
|
+
readonly payload?: unknown;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A unit of engine functionality: its identity, version, dependencies, conflicts, required ports,
|
|
120
|
+
* and contributions.
|
|
121
|
+
*
|
|
122
|
+
* The registry's whole input. Everything a bundle needs and everything it offers is DECLARED, so
|
|
123
|
+
* resolution can be deterministic and order-independent.
|
|
124
|
+
*/
|
|
125
|
+
interface FeatureBundle {
|
|
126
|
+
readonly id: string;
|
|
127
|
+
readonly version: string;
|
|
128
|
+
readonly dependencies?: readonly string[];
|
|
129
|
+
readonly conflicts?: readonly string[];
|
|
130
|
+
readonly requiredPorts?: readonly string[];
|
|
131
|
+
readonly contributions: readonly Contribution[];
|
|
132
|
+
}
|
|
133
|
+
/** The resolved result: every contribution selected, indexed by `(kind, id)`. */
|
|
134
|
+
interface ResolvedRegistry {
|
|
135
|
+
readonly extensions: ReadonlyMap<string, FeatureBundle>;
|
|
136
|
+
readonly contributions: ReadonlyMap<string, Contribution>;
|
|
137
|
+
get(kind: Contribution['kind'], id: string): Contribution | undefined;
|
|
138
|
+
}
|
|
139
|
+
/** How resolution treats optional bundles and replacement policies. */
|
|
140
|
+
interface ResolveOptions {
|
|
141
|
+
/** Runtime port ids available in this environment (design D9 / task 0.3). */
|
|
142
|
+
readonly availablePorts?: readonly string[];
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Resolve a set of bundles into one registry, or throw.
|
|
146
|
+
*
|
|
147
|
+
* Deterministic and registration-order independent: selection is by `(kind, id)` plus version,
|
|
148
|
+
* and ties break only on declared policy. Array order never decides anything.
|
|
149
|
+
*
|
|
150
|
+
* @throws RegistryError naming every bundle responsible for the failure.
|
|
151
|
+
*/
|
|
152
|
+
declare function resolve(bundles: readonly FeatureBundle[], options?: ResolveOptions): ResolvedRegistry;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Typed-origin domains (design D5 / ADR-S5). `mutation.*` are the canonical
|
|
156
|
+
* write origins; `projection` and `awareness` MUST NOT enter history, audit,
|
|
157
|
+
* snapshots, or replication.
|
|
158
|
+
*/
|
|
159
|
+
declare const ORIGIN_IDS: {
|
|
160
|
+
readonly mutationHuman: "dev.docx-editor.core.origin.mutation.human";
|
|
161
|
+
readonly mutationAgent: "dev.docx-editor.core.origin.mutation.agent";
|
|
162
|
+
readonly mutationRemote: "dev.docx-editor.core.origin.mutation.remote";
|
|
163
|
+
readonly mutationUndo: "dev.docx-editor.core.origin.mutation.undo";
|
|
164
|
+
readonly mutationRedo: "dev.docx-editor.core.origin.mutation.redo";
|
|
165
|
+
readonly mutationMigration: "dev.docx-editor.core.origin.mutation.migration";
|
|
166
|
+
readonly mutationRepair: "dev.docx-editor.core.origin.mutation.repair";
|
|
167
|
+
readonly mutationServer: "dev.docx-editor.core.origin.mutation.server";
|
|
168
|
+
readonly projection: "dev.docx-editor.core.origin.projection";
|
|
169
|
+
readonly awareness: "dev.docx-editor.core.origin.awareness";
|
|
170
|
+
};
|
|
171
|
+
/** Canonical writes carry a mutation origin; these are the undoable-eligibility inputs. */
|
|
172
|
+
declare const CANONICAL_MUTATION_ORIGINS: readonly string[];
|
|
173
|
+
/** Origins that never enter authored state / history / audit / snapshot / replication. */
|
|
174
|
+
declare const NON_CANONICAL_ORIGINS: readonly string[];
|
|
175
|
+
/**
|
|
176
|
+
* Result taxonomy (design D8 / task 7.8). A transport/protocol failure that
|
|
177
|
+
* prevents a valid envelope is a typed exception, not a member here.
|
|
178
|
+
*/
|
|
179
|
+
declare const RESULT_IDS: {
|
|
180
|
+
readonly applied: "dev.docx-editor.core.result.applied";
|
|
181
|
+
readonly validation: "dev.docx-editor.core.result.validation";
|
|
182
|
+
readonly conflict: "dev.docx-editor.core.result.conflict";
|
|
183
|
+
readonly resource: "dev.docx-editor.core.result.resource";
|
|
184
|
+
readonly authorization: "dev.docx-editor.core.result.authorization";
|
|
185
|
+
readonly aborted: "dev.docx-editor.core.result.aborted";
|
|
186
|
+
};
|
|
187
|
+
/** Runtime ports (extensions-and-runtime-ports spec; design D9). */
|
|
188
|
+
declare const RUNTIME_PORT_IDS: {
|
|
189
|
+
readonly fonts: "dev.docx-editor.core.port.fonts";
|
|
190
|
+
readonly shaping: "dev.docx-editor.core.port.shaping";
|
|
191
|
+
readonly images: "dev.docx-editor.core.port.images";
|
|
192
|
+
readonly clock: "dev.docx-editor.core.port.clock";
|
|
193
|
+
readonly identity: "dev.docx-editor.core.port.identity";
|
|
194
|
+
readonly persistence: "dev.docx-editor.core.port.persistence";
|
|
195
|
+
readonly transport: "dev.docx-editor.core.port.transport";
|
|
196
|
+
readonly scheduling: "dev.docx-editor.core.port.scheduling";
|
|
197
|
+
readonly audit: "dev.docx-editor.core.port.audit";
|
|
198
|
+
readonly authorization: "dev.docx-editor.core.port.authorization";
|
|
199
|
+
readonly resourceAccounting: "dev.docx-editor.core.port.resource-accounting";
|
|
200
|
+
readonly cancellation: "dev.docx-editor.core.port.cancellation";
|
|
201
|
+
readonly externalResourceConsent: "dev.docx-editor.core.port.external-resource-consent";
|
|
202
|
+
};
|
|
203
|
+
/** Layout dependency keys (design D6 / task 8.2). */
|
|
204
|
+
declare const DEPENDENCY_KEY_IDS: {
|
|
205
|
+
readonly style: "dev.docx-editor.core.dep.style";
|
|
206
|
+
readonly numbering: "dev.docx-editor.core.dep.numbering";
|
|
207
|
+
readonly section: "dev.docx-editor.core.dep.section";
|
|
208
|
+
readonly story: "dev.docx-editor.core.dep.story";
|
|
209
|
+
readonly font: "dev.docx-editor.core.dep.font";
|
|
210
|
+
readonly image: "dev.docx-editor.core.dep.image";
|
|
211
|
+
readonly table: "dev.docx-editor.core.dep.table";
|
|
212
|
+
readonly field: "dev.docx-editor.core.dep.field";
|
|
213
|
+
readonly note: "dev.docx-editor.core.dep.note";
|
|
214
|
+
readonly headerFooter: "dev.docx-editor.core.dep.header-footer";
|
|
215
|
+
readonly annotation: "dev.docx-editor.core.dep.annotation";
|
|
216
|
+
};
|
|
217
|
+
/** Every frozen id as a flat list, for the immutability / uniqueness gate. */
|
|
218
|
+
declare const ALL_FROZEN_IDS: readonly string[];
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A counter's limit was reached. Carries the label, the limit, and what was attempted.
|
|
222
|
+
*
|
|
223
|
+
* Thrown at the N → N+1 boundary, so the increment that would have crossed the limit never
|
|
224
|
+
* happens rather than being detected afterwards.
|
|
225
|
+
*/
|
|
226
|
+
declare class LimitExceededError extends Error {
|
|
227
|
+
readonly label: string;
|
|
228
|
+
readonly limit: number;
|
|
229
|
+
readonly attempted: number;
|
|
230
|
+
constructor(label: string, limit: number, attempted: number);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Overflow-safe counting against a fixed limit.
|
|
234
|
+
*
|
|
235
|
+
* Never silently wraps: it rejects the increment that would cross its limit AND any arithmetic
|
|
236
|
+
* that would exceed `Number.MAX_SAFE_INTEGER`. That second guard is the point — a file-supplied
|
|
237
|
+
* count must never be trusted into an allocation, and a wrapped counter reads as small.
|
|
238
|
+
*/
|
|
239
|
+
declare class BoundedCounter {
|
|
240
|
+
readonly label: string;
|
|
241
|
+
readonly limit: number;
|
|
242
|
+
private value;
|
|
243
|
+
constructor(label: string, limit: number);
|
|
244
|
+
get current(): number;
|
|
245
|
+
/** Remaining headroom before the limit. */
|
|
246
|
+
get remaining(): number;
|
|
247
|
+
/**
|
|
248
|
+
* Add `n` (default 1). Throws LimitExceededError if the result would exceed the
|
|
249
|
+
* limit (so `limit` itself is reachable but `limit + 1` is not) or overflow the
|
|
250
|
+
* safe-integer range. On rejection the counter is unchanged.
|
|
251
|
+
*/
|
|
252
|
+
add(n?: number): number;
|
|
253
|
+
/** Whether adding `n` would be accepted, without mutating. */
|
|
254
|
+
canAdd(n?: number): boolean;
|
|
255
|
+
release(n: number): void;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** A budget was misused: over-carved, over-reserved, or disposed with children outstanding. */
|
|
259
|
+
declare class BudgetError extends Error {
|
|
260
|
+
constructor(message: string);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Capacity claimed BEFORE it is allocated.
|
|
264
|
+
*
|
|
265
|
+
* Reserve-then-allocate rather than allocate-then-check: discovering the overrun after the
|
|
266
|
+
* allocation has already happened defeats the point of having a budget.
|
|
267
|
+
*/
|
|
268
|
+
interface Reservation {
|
|
269
|
+
readonly amount: number;
|
|
270
|
+
readonly released: boolean;
|
|
271
|
+
release(): void;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* One node in the hierarchical resource budget tree.
|
|
275
|
+
*
|
|
276
|
+
* An operation owns a root budget; parsers, extensions, workers, layout, transport and output
|
|
277
|
+
* carve children from it, so no subsystem can consume more than the operation as a whole allows.
|
|
278
|
+
*
|
|
279
|
+
* `dispose()` REFUSES while children or reservations are outstanding, then runs registered
|
|
280
|
+
* cleanups LIFO — spill files, worker termination — even if one throws. A leaked child budget is a
|
|
281
|
+
* leaked worker, so the refusal is the diagnostic.
|
|
282
|
+
*/
|
|
283
|
+
declare class Budget {
|
|
284
|
+
readonly label: string;
|
|
285
|
+
readonly capacity: number;
|
|
286
|
+
private readonly used;
|
|
287
|
+
private readonly children;
|
|
288
|
+
private activeReservations;
|
|
289
|
+
private readonly cleanups;
|
|
290
|
+
private disposed;
|
|
291
|
+
private carveAmount;
|
|
292
|
+
private parent?;
|
|
293
|
+
constructor(label: string, capacity: number);
|
|
294
|
+
get inUse(): number;
|
|
295
|
+
get available(): number;
|
|
296
|
+
get isDisposed(): boolean;
|
|
297
|
+
/** Reserve `amount` from this budget. Throws if it would exceed capacity. */
|
|
298
|
+
reserve(amount: number): Reservation;
|
|
299
|
+
/** Carve a child budget of `capacity` out of this budget's headroom. */
|
|
300
|
+
child(label: string, capacity: number): Budget;
|
|
301
|
+
/** Register cleanup for a spill file, worker, or queue tied to this budget. */
|
|
302
|
+
onRelease(cleanup: () => void): void;
|
|
303
|
+
/**
|
|
304
|
+
* Release this budget. Refuses while any child budget or reservation is
|
|
305
|
+
* outstanding (children must finish first). Runs cleanups LIFO, returns the
|
|
306
|
+
* carve-out to the parent, and marks disposed. Cleanup errors are collected and
|
|
307
|
+
* rethrown after every cleanup has run.
|
|
308
|
+
*/
|
|
309
|
+
dispose(): void;
|
|
310
|
+
private assertLive;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Which side of the point of no return an operation is on.
|
|
315
|
+
*
|
|
316
|
+
* Canonical publication is that point. Cancelling BEFORE it rolls the whole operation back;
|
|
317
|
+
* cancelling after it leaves the commit standing and cancels only derived work — layout, export,
|
|
318
|
+
* caches.
|
|
319
|
+
*/
|
|
320
|
+
type CancellationPhase = 'pre-publication' | 'post-publication';
|
|
321
|
+
/** Thrown from a `checkpoint()` when the operation has been cancelled. */
|
|
322
|
+
declare class CancellationError extends Error {
|
|
323
|
+
/** True when the commit is already published and only derived work is cancelled. */
|
|
324
|
+
readonly derivedOnly: boolean;
|
|
325
|
+
constructor(
|
|
326
|
+
/** True when the commit is already published and only derived work is cancelled. */
|
|
327
|
+
derivedOnly: boolean, reason?: string);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The read side of cancellation, handed to cooperative work.
|
|
331
|
+
*
|
|
332
|
+
* Cooperative rather than pre-emptive: long work calls `checkpoint()` at declared intervals, which
|
|
333
|
+
* is what lets an operation unwind promptly without the engine having to interrupt it mid-mutation.
|
|
334
|
+
*/
|
|
335
|
+
interface CancellationToken {
|
|
336
|
+
readonly isCancelled: boolean;
|
|
337
|
+
readonly phase: CancellationPhase;
|
|
338
|
+
/** True only when cancelled AND the commit is already published. */
|
|
339
|
+
readonly derivedOnly: boolean;
|
|
340
|
+
/** Throws CancellationError if cancelled; call at declared checkpoint intervals. */
|
|
341
|
+
checkpoint(): void;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* The write side of cancellation, and the owner of the phase.
|
|
345
|
+
*
|
|
346
|
+
* Whoever starts an operation holds this; everything it calls gets only the token.
|
|
347
|
+
*/
|
|
348
|
+
declare class CancellationController {
|
|
349
|
+
private _cancelled;
|
|
350
|
+
private _phase;
|
|
351
|
+
private _reason?;
|
|
352
|
+
cancel(reason?: string): void;
|
|
353
|
+
/** Mark canonical publication — the point of no return. */
|
|
354
|
+
markPublished(): void;
|
|
355
|
+
get isCancelled(): boolean;
|
|
356
|
+
get phase(): CancellationPhase;
|
|
357
|
+
get isPublished(): boolean;
|
|
358
|
+
/** Cancelled after publication -> full rollback is no longer possible. */
|
|
359
|
+
get derivedOnly(): boolean;
|
|
360
|
+
get token(): CancellationToken;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/** Injectable time source — the engine never calls Date.now directly (determinism). */
|
|
364
|
+
interface ClockPort {
|
|
365
|
+
now(): number;
|
|
366
|
+
}
|
|
367
|
+
/** Stable id minting for sessions, commits, and allocator seeds. */
|
|
368
|
+
interface IdentityPort {
|
|
369
|
+
newId(): string;
|
|
370
|
+
}
|
|
371
|
+
/** Cooperative scheduling of deferred work. */
|
|
372
|
+
interface SchedulingPort {
|
|
373
|
+
schedule(task: () => void): void;
|
|
374
|
+
}
|
|
375
|
+
/** Redacted observability sink (raw text never enters here — design D5). */
|
|
376
|
+
interface AuditPort {
|
|
377
|
+
record(entry: {
|
|
378
|
+
readonly kind: string;
|
|
379
|
+
readonly at: number;
|
|
380
|
+
readonly meta?: Record<string, unknown>;
|
|
381
|
+
}): void;
|
|
382
|
+
}
|
|
383
|
+
/** Read/write/export authorization decisions. */
|
|
384
|
+
interface AuthorizationPort {
|
|
385
|
+
authorize(action: string, subject?: string): boolean;
|
|
386
|
+
}
|
|
387
|
+
/** Explicit consent gate for any remote/external resource (no zero-click fetch). */
|
|
388
|
+
interface ExternalResourceConsentPort {
|
|
389
|
+
requestConsent(target: string): boolean;
|
|
390
|
+
}
|
|
391
|
+
/** Cancellation source for the current operation. */
|
|
392
|
+
interface CancellationPort {
|
|
393
|
+
readonly token: CancellationToken;
|
|
394
|
+
}
|
|
395
|
+
/** Where documents are stored and retrieved. Opaque marker until its milestone. */
|
|
396
|
+
interface PersistencePort {
|
|
397
|
+
readonly kind: 'persistence';
|
|
398
|
+
}
|
|
399
|
+
/** How the engine talks to a remote peer. Opaque marker until its milestone. */
|
|
400
|
+
interface TransportPort {
|
|
401
|
+
readonly kind: 'transport';
|
|
402
|
+
}
|
|
403
|
+
/** Where font bytes come from. Opaque marker until its milestone. */
|
|
404
|
+
interface FontPort {
|
|
405
|
+
readonly kind: 'font';
|
|
406
|
+
}
|
|
407
|
+
/** Which shaping backend measures text. Opaque marker until its milestone. */
|
|
408
|
+
interface ShapingPort {
|
|
409
|
+
readonly kind: 'shaping';
|
|
410
|
+
}
|
|
411
|
+
/** How image bytes are decoded. Opaque marker until its milestone. */
|
|
412
|
+
interface ImagePort {
|
|
413
|
+
readonly kind: 'image';
|
|
414
|
+
}
|
|
415
|
+
/** Where resource consumption is reported. Opaque marker until its milestone. */
|
|
416
|
+
interface ResourceAccountingPort {
|
|
417
|
+
readonly kind: 'resource-accounting';
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* A required port the runtime did not provide.
|
|
421
|
+
*
|
|
422
|
+
* Thrown rather than falling back: silently reaching for a browser global when a worker or server
|
|
423
|
+
* adapter did not supply a port is exactly the coupling ports exist to prevent.
|
|
424
|
+
*/
|
|
425
|
+
declare class PortResolutionError extends Error {
|
|
426
|
+
readonly portId: string;
|
|
427
|
+
constructor(portId: string);
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The environment-dependent services an engine instance may reach.
|
|
431
|
+
*
|
|
432
|
+
* Everything outside pure computation — the clock, identity minting, scheduling, fonts, images —
|
|
433
|
+
* is reached only through here, which is what lets browser, worker and server adapters supply
|
|
434
|
+
* only what their runtime actually offers.
|
|
435
|
+
*/
|
|
436
|
+
declare class PortRegistry {
|
|
437
|
+
private readonly ports;
|
|
438
|
+
provide(portId: string, port: unknown): this;
|
|
439
|
+
has(portId: string): boolean;
|
|
440
|
+
/** Resolve a port by id or throw. */
|
|
441
|
+
resolve<T>(portId: string): T;
|
|
442
|
+
/** Assert every id in `portIds` is present; returns the missing ones (empty if all present). */
|
|
443
|
+
missing(portIds: readonly string[]): string[];
|
|
444
|
+
/** The provided port ids (for bridging to registry.resolve availablePorts). */
|
|
445
|
+
availablePorts(): string[];
|
|
446
|
+
clock(): ClockPort;
|
|
447
|
+
identity(): IdentityPort;
|
|
448
|
+
}
|
|
449
|
+
/** Deterministic clock: starts at `start` and advances by `step` each call. */
|
|
450
|
+
declare class DeterministicClock implements ClockPort {
|
|
451
|
+
private readonly step;
|
|
452
|
+
private t;
|
|
453
|
+
constructor(start?: number, step?: number);
|
|
454
|
+
now(): number;
|
|
455
|
+
}
|
|
456
|
+
/** Sequential identity for tests and deterministic fixtures. */
|
|
457
|
+
declare class SequentialIdentity implements IdentityPort {
|
|
458
|
+
private readonly prefix;
|
|
459
|
+
private n;
|
|
460
|
+
constructor(prefix?: string);
|
|
461
|
+
newId(): string;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/** What one operation is started with. Only `ports` is required. */
|
|
465
|
+
interface OperationInit {
|
|
466
|
+
readonly ports: PortRegistry;
|
|
467
|
+
readonly limits?: Partial<ResourceLimits>;
|
|
468
|
+
readonly config?: Readonly<Record<string, unknown>>;
|
|
469
|
+
/** Root budget capacity in abstract units (default: the byte decompression limit). */
|
|
470
|
+
readonly capacity?: number;
|
|
471
|
+
/** Operation identity (from IdentityPort in production); defaulted for tests. */
|
|
472
|
+
readonly id?: string;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* The frozen environment of one operation: its limits, config, ports, root budget and
|
|
476
|
+
* cancellation controller.
|
|
477
|
+
*
|
|
478
|
+
* IMMUTABLE for the operation's lifetime, which is what lets cache reuse and replica agreement
|
|
479
|
+
* key on it. A new operation always gets a fresh snapshot rather than observing a mutated one.
|
|
480
|
+
*/
|
|
481
|
+
interface OperationContext {
|
|
482
|
+
readonly id: string;
|
|
483
|
+
readonly limits: ResourceLimits;
|
|
484
|
+
readonly config: Readonly<Record<string, unknown>>;
|
|
485
|
+
readonly ports: PortRegistry;
|
|
486
|
+
readonly budget: Budget;
|
|
487
|
+
readonly cancellation: CancellationController;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Begin an operation, capturing its immutable environment snapshot.
|
|
491
|
+
*
|
|
492
|
+
* Resolves and freezes limits and configuration, carves the root budget, and creates the
|
|
493
|
+
* cancellation controller — everything downstream work needs, fixed for the operation's duration.
|
|
494
|
+
*/
|
|
495
|
+
declare function beginOperation(init: OperationInit): OperationContext;
|
|
496
|
+
/** Release the operation's root budget (requires every child/reservation released). */
|
|
497
|
+
declare function endOperation(ctx: OperationContext): void;
|
|
498
|
+
|
|
499
|
+
/** What a limit counts. Typed so a byte cap and a depth cap cannot be compared by accident. */
|
|
500
|
+
type LimitUnit = 'bytes' | 'count' | 'depth' | 'ratio' | 'passes';
|
|
501
|
+
/**
|
|
502
|
+
* Where a limit is enforced.
|
|
503
|
+
*
|
|
504
|
+
* Declared per limit so enforcement happens at the boundary that can still refuse cheaply — a zip
|
|
505
|
+
* cap checked during layout has already let the bomb decompress.
|
|
506
|
+
*/
|
|
507
|
+
type EnforcementPhase = 'package-read' | 'xml-parse' | 'layout' | 'output';
|
|
508
|
+
/** One limit's unit and enforcement phase — the metadata that makes limits testable uniformly. */
|
|
509
|
+
interface LimitSpec {
|
|
510
|
+
readonly unit: LimitUnit;
|
|
511
|
+
readonly phase: EnforcementPhase;
|
|
512
|
+
}
|
|
513
|
+
/** Unit + enforcement phase for every resource limit. */
|
|
514
|
+
declare const LIMIT_SPECS: Readonly<Record<keyof ResourceLimits, LimitSpec>>;
|
|
515
|
+
/** Every limit name, for iterating the specs and asserting each has a unit and phase. */
|
|
516
|
+
declare const LIMIT_KEYS: (keyof ResourceLimits)[];
|
|
517
|
+
/** A phase-scoped overflow-safe counter for one limit. */
|
|
518
|
+
declare function makeLimitCounter(limits: ResourceLimits, key: keyof ResourceLimits): BoundedCounter;
|
|
519
|
+
/**
|
|
520
|
+
* Deterministic memory substitute. Tracks current and peak "allocated" units
|
|
521
|
+
* against a hard byte ceiling; `allocate` fails closed (LimitExceededError) past
|
|
522
|
+
* the limit. Used in place of process RSS so memory-limit tests are reproducible
|
|
523
|
+
* and cancellation/cleanup can be asserted without a real allocator.
|
|
524
|
+
*/
|
|
525
|
+
declare class DeterministicMemoryMeter {
|
|
526
|
+
private readonly counter;
|
|
527
|
+
private current;
|
|
528
|
+
private peak;
|
|
529
|
+
constructor(limitBytes: number);
|
|
530
|
+
allocate(bytes: number): void;
|
|
531
|
+
free(bytes: number): void;
|
|
532
|
+
get currentBytes(): number;
|
|
533
|
+
get peakBytes(): number;
|
|
534
|
+
/** Release everything (cancellation/cleanup path). */
|
|
535
|
+
reset(): void;
|
|
536
|
+
}
|
|
537
|
+
/** Assert the finite-default / hard-ceiling invariant holds for every limit. */
|
|
538
|
+
declare function assertLimitInvariants(): void;
|
|
539
|
+
|
|
540
|
+
/** Any JSON value. The domain canonicalization and stable hashing operate over. */
|
|
541
|
+
type Json = null | boolean | number | string | Json[] | {
|
|
542
|
+
[k: string]: Json;
|
|
543
|
+
};
|
|
544
|
+
/**
|
|
545
|
+
* Produce a canonical string for `value`, dropping any object key whose name is
|
|
546
|
+
* in `ephemera` at any depth. Numbers are emitted losslessly; -0 normalizes to 0;
|
|
547
|
+
* non-finite numbers are rejected (they must never enter a comparator input).
|
|
548
|
+
*/
|
|
549
|
+
declare function canonicalize(value: unknown, ephemera?: ReadonlySet<string>): string;
|
|
550
|
+
/** 64-bit FNV-1a over the canonical form, returned as a 16-char hex string. */
|
|
551
|
+
declare function stableHash(value: unknown, ephemera?: ReadonlySet<string>): string;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* How one artifact class is compared.
|
|
555
|
+
*
|
|
556
|
+
* Frozen per artifact: nothing here invents a tolerance for an artifact the spec requires to
|
|
557
|
+
* match exactly.
|
|
558
|
+
*/
|
|
559
|
+
type ComparatorMode = 'canonical-exact' | 'exact' | 'tolerance' | 'sync-optimization-only';
|
|
560
|
+
/** One artifact class's comparison mode and its declared ephemera — fields excluded from equality. */
|
|
561
|
+
interface ComparatorDescriptor {
|
|
562
|
+
readonly id: string;
|
|
563
|
+
readonly mode: ComparatorMode;
|
|
564
|
+
/** Object keys excluded from canonical-exact comparison (declared ephemera). */
|
|
565
|
+
readonly ephemera: readonly string[];
|
|
566
|
+
readonly note: string;
|
|
567
|
+
}
|
|
568
|
+
/** The frozen artifact comparator set. */
|
|
569
|
+
declare const COMPARATORS: {
|
|
570
|
+
readonly authoredState: {
|
|
571
|
+
readonly id: "dev.docx-editor.core.comparator.authored-state";
|
|
572
|
+
readonly mode: "canonical-exact";
|
|
573
|
+
readonly ephemera: readonly ["revision", "provenance", "producedAt", "commitId"];
|
|
574
|
+
readonly note: "canonical normalized authored records; ephemera excluded";
|
|
575
|
+
};
|
|
576
|
+
readonly anchor: {
|
|
577
|
+
readonly id: "dev.docx-editor.core.comparator.anchor";
|
|
578
|
+
readonly mode: "exact";
|
|
579
|
+
readonly ephemera: readonly [];
|
|
580
|
+
readonly note: "internal anchor identity/affinity compares exactly";
|
|
581
|
+
};
|
|
582
|
+
readonly yjsStateVector: {
|
|
583
|
+
readonly id: "dev.docx-editor.core.comparator.yjs-state-vector";
|
|
584
|
+
readonly mode: "sync-optimization-only";
|
|
585
|
+
readonly ephemera: readonly [];
|
|
586
|
+
readonly note: "exchange optimization only; never proves update or delete-set coverage";
|
|
587
|
+
};
|
|
588
|
+
readonly shapedRun: {
|
|
589
|
+
readonly id: "dev.docx-editor.core.comparator.shaped-run";
|
|
590
|
+
readonly mode: "exact";
|
|
591
|
+
readonly ephemera: readonly [];
|
|
592
|
+
readonly note: "glyph ids, clusters, and fixed-point advances compare exactly";
|
|
593
|
+
};
|
|
594
|
+
readonly paginationFingerprint: {
|
|
595
|
+
readonly id: "dev.docx-editor.core.comparator.pagination-fingerprint";
|
|
596
|
+
readonly mode: "exact";
|
|
597
|
+
readonly ephemera: readonly [];
|
|
598
|
+
readonly note: "page/column boundaries, break causes, fixed-point geometry compare exactly";
|
|
599
|
+
};
|
|
600
|
+
readonly semanticTree: {
|
|
601
|
+
readonly id: "dev.docx-editor.core.comparator.semantic-tree";
|
|
602
|
+
readonly mode: "exact";
|
|
603
|
+
readonly ephemera: readonly [];
|
|
604
|
+
readonly note: "reading order, roles, headings, alt text compare exactly";
|
|
605
|
+
};
|
|
606
|
+
readonly hitTest: {
|
|
607
|
+
readonly id: "dev.docx-editor.core.comparator.hit-test";
|
|
608
|
+
readonly mode: "exact";
|
|
609
|
+
readonly ephemera: readonly [];
|
|
610
|
+
readonly note: "resolved hit target and cluster affinity compare exactly";
|
|
611
|
+
};
|
|
612
|
+
readonly pdfSemantics: {
|
|
613
|
+
readonly id: "dev.docx-editor.core.comparator.pdf-semantics";
|
|
614
|
+
readonly mode: "canonical-exact";
|
|
615
|
+
readonly ephemera: readonly ["objectNumber", "producer", "creationDate", "modDate", "subsetTag"];
|
|
616
|
+
readonly note: "canonical semantic PDF objects; container ephemera excluded";
|
|
617
|
+
};
|
|
618
|
+
readonly rasterCheckpoint: {
|
|
619
|
+
readonly id: "dev.docx-editor.core.comparator.raster-checkpoint";
|
|
620
|
+
readonly mode: "tolerance";
|
|
621
|
+
readonly ephemera: readonly [];
|
|
622
|
+
readonly note: "documented unavoidable raster comparison; explicit tolerance only";
|
|
623
|
+
};
|
|
624
|
+
readonly benchmarkEvidence: {
|
|
625
|
+
readonly id: "dev.docx-editor.core.comparator.benchmark-evidence";
|
|
626
|
+
readonly mode: "sync-optimization-only";
|
|
627
|
+
readonly ephemera: readonly [];
|
|
628
|
+
readonly note: "diagnostic evidence, not an equivalence basis";
|
|
629
|
+
};
|
|
630
|
+
};
|
|
631
|
+
/** Which frozen comparator to use. */
|
|
632
|
+
type ComparatorName = keyof typeof COMPARATORS;
|
|
633
|
+
/** Whether two artifacts matched, and where they diverged when they did not. */
|
|
634
|
+
interface ComparisonResult {
|
|
635
|
+
readonly equal: boolean;
|
|
636
|
+
/** Canonical forms when unequal (diagnostic). */
|
|
637
|
+
readonly left?: string;
|
|
638
|
+
readonly right?: string;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Compare two artifacts under a named comparator. `canonical-exact` and `exact`
|
|
642
|
+
* compare canonical forms (exact drops no ephemera). `tolerance` requires an
|
|
643
|
+
* epsilon and compares finite numbers structurally. `sync-optimization-only`
|
|
644
|
+
* throws — such artifacts are never an equivalence basis.
|
|
645
|
+
*/
|
|
646
|
+
declare function compareArtifacts(name: ComparatorName, left: unknown, right: unknown, opts?: {
|
|
647
|
+
epsilon?: number;
|
|
648
|
+
}): ComparisonResult;
|
|
649
|
+
/** Fingerprint an artifact under a named comparator's canonicalization policy. */
|
|
650
|
+
declare function fingerprint(name: ComparatorName, value: unknown): string;
|
|
651
|
+
|
|
652
|
+
/** Whether a recorded conformance step was expected to commit or be refused. */
|
|
653
|
+
type FixtureOutcome = 'applied' | 'aborted' | 'validation' | 'conflict' | 'resource' | 'authorization';
|
|
654
|
+
/** The change one step produced, summarized to what a fixture can compare across runtimes. */
|
|
655
|
+
interface ModelChangeSummary {
|
|
656
|
+
readonly fromRevision: number;
|
|
657
|
+
readonly toRevision: number;
|
|
658
|
+
readonly origin: string;
|
|
659
|
+
readonly dirty: readonly string[];
|
|
660
|
+
readonly dependencyKeys: readonly string[];
|
|
661
|
+
}
|
|
662
|
+
/** Where anchors sat after a step — how a fixture proves positions survived an edit. */
|
|
663
|
+
interface AnchorSnapshot {
|
|
664
|
+
readonly anchorId: string;
|
|
665
|
+
readonly story: string;
|
|
666
|
+
readonly block: string;
|
|
667
|
+
readonly affinity: 'before' | 'after';
|
|
668
|
+
}
|
|
669
|
+
/** What one step must produce: its outcome, its change summary, and its authored-state hash. */
|
|
670
|
+
interface FixtureExpectation {
|
|
671
|
+
readonly outcome: FixtureOutcome;
|
|
672
|
+
/** Present iff outcome === 'applied'; MUST be > baseRevision. */
|
|
673
|
+
readonly committedRevision?: number;
|
|
674
|
+
readonly modelChange?: ModelChangeSummary;
|
|
675
|
+
/** 16-hex authored-state fingerprint (comparator 0.4). */
|
|
676
|
+
readonly authoredStateHash?: string;
|
|
677
|
+
readonly outputHash?: string;
|
|
678
|
+
readonly anchors?: readonly AnchorSnapshot[];
|
|
679
|
+
}
|
|
680
|
+
/** One recorded operation and what it was expected to do. */
|
|
681
|
+
interface FixtureStep {
|
|
682
|
+
readonly baseRevision: number;
|
|
683
|
+
readonly origin: string;
|
|
684
|
+
/** Opaque DocOp payloads (schema owned by section 4). */
|
|
685
|
+
readonly ops: readonly unknown[];
|
|
686
|
+
readonly expect: FixtureExpectation;
|
|
687
|
+
}
|
|
688
|
+
/** Opaque encoded backend bytes (snapshot or replication update). */
|
|
689
|
+
interface EncodedEnvelope {
|
|
690
|
+
readonly kind: 'snapshot' | 'update';
|
|
691
|
+
readonly protocolVersion: number;
|
|
692
|
+
readonly schemaVersion: number;
|
|
693
|
+
readonly documentId: string;
|
|
694
|
+
readonly byteLength: number;
|
|
695
|
+
/** Hex-encoded opaque bytes; length MUST equal byteLength. */
|
|
696
|
+
readonly bytesHex: string;
|
|
697
|
+
}
|
|
698
|
+
/** Which implementation recorded a fixture — local store, Yjs, or a binding. */
|
|
699
|
+
type FixtureSource = {
|
|
700
|
+
readonly kind: 'create';
|
|
701
|
+
} | {
|
|
702
|
+
readonly kind: 'docx';
|
|
703
|
+
readonly sha256: string;
|
|
704
|
+
readonly bytesRef: string;
|
|
705
|
+
};
|
|
706
|
+
/**
|
|
707
|
+
* The frozen conformance container: revisions, origins, operations, changes, snapshots and
|
|
708
|
+
* hashes.
|
|
709
|
+
*
|
|
710
|
+
* The CONTAINER is what is frozen — field names, origin membership, revision monotonicity, hash
|
|
711
|
+
* format. The op and change payloads travel opaquely, so their schemas can evolve without
|
|
712
|
+
* invalidating every recorded fixture.
|
|
713
|
+
*/
|
|
714
|
+
interface ConformanceFixture {
|
|
715
|
+
readonly formatVersion: 1;
|
|
716
|
+
readonly documentId: string;
|
|
717
|
+
readonly source: FixtureSource;
|
|
718
|
+
readonly steps: readonly FixtureStep[];
|
|
719
|
+
readonly snapshots?: readonly EncodedEnvelope[];
|
|
720
|
+
readonly updates?: readonly EncodedEnvelope[];
|
|
721
|
+
}
|
|
722
|
+
/** Whether a fixture is well-formed, listing every structural violation. */
|
|
723
|
+
interface ValidationResult {
|
|
724
|
+
readonly valid: boolean;
|
|
725
|
+
readonly errors: readonly string[];
|
|
726
|
+
}
|
|
727
|
+
/** Structurally validate a fixture against the frozen format rules. */
|
|
728
|
+
declare function validateFixture(fixture: ConformanceFixture): ValidationResult;
|
|
729
|
+
|
|
730
|
+
/** The outcome a store reports for one replayed step. */
|
|
731
|
+
interface ReplayOutcome {
|
|
732
|
+
readonly outcome: FixtureOutcome;
|
|
733
|
+
readonly committedRevision?: number;
|
|
734
|
+
/** Canonical authored-state value AFTER the step (hashed via comparator 0.4). */
|
|
735
|
+
readonly authoredState?: unknown;
|
|
736
|
+
}
|
|
737
|
+
/** The interface a conformance runtime implements so one fixture can drive every backend. */
|
|
738
|
+
interface ReplayStore {
|
|
739
|
+
/** Initialize from the fixture source; return the initial revision (0 for create). */
|
|
740
|
+
init(fixture: ConformanceFixture): number;
|
|
741
|
+
/** Apply one step and report what happened. */
|
|
742
|
+
applyStep(step: FixtureStep): ReplayOutcome;
|
|
743
|
+
}
|
|
744
|
+
/** What replaying a fixture produced: per-step outcomes and where they diverged. */
|
|
745
|
+
interface ReplayReport {
|
|
746
|
+
readonly ok: boolean;
|
|
747
|
+
readonly mismatches: readonly string[];
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Replay a fixture against a store and check every step's outcome, committed
|
|
751
|
+
* revision, and authored-state fingerprint against the fixture's expectations.
|
|
752
|
+
*/
|
|
753
|
+
declare function replayFixture(fixture: ConformanceFixture, store: ReplayStore): ReplayReport;
|
|
754
|
+
/** Hash a store's authored state, so two runtimes replaying one fixture can be compared. */
|
|
755
|
+
declare function hashAuthored(authoredState: unknown): string;
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Keys that must never be assigned from file data.
|
|
759
|
+
*
|
|
760
|
+
* XML attribute names become object keys, and a document controls those names — assigning
|
|
761
|
+
* `__proto__` into an ordinary object is the prototype-pollution hazard this engine audits for.
|
|
762
|
+
*/
|
|
763
|
+
declare const DANGEROUS_KEYS: readonly string[];
|
|
764
|
+
/** A file-derived key that would pollute a prototype. Refused, never sanitized-and-accepted. */
|
|
765
|
+
declare class DangerousKeyError extends Error {
|
|
766
|
+
readonly key: string;
|
|
767
|
+
readonly path: string;
|
|
768
|
+
constructor(key: string, path: string);
|
|
769
|
+
}
|
|
770
|
+
/** Whether a key is one of {@link DANGEROUS_KEYS}. */
|
|
771
|
+
declare function isDangerousKey(key: string): boolean;
|
|
772
|
+
/** A fresh null-prototype record — the only object shape parser intermediates use. */
|
|
773
|
+
declare function nullRecord<T = unknown>(): Record<string, T>;
|
|
774
|
+
/**
|
|
775
|
+
* Recursively convert `value` into null-prototype records, rejecting any
|
|
776
|
+
* dangerous object key. Arrays and primitives pass through (arrays rebuilt so no
|
|
777
|
+
* inherited prototype pollution survives). Throws DangerousKeyError on the first
|
|
778
|
+
* unsafe key, naming its path. Cyclic inputs are rejected.
|
|
779
|
+
*/
|
|
780
|
+
declare function toSafeRecord(value: unknown, path?: string): unknown;
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Serialize normalized XML from a canonical part using repository-controlled
|
|
784
|
+
* prefixes and validated, escaped names and values. This does not yet replace
|
|
785
|
+
* writeDocx; package integration belongs to the subsequent migration pass.
|
|
786
|
+
*/
|
|
787
|
+
declare function serializeOoxmlPart(part: OoxmlPart): string;
|
|
788
|
+
/** Repository-owned namespace-aware semantic XML oracle. */
|
|
789
|
+
declare function canonicalOoxmlFingerprint(value: OoxmlPart | OoxmlNode): string;
|
|
790
|
+
/**
|
|
791
|
+
* Structural equality of two canonical trees, ignoring node ids.
|
|
792
|
+
*
|
|
793
|
+
* Ids differ between two parses of the same bytes, so comparing them would report every reopen as
|
|
794
|
+
* a change. This compares what the document SAYS.
|
|
795
|
+
*/
|
|
796
|
+
declare function ooxmlTreesEqual(left: OoxmlPart | OoxmlNode, right: OoxmlPart | OoxmlNode): boolean;
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Validate a parser-created or copy-modified immutable part before publication.
|
|
800
|
+
* Future tree-edit primitives can retain shared nodes and their IDs, while any
|
|
801
|
+
* replacement chooses explicitly whether to retain or allocate identity.
|
|
802
|
+
*/
|
|
803
|
+
declare function validateOoxmlPart(part: OoxmlPart): OoxmlInvariantResult;
|
|
804
|
+
|
|
805
|
+
/** Locate `[Content_Types].xml` bytes regardless of zip key spelling. */
|
|
806
|
+
declare function contentTypesPartBytes(pkg: OoxmlPackage): {
|
|
807
|
+
readonly storageKey: string;
|
|
808
|
+
readonly bytes: Uint8Array;
|
|
809
|
+
} | null;
|
|
810
|
+
/**
|
|
811
|
+
* What a package-level invariant walk found wrong.
|
|
812
|
+
*
|
|
813
|
+
* Package invariants are cross-PART: a relationship pointing at a part that does not exist, or a
|
|
814
|
+
* part no content-type record covers. Neither is visible from inside a single part's tree.
|
|
815
|
+
*/
|
|
816
|
+
type PackageInvariantCode = 'dangling-relationship' | 'missing-content-type'
|
|
817
|
+
/** A part occupies a name OPC reserves for package infrastructure. */
|
|
818
|
+
| 'reserved-part-name'
|
|
819
|
+
/** Two parts whose names differ only by case, which OPC treats as one part. */
|
|
820
|
+
| 'duplicate-part-name'
|
|
821
|
+
/** A part name the OPC screens refuse; `writeZip` would throw on save. */
|
|
822
|
+
| 'unsafe-part-name';
|
|
823
|
+
/** One package invariant violation, located by part name and relationship id. */
|
|
824
|
+
interface PackageInvariantIssue {
|
|
825
|
+
readonly code: PackageInvariantCode;
|
|
826
|
+
/** The part the issue is about: the missing target, or the part with no type. */
|
|
827
|
+
readonly partName: string;
|
|
828
|
+
/** For a dangling relationship, the owner that points at nothing. */
|
|
829
|
+
readonly ownerPart?: string;
|
|
830
|
+
}
|
|
831
|
+
/** Whether a package satisfies its cross-part invariants, listing every violation otherwise. */
|
|
832
|
+
type PackageInvariantResult = {
|
|
833
|
+
readonly ok: true;
|
|
834
|
+
} | {
|
|
835
|
+
readonly ok: false;
|
|
836
|
+
readonly issues: readonly PackageInvariantIssue[];
|
|
837
|
+
};
|
|
838
|
+
/** Relationship records owned by one part, in authored order. */
|
|
839
|
+
declare function relationshipsOf(pkg: OoxmlPackage, ownerPart: string): readonly RelationshipRecord[];
|
|
840
|
+
/** The content type that resolves for a part name, or null when nothing declares one. */
|
|
841
|
+
declare function resolveContentTypeOf(pkg: OoxmlPackage, partName: string): string | null;
|
|
842
|
+
/** The `.rels` part that owns a part's relationships, by OPC convention. */
|
|
843
|
+
declare function relsPartNameFor(partName: string): string;
|
|
844
|
+
/**
|
|
845
|
+
* Declare a content type for a part, by upserting an `<Override>` in the content-types tree.
|
|
846
|
+
*
|
|
847
|
+
* A no-op when the part already resolves to the same type, so repeating a write does not append
|
|
848
|
+
* a duplicate entry. Resolving to a DIFFERENT type replaces the existing Override in place.
|
|
849
|
+
* `forceOverride` writes an explicit Override even when a Default already matches.
|
|
850
|
+
*/
|
|
851
|
+
declare function withContentTypeOverride(pkg: OoxmlPackage, partName: string, contentType: string, options?: {
|
|
852
|
+
readonly forceOverride?: boolean;
|
|
853
|
+
}): OoxmlPackage;
|
|
854
|
+
/** Add or replace one relationship on a part, returning a new package. */
|
|
855
|
+
declare function withRelationship(pkg: OoxmlPackage, ownerPart: string, type: string, rawTarget: string): {
|
|
856
|
+
readonly pkg: OoxmlPackage;
|
|
857
|
+
readonly relationshipId: string;
|
|
858
|
+
readonly ok: boolean;
|
|
859
|
+
};
|
|
860
|
+
/**
|
|
861
|
+
* Add a part that does not exist yet, with the content-type override it needs to be openable.
|
|
862
|
+
*
|
|
863
|
+
* Deliberately does NOT create a relationship: a part is reachable because something points at
|
|
864
|
+
* it, and which part points at it is the caller's decision. Creating one here would guess.
|
|
865
|
+
*/
|
|
866
|
+
declare function withNewPart(pkg: OoxmlPackage, partName: string, root: OoxmlElement, contentType: string): OoxmlPackage;
|
|
867
|
+
/**
|
|
868
|
+
* The two invariants a package must satisfy before it is published.
|
|
869
|
+
*
|
|
870
|
+
* Both describe the half-written state that splitting a multi-part write across transactions
|
|
871
|
+
* produces, and both make a package Word refuses to open:
|
|
872
|
+
*
|
|
873
|
+
* - a relationship pointing at a part nobody created;
|
|
874
|
+
* - a part with no content type, which is unopenable even though the XML is well formed.
|
|
875
|
+
*
|
|
876
|
+
* Checked at the commit boundary rather than inside each primitive, for the same reason part
|
|
877
|
+
* validation moved there: a transaction is allowed to pass through an inconsistent intermediate
|
|
878
|
+
* as long as nothing can observe it.
|
|
879
|
+
*/
|
|
880
|
+
declare function validatePackageInvariants(pkg: OoxmlPackage): PackageInvariantResult;
|
|
881
|
+
|
|
882
|
+
/** Whether a string is a valid XML NCName — a name with no colon. */
|
|
883
|
+
declare function isValidNCName(name: string): boolean;
|
|
884
|
+
/** A QName is an optional `prefix:` (both NCNames) — never attacker-derived. */
|
|
885
|
+
declare function isValidQName(name: string): boolean;
|
|
886
|
+
/**
|
|
887
|
+
* Validate a qualified name, throwing when it is malformed.
|
|
888
|
+
*
|
|
889
|
+
* Guards the serializer: an invalid QName written into XML produces a file Word cannot open, so
|
|
890
|
+
* it fails here rather than at save.
|
|
891
|
+
*/
|
|
892
|
+
declare function assertValidQName(name: string): void;
|
|
893
|
+
/**
|
|
894
|
+
* Controlled namespace-prefix allocation: deterministic, collision-free prefixes
|
|
895
|
+
* for namespace URIs. A known URI always yields the same registered prefix; new
|
|
896
|
+
* URIs get a generated `ns{n}` prefix, never one derived from file content.
|
|
897
|
+
*/
|
|
898
|
+
declare class PrefixAllocator {
|
|
899
|
+
private readonly byUri;
|
|
900
|
+
private readonly usedPrefixes;
|
|
901
|
+
private counter;
|
|
902
|
+
constructor(known?: Readonly<Record<string, string>>);
|
|
903
|
+
prefixFor(namespaceUri: string): string;
|
|
904
|
+
/** The declared bindings, for emitting xmlns declarations. */
|
|
905
|
+
bindings(): {
|
|
906
|
+
prefix: string;
|
|
907
|
+
uri: string;
|
|
908
|
+
}[];
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/**
|
|
912
|
+
* A hyperlink target projected for a RUNTIME sink, or the reason it was withheld.
|
|
913
|
+
*
|
|
914
|
+
* The authored target stays authored — the record layer keeps it verbatim and escapes it into
|
|
915
|
+
* owned OOXML, which is not a runtime sink. Only this allowlist-sanitized projection reaches DOM,
|
|
916
|
+
* CSS, navigation or fetch, so `javascript:`, `data:` and `vbscript:` never leave the boundary.
|
|
917
|
+
*/
|
|
918
|
+
type HrefProjection = {
|
|
919
|
+
readonly ok: true;
|
|
920
|
+
readonly href: string;
|
|
921
|
+
} | {
|
|
922
|
+
readonly ok: false;
|
|
923
|
+
readonly inert: true;
|
|
924
|
+
};
|
|
925
|
+
/**
|
|
926
|
+
* Project a file-derived URL for a DOM/navigation sink. Strips embedded
|
|
927
|
+
* tab/LF/CR (used to smuggle `java\nscript:`), allows relative URLs and the
|
|
928
|
+
* scheme allowlist, and renders everything else inert. Never fetches.
|
|
929
|
+
*/
|
|
930
|
+
declare function sanitizeHref(raw: string): HrefProjection;
|
|
931
|
+
/** XML-escape a value for validated serialization back into owned OOXML. */
|
|
932
|
+
declare function escapeXml(value: string): string;
|
|
933
|
+
/**
|
|
934
|
+
* CSS string-escape a file-derived value (e.g. an `@font-face` family name or an
|
|
935
|
+
* inline style value). Emits `\<hex> ` escapes for quotes, backslash, and
|
|
936
|
+
* controls so the value cannot break out of its CSS string.
|
|
937
|
+
*/
|
|
938
|
+
declare function escapeCssString(value: string): string;
|
|
939
|
+
/** Whether a file-derived CSS fragment contains a `url()` or `@import` (must be rejected). */
|
|
940
|
+
declare function containsCssFetch(value: string): boolean;
|
|
941
|
+
/**
|
|
942
|
+
* Content that must never execute or auto-resolve: OLE objects, macros, DDE and `INCLUDE*` field
|
|
943
|
+
* instructions.
|
|
944
|
+
*
|
|
945
|
+
* Rendered INERT by default rather than stripped — removing it would be a lossless-preservation
|
|
946
|
+
* failure, so it is carried and never acted on.
|
|
947
|
+
*/
|
|
948
|
+
declare const INERT_EXECUTABLE_KINDS: readonly ["field-dde", "field-include", "macro", "activex", "ole", "embedded-object", "executable-relationship"];
|
|
949
|
+
/** One of {@link INERT_EXECUTABLE_KINDS}. */
|
|
950
|
+
/** One of {@link INERT_EXECUTABLE_KINDS} — content carried but never executed. */
|
|
951
|
+
type InertExecutableKind = (typeof INERT_EXECUTABLE_KINDS)[number];
|
|
952
|
+
/** Whether a content kind is one this engine refuses to execute or auto-resolve. */
|
|
953
|
+
declare function isInertExecutable(kind: string): boolean;
|
|
954
|
+
/** Whether a field instruction's leading keyword is safe to evaluate. */
|
|
955
|
+
declare function isEvaluableField(instruction: string): boolean;
|
|
956
|
+
/** One item a scrub pass considers: its kind, and where in the package it sits. */
|
|
957
|
+
interface ContentItem {
|
|
958
|
+
readonly id: string;
|
|
959
|
+
readonly kind: string;
|
|
960
|
+
}
|
|
961
|
+
/**
|
|
962
|
+
* What an explicit scrub removed.
|
|
963
|
+
*
|
|
964
|
+
* A scrub DECLARES ITSELF non-lossless: removing executable content changes the file, which is a
|
|
965
|
+
* choice a caller makes deliberately rather than a default the engine applies.
|
|
966
|
+
*/
|
|
967
|
+
interface ScrubResult {
|
|
968
|
+
readonly kept: readonly ContentItem[];
|
|
969
|
+
readonly removed: readonly ContentItem[];
|
|
970
|
+
/** A scrub that removes anything is non-lossless by definition. */
|
|
971
|
+
readonly nonLossless: boolean;
|
|
972
|
+
}
|
|
973
|
+
/** Explicit scrub export: remove inert executable classes, report removals. */
|
|
974
|
+
declare function scrubExport(items: readonly ContentItem[]): ScrubResult;
|
|
975
|
+
|
|
976
|
+
/** An edited part, or the invariant violations that rejected the edit. */
|
|
977
|
+
type OoxmlEditResult = {
|
|
978
|
+
readonly ok: true;
|
|
979
|
+
readonly part: OoxmlPart;
|
|
980
|
+
} | {
|
|
981
|
+
readonly ok: false;
|
|
982
|
+
readonly issues: readonly OoxmlInvariantIssue[];
|
|
983
|
+
};
|
|
984
|
+
/**
|
|
985
|
+
* How an edit primitive validates its result.
|
|
986
|
+
*
|
|
987
|
+
* By default every primitive runs the full-part invariant validation before handing its
|
|
988
|
+
* result back — the safe reading for an isolated call. A TRANSACTION applying many ops pays
|
|
989
|
+
* that full-tree walk once per primitive, which is what made a hundred-paragraph paste
|
|
990
|
+
* quadratic; it defers instead, and runs the same validation ONCE on the final tree before
|
|
991
|
+
* anything is published. Deferring is only sound for a caller that owns a commit boundary:
|
|
992
|
+
* nothing may escape between the unvalidated intermediate and the validated result.
|
|
993
|
+
*/
|
|
994
|
+
interface EditOptions {
|
|
995
|
+
readonly deferValidation?: boolean;
|
|
996
|
+
}
|
|
997
|
+
/** Every node id currently present in the part. */
|
|
998
|
+
declare function collectNodeIds(part: OoxmlPart): Set<string>;
|
|
999
|
+
/**
|
|
1000
|
+
* Mint ids for nodes an edit introduces.
|
|
1001
|
+
*
|
|
1002
|
+
* Deterministic and collision-checked against the whole part: a structural-path id from the
|
|
1003
|
+
* original parse (`/word/document.xml#0.1.2`) and a minted one (`/word/document.xml#new:3`)
|
|
1004
|
+
* can never coincide, and the counter skips anything already taken so repeated edits in one
|
|
1005
|
+
* session stay unique.
|
|
1006
|
+
*
|
|
1007
|
+
* Checks the part's node index directly rather than copying every id into a fresh set: the
|
|
1008
|
+
* copy was O(document) per op, and an allocator is created for every op.
|
|
1009
|
+
*/
|
|
1010
|
+
declare function createNodeIdAllocator(part: OoxmlPart): () => string;
|
|
1011
|
+
/** Whether a node id exists in the part. */
|
|
1012
|
+
declare function hasNode(part: OoxmlPart, nodeId: string): boolean;
|
|
1013
|
+
/** Read a node back out of a part by id. */
|
|
1014
|
+
declare function findNode(part: OoxmlPart, nodeId: string): OoxmlNode | null;
|
|
1015
|
+
/** The element that holds a node, or null for the root and for unknown ids. */
|
|
1016
|
+
declare function parentNodeOf(part: OoxmlPart, nodeId: string): OoxmlElement | null;
|
|
1017
|
+
/** Replace one node's children wholesale. */
|
|
1018
|
+
declare function replaceChildren(part: OoxmlPart, nodeId: string, children: readonly OoxmlNode[], options?: EditOptions): OoxmlEditResult;
|
|
1019
|
+
/** Insert children into a node at `index` (clamped to the child list). */
|
|
1020
|
+
declare function insertChildren(part: OoxmlPart, nodeId: string, index: number, children: readonly OoxmlNode[], options?: EditOptions): OoxmlEditResult;
|
|
1021
|
+
/** Replace one node with another, keeping its position among its siblings. */
|
|
1022
|
+
declare function replaceNode(part: OoxmlPart, nodeId: string, replacement: OoxmlNode, options?: EditOptions): OoxmlEditResult;
|
|
1023
|
+
/** Remove a node and its subtree. */
|
|
1024
|
+
declare function removeNode(part: OoxmlPart, nodeId: string, options?: EditOptions): OoxmlEditResult;
|
|
1025
|
+
/**
|
|
1026
|
+
* Apply several primitives as ONE atomic step.
|
|
1027
|
+
*
|
|
1028
|
+
* Each edit runs against the result of the previous one, and the whole sequence is
|
|
1029
|
+
* validated once at the end. If any step fails, the ORIGINAL part is what the caller keeps
|
|
1030
|
+
* — there is no partially-edited intermediate to publish. This is the shape a multi-`DocOp`
|
|
1031
|
+
* store transaction needs.
|
|
1032
|
+
*/
|
|
1033
|
+
declare function applyEdits(part: OoxmlPart, edits: readonly ((current: OoxmlPart) => OoxmlEditResult)[], options?: EditOptions): OoxmlEditResult;
|
|
1034
|
+
|
|
1035
|
+
/** UTF-16 placeholder for a page break in paragraph text projections. */
|
|
1036
|
+
declare const PAGE_BREAK_CHAR = "\f";
|
|
1037
|
+
/** What a `w:br` breaks. `other` covers values this engine does not model, kept losslessly. */
|
|
1038
|
+
type HardBreakKind = 'line' | 'page' | 'column' | 'other';
|
|
1039
|
+
/** Read the semantic break kind from a typed `w:br` node. */
|
|
1040
|
+
declare function hardBreakKind(node: OoxmlHardBreakNode): HardBreakKind;
|
|
1041
|
+
/** Whether a node is a `w:br` with `w:type="page"`. */
|
|
1042
|
+
declare function isPageBreakNode(node: OoxmlNode): node is OoxmlHardBreakNode;
|
|
1043
|
+
/** Text projection for a `w:br` node — one UTF-16 code unit per break. */
|
|
1044
|
+
declare function hardBreakText(node: OoxmlHardBreakNode): string;
|
|
1045
|
+
/** Attributes for a newly authored `w:br`. */
|
|
1046
|
+
declare function hardBreakAttributes(kind: 'line' | 'page'): OoxmlHardBreakNode['attributes'];
|
|
1047
|
+
|
|
1048
|
+
/** UTF-16 placeholder for one atomic field unit in `paragraphTextOf` / segments. */
|
|
1049
|
+
declare const FIELD_ATOM_CHAR = "\uFFFC";
|
|
1050
|
+
/**
|
|
1051
|
+
* Which part of a complex field a `w:fldChar` marks.
|
|
1052
|
+
*
|
|
1053
|
+
* A complex field spans many runs: `begin`, the instruction, `separate`, the cached result, then
|
|
1054
|
+
* `end` — which is why a field is one logical unit across several nodes.
|
|
1055
|
+
*/
|
|
1056
|
+
type FldCharType = 'begin' | 'separate' | 'end';
|
|
1057
|
+
/** Read `@w:fldCharType` when present and schema-legal. */
|
|
1058
|
+
declare function fldCharType(node: OoxmlNode): FldCharType | null;
|
|
1059
|
+
/** Whether a node is a `w:fldChar` field boundary marker. */
|
|
1060
|
+
declare function isFldCharNode(node: OoxmlNode): node is OoxmlFldCharNode;
|
|
1061
|
+
/**
|
|
1062
|
+
* Whether a node is `w:instrText` — a field's instruction.
|
|
1063
|
+
*
|
|
1064
|
+
* Instructions are never EXECUTED or auto-resolved: `DDE` and `INCLUDE*` render inert.
|
|
1065
|
+
*/
|
|
1066
|
+
declare function isInstrTextNode(node: OoxmlNode): node is OoxmlInstrTextNode;
|
|
1067
|
+
/** Whether a node is `w:fldSimple` — a field whose instruction and result are one element. */
|
|
1068
|
+
declare function isFldSimpleNode(node: OoxmlNode): node is OoxmlFldSimpleNode;
|
|
1069
|
+
/** Typed or generic `w:fldChar` with the given type. */
|
|
1070
|
+
declare function isFldChar(node: OoxmlNode, type: FldCharType): boolean;
|
|
1071
|
+
/** Typed or generic `w:instrText`. */
|
|
1072
|
+
declare function isInstrText(node: OoxmlNode): boolean;
|
|
1073
|
+
/** Typed or generic `w:fldSimple`. */
|
|
1074
|
+
declare function isFldSimple(node: OoxmlNode): boolean;
|
|
1075
|
+
/** Concatenated text descendants of `w:instrText` (instruction only — never executed). */
|
|
1076
|
+
declare function instrTextValue(node: OoxmlNode): string;
|
|
1077
|
+
/** `@w:instr` on `w:fldSimple`, or undefined when absent. */
|
|
1078
|
+
declare function fldSimpleInstr(node: OoxmlNode): string | undefined;
|
|
1079
|
+
/**
|
|
1080
|
+
* Read an on/off WML attribute (`dirty` / `fldLock`).
|
|
1081
|
+
*
|
|
1082
|
+
* Returns `undefined` when absent, otherwise the OOXML on/off interpretation
|
|
1083
|
+
* (present without val, or val not explicitly off → true).
|
|
1084
|
+
*/
|
|
1085
|
+
declare function fieldOnOffAttribute(node: OoxmlNode, localName: 'dirty' | 'fldLock'): boolean | undefined;
|
|
1086
|
+
/** Model text contributed by one atomic field unit. */
|
|
1087
|
+
declare function fieldAtomText(): typeof FIELD_ATOM_CHAR;
|
|
1088
|
+
/**
|
|
1089
|
+
* True when a node is field chrome that never contributes its own model text outside
|
|
1090
|
+
* an atomic span (markers + instruction). Cached result `w:t` is separate.
|
|
1091
|
+
*/
|
|
1092
|
+
declare function isFieldChrome(node: OoxmlNode): boolean;
|
|
1093
|
+
/**
|
|
1094
|
+
* One atomic field span inside a paragraph for caret / delete / selection.
|
|
1095
|
+
*
|
|
1096
|
+
* `removeNodeIds` lists every node that must leave with the unit (begin…end chrome and
|
|
1097
|
+
* cached-result content for complex fields; the `fldSimple` element for simple fields).
|
|
1098
|
+
*
|
|
1099
|
+
* `formatRunIds` lists the runs whose `w:rPr` owns displayed result formatting — result-phase
|
|
1100
|
+
* runs with measurable cache text for complex fields, child `w:r`s for `fldSimple`, or the
|
|
1101
|
+
* separate/begin run when the result is empty. Delete / caret addressing still uses `runId`
|
|
1102
|
+
* (the begin / simple node); formatting must not rewrite chrome-only begin runs when the
|
|
1103
|
+
* painted glyphs come from a different result run.
|
|
1104
|
+
*/
|
|
1105
|
+
interface AtomicFieldSpan {
|
|
1106
|
+
readonly kind: 'complex' | 'simple';
|
|
1107
|
+
/** Addressable segment node (begin `fldChar` or `fldSimple`). */
|
|
1108
|
+
readonly node: OoxmlNode;
|
|
1109
|
+
/** Run that owns the begin marker; empty string for paragraph-level `fldSimple`. */
|
|
1110
|
+
readonly runId: string;
|
|
1111
|
+
readonly removeNodeIds: readonly string[];
|
|
1112
|
+
/** Runs that own displayed result formatting (may differ from `runId`). */
|
|
1113
|
+
readonly formatRunIds: readonly string[];
|
|
1114
|
+
}
|
|
1115
|
+
/**
|
|
1116
|
+
* Collect well-formed atomic field spans in document order.
|
|
1117
|
+
*
|
|
1118
|
+
* Demotion (no span emitted — callers surface interior text normally):
|
|
1119
|
+
* - `end` without matching `begin`
|
|
1120
|
+
* - orphan `instrText` outside an open field
|
|
1121
|
+
* - missing `end` before paragraph end
|
|
1122
|
+
* - nesting deeper than `maxNesting`
|
|
1123
|
+
* - instruction longer than `maxInstructionChars` (still forms a span when begin/end
|
|
1124
|
+
* pair closes, but callers may treat evaluation as inert; addressing stays atomic)
|
|
1125
|
+
*
|
|
1126
|
+
* Cross-paragraph fields never form: this walk is per paragraph.
|
|
1127
|
+
*/
|
|
1128
|
+
declare function atomicFieldSpansOf(paragraph: OoxmlParagraphNode, options?: {
|
|
1129
|
+
readonly maxNesting?: number;
|
|
1130
|
+
readonly maxInstructionChars?: number;
|
|
1131
|
+
}): readonly AtomicFieldSpan[];
|
|
1132
|
+
/** Whether `fldCharType` is a legal ST_FldCharType value (used by tests / guards). */
|
|
1133
|
+
declare function isLegalFldCharType(value: string): boolean;
|
|
1134
|
+
|
|
1135
|
+
/** Parsed TOC instruction. Unknown switches are ignored for generation but left on the wire. */
|
|
1136
|
+
interface TocInstruction {
|
|
1137
|
+
readonly keyword: 'TOC';
|
|
1138
|
+
/** Include hyperlinks (`\\h`). */
|
|
1139
|
+
readonly hyperlink: boolean;
|
|
1140
|
+
/** Omit page numbers (`\\n`). */
|
|
1141
|
+
readonly omitPageNumbers: boolean;
|
|
1142
|
+
/** Inclusive 1-based outline levels from `\\o "n-m"`. Defaults 1–9. */
|
|
1143
|
+
readonly outlineStart: number;
|
|
1144
|
+
readonly outlineEnd: number;
|
|
1145
|
+
readonly raw: string;
|
|
1146
|
+
}
|
|
1147
|
+
/** Longest TOC field instruction read. Instructions come from a file; the parse is bounded. */
|
|
1148
|
+
declare const TOC_MAX_INSTRUCTION_CHARS = 256;
|
|
1149
|
+
/** Most entries one generated table of contents may hold. */
|
|
1150
|
+
declare const TOC_MAX_ENTRIES = 512;
|
|
1151
|
+
/** Most bookmarks minted during one TOC refresh. */
|
|
1152
|
+
declare const TOC_MAX_BOOKMARKS_PER_REFRESH = 512;
|
|
1153
|
+
/** Deepest nested field instruction followed. Caps recursion on file-supplied structure. */
|
|
1154
|
+
declare const TOC_MAX_FIELD_NESTING = 4;
|
|
1155
|
+
/**
|
|
1156
|
+
* Most layout passes a TOC refresh runs before settling.
|
|
1157
|
+
*
|
|
1158
|
+
* Page numbers change the TOC's own height, which changes page numbers — bounded so a
|
|
1159
|
+
* non-converging document stops rather than looping.
|
|
1160
|
+
*/
|
|
1161
|
+
declare const TOC_MAX_PAGE_PASSES = 3;
|
|
1162
|
+
/**
|
|
1163
|
+
* Parse a TOC field instruction string.
|
|
1164
|
+
*
|
|
1165
|
+
* Returns null when the leading keyword is not TOC, the string is over-long, or the
|
|
1166
|
+
* outline range is hostile. Does not evaluate or fetch anything.
|
|
1167
|
+
*/
|
|
1168
|
+
declare function parseTocInstruction(raw: string): TocInstruction | null;
|
|
1169
|
+
|
|
1170
|
+
/** A table of contents found in a document, whether wrapped in an SDT or a bare field. */
|
|
1171
|
+
interface DetectedToc {
|
|
1172
|
+
/** Enclosing control id when it identifies one TOC, otherwise the begin fldChar id. */
|
|
1173
|
+
readonly id: string;
|
|
1174
|
+
readonly beginNodeId: string;
|
|
1175
|
+
readonly beginParagraphId: string;
|
|
1176
|
+
readonly endParagraphId: string;
|
|
1177
|
+
readonly resultParagraphIds: readonly string[];
|
|
1178
|
+
/** Direct parent whose paragraph children delimit the cached result. */
|
|
1179
|
+
readonly containerId: string;
|
|
1180
|
+
readonly contentControlId?: string;
|
|
1181
|
+
readonly instruction: TocInstruction;
|
|
1182
|
+
}
|
|
1183
|
+
/** Discover refreshable body TOCs without evaluating any field instruction. */
|
|
1184
|
+
declare function detectBodyTocs(part: OoxmlPart): readonly DetectedToc[];
|
|
1185
|
+
/** Locate the table of contents containing a position, or null. */
|
|
1186
|
+
declare function findDetectedToc(tocs: readonly DetectedToc[], tocId: string): DetectedToc | null;
|
|
1187
|
+
|
|
1188
|
+
/** Outline heading shape consumed by TOC planning (mirrors DocumentOutlineEntry). */
|
|
1189
|
+
interface TocOutlineHeading {
|
|
1190
|
+
readonly text: string;
|
|
1191
|
+
readonly level: number;
|
|
1192
|
+
readonly blockId: string;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
/** Left-indent step between TOC levels, in twips (matches `scripts/demo-doc/toc-block.xml`). */
|
|
1196
|
+
declare const TOC_LEVEL_INDENT_TWIPS = 240;
|
|
1197
|
+
/** Bounded left-indent twips for a TOC entry level (0-based heading depth). */
|
|
1198
|
+
declare function tocLeftIndentTwips(level: number): number;
|
|
1199
|
+
/** One planned TOC entry: its level, its text, and the heading it points at. */
|
|
1200
|
+
interface TocEntryPlan {
|
|
1201
|
+
readonly level: number;
|
|
1202
|
+
readonly text: string;
|
|
1203
|
+
readonly headingParagraphId: string;
|
|
1204
|
+
readonly bookmarkName: string;
|
|
1205
|
+
readonly pageNumberText: string;
|
|
1206
|
+
}
|
|
1207
|
+
/** Build one TOC entry paragraph node. */
|
|
1208
|
+
declare function buildTocEntryParagraph(mint: () => string, entry: TocEntryPlan, instruction: TocInstruction, paragraphPropertiesTemplate?: OoxmlNode): OoxmlNode;
|
|
1209
|
+
/** Build an SDT-wrapped complex TOC field with an already planned cached result. */
|
|
1210
|
+
declare function buildTocContentControl(mint: () => string, entries: readonly TocEntryPlan[], instruction: TocInstruction, alias: string): OoxmlNode;
|
|
1211
|
+
/**
|
|
1212
|
+
* Word flattens manual line/tab breaks from a heading into spaces in its TOC cache.
|
|
1213
|
+
* Carrying them verbatim makes a short title wrap even when the row has ample room, and the
|
|
1214
|
+
* same normalization is what lets a cached row be matched back to the heading it came from.
|
|
1215
|
+
*/
|
|
1216
|
+
declare function tocEntryText(text: string): string;
|
|
1217
|
+
/**
|
|
1218
|
+
* Plan TOC entries from the outline and existing bookmarks.
|
|
1219
|
+
*/
|
|
1220
|
+
declare function planTocEntries(part: OoxmlPart, outline: readonly TocOutlineHeading[], instruction: TocInstruction, pageNumberByParagraphId: ReadonlyMap<string, string>, excludeParagraphIds: ReadonlySet<string>): {
|
|
1221
|
+
readonly entries: readonly TocEntryPlan[];
|
|
1222
|
+
readonly bookmarksToCreate: readonly {
|
|
1223
|
+
paragraphId: string;
|
|
1224
|
+
name: string;
|
|
1225
|
+
}[];
|
|
1226
|
+
};
|
|
1227
|
+
/** Create bookmarkStart/End pair nodes for insertion at the start of a paragraph. */
|
|
1228
|
+
declare function bookmarkPairNodes(mint: () => string, name: string, id: string): {
|
|
1229
|
+
readonly start: OoxmlNode;
|
|
1230
|
+
readonly end: OoxmlNode;
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
/**
|
|
1234
|
+
* The heading each cached result row stands for, aligned with `toc.resultParagraphIds`.
|
|
1235
|
+
*
|
|
1236
|
+
* `null` for a row that names no heading this document still has — a stale row, or the
|
|
1237
|
+
* chrome/blank paragraphs a cached result can carry. Callers leave those alone rather than
|
|
1238
|
+
* writing another row's number into them.
|
|
1239
|
+
*/
|
|
1240
|
+
declare function resolveTocRowHeadings(part: OoxmlPart, toc: DetectedToc, outline: readonly TocOutlineHeading[], excludeParagraphIds: ReadonlySet<string>): readonly (string | null)[];
|
|
1241
|
+
|
|
1242
|
+
/** One paragraph in the index: its node id, its story, and its position. */
|
|
1243
|
+
interface ParagraphIndexEntry {
|
|
1244
|
+
/** Canonical tree node id — the stable identity operations address. */
|
|
1245
|
+
readonly nodeId: string;
|
|
1246
|
+
/** Position among the story's paragraphs, in document order. */
|
|
1247
|
+
readonly ordinal: number;
|
|
1248
|
+
/** Concatenated text content, with tabs and breaks mapped as the model reads them. */
|
|
1249
|
+
readonly text: string;
|
|
1250
|
+
/** Node ids of the paragraph's runs, in order. */
|
|
1251
|
+
readonly runIds: readonly string[];
|
|
1252
|
+
}
|
|
1253
|
+
/** One story root — body, a header/footer variant, or a note part. */
|
|
1254
|
+
interface StoryIndexEntry {
|
|
1255
|
+
/** The part the story lives in. */
|
|
1256
|
+
readonly partName: string;
|
|
1257
|
+
/** The `w:body` (or story root) node id. */
|
|
1258
|
+
readonly rootId: string;
|
|
1259
|
+
readonly paragraphs: readonly ParagraphIndexEntry[];
|
|
1260
|
+
}
|
|
1261
|
+
/** One style definition, indexed by the id content references it under. */
|
|
1262
|
+
interface StyleIndexEntry {
|
|
1263
|
+
readonly styleId: string;
|
|
1264
|
+
readonly type: string;
|
|
1265
|
+
readonly nodeId: string;
|
|
1266
|
+
readonly name: string | null;
|
|
1267
|
+
readonly basedOn: string | null;
|
|
1268
|
+
/** Whether the part marks this the default style for its type (`w:default="1"`). */
|
|
1269
|
+
readonly isDefault: boolean;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* The derived lookups over a part: paragraphs, stories and styles by id.
|
|
1273
|
+
*
|
|
1274
|
+
* Diff-patched on commit rather than rebuilt — rebuilding every index per keystroke is what made
|
|
1275
|
+
* typing scale with document length.
|
|
1276
|
+
*/
|
|
1277
|
+
interface OoxmlIndexes {
|
|
1278
|
+
/** The tree revision these projections were derived from. */
|
|
1279
|
+
readonly revision: number;
|
|
1280
|
+
/** Body story first, then any other story parts, keyed by part name. */
|
|
1281
|
+
readonly stories: ReadonlyMap<string, StoryIndexEntry>;
|
|
1282
|
+
/** Every paragraph across every story, keyed by node id. */
|
|
1283
|
+
readonly paragraphs: ReadonlyMap<string, ParagraphIndexEntry>;
|
|
1284
|
+
readonly relationships: ReadonlyMap<string, readonly RelationshipRecord[]>;
|
|
1285
|
+
readonly styles: ReadonlyMap<string, StyleIndexEntry>;
|
|
1286
|
+
}
|
|
1287
|
+
/** Style definitions, read out of the generic `w:styles` tree. */
|
|
1288
|
+
declare function indexStyles(part: OoxmlPart | undefined): Map<string, StyleIndexEntry>;
|
|
1289
|
+
/** The package's style definitions part, if it has one. */
|
|
1290
|
+
declare function stylesPartOf(pkg: OoxmlPackage): OoxmlPart | undefined;
|
|
1291
|
+
/**
|
|
1292
|
+
* Derive every index from one canonical package revision.
|
|
1293
|
+
*
|
|
1294
|
+
* Pure: the same package and revision always produce the same index, which is what makes
|
|
1295
|
+
* "rebuild rather than mutate" a safe invalidation strategy.
|
|
1296
|
+
*/
|
|
1297
|
+
declare function deriveOoxmlIndexes(pkg: OoxmlPackage, revision: number): OoxmlIndexes;
|
|
1298
|
+
|
|
1299
|
+
/** One paragraph's meaning, independent of how it was spelled in XML. */
|
|
1300
|
+
interface ParagraphDigest {
|
|
1301
|
+
/** Ordinal identity within its story. Node ids are NOT used: a reopened package
|
|
1302
|
+
* legitimately re-derives them, and requiring them to match would test the id scheme
|
|
1303
|
+
* rather than the content. */
|
|
1304
|
+
readonly ordinal: number;
|
|
1305
|
+
/**
|
|
1306
|
+
* Where the paragraph SITS: the element path from the story root, so a paragraph that
|
|
1307
|
+
* moves out of the table cell it was written in is a difference rather than a paragraph
|
|
1308
|
+
* with the same ordinal and the same text.
|
|
1309
|
+
*/
|
|
1310
|
+
readonly path: string;
|
|
1311
|
+
/**
|
|
1312
|
+
* `w14:paraId`, uppercase-normalized (matching is case-insensitive), or null. This is
|
|
1313
|
+
* MANAGED identity — the agent contract anchors on it and comment threading references
|
|
1314
|
+
* it — so a serializer silently dropping it must be a digest difference. Other paragraph
|
|
1315
|
+
* attributes (`w:rsidR` …) stay deliberately undigested: they are revision noise.
|
|
1316
|
+
*/
|
|
1317
|
+
readonly paraId: string | null;
|
|
1318
|
+
/** Text content, including tabs and hard breaks as their characters. */
|
|
1319
|
+
readonly text: string;
|
|
1320
|
+
/** Accepted paragraph properties, as sorted tokens including nested children. */
|
|
1321
|
+
readonly paragraphProperties: readonly string[];
|
|
1322
|
+
/** Per-run accepted properties, in run order. */
|
|
1323
|
+
readonly runProperties: readonly (readonly string[])[];
|
|
1324
|
+
/** Fingerprints of every generic (unknown) subtree, in document order. */
|
|
1325
|
+
readonly genericStructure: readonly string[];
|
|
1326
|
+
}
|
|
1327
|
+
/** One story reduced to its semantic content, ignoring everything a normalized re-emit changes. */
|
|
1328
|
+
interface StoryDigest {
|
|
1329
|
+
readonly partName: string;
|
|
1330
|
+
readonly paragraphs: readonly ParagraphDigest[];
|
|
1331
|
+
/**
|
|
1332
|
+
* Every block-level element of the part OUTSIDE a paragraph, in document order, as
|
|
1333
|
+
* `path name(attributes)` — the tables, rows, cells, sections, content controls and
|
|
1334
|
+
* their property children, each at the position that says what contains it.
|
|
1335
|
+
*
|
|
1336
|
+
* A paragraph's own internals are not here (they are its `ParagraphDigest`), which keeps
|
|
1337
|
+
* this list proportional to a document's STRUCTURE rather than its text.
|
|
1338
|
+
*/
|
|
1339
|
+
readonly structure: readonly string[];
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* The whole document's semantic content — one of the D9 losslessness oracles.
|
|
1343
|
+
*
|
|
1344
|
+
* What a save/reopen round-trip is compared on. Byte identity applies only to non-XML parts;
|
|
1345
|
+
* modelled parts re-emit normalized, so equality is asserted HERE rather than on bytes.
|
|
1346
|
+
*/
|
|
1347
|
+
interface SemanticDigest {
|
|
1348
|
+
readonly stories: readonly StoryDigest[];
|
|
1349
|
+
}
|
|
1350
|
+
/** Where two digests diverge — the diagnostic when a round-trip loses something. */
|
|
1351
|
+
type DigestDifference = {
|
|
1352
|
+
readonly path: string;
|
|
1353
|
+
readonly before: string;
|
|
1354
|
+
readonly after: string;
|
|
1355
|
+
};
|
|
1356
|
+
/** Digest one part: its paragraphs, and the structure that holds them. */
|
|
1357
|
+
declare function digestPart(part: OoxmlPart): StoryDigest | null;
|
|
1358
|
+
/** Digest every part, in the given order. */
|
|
1359
|
+
declare function semanticDigest(parts: Iterable<OoxmlPart>): SemanticDigest;
|
|
1360
|
+
/**
|
|
1361
|
+
* Every way two digests differ, as readable paths.
|
|
1362
|
+
*
|
|
1363
|
+
* Returns the differences rather than a boolean because "the round trip lost something" is
|
|
1364
|
+
* useless without saying what — the failure this oracle exists to catch is a silent drop,
|
|
1365
|
+
* and a bare `false` reproduces the silence.
|
|
1366
|
+
*/
|
|
1367
|
+
declare function diffSemanticDigests(before: SemanticDigest, after: SemanticDigest): DigestDifference[];
|
|
1368
|
+
|
|
1369
|
+
/** Valid per MS-DOCX: 8 hex digits, non-zero, below 0x80000000. */
|
|
1370
|
+
declare function isValidParaId(value: string): boolean;
|
|
1371
|
+
/** The authored `w14:paraId` value of an element, verbatim, or null. */
|
|
1372
|
+
declare function paraIdOf(node: OoxmlNode): string | null;
|
|
1373
|
+
/** FNV-1a over UTF-16 code units — the deterministic mint every id derivation shares. */
|
|
1374
|
+
declare function fnv1a32(value: string): number;
|
|
1375
|
+
/**
|
|
1376
|
+
* Deterministic 8-hex mint in (0x00000000, 0x80000000), collision-free against
|
|
1377
|
+
* `used` (uppercase hex). Same seed + same used-set → same value, which is what
|
|
1378
|
+
* keeps `splitParagraphMany` byte-identical to its equivalent single splits and
|
|
1379
|
+
* a reopened save identical to the session that produced it.
|
|
1380
|
+
*/
|
|
1381
|
+
declare function mintParaId(seed: string, used: ReadonlySet<string>): string;
|
|
1382
|
+
/** Every valid `w14:paraId` in the tree, uppercased. Memoized per root object. */
|
|
1383
|
+
declare function usedParaIds(root: OoxmlElement): ReadonlySet<string>;
|
|
1384
|
+
/**
|
|
1385
|
+
* The non-empty prefix the part ROOT binds to the w14 namespace, or null.
|
|
1386
|
+
*
|
|
1387
|
+
* Minted attributes require an in-scope binding (the invariant validator reports
|
|
1388
|
+
* `invalid-qname` otherwise, and the serializer would allocate an `nsN` alias).
|
|
1389
|
+
* Only the root binding counts: a binding authored on some descendant does not
|
|
1390
|
+
* cover paragraphs elsewhere in the tree.
|
|
1391
|
+
*/
|
|
1392
|
+
declare function w14RootPrefix(root: OoxmlElement): string | null;
|
|
1393
|
+
/** 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. */
|
|
1394
|
+
declare function mintedParagraphIdentityAttributes(prefix: string, value: string): readonly OoxmlAttribute[];
|
|
1395
|
+
/**
|
|
1396
|
+
* Load-time paragraph-identity normalization for a session's main part.
|
|
1397
|
+
*
|
|
1398
|
+
* Keeps every valid, first-seen paraId verbatim; mints (deterministically, seeded
|
|
1399
|
+
* by the paragraph's structural node id) for paragraphs whose id is missing,
|
|
1400
|
+
* malformed, zero, out of range, or a duplicate. Adds the root `xmlns` binding
|
|
1401
|
+
* the minted attributes need. Returns the INPUT PART REFERENCE when there is
|
|
1402
|
+
* nothing to do — a document Word saved yesterday re-serializes byte-identical.
|
|
1403
|
+
*
|
|
1404
|
+
* Attribute-only rebuilds move no child, so every node keeps its structural-path
|
|
1405
|
+
* id; the copy respreads only the ancestors of changed paragraphs.
|
|
1406
|
+
*
|
|
1407
|
+
* Fail-open: if the rebuilt part does not validate (a bug, or a pathology the
|
|
1408
|
+
* prefix choice could not defuse), the original part is returned — a document
|
|
1409
|
+
* must never become unopenable over an identity enhancement.
|
|
1410
|
+
*/
|
|
1411
|
+
declare function normalizeParagraphIdentity(part: OoxmlPart): OoxmlPart;
|
|
1412
|
+
|
|
1413
|
+
/** The package with a hyperlink relationship guaranteed present, and that relationship's id. */
|
|
1414
|
+
interface EnsuredHyperlinkRelationship {
|
|
1415
|
+
readonly pkg: OoxmlPackage;
|
|
1416
|
+
readonly relationshipId: string;
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* The target this engine would write for `url`, or null when it would write none.
|
|
1420
|
+
*
|
|
1421
|
+
* The VALIDATION half of {@link ensureHyperlinkRelationship}, exported so a caller that must decide
|
|
1422
|
+
* "would this be authored?" before it is allowed to change the package can ask without changing it.
|
|
1423
|
+
* A relationship outlives a refusal — it lives beside the trees, outside the undo stack — so a
|
|
1424
|
+
* caller planning a batch that may yet be refused has to ask this and mint later.
|
|
1425
|
+
*
|
|
1426
|
+
* Same rules, one implementation: `sanitizeHref`'s allowlist, a bound on the length, XML-writable
|
|
1427
|
+
* text, and the absolute-URI gate the READ side applies. There is no legitimate reason for this
|
|
1428
|
+
* engine to author a scheme it would refuse to open.
|
|
1429
|
+
*/
|
|
1430
|
+
declare function authorableHyperlinkTarget(url: string): string | null;
|
|
1431
|
+
/**
|
|
1432
|
+
* The external hyperlink relationship for `url` on `ownerPart` (default: main document),
|
|
1433
|
+
* reusing an existing one with the same target, or `null` when the URL is not something to
|
|
1434
|
+
* write.
|
|
1435
|
+
*
|
|
1436
|
+
* REUSE IS BY EXACT TARGET, matching Word: linking twice to the same address produces one
|
|
1437
|
+
* relationship. It is safe because a hyperlink relationship carries nothing but its target —
|
|
1438
|
+
* two links sharing one are indistinguishable from two links with identical targets, and
|
|
1439
|
+
* retargeting one always mints rather than rewriting (see the edit op).
|
|
1440
|
+
*
|
|
1441
|
+
* Ownership follows the story that holds the `w:hyperlink`: a header/footer or notes part
|
|
1442
|
+
* mints into that part's `.rels`, never into `document.xml.rels`. Passing an owner the
|
|
1443
|
+
* package does not declare fails closed (`null`) so a scoped insert cannot leave a stray
|
|
1444
|
+
* body relationship behind.
|
|
1445
|
+
*
|
|
1446
|
+
* The URL is refused unless `sanitizeHref` admits it. Storing a `javascript:` target that a
|
|
1447
|
+
* FILE authored is required — round-tripping never rewrites a document — but AUTHORING one
|
|
1448
|
+
* here is not: there is no legitimate reason for this engine to write a scheme it would then
|
|
1449
|
+
* refuse to open, and writing it would hand the next reader a live target this reader made.
|
|
1450
|
+
*/
|
|
1451
|
+
declare function ensureHyperlinkRelationship(pkg: OoxmlPackage, url: string, ownerPart?: string): EnsuredHyperlinkRelationship | null;
|
|
1452
|
+
/**
|
|
1453
|
+
* What a part's relationships answer for one `r:id`, over both maps.
|
|
1454
|
+
*
|
|
1455
|
+
* `relationships` holds the internal records and `externalTargets` the external ones, so a
|
|
1456
|
+
* resolver reading only the first sees every hyperlink as dangling.
|
|
1457
|
+
*/
|
|
1458
|
+
declare function relationshipTargetIn(pkg: OoxmlPackage, ownerPart: string, relationshipId: string): {
|
|
1459
|
+
readonly target: string;
|
|
1460
|
+
readonly external: boolean;
|
|
1461
|
+
readonly sinkSafe?: boolean;
|
|
1462
|
+
} | null;
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* Delete a comment thread outright: body, thread state and story markers.
|
|
1466
|
+
*
|
|
1467
|
+
* Returns the package unchanged when the id names no comment, and null when a removal was
|
|
1468
|
+
* refused — a caller inside a transaction rolls back rather than committing a package whose
|
|
1469
|
+
* comment is half gone.
|
|
1470
|
+
*/
|
|
1471
|
+
declare function deleteCommentThread(pkg: OoxmlPackage, commentId: string): OoxmlPackage | null;
|
|
1472
|
+
/**
|
|
1473
|
+
* Delete every comment the edit between `before` and `after` emptied.
|
|
1474
|
+
*
|
|
1475
|
+
* The shape `cascadeDeletedNoteReferences` established: a before/after diff rather than a rule
|
|
1476
|
+
* inside each op, because the ops that can empty a range are several (`deleteText`,
|
|
1477
|
+
* `deleteBlock`, a row deletion, accepting a tracked deletion) and a rule written into each one
|
|
1478
|
+
* drifts.
|
|
1479
|
+
*
|
|
1480
|
+
* THE TEST, exactly. A comment dies when the words it covered are gone and nothing still
|
|
1481
|
+
* places it: either every marker naming it went with them, or its markers are all still there
|
|
1482
|
+
* with no characters left between them. What it deliberately does NOT do is read "no usable
|
|
1483
|
+
* range" as "emptied" — a comment whose `w:commentRangeStart` was carried off by a deleted
|
|
1484
|
+
* block still has its reference and its end, and Word keeps it, because the reference is the
|
|
1485
|
+
* element that anchors a comment. Reaping there deleted a remark whose text was still on
|
|
1486
|
+
* screen, which is the one mistake worse than leaving a stale card.
|
|
1487
|
+
*
|
|
1488
|
+
* SKIPS rather than refuses when the package is too large to scan. Returning null there made
|
|
1489
|
+
* `transact` roll the edit back, so on a package past the part cap every keystroke in a
|
|
1490
|
+
* commented paragraph was silently refused and the document read as frozen. A skipped reap
|
|
1491
|
+
* leaves a stale card — recoverable, visible, and the behaviour before any of this existed.
|
|
1492
|
+
* Null is reserved for a removal that actually failed, which the caller must roll back.
|
|
1493
|
+
*/
|
|
1494
|
+
declare function cascadeEmptiedComments(before: OoxmlPackage, after: OoxmlPackage): OoxmlPackage | null;
|
|
1495
|
+
/** Whether the package holds any comment record at all — the cheap gate before a reap. */
|
|
1496
|
+
declare function hasAnyComment(pkg: OoxmlPackage): boolean;
|
|
1497
|
+
|
|
1498
|
+
/** How deep block-level content controls may nest before the walk stops descending. */
|
|
1499
|
+
declare const MAX_STORY_SDT_NESTING = 32;
|
|
1500
|
+
/** Which kind of story a root is. Layout walks all four the same way. */
|
|
1501
|
+
type OoxmlStoryKind = 'body' | 'header' | 'footer' | 'note';
|
|
1502
|
+
/** One story root and the blocks under it, with block-level SDTs already flattened. */
|
|
1503
|
+
interface OoxmlStoryRoot {
|
|
1504
|
+
readonly kind: OoxmlStoryKind;
|
|
1505
|
+
/** The `w:body` / `w:hdr` / `w:ftr` / `w:footnote` element that holds the blocks. */
|
|
1506
|
+
readonly root: OoxmlNode;
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Every story root in a part, in document order.
|
|
1510
|
+
*
|
|
1511
|
+
* Does not descend INTO a story: a story's blocks are the walk below, and a story root never
|
|
1512
|
+
* contains another story root.
|
|
1513
|
+
*/
|
|
1514
|
+
declare function storyRootsOf(part: OoxmlPart): readonly OoxmlStoryRoot[];
|
|
1515
|
+
/** The main body story of a part, or null when the part holds none. */
|
|
1516
|
+
declare function bodyStoryRoot(part: OoxmlPart): OoxmlNode | null;
|
|
1517
|
+
/**
|
|
1518
|
+
* Every paragraph of one story, in reading order.
|
|
1519
|
+
*
|
|
1520
|
+
* Descends through tables (rows, cells, nested tables) and flattens block-level content
|
|
1521
|
+
* controls. The returned nodes are paragraph elements; a caller addresses them by `id`.
|
|
1522
|
+
*/
|
|
1523
|
+
declare function storyParagraphs(root: OoxmlNode): readonly OoxmlNode[];
|
|
1524
|
+
/**
|
|
1525
|
+
* The block walk itself, appending into `out`.
|
|
1526
|
+
*
|
|
1527
|
+
* Exported so `allParagraphs` in the binding lane is the same traversal rather than a second
|
|
1528
|
+
* copy of it.
|
|
1529
|
+
*/
|
|
1530
|
+
declare function collectStoryParagraphs(children: readonly OoxmlNode[], out: OoxmlNode[], sdtDepth: number): void;
|
|
1531
|
+
|
|
1532
|
+
/** One addressable unit of paragraph text: text, tab, hard break, or atomic field. */
|
|
1533
|
+
interface Segment {
|
|
1534
|
+
readonly runId: string;
|
|
1535
|
+
readonly node: OoxmlNode;
|
|
1536
|
+
readonly start: number;
|
|
1537
|
+
readonly end: number;
|
|
1538
|
+
/**
|
|
1539
|
+
* When set, deleting this segment removes every listed node id in one step (atomic
|
|
1540
|
+
* field begin→end or `fldSimple`). Absent for ordinary text/tab/break segments.
|
|
1541
|
+
*/
|
|
1542
|
+
readonly removeNodeIds?: readonly string[];
|
|
1543
|
+
/**
|
|
1544
|
+
* When set, run formatting for this atom targets these runs (field result ownership),
|
|
1545
|
+
* not necessarily `runId`. Absent for ordinary text/tab/break segments.
|
|
1546
|
+
*/
|
|
1547
|
+
readonly formatRunIds?: readonly string[];
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Flatten a paragraph into UTF-16 addressable segments, in document order.
|
|
1551
|
+
*
|
|
1552
|
+
* A HYPERLINK's runs are addressed too. `w:hyperlink` is a run container, not a leaf, and
|
|
1553
|
+
* the characters inside a link are ordinary paragraph text: the user selects them, types
|
|
1554
|
+
* over them and deletes them like any other. Skipping the container — which is what
|
|
1555
|
+
* iterating only direct `w:r` children did — left every link's text with no offsets at all,
|
|
1556
|
+
* so `paragraphTextOf` read "Visit or ." for a sentence that says "Visit Example.com or
|
|
1557
|
+
* Anthropic's website." and layout, selection and the ops all agreed on the wrong string.
|
|
1558
|
+
*
|
|
1559
|
+
* Inline CONTENT CONTROLS are the same class of wrapper: their `w:sdtContent` runs join the
|
|
1560
|
+
* paragraph's offset stream with no break opportunity at the boundary. Nesting is bounded
|
|
1561
|
+
* (`MAX_CONTENT_CONTROL_NESTING`); beyond the bound the wrapper is opaque so recursion
|
|
1562
|
+
* cannot exhaust the stack.
|
|
1563
|
+
*
|
|
1564
|
+
* `runId` stays the id of the run the content actually lives in, at whatever depth: the
|
|
1565
|
+
* appliers resolve it with `findNode` and rebuild that run's children, so nesting costs them
|
|
1566
|
+
* nothing.
|
|
1567
|
+
*/
|
|
1568
|
+
declare function segmentsOf(paragraph: OoxmlParagraphNode): Segment[];
|
|
1569
|
+
/** Half-open `[start, end)` of one node in its paragraph's model offset space. */
|
|
1570
|
+
interface OffsetSpan {
|
|
1571
|
+
readonly start: number;
|
|
1572
|
+
readonly end: number;
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Every node's place in the paragraph's model offset space, from the SAME walk `segmentsOf`
|
|
1576
|
+
* uses.
|
|
1577
|
+
*
|
|
1578
|
+
* The offset model has exactly one authority, and this is how a caller borrows it. Three
|
|
1579
|
+
* private walkers used to re-derive it — one in the tracked-change writer, one in the comment
|
|
1580
|
+
* anchor reader, one in the review queue — and all three disagreed with `segmentsOf` and with
|
|
1581
|
+
* each other: none gave a note reference or an atomic field its length of one, one counted a
|
|
1582
|
+
* field's instruction text as visible characters, and one never descended into `w:hyperlink`
|
|
1583
|
+
* at all. The consequences were an anchor short by a link's length, two unrelated comments
|
|
1584
|
+
* threaded onto one zero-width offset, and a tracked insert landing a character out in any
|
|
1585
|
+
* paragraph carrying a footnote. Patching each walker only resets the clock on the next drift.
|
|
1586
|
+
*
|
|
1587
|
+
* A node the walk never reaches — content under a `generic` container, or past the nesting cap
|
|
1588
|
+
* — has NO span, and {@link ParagraphOffsetIndex.lengthOf} reports zero for it. That is the
|
|
1589
|
+
* same answer `segmentsOf` gives: it contributes no addressable characters.
|
|
1590
|
+
*/
|
|
1591
|
+
interface ParagraphOffsetIndex {
|
|
1592
|
+
/** The paragraph's own length, identical to {@link paragraphLength}. */
|
|
1593
|
+
readonly length: number;
|
|
1594
|
+
readonly segments: readonly Segment[];
|
|
1595
|
+
/** Where a node sits, or null when the offset walk never reached it. */
|
|
1596
|
+
spanOf(node: OoxmlNode | string): OffsetSpan | null;
|
|
1597
|
+
/** A node's model length: `end - start` of its span, and 0 when it has none. */
|
|
1598
|
+
lengthOf(node: OoxmlNode | string): number;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* THE paragraph offset authority: maps a paragraph's UTF-16 offsets to the nodes holding them.
|
|
1602
|
+
*
|
|
1603
|
+
* One authority on purpose. An atomic field spans many nodes but is ONE unit to an offset, and a
|
|
1604
|
+
* second implementation that disagreed would place edits inside content that cannot be split.
|
|
1605
|
+
*/
|
|
1606
|
+
declare function paragraphOffsetIndex(paragraph: OoxmlParagraphNode): ParagraphOffsetIndex;
|
|
1607
|
+
/** One inline content control's identity and the UTF-16 span its content covers. */
|
|
1608
|
+
interface InlineControlSpan {
|
|
1609
|
+
readonly controlId: string;
|
|
1610
|
+
readonly start: number;
|
|
1611
|
+
readonly end: number;
|
|
1612
|
+
}
|
|
1613
|
+
/**
|
|
1614
|
+
* The innermost inline content control whose content ends exactly at `offset` — the caret
|
|
1615
|
+
* at its right outer edge. What Backspace consults to delete the node as ONE unit
|
|
1616
|
+
* (pro-review-and-custom-nodes 4.6): deleting its last character from outside would either
|
|
1617
|
+
* strip one letter from a content-locked label (refused, so the key looks dead) or leave a
|
|
1618
|
+
* half-deleted chip whose tag still claims the full payload.
|
|
1619
|
+
*/
|
|
1620
|
+
declare function inlineControlEndingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
|
|
1621
|
+
/** The forward-delete mirror: the control whose content STARTS exactly at `offset`. */
|
|
1622
|
+
declare function inlineControlStartingAt(paragraph: OoxmlParagraphNode, offset: number): InlineControlSpan | null;
|
|
1623
|
+
|
|
1624
|
+
/** Structural validation, run before any tree work so a rejection changes nothing. */
|
|
1625
|
+
declare function validateTreeOp(part: OoxmlPart, op: TreeDocOp): TreeOpRejection | null;
|
|
1626
|
+
|
|
1627
|
+
/**
|
|
1628
|
+
* Apply one validated op to a part.
|
|
1629
|
+
*
|
|
1630
|
+
* Validation runs first and returns before any tree work, so a rejected op is a true no-op:
|
|
1631
|
+
* the caller keeps the part it passed in, unchanged and still frozen.
|
|
1632
|
+
*
|
|
1633
|
+
* `options.deferValidation` passes through to the edit primitives: a transaction applying
|
|
1634
|
+
* many ops re-validates the whole part once at its commit boundary rather than after every
|
|
1635
|
+
* primitive, which is the difference between a paste that is linear and one that is
|
|
1636
|
+
* quadratic in document size.
|
|
1637
|
+
*/
|
|
1638
|
+
declare function applyTreeOp(part: OoxmlPart, op: TreeDocOp, options?: EditOptions): TreeOpResult;
|
|
1639
|
+
/** Paragraph text as the ops address it, for tests and callers computing offsets. */
|
|
1640
|
+
declare function paragraphTextOf(part: OoxmlPart, paragraphId: string): string | null;
|
|
1641
|
+
|
|
1642
|
+
/** Where a comment is anchored, in the model offset space of one story. */
|
|
1643
|
+
interface CommentAnchorRequest {
|
|
1644
|
+
readonly paragraphId: string;
|
|
1645
|
+
readonly start: number;
|
|
1646
|
+
/** May sit in the same paragraph or a later one; `endParagraphId` names it when it differs. */
|
|
1647
|
+
readonly end: number;
|
|
1648
|
+
readonly endParagraphId?: string;
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* What adding a comment needs: where it anchors, who wrote it, and its body.
|
|
1652
|
+
*
|
|
1653
|
+
* `author` is required because `CT_Comment` makes `@w:author` mandatory — a comment without one
|
|
1654
|
+
* writes invalid XML, so the write is refused rather than filled with an empty attribute.
|
|
1655
|
+
*/
|
|
1656
|
+
interface AddCommentRequest {
|
|
1657
|
+
readonly anchor: CommentAnchorRequest;
|
|
1658
|
+
/** Required by `CT_TrackChange`. A comment without one writes invalid XML. */
|
|
1659
|
+
readonly author: string;
|
|
1660
|
+
readonly initials?: string;
|
|
1661
|
+
/** ISO-8601. Absent writes no `@w:date`, because inventing one is a content change. */
|
|
1662
|
+
readonly date?: string;
|
|
1663
|
+
readonly text: string;
|
|
1664
|
+
/** The comment this replies to. Its thread link is written to `commentsExtended.xml`. */
|
|
1665
|
+
readonly replyToCommentId?: string;
|
|
1666
|
+
}
|
|
1667
|
+
/** The new comment's id and the story change, or the reason the write was refused. */
|
|
1668
|
+
type AddCommentResult = {
|
|
1669
|
+
readonly ok: true;
|
|
1670
|
+
readonly commentId: string;
|
|
1671
|
+
/**
|
|
1672
|
+
* The story transaction's own change, so the coordinator can publish it.
|
|
1673
|
+
*
|
|
1674
|
+
* A comment write commits straight on the story store rather than through
|
|
1675
|
+
* `applyTreeOps`, and the change is what carries the dirty anchor paragraphs and the
|
|
1676
|
+
* `text-local` impact. Dropping it here left the caller with nothing precise to
|
|
1677
|
+
* publish and no way to tell a committed write from an identity no-op.
|
|
1678
|
+
*/
|
|
1679
|
+
readonly change: TreeModelChange | null;
|
|
1680
|
+
} | {
|
|
1681
|
+
readonly ok: false;
|
|
1682
|
+
readonly reason: TreeOpRejection | 'invalid-author';
|
|
1683
|
+
};
|
|
1684
|
+
/**
|
|
1685
|
+
* Add a comment, or a reply, in ONE transaction.
|
|
1686
|
+
*
|
|
1687
|
+
* The comment id and the `w14:paraId` are computed before the transaction opens, because the
|
|
1688
|
+
* story markers have to carry the same id the body does and deriving each separately is how the
|
|
1689
|
+
* two come to disagree.
|
|
1690
|
+
*
|
|
1691
|
+
* `w14:paraId` is minted here and only here: on a comment WRITE. Allocating on load would
|
|
1692
|
+
* rewrite a document nobody edited and break fingerprint equality on an untouched round trip.
|
|
1693
|
+
*/
|
|
1694
|
+
declare function addComment(store: TreeDocumentStore, request: AddCommentRequest): AddCommentResult;
|
|
1695
|
+
/** Whether resolving a thread applied. Marks the comment AND every reply to it, as Word does. */
|
|
1696
|
+
type SetCommentResolvedResult = {
|
|
1697
|
+
readonly ok: true;
|
|
1698
|
+
readonly changed: boolean;
|
|
1699
|
+
/** The story transaction's change, for the coordinator to publish — see {@link AddCommentResult}. */
|
|
1700
|
+
readonly change: TreeModelChange | null;
|
|
1701
|
+
} | {
|
|
1702
|
+
readonly ok: false;
|
|
1703
|
+
readonly reason: TreeOpRejection | 'unknown-comment';
|
|
1704
|
+
};
|
|
1705
|
+
/**
|
|
1706
|
+
* Mark a comment thread resolved, or reopen it.
|
|
1707
|
+
*
|
|
1708
|
+
* A THREAD, not one remark: Word resolves a conversation, and its own pane greys the replies with
|
|
1709
|
+
* the comment they answer. Resolving only the parent would leave a file whose reply still reads as
|
|
1710
|
+
* open under a closed remark — a state Word does not produce and no reader would draw sensibly.
|
|
1711
|
+
*
|
|
1712
|
+
* `@w15:done` lives in `commentsExtended.xml`, which many documents do not have: a file with no
|
|
1713
|
+
* reply has no thread state to record. So the part is created when it is missing, exactly as
|
|
1714
|
+
* {@link addComment} creates it, and every comment being resolved gets an entry — a comment with
|
|
1715
|
+
* no `w14:paraId` gets one minted, because the state is keyed by it and there is nothing else to
|
|
1716
|
+
* key it by.
|
|
1717
|
+
*
|
|
1718
|
+
* ONE package transaction: the part, its relationship, its content-type override and the entries
|
|
1719
|
+
* commit together, so a resolved thread is never half-recorded.
|
|
1720
|
+
*/
|
|
1721
|
+
declare function setCommentResolved(store: TreeDocumentStore, commentId: string, resolved: boolean): SetCommentResolvedResult;
|
|
1722
|
+
/** A comment part exists and declares the comment content type. */
|
|
1723
|
+
declare function hasCommentPart(pkg: OoxmlPackage, storyPartName: string): boolean;
|
|
1724
|
+
/** Exposed so a surface can tell "no comment part yet" from "no comments". */
|
|
1725
|
+
declare function commentPartNameOf(pkg: OoxmlPackage, storyPartName: string): string;
|
|
1726
|
+
/**
|
|
1727
|
+
* The `commentsExtended.xml` a story points at.
|
|
1728
|
+
*
|
|
1729
|
+
* Exported for the same reason as {@link commentPartNameOf}: the READER has to resolve the
|
|
1730
|
+
* same name the writer does. Hardcoding `/word/comments.xml` on one side and following the
|
|
1731
|
+
* relationship on the other is a split that shows up as a comment written and never read back.
|
|
1732
|
+
*/
|
|
1733
|
+
declare function commentsExtendedPartNameOf(pkg: OoxmlPackage, storyPartName: string): string;
|
|
1734
|
+
|
|
1735
|
+
/** The D8 paragraph op vocabulary. */
|
|
1736
|
+
declare const AUTHORABLE_PARAGRAPH_PROPERTIES: ReadonlySet<string>;
|
|
1737
|
+
/** The D8 run op vocabulary, for `w:rPr` on a run and on the paragraph mark alike. */
|
|
1738
|
+
declare const AUTHORABLE_RUN_PROPERTIES: ReadonlySet<string>;
|
|
1739
|
+
/**
|
|
1740
|
+
* Whether an op may name this run property at all.
|
|
1741
|
+
*
|
|
1742
|
+
* The stored-marks lane needs this AT ARM TIME. Every other write reaches the store in the same
|
|
1743
|
+
* turn as the press, so a name the store refuses surfaces immediately; an ARMED property is not
|
|
1744
|
+
* applied until the user types, and it rides the keystroke's own transaction — a name outside the
|
|
1745
|
+
* vocabulary would take the typed characters down with it, silently, on every keystroke until the
|
|
1746
|
+
* caret moved.
|
|
1747
|
+
*/
|
|
1748
|
+
declare function isAuthorableRunProperty(localName: string): boolean;
|
|
1749
|
+
/**
|
|
1750
|
+
* A node's own property container (`w:pPr`, `w:rPr`) among its children.
|
|
1751
|
+
*
|
|
1752
|
+
* A container the canonical read demoted to generic is still the node's own properties —
|
|
1753
|
+
* matching only the typed kind lost the whole set.
|
|
1754
|
+
*/
|
|
1755
|
+
declare function propertyContainer(parent: OoxmlNode | null | undefined, kind: 'paragraphProperties' | 'runProperties', localName: 'pPr' | 'rPr'): OoxmlNode | undefined;
|
|
1756
|
+
/** What a container itself authors, narrowed to the names an op is allowed to carry. */
|
|
1757
|
+
declare function authoredProperties(container: OoxmlNode | undefined, authorable: ReadonlySet<string>): readonly OoxmlProperty[];
|
|
1758
|
+
/**
|
|
1759
|
+
* What a paragraph itself authors: its own `w:pPr`, narrowed to what an op can express.
|
|
1760
|
+
*
|
|
1761
|
+
* Properties outside the vocabulary are dropped from the OP, not from the paragraph: the applier
|
|
1762
|
+
* keeps every `w:pPr` child an op cannot name (the mark, `w:sectPr`, `w:pBdr`, `w:outlineLvl`)
|
|
1763
|
+
* exactly as authored.
|
|
1764
|
+
*/
|
|
1765
|
+
declare function directParagraphProperties(part: OoxmlPart, paragraphId: string): readonly OoxmlProperty[];
|
|
1766
|
+
/**
|
|
1767
|
+
* What a paragraph MARK itself authors: `w:pPr/w:rPr`, narrowed to the run vocabulary.
|
|
1768
|
+
*
|
|
1769
|
+
* Same rule as a run's own `w:rPr`, for the same reason — the mark is a run property container,
|
|
1770
|
+
* and `setParagraphMarkProperties` rewrites the names its op carries.
|
|
1771
|
+
*/
|
|
1772
|
+
declare function directParagraphMarkProperties(part: OoxmlPart, paragraphId: string): readonly OoxmlProperty[];
|
|
1773
|
+
/**
|
|
1774
|
+
* Merge properties into a set, replacing any entry with the same name.
|
|
1775
|
+
*
|
|
1776
|
+
* `setRunProperties` and `setParagraphProperties` REPLACE the whole container, so sending one
|
|
1777
|
+
* property alone deleted every other: pressing Bold stripped a run's font, size and colour, and
|
|
1778
|
+
* pressing Centre stripped a paragraph's style, numbering and indents.
|
|
1779
|
+
*
|
|
1780
|
+
* Takes one property or a list, because a toolbar press carries one and an object-model
|
|
1781
|
+
* formatting write carries several at once — and applying several one at a time would be the
|
|
1782
|
+
* same fold written at every call site.
|
|
1783
|
+
*/
|
|
1784
|
+
declare function mergedProperties(existing: readonly OoxmlProperty[], incoming: OoxmlProperty | readonly OoxmlProperty[]): OoxmlProperty[];
|
|
1785
|
+
/** Per-run UTF-16 ranges from `segmentsOf` (fields/notes collapse to one unit on begin). */
|
|
1786
|
+
declare function runAddressRanges(paragraph: Extract<OoxmlNode, {
|
|
1787
|
+
kind: 'paragraph';
|
|
1788
|
+
}>): Map<string, {
|
|
1789
|
+
start: number;
|
|
1790
|
+
end: number;
|
|
1791
|
+
}>;
|
|
1792
|
+
/** Runs that own field-result formatting for atoms in this paragraph. */
|
|
1793
|
+
declare function formatOwnedRunIds(paragraph: Extract<OoxmlNode, {
|
|
1794
|
+
kind: 'paragraph';
|
|
1795
|
+
}>): ReadonlySet<string>;
|
|
1796
|
+
/** One run's share of a range edit: the slice it covers and the properties to write there. */
|
|
1797
|
+
interface RunPropertyEdit {
|
|
1798
|
+
readonly start: number;
|
|
1799
|
+
readonly end: number;
|
|
1800
|
+
readonly properties: readonly OoxmlProperty[];
|
|
1801
|
+
/**
|
|
1802
|
+
* When set, `setRunProperties` formats only these runs (field result ownership). Needed when
|
|
1803
|
+
* several result runs share one atom offset so each keeps its own merged bag.
|
|
1804
|
+
*/
|
|
1805
|
+
readonly targetRunIds?: readonly string[];
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* A range run-property change, split into ONE edit per run it covers, each merged over that
|
|
1809
|
+
* run's own `w:rPr`.
|
|
1810
|
+
*
|
|
1811
|
+
* Neither half of that is optional. The base MUST be the run's own properties (see this file's
|
|
1812
|
+
* header). And the split MUST be per run: the op REPLACES the properties it names across its
|
|
1813
|
+
* whole range, so one op carrying one run's bag over a mixed selection homogenised it — bolding
|
|
1814
|
+
* `hello ` + `Georgia` rewrote the second run's `w:rFonts` with the first's. Runs are addressed by
|
|
1815
|
+
* offset rather than by id because these edits apply in sequence and the applier splits runs at
|
|
1816
|
+
* the range edges; offsets are unmoved by a property write, ids are not.
|
|
1817
|
+
*/
|
|
1818
|
+
declare function runPropertyEdits(part: OoxmlPart, paragraphId: string, start: number, end: number, incoming: OoxmlProperty | readonly OoxmlProperty[]): readonly RunPropertyEdit[];
|
|
1819
|
+
/**
|
|
1820
|
+
* Every run that contributes at least one character of `[start, end)`, in document order.
|
|
1821
|
+
*
|
|
1822
|
+
* A COLLAPSED range answers the run it sits inside, which is what a caret reads. Callers that
|
|
1823
|
+
* want the empty answer for a collapsed range check the offsets themselves.
|
|
1824
|
+
*/
|
|
1825
|
+
declare function runsCovering(part: OoxmlPart, paragraphId: string, start: number, end: number): readonly OoxmlNode[];
|
|
1826
|
+
|
|
1827
|
+
interface RevisionSite {
|
|
1828
|
+
readonly node: OoxmlElement;
|
|
1829
|
+
readonly parent: OoxmlElement | null;
|
|
1830
|
+
/** True when this site cannot be resolved and the whole op must refuse. */
|
|
1831
|
+
readonly refused: boolean;
|
|
1832
|
+
/** True for `w:pPr/w:rPr/w:ins|w:del` — the paragraph MARK, not content. */
|
|
1833
|
+
readonly paragraphMark: boolean;
|
|
1834
|
+
/** True for `w:rPrChange` / `w:pPrChange`. */
|
|
1835
|
+
readonly propertyChange: boolean;
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Every revision-bearing element in the part, with the classification that decides whether it
|
|
1839
|
+
* can be resolved.
|
|
1840
|
+
*
|
|
1841
|
+
* One walk, so accept-all does not pay a traversal per revision — and paragraphs the last
|
|
1842
|
+
* commit did not touch are answered from {@link paragraphSitesCache} rather than re-walked.
|
|
1843
|
+
*/
|
|
1844
|
+
declare function collectRevisionSites(part: OoxmlPart): RevisionSite[];
|
|
1845
|
+
|
|
1846
|
+
/** The `w15` namespace: `commentsExtended.xml` — thread parent and resolved state. */
|
|
1847
|
+
declare const W15_NAMESPACE_URI = "http://schemas.microsoft.com/office/word/2012/wordml";
|
|
1848
|
+
/** A position in one story: a paragraph node id plus a UTF-16 offset inside it. */
|
|
1849
|
+
interface CommentPosition {
|
|
1850
|
+
readonly paragraphId: string;
|
|
1851
|
+
readonly offset: number;
|
|
1852
|
+
}
|
|
1853
|
+
/**
|
|
1854
|
+
* Where a comment is anchored, as a range.
|
|
1855
|
+
*
|
|
1856
|
+
* `orphaned` records that the file did not give this comment a usable range — a reference with
|
|
1857
|
+
* no range markers, or a start with no end. The comment is still listed, marked orphaned,
|
|
1858
|
+
* rather than dropped: a reviewer's remark disappearing silently is worse than one that says
|
|
1859
|
+
* it lost its text.
|
|
1860
|
+
*/
|
|
1861
|
+
interface CommentAnchor {
|
|
1862
|
+
readonly commentId: string;
|
|
1863
|
+
/** Canonical name of the part the range lives in, so a header comment is attributable. */
|
|
1864
|
+
readonly partName: string;
|
|
1865
|
+
readonly start: CommentPosition;
|
|
1866
|
+
readonly end: CommentPosition;
|
|
1867
|
+
readonly orphaned: boolean;
|
|
1868
|
+
}
|
|
1869
|
+
/** One comment as authored in `word/comments.xml`. */
|
|
1870
|
+
interface CommentRecord {
|
|
1871
|
+
readonly id: string;
|
|
1872
|
+
readonly author: string;
|
|
1873
|
+
readonly initials?: string;
|
|
1874
|
+
readonly date?: string;
|
|
1875
|
+
/** Body paragraphs, as tree nodes, so the surface renders measured text rather than a string. */
|
|
1876
|
+
readonly blocks: readonly OoxmlElement[];
|
|
1877
|
+
/** `w14:paraId` of the first body paragraph — the key thread state is stored under. */
|
|
1878
|
+
readonly paraId?: string;
|
|
1879
|
+
/** `@w16cid:parentId` — the `w:id` of the comment this replies to, when the file names it. */
|
|
1880
|
+
readonly parentCommentId?: string;
|
|
1881
|
+
}
|
|
1882
|
+
/** Thread state for one comment, read from `commentsExtended.xml`. */
|
|
1883
|
+
interface CommentThreadState {
|
|
1884
|
+
/** `@w15:paraIdParent` — the comment this one replies to, absent for a top-level comment. */
|
|
1885
|
+
readonly parentParaId?: string;
|
|
1886
|
+
readonly done: boolean;
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Every comment anchor in one story, in document order.
|
|
1890
|
+
*
|
|
1891
|
+
* Overlapping and nested ranges are supported because each anchor is resolved independently —
|
|
1892
|
+
* Word produces both, and a model that assumed ranges nest cleanly would mis-anchor them.
|
|
1893
|
+
*
|
|
1894
|
+
* A start with no matching end anchors to the end of its own paragraph and is reported orphaned
|
|
1895
|
+
* rather than guessed at: extending it to the next end marker would attach a reviewer's remark
|
|
1896
|
+
* to text they never saw.
|
|
1897
|
+
*/
|
|
1898
|
+
declare function commentAnchorsOfStory(part: OoxmlPart): CommentAnchor[];
|
|
1899
|
+
/**
|
|
1900
|
+
* The comments in `word/comments.xml`, in authored order.
|
|
1901
|
+
*
|
|
1902
|
+
* Every value here comes from a file an attacker fully controls, so nothing is interpreted:
|
|
1903
|
+
* author, initials and date are carried verbatim for a surface that will set them as TEXT.
|
|
1904
|
+
*/
|
|
1905
|
+
declare function commentsOfPart(part: OoxmlPart): CommentRecord[];
|
|
1906
|
+
/**
|
|
1907
|
+
* Thread state by `w14:paraId`, from `commentsExtended.xml`.
|
|
1908
|
+
*
|
|
1909
|
+
* The part being PRESENT is not evidence of threading. `issue-68-large-comments-suggestions.docx`
|
|
1910
|
+
* ships it with 212 entries carrying `@w15:done` and not one `@w15:paraIdParent`, so it records
|
|
1911
|
+
* resolved state for a flat list. Absent parent means top-level, and that is a fact about the
|
|
1912
|
+
* file rather than a default this code chose.
|
|
1913
|
+
*/
|
|
1914
|
+
declare function threadStateOfPart(part: OoxmlPart): Map<string, CommentThreadState>;
|
|
1915
|
+
|
|
1916
|
+
/** A position in the model offset space of one story. */
|
|
1917
|
+
interface ReviewPosition {
|
|
1918
|
+
readonly paragraphId: string;
|
|
1919
|
+
readonly offset: number;
|
|
1920
|
+
}
|
|
1921
|
+
/** Where an item is anchored: a range in one story. */
|
|
1922
|
+
interface ReviewRange {
|
|
1923
|
+
readonly partName: string;
|
|
1924
|
+
readonly start: ReviewPosition;
|
|
1925
|
+
readonly end: ReviewPosition;
|
|
1926
|
+
}
|
|
1927
|
+
/**
|
|
1928
|
+
* What kind of decision a revision card represents.
|
|
1929
|
+
*
|
|
1930
|
+
* Wider than the four content wrappers, because a reviewer has to be shown every pending
|
|
1931
|
+
* decision, including the ones that decorate no characters. A card the surface cannot show is
|
|
1932
|
+
* a change the reviewer never learns about — and `acceptAllRevisions` refuses if ANY revision
|
|
1933
|
+
* in the document is one the engine cannot resolve, so an invisible one makes Accept All fail
|
|
1934
|
+
* for a reason nothing on screen explains.
|
|
1935
|
+
*/
|
|
1936
|
+
type ReviewRevisionKind = 'insert' | 'delete'
|
|
1937
|
+
/**
|
|
1938
|
+
* A deletion and an insertion that are one edit: text typed over a selection.
|
|
1939
|
+
*
|
|
1940
|
+
* Word shows these as a single `Replaced "x" with "y"` card, and resolving one half
|
|
1941
|
+
* without the other is never what the reviewer meant — accepting the deletion alone
|
|
1942
|
+
* leaves the replacement text unproposed, rejecting it alone leaves both.
|
|
1943
|
+
*/
|
|
1944
|
+
| 'replace' | 'moveFrom' | 'moveTo'
|
|
1945
|
+
/** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
|
|
1946
|
+
| 'format'
|
|
1947
|
+
/** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
|
|
1948
|
+
| 'paragraphMark'
|
|
1949
|
+
/** A row, cell, section or grid revision. Supported row revisions are resolvable. */
|
|
1950
|
+
| 'structural';
|
|
1951
|
+
/** One tracked change as the store derives it, keyed per decision rather than per site. */
|
|
1952
|
+
interface ReviewRevisionItem {
|
|
1953
|
+
readonly kind: 'revision';
|
|
1954
|
+
/** Stable across renders and unique per DECISION, not per site. */
|
|
1955
|
+
readonly id: string;
|
|
1956
|
+
/** The payload `acceptRevision` / `rejectRevision` take. */
|
|
1957
|
+
readonly address: RevisionAddress;
|
|
1958
|
+
/**
|
|
1959
|
+
* EVERY address this decision covers, `address` first.
|
|
1960
|
+
*
|
|
1961
|
+
* More than one only for a replacement, whose halves a foreign editor may have written
|
|
1962
|
+
* as two independent revisions. Accept and reject walk all of them in one transaction:
|
|
1963
|
+
* resolving one half and leaving the other is a state no reviewer asked for.
|
|
1964
|
+
*/
|
|
1965
|
+
readonly addresses: readonly RevisionAddress[];
|
|
1966
|
+
/** The words a replacement removes. Empty for every other kind. */
|
|
1967
|
+
readonly replacedText: string;
|
|
1968
|
+
readonly revisionKind: ReviewRevisionKind;
|
|
1969
|
+
readonly author: string;
|
|
1970
|
+
readonly date?: string;
|
|
1971
|
+
/** Text the revision covers, for the card summary. Empty for changes with no characters. */
|
|
1972
|
+
readonly text: string;
|
|
1973
|
+
/** Every site this decision touches, in document order. */
|
|
1974
|
+
readonly ranges: readonly ReviewRange[];
|
|
1975
|
+
/**
|
|
1976
|
+
* How many leading `ranges` are the STRUCK half of a replacement.
|
|
1977
|
+
*
|
|
1978
|
+
* A replacement's card is one decision but its ranges are two colours — red over what is
|
|
1979
|
+
* going, green over what takes its place. ABSENT when the halves do not split at a single
|
|
1980
|
+
* point, which is what a file recording both under one revision id can produce; a surface
|
|
1981
|
+
* then has no basis for two colours and should paint one neutral band rather than guess.
|
|
1982
|
+
*/
|
|
1983
|
+
readonly replacedRangeCount?: number;
|
|
1984
|
+
/**
|
|
1985
|
+
* True when the engine cannot resolve this kind, so accept and reject must not be offered.
|
|
1986
|
+
*
|
|
1987
|
+
* Derived HERE rather than from a caller-supplied predicate: the refusal list is internal,
|
|
1988
|
+
* and a surface asked to compute it would have to guess. A card that offers a button the
|
|
1989
|
+
* engine will refuse is worse than one that explains why it cannot.
|
|
1990
|
+
*/
|
|
1991
|
+
readonly readOnly: boolean;
|
|
1992
|
+
/** The other half of a move, or the other side of a delete/insert replacement. */
|
|
1993
|
+
readonly pairedWith?: string;
|
|
1994
|
+
/**
|
|
1995
|
+
* Comments answering this change, in document order.
|
|
1996
|
+
*
|
|
1997
|
+
* A reply to a tracked change IS a comment: `w:ins` and `w:del` carry `(@w:id, @w:author,
|
|
1998
|
+
* @w:date)` and no body, so `replyToReviewItem` writes the text as a comment over the
|
|
1999
|
+
* revision's own range. Nothing recorded the link, so the reply came back as a separate
|
|
2000
|
+
* card — the reader saw their answer detach from the change it answered. The range is the
|
|
2001
|
+
* link, and it is the same evidence `commentItemsOf` threads coincident comments on.
|
|
2002
|
+
*/
|
|
2003
|
+
readonly replyIds: readonly string[];
|
|
2004
|
+
}
|
|
2005
|
+
/** One comment as the store derives it, with its thread links resolved. */
|
|
2006
|
+
interface ReviewCommentItem {
|
|
2007
|
+
readonly kind: 'comment';
|
|
2008
|
+
readonly id: string;
|
|
2009
|
+
readonly comment: CommentRecord;
|
|
2010
|
+
readonly range: ReviewRange | null;
|
|
2011
|
+
readonly resolved: boolean;
|
|
2012
|
+
/** The comment this replies to, absent for a top-level comment. */
|
|
2013
|
+
readonly parentId?: string;
|
|
2014
|
+
/**
|
|
2015
|
+
* The REVISION this comment answers, when it covers exactly that change's characters.
|
|
2016
|
+
*
|
|
2017
|
+
* Separate from {@link parentId} rather than folded into it: the two name different item
|
|
2018
|
+
* kinds, and a surface resolving one id against the comment index would find nothing.
|
|
2019
|
+
*/
|
|
2020
|
+
readonly parentRevisionId?: string;
|
|
2021
|
+
/** Replies to this comment, in document order. Empty for a reply or a childless comment. */
|
|
2022
|
+
readonly replyIds: readonly string[];
|
|
2023
|
+
/** True when the file gave this comment no usable range. */
|
|
2024
|
+
readonly orphaned: boolean;
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* One pending decision as the STORE derives it: a tracked change or a comment. Discriminate on
|
|
2028
|
+
* `kind`.
|
|
2029
|
+
*
|
|
2030
|
+
* The layout layer's own `ReviewItem` widens this with the pro custom-node card, which has no
|
|
2031
|
+
* store representation.
|
|
2032
|
+
*/
|
|
2033
|
+
type ReviewItem = ReviewRevisionItem | ReviewCommentItem;
|
|
2034
|
+
/** The stable key a surface uses for the active item and for a React list. */
|
|
2035
|
+
declare function reviewItemKey(item: ReviewItem): string;
|
|
2036
|
+
/** Plain text of a comment's body, so a card never re-implements the run walk. */
|
|
2037
|
+
declare function commentBodyText(comment: CommentRecord): string;
|
|
2038
|
+
/** Author initials for an avatar, from `@w:initials` or the name. */
|
|
2039
|
+
declare function commentInitials(comment: CommentRecord): string;
|
|
2040
|
+
/** The `w:p` a node sits inside, and the model offset it starts at within that paragraph. */
|
|
2041
|
+
interface SiteLocation {
|
|
2042
|
+
readonly paragraphId: string;
|
|
2043
|
+
readonly start: number;
|
|
2044
|
+
readonly end: number;
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Locate every revision site in one walk.
|
|
2048
|
+
*
|
|
2049
|
+
* One walk rather than a lookup per site: `resolveRevisions` learned the same lesson the hard
|
|
2050
|
+
* way, where a per-site tree walk inside a per-site loop made accept-all quadratic.
|
|
2051
|
+
*
|
|
2052
|
+
* Offsets come from `paragraphOffsetIndex`, which is `segmentsOf`'s walk. A private one here
|
|
2053
|
+
* measured a run by summing its text and gave a note reference, an atomic field and a field's
|
|
2054
|
+
* instruction text the wrong lengths, so every card in a paragraph holding one reported a
|
|
2055
|
+
* range the caret and the ops disagreed with.
|
|
2056
|
+
*/
|
|
2057
|
+
declare function locateSites(part: OoxmlPart): Map<string, SiteLocation>;
|
|
2058
|
+
/**
|
|
2059
|
+
* Every revision in one story, one card per DECISION.
|
|
2060
|
+
*
|
|
2061
|
+
* Sites sharing an `(id, author, date)` triple are ONE revision — a tracked row insertion is
|
|
2062
|
+
* `w:trPr/w:ins` plus `w:cellIns` on every cell — so they coalesce into one card listing every
|
|
2063
|
+
* range it touches. Keying per site would show the reviewer four decisions where there is one,
|
|
2064
|
+
* and accepting any of them would make the other three vanish.
|
|
2065
|
+
*/
|
|
2066
|
+
declare function revisionItemsOf(part: OoxmlPart): ReviewRevisionItem[];
|
|
2067
|
+
/**
|
|
2068
|
+
* Comment cards, threaded however the file says so and flat when nothing says so.
|
|
2069
|
+
*
|
|
2070
|
+
* ECMA-376 §17.13.4.2 gives `CT_Comment` no parent pointer, so threading is never something
|
|
2071
|
+
* the standard states outright. Three sources, strongest first: `@w15:paraIdParent`, then
|
|
2072
|
+
* `@w16cid:parentId` — both in namespaces outside Part 1 — and finally a COINCIDENT anchor, a
|
|
2073
|
+
* comment whose `w:commentRangeStart`/`End` cover exactly the characters an earlier comment's
|
|
2074
|
+
* cover. The ranges are Part 1's own vocabulary and the only part of a thread that survives a
|
|
2075
|
+
* producer dropping the extension parts. Coincidence is the last resort and never overrides a
|
|
2076
|
+
* stated link.
|
|
2077
|
+
*
|
|
2078
|
+
* Deliberately not containment. A remark on one word inside another remark's sentence nests
|
|
2079
|
+
* without being a reply, and reading that as a thread would bury an independent comment inside
|
|
2080
|
+
* someone else's.
|
|
2081
|
+
*/
|
|
2082
|
+
declare function commentItemsOf(comments: readonly CommentRecord[], anchors: readonly {
|
|
2083
|
+
commentId: string;
|
|
2084
|
+
partName: string;
|
|
2085
|
+
start: ReviewPosition;
|
|
2086
|
+
end: ReviewPosition;
|
|
2087
|
+
orphaned: boolean;
|
|
2088
|
+
}[], threadState: ReadonlyMap<string, CommentThreadState>): ReviewCommentItem[];
|
|
2089
|
+
/** What review derivation reads: one story part plus its comment parts. */
|
|
2090
|
+
interface ReviewModelInput {
|
|
2091
|
+
/** The story the ranges live in — the main document, a header, a note. */
|
|
2092
|
+
readonly storyPart: OoxmlPart;
|
|
2093
|
+
/**
|
|
2094
|
+
* Header/footer story parts, in section order. Their revisions and comment anchors join
|
|
2095
|
+
* the queue: a tracked change in a header is a pending decision like any other, and a
|
|
2096
|
+
* queue that only walked the body silently hid it from the rail AND from Accept All.
|
|
2097
|
+
*/
|
|
2098
|
+
readonly furnitureParts?: readonly OoxmlPart[] | undefined;
|
|
2099
|
+
/** `word/comments.xml`, absent when the package has none. */
|
|
2100
|
+
readonly commentsPart?: OoxmlPart | undefined;
|
|
2101
|
+
/** `word/commentsExtended.xml`, absent when the package has none. */
|
|
2102
|
+
readonly commentsExtendedPart?: OoxmlPart | undefined;
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Everything the review surface lists, in document order.
|
|
2106
|
+
*
|
|
2107
|
+
* Order is by paragraph position within the story, then by offset. A comment and the revision
|
|
2108
|
+
* it covers therefore arrive together, which is what lets a surface group them. Furniture
|
|
2109
|
+
* stories rank after the body in one merged order — their geometry (the page they first paint
|
|
2110
|
+
* on) is a layout question the queue deliberately does not answer.
|
|
2111
|
+
*/
|
|
2112
|
+
declare function collectReviewItems(input: ReviewModelInput): ReviewItem[];
|
|
2113
|
+
/**
|
|
2114
|
+
* The shape {@link linkRevisionReplies} needs, stated STRUCTURALLY.
|
|
2115
|
+
*
|
|
2116
|
+
* The store's queue is revisions and comments; the layout lane's adds a third kind for custom
|
|
2117
|
+
* nodes, and neither union is assignable to the other. Both lanes have to run this pass — the
|
|
2118
|
+
* store on the full derivation, the session on the locally patched list — so the pass is
|
|
2119
|
+
* written against the fields it actually reads rather than against either union, and hands
|
|
2120
|
+
* back the caller's own item type.
|
|
2121
|
+
*/
|
|
2122
|
+
interface LinkableReviewItem {
|
|
2123
|
+
readonly kind: string;
|
|
2124
|
+
readonly id: string;
|
|
2125
|
+
readonly ranges?: readonly ReviewRange[];
|
|
2126
|
+
readonly range?: ReviewRange | null;
|
|
2127
|
+
readonly parentId?: string;
|
|
2128
|
+
readonly parentRevisionId?: string;
|
|
2129
|
+
readonly replyIds?: readonly string[];
|
|
2130
|
+
readonly orphaned?: boolean;
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2133
|
+
* Attach each comment that answers a tracked change to the change it answers.
|
|
2134
|
+
*
|
|
2135
|
+
* The evidence is the RANGE, exactly as it is for a coincident comment thread: replying to a
|
|
2136
|
+
* revision writes a comment over that revision's own characters, because OOXML gives `w:ins`
|
|
2137
|
+
* and `w:del` nowhere else to put the text. Without this the reply came back as an independent
|
|
2138
|
+
* card in the rail, sitting beside the change rather than inside it, and the reader had no way
|
|
2139
|
+
* to see which change their answer belonged to.
|
|
2140
|
+
*
|
|
2141
|
+
* Three things keep it from over-claiming. A ZERO-WIDTH range is evidence of nothing — the same
|
|
2142
|
+
* rule the comment threading uses, and a format or paragraph-mark revision decorates no
|
|
2143
|
+
* characters at all. A comment already stated to be a REPLY to another comment is not claimed
|
|
2144
|
+
* directly, because a stated link always beats an inferred one. And the FIRST revision on a span
|
|
2145
|
+
* wins, so a card cannot claim a reply another card already holds.
|
|
2146
|
+
*
|
|
2147
|
+
* The WHOLE conversation moves, not its head. A change's card renders `replyIds` as a flat list,
|
|
2148
|
+
* so linking only the top comment of a thread left every answer to that answer rendered by
|
|
2149
|
+
* nobody: reply twice to one change and the second reply existed in `comments.xml` and appeared
|
|
2150
|
+
* nowhere on screen. Descendants ride along, in the order they were authored.
|
|
2151
|
+
*
|
|
2152
|
+
* IDEMPOTENT, and that is load-bearing. The session re-runs this over a list whose comments are
|
|
2153
|
+
* ALREADY linked, so a pass that only ever added links left a stale `parentRevisionId` behind
|
|
2154
|
+
* when a keystroke shifted the revision's offsets out from under it — the rail filters such a
|
|
2155
|
+
* comment out of its roots, and with no revision claiming it any more the card vanished until
|
|
2156
|
+
* the next full re-derivation. Every link is rebuilt from the ranges on every pass.
|
|
2157
|
+
*/
|
|
2158
|
+
declare function linkRevisionReplies<T extends LinkableReviewItem>(items: readonly T[]): T[];
|
|
2159
|
+
/** Paragraph node id → document position, from the TREE rather than from a layout. */
|
|
2160
|
+
declare function paragraphOrderOfPart(part: OoxmlPart): Map<string, number>;
|
|
2161
|
+
/**
|
|
2162
|
+
* Every range a decision touches. One card can cover several, in different paragraphs.
|
|
2163
|
+
*
|
|
2164
|
+
* Exported because the geometry half in the layout lane asks the same question, and a second
|
|
2165
|
+
* copy of "which ranges does this item cover" is how a card comes to be painted over one range
|
|
2166
|
+
* and activated by another.
|
|
2167
|
+
*/
|
|
2168
|
+
declare function reviewItemRanges(item: ReviewItem): readonly ReviewRange[];
|
|
2169
|
+
/** The range an item is anchored at — where its card belongs and how it sorts. */
|
|
2170
|
+
declare function firstReviewRange(item: ReviewItem): ReviewRange | null;
|
|
2171
|
+
|
|
2172
|
+
/**
|
|
2173
|
+
* Longest accepted query. A query is host input rather than file content, but the scan is
|
|
2174
|
+
* proportional to it and there is no legitimate find phrase this long.
|
|
2175
|
+
*/
|
|
2176
|
+
declare const SEARCH_QUERY_MAX = 256;
|
|
2177
|
+
/**
|
|
2178
|
+
* Most matches one search returns. A single-character query against a long document would
|
|
2179
|
+
* otherwise allocate an entry per character; the scan stops here instead. A caller showing a
|
|
2180
|
+
* count treats a full result as "at least this many".
|
|
2181
|
+
*/
|
|
2182
|
+
declare const SEARCH_MATCH_LIMIT = 2000;
|
|
2183
|
+
/** How a text search is narrowed. Options the engine cannot honour are refused, never ignored. */
|
|
2184
|
+
interface TextMatchOptions {
|
|
2185
|
+
readonly matchCase?: boolean;
|
|
2186
|
+
readonly wholeWord?: boolean;
|
|
2187
|
+
/**
|
|
2188
|
+
* Report only matches lying wholly inside `[from, to)`.
|
|
2189
|
+
*
|
|
2190
|
+
* A WINDOW ON THE TEXT, not a slice of it: the whole-word test still reads the characters on
|
|
2191
|
+
* either side of the window, so scanning the first three characters of `category` for `cat`
|
|
2192
|
+
* finds nothing when whole words were asked for. Slicing first would answer a match, because
|
|
2193
|
+
* the `e` that disqualifies it would have been cut off — the window is where a caller is
|
|
2194
|
+
* looking, not what the paragraph says.
|
|
2195
|
+
*/
|
|
2196
|
+
readonly from?: number;
|
|
2197
|
+
readonly to?: number;
|
|
2198
|
+
}
|
|
2199
|
+
/** One occurrence, as UTF-16 offsets into the text that was scanned. */
|
|
2200
|
+
interface TextOccurrence {
|
|
2201
|
+
readonly start: number;
|
|
2202
|
+
readonly length: number;
|
|
2203
|
+
}
|
|
2204
|
+
/** Where a phrase occurs in a story, as paragraph-plus-offset ranges. */
|
|
2205
|
+
interface TextOccurrences {
|
|
2206
|
+
readonly matches: readonly TextOccurrence[];
|
|
2207
|
+
/** The scan stopped at `limit` with occurrences still ahead of it. */
|
|
2208
|
+
readonly truncated: boolean;
|
|
2209
|
+
}
|
|
2210
|
+
/**
|
|
2211
|
+
* Lower-case `text` WITHOUT changing its length.
|
|
2212
|
+
*
|
|
2213
|
+
* `String.prototype.toLowerCase` can expand (Turkish dotted capital I lowercases to two code
|
|
2214
|
+
* units), and an expansion mid-paragraph would slide every offset after it — the match would
|
|
2215
|
+
* be reported at the wrong place. The per-unit fallback folds only the characters that stay
|
|
2216
|
+
* one unit, so an expanding character simply compares case-sensitively. That is a real
|
|
2217
|
+
* degradation and it is the safe direction: a missed case-insensitive match beats a match
|
|
2218
|
+
* reported at an offset an editor then selects.
|
|
2219
|
+
*/
|
|
2220
|
+
declare function foldCase(text: string): string;
|
|
2221
|
+
/** Whether a match at `[start, end)` in `text` stands alone as a word. */
|
|
2222
|
+
declare function isWholeWord(text: string, start: number, end: number): boolean;
|
|
2223
|
+
/** Whether a query is one this module will scan for at all. */
|
|
2224
|
+
declare function isSearchableQuery(query: unknown): query is string;
|
|
2225
|
+
/**
|
|
2226
|
+
* Every occurrence of `query` in `text`, in order, NON-OVERLAPPING.
|
|
2227
|
+
*
|
|
2228
|
+
* Non-overlapping is what a find dialog counts: `aa` in `aaaa` is two, not three. `limit` is
|
|
2229
|
+
* the caller's remaining budget, so a caller scanning many paragraphs enforces ONE global cap
|
|
2230
|
+
* rather than one per paragraph.
|
|
2231
|
+
*/
|
|
2232
|
+
declare function findOccurrences(text: string, query: string, limit: number, options?: TextMatchOptions): TextOccurrences;
|
|
2233
|
+
|
|
2234
|
+
/** Impact class for a drawing op — metadata/locks are text-local; geometry/wrap are flow-structural. */
|
|
2235
|
+
declare function drawingOpImpact(op: DrawingTreeDocOp): ImpactClass;
|
|
2236
|
+
/** Validate a drawing op's extents, crops and positions before it reaches the store. */
|
|
2237
|
+
declare function validateDrawingOp(part: OoxmlPart, op: DrawingTreeDocOp): TreeOpRejection | null;
|
|
2238
|
+
/** Map the nine wrap targets to anchor wrap element + behindDoc. */
|
|
2239
|
+
declare function wrapTargetToAnchorSpec(target: ImageWrapTarget): {
|
|
2240
|
+
readonly behindDocument: boolean;
|
|
2241
|
+
readonly wrapLocalName: 'wrapNone' | 'wrapSquare' | 'wrapTight' | 'wrapThrough' | 'wrapTopAndBottom';
|
|
2242
|
+
readonly wrapText?: 'bothSides' | 'left' | 'right' | 'largest';
|
|
2243
|
+
};
|
|
2244
|
+
/** Whether an op is one of the drawing ops. Narrows the type. */
|
|
2245
|
+
declare function isDrawingTreeDocOp(op: TreeDocOp): op is DrawingTreeDocOp;
|
|
2246
|
+
|
|
2247
|
+
export { ALL_FROZEN_IDS, AUTHORABLE_PARAGRAPH_PROPERTIES, AUTHORABLE_RUN_PROPERTIES, type AddCommentRequest, type AddCommentResult, type AnchorSnapshot, type AtomicFieldSpan, type AuditPort, type AuthorizationPort, BoundedCounter, Budget, BudgetError, CANONICAL_MUTATION_ORIGINS, COMPARATORS, CancellationController, CancellationError, type CancellationPhase, type CancellationPort, type CancellationToken, type CapabilityId, type ClockPort, type CommentAnchor, type CommentAnchorRequest, type CommentPosition, type CommentRecord, type CommentThreadState, type ComparatorDescriptor, type ComparatorMode, type ComparatorName, type ComparisonResult, type ConformanceFixture, type ContentItem, type Contribution, DANGEROUS_KEYS, DEPENDENCY_KEY_IDS, DangerousKeyError, type DetectedToc, DeterministicClock, DeterministicMemoryMeter, type DigestDifference, DrawingTreeDocOp, type EditOptions, type EncodedEnvelope, type EnforcementPhase, type EnsuredHyperlinkRelationship, type ExternalResourceConsentPort, FIELD_ATOM_CHAR, type FeatureBundle, type FixtureExpectation, type FixtureOutcome, type FixtureSource, type FixtureStep, type FldCharType, type FontPort, type HardBreakKind, type HrefProjection, ID_KINDS, INERT_EXECUTABLE_KINDS, type IdKind, type IdentityPort, type ImagePort, ImageWrapTarget, ImpactClass, type InertExecutableKind, type InlineControlSpan, type Json, LIMIT_KEYS, LIMIT_SPECS, LimitExceededError, type LimitSpec, type LimitUnit, type LinkableReviewItem, MAX_STORY_SDT_NESTING, type ModelChangeSummary, NON_CANONICAL_ORIGINS, ORIGIN_IDS, type OffsetSpan, OoxmlAttribute, type OoxmlEditResult, OoxmlElement, OoxmlFldCharNode, OoxmlFldSimpleNode, OoxmlHardBreakNode, type OoxmlIndexes, OoxmlInstrTextNode, OoxmlInvariantIssue, OoxmlInvariantResult, OoxmlNode, OoxmlPackage, OoxmlParagraphNode, OoxmlPart, OoxmlProperty, type OoxmlStoryKind, type OoxmlStoryRoot, type OperationContext, type OperationInit, PAGE_BREAK_CHAR, type PackageInvariantCode, type PackageInvariantIssue, type PackageInvariantResult, type ParagraphDigest, type ParagraphIndexEntry, type ParagraphOffsetIndex, type PersistencePort, PortRegistry, PortResolutionError, PrefixAllocator, RESULT_IDS, RUNTIME_PORT_IDS, RegistryError, type RegistryErrorCode, RelationshipRecord, type ReplacementPolicy, type ReplayOutcome, type ReplayReport, type ReplayStore, type Reservation, type ResolveOptions, type ResolvedRegistry, type ResourceAccountingPort, ResourceLimits, type ReviewCommentItem, type ReviewItem, type ReviewModelInput, type ReviewPosition, type ReviewRange, type ReviewRevisionItem, type ReviewRevisionKind, RevisionAddress, type RunPropertyEdit, SEARCH_MATCH_LIMIT, SEARCH_QUERY_MAX, type SchedulingPort, type ScrubResult, type Segment, type SemVer, type SemanticDigest, SequentialIdentity, type SetCommentResolvedResult, type ShapingPort, type StoryDigest, type StoryIndexEntry, type StyleIndexEntry, TOC_LEVEL_INDENT_TWIPS, TOC_MAX_BOOKMARKS_PER_REFRESH, TOC_MAX_ENTRIES, TOC_MAX_FIELD_NESTING, TOC_MAX_INSTRUCTION_CHARS, TOC_MAX_PAGE_PASSES, type TextMatchOptions, type TextOccurrence, type TextOccurrences, type TocEntryPlan, type TocInstruction, type TocOutlineHeading, type TransportPort, TreeDocOp, TreeDocumentStore, TreeModelChange, TreeOpRejection, TreeOpResult, type ValidationResult, W15_NAMESPACE_URI, addComment, applyEdits, applyTreeOp, assertLimitInvariants, assertValidId, assertValidQName, atomicFieldSpansOf, authorableHyperlinkTarget, authoredProperties, beginOperation, bodyStoryRoot, bookmarkPairNodes, buildTocContentControl, buildTocEntryParagraph, canonicalOoxmlFingerprint, canonicalize, cascadeEmptiedComments, collectNodeIds, collectReviewItems, collectRevisionSites, collectStoryParagraphs, commentAnchorsOfStory, commentBodyText, commentInitials, commentItemsOf, commentPartNameOf, commentsExtendedPartNameOf, commentsOfPart, compareArtifacts, compareSemVer, containsCssFetch, contentTypesPartBytes, createNodeIdAllocator, deleteCommentThread, deriveOoxmlIndexes, detectBodyTocs, diffSemanticDigests, digestPart, directParagraphMarkProperties, directParagraphProperties, drawingOpImpact, endOperation, ensureHyperlinkRelationship, escapeCssString, escapeXml, fieldAtomText, fieldOnOffAttribute, findDetectedToc, findNode, findOccurrences, fingerprint, firstReviewRange, fldCharType, fldSimpleInstr, fnv1a32, foldCase, formatOwnedRunIds, hardBreakAttributes, hardBreakKind, hardBreakText, hasAnyComment, hasCommentPart, hasNode, hashAuthored, indexStyles, inlineControlEndingAt, inlineControlStartingAt, insertChildren, instrTextValue, isAuthorableRunProperty, isDangerousKey, isDrawingTreeDocOp, isEvaluableField, isFieldChrome, isFldChar, isFldCharNode, isFldSimple, isFldSimpleNode, isInertExecutable, isInstrText, isInstrTextNode, isLegalFldCharType, isPageBreakNode, isSearchableQuery, isValidId, isValidNCName, isValidParaId, isValidQName, isWholeWord, linkRevisionReplies, locateSites, makeLimitCounter, mergedProperties, mintParaId, mintedParagraphIdentityAttributes, normalizeParagraphIdentity, nullRecord, ooxmlTreesEqual, paraIdOf, paragraphOffsetIndex, paragraphOrderOfPart, paragraphTextOf, parentNodeOf, parseSemVer, parseTocInstruction, planTocEntries, propertyContainer, relationshipTargetIn, relationshipsOf, relsPartNameFor, removeNode, replaceChildren, replaceNode, replayFixture, resolve, resolveContentTypeOf, resolveTocRowHeadings, reviewItemKey, reviewItemRanges, revisionItemsOf, runAddressRanges, runPropertyEdits, runsCovering, sanitizeHref, satisfies, scrubExport, segmentsOf, semanticDigest, serializeOoxmlPart, setCommentResolved, stableHash, storyParagraphs, storyRootsOf, stylesPartOf, threadStateOfPart, toSafeRecord, tocEntryText, tocLeftIndentTwips, usedParaIds, validateDrawingOp, validateFixture, validateOoxmlPart, validatePackageInvariants, validateTreeOp, w14RootPrefix, withContentTypeOverride, withNewPart, withRelationship, wrapTargetToAnchorSpec };
|