@ansonlai/docx-redline-js 0.6.2 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +30 -9
- package/CHANGELOG.md +58 -0
- package/README.md +48 -6
- package/dist/docx-redline-js.esm.js +3 -4
- package/dist/docx-redline-js.esm.js.map +2 -2
- package/dist/docx-redline-js.esm.min.js +51 -51
- package/dist/docx-redline-js.esm.min.js.map +2 -2
- package/docs/AGENT_FAST_START.md +23 -24
- package/docs/AGENT_KNOWLEDGE_BASE.md +36 -14
- package/docs/SKILL_AUTHORING.md +56 -19
- package/docs/releases/0.7.0.md +219 -0
- package/docs/schemas/document-operations.schema.json +14 -1
- package/docs/validation-reports/2026-09-14-localized-patching-rollout.md +90 -0
- package/index.d.ts +2 -0
- package/node/cli-help.js +26 -6
- package/node/cli.js +300 -35
- package/package.json +5 -1
- package/services/batch-operation-orchestrator.js +25 -16
- package/services/document-inspection.js +2 -3
- package/services/document-operation-applier.js +78 -9
- package/services/document-operation-contract.js +105 -7
- package/services/document-operation-mutations.js +36 -36
- package/services/localized-replacement-compiler.js +304 -0
- package/services/operation-batch-compiler.js +42 -1
- package/services/operation-preflight.js +10 -6
- package/services/standalone-operation-runner.d.ts +79 -7
|
@@ -9,15 +9,23 @@ import {
|
|
|
9
9
|
normalizeTargetDescriptor,
|
|
10
10
|
validateDocumentOperation
|
|
11
11
|
} from './document-operation-contract.js';
|
|
12
|
+
import { compileExactReplacements } from './localized-replacement-compiler.js';
|
|
12
13
|
|
|
13
14
|
const TEXT_WRITE_KINDS = new Set(['redline', 'restore', 'rejected-insert']);
|
|
14
15
|
const FORMAT_WRITE_KINDS = new Set(['highlight', 'format', 'paragraph-format']);
|
|
15
16
|
|
|
16
17
|
function normalizedError(error) {
|
|
18
|
+
const details = {};
|
|
19
|
+
if (error && typeof error === 'object') {
|
|
20
|
+
for (const [key, value] of Object.entries(error)) {
|
|
21
|
+
if (key === 'name' || key === 'stack' || key === 'message' || key === 'code') continue;
|
|
22
|
+
details[key] = value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
17
25
|
return {
|
|
18
26
|
code: typeof error?.code === 'string' && error.code ? error.code : 'OPERATION_ERROR',
|
|
19
27
|
message: error?.message || String(error),
|
|
20
|
-
...
|
|
28
|
+
...details
|
|
21
29
|
};
|
|
22
30
|
}
|
|
23
31
|
|
|
@@ -266,6 +274,39 @@ export function compileOperationBatch(xmlDoc, operations = [], options = {}) {
|
|
|
266
274
|
compiled._compiledResolvedBy = start.resolvedBy;
|
|
267
275
|
if (binding.warnings.length > 0) compiled._compiledWarnings = binding.warnings;
|
|
268
276
|
|
|
277
|
+
if (Array.isArray(operation.replacements)) {
|
|
278
|
+
const patchCompilation = compileExactReplacements(
|
|
279
|
+
start.metadata.text,
|
|
280
|
+
operation.replacements
|
|
281
|
+
);
|
|
282
|
+
if (!patchCompilation.ok) {
|
|
283
|
+
throw Object.assign(new Error(patchCompilation.error.message), patchCompilation.error);
|
|
284
|
+
}
|
|
285
|
+
compiled.modified = patchCompilation.desiredText;
|
|
286
|
+
delete compiled.replacements;
|
|
287
|
+
compiled._localizedReplacementCompilation = {
|
|
288
|
+
sourceText: start.metadata.text,
|
|
289
|
+
desiredText: patchCompilation.desiredText,
|
|
290
|
+
replacements: patchCompilation.replacements
|
|
291
|
+
};
|
|
292
|
+
const canonicalValidation = validateDocumentOperation(compiled);
|
|
293
|
+
if (!canonicalValidation.valid) {
|
|
294
|
+
throw Object.assign(
|
|
295
|
+
new Error(canonicalValidation.error.message),
|
|
296
|
+
canonicalValidation.error
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
} else if (operation.operationKind === 'restore' && sourceOperation.modified === undefined) {
|
|
300
|
+
compiled.modified = start.metadata.text;
|
|
301
|
+
const canonicalValidation = validateDocumentOperation(compiled);
|
|
302
|
+
if (!canonicalValidation.valid) {
|
|
303
|
+
throw Object.assign(
|
|
304
|
+
new Error(canonicalValidation.error.message),
|
|
305
|
+
canonicalValidation.error
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
269
310
|
const targetEndDescriptor = operation.targetEndDescriptor
|
|
270
311
|
|| (operation.targetEndRef != null
|
|
271
312
|
? normalizeTargetDescriptor(null, operation.targetEndRef, operation.targetDescriptor.revisionView)
|
|
@@ -136,11 +136,16 @@ export function preflightOperations(documentXml, operations, author, options = {
|
|
|
136
136
|
let commentsRequired = false;
|
|
137
137
|
let numberingRequired = false;
|
|
138
138
|
|
|
139
|
-
for (let index = 0; index < sourceOperations.length; index++) {
|
|
140
|
-
const sourceOperation = sourceOperations[index];
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
139
|
+
for (let index = 0; index < sourceOperations.length; index++) {
|
|
140
|
+
const sourceOperation = sourceOperations[index];
|
|
141
|
+
const compiledBinding = batchCompilation.bindings[index];
|
|
142
|
+
const compiledOperation = batchCompilation.compiledOperations[index];
|
|
143
|
+
const operationInput = !compiledBinding?.error && compiledOperation?._localizedReplacementCompilation
|
|
144
|
+
? compiledOperation
|
|
145
|
+
: sourceOperation;
|
|
146
|
+
const validation = validateDocumentOperation(operationInput);
|
|
147
|
+
const fallbackOperation = normalizeDocumentOperation(sourceOperation);
|
|
148
|
+
const operation = validation.operation || fallbackOperation;
|
|
144
149
|
const authorUsed = resolveDocumentOperationAuthor(operation, author, getDefaultAuthor());
|
|
145
150
|
authorsUsed.add(authorUsed);
|
|
146
151
|
|
|
@@ -187,7 +192,6 @@ export function preflightOperations(documentXml, operations, author, options = {
|
|
|
187
192
|
continue;
|
|
188
193
|
}
|
|
189
194
|
|
|
190
|
-
const compiledBinding = batchCompilation.bindings[index];
|
|
191
195
|
if (compiledBinding?.error) {
|
|
192
196
|
results.push({
|
|
193
197
|
index: index + 1,
|
|
@@ -31,6 +31,12 @@ export interface RejectedTextInsertionAnchor {
|
|
|
31
31
|
offset: number;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
export interface LocalizedReplacement {
|
|
35
|
+
find: string;
|
|
36
|
+
replace: string;
|
|
37
|
+
occurrence?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
34
40
|
export interface DocumentOperationBase {
|
|
35
41
|
operationId?: string;
|
|
36
42
|
captureKey?: string;
|
|
@@ -43,9 +49,7 @@ export interface DocumentOperationBase {
|
|
|
43
49
|
insertionAffinity?: InsertionAffinity;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
type: 'redline' | 'replace' | 'format' | 'list-change' | 'table-reconciliation' | 'insert';
|
|
48
|
-
modified: string;
|
|
52
|
+
interface RedlineDocumentOperationCommon extends DocumentOperationBase {
|
|
49
53
|
structuredContent?: boolean;
|
|
50
54
|
targetEnd?: ParagraphTargetDescriptor;
|
|
51
55
|
targetEndRef?: number | string | null;
|
|
@@ -53,13 +57,50 @@ export interface RedlineDocumentOperation extends DocumentOperationBase {
|
|
|
53
57
|
anchor?: RejectedTextInsertionAnchor;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
export
|
|
60
|
+
export type RedlineDocumentOperation =
|
|
61
|
+
| (RedlineDocumentOperationCommon & {
|
|
62
|
+
type: 'redline' | 'replace';
|
|
63
|
+
modified: string;
|
|
64
|
+
replacements?: never;
|
|
65
|
+
})
|
|
66
|
+
| (RedlineDocumentOperationCommon & {
|
|
67
|
+
type: 'redline' | 'replace';
|
|
68
|
+
modified?: never;
|
|
69
|
+
/** Exact, case-sensitive replacements in one batch-start accepted-view paragraph. */
|
|
70
|
+
replacements: LocalizedReplacement[];
|
|
71
|
+
})
|
|
72
|
+
| (RedlineDocumentOperationCommon & {
|
|
73
|
+
type: 'format' | 'list-change' | 'table-reconciliation' | 'insert';
|
|
74
|
+
modified: string;
|
|
75
|
+
replacements?: never;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
interface RestoreDocumentOperationCommon extends DocumentOperationBase {
|
|
57
79
|
type: 'restore';
|
|
58
|
-
modified: string | string[];
|
|
59
|
-
targetEnd?: ParagraphTargetDescriptor;
|
|
60
|
-
targetEndRef?: number | string | null;
|
|
61
80
|
}
|
|
62
81
|
|
|
82
|
+
export type RestoreDocumentOperation =
|
|
83
|
+
| (RestoreDocumentOperationCommon & {
|
|
84
|
+
modified: string | string[];
|
|
85
|
+
replacements?: never;
|
|
86
|
+
targetEnd?: ParagraphTargetDescriptor;
|
|
87
|
+
targetEndRef?: number | string | null;
|
|
88
|
+
})
|
|
89
|
+
| (RestoreDocumentOperationCommon & {
|
|
90
|
+
modified?: never;
|
|
91
|
+
/** Exact, case-sensitive replacements in one strong rejected-view paragraph. */
|
|
92
|
+
replacements: LocalizedReplacement[];
|
|
93
|
+
targetEnd?: never;
|
|
94
|
+
targetEndRef?: never;
|
|
95
|
+
})
|
|
96
|
+
| (RestoreDocumentOperationCommon & {
|
|
97
|
+
/** Omit modified to restore one strong rejected-view paragraph verbatim. */
|
|
98
|
+
modified?: never;
|
|
99
|
+
replacements?: never;
|
|
100
|
+
targetEnd?: never;
|
|
101
|
+
targetEndRef?: never;
|
|
102
|
+
});
|
|
103
|
+
|
|
63
104
|
export interface DeleteDocumentOperation extends DocumentOperationBase {
|
|
64
105
|
type: 'delete';
|
|
65
106
|
modified?: '';
|
|
@@ -133,6 +174,7 @@ export interface ResolvedDocumentTarget {
|
|
|
133
174
|
paragraphId: string | null;
|
|
134
175
|
text: string;
|
|
135
176
|
fingerprint?: string;
|
|
177
|
+
revisionView?: 'accepted' | 'rejected';
|
|
136
178
|
inTable?: boolean;
|
|
137
179
|
targetTextMatch?: {
|
|
138
180
|
mode: 'exact' | 'space_equivalent' | 'normalized';
|
|
@@ -190,6 +232,34 @@ export interface MutationReceipt {
|
|
|
190
232
|
warnings: string[];
|
|
191
233
|
}
|
|
192
234
|
|
|
235
|
+
export interface LocalizedReplacementChange {
|
|
236
|
+
kind: 'localized_replacement';
|
|
237
|
+
committed: boolean;
|
|
238
|
+
finalDisposition: 'applied' | 'rolled_back';
|
|
239
|
+
target: {
|
|
240
|
+
paragraphId?: string;
|
|
241
|
+
index?: number;
|
|
242
|
+
fingerprint?: string;
|
|
243
|
+
humanReference?: string;
|
|
244
|
+
};
|
|
245
|
+
context?: {
|
|
246
|
+
search?: string;
|
|
247
|
+
range?: string;
|
|
248
|
+
anchorMatchCount?: number;
|
|
249
|
+
humanReference?: string;
|
|
250
|
+
};
|
|
251
|
+
replacements: Array<{
|
|
252
|
+
find: string;
|
|
253
|
+
replace: string;
|
|
254
|
+
occurrence?: number;
|
|
255
|
+
beforeExcerpt: string;
|
|
256
|
+
afterExcerpt: string;
|
|
257
|
+
}>;
|
|
258
|
+
verification: {
|
|
259
|
+
acceptedViewMatchesCompiledText: boolean;
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
193
263
|
export interface DocumentOperationResult {
|
|
194
264
|
documentXml: string;
|
|
195
265
|
hasChanges: boolean;
|
|
@@ -207,6 +277,7 @@ export interface DocumentOperationResult {
|
|
|
207
277
|
resolvedTarget?: ResolvedDocumentTarget;
|
|
208
278
|
resolvedAnchor?: ResolvedCommentAnchor;
|
|
209
279
|
receipt?: MutationReceipt;
|
|
280
|
+
change?: LocalizedReplacementChange;
|
|
210
281
|
}
|
|
211
282
|
|
|
212
283
|
export interface BatchOperationItemResult {
|
|
@@ -221,6 +292,7 @@ export interface BatchOperationItemResult {
|
|
|
221
292
|
warnings?: string[];
|
|
222
293
|
error?: RedlineError;
|
|
223
294
|
receipt?: MutationReceipt;
|
|
295
|
+
change?: LocalizedReplacementChange;
|
|
224
296
|
}
|
|
225
297
|
|
|
226
298
|
export interface DocumentOperationBatchResult {
|