@docx-editor.dev/core/contracts/modules
@docx-editor.dev/core/contracts/modules — the EditorModule seam.
How a paid or optional capability contributes to the engine without the free tier importing it: the review module and custom nodes both arrive through here, and their absence leaves the corresponding chrome disabled with the engine's own reason.
NOT a plugin system. The shape is closed: every contribution point is named here, core iterates registered modules at its existing dispatch points, and core never imports a capability package. A capability absent from this file is not extendable from outside — deliberately, so the one-pipeline principle survives the packaging boundary. The free engine behaves identically with an empty registry: documents round-trip losslessly, revisions render in their final-state projection, and the review chrome slots stay disabled with the engine's own reason.
Functions (1)
resolveEditorModulesfunctionSource ↗
Resolve construction-time modules into the registry the instance dispatches over.
declare function resolveEditorModules(modules: readonly EditorModule[] | undefined): EditorModuleRegistry;Interfaces (25)
CollaborationModuleContributioninterfaceSource ↗
What a collaboration module contributes: the replica the surface attaches.
interface CollaborationModuleContribution| Member | Type | Summary |
|---|---|---|
| session | EditorCollaborationSession | A ready session. The host creates the Yjs room, then wraps it with `collaborationModule({ session })`. |
CommentRecordinterfaceSource ↗
One comment as authored in word/comments.xml.
interface CommentRecord| Member | Type | Summary |
|---|---|---|
| author | string | |
| blocks | readonly OoxmlElement[] | Body paragraphs, as tree nodes, so the surface renders measured text rather than a string. |
| date? | string | |
| id | string | |
| initials? | string | |
| paraId? | string | `w14:paraId` of the last body paragraph — the key thread state is stored under. |
| parentCommentId? | string | `@w16cid:parentId` — the `w:id` of the comment this replies to, when the file names it. |
CompareDocumentsDialoginterfaceSource ↗
Compare documents dialog supplied by the review module.
interface CompareDocumentsDialog| Member | Type | Summary |
|---|---|---|
| destroy | | |
| open | |
CompareDocumentsDialogHostinterfaceSource ↗
What the Compare documents dialog may use: its session, mount, and strings.
interface CompareDocumentsDialogHost extends CompareDocumentsDialogSession| Member | Type | Summary |
|---|---|---|
| container | HTMLElement | |
| translate? | (key: string) => string | undefined |
CompareDocumentsDialogSessioninterfaceSource ↗
One open Compare documents dialog. The editor owns it: the session aborts when the dialog closes, is replaced, or the document, mount, or editor goes away.
interface CompareDocumentsDialogSession| Member | Type | Summary |
|---|---|---|
| apply | | Compare the open document with the revised DOCX and record the differences in it as tracked changes, as one undoable step. Closes the session when the document changes. With no differences (`changed` false) or a refusal, the session stays open with a message to show. |
| canApply | | Whether a compare can run now. |
| cancel | | Close the dialog. A running background compare stops, and nothing changes. |
| cancelable | boolean | Whether `cancel()` can stop a running compare: true when compares run in the background. A compare that falls back to the main thread, because the worker never started, cannot stop. |
| defaultAuthor | string | The author name the dialog offers first: the editor's author, or an empty string. |
| originalHasTrackedChanges | boolean | Whether the open document has tracked changes when the dialog opens. The compare treats them as accepted, so a dialog says so before the user compares. |
| signal | AbortSignal | Aborts on close, replacement, load, detach, or destroy. |
CompareDocumentsEngineOptionsinterfaceSource ↗
Options of one document compare.
interface CompareDocumentsEngineOptions| Member | Type | Summary |
|---|---|---|
| authorName? | string | Author recorded on every revision. The engine picks a default when absent, empty, or only white space. |
| detectFormatChanges? | boolean | Record formatting differences as formatting revisions. Default `true`. |
| removeDateAndTime? | boolean | Omit the revision timestamps. Default `false`. |
CompareDocumentsProgressinterfaceSource ↗
How far a background compare is.
interface CompareDocumentsProgress| Member | Type | Summary |
|---|---|---|
| fraction | number | The share of the work done, from 0 to 1. It never decreases. |
| phase | CompareDocumentsPhase | The step that runs now. |
CompareDocumentsRequestinterfaceSource ↗
What one compare request carries: the revised DOCX and its options.
interface CompareDocumentsRequest| Member | Type | Summary |
|---|---|---|
| authorName? | string | Author recorded on every revision. When absent or blank, the editor's author. Without an editor author, the revised document's last author, then its creator, then `Comparison`. |
| detectFormatChanges? | boolean | Record formatting differences as formatting revisions. Default `true`. |
| removeDateAndTime? | boolean | Write the revisions without dates. Default `false`. |
| revised | Uint8Array | The revised document's DOCX bytes. The open document is the original. |
ContentTypeIndexinterfaceSource ↗
The resolved lookup: Override by case-folded part name, Default by case-insensitive extension.
interface ContentTypeIndex| Member | Type | Summary |
|---|---|---|
| defaults | ReadonlyMap<string, string> | ext key - single MIME (identical duplicates collapsed). |
| overrides | ReadonlyMap<string, string> | case-folded part name - MIME. |
EditorModuleinterfaceSource ↗
One registered capability module. Registration is construction-time (createDocxEditor({ modules })) and immutable for the instance's lifetime.
interface EditorModule| Member | Type | Summary |
|---|---|---|
| collaboration? | CollaborationModuleContribution | Collaboration replica. Absent, the editor stays single-user: local undo stays the authority, and `snapshot().collaborationStatus` is `'inactive'`. |
| customNodePayloadNamespaces? | readonly string[] | customXml payload namespaces this module OWNS, swept for orphans when a document opens. |
| customNodes? | readonly unknown[] | Custom inline node definitions. Reserved: the definition shape lands with the custom-nodes lane; the registry carries them opaquely until then. |
| id | string | Diagnostic identity (`'review'`, `'custom-nodes'`); not a dispatch key. |
| onCustomNodeDiagnostic? | (diagnostic: unknown) => void | Told when the recognition pass finds something wrong with a node in THIS editor's document. |
| onDiagnostic? | (event: EditorDiagnostic) => void | Told about every anonymous diagnostic of this editor, from its first open on. |
| review? | ReviewModuleContribution | Review capability: queue derivation, commands gate, display modes. |
| wantsDiagnostic? | (kind: EditorDiagnosticKind) => boolean | Whether `onDiagnostic` wants events of `kind` now. Absent, it wants every kind. The editor asks before work that only a diagnostic needs, such as timing key presses for `performance`, and skips that work when no listener wants it. Read often: keep it cheap. |
EditorModuleRegistryinterfaceSource ↗
The resolved registry the editor instance holds: at most one review contribution and at most one collaboration contribution (first registration wins for each), all custom node definitions in registration order.
interface EditorModuleRegistry| Member | Type | Summary |
|---|---|---|
| collaboration | CollaborationModuleContribution | null | |
| customNodeDiagnostics | readonly ((diagnostic: unknown) => void)[] | Every registered diagnostic listener, in registration order. |
| customNodePayloadNamespaces | readonly string[] | Every claimed payload namespace, deduplicated, in registration order. |
| customNodes | readonly unknown[] | |
| diagnosticListeners | readonly ((event: EditorDiagnostic) => void)[] | Every registered `onDiagnostic` listener, in registration order. |
| review | ReviewModuleContribution | null | |
| wantsDiagnostic | (kind: EditorDiagnosticKind) => boolean | Whether any module with `onDiagnostic` wants events of `kind` now. |
OoxmlExternalTargetinterfaceSource ↗
An external relationship target. Retained verbatim as authored evidence, with the sink-safety verdict alongside it. Never resolved against the package, never fetched.
interface OoxmlExternalTarget| Member | Type | Summary |
|---|---|---|
| id | string | |
| ownerPart | string | |
| rawTarget | string | |
| sinkSafe | boolean | False when the target is not a safe sink (javascript:, file:, ...). Still not fetched. |
| type | string |
OoxmlPackageinterfaceSource ↗
A loaded package: every XML part as a canonical tree, plus the non-XML parts kept verbatim.
The preservation model in one value. Modelled parts re-emit normalized; everything else is byte-identical, which is why an unrecognized part never costs a document anything.
interface OoxmlPackage| Member | Type | Summary |
|---|---|---|
| contentTypes | ContentTypeIndex | |
| externalTargets | readonly OoxmlExternalTarget[] | |
| mainDocumentPart | string | Canonical name of the part the root `officeDocument` relationship points at. |
| partBytes | ReadonlyMap<string, Uint8Array> | Raw bytes of every entry, including the non-XML parts that have no tree. |
| parts | ReadonlyMap<string, OoxmlPart> | Canonical trees, keyed by canonical part name. Non-XML parts are absent by design. |
| relationships | ReadonlyMap<string, readonly RelationshipRecord[]> | Internal relationships by owner part, in authored order. |
OoxmlPartinterfaceSource ↗
One XML part of the package, parsed into a canonical tree.
interface OoxmlPart| Member | Type | Summary |
|---|---|---|
| contentType | string | |
| id | string | |
| name | string | Canonical part name, e.g. `/word/document.xml`. |
| root | OoxmlElement |
RelationshipRecordinterfaceSource ↗
One authored relationship, with nothing materialized away: owner part, id, type, raw target lexical form, mode, and position.
interface RelationshipRecord| Member | Type | Summary |
|---|---|---|
| id | string | |
| order | number | |
| ownerPart | string | |
| rawTarget | string | |
| targetMode | TargetMode | |
| type | string |
ReviewCommentIteminterfaceSource ↗
One comment as a review card. A reply carries parentId; OOXML gives replies no separate element, so threads are reconstructed from that link.
interface ReviewCommentItem| Member | Type | Summary |
|---|---|---|
| comment | CommentRecord | |
| id | string | |
| kind | 'comment' | |
| orphaned | boolean | True when the file gave this comment no usable range. |
| parentId? | string | The comment this replies to, absent for a top-level comment. |
| parentRevisionId? | string | The REVISION this comment answers, when it covers exactly that change's characters. |
| range | ReviewRange | null | |
| replyIds | readonly string[] | Replies to this comment, in document order. Empty for a reply or a childless comment. |
| resolved | boolean |
ReviewCustomIteminterfaceSource ↗
A card contributed by a recognized custom node (defineCustomNode with a reviewCard hook), anchored at the node's range.
Informational, never resolvable: there is nothing to accept or reject, so the engine refuses those verbs on it. title and detail are HOST-authored (the definition's hook produced them), but attrs and text originate in a file an attacker controls — a surface renders every one of these as text, never markup.
interface ReviewCustomItem| Member | Type | Summary |
|---|---|---|
| attrs | Readonly<Record<string, string>> | Attrs decoded from the tag, after the definition's recognition hook. Untrusted input. |
| carded | boolean | Whether this node asked for a sidebar card. |
| data? | unknown | The payload the node's control binds to, after the definition validated it. |
| detail? | string | Card body, from the definition's `reviewCard` hook. |
| icon? | string | Glyph for this node in the collapsed rail, as an SVG path in a `0 -960 960 960` viewBox. |
| id | string | The SDT node's stable id in the canonical tree. |
| kind | 'custom' | |
| name | string | The definition's `name`. |
| range | ReviewRange | null | |
| tag | string | The raw `w:tag` the node was recognized from. Untrusted input. |
| text | string | The SDT's literal content text. Untrusted input. |
| title | string | Card title, from the definition's `reviewCard` hook. Empty when `carded` is false. |
ReviewModelInputinterfaceSource ↗
What the review queue derivation reads: one story part plus its comment parts.
interface ReviewModelInput| Member | Type | Summary |
|---|---|---|
| commentsExtendedPart? | OoxmlPart | undefined | `word/commentsExtended.xml`, absent when the package has none. |
| commentsPart? | OoxmlPart | undefined | `word/comments.xml`, absent when the package has none. |
| customNodePayloads? | ReadonlyMap<string, {
readonly nodeId: string;
readonly label: string;
readonly data: string;
}> | undefined | The payload each of the story's controls binds to, keyed by the control's node id. |
| customNodes? | readonly unknown[] | undefined | Custom node definitions from the module registry, forwarded OPAQUELY. |
| furnitureParts? | readonly OoxmlPart[] | undefined | Header/footer story parts, in section order. Their revisions and comment anchors join the queue: a tracked change in a header is a pending decision like any other, and a queue that only walked the body silently hid it from the rail AND from Accept All. |
| reportCustomNodeDiagnostic? | ((diagnostic: unknown) => void) | undefined | Where a capability package reports a node it could not read. Supplied per editor, so a page with two of them keeps their diagnostics apart. |
| storyPart | OoxmlPart | The story the ranges live in — the main document, a header, a note. |
| stylesPart? | OoxmlPart | undefined | Shared style definitions. Their revisions have no paragraph range. |
ReviewModuleContributioninterfaceSource ↗
What a review module contributes: the queue derivation, and the revision display modes the editor may enter beyond the free tier's final-state projection.
interface ReviewModuleContribution| Member | Type | Summary |
|---|---|---|
| collectReviewItems | CollectReviewItems | The review queue derivation. |
| compareDocuments? | CompareDocumentsEngine | Document compare. Absent, compare commands refuse. |
| compareDocumentsInBackground? | CompareDocumentsBackgroundEngine | The same compare off the main thread. When present, the Compare documents dialog uses it, reports progress, and can stop a running compare. If it rejects before its first progress report, the dialog compares with `compareDocuments` instead. The `compareDocuments` command stays synchronous and uses `compareDocuments`. |
| createCompareDocumentsDialog? | (host: CompareDocumentsDialogHost) => CompareDocumentsDialog | Create the Compare documents dialog when a host opens it. |
| createRevisionMarkupDialog? | (host: RevisionMarkupDialogHost) => RevisionMarkupDialog | Create local review settings chrome when a host opens the dialog. |
| displayModes | readonly ReviewDisplayMode[] | Display modes this module unlocks (the free engine renders `proposed` only). |
| pane? | ReviewPaneOptions | The review pane settings the editor starts with. `setReviewPaneOptions` changes them later. |
| revisionItemsOfParagraph | (part: OoxmlPart, paragraphId: string) => readonly ReviewRevisionItem[] | Revisions wholly inside one paragraph — for the conservative local review patch after a text-local body edit. |
ReviewPositioninterfaceSource ↗
A position in the model offset space of one story.
interface ReviewPosition| Member | Type | Summary |
|---|---|---|
| offset | number | |
| paragraphId | string |
ReviewRangeinterfaceSource ↗
Where an item is anchored: a range in one story.
interface ReviewRange| Member | Type | Summary |
|---|---|---|
| end | ReviewPosition | |
| partName | string | |
| start | ReviewPosition |
ReviewRevisionIteminterfaceSource ↗
One tracked change as a review card.
Keyed per DECISION rather than per site: a revision spanning three ranges is one card, because accepting it accepts all three.
interface ReviewRevisionItem| Member | Type | Summary |
|---|---|---|
| address | RevisionAddress | The payload `acceptRevision` / `rejectRevision` take. |
| addresses | readonly RevisionAddress[] | EVERY address this decision covers, `address` first. |
| author | string | |
| date? | string | |
| formattingChanges? | readonly {
readonly property: 'bold' | 'italic' | 'underline' | 'strike' | 'fontFamily' | 'fontSize' | 'color' | 'highlight' | 'alignment' | 'leftIndent' | 'rightIndent' | 'firstLineIndent' | 'hangingIndent' | 'spaceBefore' | 'spaceAfter' | 'direction';
readonly value: string | null;
}[] | Changed direct formatting values, ready for localized review summaries. |
| formattingKind? | string | OOXML property-change element, used to resolve independently numbered formatting kinds. |
| formattingLanguages? | readonly string[] | Newly applied proofing language codes in a tracked formatting change. |
| id | string | Stable across renders and unique per decision within this editor instance. |
| kind | 'revision' | |
| markDirection? | 'insert' | 'delete' | 'moveFrom' | 'moveTo' | WHICH decision a `paragraphMark` records, absent for every other kind. |
| nesting | number | How deeply this change is NESTED inside other changes, 0 for an unenclosed one. |
| pairedWith? | string | The other half of a move, or the other side of a delete/insert replacement. |
| ranges | readonly ReviewRange[] | Every site this decision touches, in document order. |
| readOnly | boolean | True when the engine cannot resolve this kind, so accept and reject must not be offered. |
| replacedRangeCount? | number | How many leading `ranges` are the STRUCK half of a replacement. |
| replacedText | string | The words a replacement removes. Empty for every other kind. |
| replyIds | readonly string[] | Comments answering this change, in document order. |
| revisionKind | ReviewRevisionKind | |
| structuralChanges? | readonly ('rowInsert' | 'rowDelete' | 'cellInsert' | 'cellDelete' | 'cellMerge' | 'numberingInsert')[] | Distinct structural operations covered by this decision, in document order. |
| text | string | Text the revision covers, for the card summary. Empty for changes with no characters. |
RevisionAddressinterfaceSource ↗
How a tracked change is addressed: @w:id, @w:author and @w:date together, within a part.
All of them, always. @w:id is unique only within a part, so an id alone names two revisions in any package with a header or a comments part — and it is not author-scoped either, so (part, id) would merge two authors' distinct revisions. One logical revision is also deliberately many elements sharing an id, as a tracked row insertion is, which no uniqueness rule could express.
interface RevisionAddress| Member | Type | Summary |
|---|---|---|
| author | string | |
| date? | string | Absent when the file wrote no `@w:date`; part of the identity either way. |
| id | string |
RevisionMarkupDialoginterfaceSource ↗
Viewer settings dialog supplied by the review module.
interface RevisionMarkupDialog| Member | Type | Summary |
|---|---|---|
| destroy | | |
| open | |
RevisionMarkupDialogHostinterfaceSource ↗
Local settings access for review chrome. No document mutations occur here.
interface RevisionMarkupDialogHost extends RevisionMarkupDialogSession| Member | Type | Summary |
|---|---|---|
| container | HTMLElement | |
| translate? | (key: string) => string | undefined |
Type aliases (15)
CollectReviewItemstypeSource ↗
Derives the review queue — every pending revision decision and comment thread — from one story part plus its comment parts. Implemented by the pro review module; the free engine has no implementation and reports an empty queue.
type CollectReviewItems = (input: ReviewModelInput) => readonly ReviewItem[];CompareDocumentsBackgroundEnginetypeSource ↗
Runs a compare off the page's main thread, for example in a worker. The editor's Compare documents dialog uses it when the review module provides one. It reports progress, and it stops when signal aborts: the promise then rejects with the signal's reason.
type CompareDocumentsBackgroundEngine = (original: Uint8Array, revised: Uint8Array, options: CompareDocumentsEngineOptions, control: {
readonly signal: AbortSignal;
readonly onProgress: (progress: CompareDocumentsProgress) => void;
}) => Promise<CompareDocumentsBackgroundResult>;CompareDocumentsBackgroundResulttypeSource ↗
Result of one background compare: like [CompareDocumentsEngineResult](CompareDocumentsEngineResult), as bytes.
type CompareDocumentsBackgroundResult = {
readonly ok: true;
readonly bytes: Uint8Array | null;
readonly revisionCount: number;
readonly changed: boolean;
} | {
readonly ok: false;
readonly code: CompareDocumentsEngineErrorCode;
readonly reason?: CompareDocumentsRefusalReason;
readonly message: string;
};CompareDocumentsEnginetypeSource ↗
Compares the open document (the original) with a revised DOCX and returns the original with the differences as tracked changes. Synchronous, DOM-free, and free of side effects.
type CompareDocumentsEngine = (original: OoxmlPackage, revised: Uint8Array, options: CompareDocumentsEngineOptions) => CompareDocumentsEngineResult;CompareDocumentsEngineErrorCodetypeSource ↗
Why the compare engine refused a compare, as a stable code: the codes that the engine, compareDocuments, and a background engine return. compareDocuments.errors.<code> in @docx-editor.dev/i18n translates every code.
type CompareDocumentsEngineErrorCode =
/**
* An option has a value of the wrong type, such as a `detectFormatChanges` that is not a
* boolean, or the revised document is not a `Uint8Array`.
*/
'invalidOptions'
/** The original document is not a readable DOCX package. */
| 'invalidOriginal'
/** The revised document is not a readable DOCX package. */
| 'invalidRevised'
/** The documents exceed the compare's size limits. */
| 'tooLarge'
/**
* The original document enforces protection that the compare would break: read-only,
* comments, forms, or tracked-changes protection. A compare also writes differences that
* have no tracked form, such as style definitions and content control properties, so
* tracked-changes protection refuses too.
*/
| 'protected'
/** The differences include content that no tracked change can record. */
| 'unsupportedContent';CompareDocumentsEngineResulttypeSource ↗
Result of one document compare.
type CompareDocumentsEngineResult = {
readonly ok: true;
readonly package: OoxmlPackage;
readonly revisionCount: number;
readonly changed: boolean;
} | {
readonly ok: false;
readonly code: CompareDocumentsEngineErrorCode;
readonly reason?: CompareDocumentsRefusalReason;
readonly message: string;
};CompareDocumentsErrorCodetypeSource ↗
Why a compare was refused, as a stable code. compareDocuments.errors.<code> in @docx-editor.dev/i18n translates every code. The engine and compareDocuments return [CompareDocumentsEngineErrorCode](CompareDocumentsEngineErrorCode) values. The editor command and dialog can also return the editor-only codes.
type CompareDocumentsErrorCode = CompareDocumentsEngineErrorCode
/** Editor only: no review module, no open document, or no mount. */
| 'unavailable'
/** Editor only: a collaboration session is attached. */
| 'collaboration'
/** Editor only: the document is open for viewing only. */
| 'viewing'
/** Editor only: the dialog closed, or the compare was canceled, before it finished. */
| 'closed'
/** Editor only: the revised file is a `Uint8Array` with no bytes. */
| 'empty'
/**
* Editor only: another compare of the same dialog runs, or a preview or an external document
* refresh blocks writes to the document. Try again when it ends.
*/
| 'busy'
/**
* Editor only: the open document changed while a background compare ran, so its result
* no longer applies. The editor never retries by itself.
*/
| 'documentChanged'
/** Editor only: any other failure. */
| 'failed';CompareDocumentsOutcometypeSource ↗
The outcome of one compare.
type CompareDocumentsOutcome = {
readonly ok: true;
readonly revisionCount: number;
readonly changed: boolean;
} | {
readonly ok: false;
readonly code: CompareDocumentsErrorCode;
readonly reason?: CompareDocumentsRefusalReason;
readonly message: string;
};CompareDocumentsPhasetypeSource ↗
A step of a compare that runs in the background. The steps run in this order:
1. reading: read both documents. 2. comparing: find their differences. 3. checking: check that accepting and rejecting all changes give back each document. 4. applying: apply the result to the open document.
type CompareDocumentsPhase = 'reading' | 'comparing' | 'checking' | 'applying';CompareDocumentsRefusalReasontypeSource ↗
What an unsupportedContent refusal is about, as a stable code. compareDocuments.reasons.<reason> in @docx-editor.dev/i18n translates every reason.
type CompareDocumentsRefusalReason =
/** The documents differ inside a content control that is locked or bound to data. */
'lockedContent'
/** A document holds tracked changes that cannot be accepted, so its final state is unknown. */
| 'unresolvedRevisions'
/** The documents differ in content with no tracked-change form, such as an embedded file. */
| 'untrackedContent'
/** The revised document adds section or table settings that a reject cannot restore. */
| 'unrecordedSettings'
/** A header or footer of an earlier section cannot be paired with one of the original. */
| 'headerFooter'
/** The result needs a part type that the original document does not declare. */
| 'packageStructure'
/**
* The result fails the check: accepting all changes does not give the revised text, rejecting
* them does not give the original text, or a tracked change cannot be accepted or rejected.
*/
| 'unverified';ReviewDisplayModetypeSource ↗
The views the review chrome offers, in Word's terms: the three layout projections plus Simple Markup, which reads as the proposed result with a red change bar beside every line a change touched — the document as accepting everything would leave it, and where.
type ReviewDisplayMode = RevisionDisplayMode | 'simple-markup';ReviewItemtypeSource ↗
One pending decision in the review queue: a tracked change, a comment thread, or a pro custom-node card. Discriminate on kind.
The store's own derivation (collectReviewItems) only ever produces the first two; the kind: 'custom' card is contributed by the pro review module, which recognizes the node.
type ReviewItem = ReviewRevisionItem | ReviewCommentItem | ReviewCustomItem;ReviewRevisionKindtypeSource ↗
What kind of decision a revision card represents.
Wider than the four content wrappers, because a reviewer has to be shown every pending decision, including the ones that decorate no characters. A card the surface cannot show is a change the reviewer never learns about — and acceptAllRevisions refuses if ANY revision in the document is one the engine cannot resolve, so an invisible one makes Accept All fail for a reason nothing on screen explains.
type ReviewRevisionKind = 'insert' | 'delete'
/**
* A combined deletion and insertion decision. The built-in reader lists them separately,
* unless a review query asks to pair replacements or a custom review provider combines them.
*/
| 'replace' | 'moveFrom' | 'moveTo'
/** `w:rPrChange` / `w:pPrChange` — the words are unchanged, their formatting is not. */
| 'format'
/** `w:pPr/w:rPr/w:ins|w:del` — a paragraph split or merge. */
| 'paragraphMark'
/** A row, cell, section or grid revision. Supported row revisions are resolvable. */
| 'structural';RevisionDisplayModetypeSource ↗
Which revisions layout resolves before producing pages.
- all-markup shows both halves of every change. - proposed shows what the document becomes if every change is accepted. - original shows what it was before any of them.
The last two are specified as equal to accept-all and reject-all OUTPUT, which is what makes them testable, without either applying an op.
type RevisionDisplayMode = 'all-markup' | 'proposed' | 'original';TargetModetypeSource ↗
Whether a relationship points inside the package or out of it.
The security-relevant distinction: an External target is retained VERBATIM and never owner-resolved or fetched, because auto-loading a file-supplied remote target is a zero-click external fetch.
type TargetMode = 'Internal' | 'External';