@adeu/core 1.22.0 → 1.25.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
@@ -65,6 +65,8 @@ interface TextSpan {
65
65
  del_id?: string | null;
66
66
  hyperlink_id?: string | null;
67
67
  comment_ids?: string[];
68
+ part_index: number;
69
+ is_image_marker?: boolean;
68
70
  }
69
71
  declare class DocumentMapper {
70
72
  doc: DocumentObject;
@@ -74,10 +76,26 @@ declare class DocumentMapper {
74
76
  full_text: string;
75
77
  spans: TextSpan[];
76
78
  appendix_start_index: number;
79
+ part_ranges: [number, number, string][];
80
+ private _current_part_index;
77
81
  private _text_chunks;
78
82
  private _plain_projection;
79
83
  constructor(doc: DocumentObject, clean_view?: boolean, original_view?: boolean);
80
84
  private _build_map;
85
+ /** [part_index, start, end, kind] for parts that projected any text. */
86
+ private _nonempty_part_ranges;
87
+ part_kind_of(part_index: number): string | null;
88
+ /** Kind of the part whose projected range contains `index`, or null. */
89
+ part_kind_at(index: number): string | null;
90
+ /**
91
+ * When `index` falls strictly AFTER one part's text and at-or-before the
92
+ * start of the next part's text (i.e. inside the "\n\n" separator or
93
+ * exactly at the next part's first character), returns
94
+ * [previous_part_index, next_part_index]. Returns null everywhere else —
95
+ * including index == previous part's end, which is an ordinary
96
+ * end-of-part text position, not a boundary gap.
97
+ */
98
+ part_boundary_at(index: number): [number, number] | null;
81
99
  private _map_blocks;
82
100
  private _map_table;
83
101
  private _strip_markdown_formatting;
@@ -305,6 +323,19 @@ declare class RedlineEngine {
305
323
  private _getNextId;
306
324
  private _create_track_change_tag;
307
325
  private _set_text_content;
326
+ /**
327
+ * Walks `element` to its XML root element. Word (and LibreOffice, which
328
+ * refuses to LOAD such files) only supports comment ranges in the main
329
+ * document story ("w:document") — never in headers, footers, footnotes or
330
+ * endnotes (QA 2026-07-18 H4/C1).
331
+ */
332
+ private _comment_anchor_in_main_story;
333
+ /**
334
+ * When the anchor lives outside the main document story, records a
335
+ * user-visible warning and returns true (caller must skip the comment).
336
+ * The tracked change itself still applies — only the bubble is dropped.
337
+ */
338
+ private _skip_comment_outside_main_story;
308
339
  /**
309
340
  * Attaches a comment that wraps a contiguous range within a single paragraph.
310
341
  * start_element and end_element must both be direct children of parent_element
@@ -395,6 +426,13 @@ declare class RedlineEngine {
395
426
  * [start, start+length) in `mapper`, or null if that text is not inside a
396
427
  * table row.
397
428
  */
429
+ /**
430
+ * True when a replacement anchored in a table would ADD line-separated
431
+ * pipe-delimited content — the text shape of a table row. Writing that
432
+ * into a cell renders a fake row inside one cell while the real grid
433
+ * stays unchanged (QA 2026-07-18 C2); such edits must use insert_row.
434
+ */
435
+ private static _introduces_table_row_text;
398
436
  private static _column_count_at;
399
437
  validate_review_actions(actions: any[]): string[];
400
438
  process_batch(changes: DocumentChange[], dry_run?: boolean): any;
@@ -415,12 +453,86 @@ declare class RedlineEngine {
415
453
  private _apply_single_edit_indexed;
416
454
  }
417
455
 
456
+ /** Text-space extent of one projected table row plus its cell texts. */
457
+ interface RowGeometry {
458
+ start: number;
459
+ end: number;
460
+ cells: string[];
461
+ }
462
+ /** Text-space extent of one projected top-level table. */
463
+ interface TableGeometry {
464
+ start: number;
465
+ end: number;
466
+ rows: RowGeometry[];
467
+ }
468
+ /**
469
+ * Structural map of a projection: which offset ranges belong to which OPC
470
+ * part, and where top-level table rows live. Produced in the SAME pass as
471
+ * the text, so offsets always agree with it. Consumed by the diff pipeline
472
+ * to keep generated edits from crossing part boundaries (QA 2026-07-18 C1)
473
+ * and to emit structured row operations for table changes (QA C2).
474
+ */
475
+ interface ExtractStructure {
476
+ part_ranges: [number, number, string][];
477
+ tables: TableGeometry[];
478
+ }
479
+ declare function extractTextFromBuffer(buffer: Buffer, cleanView?: boolean, includeAppendix?: boolean): Promise<string>;
480
+ declare function _extractTextFromDoc(doc: DocumentObject, cleanView?: boolean, includeAppendix?: boolean, return_paragraph_offsets?: boolean, return_structure?: boolean): string | {
481
+ text: string;
482
+ paragraph_offsets: Map<any, [number, number]>;
483
+ } | {
484
+ text: string;
485
+ structure: ExtractStructure;
486
+ };
487
+
488
+ type DiffEdit = ModifyText | InsertTableRow | DeleteTableRow;
418
489
  declare function trim_common_context(target: string, new_val: string): [number, number];
419
490
  declare function generate_edits_from_text(original_text: string, modified_text: string): ModifyText[];
491
+ /**
492
+ * DOCX-to-DOCX diff over projections extracted with return_structure=true.
493
+ *
494
+ * Improvements over a flat generate_edits_from_text pass:
495
+ * - each OPC part (header/body/footer/notes) diffs against its
496
+ * counterpart, so no edit can span a part boundary and content that
497
+ * moved between parts is reported instead of hidden (QA 2026-07-18 C1);
498
+ * - table row insertions/deletions become structured insert_row /
499
+ * delete_row operations instead of pipe-text edits (QA C2).
500
+ *
501
+ * Every ModifyText keeps its _match_start_index pinned into text_orig, which
502
+ * the engine consumes positionally (the Node engine has no
503
+ * make_edits_self_contained JSON round trip like the Python CLI).
504
+ *
505
+ * Returns { edits, warnings }. Warnings describe fallbacks the caller should
506
+ * surface (differing part layouts, unanchorable rows, …).
507
+ */
508
+ declare function generate_structured_edits(text_orig: string, struct_orig: ExtractStructure, text_mod: string, struct_mod: ExtractStructure): {
509
+ edits: DiffEdit[];
510
+ warnings: string[];
511
+ };
420
512
  declare function create_unified_diff(original_text: string, modified_text: string, context_lines?: number): string;
421
513
  declare function create_word_patch_diff(original_text: string, modified_text: string, original_path?: string, modified_path?: string): string;
422
514
 
423
- declare function apply_edits_to_markdown(markdown_text: string, edits: ModifyText[], include_index?: boolean, highlight_only?: boolean): string;
515
+ /** One entry per input edit in apply_edits_to_markdown's edit_reports. */
516
+ interface MarkupEditReport {
517
+ index: number;
518
+ status: "applied" | "failed";
519
+ error: string | null;
520
+ occurrences: number;
521
+ }
522
+ /**
523
+ * Applies edits to Markdown text and returns CriticMarkup-annotated output.
524
+ *
525
+ * Edit resolution follows the SAME semantics as `apply` (QA 2026-07-18 M1):
526
+ * - `regex: true` targets match as regular expressions
527
+ * - `match_mode` is honored: "strict" (default) refuses ambiguous targets,
528
+ * "first" marks the first occurrence, "all" marks every occurrence
529
+ * - a missing target is a per-edit failure, never a silent skip
530
+ *
531
+ * When `edit_reports` is provided, one entry per input edit is appended:
532
+ * { index: 0-based input position, status: "applied"|"failed",
533
+ * error: string|null, occurrences: number }.
534
+ */
535
+ declare function apply_edits_to_markdown(markdown_text: string, edits: ModifyText[], include_index?: boolean, highlight_only?: boolean, edit_reports?: MarkupEditReport[]): string;
424
536
 
425
537
  /**
426
538
  * Stateless paginator for projected DOCX Markdown.
@@ -456,12 +568,6 @@ interface OutlineNode {
456
568
  }
457
569
  declare function extract_outline(doc: DocumentObject, projected_body: string, body_pages: string[], body_page_offsets: number[], paragraph_offsets?: Record<string, [number, number]> | Map<any, [number, number]> | null): OutlineNode[];
458
570
 
459
- declare function extractTextFromBuffer(buffer: Buffer, cleanView?: boolean): Promise<string>;
460
- declare function _extractTextFromDoc(doc: DocumentObject, cleanView?: boolean, includeAppendix?: boolean, return_paragraph_offsets?: boolean): string | {
461
- text: string;
462
- paragraph_offsets: Map<any, [number, number]>;
463
- };
464
-
465
571
  interface FinalizeOptions {
466
572
  filename: string;
467
573
  sanitize_mode?: 'full' | 'keep-markup' | 'baseline';
@@ -515,4 +621,4 @@ declare function userSearch(pattern: string, text: string, flags?: string): {
515
621
 
516
622
  declare function identifyEngine(): string;
517
623
 
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 };
624
+ export { BatchValidationError, type DiffEdit, DocumentMapper, DocumentObject, type ExtractStructure, type FinalizeOptions, type FinalizeResult, type MarkupEditReport, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, RegexTimeoutError, type RowGeometry, type TableGeometry, 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, generate_structured_edits, identifyEngine, paginate, split_structural_appendix, trim_common_context, userFindAllMatches, userSearch, validate_edit_strings };
package/dist/index.d.ts CHANGED
@@ -65,6 +65,8 @@ interface TextSpan {
65
65
  del_id?: string | null;
66
66
  hyperlink_id?: string | null;
67
67
  comment_ids?: string[];
68
+ part_index: number;
69
+ is_image_marker?: boolean;
68
70
  }
69
71
  declare class DocumentMapper {
70
72
  doc: DocumentObject;
@@ -74,10 +76,26 @@ declare class DocumentMapper {
74
76
  full_text: string;
75
77
  spans: TextSpan[];
76
78
  appendix_start_index: number;
79
+ part_ranges: [number, number, string][];
80
+ private _current_part_index;
77
81
  private _text_chunks;
78
82
  private _plain_projection;
79
83
  constructor(doc: DocumentObject, clean_view?: boolean, original_view?: boolean);
80
84
  private _build_map;
85
+ /** [part_index, start, end, kind] for parts that projected any text. */
86
+ private _nonempty_part_ranges;
87
+ part_kind_of(part_index: number): string | null;
88
+ /** Kind of the part whose projected range contains `index`, or null. */
89
+ part_kind_at(index: number): string | null;
90
+ /**
91
+ * When `index` falls strictly AFTER one part's text and at-or-before the
92
+ * start of the next part's text (i.e. inside the "\n\n" separator or
93
+ * exactly at the next part's first character), returns
94
+ * [previous_part_index, next_part_index]. Returns null everywhere else —
95
+ * including index == previous part's end, which is an ordinary
96
+ * end-of-part text position, not a boundary gap.
97
+ */
98
+ part_boundary_at(index: number): [number, number] | null;
81
99
  private _map_blocks;
82
100
  private _map_table;
83
101
  private _strip_markdown_formatting;
@@ -305,6 +323,19 @@ declare class RedlineEngine {
305
323
  private _getNextId;
306
324
  private _create_track_change_tag;
307
325
  private _set_text_content;
326
+ /**
327
+ * Walks `element` to its XML root element. Word (and LibreOffice, which
328
+ * refuses to LOAD such files) only supports comment ranges in the main
329
+ * document story ("w:document") — never in headers, footers, footnotes or
330
+ * endnotes (QA 2026-07-18 H4/C1).
331
+ */
332
+ private _comment_anchor_in_main_story;
333
+ /**
334
+ * When the anchor lives outside the main document story, records a
335
+ * user-visible warning and returns true (caller must skip the comment).
336
+ * The tracked change itself still applies — only the bubble is dropped.
337
+ */
338
+ private _skip_comment_outside_main_story;
308
339
  /**
309
340
  * Attaches a comment that wraps a contiguous range within a single paragraph.
310
341
  * start_element and end_element must both be direct children of parent_element
@@ -395,6 +426,13 @@ declare class RedlineEngine {
395
426
  * [start, start+length) in `mapper`, or null if that text is not inside a
396
427
  * table row.
397
428
  */
429
+ /**
430
+ * True when a replacement anchored in a table would ADD line-separated
431
+ * pipe-delimited content — the text shape of a table row. Writing that
432
+ * into a cell renders a fake row inside one cell while the real grid
433
+ * stays unchanged (QA 2026-07-18 C2); such edits must use insert_row.
434
+ */
435
+ private static _introduces_table_row_text;
398
436
  private static _column_count_at;
399
437
  validate_review_actions(actions: any[]): string[];
400
438
  process_batch(changes: DocumentChange[], dry_run?: boolean): any;
@@ -415,12 +453,86 @@ declare class RedlineEngine {
415
453
  private _apply_single_edit_indexed;
416
454
  }
417
455
 
456
+ /** Text-space extent of one projected table row plus its cell texts. */
457
+ interface RowGeometry {
458
+ start: number;
459
+ end: number;
460
+ cells: string[];
461
+ }
462
+ /** Text-space extent of one projected top-level table. */
463
+ interface TableGeometry {
464
+ start: number;
465
+ end: number;
466
+ rows: RowGeometry[];
467
+ }
468
+ /**
469
+ * Structural map of a projection: which offset ranges belong to which OPC
470
+ * part, and where top-level table rows live. Produced in the SAME pass as
471
+ * the text, so offsets always agree with it. Consumed by the diff pipeline
472
+ * to keep generated edits from crossing part boundaries (QA 2026-07-18 C1)
473
+ * and to emit structured row operations for table changes (QA C2).
474
+ */
475
+ interface ExtractStructure {
476
+ part_ranges: [number, number, string][];
477
+ tables: TableGeometry[];
478
+ }
479
+ declare function extractTextFromBuffer(buffer: Buffer, cleanView?: boolean, includeAppendix?: boolean): Promise<string>;
480
+ declare function _extractTextFromDoc(doc: DocumentObject, cleanView?: boolean, includeAppendix?: boolean, return_paragraph_offsets?: boolean, return_structure?: boolean): string | {
481
+ text: string;
482
+ paragraph_offsets: Map<any, [number, number]>;
483
+ } | {
484
+ text: string;
485
+ structure: ExtractStructure;
486
+ };
487
+
488
+ type DiffEdit = ModifyText | InsertTableRow | DeleteTableRow;
418
489
  declare function trim_common_context(target: string, new_val: string): [number, number];
419
490
  declare function generate_edits_from_text(original_text: string, modified_text: string): ModifyText[];
491
+ /**
492
+ * DOCX-to-DOCX diff over projections extracted with return_structure=true.
493
+ *
494
+ * Improvements over a flat generate_edits_from_text pass:
495
+ * - each OPC part (header/body/footer/notes) diffs against its
496
+ * counterpart, so no edit can span a part boundary and content that
497
+ * moved between parts is reported instead of hidden (QA 2026-07-18 C1);
498
+ * - table row insertions/deletions become structured insert_row /
499
+ * delete_row operations instead of pipe-text edits (QA C2).
500
+ *
501
+ * Every ModifyText keeps its _match_start_index pinned into text_orig, which
502
+ * the engine consumes positionally (the Node engine has no
503
+ * make_edits_self_contained JSON round trip like the Python CLI).
504
+ *
505
+ * Returns { edits, warnings }. Warnings describe fallbacks the caller should
506
+ * surface (differing part layouts, unanchorable rows, …).
507
+ */
508
+ declare function generate_structured_edits(text_orig: string, struct_orig: ExtractStructure, text_mod: string, struct_mod: ExtractStructure): {
509
+ edits: DiffEdit[];
510
+ warnings: string[];
511
+ };
420
512
  declare function create_unified_diff(original_text: string, modified_text: string, context_lines?: number): string;
421
513
  declare function create_word_patch_diff(original_text: string, modified_text: string, original_path?: string, modified_path?: string): string;
422
514
 
423
- declare function apply_edits_to_markdown(markdown_text: string, edits: ModifyText[], include_index?: boolean, highlight_only?: boolean): string;
515
+ /** One entry per input edit in apply_edits_to_markdown's edit_reports. */
516
+ interface MarkupEditReport {
517
+ index: number;
518
+ status: "applied" | "failed";
519
+ error: string | null;
520
+ occurrences: number;
521
+ }
522
+ /**
523
+ * Applies edits to Markdown text and returns CriticMarkup-annotated output.
524
+ *
525
+ * Edit resolution follows the SAME semantics as `apply` (QA 2026-07-18 M1):
526
+ * - `regex: true` targets match as regular expressions
527
+ * - `match_mode` is honored: "strict" (default) refuses ambiguous targets,
528
+ * "first" marks the first occurrence, "all" marks every occurrence
529
+ * - a missing target is a per-edit failure, never a silent skip
530
+ *
531
+ * When `edit_reports` is provided, one entry per input edit is appended:
532
+ * { index: 0-based input position, status: "applied"|"failed",
533
+ * error: string|null, occurrences: number }.
534
+ */
535
+ declare function apply_edits_to_markdown(markdown_text: string, edits: ModifyText[], include_index?: boolean, highlight_only?: boolean, edit_reports?: MarkupEditReport[]): string;
424
536
 
425
537
  /**
426
538
  * Stateless paginator for projected DOCX Markdown.
@@ -456,12 +568,6 @@ interface OutlineNode {
456
568
  }
457
569
  declare function extract_outline(doc: DocumentObject, projected_body: string, body_pages: string[], body_page_offsets: number[], paragraph_offsets?: Record<string, [number, number]> | Map<any, [number, number]> | null): OutlineNode[];
458
570
 
459
- declare function extractTextFromBuffer(buffer: Buffer, cleanView?: boolean): Promise<string>;
460
- declare function _extractTextFromDoc(doc: DocumentObject, cleanView?: boolean, includeAppendix?: boolean, return_paragraph_offsets?: boolean): string | {
461
- text: string;
462
- paragraph_offsets: Map<any, [number, number]>;
463
- };
464
-
465
571
  interface FinalizeOptions {
466
572
  filename: string;
467
573
  sanitize_mode?: 'full' | 'keep-markup' | 'baseline';
@@ -515,4 +621,4 @@ declare function userSearch(pattern: string, text: string, flags?: string): {
515
621
 
516
622
  declare function identifyEngine(): string;
517
623
 
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 };
624
+ export { BatchValidationError, type DiffEdit, DocumentMapper, DocumentObject, type ExtractStructure, type FinalizeOptions, type FinalizeResult, type MarkupEditReport, type OutlineNode, type PageInfo, type PaginationResult, RedlineEngine, RegexTimeoutError, type RowGeometry, type TableGeometry, 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, generate_structured_edits, identifyEngine, paginate, split_structural_appendix, trim_common_context, userFindAllMatches, userSearch, validate_edit_strings };