zero-query 0.9.8 → 1.0.0
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/README.md +55 -31
- package/cli/args.js +1 -1
- package/cli/commands/build.js +2 -2
- package/cli/commands/bundle.js +15 -15
- package/cli/commands/create.js +41 -7
- package/cli/commands/dev/devtools/index.js +1 -1
- package/cli/commands/dev/devtools/js/core.js +14 -14
- package/cli/commands/dev/devtools/js/elements.js +4 -4
- package/cli/commands/dev/devtools/js/stats.js +1 -1
- package/cli/commands/dev/devtools/styles.css +2 -2
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/logger.js +1 -1
- package/cli/commands/dev/overlay.js +21 -14
- package/cli/commands/dev/server.js +5 -5
- package/cli/commands/dev/validator.js +7 -7
- package/cli/commands/dev/watcher.js +6 -6
- package/cli/help.js +4 -2
- package/cli/index.js +2 -2
- package/cli/scaffold/default/app/app.js +17 -18
- package/cli/scaffold/default/app/components/about.js +9 -9
- package/cli/scaffold/default/app/components/api-demo.js +6 -6
- package/cli/scaffold/default/app/components/contact-card.js +4 -4
- package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
- package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
- package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
- package/cli/scaffold/default/app/components/counter.js +8 -8
- package/cli/scaffold/default/app/components/home.js +13 -13
- package/cli/scaffold/default/app/components/not-found.js +1 -1
- package/cli/scaffold/default/app/components/playground/playground.css +1 -1
- package/cli/scaffold/default/app/components/playground/playground.html +11 -11
- package/cli/scaffold/default/app/components/playground/playground.js +11 -11
- package/cli/scaffold/default/app/components/todos.js +8 -8
- package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
- package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
- package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
- package/cli/scaffold/default/app/routes.js +1 -1
- package/cli/scaffold/default/app/store.js +1 -1
- package/cli/scaffold/default/global.css +2 -2
- package/cli/scaffold/default/index.html +2 -2
- package/cli/scaffold/minimal/app/app.js +6 -7
- package/cli/scaffold/minimal/app/components/about.js +5 -5
- package/cli/scaffold/minimal/app/components/counter.js +6 -6
- package/cli/scaffold/minimal/app/components/home.js +8 -8
- package/cli/scaffold/minimal/app/components/not-found.js +1 -1
- package/cli/scaffold/minimal/app/routes.js +1 -1
- package/cli/scaffold/minimal/app/store.js +1 -1
- package/cli/scaffold/minimal/global.css +2 -2
- package/cli/scaffold/minimal/index.html +1 -1
- package/cli/scaffold/ssr/app/app.js +29 -0
- package/cli/scaffold/ssr/app/components/about.js +28 -0
- package/cli/scaffold/ssr/app/components/home.js +37 -0
- package/cli/scaffold/ssr/app/components/not-found.js +15 -0
- package/cli/scaffold/ssr/app/routes.js +6 -0
- package/cli/scaffold/ssr/global.css +113 -0
- package/cli/scaffold/ssr/index.html +31 -0
- package/cli/scaffold/ssr/package.json +8 -0
- package/cli/scaffold/ssr/server/index.js +118 -0
- package/cli/utils.js +6 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +565 -228
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +25 -12
- package/index.js +11 -7
- package/package.json +9 -3
- package/src/component.js +64 -63
- package/src/core.js +15 -15
- package/src/diff.js +38 -38
- package/src/errors.js +72 -18
- package/src/expression.js +15 -17
- package/src/http.js +4 -4
- package/src/package.json +1 -0
- package/src/reactive.js +75 -9
- package/src/router.js +104 -24
- package/src/ssr.js +133 -39
- package/src/store.js +103 -21
- package/src/utils.js +64 -12
- package/tests/audit.test.js +143 -15
- package/tests/cli.test.js +20 -20
- package/tests/component.test.js +121 -121
- package/tests/core.test.js +56 -56
- package/tests/diff.test.js +42 -42
- package/tests/errors.test.js +425 -147
- package/tests/expression.test.js +58 -53
- package/tests/http.test.js +20 -20
- package/tests/reactive.test.js +185 -24
- package/tests/router.test.js +501 -74
- package/tests/ssr.test.js +444 -10
- package/tests/store.test.js +264 -23
- package/tests/utils.test.js +163 -26
- package/types/collection.d.ts +2 -2
- package/types/component.d.ts +5 -5
- package/types/errors.d.ts +36 -4
- package/types/http.d.ts +3 -3
- package/types/misc.d.ts +9 -9
- package/types/reactive.d.ts +25 -3
- package/types/router.d.ts +10 -6
- package/types/ssr.d.ts +22 -2
- package/types/store.d.ts +40 -5
- package/types/utils.d.ts +1 -1
package/tests/utils.test.js
CHANGED
|
@@ -321,10 +321,10 @@ describe('isEqual', () => {
|
|
|
321
321
|
});
|
|
322
322
|
|
|
323
323
|
// ---------------------------------------------------------------------------
|
|
324
|
-
// deepMerge
|
|
324
|
+
// deepMerge - circular reference safety
|
|
325
325
|
// ---------------------------------------------------------------------------
|
|
326
326
|
|
|
327
|
-
describe('deepMerge
|
|
327
|
+
describe('deepMerge - circular reference safety', () => {
|
|
328
328
|
it('does not infinite-loop on circular source', () => {
|
|
329
329
|
const a = { x: 1 };
|
|
330
330
|
const b = { y: 2 };
|
|
@@ -541,14 +541,14 @@ describe('bus (event bus)', () => {
|
|
|
541
541
|
describe('bus (EventBus)', () => {
|
|
542
542
|
beforeEach(() => { bus.clear(); });
|
|
543
543
|
|
|
544
|
-
it('on/emit
|
|
544
|
+
it('on/emit - fires handler for matching events', () => {
|
|
545
545
|
const fn = vi.fn();
|
|
546
546
|
bus.on('test', fn);
|
|
547
547
|
bus.emit('test', 42);
|
|
548
548
|
expect(fn).toHaveBeenCalledWith(42);
|
|
549
549
|
});
|
|
550
550
|
|
|
551
|
-
it('off
|
|
551
|
+
it('off - removes handler', () => {
|
|
552
552
|
const fn = vi.fn();
|
|
553
553
|
bus.on('test', fn);
|
|
554
554
|
bus.off('test', fn);
|
|
@@ -564,7 +564,7 @@ describe('bus (EventBus)', () => {
|
|
|
564
564
|
expect(fn).not.toHaveBeenCalled();
|
|
565
565
|
});
|
|
566
566
|
|
|
567
|
-
it('once
|
|
567
|
+
it('once - fires handler only once', () => {
|
|
568
568
|
const fn = vi.fn();
|
|
569
569
|
bus.once('test', fn);
|
|
570
570
|
bus.emit('test', 'a');
|
|
@@ -573,7 +573,7 @@ describe('bus (EventBus)', () => {
|
|
|
573
573
|
expect(fn).toHaveBeenCalledWith('a');
|
|
574
574
|
});
|
|
575
575
|
|
|
576
|
-
it('clear
|
|
576
|
+
it('clear - removes all handlers', () => {
|
|
577
577
|
const fn = vi.fn();
|
|
578
578
|
bus.on('a', fn);
|
|
579
579
|
bus.on('b', fn);
|
|
@@ -590,10 +590,10 @@ describe('bus (EventBus)', () => {
|
|
|
590
590
|
|
|
591
591
|
|
|
592
592
|
// ===========================================================================
|
|
593
|
-
// throttle
|
|
593
|
+
// throttle - window reset
|
|
594
594
|
// ===========================================================================
|
|
595
595
|
|
|
596
|
-
describe('throttle
|
|
596
|
+
describe('throttle - edge cases', () => {
|
|
597
597
|
it('fires trailing call after wait period', async () => {
|
|
598
598
|
vi.useFakeTimers();
|
|
599
599
|
const fn = vi.fn();
|
|
@@ -612,10 +612,10 @@ describe('throttle — edge cases', () => {
|
|
|
612
612
|
|
|
613
613
|
|
|
614
614
|
// ===========================================================================
|
|
615
|
-
// deepClone
|
|
615
|
+
// deepClone - edge cases
|
|
616
616
|
// ===========================================================================
|
|
617
617
|
|
|
618
|
-
describe('deepClone
|
|
618
|
+
describe('deepClone - edge cases', () => {
|
|
619
619
|
it('clones nested arrays', () => {
|
|
620
620
|
const arr = [[1, 2], [3, 4]];
|
|
621
621
|
const clone = deepClone(arr);
|
|
@@ -631,10 +631,10 @@ describe('deepClone — edge cases', () => {
|
|
|
631
631
|
|
|
632
632
|
|
|
633
633
|
// ===========================================================================
|
|
634
|
-
// deepMerge
|
|
634
|
+
// deepMerge - multiple sources
|
|
635
635
|
// ===========================================================================
|
|
636
636
|
|
|
637
|
-
describe('deepMerge
|
|
637
|
+
describe('deepMerge - edge cases', () => {
|
|
638
638
|
it('merges from multiple sources', () => {
|
|
639
639
|
const result = deepMerge({}, { a: 1 }, { b: 2 }, { c: 3 });
|
|
640
640
|
expect(result).toEqual({ a: 1, b: 2, c: 3 });
|
|
@@ -653,10 +653,10 @@ describe('deepMerge — edge cases', () => {
|
|
|
653
653
|
|
|
654
654
|
|
|
655
655
|
// ===========================================================================
|
|
656
|
-
// isEqual
|
|
656
|
+
// isEqual - deeply nested
|
|
657
657
|
// ===========================================================================
|
|
658
658
|
|
|
659
|
-
describe('isEqual
|
|
659
|
+
describe('isEqual - edge cases', () => {
|
|
660
660
|
it('deeply nested equal objects', () => {
|
|
661
661
|
expect(isEqual({ a: { b: { c: 1 } } }, { a: { b: { c: 1 } } })).toBe(true);
|
|
662
662
|
});
|
|
@@ -686,10 +686,10 @@ describe('isEqual — edge cases', () => {
|
|
|
686
686
|
|
|
687
687
|
|
|
688
688
|
// ===========================================================================
|
|
689
|
-
// camelCase / kebabCase
|
|
689
|
+
// camelCase / kebabCase - edge cases
|
|
690
690
|
// ===========================================================================
|
|
691
691
|
|
|
692
|
-
describe('camelCase / kebabCase
|
|
692
|
+
describe('camelCase / kebabCase - edge cases', () => {
|
|
693
693
|
it('camelCase single word', () => {
|
|
694
694
|
expect(camelCase('hello')).toBe('hello');
|
|
695
695
|
});
|
|
@@ -709,10 +709,10 @@ describe('camelCase / kebabCase — edge cases', () => {
|
|
|
709
709
|
|
|
710
710
|
|
|
711
711
|
// ===========================================================================
|
|
712
|
-
// html tag
|
|
712
|
+
// html tag - escaping
|
|
713
713
|
// ===========================================================================
|
|
714
714
|
|
|
715
|
-
describe('html tag
|
|
715
|
+
describe('html tag - edge cases', () => {
|
|
716
716
|
it('handles null interp value', () => {
|
|
717
717
|
const result = html`<div>${null}</div>`;
|
|
718
718
|
expect(result).toBe('<div></div>');
|
|
@@ -734,10 +734,10 @@ describe('html tag — edge cases', () => {
|
|
|
734
734
|
|
|
735
735
|
|
|
736
736
|
// ===========================================================================
|
|
737
|
-
// storage
|
|
737
|
+
// storage - error handling
|
|
738
738
|
// ===========================================================================
|
|
739
739
|
|
|
740
|
-
describe('storage
|
|
740
|
+
describe('storage - parse error fallback', () => {
|
|
741
741
|
it('returns fallback when JSON.parse fails', () => {
|
|
742
742
|
localStorage.setItem('bad', '{invalid json');
|
|
743
743
|
expect(storage.get('bad', 'default')).toBe('default');
|
|
@@ -747,7 +747,7 @@ describe('storage — parse error fallback', () => {
|
|
|
747
747
|
|
|
748
748
|
|
|
749
749
|
// ===========================================================================
|
|
750
|
-
// NEW UTILITIES
|
|
750
|
+
// NEW UTILITIES - Array
|
|
751
751
|
// ===========================================================================
|
|
752
752
|
|
|
753
753
|
describe('range', () => {
|
|
@@ -866,7 +866,7 @@ describe('groupBy', () => {
|
|
|
866
866
|
|
|
867
867
|
|
|
868
868
|
// ===========================================================================
|
|
869
|
-
// NEW UTILITIES
|
|
869
|
+
// NEW UTILITIES - Object
|
|
870
870
|
// ===========================================================================
|
|
871
871
|
|
|
872
872
|
describe('pick', () => {
|
|
@@ -1012,7 +1012,7 @@ describe('isEmpty', () => {
|
|
|
1012
1012
|
|
|
1013
1013
|
|
|
1014
1014
|
// ===========================================================================
|
|
1015
|
-
// NEW UTILITIES
|
|
1015
|
+
// NEW UTILITIES - String
|
|
1016
1016
|
// ===========================================================================
|
|
1017
1017
|
|
|
1018
1018
|
describe('capitalize', () => {
|
|
@@ -1066,7 +1066,7 @@ describe('truncate', () => {
|
|
|
1066
1066
|
|
|
1067
1067
|
|
|
1068
1068
|
// ===========================================================================
|
|
1069
|
-
// NEW UTILITIES
|
|
1069
|
+
// NEW UTILITIES - Number
|
|
1070
1070
|
// ===========================================================================
|
|
1071
1071
|
|
|
1072
1072
|
describe('clamp', () => {
|
|
@@ -1099,7 +1099,7 @@ describe('clamp', () => {
|
|
|
1099
1099
|
|
|
1100
1100
|
|
|
1101
1101
|
// ===========================================================================
|
|
1102
|
-
// NEW UTILITIES
|
|
1102
|
+
// NEW UTILITIES - Function
|
|
1103
1103
|
// ===========================================================================
|
|
1104
1104
|
|
|
1105
1105
|
describe('memoize', () => {
|
|
@@ -1154,7 +1154,7 @@ describe('memoize', () => {
|
|
|
1154
1154
|
|
|
1155
1155
|
|
|
1156
1156
|
// ===========================================================================
|
|
1157
|
-
// NEW UTILITIES
|
|
1157
|
+
// NEW UTILITIES - Async
|
|
1158
1158
|
// ===========================================================================
|
|
1159
1159
|
|
|
1160
1160
|
describe('retry', () => {
|
|
@@ -1238,3 +1238,140 @@ describe('timeout', () => {
|
|
|
1238
1238
|
clearSpy.mockRestore();
|
|
1239
1239
|
});
|
|
1240
1240
|
});
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
// ===========================================================================
|
|
1244
|
+
// memoize - LRU behaviour
|
|
1245
|
+
// ===========================================================================
|
|
1246
|
+
|
|
1247
|
+
describe('memoize - LRU eviction', () => {
|
|
1248
|
+
it('promotes recently-read entries so they survive eviction', () => {
|
|
1249
|
+
const fn = vi.fn(x => x * 2);
|
|
1250
|
+
const mem = memoize(fn, { maxSize: 3 });
|
|
1251
|
+
|
|
1252
|
+
mem(1); // cache: [1]
|
|
1253
|
+
mem(2); // cache: [1, 2]
|
|
1254
|
+
mem(3); // cache: [1, 2, 3]
|
|
1255
|
+
|
|
1256
|
+
// Access 1 to promote it (LRU moves it to newest)
|
|
1257
|
+
mem(1); // cache: [2, 3, 1]
|
|
1258
|
+
expect(fn).toHaveBeenCalledTimes(3); // still cached, no recompute
|
|
1259
|
+
|
|
1260
|
+
// Insert 4 -> should evict 2 (least recently used), NOT 1
|
|
1261
|
+
mem(4); // cache: [3, 1, 4]
|
|
1262
|
+
expect(fn).toHaveBeenCalledTimes(4);
|
|
1263
|
+
|
|
1264
|
+
// 1 should still be cached (was promoted)
|
|
1265
|
+
mem(1);
|
|
1266
|
+
expect(fn).toHaveBeenCalledTimes(4); // no recompute
|
|
1267
|
+
|
|
1268
|
+
// 2 should be evicted
|
|
1269
|
+
mem(2);
|
|
1270
|
+
expect(fn).toHaveBeenCalledTimes(5); // recomputation
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
it('evicts in LRU order, not insertion order', () => {
|
|
1274
|
+
const fn = vi.fn(x => x);
|
|
1275
|
+
const mem = memoize(fn, { maxSize: 2 });
|
|
1276
|
+
|
|
1277
|
+
mem('a'); // [a]
|
|
1278
|
+
mem('b'); // [a, b]
|
|
1279
|
+
|
|
1280
|
+
// Read 'a' - makes 'b' the LRU
|
|
1281
|
+
mem('a'); // [b, a]
|
|
1282
|
+
|
|
1283
|
+
// Insert 'c' - should evict 'b', not 'a'
|
|
1284
|
+
mem('c'); // [a, c]
|
|
1285
|
+
|
|
1286
|
+
// 'a' still cached
|
|
1287
|
+
mem('a');
|
|
1288
|
+
expect(fn).toHaveBeenCalledTimes(3); // a, b, c
|
|
1289
|
+
|
|
1290
|
+
// 'b' was evicted
|
|
1291
|
+
mem('b');
|
|
1292
|
+
expect(fn).toHaveBeenCalledTimes(4);
|
|
1293
|
+
});
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
// ===========================================================================
|
|
1298
|
+
// deepClone - enhanced types
|
|
1299
|
+
// ===========================================================================
|
|
1300
|
+
|
|
1301
|
+
describe('deepClone - enhanced types', () => {
|
|
1302
|
+
it('clones Date objects', () => {
|
|
1303
|
+
const date = new Date('2024-01-15T12:00:00Z');
|
|
1304
|
+
const clone = deepClone(date);
|
|
1305
|
+
expect(clone).toEqual(date);
|
|
1306
|
+
expect(clone).not.toBe(date);
|
|
1307
|
+
expect(clone instanceof Date).toBe(true);
|
|
1308
|
+
expect(clone.getTime()).toBe(date.getTime());
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
it('clones nested Dates', () => {
|
|
1312
|
+
const obj = { created: new Date('2024-01-15T12:00:00Z'), meta: { updated: new Date('2024-06-15T12:00:00Z') } };
|
|
1313
|
+
const clone = deepClone(obj);
|
|
1314
|
+
clone.created.setFullYear(2000);
|
|
1315
|
+
expect(obj.created.getFullYear()).toBe(2024);
|
|
1316
|
+
});
|
|
1317
|
+
|
|
1318
|
+
it('clones RegExp', () => {
|
|
1319
|
+
const re = /hello/gi;
|
|
1320
|
+
const clone = deepClone(re);
|
|
1321
|
+
expect(clone).toEqual(re);
|
|
1322
|
+
expect(clone).not.toBe(re);
|
|
1323
|
+
expect(clone.source).toBe('hello');
|
|
1324
|
+
expect(clone.flags).toBe('gi');
|
|
1325
|
+
});
|
|
1326
|
+
|
|
1327
|
+
it('clones Map', () => {
|
|
1328
|
+
const map = new Map([['a', 1], ['b', { deep: true }]]);
|
|
1329
|
+
const clone = deepClone(map);
|
|
1330
|
+
expect(clone).not.toBe(map);
|
|
1331
|
+
expect(clone.get('a')).toBe(1);
|
|
1332
|
+
clone.get('b').deep = false;
|
|
1333
|
+
expect(map.get('b').deep).toBe(true);
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1336
|
+
it('clones Set', () => {
|
|
1337
|
+
const set = new Set([1, 2, { x: 3 }]);
|
|
1338
|
+
const clone = deepClone(set);
|
|
1339
|
+
expect(clone).not.toBe(set);
|
|
1340
|
+
expect(clone.size).toBe(3);
|
|
1341
|
+
expect(clone.has(1)).toBe(true);
|
|
1342
|
+
expect(clone.has(2)).toBe(true);
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it('handles undefined values in objects', () => {
|
|
1346
|
+
const obj = { a: 1, b: undefined, c: 'hello' };
|
|
1347
|
+
const clone = deepClone(obj);
|
|
1348
|
+
expect(clone.b).toBeUndefined();
|
|
1349
|
+
expect('b' in clone).toBe(true);
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
it('handles null values', () => {
|
|
1353
|
+
const obj = { a: null, b: { c: null } };
|
|
1354
|
+
const clone = deepClone(obj);
|
|
1355
|
+
expect(clone.a).toBeNull();
|
|
1356
|
+
expect(clone.b.c).toBeNull();
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
it('handles circular references', () => {
|
|
1360
|
+
const obj = { a: 1 };
|
|
1361
|
+
obj.self = obj;
|
|
1362
|
+
const clone = deepClone(obj);
|
|
1363
|
+
expect(clone.a).toBe(1);
|
|
1364
|
+
expect(clone.self).toBe(clone);
|
|
1365
|
+
expect(clone.self).not.toBe(obj);
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
it('handles nested circular references', () => {
|
|
1369
|
+
const a = { name: 'a' };
|
|
1370
|
+
const b = { name: 'b', ref: a };
|
|
1371
|
+
a.ref = b;
|
|
1372
|
+
const clone = deepClone(a);
|
|
1373
|
+
expect(clone.name).toBe('a');
|
|
1374
|
+
expect(clone.ref.name).toBe('b');
|
|
1375
|
+
expect(clone.ref.ref).toBe(clone);
|
|
1376
|
+
});
|
|
1377
|
+
});
|
package/types/collection.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ZQueryCollection
|
|
2
|
+
* ZQueryCollection - chainable DOM element wrapper.
|
|
3
3
|
*
|
|
4
4
|
* Returned by `$()`, `$.all()`, `$.create()`, and many traversal methods.
|
|
5
5
|
* Similar to a jQuery object: wraps an array of elements with fluent methods.
|
|
@@ -50,7 +50,7 @@ export class ZQueryCollection {
|
|
|
50
50
|
/** Convert to a plain `Element[]`. */
|
|
51
51
|
toArray(): Element[];
|
|
52
52
|
|
|
53
|
-
/** Iterable protocol
|
|
53
|
+
/** Iterable protocol - works with `for...of` and spread. */
|
|
54
54
|
[Symbol.iterator](): IterableIterator<Element>;
|
|
55
55
|
|
|
56
56
|
// -- Traversal -----------------------------------------------------------
|
package/types/component.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Component system
|
|
2
|
+
* Component system - define, mount, and manage reactive components.
|
|
3
3
|
*
|
|
4
4
|
* @module component
|
|
5
5
|
*/
|
|
@@ -21,7 +21,7 @@ export interface ComponentDefinition {
|
|
|
21
21
|
*/
|
|
22
22
|
render?(this: ComponentInstance): string;
|
|
23
23
|
|
|
24
|
-
/** CSS string
|
|
24
|
+
/** CSS string - scoped to the component root on first render. */
|
|
25
25
|
styles?: string;
|
|
26
26
|
|
|
27
27
|
/**
|
|
@@ -55,7 +55,7 @@ export interface ComponentDefinition {
|
|
|
55
55
|
destroyed?(this: ComponentInstance): void;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Computed properties
|
|
58
|
+
* Computed properties - lazy getters derived from state.
|
|
59
59
|
* Each function receives the raw state as its argument.
|
|
60
60
|
* Access via `this.computed.name` in methods and templates.
|
|
61
61
|
*/
|
|
@@ -100,7 +100,7 @@ export interface ComponentInstance {
|
|
|
100
100
|
templates: Record<string, string>;
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
* Computed properties
|
|
103
|
+
* Computed properties - lazy getters derived from state.
|
|
104
104
|
* Defined via `computed` in the component definition.
|
|
105
105
|
*/
|
|
106
106
|
readonly computed: Record<string, any>;
|
|
@@ -116,7 +116,7 @@ export interface ComponentInstance {
|
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
118
|
* Manually queue a re-render (microtask-batched).
|
|
119
|
-
* Safe to call from anywhere
|
|
119
|
+
* Safe to call from anywhere - state mutations during render are coalesced.
|
|
120
120
|
*/
|
|
121
121
|
_scheduleUpdate(): void;
|
|
122
122
|
|
package/types/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Structured error handling
|
|
2
|
+
* Structured error handling - error codes, error class, and global handler.
|
|
3
3
|
*
|
|
4
4
|
* @module errors
|
|
5
5
|
*/
|
|
@@ -41,6 +41,12 @@ export declare const ErrorCode: {
|
|
|
41
41
|
readonly HTTP_INTERCEPTOR: 'ZQ_HTTP_INTERCEPTOR';
|
|
42
42
|
readonly HTTP_PARSE: 'ZQ_HTTP_PARSE';
|
|
43
43
|
|
|
44
|
+
// SSR
|
|
45
|
+
readonly SSR_RENDER: 'ZQ_SSR_RENDER';
|
|
46
|
+
readonly SSR_COMPONENT: 'ZQ_SSR_COMPONENT';
|
|
47
|
+
readonly SSR_HYDRATION: 'ZQ_SSR_HYDRATION';
|
|
48
|
+
readonly SSR_PAGE: 'ZQ_SSR_PAGE';
|
|
49
|
+
|
|
44
50
|
// General
|
|
45
51
|
readonly INVALID_ARGUMENT: 'ZQ_INVALID_ARGUMENT';
|
|
46
52
|
};
|
|
@@ -71,13 +77,14 @@ export type ZQueryErrorHandler = (error: ZQueryError) => void;
|
|
|
71
77
|
|
|
72
78
|
/**
|
|
73
79
|
* Register a global error handler. Called whenever zQuery catches an
|
|
74
|
-
* error internally. Pass `null` to
|
|
80
|
+
* error internally. Multiple handlers are supported. Pass `null` to clear all.
|
|
81
|
+
* @returns Unsubscribe function to remove this handler.
|
|
75
82
|
*/
|
|
76
|
-
export function onError(handler: ZQueryErrorHandler | null): void;
|
|
83
|
+
export function onError(handler: ZQueryErrorHandler | null): () => void;
|
|
77
84
|
|
|
78
85
|
/**
|
|
79
86
|
* Report an error through the global handler and console.
|
|
80
|
-
* Non-throwing
|
|
87
|
+
* Non-throwing - used for recoverable errors in callbacks, lifecycle hooks, etc.
|
|
81
88
|
*/
|
|
82
89
|
export function reportError(
|
|
83
90
|
code: ErrorCodeValue,
|
|
@@ -101,3 +108,28 @@ export function guardCallback<T extends (...args: any[]) => any>(
|
|
|
101
108
|
* Throws `ZQueryError` with `INVALID_ARGUMENT` on failure.
|
|
102
109
|
*/
|
|
103
110
|
export function validate(value: any, name: string, expectedType?: string): void;
|
|
111
|
+
|
|
112
|
+
/** Formatted error structure for overlays and logging. */
|
|
113
|
+
export interface FormattedError {
|
|
114
|
+
code: string;
|
|
115
|
+
type: string;
|
|
116
|
+
message: string;
|
|
117
|
+
context: Record<string, any>;
|
|
118
|
+
stack: string;
|
|
119
|
+
cause: FormattedError | null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Format a ZQueryError into a structured object suitable for overlays/logging.
|
|
124
|
+
*/
|
|
125
|
+
export function formatError(err: ZQueryError | Error): FormattedError;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Async version of guardCallback - wraps an async function so that
|
|
129
|
+
* rejections are caught, reported, and don't crash execution.
|
|
130
|
+
*/
|
|
131
|
+
export function guardAsync<T extends (...args: any[]) => Promise<any>>(
|
|
132
|
+
fn: T,
|
|
133
|
+
code: ErrorCodeValue,
|
|
134
|
+
context?: Record<string, any>,
|
|
135
|
+
): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>> | undefined>;
|
package/types/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP Client
|
|
2
|
+
* HTTP Client - fetch-based wrapper with interceptors and auto-JSON.
|
|
3
3
|
*
|
|
4
4
|
* @module http
|
|
5
5
|
*/
|
|
@@ -61,7 +61,7 @@ export interface HttpClient {
|
|
|
61
61
|
patch<T = any>(url: string, data?: any, opts?: HttpRequestOptions): Promise<HttpResponse<T>>;
|
|
62
62
|
/** DELETE request. */
|
|
63
63
|
delete<T = any>(url: string, data?: any, opts?: HttpRequestOptions): Promise<HttpResponse<T>>;
|
|
64
|
-
/** HEAD request
|
|
64
|
+
/** HEAD request - no body, useful for checking resource existence or headers. */
|
|
65
65
|
head<T = any>(url: string, opts?: HttpRequestOptions): Promise<HttpResponse<T>>;
|
|
66
66
|
|
|
67
67
|
/** Update default configuration for all subsequent requests. */
|
|
@@ -85,7 +85,7 @@ export interface HttpClient {
|
|
|
85
85
|
/** Create a new `AbortController` for manual request cancellation. */
|
|
86
86
|
createAbort(): AbortController;
|
|
87
87
|
|
|
88
|
-
/** Direct passthrough to native `fetch()`
|
|
88
|
+
/** Direct passthrough to native `fetch()` - no JSON handling, no interceptors. */
|
|
89
89
|
raw(url: string, opts?: RequestInit): Promise<Response>;
|
|
90
90
|
}
|
|
91
91
|
|
package/types/misc.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Morph an existing DOM element's children to match new HTML.
|
|
13
|
-
* Only touches nodes that actually differ
|
|
13
|
+
* Only touches nodes that actually differ - preserves focus, scroll
|
|
14
14
|
* positions, video playback, and other live DOM state.
|
|
15
15
|
*
|
|
16
16
|
* Use `z-key="uniqueId"` attributes on list items for keyed reconciliation.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
export function morph(rootEl: Element, newHTML: string): void;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Morph a single element in place
|
|
25
|
+
* Morph a single element in place - diffs attributes and children
|
|
26
26
|
* without replacing the node reference. If the tag name matches, the
|
|
27
27
|
* element is patched in place (preserving identity). If the tag differs,
|
|
28
28
|
* the element is replaced.
|
|
@@ -58,11 +58,11 @@ export function safeEval(expr: string, scope: object[]): any;
|
|
|
58
58
|
//
|
|
59
59
|
// ─── Structural Directives ──────────────────────────────────────────────
|
|
60
60
|
//
|
|
61
|
-
// z-if="expression" Conditional rendering
|
|
61
|
+
// z-if="expression" Conditional rendering - element removed when falsy.
|
|
62
62
|
// z-else-if="expression" Else-if branch (must be immediate sibling of z-if).
|
|
63
63
|
// z-else Default branch (must follow z-if or z-else-if).
|
|
64
64
|
//
|
|
65
|
-
// z-for="item in items" List rendering
|
|
65
|
+
// z-for="item in items" List rendering - repeats the element per item.
|
|
66
66
|
// {{item.prop}} Use double-brace interpolation for item data.
|
|
67
67
|
// (item, index) in items Destructured index support.
|
|
68
68
|
// n in 5 Number range → [1, 2, 3, 4, 5].
|
|
@@ -100,9 +100,9 @@ export function safeEval(expr: string, scope: object[]): any;
|
|
|
100
100
|
// Supports: input, textarea, select, select[multiple], contenteditable.
|
|
101
101
|
// Nested keys: z-model="user.name" → this.state.user.name.
|
|
102
102
|
// Modifiers (boolean attributes on same element):
|
|
103
|
-
// z-lazy
|
|
104
|
-
// z-trim
|
|
105
|
-
// z-number
|
|
103
|
+
// z-lazy - update on 'change' instead of 'input' (update on blur).
|
|
104
|
+
// z-trim - auto .trim() whitespace before writing to state.
|
|
105
|
+
// z-number - force Number() conversion regardless of input type.
|
|
106
106
|
//
|
|
107
107
|
// z-ref="name" Element reference → this.refs.name.
|
|
108
108
|
//
|
|
@@ -134,9 +134,9 @@ export function safeEval(expr: string, scope: object[]): any;
|
|
|
134
134
|
//
|
|
135
135
|
// ─── Slot System ────────────────────────────────────────────────────────
|
|
136
136
|
//
|
|
137
|
-
// <slot> Default slot
|
|
137
|
+
// <slot> Default slot - replaced with child content
|
|
138
138
|
// passed by the parent component.
|
|
139
|
-
// <slot name="header"> Named slot
|
|
139
|
+
// <slot name="header"> Named slot - replaced with child content that
|
|
140
140
|
// has a matching slot="header" attribute.
|
|
141
141
|
// <slot>fallback</slot> Fallback content shown when no slot content provided.
|
|
142
142
|
//
|
package/types/reactive.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Reactive primitives
|
|
2
|
+
* Reactive primitives - deep proxies and signals.
|
|
3
3
|
*
|
|
4
4
|
* @module reactive
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
/** Marker properties added to every reactive proxy. */
|
|
8
8
|
export interface ReactiveProxy<T extends object = object> {
|
|
9
|
-
/** Always `true`
|
|
9
|
+
/** Always `true` - indicates this object is wrapped in a reactive Proxy. */
|
|
10
10
|
readonly __isReactive: true;
|
|
11
11
|
/** The original un-proxied object. */
|
|
12
12
|
readonly __raw: T;
|
|
@@ -65,7 +65,7 @@ export function computed<T>(fn: () => T): Signal<T>;
|
|
|
65
65
|
* Re-runs automatically when any tracked signal changes.
|
|
66
66
|
*
|
|
67
67
|
* @param fn The effect function. Executed immediately on creation, then on signal changes.
|
|
68
|
-
* @returns A dispose function
|
|
68
|
+
* @returns A dispose function - calling it stops tracking and prevents re-runs.
|
|
69
69
|
*
|
|
70
70
|
* @example
|
|
71
71
|
* const count = signal(0);
|
|
@@ -74,3 +74,25 @@ export function computed<T>(fn: () => T): Signal<T>;
|
|
|
74
74
|
* stop(); // effect no longer runs
|
|
75
75
|
*/
|
|
76
76
|
export function effect(fn: () => void): () => void;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Batch multiple signal writes - subscribers and effects fire once at the end.
|
|
80
|
+
* Nested batches are merged into the outermost one.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const a = signal(0);
|
|
84
|
+
* const b = signal(0);
|
|
85
|
+
* batch(() => {
|
|
86
|
+
* a.value = 1;
|
|
87
|
+
* b.value = 2;
|
|
88
|
+
* }); // effects run once with both values updated
|
|
89
|
+
*/
|
|
90
|
+
export function batch(fn: () => void): void;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Execute a function without tracking signal reads as dependencies.
|
|
94
|
+
* Useful inside effects when you need to read a signal without subscribing.
|
|
95
|
+
*
|
|
96
|
+
* @returns The return value of `fn`.
|
|
97
|
+
*/
|
|
98
|
+
export function untracked<T>(fn: () => T): T;
|
package/types/router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SPA Router
|
|
2
|
+
* SPA Router - history and hash-based client-side routing.
|
|
3
3
|
*
|
|
4
4
|
* @module router
|
|
5
5
|
*/
|
|
@@ -39,7 +39,11 @@ export interface NavigationContext {
|
|
|
39
39
|
|
|
40
40
|
/** Router configuration. */
|
|
41
41
|
export interface RouterConfig {
|
|
42
|
-
/**
|
|
42
|
+
/**
|
|
43
|
+
* Outlet element where route components are rendered.
|
|
44
|
+
* If omitted, the router auto-detects a `<z-outlet>` element in the DOM.
|
|
45
|
+
* Acts as an explicit override of the default `<z-outlet>` lookup.
|
|
46
|
+
*/
|
|
43
47
|
el?: string | Element;
|
|
44
48
|
/** Routing mode (default: `'history'`; `'hash'` for file:// or hash routing). */
|
|
45
49
|
mode?: 'history' | 'hash';
|
|
@@ -86,7 +90,7 @@ export interface RouterInstance {
|
|
|
86
90
|
remove(path: string): RouterInstance;
|
|
87
91
|
|
|
88
92
|
/**
|
|
89
|
-
* Navigation guard
|
|
93
|
+
* Navigation guard - runs before each route change.
|
|
90
94
|
* Return `false` to cancel, or a `string` to redirect.
|
|
91
95
|
*/
|
|
92
96
|
beforeEach(
|
|
@@ -111,10 +115,10 @@ export interface RouterInstance {
|
|
|
111
115
|
|
|
112
116
|
/**
|
|
113
117
|
* Push a lightweight history entry for in-component UI state (modal, tab, panel).
|
|
114
|
-
* The URL does NOT change
|
|
118
|
+
* The URL does NOT change - only a history entry is added so the back button
|
|
115
119
|
* can undo the UI change before navigating away from the route.
|
|
116
|
-
* @param key
|
|
117
|
-
* @param data
|
|
120
|
+
* @param key - identifier for the substate (e.g. 'modal', 'tab')
|
|
121
|
+
* @param data - arbitrary serializable state
|
|
118
122
|
* @example
|
|
119
123
|
* router.pushSubstate('modal', { id: 'confirm-delete' });
|
|
120
124
|
*/
|