@adeu/core 1.18.4 → 1.19.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.
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { createTestDocument, addParagraph } from "./test-utils.js";
3
+ import { extractTextFromBuffer } from "./ingest.js";
4
+ import { RedlineEngine } from "./engine.js";
5
+ import { generate_edits_from_text } from "./diff.js";
6
+
7
+ // Edits carrying a caller-supplied _match_start_index (the indexed fast path,
8
+ // e.g. generate_edits_from_text output fed straight into process_batch) skip
9
+ // _pre_resolve_heuristic_edit, the only place _internal_op is normally set.
10
+ // The engine used to apply such edits with op === undefined: the deletion
11
+ // sweep still ran, but no insertion branch did — a replacement silently
12
+ // degraded to a pure tracked deletion (new text lost, batch still reported
13
+ // "applied") and a pure insertion failed outright.
14
+ describe("Indexed edits (_match_start_index fast path)", () => {
15
+ it("indexed replacement produces a tracked deletion AND insertion", async () => {
16
+ const doc = await createTestDocument();
17
+ addParagraph(doc, "The fee is 100 euros.");
18
+ const engine = new RedlineEngine(doc);
19
+
20
+ const idx = engine.mapper.full_text.indexOf("100");
21
+ expect(idx).toBeGreaterThanOrEqual(0);
22
+
23
+ const stats = engine.process_batch([
24
+ {
25
+ type: "modify",
26
+ target_text: "100",
27
+ new_text: "250",
28
+ _match_start_index: idx,
29
+ },
30
+ ]);
31
+ expect(stats.edits_applied).toBe(1);
32
+
33
+ const xml = doc.element.toString();
34
+ expect(xml).toContain("<w:del");
35
+ expect(xml).toContain("<w:ins");
36
+
37
+ const buf = await doc.save();
38
+ const clean = await extractTextFromBuffer(buf, true);
39
+ expect(clean).toContain("The fee is 250 euros.");
40
+ expect(clean).not.toContain("100");
41
+ });
42
+
43
+ it("indexed pure deletion produces a tracked deletion only", async () => {
44
+ const doc = await createTestDocument();
45
+ addParagraph(doc, "Payment is strictly due on Friday.");
46
+ const engine = new RedlineEngine(doc);
47
+
48
+ const idx = engine.mapper.full_text.indexOf("strictly ");
49
+ const stats = engine.process_batch([
50
+ {
51
+ type: "modify",
52
+ target_text: "strictly ",
53
+ new_text: "",
54
+ _match_start_index: idx,
55
+ },
56
+ ]);
57
+ expect(stats.edits_applied).toBe(1);
58
+
59
+ const xml = doc.element.toString();
60
+ expect(xml).toContain("<w:del");
61
+ expect(xml).not.toContain("<w:ins");
62
+
63
+ const buf = await doc.save();
64
+ const clean = await extractTextFromBuffer(buf, true);
65
+ expect(clean).toContain("Payment is due on Friday.");
66
+ });
67
+
68
+ it("indexed pure insertion applies mid-paragraph", async () => {
69
+ const doc = await createTestDocument();
70
+ addParagraph(doc, "Payment is due on Friday.");
71
+ const engine = new RedlineEngine(doc);
72
+
73
+ const idx = engine.mapper.full_text.indexOf("due");
74
+ const stats = engine.process_batch([
75
+ {
76
+ type: "modify",
77
+ target_text: "",
78
+ new_text: "strictly ",
79
+ _match_start_index: idx,
80
+ },
81
+ ]);
82
+ expect(stats.edits_applied).toBe(1);
83
+
84
+ const xml = doc.element.toString();
85
+ expect(xml).toContain("<w:ins");
86
+ expect(xml).not.toContain("<w:del");
87
+
88
+ const buf = await doc.save();
89
+ const clean = await extractTextFromBuffer(buf, true);
90
+ expect(clean).toContain("Payment is strictly due on Friday.");
91
+ });
92
+
93
+ it("indexed insertion at index 0 lands BEFORE the first run", async () => {
94
+ const doc = await createTestDocument();
95
+ addParagraph(doc, "World peace treaty.");
96
+ const engine = new RedlineEngine(doc);
97
+
98
+ const stats = engine.process_batch([
99
+ {
100
+ type: "modify",
101
+ target_text: "",
102
+ new_text: "PREAMBLE: ",
103
+ _match_start_index: 0,
104
+ },
105
+ ]);
106
+ expect(stats.edits_applied).toBe(1);
107
+
108
+ const buf = await doc.save();
109
+ const clean = await extractTextFromBuffer(buf, true);
110
+ expect(clean).toContain("PREAMBLE: World peace treaty.");
111
+ });
112
+
113
+ it("generate_edits_from_text output feeds straight into process_batch", async () => {
114
+ const original = "Payment of 100 EUR is due within 30 days of invoice.";
115
+ const modified = "Payment of 250 EUR is due within 14 days of receipt.";
116
+
117
+ const doc = await createTestDocument();
118
+ addParagraph(doc, original);
119
+ const engine = new RedlineEngine(doc);
120
+
121
+ const edits = generate_edits_from_text(original, modified);
122
+ expect(edits.length).toBeGreaterThan(0);
123
+ expect(edits.every((e) => e._match_start_index !== undefined)).toBe(true);
124
+
125
+ const stats = engine.process_batch(edits);
126
+ expect(stats.edits_applied).toBe(edits.length);
127
+ expect(stats.edits_skipped).toBe(0);
128
+
129
+ const buf = await doc.save();
130
+ const clean = await extractTextFromBuffer(buf, true);
131
+ expect(clean).toContain(modified);
132
+ // The rejected view must still show the original wording as deletions.
133
+ const tracked = await extractTextFromBuffer(buf, false);
134
+ expect(tracked).toContain("100");
135
+ expect(tracked).toContain("250");
136
+ });
137
+ });
@@ -127,6 +127,7 @@ describe("Webinar Report Bug Reproductions", () => {
127
127
  // Ideally, the system should allow distinct targeting of cells (e.g. preserving grid coordinates).
128
128
  // But due to the coordinate-flattening design flaw, it throws "Ambiguous match".
129
129
  const res = engine.process_batch([edit as any]);
130
+ console.log("BUG 3 PROCESS_BATCH RESULT:", JSON.stringify(res, null, 2));
130
131
  expect(res.edits_applied).toBe(1);
131
132
  });
132
133
  });
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createTestDocument, addParagraph } from './test-utils.js';
3
+ import { extractTextFromBuffer } from './ingest.js';
4
+ import { RedlineEngine } from './engine.js';
5
+
6
+ describe('Surgical Word-Level Diffing', () => {
7
+ it('preserves interior unchanged words as bare text', async () => {
8
+ const doc = await createTestDocument();
9
+ addParagraph(doc, "The quick brown fox jumped.");
10
+
11
+ const engine = new RedlineEngine(doc, "Test AI");
12
+ engine.process_batch([{
13
+ type: "modify",
14
+ target_text: "The quick brown fox jumped.",
15
+ new_text: "The slow brown fox leapt."
16
+ } as any]);
17
+
18
+ const outBuf = await doc.save();
19
+ const resultText = await extractTextFromBuffer(outBuf, false);
20
+
21
+ expect(resultText).not.toContain("{--The quick brown fox jumped.--}");
22
+ expect(resultText).toContain("{--quick--}{++slow++}");
23
+ expect(resultText).toContain(" brown fox ");
24
+ expect(resultText).toContain("{--jumped--}{++leapt++}");
25
+ });
26
+ });