@ansonlai/docx-redline-js 0.4.0 → 0.5.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/AGENTS.md +589 -287
- package/ARCHITECTURE.md +215 -9
- package/CHANGELOG.md +319 -0
- package/README.md +604 -360
- package/adapters/config.js +45 -43
- package/bin/docx-redline.js +3 -0
- package/core/list-targeting.js +101 -110
- package/core/paragraph-targeting.js +501 -61
- package/core/paragraph-text.js +209 -0
- package/core/revision-cloning.js +38 -0
- package/core/types.js +64 -10
- package/core/word-xml.js +43 -15
- package/dist/docx-redline-js.esm.js +2849 -466
- package/dist/docx-redline-js.esm.js.map +4 -4
- package/dist/docx-redline-js.esm.min.js +87 -76
- package/dist/docx-redline-js.esm.min.js.map +4 -4
- package/docs/TESTING.md +342 -23
- package/docs/plans/2026-09-05-structural-revisions-and-fidelity-oracles.md +1669 -0
- package/docs/plans/completed/2026-09-01-performance-and-complexity-reduction.md +669 -0
- package/docs/plans/completed/2026-09-03-agent-friendly-document-workflows.md +427 -0
- package/docs/plans/completed/2026-09-04-comment-anchor-and-cli-reliability.md +519 -0
- package/docs/plans/completed/PERFORMANCE-CONSOLIDATION.md +69 -0
- package/docs/plans/completed/structural-revision-capability-matrix.md +115 -0
- package/docs/schemas/document-operations.schema.json +109 -0
- package/docs/test-comparison-dashboard.html +4250 -7
- package/engine/formatting-removal.js +11 -2
- package/engine/oxml-engine.js +491 -336
- package/engine/reconstruction-mode.js +15 -14
- package/engine/reconstruction-writer.js +247 -142
- package/engine/route-selection.js +35 -0
- package/engine/rpr-helpers.js +334 -35
- package/engine/run-builders.js +239 -196
- package/engine/surgical-diff-application.js +222 -37
- package/engine/surgical-mode.js +134 -6
- package/engine/surgical-spans.js +52 -1
- package/engine/table-cell-context.js +3 -6
- package/engine/table-mode.js +1 -1
- package/index.d.ts +234 -6
- package/index.js +24 -1
- package/node/cli.js +317 -0
- package/node/docx-document.js +302 -0
- package/node/index.d.ts +31 -0
- package/node/index.js +2 -0
- package/node/zip-archive.js +52 -0
- package/orchestration/list-markdown.js +10 -16
- package/orchestration/list-parsing.js +7 -12
- package/orchestration/list-structural-fallback.js +21 -10
- package/package.json +24 -3
- package/pipeline/content-analysis.js +12 -17
- package/pipeline/ingestion-export.js +3 -31
- package/pipeline/ingestion-paragraph.js +10 -5
- package/pipeline/list-generation.js +150 -55
- package/pipeline/list-markers.js +70 -3
- package/pipeline/serialization.js +4 -2
- package/pipeline/structured-content.js +160 -0
- package/scripts/apply_changes.mjs +27 -0
- package/scripts/benchmark-operation-session.mjs +137 -0
- package/scripts/benchmark-targeting-browser.html +74 -0
- package/scripts/benchmark-targeting-hot-paths.mjs +67 -0
- package/scripts/benchmark-test-runner.mjs +59 -0
- package/scripts/build-test-dashboard.mjs +23 -0
- package/scripts/export-lane1-fixtures.mjs +380 -0
- package/scripts/export-reredline-stress-fixtures.mjs +317 -0
- package/scripts/export-validation-fixtures.mjs +1 -1
- package/scripts/extract_text.mjs +7 -0
- package/scripts/generate-paragraph-boundary-fixtures.ps1 +215 -0
- package/scripts/generate-test-dashboard.mjs +362 -11
- package/scripts/lib/word-coverage-catalogue.mjs +6 -2
- package/scripts/profile-route-selection.mjs +19 -0
- package/scripts/render-agenda-multilevel.mjs +0 -5
- package/scripts/render-multilevel-cases.mjs +0 -1
- package/scripts/run-tests.mjs +107 -35
- package/scripts/word-com-corpus-suite.ps1 +3 -0
- package/scripts/word-com-differential.ps1 +64 -4
- package/scripts/word-com-suite.ps1 +3 -0
- package/services/batch-operation-orchestrator.js +494 -0
- package/services/capture-engine.js +226 -0
- package/services/comment-builders.js +23 -6
- package/services/comment-engine.js +108 -47
- package/services/comment-locator.js +187 -82
- package/services/comment-replies.js +95 -0
- package/services/document-inspection.js +258 -0
- package/services/document-operation-applier.js +372 -0
- package/services/document-operation-contract.js +323 -0
- package/services/document-operation-mutations.js +1733 -0
- package/services/document-operation-session.js +258 -0
- package/services/numbering-service.js +14 -5
- package/services/operation-heuristics.js +173 -0
- package/services/operation-preflight.js +366 -0
- package/services/receipt-collector.js +288 -0
- package/services/revision-comment-management.js +37 -5
- package/services/revision-token.js +290 -0
- package/services/standalone-docx-plumbing.js +123 -8
- package/services/standalone-operation-runner.d.ts +296 -0
- package/services/standalone-operation-runner.js +10 -1455
- package/services/table-reconciliation.js +15 -6
- package/docs/VALIDATION.md +0 -183
- package/docs/WORD-MANUAL-REVIEW.md +0 -138
- package/docs/plans/2026-09-01-performance-and-complexity-reduction.md +0 -210
- /package/docs/plans/{2026-08-30-reliability-testing-improvements.md → completed/2026-08-30-reliability-testing-improvements.md} +0 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import type { ExistingRevisionsPolicy, RedlineError, RedlineStatus, RevisionToken } from '../index.js';
|
|
2
|
+
|
|
3
|
+
export interface ParagraphTargetDescriptor {
|
|
4
|
+
text?: string;
|
|
5
|
+
exactText?: string;
|
|
6
|
+
index?: number | string;
|
|
7
|
+
paragraphIndex?: number | string;
|
|
8
|
+
paragraphId?: string;
|
|
9
|
+
occurrence?: number;
|
|
10
|
+
inTable?: boolean;
|
|
11
|
+
fingerprint?: string;
|
|
12
|
+
sourceFingerprint?: string;
|
|
13
|
+
revisionView?: 'accepted' | 'rejected';
|
|
14
|
+
captureRef?: string;
|
|
15
|
+
select?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface InsertionAffinity {
|
|
19
|
+
formatting?: 'left' | 'right' | 'none';
|
|
20
|
+
hyperlink?: 'inside' | 'outside' | 'preserve';
|
|
21
|
+
revision?: 'coalesce_same_author' | 'separate';
|
|
22
|
+
bookmark?: 'inside' | 'outside';
|
|
23
|
+
comment?: 'inside' | 'outside';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface DocumentOperationBase {
|
|
27
|
+
operationId?: string;
|
|
28
|
+
captureKey?: string;
|
|
29
|
+
target?: string | ParagraphTargetDescriptor;
|
|
30
|
+
targetRef?: number | string | null;
|
|
31
|
+
author?: string;
|
|
32
|
+
generateRedlines?: boolean;
|
|
33
|
+
existingRevisions?: ExistingRevisionsPolicy;
|
|
34
|
+
pairReplacements?: boolean;
|
|
35
|
+
insertionAffinity?: InsertionAffinity;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface RedlineDocumentOperation extends DocumentOperationBase {
|
|
39
|
+
type: 'redline' | 'replace' | 'format' | 'list-change' | 'table-reconciliation' | 'insert';
|
|
40
|
+
modified: string;
|
|
41
|
+
structuredContent?: boolean;
|
|
42
|
+
targetEnd?: ParagraphTargetDescriptor;
|
|
43
|
+
targetEndRef?: number | string | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface DeleteDocumentOperation extends DocumentOperationBase {
|
|
47
|
+
type: 'delete';
|
|
48
|
+
modified?: '';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface CommentDocumentOperation extends DocumentOperationBase {
|
|
52
|
+
type: 'comment';
|
|
53
|
+
textToComment?: string;
|
|
54
|
+
commentContent: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface CommentReplyDocumentOperation {
|
|
58
|
+
type: 'comment_reply';
|
|
59
|
+
operationId?: string;
|
|
60
|
+
parentCommentId: number | string;
|
|
61
|
+
commentContent: string;
|
|
62
|
+
author?: string;
|
|
63
|
+
date?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CharacterFormatProperties {
|
|
67
|
+
bold?: boolean;
|
|
68
|
+
italic?: boolean;
|
|
69
|
+
underline?: boolean;
|
|
70
|
+
strike?: boolean;
|
|
71
|
+
strikethrough?: boolean;
|
|
72
|
+
highlight?: string | null;
|
|
73
|
+
color?: string | null;
|
|
74
|
+
fontSize?: number | string | null;
|
|
75
|
+
fontFamily?: string | null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface ParagraphFormatProperties {
|
|
79
|
+
alignment?: 'left' | 'center' | 'right' | 'both';
|
|
80
|
+
keepNext?: boolean;
|
|
81
|
+
keepLines?: boolean;
|
|
82
|
+
pageBreakBefore?: boolean;
|
|
83
|
+
style?: string | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface HighlightDocumentOperation extends DocumentOperationBase {
|
|
87
|
+
type: 'highlight';
|
|
88
|
+
textToHighlight: string;
|
|
89
|
+
color?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CharacterFormatDocumentOperation extends DocumentOperationBase {
|
|
93
|
+
type: 'format' | 'character-format';
|
|
94
|
+
textToFormat: string;
|
|
95
|
+
properties: CharacterFormatProperties;
|
|
96
|
+
formattingRevisionPolicy?: 'always' | 'coalesce-own-insertion';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ParagraphFormatDocumentOperation extends DocumentOperationBase {
|
|
100
|
+
type: 'paragraph-format';
|
|
101
|
+
properties: ParagraphFormatProperties;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type DocumentOperation =
|
|
105
|
+
| RedlineDocumentOperation
|
|
106
|
+
| DeleteDocumentOperation
|
|
107
|
+
| CommentDocumentOperation
|
|
108
|
+
| CommentReplyDocumentOperation
|
|
109
|
+
| HighlightDocumentOperation
|
|
110
|
+
| CharacterFormatDocumentOperation
|
|
111
|
+
| ParagraphFormatDocumentOperation;
|
|
112
|
+
|
|
113
|
+
export interface ResolvedDocumentTarget {
|
|
114
|
+
index: number;
|
|
115
|
+
paragraphId: string | null;
|
|
116
|
+
text: string;
|
|
117
|
+
fingerprint?: string;
|
|
118
|
+
inTable?: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ResolvedCommentAnchor {
|
|
122
|
+
requestIndex: number;
|
|
123
|
+
paragraphIndex: number;
|
|
124
|
+
text: string;
|
|
125
|
+
resolvedBy: 'exact_anchor' | 'space_equivalent_anchor';
|
|
126
|
+
start: number;
|
|
127
|
+
end: number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface StandaloneRunnerOptions {
|
|
131
|
+
atomic?: boolean;
|
|
132
|
+
continueOnError?: boolean;
|
|
133
|
+
generateRedlines?: boolean;
|
|
134
|
+
existingRevisions?: ExistingRevisionsPolicy;
|
|
135
|
+
strictTargets?: boolean;
|
|
136
|
+
expectedRevision?: RevisionToken;
|
|
137
|
+
pairReplacements?: boolean;
|
|
138
|
+
insertionAffinity?: InsertionAffinity;
|
|
139
|
+
onInfo?: (message: string) => void;
|
|
140
|
+
onWarn?: (message: string) => void;
|
|
141
|
+
[key: string]: unknown;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface MutationReceiptRevisionItem {
|
|
145
|
+
id: string;
|
|
146
|
+
kind: 'ins' | 'del' | 'move_from' | 'move_to' | 'rPrChange' | 'pPrChange' | 'structural';
|
|
147
|
+
partName: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface MutationReceipt {
|
|
151
|
+
operationIndex: number;
|
|
152
|
+
operationId?: string;
|
|
153
|
+
attemptedDisposition: 'applied' | 'no_change' | 'refused' | 'not_attempted';
|
|
154
|
+
finalDisposition: 'applied' | 'no_change' | 'refused' | 'rolled_back' | 'not_attempted';
|
|
155
|
+
committed: boolean;
|
|
156
|
+
authorUsed?: string;
|
|
157
|
+
revisionItems: MutationReceiptRevisionItem[];
|
|
158
|
+
commentIds: string[];
|
|
159
|
+
numberingIds: string[];
|
|
160
|
+
relationshipIds: string[];
|
|
161
|
+
affectedTargets: ResolvedDocumentTarget[];
|
|
162
|
+
warnings: string[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface DocumentOperationResult {
|
|
166
|
+
documentXml: string;
|
|
167
|
+
hasChanges: boolean;
|
|
168
|
+
numberingXml?: string | null;
|
|
169
|
+
commentsXml?: string | null;
|
|
170
|
+
commentsExtendedXml?: string | null;
|
|
171
|
+
commentsXmlMode?: 'merge' | 'replace';
|
|
172
|
+
commentsExtendedXmlMode?: 'merge' | 'replace';
|
|
173
|
+
warnings?: string[];
|
|
174
|
+
status?: RedlineStatus;
|
|
175
|
+
error?: RedlineError;
|
|
176
|
+
operationType: 'redline' | 'comment' | 'comment_reply' | 'highlight' | 'format' | 'paragraph-format';
|
|
177
|
+
authorUsed: string;
|
|
178
|
+
resolvedBy?: string;
|
|
179
|
+
resolvedTarget?: ResolvedDocumentTarget;
|
|
180
|
+
resolvedAnchor?: ResolvedCommentAnchor;
|
|
181
|
+
receipt?: MutationReceipt;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface BatchOperationItemResult {
|
|
185
|
+
index: number;
|
|
186
|
+
type: string;
|
|
187
|
+
operationType: 'redline' | 'comment' | 'comment_reply' | 'highlight' | 'format' | 'paragraph-format';
|
|
188
|
+
status: 'applied' | 'no_change' | 'error';
|
|
189
|
+
authorUsed: string;
|
|
190
|
+
resolvedBy?: string;
|
|
191
|
+
resolvedTarget?: ResolvedDocumentTarget;
|
|
192
|
+
resolvedAnchor?: ResolvedCommentAnchor;
|
|
193
|
+
warnings?: string[];
|
|
194
|
+
error?: RedlineError;
|
|
195
|
+
receipt?: MutationReceipt;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export interface DocumentOperationBatchResult {
|
|
199
|
+
documentXml: string;
|
|
200
|
+
hasChanges: boolean;
|
|
201
|
+
commentsXml: string | null;
|
|
202
|
+
commentsExtendedXml?: string | null;
|
|
203
|
+
commentsXmlMode?: 'merge' | 'replace';
|
|
204
|
+
commentsExtendedXmlMode?: 'merge' | 'replace';
|
|
205
|
+
numberingXmlParts: string[];
|
|
206
|
+
results: BatchOperationItemResult[];
|
|
207
|
+
receipts?: MutationReceipt[];
|
|
208
|
+
executionOrder: number[];
|
|
209
|
+
/** Authors attached to committed changes. Empty when an atomic batch rolls back. */
|
|
210
|
+
authorsUsed: string[];
|
|
211
|
+
rolledBack?: boolean;
|
|
212
|
+
status?: RedlineStatus;
|
|
213
|
+
error?: RedlineError;
|
|
214
|
+
warnings?: string[];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export interface OperationPreflightItemResult {
|
|
218
|
+
index: number;
|
|
219
|
+
type: string;
|
|
220
|
+
operationType: 'redline' | 'comment' | 'comment_reply' | 'highlight' | 'format' | 'paragraph-format';
|
|
221
|
+
status: 'ready' | 'deferred' | 'error';
|
|
222
|
+
authorUsed: string;
|
|
223
|
+
resolvedBy?: string;
|
|
224
|
+
captureRef?: string;
|
|
225
|
+
select?: string;
|
|
226
|
+
resolvedTarget?: ResolvedDocumentTarget;
|
|
227
|
+
matchDiagnostics?: {
|
|
228
|
+
exactTextMatch: boolean;
|
|
229
|
+
normalizedTextMatch: boolean;
|
|
230
|
+
suppliedText: string;
|
|
231
|
+
actualText: string;
|
|
232
|
+
};
|
|
233
|
+
anchor?: {
|
|
234
|
+
text: string;
|
|
235
|
+
found: boolean;
|
|
236
|
+
resolvedBy?: 'exact_anchor' | 'space_equivalent_anchor';
|
|
237
|
+
start?: number;
|
|
238
|
+
end?: number;
|
|
239
|
+
candidates?: Array<{ start: number; end: number }>;
|
|
240
|
+
} | null;
|
|
241
|
+
hasRevisions?: boolean;
|
|
242
|
+
existingRevisions?: ExistingRevisionsPolicy;
|
|
243
|
+
warnings?: string[];
|
|
244
|
+
error?: RedlineError & { candidates?: ResolvedDocumentTarget[] };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface OperationConflict {
|
|
248
|
+
code: 'OVERLAPPING_TEXT_EDITS' | 'REVISION_ORDER_CONFLICT' | string;
|
|
249
|
+
message: string;
|
|
250
|
+
operationIndexes: number[];
|
|
251
|
+
target: ResolvedDocumentTarget;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export interface OperationPreflightResult {
|
|
255
|
+
valid: boolean;
|
|
256
|
+
status: 'ok' | 'error';
|
|
257
|
+
results: OperationPreflightItemResult[];
|
|
258
|
+
conflicts: OperationConflict[];
|
|
259
|
+
authorsUsed: string[];
|
|
260
|
+
requiredArtifacts: { comments: boolean; numbering: boolean };
|
|
261
|
+
error?: RedlineError;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface OperationDependencyPlan {
|
|
265
|
+
valid: boolean;
|
|
266
|
+
scheduled?: Array<{ operation: DocumentOperation; index: number }>;
|
|
267
|
+
captureProducers?: Map<string, number>;
|
|
268
|
+
error?: RedlineError;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function buildOperationDependencyPlan(operations?: DocumentOperation[]): OperationDependencyPlan;
|
|
272
|
+
|
|
273
|
+
export function orderOperationsForStableTargets(operations?: DocumentOperation[]): DocumentOperation[];
|
|
274
|
+
|
|
275
|
+
export function preflightOperations(
|
|
276
|
+
documentXml: string,
|
|
277
|
+
operations: DocumentOperation[],
|
|
278
|
+
author?: string,
|
|
279
|
+
options?: StandaloneRunnerOptions
|
|
280
|
+
): OperationPreflightResult;
|
|
281
|
+
|
|
282
|
+
export function applyOperationToDocumentXml(
|
|
283
|
+
documentXml: string,
|
|
284
|
+
operation: DocumentOperation,
|
|
285
|
+
author?: string,
|
|
286
|
+
runtimeContext?: Record<string, unknown> | null,
|
|
287
|
+
options?: StandaloneRunnerOptions
|
|
288
|
+
): Promise<DocumentOperationResult>;
|
|
289
|
+
|
|
290
|
+
export function applyOperationsToDocumentXml(
|
|
291
|
+
documentXml: string,
|
|
292
|
+
operations: DocumentOperation[],
|
|
293
|
+
author?: string,
|
|
294
|
+
runtimeContext?: Record<string, unknown> | null,
|
|
295
|
+
options?: StandaloneRunnerOptions
|
|
296
|
+
): Promise<DocumentOperationBatchResult>;
|