zero-query 0.6.3 → 0.8.6

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 (72) hide show
  1. package/README.md +39 -29
  2. package/cli/commands/build.js +113 -4
  3. package/cli/commands/bundle.js +392 -29
  4. package/cli/commands/create.js +1 -1
  5. package/cli/commands/dev/devtools/index.js +56 -0
  6. package/cli/commands/dev/devtools/js/components.js +49 -0
  7. package/cli/commands/dev/devtools/js/core.js +409 -0
  8. package/cli/commands/dev/devtools/js/elements.js +413 -0
  9. package/cli/commands/dev/devtools/js/network.js +166 -0
  10. package/cli/commands/dev/devtools/js/performance.js +73 -0
  11. package/cli/commands/dev/devtools/js/router.js +105 -0
  12. package/cli/commands/dev/devtools/js/source.js +132 -0
  13. package/cli/commands/dev/devtools/js/stats.js +35 -0
  14. package/cli/commands/dev/devtools/js/tabs.js +79 -0
  15. package/cli/commands/dev/devtools/panel.html +95 -0
  16. package/cli/commands/dev/devtools/styles.css +244 -0
  17. package/cli/commands/dev/index.js +29 -4
  18. package/cli/commands/dev/logger.js +6 -1
  19. package/cli/commands/dev/overlay.js +428 -2
  20. package/cli/commands/dev/server.js +42 -5
  21. package/cli/commands/dev/watcher.js +59 -1
  22. package/cli/help.js +8 -5
  23. package/cli/scaffold/{scripts → app}/app.js +16 -23
  24. package/cli/scaffold/{scripts → app}/components/about.js +4 -4
  25. package/cli/scaffold/{scripts → app}/components/api-demo.js +1 -1
  26. package/cli/scaffold/{scripts → app}/components/contacts/contacts.css +0 -7
  27. package/cli/scaffold/{scripts → app}/components/contacts/contacts.html +3 -3
  28. package/cli/scaffold/app/components/home.js +137 -0
  29. package/cli/scaffold/{scripts → app}/routes.js +1 -1
  30. package/cli/scaffold/{scripts → app}/store.js +6 -6
  31. package/cli/scaffold/assets/.gitkeep +0 -0
  32. package/cli/scaffold/{styles/styles.css → global.css} +4 -2
  33. package/cli/scaffold/index.html +12 -11
  34. package/cli/utils.js +111 -6
  35. package/dist/zquery.dist.zip +0 -0
  36. package/dist/zquery.js +1122 -158
  37. package/dist/zquery.min.js +3 -16
  38. package/index.d.ts +129 -1290
  39. package/index.js +15 -10
  40. package/package.json +7 -6
  41. package/src/component.js +172 -49
  42. package/src/core.js +359 -18
  43. package/src/diff.js +256 -58
  44. package/src/expression.js +33 -3
  45. package/src/reactive.js +37 -5
  46. package/src/router.js +243 -7
  47. package/tests/component.test.js +886 -0
  48. package/tests/core.test.js +977 -0
  49. package/tests/diff.test.js +525 -0
  50. package/tests/errors.test.js +162 -0
  51. package/tests/expression.test.js +482 -0
  52. package/tests/http.test.js +289 -0
  53. package/tests/reactive.test.js +339 -0
  54. package/tests/router.test.js +649 -0
  55. package/tests/store.test.js +379 -0
  56. package/tests/utils.test.js +512 -0
  57. package/types/collection.d.ts +383 -0
  58. package/types/component.d.ts +217 -0
  59. package/types/errors.d.ts +103 -0
  60. package/types/http.d.ts +81 -0
  61. package/types/misc.d.ts +179 -0
  62. package/types/reactive.d.ts +76 -0
  63. package/types/router.d.ts +161 -0
  64. package/types/ssr.d.ts +49 -0
  65. package/types/store.d.ts +107 -0
  66. package/types/utils.d.ts +142 -0
  67. package/cli/commands/dev.old.js +0 -520
  68. package/cli/scaffold/scripts/components/home.js +0 -137
  69. /package/cli/scaffold/{scripts → app}/components/contacts/contacts.js +0 -0
  70. /package/cli/scaffold/{scripts → app}/components/counter.js +0 -0
  71. /package/cli/scaffold/{scripts → app}/components/not-found.js +0 -0
  72. /package/cli/scaffold/{scripts → app}/components/todos.js +0 -0
@@ -0,0 +1,482 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { safeEval } from '../src/expression.js';
3
+
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Helpers
7
+ // ---------------------------------------------------------------------------
8
+ const eval_ = (expr, ...scopes) => safeEval(expr, scopes.length ? scopes : [{}]);
9
+
10
+
11
+ // ---------------------------------------------------------------------------
12
+ // Literals
13
+ // ---------------------------------------------------------------------------
14
+
15
+ describe('expression parser — literals', () => {
16
+ it('numbers', () => {
17
+ expect(eval_('42')).toBe(42);
18
+ expect(eval_('3.14')).toBe(3.14);
19
+ expect(eval_('0xFF')).toBe(255);
20
+ expect(eval_('1e3')).toBe(1000);
21
+ });
22
+
23
+ it('strings', () => {
24
+ expect(eval_("'hello'")).toBe('hello');
25
+ expect(eval_('"world"')).toBe('world');
26
+ expect(eval_("'it\\'s'")).toBe("it's");
27
+ });
28
+
29
+ it('booleans and null/undefined', () => {
30
+ expect(eval_('true')).toBe(true);
31
+ expect(eval_('false')).toBe(false);
32
+ expect(eval_('null')).toBe(null);
33
+ expect(eval_('undefined')).toBe(undefined);
34
+ });
35
+
36
+ it('empty expression returns undefined', () => {
37
+ expect(eval_('')).toBe(undefined);
38
+ expect(eval_(' ')).toBe(undefined);
39
+ });
40
+ });
41
+
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Arithmetic
45
+ // ---------------------------------------------------------------------------
46
+
47
+ describe('expression parser — arithmetic', () => {
48
+ it('basic operations', () => {
49
+ expect(eval_('2 + 3')).toBe(5);
50
+ expect(eval_('10 - 4')).toBe(6);
51
+ expect(eval_('3 * 7')).toBe(21);
52
+ expect(eval_('15 / 3')).toBe(5);
53
+ expect(eval_('10 % 3')).toBe(1);
54
+ });
55
+
56
+ it('operator precedence', () => {
57
+ expect(eval_('2 + 3 * 4')).toBe(14);
58
+ expect(eval_('(2 + 3) * 4')).toBe(20);
59
+ });
60
+
61
+ it('unary operators', () => {
62
+ expect(eval_('-5')).toBe(-5);
63
+ expect(eval_('+3')).toBe(3);
64
+ });
65
+ });
66
+
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Comparison & logical
70
+ // ---------------------------------------------------------------------------
71
+
72
+ describe('expression parser — comparison', () => {
73
+ it('equality', () => {
74
+ expect(eval_('1 === 1')).toBe(true);
75
+ expect(eval_('1 !== 2')).toBe(true);
76
+ expect(eval_("1 == '1'")).toBe(true);
77
+ expect(eval_("1 != '2'")).toBe(true);
78
+ });
79
+
80
+ it('relational', () => {
81
+ expect(eval_('3 > 2')).toBe(true);
82
+ expect(eval_('3 < 2')).toBe(false);
83
+ expect(eval_('3 >= 3')).toBe(true);
84
+ expect(eval_('3 <= 2')).toBe(false);
85
+ });
86
+ });
87
+
88
+
89
+ describe('expression parser — logical', () => {
90
+ it('&& and ||', () => {
91
+ expect(eval_('true && false')).toBe(false);
92
+ expect(eval_('true || false')).toBe(true);
93
+ expect(eval_('0 || 42')).toBe(42);
94
+ expect(eval_("'' || 'default'")).toBe('default');
95
+ });
96
+
97
+ it('!', () => {
98
+ expect(eval_('!true')).toBe(false);
99
+ expect(eval_('!false')).toBe(true);
100
+ expect(eval_('!0')).toBe(true);
101
+ });
102
+
103
+ it('nullish coalescing ??', () => {
104
+ expect(eval_('null ?? 10')).toBe(10);
105
+ expect(eval_('undefined ?? 20')).toBe(20);
106
+ expect(eval_('0 ?? 30')).toBe(0);
107
+ expect(eval_("'' ?? 'fallback'")).toBe('');
108
+ });
109
+ });
110
+
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Ternary
114
+ // ---------------------------------------------------------------------------
115
+
116
+ describe('expression parser — ternary', () => {
117
+ it('evaluates truthy branch', () => {
118
+ expect(eval_("true ? 'yes' : 'no'")).toBe('yes');
119
+ });
120
+
121
+ it('evaluates falsy branch', () => {
122
+ expect(eval_("false ? 'yes' : 'no'")).toBe('no');
123
+ });
124
+
125
+ it('works with expressions', () => {
126
+ expect(eval_('5 > 3 ? 10 : 20')).toBe(10);
127
+ });
128
+ });
129
+
130
+
131
+ // ---------------------------------------------------------------------------
132
+ // Property access & scope
133
+ // ---------------------------------------------------------------------------
134
+
135
+ describe('expression parser — property access', () => {
136
+ it('reads scope variables', () => {
137
+ expect(eval_('x', { x: 42 })).toBe(42);
138
+ expect(eval_('name', { name: 'Tony' })).toBe('Tony');
139
+ });
140
+
141
+ it('dot access', () => {
142
+ expect(eval_('user.name', { user: { name: 'Tony' } })).toBe('Tony');
143
+ });
144
+
145
+ it('computed access', () => {
146
+ expect(eval_('items[0]', { items: ['a', 'b', 'c'] })).toBe('a');
147
+ expect(eval_('obj[key]', { obj: { x: 1 }, key: 'x' })).toBe(1);
148
+ });
149
+
150
+ it('optional chaining ?.', () => {
151
+ expect(eval_('user?.name', { user: null })).toBe(undefined);
152
+ expect(eval_('user?.name', { user: { name: 'Tony' } })).toBe('Tony');
153
+ });
154
+
155
+ it('returns undefined for missing scope keys', () => {
156
+ expect(eval_('missing')).toBe(undefined);
157
+ expect(eval_('a.b.c', { a: {} })).toBe(undefined);
158
+ });
159
+ });
160
+
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // Method calls
164
+ // ---------------------------------------------------------------------------
165
+
166
+ describe('expression parser — method calls', () => {
167
+ it('string methods', () => {
168
+ expect(eval_("'hello'.toUpperCase()")).toBe('HELLO');
169
+ expect(eval_("'hello world'.split(' ')")).toEqual(['hello', 'world']);
170
+ expect(eval_("'abc'.includes('b')")).toBe(true);
171
+ });
172
+
173
+ it('array methods', () => {
174
+ expect(eval_('items.length', { items: [1, 2, 3] })).toBe(3);
175
+ expect(eval_('items.includes(2)', { items: [1, 2, 3] })).toBe(true);
176
+ expect(eval_("items.join(',')", { items: [1, 2, 3] })).toBe('1,2,3');
177
+ });
178
+
179
+ it('custom function calls', () => {
180
+ const add = (a, b) => a + b;
181
+ expect(eval_('add(1, 2)', { add })).toBe(3);
182
+ });
183
+ });
184
+
185
+
186
+ // ---------------------------------------------------------------------------
187
+ // Built-in globals
188
+ // ---------------------------------------------------------------------------
189
+
190
+ describe('expression parser — built-in globals', () => {
191
+ it('Math', () => {
192
+ expect(eval_('Math.PI')).toBeCloseTo(3.14159);
193
+ expect(eval_('Math.max(1, 5, 3)')).toBe(5);
194
+ expect(eval_('Math.abs(-7)')).toBe(7);
195
+ });
196
+
197
+ it('JSON', () => {
198
+ expect(eval_("JSON.parse('{\"a\":1}')")).toEqual({ a: 1 });
199
+ });
200
+
201
+ it('parseInt/parseFloat', () => {
202
+ expect(eval_("parseInt('42')")).toBe(42);
203
+ expect(eval_("parseFloat('3.14')")).toBeCloseTo(3.14);
204
+ });
205
+
206
+ it('isNaN', () => {
207
+ expect(eval_('isNaN(NaN)')).toBe(true);
208
+ expect(eval_('isNaN(5)')).toBe(false);
209
+ });
210
+ });
211
+
212
+
213
+ // ---------------------------------------------------------------------------
214
+ // Template literals
215
+ // ---------------------------------------------------------------------------
216
+
217
+ describe('expression parser — template literals', () => {
218
+ it('simple interpolation', () => {
219
+ expect(eval_('`Hello ${name}`', { name: 'Tony' })).toBe('Hello Tony');
220
+ });
221
+
222
+ it('expression inside interpolation', () => {
223
+ expect(eval_('`${a + b}`', { a: 1, b: 2 })).toBe('3');
224
+ });
225
+
226
+ it('no interpolation', () => {
227
+ expect(eval_('`plain text`')).toBe('plain text');
228
+ });
229
+ });
230
+
231
+
232
+ // ---------------------------------------------------------------------------
233
+ // Array & object literals
234
+ // ---------------------------------------------------------------------------
235
+
236
+ describe('expression parser — array/object literals', () => {
237
+ it('array literal', () => {
238
+ expect(eval_('[1, 2, 3]')).toEqual([1, 2, 3]);
239
+ expect(eval_('[]')).toEqual([]);
240
+ });
241
+
242
+ it('object literal', () => {
243
+ expect(eval_("{ x: 1, y: 'two' }")).toEqual({ x: 1, y: 'two' });
244
+ });
245
+
246
+ it('shorthand property', () => {
247
+ expect(eval_('{ x }', { x: 42 })).toEqual({ x: 42 });
248
+ });
249
+ });
250
+
251
+
252
+ // ---------------------------------------------------------------------------
253
+ // Arrow functions
254
+ // ---------------------------------------------------------------------------
255
+
256
+ describe('expression parser — arrow functions', () => {
257
+ it('single-param arrow', () => {
258
+ const fn = eval_('x => x * 2');
259
+ expect(fn(3)).toBe(6);
260
+ });
261
+
262
+ it('multi-param arrow', () => {
263
+ const fn = eval_('(a, b) => a + b');
264
+ expect(fn(1, 2)).toBe(3);
265
+ });
266
+
267
+ it('no-param arrow', () => {
268
+ const fn = eval_('() => 42');
269
+ expect(fn()).toBe(42);
270
+ });
271
+
272
+ it('arrow with scope access', () => {
273
+ const items = [1, 2, 3];
274
+ expect(eval_('items.filter(x => x > 1)', { items })).toEqual([2, 3]);
275
+ });
276
+ });
277
+
278
+
279
+ // ---------------------------------------------------------------------------
280
+ // typeof
281
+ // ---------------------------------------------------------------------------
282
+
283
+ describe('expression parser — typeof', () => {
284
+ it('typeof string', () => {
285
+ expect(eval_("typeof 'hello'")).toBe('string');
286
+ });
287
+
288
+ it('typeof number', () => {
289
+ expect(eval_('typeof 42')).toBe('number');
290
+ });
291
+
292
+ it('typeof undefined variable', () => {
293
+ expect(eval_('typeof missing')).toBe('undefined');
294
+ });
295
+ });
296
+
297
+
298
+ // ---------------------------------------------------------------------------
299
+ // Safety / security
300
+ // ---------------------------------------------------------------------------
301
+
302
+ describe('expression parser — safety', () => {
303
+ it('blocks constructor access', () => {
304
+ expect(eval_("''.constructor")).toBe(undefined);
305
+ });
306
+
307
+ it('blocks __proto__ access', () => {
308
+ expect(eval_("({}).__proto__", {})).toBe(undefined);
309
+ });
310
+
311
+ it('returns undefined for invalid expressions', () => {
312
+ expect(eval_('!!!!')).toBe(undefined);
313
+ });
314
+
315
+ it('handles deeply nested safe access', () => {
316
+ const data = { a: { b: { c: { d: 'deep' } } } };
317
+ expect(eval_('a.b.c.d', data)).toBe('deep');
318
+ });
319
+ });
320
+
321
+
322
+ // ---------------------------------------------------------------------------
323
+ // Multi-scope resolution
324
+ // ---------------------------------------------------------------------------
325
+
326
+ describe('expression parser — multi-scope', () => {
327
+ it('checks scope layers in order', () => {
328
+ expect(safeEval('x', [{ x: 'first' }, { x: 'second' }])).toBe('first');
329
+ });
330
+
331
+ it('falls through to second scope', () => {
332
+ expect(safeEval('y', [{ x: 1 }, { y: 2 }])).toBe(2);
333
+ });
334
+ });
335
+
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // in operator
339
+ // ---------------------------------------------------------------------------
340
+
341
+ describe('expression parser — in operator', () => {
342
+ it('checks property existence', () => {
343
+ expect(eval_("'x' in obj", { obj: { x: 1 } })).toBe(true);
344
+ expect(eval_("'y' in obj", { obj: { x: 1 } })).toBe(false);
345
+ });
346
+ });
347
+
348
+
349
+ // ---------------------------------------------------------------------------
350
+ // instanceof operator
351
+ // ---------------------------------------------------------------------------
352
+
353
+ describe('expression parser — instanceof', () => {
354
+ it('checks instanceOf', () => {
355
+ expect(eval_('arr instanceof Array', { arr: [1, 2] })).toBe(true);
356
+ expect(eval_('obj instanceof Array', { obj: {} })).toBe(false);
357
+ });
358
+ });
359
+
360
+
361
+ // ---------------------------------------------------------------------------
362
+ // Nested ternary
363
+ // ---------------------------------------------------------------------------
364
+
365
+ describe('expression parser — nested ternary', () => {
366
+ it('evaluates simple ternary correctly', () => {
367
+ expect(eval_("x > 10 ? 'big' : 'small'", { x: 12 })).toBe('big');
368
+ expect(eval_("x > 10 ? 'big' : 'small'", { x: 2 })).toBe('small');
369
+ });
370
+ });
371
+
372
+
373
+ // ---------------------------------------------------------------------------
374
+ // Chained method calls
375
+ // ---------------------------------------------------------------------------
376
+
377
+ describe('expression parser — chained calls', () => {
378
+ it('chains array methods', () => {
379
+ expect(eval_('items.filter(x => x > 1).map(x => x * 2)', { items: [1, 2, 3] })).toEqual([4, 6]);
380
+ });
381
+
382
+ it('chains string methods', () => {
383
+ expect(eval_("name.trim().toUpperCase()", { name: ' hello ' })).toBe('HELLO');
384
+ });
385
+ });
386
+
387
+
388
+ // ---------------------------------------------------------------------------
389
+ // Spread operator
390
+ // ---------------------------------------------------------------------------
391
+
392
+ describe('expression parser — spread / rest', () => {
393
+ it('spread is not supported — returns gracefully', () => {
394
+ // The parser does not support spread syntax; verify it doesn't throw
395
+ const result = eval_('[...items, 4]', { items: [1, 2, 3] });
396
+ expect(result).toBeDefined();
397
+ });
398
+ });
399
+
400
+
401
+ // ---------------------------------------------------------------------------
402
+ // Destructuring assignment in arrow body
403
+ // ---------------------------------------------------------------------------
404
+
405
+ describe('expression parser — complex arrow', () => {
406
+ it('arrow as callback in array method', () => {
407
+ const items = [{ n: 'a' }, { n: 'b' }];
408
+ expect(eval_('items.map(x => x.n)', { items })).toEqual(['a', 'b']);
409
+ });
410
+
411
+ it('arrow with ternary body', () => {
412
+ const fn = eval_('x => x > 0 ? "pos" : "neg"');
413
+ expect(fn(1)).toBe('pos');
414
+ expect(fn(-1)).toBe('neg');
415
+ });
416
+ });
417
+
418
+
419
+ // ---------------------------------------------------------------------------
420
+ // Bitwise operators
421
+ // ---------------------------------------------------------------------------
422
+
423
+ describe('expression parser — bitwise', () => {
424
+ it('bitwise operators are not supported — does not throw', () => {
425
+ // The expression parser does not implement bitwise operators
426
+ // Verify graceful fallback rather than crashes
427
+ expect(() => eval_('5 | 3')).not.toThrow();
428
+ expect(() => eval_('5 & 3')).not.toThrow();
429
+ expect(() => eval_('5 ^ 3')).not.toThrow();
430
+ });
431
+ });
432
+
433
+
434
+ // ---------------------------------------------------------------------------
435
+ // Comma expressions
436
+ // ---------------------------------------------------------------------------
437
+
438
+ describe('expression parser — comma', () => {
439
+ it('comma expressions are not supported — does not throw', () => {
440
+ // The parser does not support comma expressions
441
+ expect(() => eval_('(1, 2, 3)')).not.toThrow();
442
+ });
443
+ });
444
+
445
+
446
+ // ---------------------------------------------------------------------------
447
+ // Edge cases
448
+ // ---------------------------------------------------------------------------
449
+
450
+ describe('expression parser — edge cases', () => {
451
+ it('handles very long dot chains', () => {
452
+ const data = { a: { b: { c: { d: { e: 42 } } } } };
453
+ expect(eval_('a.b.c.d.e', data)).toBe(42);
454
+ });
455
+
456
+ it('handles numeric string keys in bracket access', () => {
457
+ expect(eval_("items['0']", { items: ['a', 'b'] })).toBe('a');
458
+ });
459
+
460
+ it('handles conditional access chains', () => {
461
+ expect(eval_('a?.b?.c', { a: null })).toBe(undefined);
462
+ expect(eval_('a?.b?.c', { a: { b: { c: 1 } } })).toBe(1);
463
+ });
464
+
465
+ it('handles string with special characters', () => {
466
+ expect(eval_("'hello\\nworld'")).toContain('hello');
467
+ });
468
+
469
+ it('handles negative numbers in expressions', () => {
470
+ expect(eval_('-1 + -2')).toBe(-3);
471
+ });
472
+
473
+ it('exponentiation ** is not supported — does not throw', () => {
474
+ // The parser does not implement ** operator
475
+ expect(() => eval_('2 ** 3')).not.toThrow();
476
+ });
477
+
478
+ it('handles empty array/object', () => {
479
+ expect(eval_('[]')).toEqual([]);
480
+ expect(eval_('{}')).toEqual({});
481
+ });
482
+ });