@memberjunction/global 5.51.2 → 5.51.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.
Files changed (42) hide show
  1. package/README.md +2 -2
  2. package/dist/SQLExpressionValidator.d.ts +37 -31
  3. package/dist/SQLExpressionValidator.d.ts.map +1 -1
  4. package/dist/SQLExpressionValidator.js +129 -117
  5. package/dist/SQLExpressionValidator.js.map +1 -1
  6. package/dist/SQLScreeningScanner.d.ts +91 -0
  7. package/dist/SQLScreeningScanner.d.ts.map +1 -0
  8. package/dist/SQLScreeningScanner.js +332 -0
  9. package/dist/SQLScreeningScanner.js.map +1 -0
  10. package/dist/SafeExpressionEvaluator.d.ts +131 -6
  11. package/dist/SafeExpressionEvaluator.d.ts.map +1 -1
  12. package/dist/SafeExpressionEvaluator.js +344 -41
  13. package/dist/SafeExpressionEvaluator.js.map +1 -1
  14. package/dist/__tests__/SQLExpressionValidator.security.test.js +176 -2
  15. package/dist/__tests__/SQLExpressionValidator.security.test.js.map +1 -1
  16. package/dist/__tests__/SQLScreeningScanner.test.d.ts +2 -0
  17. package/dist/__tests__/SQLScreeningScanner.test.d.ts.map +1 -0
  18. package/dist/__tests__/SQLScreeningScanner.test.js +172 -0
  19. package/dist/__tests__/SQLScreeningScanner.test.js.map +1 -0
  20. package/dist/__tests__/SafeExpressionEvaluator.security.test.d.ts +2 -0
  21. package/dist/__tests__/SafeExpressionEvaluator.security.test.d.ts.map +1 -0
  22. package/dist/__tests__/SafeExpressionEvaluator.security.test.js +284 -0
  23. package/dist/__tests__/SafeExpressionEvaluator.security.test.js.map +1 -0
  24. package/dist/__tests__/SafeExpressionEvaluator.test.d.ts +2 -0
  25. package/dist/__tests__/SafeExpressionEvaluator.test.d.ts.map +1 -0
  26. package/dist/__tests__/SafeExpressionEvaluator.test.js +355 -0
  27. package/dist/__tests__/SafeExpressionEvaluator.test.js.map +1 -0
  28. package/dist/__tests__/util.jsonRepair.test.d.ts +2 -0
  29. package/dist/__tests__/util.jsonRepair.test.d.ts.map +1 -0
  30. package/dist/__tests__/util.jsonRepair.test.js +112 -0
  31. package/dist/__tests__/util.jsonRepair.test.js.map +1 -0
  32. package/dist/__tests__/util.test.js +19 -1
  33. package/dist/__tests__/util.test.js.map +1 -1
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +1 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/util.d.ts +80 -0
  39. package/dist/util.d.ts.map +1 -1
  40. package/dist/util.js +220 -2
  41. package/dist/util.js.map +1 -1
  42. package/package.json +2 -1
@@ -0,0 +1,355 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { SafeExpressionEvaluator, defaultExpressionEvaluator, } from '../SafeExpressionEvaluator.js';
3
+ /**
4
+ * Functional tests for SafeExpressionEvaluator.
5
+ *
6
+ * These exercise the documented contract: evaluate a boolean expression against a
7
+ * context object, returning an {@link ExpressionEvaluationResult}. Adversarial /
8
+ * sandbox-escape scenarios live in SafeExpressionEvaluator.security.test.ts.
9
+ */
10
+ describe('SafeExpressionEvaluator', () => {
11
+ let evaluator;
12
+ beforeEach(() => {
13
+ evaluator = new SafeExpressionEvaluator();
14
+ });
15
+ // ---------------------------------------------------------------
16
+ // Simple comparisons over context data
17
+ // ---------------------------------------------------------------
18
+ describe('comparison operators', () => {
19
+ it('should evaluate == to true when values match', () => {
20
+ const r = evaluator.evaluate("status == 'active'", { status: 'active' });
21
+ expect(r.success).toBe(true);
22
+ expect(r.value).toBe(true);
23
+ });
24
+ it('should evaluate == to false when values differ', () => {
25
+ const r = evaluator.evaluate("status == 'active'", { status: 'inactive' });
26
+ expect(r.success).toBe(true);
27
+ expect(r.value).toBe(false);
28
+ });
29
+ it('should evaluate strict === correctly', () => {
30
+ expect(evaluator.evaluate('count === 5', { count: 5 }).value).toBe(true);
31
+ expect(evaluator.evaluate('count === 5', { count: 6 }).value).toBe(false);
32
+ });
33
+ it('should evaluate != and !==', () => {
34
+ expect(evaluator.evaluate('count != 5', { count: 6 }).value).toBe(true);
35
+ expect(evaluator.evaluate('count !== 5', { count: 5 }).value).toBe(false);
36
+ });
37
+ it('should evaluate <, >, <=, >=', () => {
38
+ expect(evaluator.evaluate('n < 10', { n: 5 }).value).toBe(true);
39
+ expect(evaluator.evaluate('n > 10', { n: 5 }).value).toBe(false);
40
+ expect(evaluator.evaluate('n <= 5', { n: 5 }).value).toBe(true);
41
+ expect(evaluator.evaluate('n >= 6', { n: 5 }).value).toBe(false);
42
+ });
43
+ });
44
+ // ---------------------------------------------------------------
45
+ // Logical operators
46
+ // ---------------------------------------------------------------
47
+ describe('logical operators', () => {
48
+ it('should evaluate && (AND)', () => {
49
+ const ctx = { a: true, b: false };
50
+ expect(evaluator.evaluate('a && b', ctx).value).toBe(false);
51
+ expect(evaluator.evaluate('a && !b', ctx).value).toBe(true);
52
+ });
53
+ it('should evaluate || (OR)', () => {
54
+ const ctx = { a: false, b: true };
55
+ expect(evaluator.evaluate('a || b', ctx).value).toBe(true);
56
+ expect(evaluator.evaluate('a || false', ctx).value).toBe(false);
57
+ });
58
+ it('should evaluate ! (NOT)', () => {
59
+ expect(evaluator.evaluate('!isActive', { isActive: false }).value).toBe(true);
60
+ expect(evaluator.evaluate('!isActive', { isActive: true }).value).toBe(false);
61
+ });
62
+ it('should combine comparisons with logical operators', () => {
63
+ const ctx = { tier: 'premium', total: 1500 };
64
+ const r = evaluator.evaluate("tier == 'premium' && total > 1000", ctx);
65
+ expect(r.success).toBe(true);
66
+ expect(r.value).toBe(true);
67
+ });
68
+ });
69
+ // ---------------------------------------------------------------
70
+ // Nested property + array access
71
+ // ---------------------------------------------------------------
72
+ describe('property and array access', () => {
73
+ it('should resolve nested dot-notation properties', () => {
74
+ const ctx = {
75
+ payload: { customer: { tier: 'premium' }, order: { total: 1500 } },
76
+ };
77
+ const r = evaluator.evaluate("payload.customer.tier == 'premium' && payload.order.total > 1000", ctx);
78
+ expect(r.success).toBe(true);
79
+ expect(r.value).toBe(true);
80
+ });
81
+ it('should resolve array index access', () => {
82
+ const ctx = { items: [10, 20, 30] };
83
+ expect(evaluator.evaluate('items[0] == 10', ctx).value).toBe(true);
84
+ expect(evaluator.evaluate('items[2] > items[0]', ctx).value).toBe(true);
85
+ });
86
+ });
87
+ // ---------------------------------------------------------------
88
+ // Safe methods
89
+ // ---------------------------------------------------------------
90
+ describe('safe string/array methods', () => {
91
+ it('should support .length', () => {
92
+ expect(evaluator.evaluate('name.length > 3', { name: 'Alice' }).value).toBe(true);
93
+ expect(evaluator.evaluate('name.length > 3', { name: 'Al' }).value).toBe(false);
94
+ });
95
+ it('should support .includes()', () => {
96
+ expect(evaluator.evaluate("tags.includes('urgent')", { tags: ['work', 'urgent'] }).value).toBe(true);
97
+ });
98
+ it('should support .startsWith() and .endsWith()', () => {
99
+ const ctx = { code: 'AB-123' };
100
+ expect(evaluator.evaluate("code.startsWith('AB')", ctx).value).toBe(true);
101
+ expect(evaluator.evaluate("code.endsWith('123')", ctx).value).toBe(true);
102
+ });
103
+ it('should support .toLowerCase() in a comparison', () => {
104
+ const r = evaluator.evaluate("name.toLowerCase() == 'alice'", { name: 'ALICE' });
105
+ expect(r.success).toBe(true);
106
+ expect(r.value).toBe(true);
107
+ });
108
+ });
109
+ // ---------------------------------------------------------------
110
+ // Array higher-order methods
111
+ // ---------------------------------------------------------------
112
+ describe('array higher-order methods', () => {
113
+ it('should support .some()', () => {
114
+ const ctx = { items: [{ price: 50 }, { price: 150 }] };
115
+ const r = evaluator.evaluate('items.some(item => item.price > 100)', ctx);
116
+ expect(r.success).toBe(true);
117
+ expect(r.value).toBe(true);
118
+ });
119
+ it('should support .every()', () => {
120
+ const ctx = { items: [{ price: 50 }, { price: 150 }] };
121
+ expect(evaluator.evaluate('items.every(item => item.price > 0)', ctx).value).toBe(true);
122
+ expect(evaluator.evaluate('items.every(item => item.price > 100)', ctx).value).toBe(false);
123
+ });
124
+ it('should support .filter().length', () => {
125
+ const ctx = {
126
+ items: [{ price: 50 }, { price: 150 }, { price: 200 }],
127
+ };
128
+ const r = evaluator.evaluate('items.filter(item => item.price > 100).length == 2', ctx);
129
+ expect(r.success).toBe(true);
130
+ expect(r.value).toBe(true);
131
+ });
132
+ });
133
+ // ---------------------------------------------------------------
134
+ // typeof
135
+ // ---------------------------------------------------------------
136
+ describe('typeof', () => {
137
+ it('should evaluate typeof checks', () => {
138
+ expect(evaluator.evaluate("typeof name == 'string'", { name: 'x' }).value).toBe(true);
139
+ expect(evaluator.evaluate("typeof count == 'number'", { count: 5 }).value).toBe(true);
140
+ });
141
+ });
142
+ // ---------------------------------------------------------------
143
+ // Edge values: null / undefined / empty
144
+ // ---------------------------------------------------------------
145
+ describe('edge values', () => {
146
+ it('should compare against null', () => {
147
+ expect(evaluator.evaluate('x == null', { x: null }).value).toBe(true);
148
+ expect(evaluator.evaluate('x === null', { x: null }).value).toBe(true);
149
+ });
150
+ it('should treat missing property (undefined) loosely equal to null', () => {
151
+ const ctx = { obj: {} };
152
+ // obj.missing is undefined; undefined == null is true in JS
153
+ expect(evaluator.evaluate('obj.missing == null', ctx).value).toBe(true);
154
+ });
155
+ it('should coerce a truthy non-boolean result via Boolean()', () => {
156
+ // The evaluator wraps the expression in Boolean(...): a bare truthy string yields true
157
+ const r = evaluator.evaluate('name', { name: 'non-empty' });
158
+ expect(r.success).toBe(true);
159
+ expect(r.value).toBe(true);
160
+ });
161
+ it('should coerce an empty-string result to false', () => {
162
+ const r = evaluator.evaluate('name', { name: '' });
163
+ expect(r.success).toBe(true);
164
+ expect(r.value).toBe(false);
165
+ });
166
+ it('should handle an empty context object with a self-contained expression', () => {
167
+ const r = evaluator.evaluate('1 < 2', {});
168
+ expect(r.success).toBe(true);
169
+ expect(r.value).toBe(true);
170
+ });
171
+ });
172
+ // ---------------------------------------------------------------
173
+ // Error paths for malformed / invalid input
174
+ // ---------------------------------------------------------------
175
+ describe('error handling', () => {
176
+ it('should fail on an empty expression', () => {
177
+ const r = evaluator.evaluate('', { a: 1 });
178
+ expect(r.success).toBe(false);
179
+ expect(r.error).toContain('non-empty string');
180
+ });
181
+ it('should evaluate a whitespace-only expression to false (Boolean() of no argument)', () => {
182
+ // A non-empty but whitespace-only string passes validation and compiles to
183
+ // `Boolean( )`, which is Boolean() === false — so it succeeds with value false.
184
+ const r = evaluator.evaluate(' ', { a: 1 });
185
+ expect(r.success).toBe(true);
186
+ expect(r.value).toBe(false);
187
+ });
188
+ it('should fail on unbalanced parentheses', () => {
189
+ const r = evaluator.evaluate('(a > 1', { a: 5 });
190
+ expect(r.success).toBe(false);
191
+ expect(r.error).toContain('Unbalanced parentheses');
192
+ });
193
+ it('should fail on a closing paren before an opening paren', () => {
194
+ const r = evaluator.evaluate('a > 1)', { a: 5 });
195
+ expect(r.success).toBe(false);
196
+ expect(r.error).toContain('Unbalanced parentheses');
197
+ });
198
+ it('should fail on a syntactically malformed expression', () => {
199
+ const r = evaluator.evaluate('a ===', { a: 1 });
200
+ expect(r.success).toBe(false);
201
+ expect(r.error).toBeDefined();
202
+ });
203
+ it('should fail when referencing an unknown identifier not in context', () => {
204
+ const r = evaluator.evaluate('missingVar > 1', { a: 1 });
205
+ expect(r.success).toBe(false);
206
+ expect(r.error).toContain('Expression evaluation failed');
207
+ });
208
+ it('should fail when the expression exceeds the 1000-character limit', () => {
209
+ const long = '1 == 1 && '.repeat(200) + '1 == 1'; // well over 1000 chars
210
+ const r = evaluator.evaluate(long, {});
211
+ expect(r.success).toBe(false);
212
+ expect(r.error).toContain('maximum length');
213
+ });
214
+ it('should fail when a context key is not a valid JS identifier', () => {
215
+ // Context keys become Function parameter names; "my-key" is not a valid identifier.
216
+ const ctx = { 'my-key': 1 };
217
+ const r = evaluator.evaluate('1 == 1', ctx);
218
+ expect(r.success).toBe(false);
219
+ expect(r.error).toBeDefined();
220
+ });
221
+ });
222
+ // ---------------------------------------------------------------
223
+ // Diagnostics
224
+ // ---------------------------------------------------------------
225
+ describe('diagnostics', () => {
226
+ it('should omit diagnostics by default', () => {
227
+ const r = evaluator.evaluate('a == 1', { a: 1 });
228
+ expect(r.diagnostics).toBeUndefined();
229
+ });
230
+ it('should include diagnostics when enabled on success', () => {
231
+ const r = evaluator.evaluate('a == 1', { a: 1 }, true);
232
+ expect(r.diagnostics).toBeDefined();
233
+ expect(r.diagnostics?.expression).toBe('a == 1');
234
+ expect(r.diagnostics?.context).toEqual({ a: 1 });
235
+ expect(typeof r.diagnostics?.evaluationTime).toBe('number');
236
+ });
237
+ it('should include diagnostics when enabled on failure', () => {
238
+ const r = evaluator.evaluate('', { a: 1 }, true);
239
+ expect(r.success).toBe(false);
240
+ expect(r.diagnostics).toBeDefined();
241
+ expect(r.diagnostics?.expression).toBe('');
242
+ });
243
+ });
244
+ // ---------------------------------------------------------------
245
+ // Caching behaviour (observable: repeated evals stay consistent)
246
+ // ---------------------------------------------------------------
247
+ describe('compiled-expression cache', () => {
248
+ it('should return consistent results across repeated evaluations of the same expression', () => {
249
+ const first = evaluator.evaluate('a > 1', { a: 5 });
250
+ const second = evaluator.evaluate('a > 1', { a: 5 });
251
+ expect(first.value).toBe(true);
252
+ expect(second.value).toBe(true);
253
+ });
254
+ it('should honour different context values on a cached expression', () => {
255
+ // Same expression + same context keys -> cache hit, but different values must still be applied
256
+ expect(evaluator.evaluate('a > 1', { a: 5 }).value).toBe(true);
257
+ expect(evaluator.evaluate('a > 1', { a: 0 }).value).toBe(false);
258
+ });
259
+ });
260
+ // ---------------------------------------------------------------
261
+ // evaluateMultiple
262
+ // ---------------------------------------------------------------
263
+ describe('evaluateMultiple', () => {
264
+ it('should evaluate several named expressions against one context', () => {
265
+ const results = evaluator.evaluateMultiple([
266
+ { expression: 'a > 1', name: 'aPositive' },
267
+ { expression: "b == 'x'", name: 'bIsX' },
268
+ ], { a: 5, b: 'x' });
269
+ expect(results.aPositive.value).toBe(true);
270
+ expect(results.bIsX.value).toBe(true);
271
+ });
272
+ it('should key unnamed expressions by index', () => {
273
+ const results = evaluator.evaluateMultiple([{ expression: 'a > 1' }], { a: 5 });
274
+ expect(results.expression_0.value).toBe(true);
275
+ });
276
+ it('should report per-expression failures independently', () => {
277
+ const results = evaluator.evaluateMultiple([
278
+ { expression: 'a > 1', name: 'ok' },
279
+ { expression: 'a ===', name: 'broken' },
280
+ ], { a: 5 });
281
+ expect(results.ok.success).toBe(true);
282
+ expect(results.broken.success).toBe(false);
283
+ });
284
+ });
285
+ // ---------------------------------------------------------------
286
+ // Safe globals — the shapes authored specs write
287
+ // ---------------------------------------------------------------
288
+ describe('safe global calls', () => {
289
+ // `condition-roots.ts` blesses these twelve names at the task-graph door, and
290
+ // `1efc248ac5` shipped that decision precisely because refusing them rejects specs
291
+ // that run correctly. Receiver and method are both fixed identifiers, so nothing
292
+ // here is reachable by string assembly.
293
+ const ctx = { payload: { count: 4, raw: '7', id: 42 }, output: { delta: -3 } };
294
+ it.each([
295
+ ['Math.abs(output.delta) < 5', true],
296
+ ['Math.max(payload.count, 9) === 9', true],
297
+ ['Number(payload.count) > 3', true],
298
+ ['String(payload.id).length > 0', true],
299
+ ['Boolean(payload.count) === true', true],
300
+ ['Array.isArray(output) === false', true],
301
+ ['Object.keys(payload).length > 0', true],
302
+ ["JSON.stringify(payload) !== ''", true],
303
+ ['Date.now() > 0', true],
304
+ ['parseInt(payload.raw) > 0', true],
305
+ ['parseFloat(payload.raw) === 7', true],
306
+ ['isNaN(payload.count) === false', true],
307
+ ['isFinite(payload.count) === true', true],
308
+ ['Number.isInteger(payload.count)', true],
309
+ ])('should evaluate %s', (expression, expected) => {
310
+ const r = evaluator.evaluate(expression, ctx);
311
+ expect(r.error).toBeUndefined();
312
+ expect(r.value).toBe(expected);
313
+ });
314
+ it('should reject a namespace method that is not on the list', () => {
315
+ // Mutators stay out even on an allowed namespace.
316
+ const r = evaluator.evaluate('Object.assign(payload) !== null', ctx);
317
+ expect(r.success).toBe(false);
318
+ expect(r.error).toContain('forbidden construct');
319
+ });
320
+ it('should reject a global that is not on the list', () => {
321
+ expect(evaluator.evaluate('Symbol("x") !== null', {}).success).toBe(false);
322
+ expect(evaluator.evaluate('fetch("/x") !== null', {}).success).toBe(false);
323
+ });
324
+ });
325
+ // ---------------------------------------------------------------
326
+ // Optional chaining
327
+ // ---------------------------------------------------------------
328
+ describe('optional chaining', () => {
329
+ it('should read through a present chain', () => {
330
+ const r = evaluator.evaluate("payload?.customer?.tier == 'premium'", {
331
+ payload: { customer: { tier: 'premium' } },
332
+ });
333
+ expect(r.value).toBe(true);
334
+ });
335
+ it('should short-circuit on an absent link rather than throwing', () => {
336
+ const r = evaluator.evaluate("payload?.customer?.tier == 'premium'", { payload: {} });
337
+ expect(r.success).toBe(true);
338
+ expect(r.value).toBe(false);
339
+ });
340
+ it('should apply the same rules to an optional index and an optional call', () => {
341
+ expect(evaluator.evaluate('items?.[0]?.price > 100', { items: [{ price: 150 }] }).value).toBe(true);
342
+ expect(evaluator.evaluate("name?.toLowerCase() == 'ada'", { name: 'Ada' }).value).toBe(true);
343
+ });
344
+ });
345
+ // ---------------------------------------------------------------
346
+ // Default exported instance
347
+ // ---------------------------------------------------------------
348
+ describe('defaultExpressionEvaluator', () => {
349
+ it('should be a usable SafeExpressionEvaluator instance', () => {
350
+ expect(defaultExpressionEvaluator).toBeInstanceOf(SafeExpressionEvaluator);
351
+ expect(defaultExpressionEvaluator.evaluate('1 == 1', {}).value).toBe(true);
352
+ });
353
+ });
354
+ });
355
+ //# sourceMappingURL=SafeExpressionEvaluator.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SafeExpressionEvaluator.test.js","sourceRoot":"","sources":["../../src/__tests__/SafeExpressionEvaluator.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=util.jsonRepair.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.jsonRepair.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/util.jsonRepair.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { CleanJSON, RepairJSONEscaping } from '../util.js';
3
+ /**
4
+ * A response shaped like the ones that motivated this work: a JSON envelope whose string value
5
+ * holds fenced markdown, with the quotes inside the fence left unescaped. Everything else in the
6
+ * document — including the newlines — is escaped correctly, which is what makes the defect so
7
+ * narrow and so recoverable.
8
+ */
9
+ const mermaidLabels = [
10
+ 'vwMembers ||--o{ vwInvoices : "has invoices"',
11
+ 'vwInvoices ||--o{ vwLineItems : "contains items"',
12
+ ].join('\\n ');
13
+ const brokenEnvelope = '{"title":"Report","functionalRequirements":"## Data\\n\\n```mermaid\\nerDiagram\\n ' +
14
+ mermaidLabels +
15
+ '\\n```\\n\\nDone."}';
16
+ describe('CleanJSON — interior markdown fences', () => {
17
+ it('does not extract a fence that lives inside a string value', () => {
18
+ // The whole point: a ```mermaid fence inside a string value belongs to the value, not to
19
+ // the response. Extracting it silently discarded the entire document.
20
+ let thrown = null;
21
+ try {
22
+ CleanJSON(brokenEnvelope);
23
+ }
24
+ catch (error) {
25
+ thrown = error;
26
+ }
27
+ expect(thrown).not.toBeNull();
28
+ // The real defect must surface — not a phantom error about the fence contents.
29
+ expect(thrown.message).not.toContain('mermaid');
30
+ expect(thrown.message).toMatch(/position \d+/);
31
+ });
32
+ it('exposes the untouched top-level parse error as `cause`', () => {
33
+ let thrown = null;
34
+ try {
35
+ CleanJSON(brokenEnvelope);
36
+ }
37
+ catch (error) {
38
+ thrown = error;
39
+ }
40
+ const cause = thrown.cause;
41
+ expect(cause).toBeInstanceOf(Error);
42
+ expect(cause.message).toMatch(/position \d+/);
43
+ });
44
+ it('still extracts JSON from a genuinely fence-wrapped response', () => {
45
+ const result = CleanJSON('```json\n{"extracted": true}\n```');
46
+ expect(JSON.parse(result)).toEqual({ extracted: true });
47
+ });
48
+ it('still extracts JSON buried in surrounding prose', () => {
49
+ const result = CleanJSON('Some text ```json\n{"extracted": true}\n``` more text');
50
+ expect(JSON.parse(result)).toEqual({ extracted: true });
51
+ });
52
+ it('leaves a valid document containing a fence completely alone', () => {
53
+ const valid = JSON.stringify({ text: '```mermaid\nerDiagram\n A ||--o{ B : label\n```' });
54
+ const result = CleanJSON(valid);
55
+ expect(JSON.parse(result)).toEqual(JSON.parse(valid));
56
+ });
57
+ });
58
+ describe('RepairJSONEscaping', () => {
59
+ it('escapes unescaped quotes inside a string value and preserves the content', () => {
60
+ const result = RepairJSONEscaping(brokenEnvelope);
61
+ expect(result.repaired).toBe(true);
62
+ // Two quoted labels, two quotes each.
63
+ expect(result.repairedOffsets).toHaveLength(4);
64
+ expect(result.value.title).toBe('Report');
65
+ // The diagram survives verbatim — repair adds escapes, it never rewrites content.
66
+ expect(result.value.functionalRequirements).toContain('vwMembers ||--o{ vwInvoices : "has invoices"');
67
+ expect(result.value.functionalRequirements).toContain('vwInvoices ||--o{ vwLineItems : "contains items"');
68
+ });
69
+ it('escapes raw control characters inside a string value', () => {
70
+ const result = RepairJSONEscaping('{"a":"line one\nline two"}');
71
+ expect(result.repaired).toBe(true);
72
+ expect(result.value.a).toBe('line one\nline two');
73
+ });
74
+ it('reports repaired: false for already-valid JSON, leaving it untouched', () => {
75
+ // Valid input needs no repair; `repaired` marks whether anything was changed.
76
+ const result = RepairJSONEscaping('{"a":1}');
77
+ expect(result.repaired).toBe(false);
78
+ expect(result.repairedOffsets).toHaveLength(0);
79
+ expect(result.value).toEqual({ a: 1 });
80
+ });
81
+ it('gives up rather than guessing on input that is not JSON at all', () => {
82
+ const result = RepairJSONEscaping('I am executing the action now.');
83
+ expect(result.repaired).toBe(false);
84
+ expect(result.value).toBeUndefined();
85
+ expect(result.reason).toBeTruthy();
86
+ });
87
+ it('gives up on truncated output instead of fabricating structure', () => {
88
+ // A response cut off mid-string cannot be recovered by escaping, and must not be
89
+ // "fixed" into something that parses but misrepresents what the model said.
90
+ const result = RepairJSONEscaping('{"a":"unterminated');
91
+ expect(result.repaired).toBe(false);
92
+ expect(result.value).toBeUndefined();
93
+ });
94
+ it('respects the repair ceiling', () => {
95
+ const result = RepairJSONEscaping(brokenEnvelope, 1);
96
+ expect(result.repaired).toBe(false);
97
+ expect(result.reason).toContain('after 1 repairs');
98
+ });
99
+ it('does not treat an already-escaped quote as needing repair', () => {
100
+ // `\\"` is a literal backslash followed by a real string terminator — the quote is NOT
101
+ // escaped. Miscounting the backslash run would corrupt the document.
102
+ const valid = JSON.stringify({ path: 'C:\\' });
103
+ const result = RepairJSONEscaping(valid);
104
+ expect(result.repaired).toBe(false);
105
+ expect(result.value).toEqual({ path: 'C:\\' });
106
+ });
107
+ it('handles null and empty input', () => {
108
+ expect(RepairJSONEscaping(null).repaired).toBe(false);
109
+ expect(RepairJSONEscaping('').repaired).toBe(false);
110
+ });
111
+ });
112
+ //# sourceMappingURL=util.jsonRepair.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.jsonRepair.test.js","sourceRoot":"","sources":["../../src/__tests__/util.jsonRepair.test.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi } from 'vitest';
2
- import { GetGlobalObjectStore, CleanJSON, SafeJSONParse, CleanAndParseJSON, CopyScalarsAndArrays, convertCamelCaseToHaveSpaces, createDisplayName, generatePluralName, getIrregularPlural, stripWhitespace, uuidv4, stripTrailingChars, replaceAllSpaces, IsOnlyTimezoneShift, EscapeHTML, HighlightSearchMatches, } from '../util.js';
2
+ import { GetGlobalObjectStore, CleanJSON, SafeJSONParse, CleanAndParseJSON, CopyScalarsAndArrays, convertCamelCaseToHaveSpaces, createDisplayName, generatePluralName, getIrregularPlural, stripWhitespace, uuidv4, stripTrailingChars, replaceAllSpaces, IsOnlyTimezoneShift, EscapeHTML, EscapeSQLString, HighlightSearchMatches, } from '../util.js';
3
3
  describe('GetGlobalObjectStore', () => {
4
4
  it('should return a non-null object in Node environment', () => {
5
5
  const store = GetGlobalObjectStore();
@@ -561,4 +561,22 @@ describe('HighlightSearchMatches', () => {
561
561
  expect(HighlightSearchMatches('<i>x</i>', ' ')).toBe('&lt;i&gt;x&lt;/i&gt;');
562
562
  });
563
563
  });
564
+ describe('EscapeSQLString', () => {
565
+ it('escapes single quotes by doubling them', () => {
566
+ expect(EscapeSQLString("O'Brien")).toBe("O''Brien");
567
+ expect(EscapeSQLString("user's 'name'")).toBe("user''s ''name''");
568
+ });
569
+ it('strips null bytes to prevent injection', () => {
570
+ expect(EscapeSQLString("test\0injection")).toBe("testinjection");
571
+ expect(EscapeSQLString("user\0' OR '1'='1")).toBe("user'' OR ''1''=''1");
572
+ });
573
+ it('handles null and undefined safely', () => {
574
+ expect(EscapeSQLString(null)).toBe('');
575
+ expect(EscapeSQLString(undefined)).toBe('');
576
+ });
577
+ it('handles empty string and clean strings', () => {
578
+ expect(EscapeSQLString('')).toBe('');
579
+ expect(EscapeSQLString('plain.email@example.com')).toBe('plain.email@example.com');
580
+ });
581
+ });
564
582
  //# sourceMappingURL=util.test.js.map