@beyondwork/docx-react-component 1.0.106 → 1.0.109
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/package.json +19 -5
- package/src/api/geometry-overlay-rects.ts +5 -0
- package/src/api/package-version.ts +1 -1
- package/src/api/page-anchor-id.ts +5 -0
- package/src/api/public-types.ts +16 -9
- package/src/api/table-node-specs.ts +6 -0
- package/src/api/v3/_create.ts +2 -1
- package/src/api/v3/_page-anchor-id.ts +52 -0
- package/src/api/v3/_runtime-handle.ts +92 -1
- package/src/api/v3/ai/_audit-time.ts +5 -0
- package/src/api/v3/ai/_pe2-evidence.ts +38 -0
- package/src/api/v3/ai/attach.ts +7 -2
- package/src/api/v3/ai/replacement.ts +101 -18
- package/src/api/v3/ai/resolve.ts +2 -2
- package/src/api/v3/ai/review.ts +177 -3
- package/src/api/v3/index.ts +1 -0
- package/src/api/v3/runtime/collab.ts +462 -0
- package/src/api/v3/runtime/document.ts +503 -20
- package/src/api/v3/runtime/geometry.ts +97 -0
- package/src/api/v3/runtime/layout.ts +744 -0
- package/src/api/v3/runtime/perf-probe.ts +14 -0
- package/src/api/v3/runtime/viewport.ts +9 -8
- package/src/api/v3/ui/_types.ts +149 -55
- package/src/api/v3/ui/chrome-preset-model.ts +5 -5
- package/src/api/v3/ui/debug.ts +115 -2
- package/src/api/v3/ui/index.ts +13 -0
- package/src/api/v3/ui/overlays.ts +0 -8
- package/src/api/v3/ui/surface.ts +56 -0
- package/src/api/v3/ui/viewport.ts +22 -9
- package/src/core/commands/image-commands.ts +1 -0
- package/src/core/commands/index.ts +6 -0
- package/src/core/schema/text-schema.ts +43 -5
- package/src/core/selection/mapping.ts +8 -1
- package/src/core/selection/review-anchors.ts +5 -1
- package/src/core/state/text-transaction.ts +8 -2
- package/src/io/export/serialize-revisions.ts +149 -1
- package/src/io/normalize/normalize-text.ts +6 -0
- package/src/io/ooxml/parse-bookmark-references.ts +55 -0
- package/src/io/ooxml/parse-fields.ts +24 -2
- package/src/io/ooxml/parse-headers-footers.ts +38 -5
- package/src/io/ooxml/parse-main-document.ts +153 -9
- package/src/io/ooxml/parse-numbering.ts +20 -0
- package/src/io/ooxml/parse-revisions.ts +19 -8
- package/src/io/opc/package-reader.ts +98 -8
- package/src/model/anchor.ts +4 -3
- package/src/model/canonical-document.ts +220 -2
- package/src/model/canonical-hash.ts +221 -0
- package/src/model/canonical-layout-inputs.ts +245 -6
- package/src/model/layout/index.ts +1 -0
- package/src/model/layout/page-graph-types.ts +118 -1
- package/src/model/review/revision-types.ts +14 -3
- package/src/preservation/store.ts +20 -4
- package/src/review/README.md +1 -1
- package/src/review/store/revision-actions.ts +14 -2
- package/src/runtime/collab/event-types.ts +67 -1
- package/src/runtime/collab/runtime-collab-sync.ts +177 -5
- package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
- package/src/runtime/document-heading-outline.ts +147 -0
- package/src/runtime/document-navigation.ts +8 -243
- package/src/runtime/document-runtime.ts +240 -97
- package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
- package/src/runtime/formatting/layout-inputs.ts +38 -5
- package/src/runtime/formatting/numbering/geometry.ts +28 -2
- package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
- package/src/runtime/geometry/caret-geometry.ts +5 -6
- package/src/runtime/geometry/geometry-facet.ts +60 -10
- package/src/runtime/geometry/geometry-index.ts +591 -20
- package/src/runtime/geometry/geometry-types.ts +59 -0
- package/src/runtime/geometry/hit-test.ts +11 -1
- package/src/runtime/geometry/overlay-rects.ts +5 -3
- package/src/runtime/geometry/project-anchors.ts +1 -1
- package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
- package/src/runtime/layout/index.ts +6 -0
- package/src/runtime/layout/layout-engine-instance.ts +6 -1
- package/src/runtime/layout/layout-engine-version.ts +181 -16
- package/src/runtime/layout/layout-facet-types.ts +6 -0
- package/src/runtime/layout/page-graph.ts +21 -4
- package/src/runtime/layout/paginated-layout-engine.ts +139 -15
- package/src/runtime/layout/project-block-fragments.ts +265 -7
- package/src/runtime/layout/public-facet.ts +78 -24
- package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
- package/src/runtime/layout/table-row-split.ts +92 -35
- package/src/runtime/prerender/cache-envelope.ts +2 -2
- package/src/runtime/prerender/cache-key.ts +5 -4
- package/src/runtime/prerender/customxml-cache.ts +0 -1
- package/src/runtime/render/render-kernel.ts +1 -1
- package/src/runtime/revision-runtime.ts +112 -10
- package/src/runtime/scopes/_scope-dependencies.ts +1 -0
- package/src/runtime/scopes/action-validation.ts +22 -2
- package/src/runtime/scopes/capabilities.ts +316 -0
- package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
- package/src/runtime/scopes/compiler-service.ts +108 -4
- package/src/runtime/scopes/content-control-evidence.ts +79 -0
- package/src/runtime/scopes/create-issue.ts +5 -5
- package/src/runtime/scopes/evidence.ts +91 -0
- package/src/runtime/scopes/formatting/apply.ts +2 -0
- package/src/runtime/scopes/geometry-evidence.ts +130 -0
- package/src/runtime/scopes/index.ts +54 -0
- package/src/runtime/scopes/issue-lifecycle.ts +224 -0
- package/src/runtime/scopes/layout-evidence.ts +374 -0
- package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
- package/src/runtime/scopes/preservation-boundary.ts +7 -1
- package/src/runtime/scopes/replacement/apply.ts +97 -34
- package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
- package/src/runtime/scopes/semantic-scope-types.ts +242 -3
- package/src/runtime/scopes/visualization.ts +28 -0
- package/src/runtime/surface-projection.ts +44 -5
- package/src/runtime/telemetry/perf-probe.ts +216 -0
- package/src/runtime/virtualized-rendering.ts +36 -1
- package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
- package/src/runtime/workflow/coordinator.ts +39 -11
- package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
- package/src/runtime/workflow/index.ts +3 -0
- package/src/runtime/workflow/overlay-lane-types.ts +58 -0
- package/src/runtime/workflow/overlay-lanes.ts +168 -10
- package/src/runtime/workflow/overlay-store.ts +2 -2
- package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
- package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
- package/src/session/_sync-legacy.ts +17 -27
- package/src/session/import/loader.ts +6 -4
- package/src/session/import/source-package-evidence.ts +186 -2
- package/src/session/index.ts +5 -6
- package/src/session/session.ts +30 -56
- package/src/session/types.ts +8 -13
- package/src/shell/session-bootstrap.ts +155 -81
- package/src/ui/WordReviewEditor.tsx +520 -12
- package/src/ui/editor-shell-view.tsx +14 -4
- package/src/ui/editor-surface-controller.tsx +5 -3
- package/src/ui/headless/selection-tool-resolver.ts +1 -2
- package/src/ui/presence-overlay-lane.ts +0 -1
- package/src/ui/ui-controller-factory.ts +7 -0
- package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
- package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
- package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
- package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
- package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
- package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
- package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
- package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
- package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
- package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
- package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
- package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
- package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
- package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
- package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
- package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
- package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
- package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
- package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
- package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
- package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
- package/src/ui-tailwind/debug/README.md +4 -1
- package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
- package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
- package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
- package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
- package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
- package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
- package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
- package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
- package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
- package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
- package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
- package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
- package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
- package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
- package/src/ui-tailwind/review-workspace/types.ts +26 -12
- package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
- package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
- package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
- package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
- package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
- package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
- package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
- package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
- package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
- package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
|
@@ -29,21 +29,54 @@ export interface OpcPackage {
|
|
|
29
29
|
sourceByteLength: number;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export interface OpcPackageReadLimits {
|
|
33
|
+
maxEntries?: number;
|
|
34
|
+
maxEntryUncompressedBytes?: number;
|
|
35
|
+
maxTotalUncompressedBytes?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ReadOpcPackageOptions {
|
|
39
|
+
limits?: OpcPackageReadLimits;
|
|
40
|
+
}
|
|
41
|
+
|
|
32
42
|
const EOCD_SIGNATURE = 0x06054b50;
|
|
33
43
|
const CENTRAL_DIRECTORY_SIGNATURE = 0x02014b50;
|
|
34
44
|
const LOCAL_FILE_HEADER_SIGNATURE = 0x04034b50;
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
const DEFAULT_MAX_ZIP_ENTRIES = 4096;
|
|
47
|
+
const DEFAULT_MAX_ENTRY_UNCOMPRESSED_BYTES = 64 * 1024 * 1024;
|
|
48
|
+
const DEFAULT_MAX_TOTAL_UNCOMPRESSED_BYTES = 256 * 1024 * 1024;
|
|
49
|
+
|
|
50
|
+
interface ResolvedOpcPackageReadLimits {
|
|
51
|
+
maxEntries: number;
|
|
52
|
+
maxEntryUncompressedBytes: number;
|
|
53
|
+
maxTotalUncompressedBytes: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function readOpcPackage(
|
|
57
|
+
source: Uint8Array | ArrayBuffer,
|
|
58
|
+
options: ReadOpcPackageOptions = {},
|
|
59
|
+
): OpcPackage {
|
|
37
60
|
const bytes = source instanceof Uint8Array ? source : new Uint8Array(source);
|
|
38
|
-
const
|
|
61
|
+
const limits = resolveReadLimits(options.limits);
|
|
62
|
+
const centralDirectory = readCentralDirectory(bytes, limits);
|
|
39
63
|
const parts = new Map<string, OpcPackagePart>();
|
|
64
|
+
let totalUncompressedBytes = 0;
|
|
40
65
|
|
|
41
66
|
for (const entry of centralDirectory) {
|
|
42
67
|
if (entry.path.endsWith("/")) {
|
|
43
68
|
continue;
|
|
44
69
|
}
|
|
45
70
|
|
|
46
|
-
|
|
71
|
+
assertEntryWithinLimits(entry, limits);
|
|
72
|
+
totalUncompressedBytes += entry.uncompressedSize;
|
|
73
|
+
if (totalUncompressedBytes > limits.maxTotalUncompressedBytes) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Invalid ZIP archive: cumulative uncompressed size ${totalUncompressedBytes} exceeds limit ${limits.maxTotalUncompressedBytes}.`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const storedBytes = readZipEntry(bytes, entry, limits);
|
|
47
80
|
const normalizedPath = normalizePartPath(entry.path);
|
|
48
81
|
const surfaceKind = getSurfaceKind(normalizedPath);
|
|
49
82
|
|
|
@@ -111,15 +144,38 @@ export function readOpcPackage(source: Uint8Array | ArrayBuffer): OpcPackage {
|
|
|
111
144
|
};
|
|
112
145
|
}
|
|
113
146
|
|
|
114
|
-
function
|
|
147
|
+
function resolveReadLimits(
|
|
148
|
+
limits: OpcPackageReadLimits | undefined,
|
|
149
|
+
): ResolvedOpcPackageReadLimits {
|
|
150
|
+
return {
|
|
151
|
+
maxEntries: limits?.maxEntries ?? DEFAULT_MAX_ZIP_ENTRIES,
|
|
152
|
+
maxEntryUncompressedBytes:
|
|
153
|
+
limits?.maxEntryUncompressedBytes ?? DEFAULT_MAX_ENTRY_UNCOMPRESSED_BYTES,
|
|
154
|
+
maxTotalUncompressedBytes:
|
|
155
|
+
limits?.maxTotalUncompressedBytes ?? DEFAULT_MAX_TOTAL_UNCOMPRESSED_BYTES,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function readCentralDirectory(
|
|
160
|
+
archive: Uint8Array,
|
|
161
|
+
limits: ResolvedOpcPackageReadLimits,
|
|
162
|
+
): ZipCentralDirectoryEntry[] {
|
|
115
163
|
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
116
164
|
const eocdOffset = findEndOfCentralDirectory(archive, view);
|
|
117
165
|
const entryCount = view.getUint16(eocdOffset + 10, true);
|
|
166
|
+
if (entryCount > limits.maxEntries) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Invalid ZIP archive: entry count ${entryCount} exceeds limit ${limits.maxEntries}.`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
118
171
|
const centralDirectoryOffset = view.getUint32(eocdOffset + 16, true);
|
|
119
172
|
const entries: ZipCentralDirectoryEntry[] = [];
|
|
120
173
|
let offset = centralDirectoryOffset;
|
|
121
174
|
|
|
122
175
|
for (let index = 0; index < entryCount; index += 1) {
|
|
176
|
+
if (offset + 46 > archive.byteLength) {
|
|
177
|
+
throw new Error(`Invalid ZIP central directory entry at offset ${offset}.`);
|
|
178
|
+
}
|
|
123
179
|
const signature = view.getUint32(offset, true);
|
|
124
180
|
if (signature !== CENTRAL_DIRECTORY_SIGNATURE) {
|
|
125
181
|
throw new Error(`Invalid ZIP central directory signature at offset ${offset}.`);
|
|
@@ -135,6 +191,10 @@ function readCentralDirectory(archive: Uint8Array): ZipCentralDirectoryEntry[] {
|
|
|
135
191
|
const fileCommentLength = view.getUint16(offset + 32, true);
|
|
136
192
|
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
137
193
|
const fileNameOffset = offset + 46;
|
|
194
|
+
const nextOffset = fileNameOffset + fileNameLength + extraFieldLength + fileCommentLength;
|
|
195
|
+
if (nextOffset > archive.byteLength) {
|
|
196
|
+
throw new Error(`Invalid ZIP central directory entry at offset ${offset}.`);
|
|
197
|
+
}
|
|
138
198
|
const fileName = decodeUtf8(archive.subarray(fileNameOffset, fileNameOffset + fileNameLength));
|
|
139
199
|
|
|
140
200
|
entries.push({
|
|
@@ -147,7 +207,7 @@ function readCentralDirectory(archive: Uint8Array): ZipCentralDirectoryEntry[] {
|
|
|
147
207
|
generalPurposeBitFlag,
|
|
148
208
|
});
|
|
149
209
|
|
|
150
|
-
offset
|
|
210
|
+
offset = nextOffset;
|
|
151
211
|
}
|
|
152
212
|
|
|
153
213
|
return entries;
|
|
@@ -167,9 +227,27 @@ function findEndOfCentralDirectory(archive: Uint8Array, view: DataView): number
|
|
|
167
227
|
throw new Error("Invalid ZIP archive: end of central directory not found.");
|
|
168
228
|
}
|
|
169
229
|
|
|
170
|
-
function
|
|
230
|
+
function assertEntryWithinLimits(
|
|
231
|
+
entry: ZipCentralDirectoryEntry,
|
|
232
|
+
limits: ResolvedOpcPackageReadLimits,
|
|
233
|
+
): void {
|
|
234
|
+
if (entry.uncompressedSize > limits.maxEntryUncompressedBytes) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Invalid ZIP archive: entry ${entry.path} uncompressed size ${entry.uncompressedSize} exceeds limit ${limits.maxEntryUncompressedBytes}.`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function readZipEntry(
|
|
242
|
+
archive: Uint8Array,
|
|
243
|
+
entry: ZipCentralDirectoryEntry,
|
|
244
|
+
limits: ResolvedOpcPackageReadLimits,
|
|
245
|
+
): Uint8Array {
|
|
171
246
|
const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
|
|
172
247
|
const headerOffset = entry.localHeaderOffset;
|
|
248
|
+
if (headerOffset + 30 > archive.byteLength) {
|
|
249
|
+
throw new Error(`Invalid ZIP local file header offset ${headerOffset}.`);
|
|
250
|
+
}
|
|
173
251
|
const signature = view.getUint32(headerOffset, true);
|
|
174
252
|
if (signature !== LOCAL_FILE_HEADER_SIGNATURE) {
|
|
175
253
|
throw new Error(`Invalid ZIP local file header signature at offset ${headerOffset}.`);
|
|
@@ -178,16 +256,28 @@ function readZipEntry(archive: Uint8Array, entry: ZipCentralDirectoryEntry): Uin
|
|
|
178
256
|
const fileNameLength = view.getUint16(headerOffset + 26, true);
|
|
179
257
|
const extraFieldLength = view.getUint16(headerOffset + 28, true);
|
|
180
258
|
const dataOffset = headerOffset + 30 + fileNameLength + extraFieldLength;
|
|
259
|
+
if (dataOffset + entry.compressedSize > archive.byteLength) {
|
|
260
|
+
throw new Error(`Invalid ZIP entry ${entry.path}: compressed data exceeds archive bounds.`);
|
|
261
|
+
}
|
|
181
262
|
const compressed = archive.subarray(dataOffset, dataOffset + entry.compressedSize);
|
|
182
263
|
|
|
264
|
+
let inflated: Uint8Array;
|
|
183
265
|
switch (entry.compression) {
|
|
184
266
|
case "store":
|
|
185
|
-
|
|
267
|
+
inflated = new Uint8Array(compressed);
|
|
268
|
+
break;
|
|
186
269
|
case "deflate":
|
|
187
|
-
|
|
270
|
+
inflated = inflateSync(compressed);
|
|
271
|
+
break;
|
|
188
272
|
default:
|
|
189
273
|
throw new Error(`Unsupported ZIP compression for ${entry.path}.`);
|
|
190
274
|
}
|
|
275
|
+
if (inflated.byteLength > limits.maxEntryUncompressedBytes) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Invalid ZIP archive: entry ${entry.path} inflated size ${inflated.byteLength} exceeds limit ${limits.maxEntryUncompressedBytes}.`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return inflated;
|
|
191
281
|
}
|
|
192
282
|
|
|
193
283
|
function mapCompressionMethod(method: number): OpcCompressionMethod {
|
package/src/model/anchor.ts
CHANGED
|
@@ -28,14 +28,15 @@ export interface BoundaryAssoc {
|
|
|
28
28
|
/**
|
|
29
29
|
* Range anchor — flat shape `{ kind: "range", from, to, assoc }` after the
|
|
30
30
|
* 2026-04-23 flat-wins collapse. Prior to the collapse this carried a nested
|
|
31
|
-
* `{ range: DocRange, assoc }` shape.
|
|
32
|
-
* for
|
|
33
|
-
*
|
|
31
|
+
* `{ range: DocRange, assoc }` shape. `range` remains as a readback-compatible
|
|
32
|
+
* mirror for review/package round-trip consumers that still inspect nested
|
|
33
|
+
* active ranges; `from` / `to` remain authoritative.
|
|
34
34
|
*/
|
|
35
35
|
export interface RangeAnchor {
|
|
36
36
|
kind: "range";
|
|
37
37
|
from: Position;
|
|
38
38
|
to: Position;
|
|
39
|
+
range?: DocRange;
|
|
39
40
|
assoc: BoundaryAssoc;
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -108,6 +108,43 @@ export interface CanonicalDocumentFragment {
|
|
|
108
108
|
readonly blocks: BlockNode[];
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Durable source-package pointer for canonical facts that must reconcile with
|
|
113
|
+
* Word-first telemetry without exposing package XML or document text.
|
|
114
|
+
*/
|
|
115
|
+
export interface CanonicalSourceRef {
|
|
116
|
+
readonly sourceId: string;
|
|
117
|
+
readonly partPath?: string;
|
|
118
|
+
readonly storyKind?: string;
|
|
119
|
+
readonly element?: string;
|
|
120
|
+
readonly ordinal?: number;
|
|
121
|
+
readonly startOffset?: number;
|
|
122
|
+
readonly endOffset?: number;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Redacted field evidence used by layout/runtime telemetry. Raw instruction
|
|
127
|
+
* and result text remain on editable field nodes and registry entries where
|
|
128
|
+
* they already exist; this shape is the hash-addressable comparison surface.
|
|
129
|
+
*/
|
|
130
|
+
export interface CanonicalFieldEvidenceRef {
|
|
131
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
132
|
+
readonly instructionHash?: string;
|
|
133
|
+
readonly resultHash?: string;
|
|
134
|
+
readonly locked?: boolean;
|
|
135
|
+
readonly dirty?: boolean;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface CanonicalHyperlinkFieldCarrier {
|
|
139
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
140
|
+
readonly fieldFamily?: FieldFamily;
|
|
141
|
+
readonly refreshStatus?: FieldRefreshStatus;
|
|
142
|
+
readonly instructionHash?: string;
|
|
143
|
+
readonly resultHash?: string;
|
|
144
|
+
readonly locked?: boolean;
|
|
145
|
+
readonly dirty?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
111
148
|
/**
|
|
112
149
|
* Draft-only mutable view of `CanonicalDocumentFragment`. Same
|
|
113
150
|
* semantics as {@link Mutable}. Used at fragment-construction sites;
|
|
@@ -370,6 +407,8 @@ export interface NumPicBullet {
|
|
|
370
407
|
|
|
371
408
|
export interface AbstractNumberingDefinition {
|
|
372
409
|
readonly abstractNumberingId: string;
|
|
410
|
+
/** Source numbering-part pointer for Word telemetry joins. */
|
|
411
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
373
412
|
readonly levels: NumberingLevelDefinition[];
|
|
374
413
|
readonly nsid?: string;
|
|
375
414
|
readonly multiLevelType?: "singleLevel" | "multilevel" | "hybridMultilevel";
|
|
@@ -421,6 +460,8 @@ export interface NumberingLevelOverrideDefinition {
|
|
|
421
460
|
|
|
422
461
|
export interface NumberingInstance {
|
|
423
462
|
readonly numberingInstanceId: string;
|
|
463
|
+
/** Source numbering-part pointer for Word telemetry joins. */
|
|
464
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
424
465
|
readonly abstractNumberingId: string;
|
|
425
466
|
readonly overrides: NumberingLevelOverride[];
|
|
426
467
|
}
|
|
@@ -1049,9 +1090,12 @@ export interface CanonicalFontEntry {
|
|
|
1049
1090
|
|
|
1050
1091
|
export interface ParagraphNode {
|
|
1051
1092
|
readonly type: "paragraph";
|
|
1093
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1052
1094
|
readonly styleId?: string;
|
|
1053
1095
|
numbering?: {
|
|
1054
1096
|
readonly numberingInstanceId: string;
|
|
1097
|
+
/** Source `<w:numPr>` pointer for Word telemetry joins. */
|
|
1098
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1055
1099
|
readonly level: number;
|
|
1056
1100
|
};
|
|
1057
1101
|
readonly alignment?: "left" | "center" | "right" | "both" | "distribute";
|
|
@@ -1230,6 +1274,7 @@ export interface TableStyleFormatting {
|
|
|
1230
1274
|
|
|
1231
1275
|
export interface TableNode {
|
|
1232
1276
|
readonly type: "table";
|
|
1277
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1233
1278
|
readonly styleId?: string;
|
|
1234
1279
|
/** @deprecated Use `unknownPropertyChildren`. Kept for snapshot back-compat. */
|
|
1235
1280
|
readonly propertiesXml?: string;
|
|
@@ -1358,6 +1403,7 @@ export type PreserveOnlyFieldFamily =
|
|
|
1358
1403
|
| "TIME"
|
|
1359
1404
|
| "AUTHOR"
|
|
1360
1405
|
| "FILENAME"
|
|
1406
|
+
| "HYPERLINK"
|
|
1361
1407
|
| "MERGEFIELD"
|
|
1362
1408
|
| "IF"
|
|
1363
1409
|
| "SEQ"
|
|
@@ -1381,14 +1427,20 @@ export type FieldRefreshStatus =
|
|
|
1381
1427
|
export interface FieldNode {
|
|
1382
1428
|
readonly type: "field";
|
|
1383
1429
|
readonly fieldType: "simple" | "complex";
|
|
1430
|
+
/** Stable canonical field identity for source/runtime telemetry joins. */
|
|
1431
|
+
readonly canonicalFieldId?: string;
|
|
1384
1432
|
readonly instruction: string;
|
|
1385
1433
|
readonly children: InlineNode[];
|
|
1434
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1386
1435
|
/** Classified field family. Undefined for legacy snapshots. */
|
|
1387
1436
|
readonly fieldFamily?: FieldFamily;
|
|
1388
1437
|
/** Target bookmark name for REF/PAGEREF/NOTEREF fields. */
|
|
1389
1438
|
readonly fieldTarget?: string;
|
|
1390
1439
|
/** Runtime refresh status. Undefined for legacy or preserve-only fields. */
|
|
1391
1440
|
readonly refreshStatus?: FieldRefreshStatus;
|
|
1441
|
+
/** Word field state flags when parsed from source package evidence. */
|
|
1442
|
+
readonly locked?: boolean;
|
|
1443
|
+
readonly dirty?: boolean;
|
|
1392
1444
|
/** Parsed field switches. Present only when the instruction contains recognized switches. */
|
|
1393
1445
|
switches?: {
|
|
1394
1446
|
readonly hyperlink?: boolean;
|
|
@@ -1460,6 +1512,8 @@ export interface FieldRegistry {
|
|
|
1460
1512
|
export interface FieldRegistryEntry {
|
|
1461
1513
|
/** Stable document-order index of this field (0-based). */
|
|
1462
1514
|
readonly fieldIndex: number;
|
|
1515
|
+
/** Stable canonical field identity for source/runtime telemetry joins. */
|
|
1516
|
+
readonly canonicalFieldId?: string;
|
|
1463
1517
|
/** Classified field family. */
|
|
1464
1518
|
readonly fieldFamily: FieldFamily;
|
|
1465
1519
|
/** Whether the field is in the supported refresh slice. */
|
|
@@ -1474,8 +1528,13 @@ export interface FieldRegistryEntry {
|
|
|
1474
1528
|
readonly paragraphIndex: number;
|
|
1475
1529
|
/** Stable story key for the story that owns this field instance. */
|
|
1476
1530
|
readonly storyKey?: string;
|
|
1531
|
+
/** Source-package pointer for joining this field to Word oracle telemetry. */
|
|
1532
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1477
1533
|
/** Runtime refresh status. */
|
|
1478
1534
|
readonly refreshStatus: FieldRefreshStatus;
|
|
1535
|
+
/** Word field state flags when parsed from source package evidence. */
|
|
1536
|
+
readonly locked?: boolean;
|
|
1537
|
+
readonly dirty?: boolean;
|
|
1479
1538
|
/** Parsed field switches carried from FieldNode.switches. Present only for REF/PAGEREF/NOTEREF/TOC entries with recognized switches. */
|
|
1480
1539
|
readonly switches?: FieldNode["switches"];
|
|
1481
1540
|
}
|
|
@@ -1813,12 +1872,16 @@ export interface SymbolNode {
|
|
|
1813
1872
|
export interface HyperlinkNode {
|
|
1814
1873
|
readonly type: "hyperlink";
|
|
1815
1874
|
readonly href: string;
|
|
1875
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1876
|
+
/** Treats `<w:hyperlink>` as a Word field carrier without forcing raw text into telemetry. */
|
|
1877
|
+
readonly fieldCarrier?: CanonicalHyperlinkFieldCarrier;
|
|
1816
1878
|
readonly children: Array<TextNode | HardBreakNode | ColumnBreakNode | PageBreakNode | TabNode | SymbolNode>;
|
|
1817
1879
|
}
|
|
1818
1880
|
|
|
1819
1881
|
export interface ImageNode {
|
|
1820
1882
|
readonly type: "image";
|
|
1821
1883
|
readonly mediaId: string;
|
|
1884
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
1822
1885
|
readonly altText?: string;
|
|
1823
1886
|
readonly placementXml?: string;
|
|
1824
1887
|
readonly display?: "inline" | "floating";
|
|
@@ -2150,6 +2213,7 @@ export interface TextBoxBodyProperties {
|
|
|
2150
2213
|
|
|
2151
2214
|
export interface DrawingFrameNode {
|
|
2152
2215
|
readonly type: "drawing_frame";
|
|
2216
|
+
readonly sourceRef?: CanonicalSourceRef;
|
|
2153
2217
|
readonly anchor: AnchorGeometry;
|
|
2154
2218
|
content:
|
|
2155
2219
|
| PictureContent
|
|
@@ -2251,7 +2315,13 @@ export interface CommentThreadMetadata {
|
|
|
2251
2315
|
}
|
|
2252
2316
|
|
|
2253
2317
|
export interface RevisionPropertyChangeData {
|
|
2254
|
-
readonly xmlTag:
|
|
2318
|
+
readonly xmlTag:
|
|
2319
|
+
| "pPrChange"
|
|
2320
|
+
| "sectPrChange"
|
|
2321
|
+
| "tblPrChange"
|
|
2322
|
+
| "trPrChange"
|
|
2323
|
+
| "tcPrChange"
|
|
2324
|
+
| "rPrChange";
|
|
2255
2325
|
readonly beforeXml: string;
|
|
2256
2326
|
}
|
|
2257
2327
|
|
|
@@ -2868,6 +2938,13 @@ function validateNumberingCatalog(
|
|
|
2868
2938
|
message: "abstractNumberingId must match the map key.",
|
|
2869
2939
|
});
|
|
2870
2940
|
}
|
|
2941
|
+
if (definitionRecord?.sourceRef !== undefined) {
|
|
2942
|
+
validateCanonicalSourceRef(
|
|
2943
|
+
definitionRecord.sourceRef,
|
|
2944
|
+
`${path}.abstractDefinitions.${abstractId}.sourceRef`,
|
|
2945
|
+
issues,
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2871
2948
|
}
|
|
2872
2949
|
}
|
|
2873
2950
|
|
|
@@ -2900,6 +2977,13 @@ function validateNumberingCatalog(
|
|
|
2900
2977
|
`${path}.instances.${instanceId}.abstractNumberingId`,
|
|
2901
2978
|
issues,
|
|
2902
2979
|
);
|
|
2980
|
+
if (instanceRecord.sourceRef !== undefined) {
|
|
2981
|
+
validateCanonicalSourceRef(
|
|
2982
|
+
instanceRecord.sourceRef,
|
|
2983
|
+
`${path}.instances.${instanceId}.sourceRef`,
|
|
2984
|
+
issues,
|
|
2985
|
+
);
|
|
2986
|
+
}
|
|
2903
2987
|
}
|
|
2904
2988
|
}
|
|
2905
2989
|
}
|
|
@@ -2979,6 +3063,9 @@ function validateDocumentNode(
|
|
|
2979
3063
|
if (type === "paragraph" && record.styleId !== undefined) {
|
|
2980
3064
|
expectDomainString(record.styleId, "styleId", `${path}.styleId`, issues);
|
|
2981
3065
|
}
|
|
3066
|
+
if (type === "paragraph" && record.sourceRef !== undefined) {
|
|
3067
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3068
|
+
}
|
|
2982
3069
|
if (type === "paragraph" && record.numbering !== undefined) {
|
|
2983
3070
|
const numbering = asPlainObject(record.numbering, `${path}.numbering`, issues);
|
|
2984
3071
|
if (numbering) {
|
|
@@ -2988,6 +3075,9 @@ function validateDocumentNode(
|
|
|
2988
3075
|
`${path}.numbering.numberingInstanceId`,
|
|
2989
3076
|
issues,
|
|
2990
3077
|
);
|
|
3078
|
+
if (numbering.sourceRef !== undefined) {
|
|
3079
|
+
validateCanonicalSourceRef(numbering.sourceRef, `${path}.numbering.sourceRef`, issues);
|
|
3080
|
+
}
|
|
2991
3081
|
if (typeof numbering.level !== "number") {
|
|
2992
3082
|
issues.push({
|
|
2993
3083
|
path: `${path}.numbering.level`,
|
|
@@ -2998,6 +3088,12 @@ function validateDocumentNode(
|
|
|
2998
3088
|
}
|
|
2999
3089
|
if (type === "hyperlink") {
|
|
3000
3090
|
expectString(record.href, `${path}.href`, issues);
|
|
3091
|
+
if (record.sourceRef !== undefined) {
|
|
3092
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3093
|
+
}
|
|
3094
|
+
if (record.fieldCarrier !== undefined) {
|
|
3095
|
+
validateHyperlinkFieldCarrier(record.fieldCarrier, `${path}.fieldCarrier`, issues);
|
|
3096
|
+
}
|
|
3001
3097
|
}
|
|
3002
3098
|
return;
|
|
3003
3099
|
case "alt_chunk":
|
|
@@ -3005,6 +3101,9 @@ function validateDocumentNode(
|
|
|
3005
3101
|
return;
|
|
3006
3102
|
case "image":
|
|
3007
3103
|
expectDomainString(record.mediaId, "mediaId", `${path}.mediaId`, issues);
|
|
3104
|
+
if (record.sourceRef !== undefined) {
|
|
3105
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3106
|
+
}
|
|
3008
3107
|
if (record.placementXml !== undefined) {
|
|
3009
3108
|
expectString(record.placementXml, `${path}.placementXml`, issues);
|
|
3010
3109
|
}
|
|
@@ -3030,6 +3129,9 @@ function validateDocumentNode(
|
|
|
3030
3129
|
}
|
|
3031
3130
|
return;
|
|
3032
3131
|
case "table":
|
|
3132
|
+
if (record.sourceRef !== undefined) {
|
|
3133
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3134
|
+
}
|
|
3033
3135
|
if (!Array.isArray(record.gridColumns)) {
|
|
3034
3136
|
issues.push({ path: `${path}.gridColumns`, message: "gridColumns must be an array." });
|
|
3035
3137
|
}
|
|
@@ -3061,7 +3163,15 @@ function validateDocumentNode(
|
|
|
3061
3163
|
return;
|
|
3062
3164
|
case "field":
|
|
3063
3165
|
expectString(record.fieldType, `${path}.fieldType`, issues);
|
|
3166
|
+
if (record.canonicalFieldId !== undefined) {
|
|
3167
|
+
expectString(record.canonicalFieldId, `${path}.canonicalFieldId`, issues);
|
|
3168
|
+
}
|
|
3064
3169
|
expectString(record.instruction, `${path}.instruction`, issues);
|
|
3170
|
+
if (record.sourceRef !== undefined) {
|
|
3171
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3172
|
+
}
|
|
3173
|
+
validateOptionalBoolean(record.locked, `${path}.locked`, issues);
|
|
3174
|
+
validateOptionalBoolean(record.dirty, `${path}.dirty`, issues);
|
|
3065
3175
|
if (!Array.isArray(record.children)) {
|
|
3066
3176
|
issues.push({ path: `${path}.children`, message: "children must be an array." });
|
|
3067
3177
|
} else {
|
|
@@ -3137,6 +3247,9 @@ function validateDocumentNode(
|
|
|
3137
3247
|
}
|
|
3138
3248
|
return;
|
|
3139
3249
|
case "drawing_frame": {
|
|
3250
|
+
if (record.sourceRef !== undefined) {
|
|
3251
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3252
|
+
}
|
|
3140
3253
|
const anchor = asPlainObject(record.anchor, `${path}.anchor`, issues);
|
|
3141
3254
|
const content = asPlainObject(record.content, `${path}.content`, issues);
|
|
3142
3255
|
if (anchor) {
|
|
@@ -3230,6 +3343,99 @@ function validateDocumentNode(
|
|
|
3230
3343
|
}
|
|
3231
3344
|
}
|
|
3232
3345
|
|
|
3346
|
+
function validateOptionalBoolean(
|
|
3347
|
+
value: unknown,
|
|
3348
|
+
path: string,
|
|
3349
|
+
issues: ModelValidationIssue[],
|
|
3350
|
+
): void {
|
|
3351
|
+
if (value !== undefined && typeof value !== "boolean") {
|
|
3352
|
+
issues.push({ path, message: "must be a boolean when present." });
|
|
3353
|
+
}
|
|
3354
|
+
}
|
|
3355
|
+
|
|
3356
|
+
function validateCanonicalSourceRef(
|
|
3357
|
+
value: unknown,
|
|
3358
|
+
path: string,
|
|
3359
|
+
issues: ModelValidationIssue[],
|
|
3360
|
+
): void {
|
|
3361
|
+
const record = asPlainObject(value, path, issues);
|
|
3362
|
+
if (!record) {
|
|
3363
|
+
return;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
expectString(record.sourceId, `${path}.sourceId`, issues);
|
|
3367
|
+
|
|
3368
|
+
for (const key of ["partPath", "storyKind", "element"] as const) {
|
|
3369
|
+
if (record[key] !== undefined) {
|
|
3370
|
+
expectString(record[key], `${path}.${key}`, issues);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
|
|
3374
|
+
for (const key of ["ordinal", "startOffset", "endOffset"] as const) {
|
|
3375
|
+
if (record[key] !== undefined && (!Number.isInteger(record[key]) || (record[key] as number) < 0)) {
|
|
3376
|
+
issues.push({
|
|
3377
|
+
path: `${path}.${key}`,
|
|
3378
|
+
message: `${key} must be a non-negative integer when present.`,
|
|
3379
|
+
});
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
function validateHashRef(
|
|
3385
|
+
value: unknown,
|
|
3386
|
+
path: string,
|
|
3387
|
+
issues: ModelValidationIssue[],
|
|
3388
|
+
): void {
|
|
3389
|
+
if (value === undefined) return;
|
|
3390
|
+
const hash = expectString(value, path, issues);
|
|
3391
|
+
if (hash && !/^[A-Fa-f0-9]{64}$/.test(hash)) {
|
|
3392
|
+
issues.push({
|
|
3393
|
+
path,
|
|
3394
|
+
message: "hash must be a 64-character hex SHA-256 digest.",
|
|
3395
|
+
});
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
|
|
3399
|
+
function validateHyperlinkFieldCarrier(
|
|
3400
|
+
value: unknown,
|
|
3401
|
+
path: string,
|
|
3402
|
+
issues: ModelValidationIssue[],
|
|
3403
|
+
): void {
|
|
3404
|
+
const record = asPlainObject(value, path, issues);
|
|
3405
|
+
if (!record) {
|
|
3406
|
+
return;
|
|
3407
|
+
}
|
|
3408
|
+
|
|
3409
|
+
if (record.sourceRef !== undefined) {
|
|
3410
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
3411
|
+
}
|
|
3412
|
+
if (
|
|
3413
|
+
record.fieldFamily !== undefined &&
|
|
3414
|
+
!(
|
|
3415
|
+
SUPPORTED_FIELD_FAMILIES.has(record.fieldFamily as SupportedFieldFamily) ||
|
|
3416
|
+
PRESERVE_ONLY_FIELD_FAMILIES.has(record.fieldFamily as PreserveOnlyFieldFamily)
|
|
3417
|
+
)
|
|
3418
|
+
) {
|
|
3419
|
+
issues.push({
|
|
3420
|
+
path: `${path}.fieldFamily`,
|
|
3421
|
+
message: "fieldFamily must be a SupportedFieldFamily or PreserveOnlyFieldFamily when present.",
|
|
3422
|
+
});
|
|
3423
|
+
}
|
|
3424
|
+
if (
|
|
3425
|
+
record.refreshStatus !== undefined &&
|
|
3426
|
+
!FIELD_REFRESH_STATUSES.has(record.refreshStatus as FieldRefreshStatus)
|
|
3427
|
+
) {
|
|
3428
|
+
issues.push({
|
|
3429
|
+
path: `${path}.refreshStatus`,
|
|
3430
|
+
message: "refreshStatus must be one of: current, stale, unresolvable, preserve-only.",
|
|
3431
|
+
});
|
|
3432
|
+
}
|
|
3433
|
+
validateHashRef(record.instructionHash, `${path}.instructionHash`, issues);
|
|
3434
|
+
validateHashRef(record.resultHash, `${path}.resultHash`, issues);
|
|
3435
|
+
validateOptionalBoolean(record.locked, `${path}.locked`, issues);
|
|
3436
|
+
validateOptionalBoolean(record.dirty, `${path}.dirty`, issues);
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3233
3439
|
function validateFloatingImageProperties(
|
|
3234
3440
|
value: unknown,
|
|
3235
3441
|
path: string,
|
|
@@ -4290,6 +4496,7 @@ const PRESERVE_ONLY_FIELD_FAMILIES: ReadonlySet<PreserveOnlyFieldFamily> = new S
|
|
|
4290
4496
|
"TIME",
|
|
4291
4497
|
"AUTHOR",
|
|
4292
4498
|
"FILENAME",
|
|
4499
|
+
"HYPERLINK",
|
|
4293
4500
|
"MERGEFIELD",
|
|
4294
4501
|
"IF",
|
|
4295
4502
|
"SEQ",
|
|
@@ -4381,6 +4588,10 @@ function validateFieldRegistryEntry(
|
|
|
4381
4588
|
});
|
|
4382
4589
|
}
|
|
4383
4590
|
|
|
4591
|
+
if (record.canonicalFieldId !== undefined) {
|
|
4592
|
+
expectString(record.canonicalFieldId, `${path}.canonicalFieldId`, issues);
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4384
4595
|
if (
|
|
4385
4596
|
typeof record.fieldFamily !== "string" ||
|
|
4386
4597
|
!(
|
|
@@ -4419,6 +4630,10 @@ function validateFieldRegistryEntry(
|
|
|
4419
4630
|
expectString(record.storyKey, `${path}.storyKey`, issues);
|
|
4420
4631
|
}
|
|
4421
4632
|
|
|
4633
|
+
if (record.sourceRef !== undefined) {
|
|
4634
|
+
validateCanonicalSourceRef(record.sourceRef, `${path}.sourceRef`, issues);
|
|
4635
|
+
}
|
|
4636
|
+
|
|
4422
4637
|
if (
|
|
4423
4638
|
typeof record.refreshStatus !== "string" ||
|
|
4424
4639
|
!FIELD_REFRESH_STATUSES.has(record.refreshStatus as FieldRefreshStatus)
|
|
@@ -4426,9 +4641,12 @@ function validateFieldRegistryEntry(
|
|
|
4426
4641
|
issues.push({
|
|
4427
4642
|
path: `${path}.refreshStatus`,
|
|
4428
4643
|
message:
|
|
4429
|
-
|
|
4644
|
+
"refreshStatus must be one of: current, stale, unresolvable, preserve-only.",
|
|
4430
4645
|
});
|
|
4431
4646
|
}
|
|
4647
|
+
|
|
4648
|
+
validateOptionalBoolean(record.locked, `${path}.locked`, issues);
|
|
4649
|
+
validateOptionalBoolean(record.dirty, `${path}.dirty`, issues);
|
|
4432
4650
|
}
|
|
4433
4651
|
|
|
4434
4652
|
function validateTocStructure(
|