@docx-editor.dev/core 0.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +91 -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 +88 -0
- package/dist/binding.d.ts +88 -0
- package/dist/binding.js +1 -0
- package/dist/chunk-2DTQDAN7.js +2 -0
- package/dist/chunk-3JKQE4QJ.cjs +2 -0
- package/dist/chunk-4WWEDTTE.js +2 -0
- package/dist/chunk-6WIIHJFY.js +1 -0
- package/dist/chunk-7VQ7YLW3.cjs +1 -0
- package/dist/chunk-BS554B6D.cjs +2 -0
- package/dist/chunk-BZK64YU3.cjs +10 -0
- package/dist/chunk-CZFUMACF.cjs +12 -0
- package/dist/chunk-GQQOL4PV.js +1 -0
- package/dist/chunk-HBL3NNH5.cjs +1 -0
- package/dist/chunk-JQFDLZXQ.cjs +2 -0
- package/dist/chunk-O2WQY7IT.cjs +5 -0
- package/dist/chunk-PC76F2HR.cjs +1 -0
- package/dist/chunk-QRAUDDVY.js +2 -0
- package/dist/chunk-REZUWYB7.js +1 -0
- package/dist/chunk-STCITBJK.js +12 -0
- package/dist/chunk-TBBXCC7D.js +5 -0
- package/dist/chunk-TSXT537P.cjs +12 -0
- package/dist/chunk-WOGYVM3W.js +12 -0
- package/dist/chunk-Y7TA7TPF.js +10 -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 +117 -0
- package/dist/contracts/modules.d.ts +117 -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/custom-node-writes-YqISlSRl.d.cts +885 -0
- package/dist/custom-node-writes-ttKuYd0M.d.ts +885 -0
- package/dist/editor-B93PggnR.d.cts +1690 -0
- package/dist/editor-CFsSrxGR.d.ts +1690 -0
- package/dist/editor.cjs +1 -0
- package/dist/editor.css +7106 -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-C_YnqPhn.d.ts +203 -0
- package/dist/image-resources-DwpNnTg3.d.cts +203 -0
- package/dist/index-BjZelfWL.d.cts +2532 -0
- package/dist/index-DP1yJJ3R.d.ts +2532 -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 +2366 -0
- package/dist/layout.d.ts +2366 -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/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-BWVA3Q1p.d.ts +1255 -0
- package/dist/protocol-FAz_nPm_.d.cts +1255 -0
- package/dist/review-reads-CsoXSvUg.d.ts +401 -0
- package/dist/review-reads-qW0jvdF1.d.cts +401 -0
- package/dist/review-support-D64Wu236.d.cts +358 -0
- package/dist/review-support-nsLXC9jf.d.ts +358 -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-D_8RbPII.d.ts +427 -0
- package/dist/semantic-cell-selection-DwLB71fF.d.cts +427 -0
- package/dist/semantic-interaction-CXH2wFqi.d.ts +187 -0
- package/dist/semantic-interaction-DqcEonqD.d.cts +187 -0
- package/dist/semantic-paint-drawings-B5Wk2so5.d.ts +21 -0
- package/dist/semantic-paint-drawings-x8g44lD_.d.cts +21 -0
- package/dist/semantic-records-COVFW5aS.d.cts +1505 -0
- package/dist/semantic-records-zTC0_1zi.d.ts +1505 -0
- package/dist/shaped-measurer-AFnFxoot.d.cts +613 -0
- package/dist/shaped-measurer-CZXL-a6v.d.ts +613 -0
- package/dist/store.cjs +1 -0
- package/dist/store.d.cts +2153 -0
- package/dist/store.d.ts +2153 -0
- package/dist/store.js +1 -0
- package/dist/tree-op-types-DWLCllG7.d.ts +1486 -0
- package/dist/tree-op-types-UOXlRUqQ.d.cts +1486 -0
- package/dist/tree-session-BtVFXsF6.d.ts +580 -0
- package/dist/tree-session-DxIOHUOD.d.cts +580 -0
- package/dist/tree-store-B1lEkdXc.d.cts +273 -0
- package/dist/tree-store-BscFPB7x.d.ts +273 -0
- package/package.json +110 -8
- package/index.js +0 -1
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
import { e as OoxmlPart, O as OoxmlNode } from './ooxml-tree-BU0e3DVi.cjs';
|
|
2
|
+
import { O as OoxmlPackage } from './ooxml-package-CR5us6ad.cjs';
|
|
3
|
+
import { S as SupportedImageMime, b as ImageDecodePort } from './image-resources-DwpNnTg3.cjs';
|
|
4
|
+
import { m as DrawingTreeDocOp, a as TreeOpRejection, T as TreeDocOp } from './tree-op-types-UOXlRUqQ.cjs';
|
|
5
|
+
import { c as HeaderFooterKind, H as HeaderFooterVariant } from './hf-references-CgEOSw3i.cjs';
|
|
6
|
+
import { N as NoteKind } from './note-nodes-C6DLxlvj.cjs';
|
|
7
|
+
import { T as TreeModelChange, a as TreeStoryRef, b as TreeDocumentStore, c as TransactionContext, d as TransactOptions, S as SelectionMark, e as TreeDocumentCheckpoint } from './tree-store-B1lEkdXc.cjs';
|
|
8
|
+
|
|
9
|
+
/** A freshly allocated drawing property id, or the reason one could not be minted. */
|
|
10
|
+
type DrawingPropertyIdResult = {
|
|
11
|
+
readonly ok: true;
|
|
12
|
+
readonly id: number;
|
|
13
|
+
} | {
|
|
14
|
+
readonly ok: false;
|
|
15
|
+
readonly reason: 'invalidArgs';
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Mint a `wp:docPr` id unused anywhere in the package.
|
|
19
|
+
*
|
|
20
|
+
* Package-wide rather than per-part: Word treats these ids as document-global, and a collision
|
|
21
|
+
* makes it renumber on open.
|
|
22
|
+
*/
|
|
23
|
+
declare function allocateDrawingPropertyId(pkg: OoxmlPackage): DrawingPropertyIdResult;
|
|
24
|
+
/**
|
|
25
|
+
* Store raw bytes for a part and declare its exact content type.
|
|
26
|
+
*
|
|
27
|
+
* Does not create a relationship; callers decide which owner points at the part.
|
|
28
|
+
*/
|
|
29
|
+
declare function withBinaryPart(pkg: OoxmlPackage, partName: string, bytes: Uint8Array, contentType: string): OoxmlPackage;
|
|
30
|
+
/**
|
|
31
|
+
* Add image bytes to the package: the media part, its content type, and the relationship.
|
|
32
|
+
*
|
|
33
|
+
* All three together — bytes with no relationship are unreachable, and a relationship with no
|
|
34
|
+
* content-type record makes the package invalid.
|
|
35
|
+
*/
|
|
36
|
+
declare function withEmbeddedImage(pkg: OoxmlPackage, ownerPartName: string, input: Readonly<{
|
|
37
|
+
bytes: Uint8Array;
|
|
38
|
+
mime: SupportedImageMime;
|
|
39
|
+
}>): Readonly<{
|
|
40
|
+
ok: true;
|
|
41
|
+
pkg: OoxmlPackage;
|
|
42
|
+
partName: string;
|
|
43
|
+
relationshipId: string;
|
|
44
|
+
docPrId: number;
|
|
45
|
+
}> | Readonly<{
|
|
46
|
+
ok: false;
|
|
47
|
+
reason: 'invalidArgs' | 'invalid-image';
|
|
48
|
+
}>;
|
|
49
|
+
/** Remove an orphaned image media part after a package-wide internal relationship target check. */
|
|
50
|
+
declare function withoutUnreferencedImagePart(pkg: OoxmlPackage, partName: string): OoxmlPackage;
|
|
51
|
+
interface ExternalImageFetchPort {
|
|
52
|
+
/** Atomically resolve, reject non-public addresses, and connect for one HTTPS hop. */
|
|
53
|
+
requestPublicHttps(url: string, init: Readonly<{
|
|
54
|
+
redirect: 'manual';
|
|
55
|
+
signal: AbortSignal;
|
|
56
|
+
}>): Promise<Readonly<{
|
|
57
|
+
status: number;
|
|
58
|
+
location: string | null;
|
|
59
|
+
contentType: string | null;
|
|
60
|
+
body: AsyncIterable<Uint8Array>;
|
|
61
|
+
/** Must equal the requested absolute URL for this hop. */
|
|
62
|
+
connectedUrl: string;
|
|
63
|
+
}>>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Which of a family's four faces an embedded font relationship supplies. */
|
|
67
|
+
type FontStyleKey = 'regular' | 'bold' | 'italic' | 'boldItalic';
|
|
68
|
+
/**
|
|
69
|
+
* One font whose bytes travel inside the package.
|
|
70
|
+
*
|
|
71
|
+
* The family name is the DOCUMENT's and is not validated — it is attacker-controlled, so it is
|
|
72
|
+
* escaped into CSS and never registered globally on `document.fonts`, where it would shadow the
|
|
73
|
+
* host application's own fonts.
|
|
74
|
+
*/
|
|
75
|
+
interface EmbeddedFont {
|
|
76
|
+
/** The family as the document names it. Not validated against anything. */
|
|
77
|
+
readonly family: string;
|
|
78
|
+
readonly style: FontStyleKey;
|
|
79
|
+
/** Deobfuscated bytes, ready to be offered to a font validator. */
|
|
80
|
+
readonly bytes: Uint8Array;
|
|
81
|
+
/** The part they came from, for diagnostics. */
|
|
82
|
+
readonly partName: string;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Undo Word's embedded-font obfuscation (ECMA-376 Part 4 §2.8.1).
|
|
86
|
+
*
|
|
87
|
+
* The key is the `w:fontKey` GUID's 16 bytes in REVERSED order — not the per-group
|
|
88
|
+
* little-endian reading a GUID usually gets. It is XORed over the first 32 bytes of the
|
|
89
|
+
* part, applied twice. Getting the order wrong produces a font that looks corrupt rather
|
|
90
|
+
* than obfuscated, which is indistinguishable from a damaged file at the point it fails.
|
|
91
|
+
*
|
|
92
|
+
* Pure XOR, so the same operation obfuscates and deobfuscates.
|
|
93
|
+
*/
|
|
94
|
+
declare function deobfuscateFont(bytes: Uint8Array, fontKey: string): Uint8Array | null;
|
|
95
|
+
/** Limits and instrumentation for reading embedded fonts out of a package. */
|
|
96
|
+
interface ReadEmbeddedFontsOptions {
|
|
97
|
+
/** Refuse a font part larger than this. Defaults to 16 MB. */
|
|
98
|
+
readonly maxFontBytes?: number;
|
|
99
|
+
/** Refuse more than this many fonts. Defaults to 64. */
|
|
100
|
+
readonly maxFonts?: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Every font the package embeds, deobfuscated.
|
|
104
|
+
*
|
|
105
|
+
* Silently skips anything malformed rather than rejecting the document: a broken font table
|
|
106
|
+
* is a reason to fall back to substitution, never a reason to refuse to open the file.
|
|
107
|
+
*/
|
|
108
|
+
declare function readEmbeddedFonts(pkg: OoxmlPackage, fontTable: OoxmlPart | undefined, options?: ReadEmbeddedFontsOptions): EmbeddedFont[];
|
|
109
|
+
|
|
110
|
+
/** The two list kinds a toolbar offers. */
|
|
111
|
+
type ListKind = 'bullet' | 'ordered';
|
|
112
|
+
interface EnsuredListDefinition {
|
|
113
|
+
readonly pkg: OoxmlPackage;
|
|
114
|
+
/** The `w:numId` a paragraph's `w:numPr` should name. */
|
|
115
|
+
readonly numId: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Find or create a list definition of `kind`, returning the package that holds it.
|
|
119
|
+
*
|
|
120
|
+
* An existing definition of the same kind is REUSED rather than duplicated: Word does the
|
|
121
|
+
* same, and a document that gains one `w:abstractNum` per toggled paragraph becomes
|
|
122
|
+
* unreadable. Returns null only when the part cannot be built at all.
|
|
123
|
+
*/
|
|
124
|
+
declare function ensureListDefinition(pkg: OoxmlPackage, kind: ListKind): EnsuredListDefinition | null;
|
|
125
|
+
/**
|
|
126
|
+
* Declare `level` in the definition `numId` names, with Word's default format for that
|
|
127
|
+
* level, or refuse.
|
|
128
|
+
*
|
|
129
|
+
* Word never greys Increase Indent out on a list item: demoting past the deepest level a
|
|
130
|
+
* `w:abstractNum` declares makes Word DEFINE the level, cycling its stock bullets
|
|
131
|
+
* (Symbol •, Courier `o`, Wingdings ▪) or number formats (decimal, lowerLetter,
|
|
132
|
+
* lowerRoman) by depth. This is that write. An already-declared level returns the package
|
|
133
|
+
* unchanged, so callers may ask first and act second without a second lookup.
|
|
134
|
+
*
|
|
135
|
+
* A delegating definition (`w:numStyleLink`, 17.9.21) is refused: its levels live on the
|
|
136
|
+
* linked style's definition, and a `w:lvl` grafted here would be shadowed the moment the
|
|
137
|
+
* link resolves.
|
|
138
|
+
*/
|
|
139
|
+
declare function ensureNumberingLevel(pkg: OoxmlPackage, numId: string, level: number, kind: ListKind): OoxmlPackage | null;
|
|
140
|
+
|
|
141
|
+
/** One bookmark's name and the range its marker pair currently encloses. */
|
|
142
|
+
interface BookmarkAnchor {
|
|
143
|
+
readonly name: string;
|
|
144
|
+
/** Canonical node id of the paragraph the marker sits in. */
|
|
145
|
+
readonly paragraphId: string;
|
|
146
|
+
/** UTF-16 offset within that paragraph, in `paragraphTextOf`'s vocabulary. */
|
|
147
|
+
readonly offset: number;
|
|
148
|
+
}
|
|
149
|
+
/** Bookmarks by name. A name the document declares twice resolves to the first in order. */
|
|
150
|
+
type BookmarkIndex = ReadonlyMap<string, BookmarkAnchor>;
|
|
151
|
+
/**
|
|
152
|
+
* Build `name -> position` for every bookmark start in a part, in document order.
|
|
153
|
+
*
|
|
154
|
+
* FIRST IN DOCUMENT ORDER WINS on a duplicate name. Word treats a repeated bookmark name as
|
|
155
|
+
* the same bookmark and jumps to the first, and the alternative — last-wins — makes a jump
|
|
156
|
+
* target move when an edit far away happens to duplicate a name.
|
|
157
|
+
*
|
|
158
|
+
* The offset is measured the way the ops measure: text of the runs before the marker inside
|
|
159
|
+
* its own paragraph, hyperlink runs included, so the position a jump places the caret at is a
|
|
160
|
+
* position `setSelection` accepts.
|
|
161
|
+
*/
|
|
162
|
+
declare function buildBookmarkIndex(part: OoxmlPart): BookmarkIndex;
|
|
163
|
+
|
|
164
|
+
/** Lifecycle impact — furniture always reaches multiple pages; never narrower than flow-structural. */
|
|
165
|
+
type HeaderFooterLifecycleImpact = 'flow-structural' | 'global';
|
|
166
|
+
/**
|
|
167
|
+
* A header/footer lifecycle mutation: create, remove, or relink a variant.
|
|
168
|
+
*
|
|
169
|
+
* Package-level, like note lifecycle: each touches the document, the header/footer part, the
|
|
170
|
+
* relationships and `[Content_Types].xml` together.
|
|
171
|
+
*/
|
|
172
|
+
type HeaderFooterLifecycleOp = {
|
|
173
|
+
readonly op: 'createHeaderFooter';
|
|
174
|
+
readonly sectionIndex: number;
|
|
175
|
+
readonly kind: HeaderFooterKind;
|
|
176
|
+
readonly variant: HeaderFooterVariant;
|
|
177
|
+
/** When true, also set section `w:titlePg` in the same package transaction. */
|
|
178
|
+
readonly titlePage?: boolean;
|
|
179
|
+
/** When true, also set document `w:evenAndOddHeaders` in the same package transaction. */
|
|
180
|
+
readonly evenAndOddHeaders?: boolean;
|
|
181
|
+
} | {
|
|
182
|
+
readonly op: 'deleteHeaderFooter';
|
|
183
|
+
readonly sectionIndex: number;
|
|
184
|
+
readonly kind: HeaderFooterKind;
|
|
185
|
+
readonly variant: HeaderFooterVariant;
|
|
186
|
+
} | {
|
|
187
|
+
readonly op: 'linkToPrevious';
|
|
188
|
+
readonly sectionIndex: number;
|
|
189
|
+
readonly kind: HeaderFooterKind;
|
|
190
|
+
readonly variant: HeaderFooterVariant;
|
|
191
|
+
} | {
|
|
192
|
+
readonly op: 'unlinkFromPrevious';
|
|
193
|
+
readonly sectionIndex: number;
|
|
194
|
+
readonly kind: HeaderFooterKind;
|
|
195
|
+
readonly variant: HeaderFooterVariant;
|
|
196
|
+
} | {
|
|
197
|
+
readonly op: 'setSectionFurnitureOptions';
|
|
198
|
+
readonly sectionIndex?: number;
|
|
199
|
+
readonly titlePage?: boolean;
|
|
200
|
+
readonly evenAndOddHeaders?: boolean;
|
|
201
|
+
readonly headerDistanceTwips?: number;
|
|
202
|
+
readonly footerDistanceTwips?: number;
|
|
203
|
+
};
|
|
204
|
+
/** Why a header/footer lifecycle op was refused. */
|
|
205
|
+
type HeaderFooterLifecycleRejection = 'invalidArgs' | 'tree-invariant';
|
|
206
|
+
/** A new package, or a typed rejection. Pure and all-or-nothing — no partial writes. */
|
|
207
|
+
type HeaderFooterLifecycleResult = {
|
|
208
|
+
readonly ok: true;
|
|
209
|
+
readonly package: OoxmlPackage;
|
|
210
|
+
readonly impact: HeaderFooterLifecycleImpact;
|
|
211
|
+
readonly createdRId?: string;
|
|
212
|
+
readonly createdPartName?: string;
|
|
213
|
+
} | {
|
|
214
|
+
readonly ok: false;
|
|
215
|
+
readonly reason: HeaderFooterLifecycleRejection;
|
|
216
|
+
readonly detail?: string;
|
|
217
|
+
};
|
|
218
|
+
/** Whether an op is a header/footer lifecycle op rather than a story-level one. */
|
|
219
|
+
declare function isHeaderFooterLifecycleOp(op: {
|
|
220
|
+
readonly op: string;
|
|
221
|
+
}): op is HeaderFooterLifecycleOp;
|
|
222
|
+
/**
|
|
223
|
+
* Apply one furniture lifecycle op atomically. Rejected ops leave the input package
|
|
224
|
+
* untouched (pure function — callers discard the result).
|
|
225
|
+
*/
|
|
226
|
+
declare function applyHeaderFooterLifecycleOp(pkg: OoxmlPackage, op: HeaderFooterLifecycleOp): HeaderFooterLifecycleResult;
|
|
227
|
+
|
|
228
|
+
/** Cap on nodes visited while scanning for note references across stories. */
|
|
229
|
+
declare const MAX_NOTE_REFERENCE_SCAN = 20000;
|
|
230
|
+
/**
|
|
231
|
+
* Cap on XML parts walked in one package-wide note-reference scan (N/N+1 gate).
|
|
232
|
+
* Soft targets are not allowed: exceeding this marks the shared budget truncated.
|
|
233
|
+
*/
|
|
234
|
+
declare const MAX_NOTE_REFERENCE_PARTS = 256;
|
|
235
|
+
/**
|
|
236
|
+
* A load-time note problem worth reporting.
|
|
237
|
+
*
|
|
238
|
+
* `dangling-note-reference` is a citation pointing at no note; `note-reference-scan-truncated`
|
|
239
|
+
* says the scan hit its budget, so absence of further diagnostics is not proof of correctness.
|
|
240
|
+
*/
|
|
241
|
+
type NoteDiagnosticCode = 'dangling-note-reference' | 'note-reference-scan-truncated';
|
|
242
|
+
/**
|
|
243
|
+
* Load-time note diagnostics. Array API preserved; truncation is signaled as a typed
|
|
244
|
+
* entry rather than by throwing or rejecting the package.
|
|
245
|
+
*/
|
|
246
|
+
type NoteDiagnostic = {
|
|
247
|
+
readonly code: 'dangling-note-reference';
|
|
248
|
+
readonly noteKind: NoteKind;
|
|
249
|
+
readonly noteId: number;
|
|
250
|
+
/** Paragraph / container node id when known. */
|
|
251
|
+
readonly sourceNodeId?: string;
|
|
252
|
+
} | {
|
|
253
|
+
/** Hard visited/part budget or soft hit cap stopped the scan before full coverage. */
|
|
254
|
+
readonly code: 'note-reference-scan-truncated';
|
|
255
|
+
};
|
|
256
|
+
/** One note reference found in a story, with where it sits. */
|
|
257
|
+
interface NoteReferenceHit {
|
|
258
|
+
readonly noteKind: NoteKind;
|
|
259
|
+
readonly noteId: number;
|
|
260
|
+
readonly nodeId: string;
|
|
261
|
+
readonly paragraphId: string;
|
|
262
|
+
/** Canonical UTF-16 atom offset within {@link paragraphId} (U+FFFC model). */
|
|
263
|
+
readonly atomOffset: number;
|
|
264
|
+
readonly customMarkFollows: boolean;
|
|
265
|
+
/** Canonical part name that owns this reference. */
|
|
266
|
+
readonly partName: string;
|
|
267
|
+
}
|
|
268
|
+
/** Mutable visited-node + part budget shared across parts / package snapshots. */
|
|
269
|
+
interface NoteReferenceScanBudget {
|
|
270
|
+
visited: number;
|
|
271
|
+
readonly maxVisited: number;
|
|
272
|
+
parts: number;
|
|
273
|
+
readonly maxParts: number;
|
|
274
|
+
/** Set when a walk stops before finishing because a cap was hit. */
|
|
275
|
+
truncated: boolean;
|
|
276
|
+
}
|
|
277
|
+
/** A bounded budget for scanning note references, so a crafted document cannot stall a load. */
|
|
278
|
+
declare function createNoteReferenceScanBudget(maxVisited?: number, maxParts?: number): NoteReferenceScanBudget;
|
|
279
|
+
/**
|
|
280
|
+
* Resolve the footnotes or endnotes part via safe Internal document relationships.
|
|
281
|
+
*
|
|
282
|
+
* Unusable matching relationships (External, unsafe target, missing part, wrong root)
|
|
283
|
+
* are skipped — never fetched, never accepted — so a decoy first match cannot hide a
|
|
284
|
+
* later usable Internal notes part (same continue-past-bad pattern as settingsPartOf).
|
|
285
|
+
*/
|
|
286
|
+
declare function resolveNotesPart(pkg: OoxmlPackage, noteKind: NoteKind): OoxmlPart | null;
|
|
287
|
+
/**
|
|
288
|
+
* Walk a part for addressable typed note references. Bounded by visited nodes; skips deep
|
|
289
|
+
* hostile nesting by marking the shared budget truncated. When `budget` is supplied it is
|
|
290
|
+
* shared and mutated in place. Hits are segment-aligned (`segmentsOf`); demoted wrappers
|
|
291
|
+
* never invent atomOffsets.
|
|
292
|
+
*/
|
|
293
|
+
declare function collectNoteReferences(part: OoxmlPart, options?: {
|
|
294
|
+
readonly maxHits?: number;
|
|
295
|
+
readonly budget?: NoteReferenceScanBudget;
|
|
296
|
+
}): readonly NoteReferenceHit[];
|
|
297
|
+
/** Collect references across every XML part under one shared part + visited-node budget. */
|
|
298
|
+
declare function collectPackageNoteReferences(pkg: OoxmlPackage, options?: {
|
|
299
|
+
readonly budget?: NoteReferenceScanBudget;
|
|
300
|
+
/** Soft hit cap for diagnostics. Omit / Infinity for mutation scans. */
|
|
301
|
+
readonly maxHits?: number;
|
|
302
|
+
}): readonly NoteReferenceHit[];
|
|
303
|
+
/**
|
|
304
|
+
* Load diagnostics for dangling note references. Fail-open: never throws or mutates;
|
|
305
|
+
* returns diagnostics for callers to surface. Does not invent missing note bodies.
|
|
306
|
+
*
|
|
307
|
+
* When the hard visited/part budget truncates or the soft hit cap binds, appends a single
|
|
308
|
+
* `note-reference-scan-truncated` entry so incomplete coverage is visible without breaking
|
|
309
|
+
* consumers that filter on `dangling-note-reference`.
|
|
310
|
+
*/
|
|
311
|
+
declare function diagnoseNoteReferences(pkg: OoxmlPackage): readonly NoteDiagnostic[];
|
|
312
|
+
/** Whether a notes-part root contains a note with the given id (any type). */
|
|
313
|
+
declare function notesPartHasId(part: OoxmlPart, noteId: number): boolean;
|
|
314
|
+
/** List normal (body) note ids in document order, bounded. */
|
|
315
|
+
declare function normalNoteIds(part: OoxmlPart): readonly number[];
|
|
316
|
+
|
|
317
|
+
/** How far a note lifecycle op reaches — which parts a caller must expect to have changed. */
|
|
318
|
+
type NoteLifecycleImpact = 'flow-structural' | 'global';
|
|
319
|
+
/**
|
|
320
|
+
* A note lifecycle mutation: insert, delete, convert, or set properties.
|
|
321
|
+
*
|
|
322
|
+
* Package-level rather than story-level, because every one of these touches the main document,
|
|
323
|
+
* the notes part, the document relationships AND `[Content_Types].xml` together.
|
|
324
|
+
*/
|
|
325
|
+
type NoteLifecycleOp = {
|
|
326
|
+
readonly op: 'insertNote';
|
|
327
|
+
readonly noteKind: NoteKind;
|
|
328
|
+
readonly paragraphId: string;
|
|
329
|
+
readonly offset: number;
|
|
330
|
+
} | {
|
|
331
|
+
readonly op: 'deleteNote';
|
|
332
|
+
readonly noteKind: NoteKind;
|
|
333
|
+
readonly noteId: number;
|
|
334
|
+
} | {
|
|
335
|
+
readonly op: 'convertNote';
|
|
336
|
+
readonly fromKind: NoteKind;
|
|
337
|
+
readonly noteId: number;
|
|
338
|
+
} | {
|
|
339
|
+
/**
|
|
340
|
+
* Convert every normal note of `fromKind` in document order. One atomic package
|
|
341
|
+
* transaction / undo unit with the same validation as repeated `convertNote`.
|
|
342
|
+
*/
|
|
343
|
+
readonly op: 'convertAllNotes';
|
|
344
|
+
readonly fromKind: NoteKind;
|
|
345
|
+
} | {
|
|
346
|
+
readonly op: 'setNoteProperties';
|
|
347
|
+
readonly scope: 'document' | 'section';
|
|
348
|
+
readonly sectionIndex?: number;
|
|
349
|
+
readonly footnote?: {
|
|
350
|
+
readonly numFmt?: string;
|
|
351
|
+
readonly numRestart?: string;
|
|
352
|
+
readonly position?: string;
|
|
353
|
+
readonly numStart?: number;
|
|
354
|
+
};
|
|
355
|
+
readonly endnote?: {
|
|
356
|
+
readonly numFmt?: string;
|
|
357
|
+
readonly numRestart?: string;
|
|
358
|
+
readonly position?: string;
|
|
359
|
+
readonly numStart?: number;
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
/** Why a note lifecycle op was refused. */
|
|
363
|
+
type NoteLifecycleRejection = 'invalidArgs' | 'tree-invariant';
|
|
364
|
+
/**
|
|
365
|
+
* A new package, or a typed rejection.
|
|
366
|
+
*
|
|
367
|
+
* Application is PURE and all-or-nothing: there are no partial writes, so a refused op leaves the
|
|
368
|
+
* caller's package untouched rather than half-migrated across four parts.
|
|
369
|
+
*/
|
|
370
|
+
type NoteLifecycleResult = {
|
|
371
|
+
readonly ok: true;
|
|
372
|
+
readonly package: OoxmlPackage;
|
|
373
|
+
readonly impact: NoteLifecycleImpact;
|
|
374
|
+
readonly noteId?: number;
|
|
375
|
+
readonly noteKind?: NoteKind;
|
|
376
|
+
readonly createdPartName?: string;
|
|
377
|
+
} | {
|
|
378
|
+
readonly ok: false;
|
|
379
|
+
readonly reason: NoteLifecycleRejection;
|
|
380
|
+
readonly detail?: string;
|
|
381
|
+
};
|
|
382
|
+
/** Whether an op is a note lifecycle op rather than a story-level one. */
|
|
383
|
+
declare function isNoteLifecycleOp(op: {
|
|
384
|
+
readonly op: string;
|
|
385
|
+
}): op is NoteLifecycleOp;
|
|
386
|
+
/** Attribution and limits applied to one note lifecycle op. */
|
|
387
|
+
interface NoteLifecycleOptions {
|
|
388
|
+
/**
|
|
389
|
+
* Shared part + visited-node budget for reference scans. When omitted a fresh default
|
|
390
|
+
* budget is used. Truncation rejects the op with the original package unchanged.
|
|
391
|
+
*/
|
|
392
|
+
readonly scanBudget?: NoteReferenceScanBudget;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Apply one note lifecycle op atomically. Rejected ops leave the input package untouched.
|
|
396
|
+
*/
|
|
397
|
+
declare function applyNoteLifecycleOp(pkg: OoxmlPackage, op: NoteLifecycleOp, options?: NoteLifecycleOptions): NoteLifecycleResult;
|
|
398
|
+
/**
|
|
399
|
+
* How deleting text cascades into the notes it referenced.
|
|
400
|
+
*
|
|
401
|
+
* A note's body and the citation reaching it are one thing to a reader, so removing the reference
|
|
402
|
+
* must remove the body too or the notes part keeps an entry nothing points at.
|
|
403
|
+
*/
|
|
404
|
+
interface CascadeDeletedNoteReferencesOptions {
|
|
405
|
+
/**
|
|
406
|
+
* Independent full budgets per snapshot. When omitted each snapshot gets its own
|
|
407
|
+
* default budget — never share one counter across before/after walks.
|
|
408
|
+
*/
|
|
409
|
+
readonly beforeBudget?: NoteReferenceScanBudget;
|
|
410
|
+
readonly afterBudget?: NoteReferenceScanBudget;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Remove note bodies for references that disappeared between two package snapshots.
|
|
414
|
+
* Used when `deleteText` or `deleteBlock` removes a `noteReference` atom so body+ref stay
|
|
415
|
+
* one undo unit.
|
|
416
|
+
*
|
|
417
|
+
* Each snapshot gets an independent full visited/part budget. If either scan truncates
|
|
418
|
+
* the cascade fails closed so a hostile package cannot skip body deletion silently —
|
|
419
|
+
* without accidentally halving capacity by charging both walks to one counter.
|
|
420
|
+
*/
|
|
421
|
+
declare function cascadeDeletedNoteReferences(before: OoxmlPackage, after: OoxmlPackage, options?: CascadeDeletedNoteReferencesOptions): OoxmlPackage | null;
|
|
422
|
+
|
|
423
|
+
/** What the document asks for. Every field defaults to "the document said nothing". */
|
|
424
|
+
interface DocumentTrackingSettings {
|
|
425
|
+
/** `w:trackRevisions` — the document asks for edits to be tracked. */
|
|
426
|
+
readonly trackRevisions: boolean;
|
|
427
|
+
/**
|
|
428
|
+
* `w:documentProtection/@w:edit="trackedChanges"` — tracking may not be turned OFF.
|
|
429
|
+
*
|
|
430
|
+
* Advisory. Presenting it as enforcement would be a lie about a file anyone can edit.
|
|
431
|
+
*/
|
|
432
|
+
readonly restrictedToTrackedChanges: boolean;
|
|
433
|
+
/** `w:doNotTrackMoves` — write a move as a delete and an insert. */
|
|
434
|
+
readonly doNotTrackMoves: boolean;
|
|
435
|
+
/** `w:doNotTrackFormatting` — apply formatting without recording a `w:rPrChange`. */
|
|
436
|
+
readonly doNotTrackFormatting: boolean;
|
|
437
|
+
}
|
|
438
|
+
/** The frozen "nothing is tracked" settings — what a document with no `w:trackChanges` gets. */
|
|
439
|
+
declare const NO_TRACKING_SETTINGS: DocumentTrackingSettings;
|
|
440
|
+
/** Read the tracking settings from a `settings.xml` root, or the defaults when it has none. */
|
|
441
|
+
declare function readTrackingSettings(settingsRoot: OoxmlNode | null | undefined): DocumentTrackingSettings;
|
|
442
|
+
|
|
443
|
+
interface InsertImageInput {
|
|
444
|
+
readonly paragraphId: string;
|
|
445
|
+
readonly offset: number;
|
|
446
|
+
readonly bytes: Uint8Array;
|
|
447
|
+
readonly mime: SupportedImageMime;
|
|
448
|
+
readonly widthPoints: number;
|
|
449
|
+
readonly heightPoints: number;
|
|
450
|
+
readonly decodePort: ImageDecodePort;
|
|
451
|
+
readonly expectedPackageRevision: number;
|
|
452
|
+
readonly commitGuard?: () => boolean;
|
|
453
|
+
readonly title?: string;
|
|
454
|
+
readonly description?: string;
|
|
455
|
+
readonly hyperlink?: string;
|
|
456
|
+
}
|
|
457
|
+
interface ReplaceImageOptions {
|
|
458
|
+
readonly expectedPackageRevision: number;
|
|
459
|
+
readonly commitGuard?: () => boolean;
|
|
460
|
+
}
|
|
461
|
+
type ImageIntentResult = (Extract<PackageTransactResult, {
|
|
462
|
+
ok: true;
|
|
463
|
+
}> & {
|
|
464
|
+
readonly drawingNodeId?: string;
|
|
465
|
+
readonly mediaPartName?: string;
|
|
466
|
+
}) | Extract<PackageTransactResult, {
|
|
467
|
+
ok: false;
|
|
468
|
+
}>;
|
|
469
|
+
interface ApplyImagePropertiesInput {
|
|
470
|
+
readonly drawingNodeId: string;
|
|
471
|
+
readonly ops: readonly DrawingTreeDocOp[];
|
|
472
|
+
readonly hyperlink: string | null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
type NoteCascadeFn = (before: OoxmlPackage, after: OoxmlPackage) => OoxmlPackage | null;
|
|
476
|
+
/**
|
|
477
|
+
* Editable story target.
|
|
478
|
+
*
|
|
479
|
+
* Body and headerFooter mirror `EditorScope`. Notes use one lazy store per notes part
|
|
480
|
+
* (`notesPart`) — not one store per note — resolved through safe document relationships.
|
|
481
|
+
*/
|
|
482
|
+
type StoryScope = {
|
|
483
|
+
readonly kind: 'body';
|
|
484
|
+
} | {
|
|
485
|
+
readonly kind: 'headerFooter';
|
|
486
|
+
readonly rId: string;
|
|
487
|
+
} | {
|
|
488
|
+
readonly kind: 'notesPart';
|
|
489
|
+
readonly noteKind: NoteKind;
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* Why a story scope could not be resolved to a part.
|
|
493
|
+
*
|
|
494
|
+
* Several of these are FILE-hostile shapes rather than caller mistakes:
|
|
495
|
+
* `external-relationship` and `bad-relationship-target` are how a crafted document tries to
|
|
496
|
+
* point a story at something outside the package, and both are refused rather than followed.
|
|
497
|
+
*/
|
|
498
|
+
type StoryTargetRejection = 'unknown-scope' | 'dangling-relationship' | 'wrong-relationship-type' | 'external-relationship' | 'bad-relationship-target' | 'missing-part' | 'not-a-story-part' | 'too-many-story-stores';
|
|
499
|
+
/** A story scope resolved to a part, or the typed reason it could not be. */
|
|
500
|
+
type StoryResolveResult = {
|
|
501
|
+
readonly ok: true;
|
|
502
|
+
readonly story: TreeStoryRef;
|
|
503
|
+
readonly store: TreeDocumentStore;
|
|
504
|
+
} | {
|
|
505
|
+
readonly ok: false;
|
|
506
|
+
readonly reason: StoryTargetRejection;
|
|
507
|
+
readonly detail?: string;
|
|
508
|
+
};
|
|
509
|
+
/** Whether a package-level transaction committed, or why it was refused. */
|
|
510
|
+
type PackageTransactResult = {
|
|
511
|
+
readonly ok: true;
|
|
512
|
+
readonly change: TreeModelChange | null;
|
|
513
|
+
} | {
|
|
514
|
+
readonly ok: false;
|
|
515
|
+
readonly reason: StoryTargetRejection | TreeOpRejection;
|
|
516
|
+
readonly detail?: string;
|
|
517
|
+
};
|
|
518
|
+
/** Cap on simultaneously opened editable story stores (body + HF parts). Fail closed. */
|
|
519
|
+
declare const DEFAULT_MAX_EDITABLE_STORY_PARTS = 64;
|
|
520
|
+
/** How a package store is constructed: limits, history depth, and review contributions. */
|
|
521
|
+
interface TreePackageStoreOptions {
|
|
522
|
+
readonly historyLimit?: number;
|
|
523
|
+
/** Bound on opened story stores; defaults to {@link DEFAULT_MAX_EDITABLE_STORY_PARTS}. */
|
|
524
|
+
readonly maxEditableStoryParts?: number;
|
|
525
|
+
/**
|
|
526
|
+
* Test seam for note-reference cascade after `deleteText` / `deleteBlock`. Production uses
|
|
527
|
+
* {@link cascadeDeletedNoteReferences}.
|
|
528
|
+
*/
|
|
529
|
+
readonly cascadeDeletedNoteReferences?: NoteCascadeFn;
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Package-level mutation authority: routes `TreeDocOp`s to the store for a story part,
|
|
533
|
+
* publishes one ModelChange / undo unit per transaction, and keeps `currentPackage()`
|
|
534
|
+
* coherent for save/reopen.
|
|
535
|
+
*/
|
|
536
|
+
declare class TreePackageStore {
|
|
537
|
+
private pkg;
|
|
538
|
+
private packageRev;
|
|
539
|
+
private readonly body;
|
|
540
|
+
/** Opened non-body story stores, keyed by canonical part name. */
|
|
541
|
+
private readonly stories;
|
|
542
|
+
/** rId → part name for opened HF stores (and resolved targets). */
|
|
543
|
+
private readonly rIdToPartName;
|
|
544
|
+
private readonly undoOrder;
|
|
545
|
+
private readonly redoOrder;
|
|
546
|
+
private readonly subscribers;
|
|
547
|
+
private readonly historyLimit;
|
|
548
|
+
private readonly maxEditableStoryParts;
|
|
549
|
+
private readonly cascadeNoteReferences;
|
|
550
|
+
private lastChange;
|
|
551
|
+
/**
|
|
552
|
+
* Hyperlink externals minted via {@link replacePackageShell} (outside package history).
|
|
553
|
+
* Re-applied on snapshot install so lifecycle undo cannot drop shell `r:id`s; not used for
|
|
554
|
+
* lifecycle-cloned owned relationships, which history snapshots already restore.
|
|
555
|
+
*/
|
|
556
|
+
private shellHyperlinks;
|
|
557
|
+
/**
|
|
558
|
+
* Open IME composition session. Captures the package/story checkpoint at begin so a
|
|
559
|
+
* mid-composition note-ref cascade can promote the whole composition to one package
|
|
560
|
+
* undo unit (or restore on cancel) instead of a story-only pointer that orphans note bodies.
|
|
561
|
+
*/
|
|
562
|
+
private compositionSession;
|
|
563
|
+
private commitCounter;
|
|
564
|
+
constructor(pkg: OoxmlPackage, main: OoxmlPart, options?: TreePackageStoreOptions);
|
|
565
|
+
get packageRevision(): number;
|
|
566
|
+
get canUndo(): boolean;
|
|
567
|
+
get canRedo(): boolean;
|
|
568
|
+
get lastModelChange(): TreeModelChange | null;
|
|
569
|
+
/** Body store — independent revision/index from every HF store. */
|
|
570
|
+
bodyStore(): TreeDocumentStore;
|
|
571
|
+
/**
|
|
572
|
+
* The current package with every opened story store's part merged in.
|
|
573
|
+
* Pure snapshot of authority; callers must not mutate.
|
|
574
|
+
*
|
|
575
|
+
* Stores whose parts are absent from the package shell (deleted furniture/notes)
|
|
576
|
+
* stay parked for undo/redo identity but are not re-injected into the snapshot.
|
|
577
|
+
*/
|
|
578
|
+
currentPackage(): OoxmlPackage;
|
|
579
|
+
subscribe(listener: (change: TreeModelChange) => void): () => void;
|
|
580
|
+
/**
|
|
581
|
+
* Resolve a story scope to its store. Fail closed for dangling / wrong-typed / missing
|
|
582
|
+
* targets — layout may fail open on the same rId, but mutation must not invent a part.
|
|
583
|
+
*/
|
|
584
|
+
resolveStory(scope: StoryScope): StoryResolveResult;
|
|
585
|
+
/** Current part for a scope, or null when the target is refused. */
|
|
586
|
+
partFor(scope: StoryScope): OoxmlPart | null;
|
|
587
|
+
/** Per-story revision, or null when the target is refused. */
|
|
588
|
+
revisionFor(scope: StoryScope): number | null;
|
|
589
|
+
/**
|
|
590
|
+
* Commit ops against one story as ONE transaction / undo unit / ModelChange.
|
|
591
|
+
* Header/footer and notes-part commits publish `impact: 'global'`.
|
|
592
|
+
* Deleting a `noteReference` via `deleteText` or a block subtree via `deleteBlock`
|
|
593
|
+
* cascades the note body in the same package undo unit.
|
|
594
|
+
*/
|
|
595
|
+
transact(scope: StoryScope, build: (ctx: TransactionContext) => void, options?: Omit<TransactOptions, 'story' | 'minimumImpact'>): PackageTransactResult;
|
|
596
|
+
/** Whether a package-wide IME composition session is open on any story. */
|
|
597
|
+
compositionSessionOpen(): boolean;
|
|
598
|
+
beginComposition(scope: StoryScope, selectionBefore?: SelectionMark | null): boolean;
|
|
599
|
+
endComposition(): void;
|
|
600
|
+
cancelComposition(): void;
|
|
601
|
+
/**
|
|
602
|
+
* Commit one furniture or note lifecycle op as a single ModelChange / undo unit that
|
|
603
|
+
* restores the entire package atomically (parts, rels, content-types, settings).
|
|
604
|
+
*/
|
|
605
|
+
applyLifecycleOp(op: HeaderFooterLifecycleOp | NoteLifecycleOp | TreeDocOp): PackageTransactResult;
|
|
606
|
+
undo(): TreeModelChange | null;
|
|
607
|
+
redo(): TreeModelChange | null;
|
|
608
|
+
selectionForUndo(): SelectionMark | null;
|
|
609
|
+
selectionForRedo(): SelectionMark | null;
|
|
610
|
+
/** How many story stores are open (body counts as one). */
|
|
611
|
+
openedStoryCount(): number;
|
|
612
|
+
/**
|
|
613
|
+
* Insert a validated raster image as one package undo unit (task 12).
|
|
614
|
+
*/
|
|
615
|
+
insertImage(scope: StoryScope, input: InsertImageInput): Promise<ImageIntentResult>;
|
|
616
|
+
/** Replace a picture drawing's embedded media in one package undo unit. */
|
|
617
|
+
replaceImage(scope: StoryScope, drawingNodeId: string, bytes: Uint8Array, mime: SupportedImageMime, decodePort: ImageDecodePort, options: ReplaceImageOptions): Promise<ImageIntentResult>;
|
|
618
|
+
/** Delete a picture drawing and collect orphaned media in one package undo unit. */
|
|
619
|
+
deleteImage(scope: StoryScope, drawingNodeId: string): ImageIntentResult;
|
|
620
|
+
/** Fetch external bytes explicitly and embed them; no fetch on open/load. */
|
|
621
|
+
embedExternalImage(scope: StoryScope, drawingNodeId: string, url: string, port: ExternalImageFetchPort, signal: AbortSignal, decodePort: ImageDecodePort): Promise<ImageIntentResult>;
|
|
622
|
+
/** Metadata plus hyperlink target creation in one package transaction. */
|
|
623
|
+
setDrawingMetadataWithHyperlink(scope: StoryScope, drawingNodeId: string, title: string, description: string, hyperlink: string | null): ImageIntentResult;
|
|
624
|
+
/** Properties batch with hyperlink relationship create/update/remove in one package unit. */
|
|
625
|
+
applyImageProperties(scope: StoryScope, input: ApplyImagePropertiesInput): ImageIntentResult;
|
|
626
|
+
/**
|
|
627
|
+
* Promote a story transaction that wrote package bytes to one package undo pointer.
|
|
628
|
+
* Used by image intents and note-reference cascade.
|
|
629
|
+
*/
|
|
630
|
+
promoteStoryTransactionToPackageUnit(beforePackage: OoxmlPackage, store: TreeDocumentStore, checkpoint: TreeDocumentCheckpoint, beforeDepth: number): TreeModelChange;
|
|
631
|
+
/**
|
|
632
|
+
* Publish a story transaction the coordinator did not run.
|
|
633
|
+
*
|
|
634
|
+
* Comment writes commit straight on the story store and hand the new shell back through
|
|
635
|
+
* {@link replacePackageShell}, so they never pass through `applyTreeOps` — the one place
|
|
636
|
+
* every other edit bumps the revision and publishes. The subscriber channel therefore
|
|
637
|
+
* stayed silent for a comment: `Editor.on('change')` never fired, and a review rail keyed
|
|
638
|
+
* on it only caught up on the next unrelated caret move, so a reply someone had just
|
|
639
|
+
* written was invisible until they clicked elsewhere.
|
|
640
|
+
*
|
|
641
|
+
* The STORY's own change is published rather than a synthetic one, because it carries the
|
|
642
|
+
* dirty anchor paragraphs and the `text-local` impact the marker ops computed; a synthetic
|
|
643
|
+
* `global` would make every comment cost a full relayout. History is deliberately
|
|
644
|
+
* untouched — the story transaction already recorded its undo entry, exactly as
|
|
645
|
+
* `applyTreeOps` leaves a non-cascading story edit.
|
|
646
|
+
*
|
|
647
|
+
* A `null` change is an identity no-op (nothing was written), and publishes nothing.
|
|
648
|
+
*/
|
|
649
|
+
publishStoryWrite(change: TreeModelChange | null): TreeModelChange | null;
|
|
650
|
+
/**
|
|
651
|
+
* Record a write that spanned SEVERAL parts as one package undo unit.
|
|
652
|
+
*
|
|
653
|
+
* A comment is not in one part — the body in `comments.xml`, the thread record in
|
|
654
|
+
* `commentsExtended.xml`, the markers in the story — and those writes reach the store
|
|
655
|
+
* through the story store directly rather than through `transact`. The story store's own
|
|
656
|
+
* history entry cannot undo them: `undo()` on a story pointer syncs the STORY PART back and
|
|
657
|
+
* nothing else, so undoing a comment restored the markers and left the body behind, or the
|
|
658
|
+
* other way round. The caller discards the story entry and hands the package it started
|
|
659
|
+
* from to this instead, which is the same promotion the note cascade does.
|
|
660
|
+
*/
|
|
661
|
+
adoptPackageUnit(before: OoxmlPackage): void;
|
|
662
|
+
/** Install a full package snapshot (public seam for post-fetch cleanup). */
|
|
663
|
+
installPackageSnapshot(snapshot: OoxmlPackage): void;
|
|
664
|
+
/**
|
|
665
|
+
* Replace the package shell while preserving opened stores. Used when numbering /
|
|
666
|
+
* content-types mutate the package outside story trees.
|
|
667
|
+
*/
|
|
668
|
+
replacePackageShell(pkg: OoxmlPackage): void;
|
|
669
|
+
/**
|
|
670
|
+
* Install a full package snapshot: body + opened story stores track the snapshot's parts.
|
|
671
|
+
* Stores whose parts disappeared stay parked (history identity preserved) so a later
|
|
672
|
+
* package undo can reconnect them; rId cache rebuilds from remaining relationships.
|
|
673
|
+
*
|
|
674
|
+
* Numbering / shell-minted hyperlink resources (via {@link replacePackageShell}) are merged
|
|
675
|
+
* onto the snapshot so lifecycle undo cannot orphan story `numId` / `r:id` references.
|
|
676
|
+
* Furniture and notes parts remain snapshot-owned; shell hyperlink `.rels` for those owners
|
|
677
|
+
* park when the part is temporarily absent and are pruned once history can no longer restore
|
|
678
|
+
* the owner. Lifecycle-cloned owned relationships are not shell-minted and GC with the part.
|
|
679
|
+
*/
|
|
680
|
+
private installPackageSnapshotInternal;
|
|
681
|
+
private openNotesPartStore;
|
|
682
|
+
private openHeaderFooterStore;
|
|
683
|
+
private storyRefForPart;
|
|
684
|
+
private syncPackageFromStore;
|
|
685
|
+
private pushUndoPointer;
|
|
686
|
+
/**
|
|
687
|
+
* Drop parked story stores that no current package part and no undo/redo pointer can
|
|
688
|
+
* restore. History-reachable identities stay so edit→delete→undo reconnects the same
|
|
689
|
+
* store; unreachable parked entries must not hold `maxEditableStoryParts` forever.
|
|
690
|
+
*
|
|
691
|
+
* Also prunes scoped hyperlink shell resources parked for owners that are no longer
|
|
692
|
+
* live and not history-reachable (see `pruneUnreachableHyperlinkShell`).
|
|
693
|
+
*/
|
|
694
|
+
private evictUnreachableStories;
|
|
695
|
+
private retainedStoryPartNames;
|
|
696
|
+
/**
|
|
697
|
+
* Owners whose scoped hyperlink shell must survive while furniture/notes parts are
|
|
698
|
+
* temporarily absent: opened/parked story retention plus every part name that package
|
|
699
|
+
* history can still restore (even when the story store was never opened).
|
|
700
|
+
*/
|
|
701
|
+
private retainedHyperlinkOwnerParts;
|
|
702
|
+
private retainPointerStoryParts;
|
|
703
|
+
private retainPointerHyperlinkOwners;
|
|
704
|
+
private publish;
|
|
705
|
+
private publishSynthetic;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* The payload half of an insert: which store, which node, and what it holds.
|
|
710
|
+
*
|
|
711
|
+
* `data` is opaque here. The lane that owns a schema is the one that declared it, and a store
|
|
712
|
+
* that parsed payloads would be a second opinion about what a host's node means.
|
|
713
|
+
*/
|
|
714
|
+
interface CustomNodePayloadWrite {
|
|
715
|
+
/** Namespace of the store's root element — what identifies one store among several. */
|
|
716
|
+
readonly namespaceUri: string;
|
|
717
|
+
/** Local name of that root. An NCName; anything else refuses. */
|
|
718
|
+
readonly rootLocalName: string;
|
|
719
|
+
/** The node's own id, which the binding's xpath quotes. */
|
|
720
|
+
readonly nodeId: string;
|
|
721
|
+
/** The text the control shows. Word paints this from the store, so an empty one is an empty chip. */
|
|
722
|
+
readonly label: string;
|
|
723
|
+
/** The payload, serialized. JSON by convention; never parsed here. */
|
|
724
|
+
readonly data: string;
|
|
725
|
+
}
|
|
726
|
+
/** Where the control goes, what it says, and the payload it carries. */
|
|
727
|
+
interface InsertCustomNodeWrite {
|
|
728
|
+
readonly paragraphId: string;
|
|
729
|
+
readonly offset: number;
|
|
730
|
+
/**
|
|
731
|
+
* Wrap rather than insert: the text from `offset` to here is removed first.
|
|
732
|
+
*
|
|
733
|
+
* The node's label REPLACES the words it covered, because that is what turning a stretch of a
|
|
734
|
+
* sentence into a citation means. Removed inside the same transaction, so a refused insert
|
|
735
|
+
* leaves the text where it was.
|
|
736
|
+
*/
|
|
737
|
+
readonly replaceUntil?: number;
|
|
738
|
+
/**
|
|
739
|
+
* Rewrite an existing control: it and the payload it bound go first, in this transaction.
|
|
740
|
+
*
|
|
741
|
+
* An UPDATE is remove-and-reinsert, because the tag codec has no in-place rewrite and
|
|
742
|
+
* pretending it did would put a second write path beside this one. Doing both halves here is
|
|
743
|
+
* what keeps it one undo step, and what stops an update from leaving a label and a payload
|
|
744
|
+
* that disagree.
|
|
745
|
+
*/
|
|
746
|
+
readonly replaceControlId?: string;
|
|
747
|
+
readonly tag: string;
|
|
748
|
+
readonly text: string;
|
|
749
|
+
readonly alias?: string;
|
|
750
|
+
/** Defaults to none. Callers that want Word's own "cannot type into it" pass `contentLocked`. */
|
|
751
|
+
readonly lock?: 'sdtLocked' | 'sdtContentLocked' | 'contentLocked';
|
|
752
|
+
/** Omitted authors an ordinary tagged control with no store — the pre-payload behaviour. */
|
|
753
|
+
readonly payload?: CustomNodePayloadWrite;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Why a payload write was refused.
|
|
757
|
+
*
|
|
758
|
+
* The tree rejections pass through unchanged, so a locked paragraph refuses a bound insert for
|
|
759
|
+
* the same named reason it refuses a plain one. The three added here are the payload's own.
|
|
760
|
+
*/
|
|
761
|
+
type CustomNodeWriteRejection = TreeOpRejection
|
|
762
|
+
/** The id, root name or namespace cannot be spelled in an XPath, so no binding could name it. */
|
|
763
|
+
| 'unaddressable-payload'
|
|
764
|
+
/** The store could not be authored — see `withCustomXmlDataPart` for every way that happens. */
|
|
765
|
+
| 'store-not-authored'
|
|
766
|
+
/** The payload or the label is past the cap. */
|
|
767
|
+
| 'payload-too-large';
|
|
768
|
+
type CustomNodeWriteResult = {
|
|
769
|
+
readonly ok: true;
|
|
770
|
+
readonly change: TreeModelChange | null;
|
|
771
|
+
/**
|
|
772
|
+
* The control this write authored, when it authored one. A rewrite replaces the control
|
|
773
|
+
* rather than editing it, so the id the caller passed in names nothing afterwards.
|
|
774
|
+
*/
|
|
775
|
+
readonly nodeId?: string;
|
|
776
|
+
} | {
|
|
777
|
+
readonly ok: false;
|
|
778
|
+
readonly reason: CustomNodeWriteRejection;
|
|
779
|
+
readonly detail?: string;
|
|
780
|
+
};
|
|
781
|
+
/**
|
|
782
|
+
* The largest payload one node may carry, in UTF-16 code units.
|
|
783
|
+
*
|
|
784
|
+
* Same figure as the read side's (`parseCustomNodeData`) and for the same reason: far past any
|
|
785
|
+
* legitimate chip, far short of anything that hurts. Checked on the WRITE too, so a document
|
|
786
|
+
* cannot be authored here holding a payload the reader will later refuse to parse.
|
|
787
|
+
*/
|
|
788
|
+
declare const MAX_CUSTOM_NODE_PAYLOAD_LENGTH: number;
|
|
789
|
+
/** The longest label a binding may paint. Word renders it as the control's whole content. */
|
|
790
|
+
declare const MAX_CUSTOM_NODE_LABEL_LENGTH = 4096;
|
|
791
|
+
/**
|
|
792
|
+
* Insert one custom node, with its payload, as a single transaction.
|
|
793
|
+
*
|
|
794
|
+
* Answers the store transaction's own change so the caller can publish it — a payload write is a
|
|
795
|
+
* package write reaching through a story store, exactly as a comment is, and the coordinator
|
|
796
|
+
* needs the change to know which paragraphs went dirty.
|
|
797
|
+
*/
|
|
798
|
+
declare function insertCustomNodeWrite(store: TreeDocumentStore, write: InsertCustomNodeWrite,
|
|
799
|
+
/**
|
|
800
|
+
* The part the customXml store hangs off, defaulting to the story being written.
|
|
801
|
+
*
|
|
802
|
+
* A chip in a header is written against the HEADER store, but Word enumerates the data
|
|
803
|
+
* store from the main document part — a store authored off a header is one Word never
|
|
804
|
+
* sees. So the caller passes the main part while the control itself lands in the story.
|
|
805
|
+
*/
|
|
806
|
+
dataOwnerPartName?: string): CustomNodeWriteResult;
|
|
807
|
+
/**
|
|
808
|
+
* Remove a control and, in the same transaction, the payload it bound.
|
|
809
|
+
*
|
|
810
|
+
* The sweep would collect the node eventually — that is what makes deletion in Word survivable —
|
|
811
|
+
* but "eventually" is the next open, and a document saved in between carries a payload for a
|
|
812
|
+
* chip that is gone. Doing it here means the ordinary case is exact and the sweep is a backstop.
|
|
813
|
+
*/
|
|
814
|
+
declare function removeCustomNodeWrite(store: TreeDocumentStore, controlNodeId: string): CustomNodeWriteResult;
|
|
815
|
+
/**
|
|
816
|
+
* What one sweep collected, or why it collected nothing.
|
|
817
|
+
*
|
|
818
|
+
* `ok: false` is NOT "the document was already tidy" — that is `ok: true` with an empty
|
|
819
|
+
* `removed`. It means a store this sweep was asked to tidy refused the rewrite, which a caller
|
|
820
|
+
* that keeps saving into the same document should know about rather than silently retry forever.
|
|
821
|
+
*/
|
|
822
|
+
type CustomNodeSweepResult = {
|
|
823
|
+
readonly ok: true;
|
|
824
|
+
readonly pkg: OoxmlPackage;
|
|
825
|
+
/** Node ids removed, across every store swept. Empty means there were no orphans. */
|
|
826
|
+
readonly removed: readonly string[];
|
|
827
|
+
} | {
|
|
828
|
+
readonly ok: false;
|
|
829
|
+
readonly reason: string;
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* Drop every payload no control binds, in the stores whose namespaces a host claims.
|
|
833
|
+
*
|
|
834
|
+
* ON OPEN, NOT ON SAVE. A chip cut to the clipboard is unbound for as long as it sits there, so a
|
|
835
|
+
* save mid-cut would destroy the payload the user is about to paste. On open the only unbound
|
|
836
|
+
* nodes are ones a control genuinely lost — deleted here, or deleted in Word, which is the case
|
|
837
|
+
* nothing else can collect.
|
|
838
|
+
*
|
|
839
|
+
* `namespaces` is the claim, and it is what keeps this off other people's stores: Word's own Cover
|
|
840
|
+
* Page Properties store rides in most templates, and a sweep that walked every customXml part
|
|
841
|
+
* would be deleting from it on the strength of a name collision.
|
|
842
|
+
*/
|
|
843
|
+
declare function sweepCustomNodePayloads(pkg: OoxmlPackage, storyPartName: string, namespaces: readonly string[]): CustomNodeSweepResult;
|
|
844
|
+
/** One control's payload, as the store holds it. Both strings are untrusted file input. */
|
|
845
|
+
interface CustomNodePayloadRead {
|
|
846
|
+
/** The store node's own id. */
|
|
847
|
+
readonly nodeId: string;
|
|
848
|
+
/** The text Word paints the control from. */
|
|
849
|
+
readonly label: string;
|
|
850
|
+
/** The payload as authored. JSON by convention, unparsed here. */
|
|
851
|
+
readonly data: string;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* The payload every control in a story binds to, keyed by the CONTROL's canonical node id.
|
|
855
|
+
*
|
|
856
|
+
* Keyed by the control rather than by the store node because that is the question a reader
|
|
857
|
+
* actually has — "what does this chip carry" — and because two stores may each hold a `cx1`.
|
|
858
|
+
* Resolved here rather than by a capability package: the stores are package parts, and a
|
|
859
|
+
* derivation that only gets story parts has no way to reach them.
|
|
860
|
+
*
|
|
861
|
+
* Every store the story relates to, so a document carrying two definitions' payloads answers
|
|
862
|
+
* for both without anyone naming a namespace.
|
|
863
|
+
*/
|
|
864
|
+
declare function customNodePayloadsByControl(pkg: OoxmlPackage, storyPartName: string): ReadonlyMap<string, CustomNodePayloadRead>;
|
|
865
|
+
/** Every payload one store holds, for a caller resolving a control's `data`. */
|
|
866
|
+
declare function customNodePayloadsOf(pkg: OoxmlPackage, storyPartName: string, namespaceUri: string): ReadonlyMap<string, {
|
|
867
|
+
readonly label: string;
|
|
868
|
+
readonly data: string;
|
|
869
|
+
}>;
|
|
870
|
+
/**
|
|
871
|
+
* What the session answers for a sweep: the ids collected, or the reason none were.
|
|
872
|
+
*
|
|
873
|
+
* Narrower than {@link CustomNodeSweepResult}, which also carries the rewritten package — the
|
|
874
|
+
* session has already installed that, and handing it back would invite a caller to install it
|
|
875
|
+
* twice.
|
|
876
|
+
*/
|
|
877
|
+
type CustomNodeSweepOutcome = {
|
|
878
|
+
readonly ok: true;
|
|
879
|
+
readonly removed: readonly string[];
|
|
880
|
+
} | {
|
|
881
|
+
readonly ok: false;
|
|
882
|
+
readonly reason: string;
|
|
883
|
+
};
|
|
884
|
+
|
|
885
|
+
export { customNodePayloadsByControl as $, type ApplyImagePropertiesInput as A, type BookmarkIndex as B, type CustomNodeWriteResult as C, type DocumentTrackingSettings as D, type EmbeddedFont as E, type FontStyleKey as F, type NoteReferenceScanBudget as G, type HeaderFooterLifecycleImpact as H, type InsertCustomNodeWrite as I, type ReadEmbeddedFontsOptions as J, type StoryResolveResult as K, type ListKind as L, MAX_CUSTOM_NODE_LABEL_LENGTH as M, NO_TRACKING_SETTINGS as N, type TreePackageStoreOptions as O, type PackageTransactResult as P, allocateDrawingPropertyId as Q, type ReplaceImageOptions as R, type StoryScope as S, TreePackageStore as T, applyHeaderFooterLifecycleOp as U, applyNoteLifecycleOp as V, buildBookmarkIndex as W, cascadeDeletedNoteReferences as X, collectNoteReferences as Y, collectPackageNoteReferences as Z, createNoteReferenceScanBudget as _, type StoryTargetRejection as a, customNodePayloadsOf as a0, deobfuscateFont as a1, diagnoseNoteReferences as a2, ensureListDefinition as a3, ensureNumberingLevel as a4, insertCustomNodeWrite as a5, isHeaderFooterLifecycleOp as a6, isNoteLifecycleOp as a7, normalNoteIds as a8, notesPartHasId as a9, readEmbeddedFonts as aa, readTrackingSettings as ab, removeCustomNodeWrite as ac, resolveNotesPart as ad, sweepCustomNodePayloads as ae, withBinaryPart as af, withEmbeddedImage as ag, withoutUnreferencedImagePart as ah, type CustomNodeSweepOutcome as b, type InsertImageInput as c, type ImageIntentResult as d, type BookmarkAnchor as e, type CascadeDeletedNoteReferencesOptions as f, type CustomNodePayloadRead as g, type CustomNodePayloadWrite as h, type CustomNodeSweepResult as i, type CustomNodeWriteRejection as j, DEFAULT_MAX_EDITABLE_STORY_PARTS as k, type DrawingPropertyIdResult as l, type HeaderFooterLifecycleOp as m, type HeaderFooterLifecycleRejection as n, type HeaderFooterLifecycleResult as o, MAX_CUSTOM_NODE_PAYLOAD_LENGTH as p, MAX_NOTE_REFERENCE_PARTS as q, MAX_NOTE_REFERENCE_SCAN as r, type NoteDiagnostic as s, type NoteDiagnosticCode as t, type NoteLifecycleImpact as u, type NoteLifecycleOp as v, type NoteLifecycleOptions as w, type NoteLifecycleRejection as x, type NoteLifecycleResult as y, type NoteReferenceHit as z };
|