@adeu/core 1.10.0 → 1.10.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/dist/index.cjs +30 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +30 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/engine.ts +126 -43
package/package.json
CHANGED
package/src/engine.ts
CHANGED
|
@@ -255,7 +255,9 @@ export class RedlineEngine {
|
|
|
255
255
|
return null;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
private _build_edit_context_previews(
|
|
258
|
+
private _build_edit_context_previews(
|
|
259
|
+
edit: any,
|
|
260
|
+
): [string | null, string | null] {
|
|
259
261
|
if (edit.type !== "modify") return [null, null];
|
|
260
262
|
if (edit._resolved_proxy_edit) {
|
|
261
263
|
edit = edit._resolved_proxy_edit;
|
|
@@ -271,7 +273,10 @@ export class RedlineEngine {
|
|
|
271
273
|
|
|
272
274
|
const before_start = Math.max(0, start_idx - 30);
|
|
273
275
|
const context_before = full_text.substring(before_start, start_idx);
|
|
274
|
-
const context_after = full_text.substring(
|
|
276
|
+
const context_after = full_text.substring(
|
|
277
|
+
start_idx + length,
|
|
278
|
+
start_idx + length + 30,
|
|
279
|
+
);
|
|
275
280
|
|
|
276
281
|
const critic_markup = `${context_before}{--${target_text}--}{++${new_text}++}${context_after}`;
|
|
277
282
|
|
|
@@ -337,7 +342,7 @@ export class RedlineEngine {
|
|
|
337
342
|
for (const tag of ["w:t", "w:tab", "w:br"]) {
|
|
338
343
|
for (const child of findAllDescendants(p, tag)) {
|
|
339
344
|
if (tag === "w:t" && !child.textContent) continue;
|
|
340
|
-
|
|
345
|
+
|
|
341
346
|
let is_deleted = false;
|
|
342
347
|
let curr = child.parentNode as Element | null;
|
|
343
348
|
while (curr && curr !== p) {
|
|
@@ -401,7 +406,10 @@ export class RedlineEngine {
|
|
|
401
406
|
if (parent) {
|
|
402
407
|
if (parent.tagName === "w:r" || parent.tagName.endsWith(":r")) {
|
|
403
408
|
const nonRprChildren = Array.from(parent.childNodes).filter(
|
|
404
|
-
(c) =>
|
|
409
|
+
(c) =>
|
|
410
|
+
c.nodeType === 1 &&
|
|
411
|
+
(c as Element).tagName !== "w:rPr" &&
|
|
412
|
+
(c as Element).tagName !== "rPr",
|
|
405
413
|
);
|
|
406
414
|
if (nonRprChildren.length <= 1) {
|
|
407
415
|
parent.parentNode?.removeChild(parent);
|
|
@@ -420,8 +428,12 @@ export class RedlineEngine {
|
|
|
420
428
|
for (const part of pkg.parts) {
|
|
421
429
|
if (part.partname.toLowerCase().includes("comments")) {
|
|
422
430
|
comment_partnames.add(part.partname);
|
|
423
|
-
const withSlash = part.partname.startsWith("/")
|
|
424
|
-
|
|
431
|
+
const withSlash = part.partname.startsWith("/")
|
|
432
|
+
? part.partname
|
|
433
|
+
: "/" + part.partname;
|
|
434
|
+
const withoutSlash = part.partname.startsWith("/")
|
|
435
|
+
? part.partname.substring(1)
|
|
436
|
+
: part.partname;
|
|
425
437
|
comment_partnames.add(withSlash);
|
|
426
438
|
comment_partnames.add(withoutSlash);
|
|
427
439
|
}
|
|
@@ -437,8 +449,10 @@ export class RedlineEngine {
|
|
|
437
449
|
const target = rel.getAttribute("Target") || "";
|
|
438
450
|
if (target.toLowerCase().includes("comments")) {
|
|
439
451
|
toRemove.push(rel);
|
|
440
|
-
|
|
441
|
-
const sourcePath = part.partname
|
|
452
|
+
|
|
453
|
+
const sourcePath = part.partname
|
|
454
|
+
.replace("/_rels/", "/")
|
|
455
|
+
.replace(".rels", "");
|
|
442
456
|
const sourcePart = pkg.getPartByPath(sourcePath);
|
|
443
457
|
if (sourcePart) {
|
|
444
458
|
const relId = rel.getAttribute("Id");
|
|
@@ -459,7 +473,10 @@ export class RedlineEngine {
|
|
|
459
473
|
const toRemove: Element[] = [];
|
|
460
474
|
for (const override of overrides) {
|
|
461
475
|
const partName = override.getAttribute("PartName") || "";
|
|
462
|
-
if (
|
|
476
|
+
if (
|
|
477
|
+
comment_partnames.has(partName) ||
|
|
478
|
+
partName.toLowerCase().includes("comments")
|
|
479
|
+
) {
|
|
463
480
|
toRemove.push(override);
|
|
464
481
|
}
|
|
465
482
|
}
|
|
@@ -469,7 +486,9 @@ export class RedlineEngine {
|
|
|
469
486
|
}
|
|
470
487
|
|
|
471
488
|
// Remove comment parts from pkg.parts
|
|
472
|
-
pkg.parts = pkg.parts.filter(
|
|
489
|
+
pkg.parts = pkg.parts.filter(
|
|
490
|
+
(p) => !p.partname.toLowerCase().includes("comments"),
|
|
491
|
+
);
|
|
473
492
|
|
|
474
493
|
// Remove comment files from pkg.unzipped
|
|
475
494
|
for (const key of Object.keys(pkg.unzipped)) {
|
|
@@ -1230,18 +1249,29 @@ export class RedlineEngine {
|
|
|
1230
1249
|
const [pfx, sfx] = trim_common_context(matched, edit.new_text || "");
|
|
1231
1250
|
const t_end = matched.length - sfx;
|
|
1232
1251
|
const final_target = matched.substring(pfx, t_end);
|
|
1233
|
-
const final_new = (edit.new_text || "").substring(
|
|
1252
|
+
const final_new = (edit.new_text || "").substring(
|
|
1253
|
+
pfx,
|
|
1254
|
+
(edit.new_text || "").length - sfx,
|
|
1255
|
+
);
|
|
1234
1256
|
if (final_target.includes("\n\n")) {
|
|
1235
1257
|
if (final_new.includes("\n\n")) {
|
|
1236
1258
|
const parts = matched.split("\n\n");
|
|
1237
|
-
if (
|
|
1259
|
+
if (
|
|
1260
|
+
parts.length >= 2 &&
|
|
1261
|
+
parts[0].trim() !== "" &&
|
|
1262
|
+
parts[parts.length - 1].trim() !== ""
|
|
1263
|
+
) {
|
|
1238
1264
|
errors.push(
|
|
1239
1265
|
`- Edit ${i + 1} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`,
|
|
1240
1266
|
);
|
|
1241
1267
|
}
|
|
1242
1268
|
} else {
|
|
1243
1269
|
const parts = final_target.split("\n\n");
|
|
1244
|
-
if (
|
|
1270
|
+
if (
|
|
1271
|
+
parts.length >= 2 &&
|
|
1272
|
+
parts[0].trim() !== "" &&
|
|
1273
|
+
parts[parts.length - 1].trim() !== ""
|
|
1274
|
+
) {
|
|
1245
1275
|
errors.push(
|
|
1246
1276
|
`- Edit ${i + 1} Failed: target_text spans a paragraph boundary with body text on both sides. The paragraph break is a structural element, not literal text, so it cannot be replaced as a single span without corrupting the document. Split this into one edit per paragraph.`,
|
|
1247
1277
|
);
|
|
@@ -1318,7 +1348,10 @@ export class RedlineEngine {
|
|
|
1318
1348
|
return errors;
|
|
1319
1349
|
}
|
|
1320
1350
|
|
|
1321
|
-
public process_batch(
|
|
1351
|
+
public process_batch(
|
|
1352
|
+
changes: DocumentChange[],
|
|
1353
|
+
dry_run: boolean = false,
|
|
1354
|
+
): any {
|
|
1322
1355
|
if (dry_run) {
|
|
1323
1356
|
const baselines = new Map<any, Element>();
|
|
1324
1357
|
for (const part of this.doc.pkg.parts) {
|
|
@@ -1345,7 +1378,10 @@ export class RedlineEngine {
|
|
|
1345
1378
|
}
|
|
1346
1379
|
}
|
|
1347
1380
|
|
|
1348
|
-
private _process_batch_internal(
|
|
1381
|
+
private _process_batch_internal(
|
|
1382
|
+
changes: DocumentChange[],
|
|
1383
|
+
dry_run_mode: boolean = false,
|
|
1384
|
+
): any {
|
|
1349
1385
|
this.skipped_details = [];
|
|
1350
1386
|
const actions = changes.filter((c) =>
|
|
1351
1387
|
["accept", "reject", "reply"].includes(c.type),
|
|
@@ -1398,7 +1434,9 @@ export class RedlineEngine {
|
|
|
1398
1434
|
if (dry_run_mode) {
|
|
1399
1435
|
for (const edit of edits) {
|
|
1400
1436
|
const single_errors = this.validate_edits([edit]);
|
|
1401
|
-
const warning = this._check_punctuation_warning(
|
|
1437
|
+
const warning = this._check_punctuation_warning(
|
|
1438
|
+
(edit as any).target_text || "",
|
|
1439
|
+
);
|
|
1402
1440
|
if (single_errors.length > 0) {
|
|
1403
1441
|
skipped_edits++;
|
|
1404
1442
|
edits_reports.push({
|
|
@@ -1428,7 +1466,10 @@ export class RedlineEngine {
|
|
|
1428
1466
|
});
|
|
1429
1467
|
} else {
|
|
1430
1468
|
skipped_edits++;
|
|
1431
|
-
const error_msg =
|
|
1469
|
+
const error_msg =
|
|
1470
|
+
this.skipped_details.length > 0
|
|
1471
|
+
? this.skipped_details[this.skipped_details.length - 1]
|
|
1472
|
+
: "Failed to apply edit";
|
|
1432
1473
|
edits_reports.push({
|
|
1433
1474
|
status: "failed",
|
|
1434
1475
|
target_text: (edit as any).target_text || "",
|
|
@@ -1445,7 +1486,7 @@ export class RedlineEngine {
|
|
|
1445
1486
|
if (errors.length > 0) {
|
|
1446
1487
|
throw new BatchValidationError(errors);
|
|
1447
1488
|
}
|
|
1448
|
-
const cloned_edits = edits.map(e => JSON.parse(JSON.stringify(e)));
|
|
1489
|
+
const cloned_edits = edits.map((e) => JSON.parse(JSON.stringify(e)));
|
|
1449
1490
|
const res = this.apply_edits(cloned_edits);
|
|
1450
1491
|
applied_edits = res[0];
|
|
1451
1492
|
skipped_edits = res[1];
|
|
@@ -1453,7 +1494,9 @@ export class RedlineEngine {
|
|
|
1453
1494
|
for (const edit of cloned_edits) {
|
|
1454
1495
|
const success = (edit as any)._applied_status || false;
|
|
1455
1496
|
const error_msg = (edit as any)._error_msg || null;
|
|
1456
|
-
const warning = this._check_punctuation_warning(
|
|
1497
|
+
const warning = this._check_punctuation_warning(
|
|
1498
|
+
(edit as any).target_text || "",
|
|
1499
|
+
);
|
|
1457
1500
|
let critic_markup = null;
|
|
1458
1501
|
let clean_text = null;
|
|
1459
1502
|
if (success) {
|
|
@@ -1482,7 +1525,7 @@ export class RedlineEngine {
|
|
|
1482
1525
|
skipped_details: this.skipped_details,
|
|
1483
1526
|
edits: edits_reports,
|
|
1484
1527
|
engine: "node",
|
|
1485
|
-
version: "1.
|
|
1528
|
+
version: "1.10.0",
|
|
1486
1529
|
};
|
|
1487
1530
|
}
|
|
1488
1531
|
|
|
@@ -1523,7 +1566,9 @@ export class RedlineEngine {
|
|
|
1523
1566
|
} else {
|
|
1524
1567
|
skipped++;
|
|
1525
1568
|
edit._applied_status = false;
|
|
1526
|
-
const target_snippet = (edit.target_text || "")
|
|
1569
|
+
const target_snippet = (edit.target_text || "")
|
|
1570
|
+
.trim()
|
|
1571
|
+
.substring(0, 40);
|
|
1527
1572
|
const msg = `- Failed to locate row target: '${target_snippet}...'`;
|
|
1528
1573
|
this.skipped_details.push(msg);
|
|
1529
1574
|
edit._error_msg = msg;
|
|
@@ -1535,7 +1580,10 @@ export class RedlineEngine {
|
|
|
1535
1580
|
for (const r of resolved) {
|
|
1536
1581
|
r._resolved_start_idx = r._match_start_index;
|
|
1537
1582
|
r._parent_edit_ref = edit;
|
|
1538
|
-
if (
|
|
1583
|
+
if (
|
|
1584
|
+
edit._resolved_start_idx === undefined ||
|
|
1585
|
+
edit._resolved_start_idx === null
|
|
1586
|
+
) {
|
|
1539
1587
|
edit._resolved_start_idx = r._resolved_start_idx;
|
|
1540
1588
|
}
|
|
1541
1589
|
if (!edit._resolved_proxy_edit) {
|
|
@@ -1563,7 +1611,8 @@ export class RedlineEngine {
|
|
|
1563
1611
|
}
|
|
1564
1612
|
|
|
1565
1613
|
resolved_edits.sort(
|
|
1566
|
-
(a, b) =>
|
|
1614
|
+
(a, b) =>
|
|
1615
|
+
(b[0]._resolved_start_idx || 0) - (a[0]._resolved_start_idx || 0),
|
|
1567
1616
|
);
|
|
1568
1617
|
const occupied_ranges: [number, number][] = [];
|
|
1569
1618
|
|
|
@@ -1722,7 +1771,11 @@ export class RedlineEngine {
|
|
|
1722
1771
|
}
|
|
1723
1772
|
|
|
1724
1773
|
private _apply_table_edit(edit: any, rebuild_map: boolean): boolean {
|
|
1725
|
-
const start_idx =
|
|
1774
|
+
const start_idx =
|
|
1775
|
+
edit._resolved_start_idx !== undefined &&
|
|
1776
|
+
edit._resolved_start_idx !== null
|
|
1777
|
+
? edit._resolved_start_idx
|
|
1778
|
+
: edit._match_start_index || 0;
|
|
1726
1779
|
const [anchor_run, anchor_para] = this.mapper.get_insertion_anchor(
|
|
1727
1780
|
start_idx,
|
|
1728
1781
|
rebuild_map,
|
|
@@ -1877,7 +1930,11 @@ export class RedlineEngine {
|
|
|
1877
1930
|
): boolean {
|
|
1878
1931
|
let op = edit._internal_op;
|
|
1879
1932
|
const active_mapper = edit._active_mapper_ref || this.mapper;
|
|
1880
|
-
const start_idx =
|
|
1933
|
+
const start_idx =
|
|
1934
|
+
edit._resolved_start_idx !== undefined &&
|
|
1935
|
+
edit._resolved_start_idx !== null
|
|
1936
|
+
? edit._resolved_start_idx
|
|
1937
|
+
: edit._match_start_index || 0;
|
|
1881
1938
|
const length = edit.target_text ? edit.target_text.length : 0;
|
|
1882
1939
|
|
|
1883
1940
|
const del_id = ["DELETION", "MODIFICATION"].includes(op)
|
|
@@ -1967,7 +2024,9 @@ export class RedlineEngine {
|
|
|
1967
2024
|
) {
|
|
1968
2025
|
const body = _bug233_target_para.parentNode as Element;
|
|
1969
2026
|
const xmlDoc = this.doc.part._element.ownerDocument!;
|
|
1970
|
-
const lines = _bug233_new
|
|
2027
|
+
const lines = _bug233_new
|
|
2028
|
+
.split(/[\r\n]+/)
|
|
2029
|
+
.filter((l: string) => l !== "");
|
|
1971
2030
|
let firstNew: Element | null = null;
|
|
1972
2031
|
let lastNew: Element | null = null;
|
|
1973
2032
|
let lastIns: Element | null = null;
|
|
@@ -2069,7 +2128,9 @@ export class RedlineEngine {
|
|
|
2069
2128
|
if (start_p) {
|
|
2070
2129
|
let first_anchor_target = result.first_node;
|
|
2071
2130
|
if (result.first_node.tagName === "w:p") {
|
|
2072
|
-
first_anchor_target =
|
|
2131
|
+
first_anchor_target =
|
|
2132
|
+
findAllDescendants(result.first_node, "w:ins")[0] ||
|
|
2133
|
+
result.first_node;
|
|
2073
2134
|
}
|
|
2074
2135
|
const start_anchor = ascend_to_paragraph_child(
|
|
2075
2136
|
first_anchor_target,
|
|
@@ -2086,22 +2147,27 @@ export class RedlineEngine {
|
|
|
2086
2147
|
end_anchor,
|
|
2087
2148
|
edit.comment,
|
|
2088
2149
|
);
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2150
|
+
}
|
|
2151
|
+
} else {
|
|
2152
|
+
// Inline only: anchor around first_node in its host paragraph.
|
|
2153
|
+
let host_p: Element | null = result.first_node;
|
|
2154
|
+
while (host_p && host_p.tagName !== "w:p")
|
|
2094
2155
|
host_p = host_p.parentNode as Element;
|
|
2095
|
-
|
|
2156
|
+
if (host_p) {
|
|
2096
2157
|
let first_anchor_target = result.first_node;
|
|
2097
2158
|
if (result.first_node.tagName === "w:p") {
|
|
2098
|
-
first_anchor_target =
|
|
2159
|
+
first_anchor_target =
|
|
2160
|
+
findAllDescendants(result.first_node, "w:ins")[0] ||
|
|
2161
|
+
result.first_node;
|
|
2099
2162
|
}
|
|
2100
|
-
const anchor = ascend_to_paragraph_child(
|
|
2163
|
+
const anchor = ascend_to_paragraph_child(
|
|
2164
|
+
first_anchor_target,
|
|
2165
|
+
host_p,
|
|
2166
|
+
);
|
|
2101
2167
|
this._attach_comment(host_p, anchor, anchor, edit.comment);
|
|
2102
|
-
}
|
|
2103
2168
|
}
|
|
2104
2169
|
}
|
|
2170
|
+
}
|
|
2105
2171
|
return true;
|
|
2106
2172
|
}
|
|
2107
2173
|
|
|
@@ -2111,7 +2177,10 @@ export class RedlineEngine {
|
|
|
2111
2177
|
length,
|
|
2112
2178
|
rebuild_map,
|
|
2113
2179
|
);
|
|
2114
|
-
const virtual_spans = active_mapper.get_virtual_spans_in_range(
|
|
2180
|
+
const virtual_spans = active_mapper.get_virtual_spans_in_range(
|
|
2181
|
+
start_idx,
|
|
2182
|
+
length,
|
|
2183
|
+
);
|
|
2115
2184
|
|
|
2116
2185
|
if (target_runs.length === 0 && virtual_spans.length === 0) return false;
|
|
2117
2186
|
|
|
@@ -2187,18 +2256,26 @@ export class RedlineEngine {
|
|
|
2187
2256
|
|
|
2188
2257
|
// PHASE 2: OOXML Paragraph Merge Protocol
|
|
2189
2258
|
if (op === "DELETION" || op === "MODIFICATION") {
|
|
2190
|
-
if (
|
|
2259
|
+
if (
|
|
2260
|
+
op === "MODIFICATION" &&
|
|
2261
|
+
target_runs.length === 0 &&
|
|
2262
|
+
virtual_spans.length > 0 &&
|
|
2263
|
+
edit.new_text
|
|
2264
|
+
) {
|
|
2191
2265
|
const first_span = virtual_spans[0];
|
|
2192
2266
|
if (first_span.paragraph) {
|
|
2193
2267
|
const p1_el = first_span.paragraph._element;
|
|
2194
2268
|
const last_runs = findAllDescendants(p1_el, "w:r");
|
|
2195
|
-
const anchor =
|
|
2196
|
-
|
|
2269
|
+
const anchor =
|
|
2270
|
+
last_runs.length > 0
|
|
2271
|
+
? new Run(last_runs[last_runs.length - 1], first_span.paragraph)
|
|
2272
|
+
: null;
|
|
2273
|
+
|
|
2197
2274
|
const result = this._track_insert_multiline(
|
|
2198
2275
|
edit.new_text,
|
|
2199
2276
|
anchor,
|
|
2200
2277
|
first_span.paragraph,
|
|
2201
|
-
ins_id
|
|
2278
|
+
ins_id!,
|
|
2202
2279
|
);
|
|
2203
2280
|
if (result.first_node) {
|
|
2204
2281
|
p1_el.appendChild(result.first_node);
|
|
@@ -2218,7 +2295,10 @@ export class RedlineEngine {
|
|
|
2218
2295
|
let pPr = findChild(p1_element, "w:pPr");
|
|
2219
2296
|
if (!pPr) {
|
|
2220
2297
|
pPr = p1_element.ownerDocument!.createElement("w:pPr") as Element;
|
|
2221
|
-
p1_element.insertBefore(
|
|
2298
|
+
p1_element.insertBefore(
|
|
2299
|
+
pPr,
|
|
2300
|
+
p1_element.firstChild as Node | null,
|
|
2301
|
+
);
|
|
2222
2302
|
}
|
|
2223
2303
|
let rPr = findChild(pPr!, "w:rPr");
|
|
2224
2304
|
if (!rPr) {
|
|
@@ -2230,7 +2310,10 @@ export class RedlineEngine {
|
|
|
2230
2310
|
|
|
2231
2311
|
const children = Array.from(p2_element.childNodes);
|
|
2232
2312
|
for (const child of children) {
|
|
2233
|
-
if (
|
|
2313
|
+
if (
|
|
2314
|
+
child.nodeType === 1 &&
|
|
2315
|
+
(child as Element).tagName === "w:pPr"
|
|
2316
|
+
) {
|
|
2234
2317
|
continue;
|
|
2235
2318
|
}
|
|
2236
2319
|
p1_element.appendChild(child);
|