zero-query 0.9.9 → 1.0.1

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 (99) hide show
  1. package/README.md +34 -33
  2. package/cli/args.js +1 -1
  3. package/cli/commands/build.js +2 -2
  4. package/cli/commands/bundle.js +21 -18
  5. package/cli/commands/create.js +9 -2
  6. package/cli/commands/dev/devtools/index.js +1 -1
  7. package/cli/commands/dev/devtools/js/core.js +14 -14
  8. package/cli/commands/dev/devtools/js/elements.js +4 -4
  9. package/cli/commands/dev/devtools/js/stats.js +1 -1
  10. package/cli/commands/dev/devtools/styles.css +2 -2
  11. package/cli/commands/dev/index.js +2 -2
  12. package/cli/commands/dev/logger.js +1 -1
  13. package/cli/commands/dev/overlay.js +21 -14
  14. package/cli/commands/dev/server.js +5 -5
  15. package/cli/commands/dev/validator.js +7 -7
  16. package/cli/commands/dev/watcher.js +6 -6
  17. package/cli/help.js +3 -3
  18. package/cli/index.js +1 -1
  19. package/cli/scaffold/default/app/app.js +17 -18
  20. package/cli/scaffold/default/app/components/about.js +9 -9
  21. package/cli/scaffold/default/app/components/api-demo.js +6 -6
  22. package/cli/scaffold/default/app/components/contact-card.js +4 -4
  23. package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
  24. package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
  25. package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
  26. package/cli/scaffold/default/app/components/counter.js +8 -8
  27. package/cli/scaffold/default/app/components/home.js +13 -13
  28. package/cli/scaffold/default/app/components/not-found.js +1 -1
  29. package/cli/scaffold/default/app/components/playground/playground.css +1 -1
  30. package/cli/scaffold/default/app/components/playground/playground.html +11 -11
  31. package/cli/scaffold/default/app/components/playground/playground.js +11 -11
  32. package/cli/scaffold/default/app/components/todos.js +8 -8
  33. package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
  34. package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
  35. package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
  36. package/cli/scaffold/default/app/routes.js +1 -1
  37. package/cli/scaffold/default/app/store.js +1 -1
  38. package/cli/scaffold/default/global.css +2 -2
  39. package/cli/scaffold/default/index.html +2 -2
  40. package/cli/scaffold/minimal/app/app.js +6 -7
  41. package/cli/scaffold/minimal/app/components/about.js +5 -5
  42. package/cli/scaffold/minimal/app/components/counter.js +6 -6
  43. package/cli/scaffold/minimal/app/components/home.js +8 -8
  44. package/cli/scaffold/minimal/app/components/not-found.js +1 -1
  45. package/cli/scaffold/minimal/app/routes.js +1 -1
  46. package/cli/scaffold/minimal/app/store.js +1 -1
  47. package/cli/scaffold/minimal/global.css +2 -2
  48. package/cli/scaffold/minimal/index.html +1 -1
  49. package/cli/scaffold/ssr/app/app.js +1 -2
  50. package/cli/scaffold/ssr/app/components/about.js +5 -5
  51. package/cli/scaffold/ssr/app/components/home.js +2 -2
  52. package/cli/scaffold/ssr/app/components/not-found.js +2 -2
  53. package/cli/scaffold/ssr/app/routes.js +1 -1
  54. package/cli/scaffold/ssr/global.css +3 -4
  55. package/cli/scaffold/ssr/index.html +2 -2
  56. package/cli/scaffold/ssr/server/index.js +26 -25
  57. package/cli/utils.js +6 -6
  58. package/dist/zquery.dist.zip +0 -0
  59. package/dist/zquery.js +508 -227
  60. package/dist/zquery.min.js +2 -2
  61. package/index.d.ts +16 -13
  62. package/index.js +7 -5
  63. package/package.json +3 -3
  64. package/src/component.js +64 -63
  65. package/src/core.js +15 -15
  66. package/src/diff.js +38 -38
  67. package/src/errors.js +17 -17
  68. package/src/expression.js +15 -17
  69. package/src/http.js +4 -4
  70. package/src/reactive.js +75 -9
  71. package/src/router.js +104 -24
  72. package/src/ssr.js +28 -28
  73. package/src/store.js +103 -21
  74. package/src/utils.js +64 -12
  75. package/tests/audit.test.js +143 -15
  76. package/tests/cli.test.js +20 -20
  77. package/tests/component.test.js +121 -121
  78. package/tests/core.test.js +56 -56
  79. package/tests/diff.test.js +42 -42
  80. package/tests/errors.test.js +5 -5
  81. package/tests/expression.test.js +58 -53
  82. package/tests/http.test.js +20 -20
  83. package/tests/reactive.test.js +185 -24
  84. package/tests/router.test.js +501 -74
  85. package/tests/ssr.test.js +15 -13
  86. package/tests/store.test.js +264 -23
  87. package/tests/test-minifier.js +153 -0
  88. package/tests/test-ssr.js +27 -0
  89. package/tests/utils.test.js +163 -26
  90. package/types/collection.d.ts +2 -2
  91. package/types/component.d.ts +5 -5
  92. package/types/errors.d.ts +3 -3
  93. package/types/http.d.ts +3 -3
  94. package/types/misc.d.ts +9 -9
  95. package/types/reactive.d.ts +25 -3
  96. package/types/router.d.ts +10 -6
  97. package/types/ssr.d.ts +2 -2
  98. package/types/store.d.ts +40 -5
  99. package/types/utils.d.ts +1 -1
@@ -12,7 +12,7 @@ const eval_ = (expr, ...scopes) => safeEval(expr, scopes.length ? scopes : [{}])
12
12
  // Literals
13
13
  // ---------------------------------------------------------------------------
14
14
 
15
- describe('expression parser literals', () => {
15
+ describe('expression parser - literals', () => {
16
16
  it('numbers', () => {
17
17
  expect(eval_('42')).toBe(42);
18
18
  expect(eval_('3.14')).toBe(3.14);
@@ -44,7 +44,7 @@ describe('expression parser — literals', () => {
44
44
  // Arithmetic
45
45
  // ---------------------------------------------------------------------------
46
46
 
47
- describe('expression parser arithmetic', () => {
47
+ describe('expression parser - arithmetic', () => {
48
48
  it('basic operations', () => {
49
49
  expect(eval_('2 + 3')).toBe(5);
50
50
  expect(eval_('10 - 4')).toBe(6);
@@ -69,7 +69,7 @@ describe('expression parser — arithmetic', () => {
69
69
  // Comparison & logical
70
70
  // ---------------------------------------------------------------------------
71
71
 
72
- describe('expression parser comparison', () => {
72
+ describe('expression parser - comparison', () => {
73
73
  it('equality', () => {
74
74
  expect(eval_('1 === 1')).toBe(true);
75
75
  expect(eval_('1 !== 2')).toBe(true);
@@ -86,7 +86,7 @@ describe('expression parser — comparison', () => {
86
86
  });
87
87
 
88
88
 
89
- describe('expression parser logical', () => {
89
+ describe('expression parser - logical', () => {
90
90
  it('&& and ||', () => {
91
91
  expect(eval_('true && false')).toBe(false);
92
92
  expect(eval_('true || false')).toBe(true);
@@ -113,7 +113,7 @@ describe('expression parser — logical', () => {
113
113
  // Ternary
114
114
  // ---------------------------------------------------------------------------
115
115
 
116
- describe('expression parser ternary', () => {
116
+ describe('expression parser - ternary', () => {
117
117
  it('evaluates truthy branch', () => {
118
118
  expect(eval_("true ? 'yes' : 'no'")).toBe('yes');
119
119
  });
@@ -132,7 +132,7 @@ describe('expression parser — ternary', () => {
132
132
  // Property access & scope
133
133
  // ---------------------------------------------------------------------------
134
134
 
135
- describe('expression parser property access', () => {
135
+ describe('expression parser - property access', () => {
136
136
  it('reads scope variables', () => {
137
137
  expect(eval_('x', { x: 42 })).toBe(42);
138
138
  expect(eval_('name', { name: 'Tony' })).toBe('Tony');
@@ -163,7 +163,7 @@ describe('expression parser — property access', () => {
163
163
  // Method calls
164
164
  // ---------------------------------------------------------------------------
165
165
 
166
- describe('expression parser method calls', () => {
166
+ describe('expression parser - method calls', () => {
167
167
  it('string methods', () => {
168
168
  expect(eval_("'hello'.toUpperCase()")).toBe('HELLO');
169
169
  expect(eval_("'hello world'.split(' ')")).toEqual(['hello', 'world']);
@@ -187,7 +187,7 @@ describe('expression parser — method calls', () => {
187
187
  // Built-in globals
188
188
  // ---------------------------------------------------------------------------
189
189
 
190
- describe('expression parser built-in globals', () => {
190
+ describe('expression parser - built-in globals', () => {
191
191
  it('Math', () => {
192
192
  expect(eval_('Math.PI')).toBeCloseTo(3.14159);
193
193
  expect(eval_('Math.max(1, 5, 3)')).toBe(5);
@@ -214,7 +214,7 @@ describe('expression parser — built-in globals', () => {
214
214
  // Template literals
215
215
  // ---------------------------------------------------------------------------
216
216
 
217
- describe('expression parser template literals', () => {
217
+ describe('expression parser - template literals', () => {
218
218
  it('simple interpolation', () => {
219
219
  expect(eval_('`Hello ${name}`', { name: 'Tony' })).toBe('Hello Tony');
220
220
  });
@@ -233,7 +233,7 @@ describe('expression parser — template literals', () => {
233
233
  // Array & object literals
234
234
  // ---------------------------------------------------------------------------
235
235
 
236
- describe('expression parser array/object literals', () => {
236
+ describe('expression parser - array/object literals', () => {
237
237
  it('array literal', () => {
238
238
  expect(eval_('[1, 2, 3]')).toEqual([1, 2, 3]);
239
239
  expect(eval_('[]')).toEqual([]);
@@ -253,7 +253,7 @@ describe('expression parser — array/object literals', () => {
253
253
  // Arrow functions
254
254
  // ---------------------------------------------------------------------------
255
255
 
256
- describe('expression parser arrow functions', () => {
256
+ describe('expression parser - arrow functions', () => {
257
257
  it('single-param arrow', () => {
258
258
  const fn = eval_('x => x * 2');
259
259
  expect(fn(3)).toBe(6);
@@ -280,7 +280,7 @@ describe('expression parser — arrow functions', () => {
280
280
  // typeof
281
281
  // ---------------------------------------------------------------------------
282
282
 
283
- describe('expression parser typeof', () => {
283
+ describe('expression parser - typeof', () => {
284
284
  it('typeof string', () => {
285
285
  expect(eval_("typeof 'hello'")).toBe('string');
286
286
  });
@@ -299,7 +299,7 @@ describe('expression parser — typeof', () => {
299
299
  // Safety / security
300
300
  // ---------------------------------------------------------------------------
301
301
 
302
- describe('expression parser safety', () => {
302
+ describe('expression parser - safety', () => {
303
303
  it('blocks constructor access', () => {
304
304
  expect(eval_("''.constructor")).toBe(undefined);
305
305
  });
@@ -323,7 +323,7 @@ describe('expression parser — safety', () => {
323
323
  // Multi-scope resolution
324
324
  // ---------------------------------------------------------------------------
325
325
 
326
- describe('expression parser multi-scope', () => {
326
+ describe('expression parser - multi-scope', () => {
327
327
  it('checks scope layers in order', () => {
328
328
  expect(safeEval('x', [{ x: 'first' }, { x: 'second' }])).toBe('first');
329
329
  });
@@ -338,7 +338,7 @@ describe('expression parser — multi-scope', () => {
338
338
  // in operator
339
339
  // ---------------------------------------------------------------------------
340
340
 
341
- describe('expression parser in operator', () => {
341
+ describe('expression parser - in operator', () => {
342
342
  it('checks property existence', () => {
343
343
  expect(eval_("'x' in obj", { obj: { x: 1 } })).toBe(true);
344
344
  expect(eval_("'y' in obj", { obj: { x: 1 } })).toBe(false);
@@ -350,7 +350,7 @@ describe('expression parser — in operator', () => {
350
350
  // instanceof operator
351
351
  // ---------------------------------------------------------------------------
352
352
 
353
- describe('expression parser instanceof', () => {
353
+ describe('expression parser - instanceof', () => {
354
354
  it('checks instanceOf', () => {
355
355
  expect(eval_('arr instanceof Array', { arr: [1, 2] })).toBe(true);
356
356
  expect(eval_('obj instanceof Array', { obj: {} })).toBe(false);
@@ -362,7 +362,7 @@ describe('expression parser — instanceof', () => {
362
362
  // Nested ternary
363
363
  // ---------------------------------------------------------------------------
364
364
 
365
- describe('expression parser nested ternary', () => {
365
+ describe('expression parser - nested ternary', () => {
366
366
  it('evaluates simple ternary correctly', () => {
367
367
  expect(eval_("x > 10 ? 'big' : 'small'", { x: 12 })).toBe('big');
368
368
  expect(eval_("x > 10 ? 'big' : 'small'", { x: 2 })).toBe('small');
@@ -374,7 +374,7 @@ describe('expression parser — nested ternary', () => {
374
374
  // Chained method calls
375
375
  // ---------------------------------------------------------------------------
376
376
 
377
- describe('expression parser chained calls', () => {
377
+ describe('expression parser - chained calls', () => {
378
378
  it('chains array methods', () => {
379
379
  expect(eval_('items.filter(x => x > 1).map(x => x * 2)', { items: [1, 2, 3] })).toEqual([4, 6]);
380
380
  });
@@ -389,8 +389,8 @@ describe('expression parser — chained calls', () => {
389
389
  // Spread operator
390
390
  // ---------------------------------------------------------------------------
391
391
 
392
- describe('expression parser spread / rest', () => {
393
- it('spread is not supported returns gracefully', () => {
392
+ describe('expression parser - spread / rest', () => {
393
+ it('spread is not supported - returns gracefully', () => {
394
394
  // The parser does not support spread syntax; verify it doesn't throw
395
395
  const result = eval_('[...items, 4]', { items: [1, 2, 3] });
396
396
  expect(result).toBeDefined();
@@ -402,7 +402,7 @@ describe('expression parser — spread / rest', () => {
402
402
  // Destructuring assignment in arrow body
403
403
  // ---------------------------------------------------------------------------
404
404
 
405
- describe('expression parser complex arrow', () => {
405
+ describe('expression parser - complex arrow', () => {
406
406
  it('arrow as callback in array method', () => {
407
407
  const items = [{ n: 'a' }, { n: 'b' }];
408
408
  expect(eval_('items.map(x => x.n)', { items })).toEqual(['a', 'b']);
@@ -420,8 +420,8 @@ describe('expression parser — complex arrow', () => {
420
420
  // Bitwise operators
421
421
  // ---------------------------------------------------------------------------
422
422
 
423
- describe('expression parser bitwise', () => {
424
- it('bitwise operators are not supported does not throw', () => {
423
+ describe('expression parser - bitwise', () => {
424
+ it('bitwise operators are not supported - does not throw', () => {
425
425
  // The expression parser does not implement bitwise operators
426
426
  // Verify graceful fallback rather than crashes
427
427
  expect(() => eval_('5 | 3')).not.toThrow();
@@ -435,8 +435,8 @@ describe('expression parser — bitwise', () => {
435
435
  // Comma expressions
436
436
  // ---------------------------------------------------------------------------
437
437
 
438
- describe('expression parser comma', () => {
439
- it('comma expressions are not supported does not throw', () => {
438
+ describe('expression parser - comma', () => {
439
+ it('comma expressions are not supported - does not throw', () => {
440
440
  // The parser does not support comma expressions
441
441
  expect(() => eval_('(1, 2, 3)')).not.toThrow();
442
442
  });
@@ -447,7 +447,7 @@ describe('expression parser — comma', () => {
447
447
  // Edge cases
448
448
  // ---------------------------------------------------------------------------
449
449
 
450
- describe('expression parser edge cases', () => {
450
+ describe('expression parser - edge cases', () => {
451
451
  it('handles very long dot chains', () => {
452
452
  const data = { a: { b: { c: { d: { e: 42 } } } } };
453
453
  expect(eval_('a.b.c.d.e', data)).toBe(42);
@@ -470,7 +470,7 @@ describe('expression parser — edge cases', () => {
470
470
  expect(eval_('-1 + -2')).toBe(-3);
471
471
  });
472
472
 
473
- it('exponentiation ** is not supported does not throw', () => {
473
+ it('exponentiation ** is not supported - does not throw', () => {
474
474
  // The parser does not implement ** operator
475
475
  expect(() => eval_('2 ** 3')).not.toThrow();
476
476
  });
@@ -486,7 +486,7 @@ describe('expression parser — edge cases', () => {
486
486
  // Optional chaining edge cases
487
487
  // ---------------------------------------------------------------------------
488
488
 
489
- describe('expression parser optional chaining edge cases', () => {
489
+ describe('expression parser - optional chaining edge cases', () => {
490
490
  it('returns undefined for null base with ?.', () => {
491
491
  expect(eval_('a?.b', { a: null })).toBeUndefined();
492
492
  });
@@ -519,7 +519,7 @@ describe('expression parser — optional chaining edge cases', () => {
519
519
  // Complex property access
520
520
  // ---------------------------------------------------------------------------
521
521
 
522
- describe('expression parser complex property access', () => {
522
+ describe('expression parser - complex property access', () => {
523
523
  it('accesses deeply nested objects', () => {
524
524
  const scope = { a: { b: { c: { d: { e: 'deep' } } } } };
525
525
  expect(eval_('a.b.c.d.e', scope)).toBe('deep');
@@ -548,7 +548,7 @@ describe('expression parser — complex property access', () => {
548
548
  // Arrow function edge cases
549
549
  // ---------------------------------------------------------------------------
550
550
 
551
- describe('expression parser arrow function edge cases', () => {
551
+ describe('expression parser - arrow function edge cases', () => {
552
552
  it('no-param arrow function', () => {
553
553
  const fn = eval_('() => 42');
554
554
  expect(fn()).toBe(42);
@@ -581,7 +581,7 @@ describe('expression parser — arrow function edge cases', () => {
581
581
  // Template literal edge cases
582
582
  // ---------------------------------------------------------------------------
583
583
 
584
- describe('expression parser template literal edge cases', () => {
584
+ describe('expression parser - template literal edge cases', () => {
585
585
  it('template with no interpolation', () => {
586
586
  expect(eval_('`hello world`')).toBe('hello world');
587
587
  });
@@ -612,7 +612,7 @@ describe('expression parser — template literal edge cases', () => {
612
612
  // Nullish coalescing edge cases
613
613
  // ---------------------------------------------------------------------------
614
614
 
615
- describe('expression parser nullish coalescing edge cases', () => {
615
+ describe('expression parser - nullish coalescing edge cases', () => {
616
616
  it('returns left side for 0', () => {
617
617
  expect(eval_('x ?? 10', { x: 0 })).toBe(0);
618
618
  });
@@ -643,7 +643,7 @@ describe('expression parser — nullish coalescing edge cases', () => {
643
643
  // Typeof edge cases
644
644
  // ---------------------------------------------------------------------------
645
645
 
646
- describe('expression parser typeof edge cases', () => {
646
+ describe('expression parser - typeof edge cases', () => {
647
647
  it('typeof undefined variable returns "undefined"', () => {
648
648
  expect(eval_('typeof x')).toBe('undefined');
649
649
  });
@@ -678,7 +678,7 @@ describe('expression parser — typeof edge cases', () => {
678
678
  // Array/Object literal edge cases
679
679
  // ---------------------------------------------------------------------------
680
680
 
681
- describe('expression parser array/object literal edge cases', () => {
681
+ describe('expression parser - array/object literal edge cases', () => {
682
682
  it('array with trailing expression', () => {
683
683
  expect(eval_('[1, 2, 3].length')).toBe(3);
684
684
  });
@@ -709,7 +709,7 @@ describe('expression parser — array/object literal edge cases', () => {
709
709
  // Method call edge cases
710
710
  // ---------------------------------------------------------------------------
711
711
 
712
- describe('expression parser method call edge cases', () => {
712
+ describe('expression parser - method call edge cases', () => {
713
713
  it('chained string methods', () => {
714
714
  expect(eval_('"Hello World".toLowerCase().split(" ")')).toEqual(['hello', 'world']);
715
715
  });
@@ -758,7 +758,7 @@ describe('expression parser — method call edge cases', () => {
758
758
  // Multi-scope resolution
759
759
  // ---------------------------------------------------------------------------
760
760
 
761
- describe('expression parser multi-scope resolution', () => {
761
+ describe('expression parser - multi-scope resolution', () => {
762
762
  it('resolves from first scope when available', () => {
763
763
  expect(eval_('x', { x: 1 }, { x: 2 })).toBe(1);
764
764
  });
@@ -777,7 +777,7 @@ describe('expression parser — multi-scope resolution', () => {
777
777
  // Security: blocked access
778
778
  // ---------------------------------------------------------------------------
779
779
 
780
- describe('expression parser security', () => {
780
+ describe('expression parser - security', () => {
781
781
  it('blocks constructor access', () => {
782
782
  expect(() => eval_('"".constructor')).not.toThrow();
783
783
  });
@@ -790,11 +790,11 @@ describe('expression parser — security', () => {
790
790
  expect(() => eval_('++++')).not.toThrow();
791
791
  });
792
792
 
793
- it('global access is sandboxed no window', () => {
793
+ it('global access is sandboxed - no window', () => {
794
794
  expect(eval_('typeof window')).toBe('undefined');
795
795
  });
796
796
 
797
- it('global access is sandboxed no document', () => {
797
+ it('global access is sandboxed - no document', () => {
798
798
  expect(eval_('typeof document')).toBe('undefined');
799
799
  });
800
800
  });
@@ -804,7 +804,7 @@ describe('expression parser — security', () => {
804
804
  // Comparison edge cases
805
805
  // ---------------------------------------------------------------------------
806
806
 
807
- describe('expression parser comparison edge cases', () => {
807
+ describe('expression parser - comparison edge cases', () => {
808
808
  it('strict equality with type mismatch', () => {
809
809
  expect(eval_('1 === "1"')).toBe(false);
810
810
  });
@@ -833,7 +833,7 @@ describe('expression parser — comparison edge cases', () => {
833
833
  // Grouping / precedence
834
834
  // ---------------------------------------------------------------------------
835
835
 
836
- describe('expression parser grouping and precedence', () => {
836
+ describe('expression parser - grouping and precedence', () => {
837
837
  it('parentheses override precedence', () => {
838
838
  expect(eval_('(2 + 3) * 4')).toBe(20);
839
839
  });
@@ -857,10 +857,10 @@ describe('expression parser — grouping and precedence', () => {
857
857
 
858
858
 
859
859
  // ===========================================================================
860
- // new keyword safe constructors
860
+ // new keyword - safe constructors
861
861
  // ===========================================================================
862
862
 
863
- describe('safeEval new keyword', () => {
863
+ describe('safeEval - new keyword', () => {
864
864
  it('creates new Date (no args)', () => {
865
865
  const result = eval_('new Date');
866
866
  expect(result).toBeInstanceOf(Date);
@@ -871,14 +871,19 @@ describe('safeEval — new keyword', () => {
871
871
  expect(result).toBeInstanceOf(Array);
872
872
  });
873
873
 
874
- it('Map/Set/RegExp in globals new creates instances', () => {
875
- // Map, Set, RegExp are now exposed as globals and whitelisted as safe constructors
874
+ it('Map/Set in globals - new creates instances', () => {
875
+ // Map, Set are exposed as globals and whitelisted as safe constructors
876
876
  expect(eval_('new Map')).toBeInstanceOf(Map);
877
877
  expect(eval_('new Set')).toBeInstanceOf(Set);
878
- expect(eval_('new RegExp')).toBeInstanceOf(RegExp);
879
878
  });
880
879
 
881
- it('new with args parser correctly passes args to constructor', () => {
880
+ it('RegExp blocked - ReDoS prevention', () => {
881
+ // RegExp is no longer exposed or allowed as constructor to prevent ReDoS attacks
882
+ expect(eval_('new RegExp')).toBeUndefined();
883
+ expect(eval_('RegExp')).toBeUndefined();
884
+ });
885
+
886
+ it('new with args - parser correctly passes args to constructor', () => {
882
887
  const result = eval_('new Date(2024, 0, 1)');
883
888
  expect(result).toBeInstanceOf(Date);
884
889
  expect(result.getFullYear()).toBe(2024);
@@ -897,7 +902,7 @@ describe('safeEval — new keyword', () => {
897
902
  // void operator
898
903
  // ===========================================================================
899
904
 
900
- describe('safeEval void operator', () => {
905
+ describe('safeEval - void operator', () => {
901
906
  it('returns undefined', () => {
902
907
  expect(eval_('void 0')).toBeUndefined();
903
908
  });
@@ -912,7 +917,7 @@ describe('safeEval — void operator', () => {
912
917
  // AST cache
913
918
  // ===========================================================================
914
919
 
915
- describe('safeEval AST cache', () => {
920
+ describe('safeEval - AST cache', () => {
916
921
  it('returns same result on repeated evaluation (cache hit)', () => {
917
922
  const r1 = eval_('1 + 2');
918
923
  const r2 = eval_('1 + 2');
@@ -933,7 +938,7 @@ describe('safeEval — AST cache', () => {
933
938
  // Optional call ?.()
934
939
  // ===========================================================================
935
940
 
936
- describe('safeEval optional call ?.()', () => {
941
+ describe('safeEval - optional call ?.()', () => {
937
942
  it('calls function when not null', () => {
938
943
  expect(eval_('fn?.()', { fn: () => 42 })).toBe(42);
939
944
  });
@@ -952,7 +957,7 @@ describe('safeEval — optional call ?.()', () => {
952
957
  // Global builtins
953
958
  // ===========================================================================
954
959
 
955
- describe('safeEval global builtins', () => {
960
+ describe('safeEval - global builtins', () => {
956
961
  it('accesses Date constructor', () => {
957
962
  expect(eval_('Date.now()')).toBeGreaterThan(0);
958
963
  });
@@ -1016,7 +1021,7 @@ describe('safeEval — global builtins', () => {
1016
1021
  // Number methods
1017
1022
  // ===========================================================================
1018
1023
 
1019
- describe('safeEval number methods', () => {
1024
+ describe('safeEval - number methods', () => {
1020
1025
  it('calls toFixed', () => {
1021
1026
  expect(eval_('x.toFixed(2)', { x: 3.14159 })).toBe('3.14');
1022
1027
  });
@@ -1031,7 +1036,7 @@ describe('safeEval — number methods', () => {
1031
1036
  // Empty/edge expressions
1032
1037
  // ===========================================================================
1033
1038
 
1034
- describe('safeEval edge cases', () => {
1039
+ describe('safeEval - edge cases', () => {
1035
1040
  it('returns undefined for empty string', () => {
1036
1041
  expect(eval_('')).toBeUndefined();
1037
1042
  });
@@ -99,7 +99,7 @@ describe('http.post', () => {
99
99
  // Error handling
100
100
  // ---------------------------------------------------------------------------
101
101
 
102
- describe('http error handling', () => {
102
+ describe('http - error handling', () => {
103
103
  it('throws on non-ok response', async () => {
104
104
  mockFetch({ error: 'Not Found' }, false, 404);
105
105
  await expect(http.get('https://api.test.com/missing')).rejects.toThrow('HTTP 404');
@@ -126,7 +126,7 @@ describe('http — error handling', () => {
126
126
  // PUT / PATCH / DELETE
127
127
  // ---------------------------------------------------------------------------
128
128
 
129
- describe('http other methods', () => {
129
+ describe('http - other methods', () => {
130
130
  it('PUT sends correct method', async () => {
131
131
  mockFetch({});
132
132
  await http.put('https://api.test.com/users/1', { name: 'Updated' });
@@ -172,7 +172,7 @@ describe('http.configure', () => {
172
172
  // Text response
173
173
  // ---------------------------------------------------------------------------
174
174
 
175
- describe('http text response', () => {
175
+ describe('http - text response', () => {
176
176
  it('parses text response', async () => {
177
177
  mockFetch('Hello World');
178
178
  const result = await http.get('https://api.test.com/text');
@@ -185,7 +185,7 @@ describe('http — text response', () => {
185
185
  // Interceptors
186
186
  // ---------------------------------------------------------------------------
187
187
 
188
- describe('http interceptors', () => {
188
+ describe('http - interceptors', () => {
189
189
  it('request interceptor via onRequest', async () => {
190
190
  http.configure({ baseURL: '' });
191
191
  http.onRequest((fetchOpts, url) => {
@@ -212,13 +212,13 @@ describe('http — interceptors', () => {
212
212
  // Timeout / abort
213
213
  // ---------------------------------------------------------------------------
214
214
 
215
- describe('http abort signal', () => {
215
+ describe('http - abort signal', () => {
216
216
  it('passes signal through options', async () => {
217
217
  const controller = new AbortController();
218
218
  mockFetch({});
219
219
  await http.get('https://api.test.com/data', null, { signal: controller.signal });
220
220
  const opts = fetchSpy.mock.calls[0][1];
221
- // Signal may be combined via AbortSignal.any verify it responds to user abort
221
+ // Signal may be combined via AbortSignal.any - verify it responds to user abort
222
222
  expect(opts.signal).toBeDefined();
223
223
  expect(opts.signal.aborted).toBe(false);
224
224
  controller.abort();
@@ -231,7 +231,7 @@ describe('http — abort signal', () => {
231
231
  // Blob response
232
232
  // ---------------------------------------------------------------------------
233
233
 
234
- describe('http blob response', () => {
234
+ describe('http - blob response', () => {
235
235
  it('can request blob responses', async () => {
236
236
  mockFetch('binary data');
237
237
  const result = await http.get('https://api.test.com/file', null, { responseType: 'blob' });
@@ -245,7 +245,7 @@ describe('http — blob response', () => {
245
245
  // HEAD and OPTIONS methods
246
246
  // ---------------------------------------------------------------------------
247
247
 
248
- describe('http raw fetch pass-through', () => {
248
+ describe('http - raw fetch pass-through', () => {
249
249
  it('raw() delegates to native fetch', async () => {
250
250
  mockFetch({ ok: true });
251
251
  await http.raw('https://api.test.com/ping', { method: 'HEAD' });
@@ -258,7 +258,7 @@ describe('http — raw fetch pass-through', () => {
258
258
  // Request with custom headers
259
259
  // ---------------------------------------------------------------------------
260
260
 
261
- describe('http custom per-request headers', () => {
261
+ describe('http - custom per-request headers', () => {
262
262
  it('merges per-request headers', async () => {
263
263
  mockFetch({});
264
264
  await http.get('https://api.test.com/data', null, {
@@ -274,7 +274,7 @@ describe('http — custom per-request headers', () => {
274
274
  // Response metadata
275
275
  // ---------------------------------------------------------------------------
276
276
 
277
- describe('http response metadata', () => {
277
+ describe('http - response metadata', () => {
278
278
  it('includes status and ok in result', async () => {
279
279
  mockFetch({ data: 'yes' }, true, 200);
280
280
  const result = await http.get('https://api.test.com/data');
@@ -326,7 +326,7 @@ describe('http.configure', () => {
326
326
  // interceptors
327
327
  // ===========================================================================
328
328
 
329
- describe('http.onRequest extra', () => {
329
+ describe('http.onRequest - extra', () => {
330
330
  it('interceptor modifying URL', async () => {
331
331
  // Note: interceptors from earlier tests may still be registered; this one
332
332
  // only adds a query param so it doesn't break others
@@ -337,16 +337,16 @@ describe('http.onRequest — extra', () => {
337
337
  });
338
338
  });
339
339
 
340
- // Test interceptor blocking in isolation it permanently modifies internal state,
340
+ // Test interceptor blocking in isolation - it permanently modifies internal state,
341
341
  // so we verify behavior without adding a blocking interceptor that breaks later tests
342
- describe('http interceptor blocking concept', () => {
342
+ describe('http - interceptor blocking concept', () => {
343
343
  it('onRequest returning false would throw blocked error', () => {
344
344
  // Just verify the error message format expected by the source code
345
345
  expect(() => { throw new Error('Request blocked by interceptor'); }).toThrow('blocked by interceptor');
346
346
  });
347
347
  });
348
348
 
349
- describe('http.onResponse extra', () => {
349
+ describe('http.onResponse - extra', () => {
350
350
  it('response interceptors are additive', async () => {
351
351
  // onResponse was already called in earlier tests; verify the callback
352
352
  const spy = vi.fn();
@@ -384,7 +384,7 @@ describe('http.delete', () => {
384
384
  // GET with existing query string
385
385
  // ===========================================================================
386
386
 
387
- describe('http.get query params', () => {
387
+ describe('http.get - query params', () => {
388
388
  it('appends params with & when URL already has ?', async () => {
389
389
  mockFetch({ ok: true });
390
390
  await http.get('https://api.test.com/search?q=hello', { page: 2 });
@@ -397,7 +397,7 @@ describe('http.get — query params', () => {
397
397
  // string body
398
398
  // ===========================================================================
399
399
 
400
- describe('http.post string body', () => {
400
+ describe('http.post - string body', () => {
401
401
  it('sends string body as-is', async () => {
402
402
  mockFetch({ ok: true });
403
403
  await http.post('https://api.test.com/raw', 'plain text body');
@@ -411,7 +411,7 @@ describe('http.post — string body', () => {
411
411
  // URL validation
412
412
  // ===========================================================================
413
413
 
414
- describe('http URL validation', () => {
414
+ describe('http - URL validation', () => {
415
415
  it('throws on missing URL', async () => {
416
416
  await expect(http.get(undefined)).rejects.toThrow('URL string');
417
417
  });
@@ -438,7 +438,7 @@ describe('http.createAbort', () => {
438
438
  // timeout / abort
439
439
  // ===========================================================================
440
440
 
441
- describe('http AbortController integration', () => {
441
+ describe('http - AbortController integration', () => {
442
442
  it('createAbort signal can be passed as option', async () => {
443
443
  const controller = http.createAbort();
444
444
  mockFetch({ ok: true });
@@ -490,7 +490,7 @@ describe('http.head', () => {
490
490
  // Interceptor unsubscribe
491
491
  // ===========================================================================
492
492
 
493
- describe('http interceptor unsubscribe', () => {
493
+ describe('http - interceptor unsubscribe', () => {
494
494
  it('onRequest returns an unsubscribe function', async () => {
495
495
  http.clearInterceptors();
496
496
  const spy = vi.fn();
@@ -584,7 +584,7 @@ describe('http.clearInterceptors', () => {
584
584
 
585
585
 
586
586
  // ===========================================================================
587
- // http.all parallel requests
587
+ // http.all - parallel requests
588
588
  // ===========================================================================
589
589
 
590
590
  describe('http.all', () => {