@adeu/core 1.20.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.d.cts CHANGED
@@ -75,6 +75,7 @@ declare class DocumentMapper {
75
75
  spans: TextSpan[];
76
76
  appendix_start_index: number;
77
77
  private _text_chunks;
78
+ private _plain_projection;
78
79
  constructor(doc: DocumentObject, clean_view?: boolean, original_view?: boolean);
79
80
  private _build_map;
80
81
  private _map_blocks;
@@ -86,6 +87,28 @@ declare class DocumentMapper {
86
87
  private _add_virtual_text;
87
88
  private _replace_smart_quotes;
88
89
  private _make_fuzzy_regex;
90
+ /**
91
+ * Returns [plain_text, offset_map] where plain_text is full_text with the
92
+ * VIRTUAL markdown style delimiters (bold/italic markers emitted around
93
+ * formatted runs) removed, and offset_map[i] is the full_text index of
94
+ * plain_text[i].
95
+ *
96
+ * Formatting run boundaries can fall mid-word (e.g. a paragraph projected
97
+ * as "**Al**pha"), where neither exact matching nor the whitespace-anchored
98
+ * fuzzy regex can find the plain target "Alpha". Matching against this
99
+ * projection and mapping the span back to full_text closes that gap (QA H2).
100
+ *
101
+ * Built lazily and invalidated by _build_map(): most batches never need it.
102
+ */
103
+ private _get_plain_projection;
104
+ /**
105
+ * Matches a markdown-stripped target against the plain projection and maps
106
+ * each hit back to a [start, length] span in full_text. Interior style
107
+ * markers end up inside the returned span (so "Alpha" over "**Al**pha"
108
+ * resolves to the "Al**pha" range); markers just outside the matched
109
+ * characters are excluded.
110
+ */
111
+ private _find_plain_projection_matches;
89
112
  find_match_index(target_text: string, is_regex?: boolean): [number, number];
90
113
  find_all_match_indices(target_text: string, is_regex?: boolean): [number, number][];
91
114
  find_target_runs(target_text: string): Run[];
@@ -174,6 +197,8 @@ declare class BatchValidationError extends Error {
174
197
  errors: string[];
175
198
  constructor(errors: string[]);
176
199
  }
200
+ declare function describe_illegal_control_chars(text: string): string | null;
201
+ declare function validate_edit_strings(edits: any[], index_offset?: number): string[];
177
202
  declare class RedlineEngine {
178
203
  doc: DocumentObject;
179
204
  author: string;
@@ -207,6 +232,43 @@ declare class RedlineEngine {
207
232
  * escalating the regex further.
208
233
  */
209
234
  private _nearest_match_hint;
235
+ private static readonly _PREVIEW_WRAPPER_PAIRS;
236
+ /**
237
+ * Makes a fixed-width slice of the raw-view projection presentable: drops
238
+ * complete {>>...<<} meta blocks (annotations of pre-existing changes, not
239
+ * part of this edit) and any wrapper fragments the window boundary chopped
240
+ * in half. Without this, previews leak internal scaffolding like
241
+ * "[Chg:5 delete]" (QA H1). Mirrors the Python engine.
242
+ */
243
+ private static _tidy_preview_context;
244
+ /**
245
+ * Snapshots the document text around a resolved edit BEFORE anything is
246
+ * applied. Previews rendered after the batch mutates the DOM cannot slice
247
+ * full_text at the stored offsets: applied edits shift offsets and inject
248
+ * tracked-change markup, garbling previews with unrelated edits and
249
+ * internal scaffolding (QA H1).
250
+ */
251
+ private _capture_preview_context;
252
+ /**
253
+ * Like _capture_preview_context, but snapshots the context around the
254
+ * ORIGINAL edit's full matched span (stashed by _pre_resolve_heuristic_edit),
255
+ * so the report preview can present the complete logical change of a
256
+ * compound modification instead of its first sub-edit.
257
+ */
258
+ private _capture_parent_preview_context;
259
+ /**
260
+ * Renders the preview from the edit's full matched span. The common
261
+ * prefix/suffix between matched and replacement text is moved into the
262
+ * surrounding context so the {--...--}{++...++} block shows the minimal
263
+ * complete change.
264
+ */
265
+ private _build_full_match_preview;
266
+ /**
267
+ * The "new text" a batch report should show for an edit. InsertTableRow has
268
+ * no new_text field — surface its cell contents rather than a misleading
269
+ * empty string (QA M4).
270
+ */
271
+ private static _report_new_text;
210
272
  private _build_edit_context_previews;
211
273
  private _scan_existing_ids;
212
274
  private _get_heading_path_and_page;
@@ -328,6 +390,12 @@ declare class RedlineEngine {
328
390
  private _clean_wrapping_comments;
329
391
  private _delete_comments_in_element;
330
392
  validate_edits(edits: any[], index_offset?: number): string[];
393
+ /**
394
+ * Number of columns (w:tc elements) in the table row containing the text at
395
+ * [start, start+length) in `mapper`, or null if that text is not inside a
396
+ * table row.
397
+ */
398
+ private static _column_count_at;
331
399
  validate_review_actions(actions: any[]): string[];
332
400
  process_batch(changes: DocumentChange[], dry_run?: boolean): any;
333
401
  private _process_batch_internal;
@@ -409,6 +477,42 @@ interface FinalizeResult {
409
477
  }
410
478
  declare function finalize_document(doc: DocumentObject, options: FinalizeOptions): Promise<FinalizeResult>;
411
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
+
412
516
  declare function identifyEngine(): string;
413
517
 
414
- 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
@@ -75,6 +75,7 @@ declare class DocumentMapper {
75
75
  spans: TextSpan[];
76
76
  appendix_start_index: number;
77
77
  private _text_chunks;
78
+ private _plain_projection;
78
79
  constructor(doc: DocumentObject, clean_view?: boolean, original_view?: boolean);
79
80
  private _build_map;
80
81
  private _map_blocks;
@@ -86,6 +87,28 @@ declare class DocumentMapper {
86
87
  private _add_virtual_text;
87
88
  private _replace_smart_quotes;
88
89
  private _make_fuzzy_regex;
90
+ /**
91
+ * Returns [plain_text, offset_map] where plain_text is full_text with the
92
+ * VIRTUAL markdown style delimiters (bold/italic markers emitted around
93
+ * formatted runs) removed, and offset_map[i] is the full_text index of
94
+ * plain_text[i].
95
+ *
96
+ * Formatting run boundaries can fall mid-word (e.g. a paragraph projected
97
+ * as "**Al**pha"), where neither exact matching nor the whitespace-anchored
98
+ * fuzzy regex can find the plain target "Alpha". Matching against this
99
+ * projection and mapping the span back to full_text closes that gap (QA H2).
100
+ *
101
+ * Built lazily and invalidated by _build_map(): most batches never need it.
102
+ */
103
+ private _get_plain_projection;
104
+ /**
105
+ * Matches a markdown-stripped target against the plain projection and maps
106
+ * each hit back to a [start, length] span in full_text. Interior style
107
+ * markers end up inside the returned span (so "Alpha" over "**Al**pha"
108
+ * resolves to the "Al**pha" range); markers just outside the matched
109
+ * characters are excluded.
110
+ */
111
+ private _find_plain_projection_matches;
89
112
  find_match_index(target_text: string, is_regex?: boolean): [number, number];
90
113
  find_all_match_indices(target_text: string, is_regex?: boolean): [number, number][];
91
114
  find_target_runs(target_text: string): Run[];
@@ -174,6 +197,8 @@ declare class BatchValidationError extends Error {
174
197
  errors: string[];
175
198
  constructor(errors: string[]);
176
199
  }
200
+ declare function describe_illegal_control_chars(text: string): string | null;
201
+ declare function validate_edit_strings(edits: any[], index_offset?: number): string[];
177
202
  declare class RedlineEngine {
178
203
  doc: DocumentObject;
179
204
  author: string;
@@ -207,6 +232,43 @@ declare class RedlineEngine {
207
232
  * escalating the regex further.
208
233
  */
209
234
  private _nearest_match_hint;
235
+ private static readonly _PREVIEW_WRAPPER_PAIRS;
236
+ /**
237
+ * Makes a fixed-width slice of the raw-view projection presentable: drops
238
+ * complete {>>...<<} meta blocks (annotations of pre-existing changes, not
239
+ * part of this edit) and any wrapper fragments the window boundary chopped
240
+ * in half. Without this, previews leak internal scaffolding like
241
+ * "[Chg:5 delete]" (QA H1). Mirrors the Python engine.
242
+ */
243
+ private static _tidy_preview_context;
244
+ /**
245
+ * Snapshots the document text around a resolved edit BEFORE anything is
246
+ * applied. Previews rendered after the batch mutates the DOM cannot slice
247
+ * full_text at the stored offsets: applied edits shift offsets and inject
248
+ * tracked-change markup, garbling previews with unrelated edits and
249
+ * internal scaffolding (QA H1).
250
+ */
251
+ private _capture_preview_context;
252
+ /**
253
+ * Like _capture_preview_context, but snapshots the context around the
254
+ * ORIGINAL edit's full matched span (stashed by _pre_resolve_heuristic_edit),
255
+ * so the report preview can present the complete logical change of a
256
+ * compound modification instead of its first sub-edit.
257
+ */
258
+ private _capture_parent_preview_context;
259
+ /**
260
+ * Renders the preview from the edit's full matched span. The common
261
+ * prefix/suffix between matched and replacement text is moved into the
262
+ * surrounding context so the {--...--}{++...++} block shows the minimal
263
+ * complete change.
264
+ */
265
+ private _build_full_match_preview;
266
+ /**
267
+ * The "new text" a batch report should show for an edit. InsertTableRow has
268
+ * no new_text field — surface its cell contents rather than a misleading
269
+ * empty string (QA M4).
270
+ */
271
+ private static _report_new_text;
210
272
  private _build_edit_context_previews;
211
273
  private _scan_existing_ids;
212
274
  private _get_heading_path_and_page;
@@ -328,6 +390,12 @@ declare class RedlineEngine {
328
390
  private _clean_wrapping_comments;
329
391
  private _delete_comments_in_element;
330
392
  validate_edits(edits: any[], index_offset?: number): string[];
393
+ /**
394
+ * Number of columns (w:tc elements) in the table row containing the text at
395
+ * [start, start+length) in `mapper`, or null if that text is not inside a
396
+ * table row.
397
+ */
398
+ private static _column_count_at;
331
399
  validate_review_actions(actions: any[]): string[];
332
400
  process_batch(changes: DocumentChange[], dry_run?: boolean): any;
333
401
  private _process_batch_internal;
@@ -409,6 +477,42 @@ interface FinalizeResult {
409
477
  }
410
478
  declare function finalize_document(doc: DocumentObject, options: FinalizeOptions): Promise<FinalizeResult>;
411
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
+
412
516
  declare function identifyEngine(): string;
413
517
 
414
- 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 };