@adeu/core 1.6.2 → 1.6.4

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.
@@ -1,58 +1,58 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createTestDocument, addParagraph } from './test-utils.js';
3
- import { DocumentObject } from './docx/bridge.js';
4
- import { extractTextFromBuffer } from './ingest.js';
5
- import { RedlineEngine } from './engine.js';
6
- import { ModifyText, AcceptChange } from './models.js';
7
-
8
- describe('Atomic Batch Pipeline (Node.js Port)', () => {
9
- it('prevents cascading misanchor when accepting changes shifts indices', async () => {
10
- // 1. Setup initial doc
11
- const doc = await createTestDocument();
12
- addParagraph(doc, "First paragraph.");
13
- addParagraph(doc, "Second paragraph.");
14
- addParagraph(doc, "Third paragraph.");
15
-
16
- // 2. Make an initial tracked change (Simulating Round 1)
17
- const engine = new RedlineEngine(doc, "Round1");
18
- engine.apply_edits([{ type: 'modify', target_text: "First", new_text: "1st" } as ModifyText]);
19
-
20
- const midBuf = await doc.save();
21
-
22
- // Verify intermediate state (Round 1)
23
- const midText = await extractTextFromBuffer(midBuf);
24
- expect(midText).toContain("{--First--}");
25
- expect(midText).toContain("{++1st++}");
26
-
27
- // Extract dynamically generated Change IDs for the Accept action
28
- const matches = Array.from(midText.matchAll(/\[Chg:(\d+)(?:\s+\w+)?\]/g));
29
- const chgIds = new Set(matches.map(m => m[1]));
30
- expect(chgIds.size).toBeGreaterThan(0);
31
-
32
- // 3. Execute the Atomic Batch (Simulating Round 2)
33
- const midDoc = await DocumentObject.load(midBuf);
34
- const engine2 = new RedlineEngine(midDoc, "Round2");
35
-
36
- const actions = Array.from(chgIds).map(id => ({ type: 'accept', target_id: `Chg:${id}` } as AcceptChange));
37
- const edits = [{ type: 'modify', target_text: "Third", new_text: "3rd" } as ModifyText];
38
-
39
- const changes = [...actions, ...edits];
40
- const stats = engine2.process_batch(changes);
41
-
42
- // 4. Assertions on the Tool Execution
43
- expect(stats.actions_applied).toBe(actions.length);
44
- expect(stats.edits_applied).toBe(1);
45
-
46
- // 5. Assertions on the Final Document State
47
- const finalBuf = await midDoc.save();
48
- const final_text = await extractTextFromBuffer(finalBuf);
49
-
50
- // The first paragraph should be cleanly accepted
51
- expect(final_text).toContain("1st paragraph.");
52
- expect(final_text).not.toContain("{--First--}");
53
-
54
- // The third paragraph should have the new tracked change anchored perfectly
55
- expect(final_text).toContain("{--Third--}");
56
- expect(final_text).toContain("{++3rd++}");
57
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createTestDocument, addParagraph } from './test-utils.js';
3
+ import { DocumentObject } from './docx/bridge.js';
4
+ import { extractTextFromBuffer } from './ingest.js';
5
+ import { RedlineEngine } from './engine.js';
6
+ import { ModifyText, AcceptChange } from './models.js';
7
+
8
+ describe('Atomic Batch Pipeline (Node.js Port)', () => {
9
+ it('prevents cascading misanchor when accepting changes shifts indices', async () => {
10
+ // 1. Setup initial doc
11
+ const doc = await createTestDocument();
12
+ addParagraph(doc, "First paragraph.");
13
+ addParagraph(doc, "Second paragraph.");
14
+ addParagraph(doc, "Third paragraph.");
15
+
16
+ // 2. Make an initial tracked change (Simulating Round 1)
17
+ const engine = new RedlineEngine(doc, "Round1");
18
+ engine.apply_edits([{ type: 'modify', target_text: "First", new_text: "1st" } as ModifyText]);
19
+
20
+ const midBuf = await doc.save();
21
+
22
+ // Verify intermediate state (Round 1)
23
+ const midText = await extractTextFromBuffer(midBuf);
24
+ expect(midText).toContain("{--First--}");
25
+ expect(midText).toContain("{++1st++}");
26
+
27
+ // Extract dynamically generated Change IDs for the Accept action
28
+ const matches = Array.from(midText.matchAll(/\[Chg:(\d+)(?:\s+\w+)?\]/g));
29
+ const chgIds = new Set(matches.map(m => m[1]));
30
+ expect(chgIds.size).toBeGreaterThan(0);
31
+
32
+ // 3. Execute the Atomic Batch (Simulating Round 2)
33
+ const midDoc = await DocumentObject.load(midBuf);
34
+ const engine2 = new RedlineEngine(midDoc, "Round2");
35
+
36
+ const actions = Array.from(chgIds).map(id => ({ type: 'accept', target_id: `Chg:${id}` } as AcceptChange));
37
+ const edits = [{ type: 'modify', target_text: "Third", new_text: "3rd" } as ModifyText];
38
+
39
+ const changes = [...actions, ...edits];
40
+ const stats = engine2.process_batch(changes);
41
+
42
+ // 4. Assertions on the Tool Execution
43
+ expect(stats.actions_applied).toBe(actions.length);
44
+ expect(stats.edits_applied).toBe(1);
45
+
46
+ // 5. Assertions on the Final Document State
47
+ const finalBuf = await midDoc.save();
48
+ const final_text = await extractTextFromBuffer(finalBuf);
49
+
50
+ // The first paragraph should be cleanly accepted
51
+ expect(final_text).toContain("1st paragraph.");
52
+ expect(final_text).not.toContain("{--First--}");
53
+
54
+ // The third paragraph should have the new tracked change anchored perfectly
55
+ expect(final_text).toContain("{--Third--}");
56
+ expect(final_text).toContain("{++3rd++}");
57
+ });
58
58
  });
@@ -1,93 +1,93 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createTestDocument, addParagraph } from './test-utils.js';
3
- import { DocumentObject } from './docx/bridge.js';
4
- import { extractTextFromBuffer } from './ingest.js';
5
- import { RedlineEngine } from './engine.js';
6
- import { ModifyText, AcceptChange, RejectChange } from './models.js';
7
-
8
- describe('Batch Reliability (Node.js Port)', () => {
9
- it('batch accept does not corrupt the document', async () => {
10
- const doc = await createTestDocument();
11
- addParagraph(doc, "Para 1");
12
- addParagraph(doc, "Para 2");
13
- addParagraph(doc, "Para 3");
14
-
15
- const engine = new RedlineEngine(doc);
16
- const edits: ModifyText[] = [
17
- { type: 'modify', target_text: "Para 1", new_text: "Para One" },
18
- { type: 'modify', target_text: "Para 2", new_text: "Para Two" },
19
- { type: 'modify', target_text: "Para 3", new_text: "Para Three" },
20
- ];
21
-
22
- engine.apply_edits(edits);
23
- const redlinedBuf = await doc.save();
24
-
25
- const text = await extractTextFromBuffer(redlinedBuf);
26
- expect(text).toContain("[Chg:1 ");
27
- expect(text).toContain("[Chg:6 ");
28
-
29
- // BATCH ACCEPT ALL
30
- const midDoc = await DocumentObject.load(redlinedBuf);
31
- const engine2 = new RedlineEngine(midDoc);
32
-
33
- const actions: AcceptChange[] = [1, 2, 3, 4, 5, 6].map(id => ({ type: 'accept', target_id: `Chg:${id}` }));
34
-
35
- // Test direct apply_review_actions (Note: method missing in raw TS port right now, needs implementing)
36
- const [applied, skipped] = (engine2 as any).apply_review_actions(actions);
37
-
38
- expect(applied).toBe(6);
39
- expect(skipped).toBe(0);
40
-
41
- const finalBuf = await midDoc.save();
42
- const final_text = await extractTextFromBuffer(finalBuf);
43
-
44
- expect(final_text).toContain("Para One");
45
- expect(final_text).toContain("Para Two");
46
- expect(final_text).toContain("Para Three");
47
-
48
- expect(final_text).not.toContain("Para 1");
49
- expect(final_text).not.toContain("Para 2");
50
- expect(final_text).not.toContain("Para 3");
51
-
52
- expect(final_text).not.toContain("[Chg:");
53
- expect(final_text).not.toContain("{++");
54
- expect(final_text).not.toContain("{--");
55
- });
56
-
57
- it('batch mixed accept and reject maintains integrity', async () => {
58
- const doc = await createTestDocument();
59
- addParagraph(doc, "Para 1");
60
- addParagraph(doc, "Para 2");
61
-
62
- const engine = new RedlineEngine(doc);
63
- const edits: ModifyText[] = [
64
- { type: 'modify', target_text: "Para 1", new_text: "Para One" },
65
- { type: 'modify', target_text: "Para 2", new_text: "Para Two" },
66
- ];
67
-
68
- engine.apply_edits(edits);
69
- const redlinedBuf = await doc.save();
70
-
71
- const midDoc = await DocumentObject.load(redlinedBuf);
72
- const engine2 = new RedlineEngine(midDoc);
73
-
74
- const actions = [
75
- { type: 'accept', target_id: "Chg:3" } as AcceptChange,
76
- { type: 'accept', target_id: "Chg:4" } as AcceptChange,
77
- { type: 'reject', target_id: "Chg:1" } as RejectChange,
78
- { type: 'reject', target_id: "Chg:2" } as RejectChange,
79
- ];
80
-
81
- const [applied] = (engine2 as any).apply_review_actions(actions);
82
- expect(applied).toBe(4);
83
-
84
- const finalBuf = await midDoc.save();
85
- const text_final = await extractTextFromBuffer(finalBuf);
86
-
87
- expect(text_final).toContain("Para One");
88
- expect(text_final).not.toContain("Para 1");
89
-
90
- expect(text_final).toContain("Para 2");
91
- expect(text_final).not.toContain("Para Two");
92
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createTestDocument, addParagraph } from './test-utils.js';
3
+ import { DocumentObject } from './docx/bridge.js';
4
+ import { extractTextFromBuffer } from './ingest.js';
5
+ import { RedlineEngine } from './engine.js';
6
+ import { ModifyText, AcceptChange, RejectChange } from './models.js';
7
+
8
+ describe('Batch Reliability (Node.js Port)', () => {
9
+ it('batch accept does not corrupt the document', async () => {
10
+ const doc = await createTestDocument();
11
+ addParagraph(doc, "Para 1");
12
+ addParagraph(doc, "Para 2");
13
+ addParagraph(doc, "Para 3");
14
+
15
+ const engine = new RedlineEngine(doc);
16
+ const edits: ModifyText[] = [
17
+ { type: 'modify', target_text: "Para 1", new_text: "Para One" },
18
+ { type: 'modify', target_text: "Para 2", new_text: "Para Two" },
19
+ { type: 'modify', target_text: "Para 3", new_text: "Para Three" },
20
+ ];
21
+
22
+ engine.apply_edits(edits);
23
+ const redlinedBuf = await doc.save();
24
+
25
+ const text = await extractTextFromBuffer(redlinedBuf);
26
+ expect(text).toContain("[Chg:1 ");
27
+ expect(text).toContain("[Chg:6 ");
28
+
29
+ // BATCH ACCEPT ALL
30
+ const midDoc = await DocumentObject.load(redlinedBuf);
31
+ const engine2 = new RedlineEngine(midDoc);
32
+
33
+ const actions: AcceptChange[] = [1, 2, 3, 4, 5, 6].map(id => ({ type: 'accept', target_id: `Chg:${id}` }));
34
+
35
+ // Test direct apply_review_actions (Note: method missing in raw TS port right now, needs implementing)
36
+ const [applied, skipped] = (engine2 as any).apply_review_actions(actions);
37
+
38
+ expect(applied).toBe(6);
39
+ expect(skipped).toBe(0);
40
+
41
+ const finalBuf = await midDoc.save();
42
+ const final_text = await extractTextFromBuffer(finalBuf);
43
+
44
+ expect(final_text).toContain("Para One");
45
+ expect(final_text).toContain("Para Two");
46
+ expect(final_text).toContain("Para Three");
47
+
48
+ expect(final_text).not.toContain("Para 1");
49
+ expect(final_text).not.toContain("Para 2");
50
+ expect(final_text).not.toContain("Para 3");
51
+
52
+ expect(final_text).not.toContain("[Chg:");
53
+ expect(final_text).not.toContain("{++");
54
+ expect(final_text).not.toContain("{--");
55
+ });
56
+
57
+ it('batch mixed accept and reject maintains integrity', async () => {
58
+ const doc = await createTestDocument();
59
+ addParagraph(doc, "Para 1");
60
+ addParagraph(doc, "Para 2");
61
+
62
+ const engine = new RedlineEngine(doc);
63
+ const edits: ModifyText[] = [
64
+ { type: 'modify', target_text: "Para 1", new_text: "Para One" },
65
+ { type: 'modify', target_text: "Para 2", new_text: "Para Two" },
66
+ ];
67
+
68
+ engine.apply_edits(edits);
69
+ const redlinedBuf = await doc.save();
70
+
71
+ const midDoc = await DocumentObject.load(redlinedBuf);
72
+ const engine2 = new RedlineEngine(midDoc);
73
+
74
+ const actions = [
75
+ { type: 'accept', target_id: "Chg:3" } as AcceptChange,
76
+ { type: 'accept', target_id: "Chg:4" } as AcceptChange,
77
+ { type: 'reject', target_id: "Chg:1" } as RejectChange,
78
+ { type: 'reject', target_id: "Chg:2" } as RejectChange,
79
+ ];
80
+
81
+ const [applied] = (engine2 as any).apply_review_actions(actions);
82
+ expect(applied).toBe(4);
83
+
84
+ const finalBuf = await midDoc.save();
85
+ const text_final = await extractTextFromBuffer(finalBuf);
86
+
87
+ expect(text_final).toContain("Para One");
88
+ expect(text_final).not.toContain("Para 1");
89
+
90
+ expect(text_final).toContain("Para 2");
91
+ expect(text_final).not.toContain("Para Two");
92
+ });
93
93
  });
@@ -1,42 +1,42 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createTestDocument, addParagraph } from './test-utils.js';
3
- import { RedlineEngine } from './engine.js';
4
- import { ModifyText } from './models.js';
5
-
6
- describe('Safety Engine Constraints (Node.js Port)', () => {
7
- it('rejects empty target heuristic to prevent accidental insertions', async () => {
8
- const doc = await createTestDocument();
9
- addParagraph(doc, "Content");
10
-
11
- const engine = new RedlineEngine(doc);
12
- const edit: ModifyText = { type: 'modify', target_text: "", new_text: "Unexpected Header" };
13
-
14
- const [applied, skipped] = engine.apply_edits([edit]);
15
-
16
- expect(applied).toBe(0);
17
- expect(skipped).toBe(1);
18
-
19
- const xml = (doc.element as Element).ownerDocument?.documentElement.toString();
20
- expect(xml).not.toContain("Unexpected Header");
21
- });
22
-
23
- it('applies heuristic edits strictly once for multiple occurrences', async () => {
24
- const doc = await createTestDocument();
25
- addParagraph(doc, "Repeat");
26
- addParagraph(doc, "Repeat");
27
-
28
- const engine = new RedlineEngine(doc);
29
- const edit: ModifyText = { type: 'modify', target_text: "Repeat", new_text: "Changed" };
30
-
31
- const [applied, skipped] = engine.apply_edits([edit]);
32
-
33
- expect(applied).toBe(1);
34
- expect(skipped).toBe(0);
35
-
36
- const xml = (doc.element as Element).ownerDocument?.documentElement.toString();
37
-
38
- // We expect one deletion of Repeat and one insertion of Changed
39
- expect((xml?.match(/<w:delText[^>]*>Repeat<\/w:delText>/g) || []).length).toBe(1);
40
- expect((xml?.match(/<w:t[^>]*>Changed<\/w:t>/g) || []).length).toBe(1);
41
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createTestDocument, addParagraph } from './test-utils.js';
3
+ import { RedlineEngine } from './engine.js';
4
+ import { ModifyText } from './models.js';
5
+
6
+ describe('Safety Engine Constraints (Node.js Port)', () => {
7
+ it('rejects empty target heuristic to prevent accidental insertions', async () => {
8
+ const doc = await createTestDocument();
9
+ addParagraph(doc, "Content");
10
+
11
+ const engine = new RedlineEngine(doc);
12
+ const edit: ModifyText = { type: 'modify', target_text: "", new_text: "Unexpected Header" };
13
+
14
+ const [applied, skipped] = engine.apply_edits([edit]);
15
+
16
+ expect(applied).toBe(0);
17
+ expect(skipped).toBe(1);
18
+
19
+ const xml = (doc.element as Element).ownerDocument?.documentElement.toString();
20
+ expect(xml).not.toContain("Unexpected Header");
21
+ });
22
+
23
+ it('applies heuristic edits strictly once for multiple occurrences', async () => {
24
+ const doc = await createTestDocument();
25
+ addParagraph(doc, "Repeat");
26
+ addParagraph(doc, "Repeat");
27
+
28
+ const engine = new RedlineEngine(doc);
29
+ const edit: ModifyText = { type: 'modify', target_text: "Repeat", new_text: "Changed" };
30
+
31
+ const [applied, skipped] = engine.apply_edits([edit]);
32
+
33
+ expect(applied).toBe(1);
34
+ expect(skipped).toBe(0);
35
+
36
+ const xml = (doc.element as Element).ownerDocument?.documentElement.toString();
37
+
38
+ // We expect one deletion of Repeat and one insertion of Changed
39
+ expect((xml?.match(/<w:delText[^>]*>Repeat<\/w:delText>/g) || []).length).toBe(1);
40
+ expect((xml?.match(/<w:t[^>]*>Changed<\/w:t>/g) || []).length).toBe(1);
41
+ });
42
42
  });