@adeu/core 1.21.0 → 1.22.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/dist/index.cjs +169 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -5
- package/dist/index.d.ts +43 -5
- package/dist/index.js +162 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/domain.ts +10 -1
- package/src/engine.ts +103 -20
- package/src/index.ts +3 -2
- package/src/mapper.ts +16 -9
- package/src/markup.test.ts +2 -1
- package/src/markup.ts +3 -1
- package/src/repro_qa_report_v5.test.ts +399 -0
- package/src/sanitize/transforms.ts +26 -2
- package/src/utils/safe-regex.ts +90 -0
package/dist/index.d.cts
CHANGED
|
@@ -197,6 +197,8 @@ declare class BatchValidationError extends Error {
|
|
|
197
197
|
errors: string[];
|
|
198
198
|
constructor(errors: string[]);
|
|
199
199
|
}
|
|
200
|
+
declare function describe_illegal_control_chars(text: string): string | null;
|
|
201
|
+
declare function validate_edit_strings(edits: any[], index_offset?: number): string[];
|
|
200
202
|
declare class RedlineEngine {
|
|
201
203
|
doc: DocumentObject;
|
|
202
204
|
author: string;
|
|
@@ -243,8 +245,8 @@ declare class RedlineEngine {
|
|
|
243
245
|
* Snapshots the document text around a resolved edit BEFORE anything is
|
|
244
246
|
* applied. Previews rendered after the batch mutates the DOM cannot slice
|
|
245
247
|
* full_text at the stored offsets: applied edits shift offsets and inject
|
|
246
|
-
* tracked-change markup,
|
|
247
|
-
*
|
|
248
|
+
* tracked-change markup, garbling previews with unrelated edits and
|
|
249
|
+
* internal scaffolding (QA H1).
|
|
248
250
|
*/
|
|
249
251
|
private _capture_preview_context;
|
|
250
252
|
/**
|
|
@@ -263,8 +265,8 @@ declare class RedlineEngine {
|
|
|
263
265
|
private _build_full_match_preview;
|
|
264
266
|
/**
|
|
265
267
|
* The "new text" a batch report should show for an edit. InsertTableRow has
|
|
266
|
-
* no new_text field — surface its cell contents
|
|
267
|
-
* empty string
|
|
268
|
+
* no new_text field — surface its cell contents rather than a misleading
|
|
269
|
+
* empty string (QA M4).
|
|
268
270
|
*/
|
|
269
271
|
private static _report_new_text;
|
|
270
272
|
private _build_edit_context_previews;
|
|
@@ -475,6 +477,42 @@ interface FinalizeResult {
|
|
|
475
477
|
}
|
|
476
478
|
declare function finalize_document(doc: DocumentObject, options: FinalizeOptions): Promise<FinalizeResult>;
|
|
477
479
|
|
|
480
|
+
/**
|
|
481
|
+
* Time-budgeted execution of USER/LLM-supplied regular expressions.
|
|
482
|
+
*
|
|
483
|
+
* `regex: true` on a ModifyText edit and `search_regex` on read_docx hand an
|
|
484
|
+
* LLM-controlled pattern to V8's backtracking engine. A pathological pattern
|
|
485
|
+
* like `(a|a)*$` against a run of repeated characters hangs the event loop
|
|
486
|
+
* indefinitely (QA 2026-07-17 F5 — ReDoS; mirrors the Python fix).
|
|
487
|
+
*
|
|
488
|
+
* JS regexes cannot be interrupted in-thread, but V8 CAN interrupt regex
|
|
489
|
+
* execution running inside a `vm` context with a `timeout` — that is the
|
|
490
|
+
* mechanism used here. `node:vm` is a builtin, preserving the zero-dependency
|
|
491
|
+
* bundle constraint.
|
|
492
|
+
*
|
|
493
|
+
* Only USER-SUPPLIED patterns belong here. The engine's own generated
|
|
494
|
+
* patterns (fuzzy matchers etc.) are built to be linear-time.
|
|
495
|
+
*/
|
|
496
|
+
declare const USER_PATTERN_TIMEOUT_MS = 2000;
|
|
497
|
+
declare class RegexTimeoutError extends Error {
|
|
498
|
+
pattern: string;
|
|
499
|
+
constructor(pattern: string);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* All non-overlapping matches of a user pattern, under the wall-clock budget.
|
|
503
|
+
* Invalid patterns throw SyntaxError exactly like `new RegExp(...)` would.
|
|
504
|
+
* The budget covers the ENTIRE scan, not just the first match.
|
|
505
|
+
*/
|
|
506
|
+
declare function userFindAllMatches(pattern: string, text: string, flags?: string): Array<{
|
|
507
|
+
start: number;
|
|
508
|
+
end: number;
|
|
509
|
+
}>;
|
|
510
|
+
/** First match of a user pattern under the wall-clock budget, or null. */
|
|
511
|
+
declare function userSearch(pattern: string, text: string, flags?: string): {
|
|
512
|
+
start: number;
|
|
513
|
+
end: number;
|
|
514
|
+
} | null;
|
|
515
|
+
|
|
478
516
|
declare function identifyEngine(): string;
|
|
479
517
|
|
|
480
|
-
export { BatchValidationError, DocumentMapper, DocumentObject, type FinalizeOptions, type FinalizeResult, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, type TextSpan, _extractTextFromDoc, apply_edits_to_markdown, create_unified_diff, create_word_patch_diff, extractTextFromBuffer, extract_outline, finalize_document, generate_edits_from_text, identifyEngine, paginate, split_structural_appendix, trim_common_context };
|
|
518
|
+
export { BatchValidationError, DocumentMapper, DocumentObject, type FinalizeOptions, type FinalizeResult, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, RegexTimeoutError, type TextSpan, USER_PATTERN_TIMEOUT_MS, _extractTextFromDoc, apply_edits_to_markdown, create_unified_diff, create_word_patch_diff, describe_illegal_control_chars, extractTextFromBuffer, extract_outline, finalize_document, generate_edits_from_text, identifyEngine, paginate, split_structural_appendix, trim_common_context, userFindAllMatches, userSearch, validate_edit_strings };
|
package/dist/index.d.ts
CHANGED
|
@@ -197,6 +197,8 @@ declare class BatchValidationError extends Error {
|
|
|
197
197
|
errors: string[];
|
|
198
198
|
constructor(errors: string[]);
|
|
199
199
|
}
|
|
200
|
+
declare function describe_illegal_control_chars(text: string): string | null;
|
|
201
|
+
declare function validate_edit_strings(edits: any[], index_offset?: number): string[];
|
|
200
202
|
declare class RedlineEngine {
|
|
201
203
|
doc: DocumentObject;
|
|
202
204
|
author: string;
|
|
@@ -243,8 +245,8 @@ declare class RedlineEngine {
|
|
|
243
245
|
* Snapshots the document text around a resolved edit BEFORE anything is
|
|
244
246
|
* applied. Previews rendered after the batch mutates the DOM cannot slice
|
|
245
247
|
* full_text at the stored offsets: applied edits shift offsets and inject
|
|
246
|
-
* tracked-change markup,
|
|
247
|
-
*
|
|
248
|
+
* tracked-change markup, garbling previews with unrelated edits and
|
|
249
|
+
* internal scaffolding (QA H1).
|
|
248
250
|
*/
|
|
249
251
|
private _capture_preview_context;
|
|
250
252
|
/**
|
|
@@ -263,8 +265,8 @@ declare class RedlineEngine {
|
|
|
263
265
|
private _build_full_match_preview;
|
|
264
266
|
/**
|
|
265
267
|
* The "new text" a batch report should show for an edit. InsertTableRow has
|
|
266
|
-
* no new_text field — surface its cell contents
|
|
267
|
-
* empty string
|
|
268
|
+
* no new_text field — surface its cell contents rather than a misleading
|
|
269
|
+
* empty string (QA M4).
|
|
268
270
|
*/
|
|
269
271
|
private static _report_new_text;
|
|
270
272
|
private _build_edit_context_previews;
|
|
@@ -475,6 +477,42 @@ interface FinalizeResult {
|
|
|
475
477
|
}
|
|
476
478
|
declare function finalize_document(doc: DocumentObject, options: FinalizeOptions): Promise<FinalizeResult>;
|
|
477
479
|
|
|
480
|
+
/**
|
|
481
|
+
* Time-budgeted execution of USER/LLM-supplied regular expressions.
|
|
482
|
+
*
|
|
483
|
+
* `regex: true` on a ModifyText edit and `search_regex` on read_docx hand an
|
|
484
|
+
* LLM-controlled pattern to V8's backtracking engine. A pathological pattern
|
|
485
|
+
* like `(a|a)*$` against a run of repeated characters hangs the event loop
|
|
486
|
+
* indefinitely (QA 2026-07-17 F5 — ReDoS; mirrors the Python fix).
|
|
487
|
+
*
|
|
488
|
+
* JS regexes cannot be interrupted in-thread, but V8 CAN interrupt regex
|
|
489
|
+
* execution running inside a `vm` context with a `timeout` — that is the
|
|
490
|
+
* mechanism used here. `node:vm` is a builtin, preserving the zero-dependency
|
|
491
|
+
* bundle constraint.
|
|
492
|
+
*
|
|
493
|
+
* Only USER-SUPPLIED patterns belong here. The engine's own generated
|
|
494
|
+
* patterns (fuzzy matchers etc.) are built to be linear-time.
|
|
495
|
+
*/
|
|
496
|
+
declare const USER_PATTERN_TIMEOUT_MS = 2000;
|
|
497
|
+
declare class RegexTimeoutError extends Error {
|
|
498
|
+
pattern: string;
|
|
499
|
+
constructor(pattern: string);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* All non-overlapping matches of a user pattern, under the wall-clock budget.
|
|
503
|
+
* Invalid patterns throw SyntaxError exactly like `new RegExp(...)` would.
|
|
504
|
+
* The budget covers the ENTIRE scan, not just the first match.
|
|
505
|
+
*/
|
|
506
|
+
declare function userFindAllMatches(pattern: string, text: string, flags?: string): Array<{
|
|
507
|
+
start: number;
|
|
508
|
+
end: number;
|
|
509
|
+
}>;
|
|
510
|
+
/** First match of a user pattern under the wall-clock budget, or null. */
|
|
511
|
+
declare function userSearch(pattern: string, text: string, flags?: string): {
|
|
512
|
+
start: number;
|
|
513
|
+
end: number;
|
|
514
|
+
} | null;
|
|
515
|
+
|
|
478
516
|
declare function identifyEngine(): string;
|
|
479
517
|
|
|
480
|
-
export { BatchValidationError, DocumentMapper, DocumentObject, type FinalizeOptions, type FinalizeResult, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, type TextSpan, _extractTextFromDoc, apply_edits_to_markdown, create_unified_diff, create_word_patch_diff, extractTextFromBuffer, extract_outline, finalize_document, generate_edits_from_text, identifyEngine, paginate, split_structural_appendix, trim_common_context };
|
|
518
|
+
export { BatchValidationError, DocumentMapper, DocumentObject, type FinalizeOptions, type FinalizeResult, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, RegexTimeoutError, type TextSpan, USER_PATTERN_TIMEOUT_MS, _extractTextFromDoc, apply_edits_to_markdown, create_unified_diff, create_word_patch_diff, describe_illegal_control_chars, extractTextFromBuffer, extract_outline, finalize_document, generate_edits_from_text, identifyEngine, paginate, split_structural_appendix, trim_common_context, userFindAllMatches, userSearch, validate_edit_strings };
|
package/dist/index.js
CHANGED
|
@@ -743,6 +743,62 @@ function extract_comments_data(pkg) {
|
|
|
743
743
|
return data;
|
|
744
744
|
}
|
|
745
745
|
|
|
746
|
+
// src/utils/safe-regex.ts
|
|
747
|
+
import * as vm from "vm";
|
|
748
|
+
var USER_PATTERN_TIMEOUT_MS = 2e3;
|
|
749
|
+
var RegexTimeoutError = class extends Error {
|
|
750
|
+
pattern;
|
|
751
|
+
constructor(pattern) {
|
|
752
|
+
super(
|
|
753
|
+
`Regular expression exceeded the ${USER_PATTERN_TIMEOUT_MS / 1e3}s matching time budget (catastrophic backtracking). Simplify the pattern \u2014 nested quantifiers like (a+)+ are the usual cause \u2014 or use a literal target instead.`
|
|
754
|
+
);
|
|
755
|
+
this.name = "RegexTimeoutError";
|
|
756
|
+
this.pattern = pattern;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
function runBudgeted(pattern, script, sandbox) {
|
|
760
|
+
try {
|
|
761
|
+
return vm.runInNewContext(script, sandbox, {
|
|
762
|
+
timeout: USER_PATTERN_TIMEOUT_MS
|
|
763
|
+
});
|
|
764
|
+
} catch (e) {
|
|
765
|
+
if (e && e.code === "ERR_SCRIPT_EXECUTION_TIMEOUT") {
|
|
766
|
+
throw new RegexTimeoutError(pattern);
|
|
767
|
+
}
|
|
768
|
+
throw e;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function userFindAllMatches(pattern, text, flags = "") {
|
|
772
|
+
const normalized = flags.includes("g") ? flags : flags + "g";
|
|
773
|
+
const re = new RegExp(pattern, normalized);
|
|
774
|
+
const raw = runBudgeted(
|
|
775
|
+
pattern,
|
|
776
|
+
`{
|
|
777
|
+
const out = [];
|
|
778
|
+
let m;
|
|
779
|
+
while ((m = re.exec(text)) !== null) {
|
|
780
|
+
out.push({ start: m.index, end: m.index + m[0].length });
|
|
781
|
+
if (m.index === re.lastIndex) re.lastIndex++;
|
|
782
|
+
}
|
|
783
|
+
out;
|
|
784
|
+
}`,
|
|
785
|
+
{ re, text }
|
|
786
|
+
);
|
|
787
|
+
return raw.map((r) => ({ start: r.start, end: r.end }));
|
|
788
|
+
}
|
|
789
|
+
function userSearch(pattern, text, flags = "") {
|
|
790
|
+
const re = new RegExp(pattern, flags.replace("g", ""));
|
|
791
|
+
const raw = runBudgeted(
|
|
792
|
+
pattern,
|
|
793
|
+
`{
|
|
794
|
+
const m = re.exec(text);
|
|
795
|
+
m ? { start: m.index, end: m.index + m[0].length } : null;
|
|
796
|
+
}`,
|
|
797
|
+
{ re, text }
|
|
798
|
+
);
|
|
799
|
+
return raw ? { start: raw.start, end: raw.end } : null;
|
|
800
|
+
}
|
|
801
|
+
|
|
746
802
|
// src/utils/docx.ts
|
|
747
803
|
var QN_W_P = "w:p";
|
|
748
804
|
var QN_W_R = "w:r";
|
|
@@ -1817,10 +1873,10 @@ ${header}`;
|
|
|
1817
1873
|
find_match_index(target_text, is_regex = false) {
|
|
1818
1874
|
if (is_regex) {
|
|
1819
1875
|
try {
|
|
1820
|
-
const
|
|
1821
|
-
|
|
1822
|
-
if (match) return [match.index, match[0].length];
|
|
1876
|
+
const match = userSearch(target_text, this.full_text);
|
|
1877
|
+
if (match) return [match.start, match.end - match.start];
|
|
1823
1878
|
} catch (e) {
|
|
1879
|
+
if (e instanceof RegexTimeoutError) throw e;
|
|
1824
1880
|
}
|
|
1825
1881
|
return [-1, 0];
|
|
1826
1882
|
}
|
|
@@ -1849,11 +1905,10 @@ ${header}`;
|
|
|
1849
1905
|
if (!target_text) return [];
|
|
1850
1906
|
if (is_regex) {
|
|
1851
1907
|
try {
|
|
1852
|
-
const
|
|
1853
|
-
|
|
1854
|
-
if (matches2.length > 0)
|
|
1855
|
-
return matches2.map((m) => [m.index, m[0].length]);
|
|
1908
|
+
const matches2 = userFindAllMatches(target_text, this.full_text);
|
|
1909
|
+
if (matches2.length > 0) return matches2.map((m) => [m.start, m.end - m.start]);
|
|
1856
1910
|
} catch (e) {
|
|
1911
|
+
if (e instanceof RegexTimeoutError) throw e;
|
|
1857
1912
|
}
|
|
1858
1913
|
return [];
|
|
1859
1914
|
}
|
|
@@ -2831,7 +2886,9 @@ function apply_edits_to_markdown(markdown_text, edits, include_index = false, hi
|
|
|
2831
2886
|
isolated_target,
|
|
2832
2887
|
isolated_new,
|
|
2833
2888
|
edit.comment,
|
|
2834
|
-
|
|
2889
|
+
// 1-based, matching apply's "Edit N" reports and batch validation
|
|
2890
|
+
// errors (QA 2026-07-17 F10; mirrors Python).
|
|
2891
|
+
orig_idx + 1,
|
|
2835
2892
|
include_index,
|
|
2836
2893
|
highlight_only
|
|
2837
2894
|
);
|
|
@@ -2976,12 +3033,36 @@ function sequential_context_hint(applied_so_far) {
|
|
|
2976
3033
|
Note: ${applied_so_far} earlier edit(s) in this batch were already applied. Batches apply sequentially \u2014 each edit must target the document text as it reads AFTER the preceding edits (e.g. target the replacement text an earlier edit introduced, not the original wording).`;
|
|
2977
3034
|
}
|
|
2978
3035
|
var TRANSACTIONAL_NOT_APPLIED_ERROR = "Not applied: the batch is transactional and other edits failed validation (see their errors). Fix or remove those edits and re-run.";
|
|
3036
|
+
var XML_ILLEGAL_CHARS_RE = /[\x00-\x08\x0b\x0c\x0e-\x1f]/g;
|
|
3037
|
+
function describe_illegal_control_chars(text) {
|
|
3038
|
+
if (!text) return null;
|
|
3039
|
+
const found = text.match(XML_ILLEGAL_CHARS_RE);
|
|
3040
|
+
if (!found) return null;
|
|
3041
|
+
const codes = Array.from(new Set(found.map((c) => `0x${c.charCodeAt(0).toString(16).padStart(2, "0")}`))).sort();
|
|
3042
|
+
return codes.join(", ");
|
|
3043
|
+
}
|
|
2979
3044
|
function validate_edit_strings(edits, index_offset = 0) {
|
|
2980
3045
|
const errors = [];
|
|
2981
3046
|
for (let i = 0; i < edits.length; i++) {
|
|
2982
3047
|
const edit = edits[i];
|
|
2983
3048
|
const t_text = edit.target_text || "";
|
|
2984
3049
|
const n_text = edit.new_text || "";
|
|
3050
|
+
const checked_fields = [
|
|
3051
|
+
["target_text", t_text],
|
|
3052
|
+
["new_text", n_text]
|
|
3053
|
+
];
|
|
3054
|
+
if (edit.comment) checked_fields.push(["comment", edit.comment]);
|
|
3055
|
+
(edit.cells || []).forEach((cell, cell_idx) => {
|
|
3056
|
+
checked_fields.push([`cells[${cell_idx}]`, cell || ""]);
|
|
3057
|
+
});
|
|
3058
|
+
for (const [field_name, field_value] of checked_fields) {
|
|
3059
|
+
const described = describe_illegal_control_chars(field_value);
|
|
3060
|
+
if (described) {
|
|
3061
|
+
errors.push(
|
|
3062
|
+
`- Edit ${i + 1 + index_offset} Failed: \`${field_name}\` contains control character(s) (${described}) that cannot be stored in a DOCX. Remove them and re-submit.`
|
|
3063
|
+
);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
2985
3066
|
if (n_text.includes("{++") || n_text.includes("{--") || n_text.includes("{>>") || n_text.includes("{==")) {
|
|
2986
3067
|
errors.push(
|
|
2987
3068
|
`- Edit ${i + 1 + index_offset} Failed: Do not manually write CriticMarkup tags ({++, {--, {>>, {==) in \`new_text\`. The engine handles redlining automatically. To add a comment, use the \`comment\` parameter.`
|
|
@@ -3288,8 +3369,8 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
3288
3369
|
* Snapshots the document text around a resolved edit BEFORE anything is
|
|
3289
3370
|
* applied. Previews rendered after the batch mutates the DOM cannot slice
|
|
3290
3371
|
* full_text at the stored offsets: applied edits shift offsets and inject
|
|
3291
|
-
* tracked-change markup,
|
|
3292
|
-
*
|
|
3372
|
+
* tracked-change markup, garbling previews with unrelated edits and
|
|
3373
|
+
* internal scaffolding (QA H1).
|
|
3293
3374
|
*/
|
|
3294
3375
|
_capture_preview_context(edit) {
|
|
3295
3376
|
if (edit.type !== "modify") return;
|
|
@@ -3390,8 +3471,8 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
3390
3471
|
}
|
|
3391
3472
|
/**
|
|
3392
3473
|
* The "new text" a batch report should show for an edit. InsertTableRow has
|
|
3393
|
-
* no new_text field — surface its cell contents
|
|
3394
|
-
* empty string
|
|
3474
|
+
* no new_text field — surface its cell contents rather than a misleading
|
|
3475
|
+
* empty string (QA M4).
|
|
3395
3476
|
*/
|
|
3396
3477
|
static _report_new_text(edit) {
|
|
3397
3478
|
if (edit && edit.type === "insert_row" && Array.isArray(edit.cells)) {
|
|
@@ -4676,7 +4757,13 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4676
4757
|
const reports_by_input = new Array(edits.length);
|
|
4677
4758
|
const validation_failed_idx = /* @__PURE__ */ new Set();
|
|
4678
4759
|
for (const { edit, i } of ordered_edits) {
|
|
4679
|
-
let single_errors
|
|
4760
|
+
let single_errors;
|
|
4761
|
+
try {
|
|
4762
|
+
single_errors = this.validate_edits([edit], i);
|
|
4763
|
+
} catch (e) {
|
|
4764
|
+
if (!(e instanceof RegexTimeoutError)) throw e;
|
|
4765
|
+
single_errors = [`- Edit ${i + 1} Failed: ${e.message}`];
|
|
4766
|
+
}
|
|
4680
4767
|
if (single_errors.length > 0) {
|
|
4681
4768
|
if (applied_edits > 0) {
|
|
4682
4769
|
const hint = sequential_context_hint(applied_edits);
|
|
@@ -4761,7 +4848,13 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4761
4848
|
const sequential_errors = [];
|
|
4762
4849
|
let applied_so_far = 0;
|
|
4763
4850
|
for (const { edit, i } of ordered_edits) {
|
|
4764
|
-
let single_errors
|
|
4851
|
+
let single_errors;
|
|
4852
|
+
try {
|
|
4853
|
+
single_errors = this.validate_edits([edit], i);
|
|
4854
|
+
} catch (e) {
|
|
4855
|
+
if (!(e instanceof RegexTimeoutError)) throw e;
|
|
4856
|
+
single_errors = [`- Edit ${i + 1} Failed: ${e.message}`];
|
|
4857
|
+
}
|
|
4765
4858
|
if (single_errors.length > 0) {
|
|
4766
4859
|
if (applied_so_far > 0) {
|
|
4767
4860
|
const hint = sequential_context_hint(applied_so_far);
|
|
@@ -4875,7 +4968,18 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
4875
4968
|
edit._error_msg = msg;
|
|
4876
4969
|
}
|
|
4877
4970
|
} else {
|
|
4878
|
-
|
|
4971
|
+
let resolved;
|
|
4972
|
+
try {
|
|
4973
|
+
resolved = this._pre_resolve_heuristic_edit(edit);
|
|
4974
|
+
} catch (e) {
|
|
4975
|
+
if (!(e instanceof RegexTimeoutError)) throw e;
|
|
4976
|
+
skipped++;
|
|
4977
|
+
edit._applied_status = false;
|
|
4978
|
+
const msg = `- Failed to apply edit targeting: '${(edit.target_text || "").substring(0, 40)}...' (${e.message})`;
|
|
4979
|
+
this.skipped_details.push(msg);
|
|
4980
|
+
edit._error_msg = msg;
|
|
4981
|
+
continue;
|
|
4982
|
+
}
|
|
4879
4983
|
if (resolved) {
|
|
4880
4984
|
if (Array.isArray(resolved)) {
|
|
4881
4985
|
for (const r of resolved) {
|
|
@@ -5034,6 +5138,16 @@ var RedlineEngine = class _RedlineEngine {
|
|
|
5034
5138
|
);
|
|
5035
5139
|
continue;
|
|
5036
5140
|
}
|
|
5141
|
+
const dup_authors = Array.from(
|
|
5142
|
+
new Set(all_nodes.map((n) => n.getAttribute("w:author") || "Unknown"))
|
|
5143
|
+
).sort();
|
|
5144
|
+
if (dup_authors.length > 1) {
|
|
5145
|
+
skipped++;
|
|
5146
|
+
this.skipped_details.push(
|
|
5147
|
+
`- Failed to apply action: ${type} on Chg:${target_id} is ambiguous. The document contains ${all_nodes.length} tracked-change elements sharing w:id=${target_id} from different authors (${dup_authors.join(", ")}) \u2014 duplicate revision IDs produced outside this engine (e.g. by a document merge or copy-paste). Acting on this ID would resolve all of them at once. Resolve these changes individually in Word, or apply the intended outcome as an explicit text edit instead.`
|
|
5148
|
+
);
|
|
5149
|
+
continue;
|
|
5150
|
+
}
|
|
5037
5151
|
for (const node of all_nodes) {
|
|
5038
5152
|
const is_ins = node.tagName === "w:ins";
|
|
5039
5153
|
const parent_tag = node.parentNode ? node.parentNode.tagName : "";
|
|
@@ -6349,6 +6463,27 @@ function scrub_doc_properties(doc) {
|
|
|
6349
6463
|
c.textContent = "";
|
|
6350
6464
|
}
|
|
6351
6465
|
});
|
|
6466
|
+
const titles = findDescendantsByLocalName(corePart._element, "title");
|
|
6467
|
+
titles.forEach((c) => {
|
|
6468
|
+
if (c.textContent) {
|
|
6469
|
+
lines.push(`Title kept (review manually): "${c.textContent}"`);
|
|
6470
|
+
}
|
|
6471
|
+
});
|
|
6472
|
+
const leakFields = [
|
|
6473
|
+
["category", "Category"],
|
|
6474
|
+
["keywords", "Keywords"],
|
|
6475
|
+
["subject", "Subject"],
|
|
6476
|
+
["contentStatus", "Content status"],
|
|
6477
|
+
["description", "Description/comments"]
|
|
6478
|
+
];
|
|
6479
|
+
for (const [local, label] of leakFields) {
|
|
6480
|
+
findDescendantsByLocalName(corePart._element, local).forEach((c) => {
|
|
6481
|
+
if (c.textContent) {
|
|
6482
|
+
lines.push(`${label}: ${c.textContent}`);
|
|
6483
|
+
c.textContent = "";
|
|
6484
|
+
}
|
|
6485
|
+
});
|
|
6486
|
+
}
|
|
6352
6487
|
const revisions = findDescendantsByLocalName(corePart._element, "revision");
|
|
6353
6488
|
revisions.forEach((c) => {
|
|
6354
6489
|
if (c.textContent && parseInt(c.textContent) > 1) {
|
|
@@ -6554,7 +6689,11 @@ function extract_all_domain_metadata(doc, base_text) {
|
|
|
6554
6689
|
for (const term of def_keys) {
|
|
6555
6690
|
if (definitions[term].count === 0) {
|
|
6556
6691
|
delete definitions[term];
|
|
6557
|
-
duplicates.
|
|
6692
|
+
if (!duplicates.has(term)) {
|
|
6693
|
+
diagnostics.push(
|
|
6694
|
+
`[Warning] Unused Definition: '${term}' is defined but never used.`
|
|
6695
|
+
);
|
|
6696
|
+
}
|
|
6558
6697
|
}
|
|
6559
6698
|
}
|
|
6560
6699
|
}
|
|
@@ -7889,10 +8028,13 @@ export {
|
|
|
7889
8028
|
DocumentMapper,
|
|
7890
8029
|
DocumentObject,
|
|
7891
8030
|
RedlineEngine,
|
|
8031
|
+
RegexTimeoutError,
|
|
8032
|
+
USER_PATTERN_TIMEOUT_MS,
|
|
7892
8033
|
_extractTextFromDoc,
|
|
7893
8034
|
apply_edits_to_markdown,
|
|
7894
8035
|
create_unified_diff,
|
|
7895
8036
|
create_word_patch_diff,
|
|
8037
|
+
describe_illegal_control_chars,
|
|
7896
8038
|
extractTextFromBuffer,
|
|
7897
8039
|
extract_outline,
|
|
7898
8040
|
finalize_document,
|
|
@@ -7900,6 +8042,9 @@ export {
|
|
|
7900
8042
|
identifyEngine,
|
|
7901
8043
|
paginate,
|
|
7902
8044
|
split_structural_appendix,
|
|
7903
|
-
trim_common_context
|
|
8045
|
+
trim_common_context,
|
|
8046
|
+
userFindAllMatches,
|
|
8047
|
+
userSearch,
|
|
8048
|
+
validate_edit_strings
|
|
7904
8049
|
};
|
|
7905
8050
|
//# sourceMappingURL=index.js.map
|