@adeu/core 1.6.2 → 1.6.5
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.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +38 -38
- package/src/comments.test.ts +37 -37
- package/src/comments.ts +450 -450
- package/src/diff.test.ts +61 -61
- package/src/diff.ts +250 -250
- package/src/docx/bridge.ts +188 -188
- package/src/docx/dom.ts +53 -53
- package/src/docx/primitives.ts +64 -64
- package/src/domain.ts +10 -10
- package/src/engine.atomic.test.ts +57 -57
- package/src/engine.batch.test.ts +92 -92
- package/src/engine.safety.test.ts +41 -41
- package/src/engine.tables.test.ts +165 -165
- package/src/engine.ts +734 -734
- package/src/index.test.ts +7 -7
- package/src/index.ts +13 -13
- package/src/ingest.test.ts +43 -43
- package/src/ingest.ts +399 -399
- package/src/mapper.test.ts +65 -65
- package/src/mapper.ts +834 -834
- package/src/markup.test.ts +149 -149
- package/src/markup.ts +322 -322
- package/src/models.ts +50 -50
- package/src/outline.ts +376 -376
- package/src/pagination.ts +238 -238
- package/src/test-utils.ts +141 -141
- package/src/utils/docx.ts +477 -477
- package/tsconfig.json +21 -21
- package/tsup.config.ts +9 -9
- package/vitest.config.ts +11 -11
package/src/markup.test.ts
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
_replace_smart_quotes,
|
|
4
|
-
_make_fuzzy_regex,
|
|
5
|
-
_find_match_in_text,
|
|
6
|
-
_build_critic_markup,
|
|
7
|
-
apply_edits_to_markdown
|
|
8
|
-
} from './markup.js';
|
|
9
|
-
import { ModifyText } from './models.js';
|
|
10
|
-
|
|
11
|
-
describe('Markup Helpers', () => {
|
|
12
|
-
it.each([
|
|
13
|
-
['"Hello" and \'World\'', '"Hello" and \'World\''],
|
|
14
|
-
['Smart “quotes” and ‘apostrophes’', '"Hello" and \'World\''.replace('Hello', 'quotes').replace('World', 'apostrophes')], // Workaround for JS literal parsing in table
|
|
15
|
-
])('replace_smart_quotes(%s)', (input, expected) => {
|
|
16
|
-
// Quick override for the manual table definition above
|
|
17
|
-
if (input.includes('Smart')) expected = 'Smart "quotes" and \'apostrophes\'';
|
|
18
|
-
expect(_replace_smart_quotes(input)).toBe(expected);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it.each([
|
|
22
|
-
['hello world', ['hello world', 'hello world', 'hello world']],
|
|
23
|
-
['[___]', ['[___]', '[_____]', '[__________]']],
|
|
24
|
-
])('make_fuzzy_regex(%s)', (inputStr, matches) => {
|
|
25
|
-
const pattern = new RegExp(_make_fuzzy_regex(inputStr));
|
|
26
|
-
for (const m of matches) {
|
|
27
|
-
expect(m).toMatch(pattern);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it.each([
|
|
32
|
-
['The quick brown fox', 'quick', 4, 9],
|
|
33
|
-
['"Hello" said the fox', '"Hello"', 0, 7],
|
|
34
|
-
['hello world', 'hello world', 0, 13],
|
|
35
|
-
['The quick brown fox', 'elephant', -1, -1],
|
|
36
|
-
['Some text', '', -1, -1],
|
|
37
|
-
])('find_match_in_text: %s targets %s', (text, target, expectedStart, expectedEnd) => {
|
|
38
|
-
const [start, end] = _find_match_in_text(text, target);
|
|
39
|
-
expect(start).toBe(expectedStart);
|
|
40
|
-
expect(end).toBe(expectedEnd);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('build_critic_markup', () => {
|
|
45
|
-
it.each([
|
|
46
|
-
{ t: 'old', n: '', expected: '{--old--}' },
|
|
47
|
-
{ t: '', n: 'new', expected: '{++new++}' },
|
|
48
|
-
{ t: 'old', n: 'new', expected: '{--old--}{++new++}' },
|
|
49
|
-
{ t: 'old', n: 'new', c: 'Changed this', expected: '{--old--}{++new++}{>>Changed this<<}' },
|
|
50
|
-
{ t: 'old', n: 'new', idx: 3, incIdx: true, expected: '{--old--}{++new++}{>>[Edit:3]<<}' },
|
|
51
|
-
{ t: 'old', n: 'new', c: 'Reason', idx: 5, incIdx: true, expected: '{--old--}{++new++}{>>Reason [Edit:5]<<}' },
|
|
52
|
-
{ t: 'target', n: 'ignored', highlight: true, expected: '{==target==}' },
|
|
53
|
-
{ t: 'target', n: 'ignored', c: 'Note', idx: 2, incIdx: true, highlight: true, expected: '{==target==}{>>Note [Edit:2]<<}' },
|
|
54
|
-
|
|
55
|
-
// Formatting
|
|
56
|
-
{ t: '**Important**', n: '**Critical**', expected: '**{--Important--}{++Critical++}**' },
|
|
57
|
-
{ t: '_emphasis_', n: '_strong emphasis_', expected: '_{--emphasis--}{++strong emphasis++}_' },
|
|
58
|
-
{ t: '**_nested_**', n: '**_deeply nested_**', expected: '**{--_nested_--}{++_deeply nested_++}**' },
|
|
59
|
-
{ t: '**unbalanced', n: '**still unbalanced', expected: '{--**unbalanced--}{++**still unbalanced++}' },
|
|
60
|
-
{ t: '__0__', n: '__1__', expected: '{--__0__--}{++__1__++}' },
|
|
61
|
-
|
|
62
|
-
// Edge Cases
|
|
63
|
-
{ t: '', n: '', expected: '' },
|
|
64
|
-
{ t: ' ', n: 'text', expected: '{-- --}{++text++}' },
|
|
65
|
-
{ t: 'Line1\nLine2', n: 'SingleLine', expected: '{--Line1\nLine2--}{++SingleLine++}' },
|
|
66
|
-
{ t: 'C++', n: 'Python', expected: '{--C++--}{++Python++}' },
|
|
67
|
-
{ t: 'old', n: 'new', c: ' ', expected: '{--old--}{++new++}{>> <<}' },
|
|
68
|
-
{ t: 'old', n: 'new', c: '', expected: '{--old--}{++new++}' },
|
|
69
|
-
{ t: '', n: 'ignored', highlight: true, expected: '' }
|
|
70
|
-
])('builds correct markup for $t -> $n', ({ t, n, c, idx = 0, incIdx = false, highlight = false, expected }) => {
|
|
71
|
-
const result = _build_critic_markup(t, n, c, idx, incIdx, highlight);
|
|
72
|
-
if (highlight && !t) {
|
|
73
|
-
expect(['', '{====}']).toContain(result);
|
|
74
|
-
} else {
|
|
75
|
-
expect(result).toBe(expected);
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
describe('apply_edits_to_markdown', () => {
|
|
81
|
-
it.each([
|
|
82
|
-
['Notice of Termination', 'Notice of Termination', 'Notice of Immediate Termination', 'Notice of {++Immediate ++}Termination'],
|
|
83
|
-
['Hello World', 'Hello World', 'Hello Universe', 'Hello {--World--}{++Universe++}'],
|
|
84
|
-
['Old Item', 'Old Item', 'New Item', '{--Old--}{++New++} Item'],
|
|
85
|
-
['Original text', 'none', 'none', 'Original text'],
|
|
86
|
-
['Remove this word please.', 'this ', '', 'Remove {--this --}word please.'],
|
|
87
|
-
['', 'x', 'y', ''],
|
|
88
|
-
['Price is $100.00 (USD).', '$100.00', '$200.00', '{--$100.00--}{++$200.00++}'],
|
|
89
|
-
['Use {curly} and [square] brackets.', '{curly}', '{braces}', '{--{curly}--}{++{braces}++}']
|
|
90
|
-
])('basic edge cases: %s', (text, target, newText, expected) => {
|
|
91
|
-
const result = target === 'none' ? apply_edits_to_markdown(text, []) : apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: newText }]);
|
|
92
|
-
expect(result).toContain(expected);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('handles modification with comment', () => {
|
|
96
|
-
const text = 'The quick brown fox.';
|
|
97
|
-
const edits: ModifyText[] = [{ type: 'modify', target_text: 'quick', new_text: 'slow', comment: 'Speed change' }];
|
|
98
|
-
expect(apply_edits_to_markdown(text, edits)).toContain('{--quick--}{++slow++}{>>Speed change<<}');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('handles highlight_only mode', () => {
|
|
102
|
-
const text = 'Highlight this section please.';
|
|
103
|
-
const edits: ModifyText[] = [{ type: 'modify', target_text: 'this section', new_text: 'ignored' }];
|
|
104
|
-
const result = apply_edits_to_markdown(text, edits, false, true);
|
|
105
|
-
expect(result).toContain('{==this section==}');
|
|
106
|
-
expect(result).not.toContain('{--');
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('preserves order of multiple edits', () => {
|
|
110
|
-
const text = 'A B C';
|
|
111
|
-
const edits: ModifyText[] = [
|
|
112
|
-
{ type: 'modify', target_text: 'A', new_text: 'X' },
|
|
113
|
-
{ type: 'modify', target_text: 'B', new_text: 'Y' },
|
|
114
|
-
{ type: 'modify', target_text: 'C', new_text: 'Z' }
|
|
115
|
-
];
|
|
116
|
-
const result = apply_edits_to_markdown(text, edits, true);
|
|
117
|
-
expect(result).toContain('[Edit:0]');
|
|
118
|
-
expect(result.indexOf('{++X++}')).toBeLessThan(result.indexOf('{++Y++}'));
|
|
119
|
-
expect(result.indexOf('{++Y++}')).toBeLessThan(result.indexOf('{++Z++}'));
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('skips overlapping edits (first wins)', () => {
|
|
123
|
-
const text = 'The quick brown fox';
|
|
124
|
-
const edits: ModifyText[] = [
|
|
125
|
-
{ type: 'modify', target_text: 'quick brown', new_text: 'slow red' },
|
|
126
|
-
{ type: 'modify', target_text: 'brown fox', new_text: 'green dog' }
|
|
127
|
-
];
|
|
128
|
-
const result = apply_edits_to_markdown(text, edits);
|
|
129
|
-
expect(result).toContain('{--quick brown--}{++slow red++}');
|
|
130
|
-
expect(result).not.toContain('green dog');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it.each([
|
|
134
|
-
['hello world', 'hello world', '{--hello world--}'],
|
|
135
|
-
['Sign here: [__________]', '[___]', '{--[__________]--}'],
|
|
136
|
-
['"Hello" said the fox.', '"Hello"', '{--"Hello"--}']
|
|
137
|
-
])('fuzzy and smart quotes: %s', (text, target, expectedSubstring) => {
|
|
138
|
-
const result = apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: 'replacement' }]);
|
|
139
|
-
expect(result).toContain(expectedSubstring);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it.each([
|
|
143
|
-
['The **quick brown fox** jumped.', 'quick brown fox', 'slow red dog', 'The **{--quick brown fox--}{++slow red dog++}** jumped.'],
|
|
144
|
-
['This is _emphasized_ text.', 'emphasized', 'highlighted', '_{--emphasized--}{++highlighted++}_'],
|
|
145
|
-
['Variable __init__ is special.', '__init__', '__setup__', '__{--init--}{++setup++}__'],
|
|
146
|
-
])('formatting noise and preservation: %s', (text, target, newText, expectedSubstring) => {
|
|
147
|
-
const result = apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: newText }]);
|
|
148
|
-
expect(result).toContain(expectedSubstring);
|
|
149
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
_replace_smart_quotes,
|
|
4
|
+
_make_fuzzy_regex,
|
|
5
|
+
_find_match_in_text,
|
|
6
|
+
_build_critic_markup,
|
|
7
|
+
apply_edits_to_markdown
|
|
8
|
+
} from './markup.js';
|
|
9
|
+
import { ModifyText } from './models.js';
|
|
10
|
+
|
|
11
|
+
describe('Markup Helpers', () => {
|
|
12
|
+
it.each([
|
|
13
|
+
['"Hello" and \'World\'', '"Hello" and \'World\''],
|
|
14
|
+
['Smart “quotes” and ‘apostrophes’', '"Hello" and \'World\''.replace('Hello', 'quotes').replace('World', 'apostrophes')], // Workaround for JS literal parsing in table
|
|
15
|
+
])('replace_smart_quotes(%s)', (input, expected) => {
|
|
16
|
+
// Quick override for the manual table definition above
|
|
17
|
+
if (input.includes('Smart')) expected = 'Smart "quotes" and \'apostrophes\'';
|
|
18
|
+
expect(_replace_smart_quotes(input)).toBe(expected);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it.each([
|
|
22
|
+
['hello world', ['hello world', 'hello world', 'hello world']],
|
|
23
|
+
['[___]', ['[___]', '[_____]', '[__________]']],
|
|
24
|
+
])('make_fuzzy_regex(%s)', (inputStr, matches) => {
|
|
25
|
+
const pattern = new RegExp(_make_fuzzy_regex(inputStr));
|
|
26
|
+
for (const m of matches) {
|
|
27
|
+
expect(m).toMatch(pattern);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it.each([
|
|
32
|
+
['The quick brown fox', 'quick', 4, 9],
|
|
33
|
+
['"Hello" said the fox', '"Hello"', 0, 7],
|
|
34
|
+
['hello world', 'hello world', 0, 13],
|
|
35
|
+
['The quick brown fox', 'elephant', -1, -1],
|
|
36
|
+
['Some text', '', -1, -1],
|
|
37
|
+
])('find_match_in_text: %s targets %s', (text, target, expectedStart, expectedEnd) => {
|
|
38
|
+
const [start, end] = _find_match_in_text(text, target);
|
|
39
|
+
expect(start).toBe(expectedStart);
|
|
40
|
+
expect(end).toBe(expectedEnd);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('build_critic_markup', () => {
|
|
45
|
+
it.each([
|
|
46
|
+
{ t: 'old', n: '', expected: '{--old--}' },
|
|
47
|
+
{ t: '', n: 'new', expected: '{++new++}' },
|
|
48
|
+
{ t: 'old', n: 'new', expected: '{--old--}{++new++}' },
|
|
49
|
+
{ t: 'old', n: 'new', c: 'Changed this', expected: '{--old--}{++new++}{>>Changed this<<}' },
|
|
50
|
+
{ t: 'old', n: 'new', idx: 3, incIdx: true, expected: '{--old--}{++new++}{>>[Edit:3]<<}' },
|
|
51
|
+
{ t: 'old', n: 'new', c: 'Reason', idx: 5, incIdx: true, expected: '{--old--}{++new++}{>>Reason [Edit:5]<<}' },
|
|
52
|
+
{ t: 'target', n: 'ignored', highlight: true, expected: '{==target==}' },
|
|
53
|
+
{ t: 'target', n: 'ignored', c: 'Note', idx: 2, incIdx: true, highlight: true, expected: '{==target==}{>>Note [Edit:2]<<}' },
|
|
54
|
+
|
|
55
|
+
// Formatting
|
|
56
|
+
{ t: '**Important**', n: '**Critical**', expected: '**{--Important--}{++Critical++}**' },
|
|
57
|
+
{ t: '_emphasis_', n: '_strong emphasis_', expected: '_{--emphasis--}{++strong emphasis++}_' },
|
|
58
|
+
{ t: '**_nested_**', n: '**_deeply nested_**', expected: '**{--_nested_--}{++_deeply nested_++}**' },
|
|
59
|
+
{ t: '**unbalanced', n: '**still unbalanced', expected: '{--**unbalanced--}{++**still unbalanced++}' },
|
|
60
|
+
{ t: '__0__', n: '__1__', expected: '{--__0__--}{++__1__++}' },
|
|
61
|
+
|
|
62
|
+
// Edge Cases
|
|
63
|
+
{ t: '', n: '', expected: '' },
|
|
64
|
+
{ t: ' ', n: 'text', expected: '{-- --}{++text++}' },
|
|
65
|
+
{ t: 'Line1\nLine2', n: 'SingleLine', expected: '{--Line1\nLine2--}{++SingleLine++}' },
|
|
66
|
+
{ t: 'C++', n: 'Python', expected: '{--C++--}{++Python++}' },
|
|
67
|
+
{ t: 'old', n: 'new', c: ' ', expected: '{--old--}{++new++}{>> <<}' },
|
|
68
|
+
{ t: 'old', n: 'new', c: '', expected: '{--old--}{++new++}' },
|
|
69
|
+
{ t: '', n: 'ignored', highlight: true, expected: '' }
|
|
70
|
+
])('builds correct markup for $t -> $n', ({ t, n, c, idx = 0, incIdx = false, highlight = false, expected }) => {
|
|
71
|
+
const result = _build_critic_markup(t, n, c, idx, incIdx, highlight);
|
|
72
|
+
if (highlight && !t) {
|
|
73
|
+
expect(['', '{====}']).toContain(result);
|
|
74
|
+
} else {
|
|
75
|
+
expect(result).toBe(expected);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('apply_edits_to_markdown', () => {
|
|
81
|
+
it.each([
|
|
82
|
+
['Notice of Termination', 'Notice of Termination', 'Notice of Immediate Termination', 'Notice of {++Immediate ++}Termination'],
|
|
83
|
+
['Hello World', 'Hello World', 'Hello Universe', 'Hello {--World--}{++Universe++}'],
|
|
84
|
+
['Old Item', 'Old Item', 'New Item', '{--Old--}{++New++} Item'],
|
|
85
|
+
['Original text', 'none', 'none', 'Original text'],
|
|
86
|
+
['Remove this word please.', 'this ', '', 'Remove {--this --}word please.'],
|
|
87
|
+
['', 'x', 'y', ''],
|
|
88
|
+
['Price is $100.00 (USD).', '$100.00', '$200.00', '{--$100.00--}{++$200.00++}'],
|
|
89
|
+
['Use {curly} and [square] brackets.', '{curly}', '{braces}', '{--{curly}--}{++{braces}++}']
|
|
90
|
+
])('basic edge cases: %s', (text, target, newText, expected) => {
|
|
91
|
+
const result = target === 'none' ? apply_edits_to_markdown(text, []) : apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: newText }]);
|
|
92
|
+
expect(result).toContain(expected);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('handles modification with comment', () => {
|
|
96
|
+
const text = 'The quick brown fox.';
|
|
97
|
+
const edits: ModifyText[] = [{ type: 'modify', target_text: 'quick', new_text: 'slow', comment: 'Speed change' }];
|
|
98
|
+
expect(apply_edits_to_markdown(text, edits)).toContain('{--quick--}{++slow++}{>>Speed change<<}');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('handles highlight_only mode', () => {
|
|
102
|
+
const text = 'Highlight this section please.';
|
|
103
|
+
const edits: ModifyText[] = [{ type: 'modify', target_text: 'this section', new_text: 'ignored' }];
|
|
104
|
+
const result = apply_edits_to_markdown(text, edits, false, true);
|
|
105
|
+
expect(result).toContain('{==this section==}');
|
|
106
|
+
expect(result).not.toContain('{--');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('preserves order of multiple edits', () => {
|
|
110
|
+
const text = 'A B C';
|
|
111
|
+
const edits: ModifyText[] = [
|
|
112
|
+
{ type: 'modify', target_text: 'A', new_text: 'X' },
|
|
113
|
+
{ type: 'modify', target_text: 'B', new_text: 'Y' },
|
|
114
|
+
{ type: 'modify', target_text: 'C', new_text: 'Z' }
|
|
115
|
+
];
|
|
116
|
+
const result = apply_edits_to_markdown(text, edits, true);
|
|
117
|
+
expect(result).toContain('[Edit:0]');
|
|
118
|
+
expect(result.indexOf('{++X++}')).toBeLessThan(result.indexOf('{++Y++}'));
|
|
119
|
+
expect(result.indexOf('{++Y++}')).toBeLessThan(result.indexOf('{++Z++}'));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('skips overlapping edits (first wins)', () => {
|
|
123
|
+
const text = 'The quick brown fox';
|
|
124
|
+
const edits: ModifyText[] = [
|
|
125
|
+
{ type: 'modify', target_text: 'quick brown', new_text: 'slow red' },
|
|
126
|
+
{ type: 'modify', target_text: 'brown fox', new_text: 'green dog' }
|
|
127
|
+
];
|
|
128
|
+
const result = apply_edits_to_markdown(text, edits);
|
|
129
|
+
expect(result).toContain('{--quick brown--}{++slow red++}');
|
|
130
|
+
expect(result).not.toContain('green dog');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it.each([
|
|
134
|
+
['hello world', 'hello world', '{--hello world--}'],
|
|
135
|
+
['Sign here: [__________]', '[___]', '{--[__________]--}'],
|
|
136
|
+
['"Hello" said the fox.', '"Hello"', '{--"Hello"--}']
|
|
137
|
+
])('fuzzy and smart quotes: %s', (text, target, expectedSubstring) => {
|
|
138
|
+
const result = apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: 'replacement' }]);
|
|
139
|
+
expect(result).toContain(expectedSubstring);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it.each([
|
|
143
|
+
['The **quick brown fox** jumped.', 'quick brown fox', 'slow red dog', 'The **{--quick brown fox--}{++slow red dog++}** jumped.'],
|
|
144
|
+
['This is _emphasized_ text.', 'emphasized', 'highlighted', '_{--emphasized--}{++highlighted++}_'],
|
|
145
|
+
['Variable __init__ is special.', '__init__', '__setup__', '__{--init--}{++setup++}__'],
|
|
146
|
+
])('formatting noise and preservation: %s', (text, target, newText, expectedSubstring) => {
|
|
147
|
+
const result = apply_edits_to_markdown(text, [{ type: 'modify', target_text: target, new_text: newText }]);
|
|
148
|
+
expect(result).toContain(expectedSubstring);
|
|
149
|
+
});
|
|
150
150
|
});
|