@vertz/ui 0.2.14 → 0.2.16

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 (32) hide show
  1. package/README.md +49 -0
  2. package/dist/shared/chunk-14eqne2a.js +10 -0
  3. package/dist/shared/{chunk-dksg08fq.js → chunk-1rxa2fz4.js} +2 -8
  4. package/dist/shared/{chunk-8hsz5y4a.js → chunk-4fwcwxn6.js} +14 -4
  5. package/dist/shared/{chunk-hw67ckr3.js → chunk-4mtn7af6.js} +230 -19
  6. package/dist/shared/{chunk-2sth83bd.js → chunk-6jyt4ycw.js} +67 -2
  7. package/dist/shared/{chunk-83g4h38e.js → chunk-6wd36w21.js} +1 -0
  8. package/dist/shared/{chunk-h89w580h.js → chunk-afawz764.js} +1 -1
  9. package/dist/shared/{chunk-nn9v1zmk.js → chunk-b0qqqk03.js} +86 -21
  10. package/dist/shared/{chunk-c9xxsrat.js → chunk-dhehvmj0.js} +179 -10
  11. package/dist/shared/{chunk-mj7b4t40.js → chunk-fkbgbf3n.js} +98 -11
  12. package/dist/shared/{chunk-c30eg6wn.js → chunk-j09yyh34.js} +79 -6
  13. package/dist/shared/chunk-mtsvrj9e.js +23 -0
  14. package/dist/shared/chunk-pnv25zep.js +7 -0
  15. package/dist/shared/{chunk-j6qyxfdc.js → chunk-vndfjfdy.js} +3 -3
  16. package/dist/src/auth/public.d.ts +69 -9
  17. package/dist/src/auth/public.js +217 -13
  18. package/dist/src/css/public.d.ts +110 -2
  19. package/dist/src/css/public.js +8 -4
  20. package/dist/src/form/public.d.ts +33 -7
  21. package/dist/src/form/public.js +2 -2
  22. package/dist/src/index.d.ts +311 -20
  23. package/dist/src/index.js +161 -14
  24. package/dist/src/internals.d.ts +141 -5
  25. package/dist/src/internals.js +17 -9
  26. package/dist/src/query/public.js +4 -3
  27. package/dist/src/router/public.d.ts +39 -9
  28. package/dist/src/router/public.js +17 -11
  29. package/dist/src/test/index.d.ts +26 -23
  30. package/dist/src/test/index.js +5 -4
  31. package/package.json +3 -3
  32. package/reactivity.json +1 -11
package/README.md CHANGED
@@ -255,6 +255,53 @@ const compiled = compileTheme(theme);
255
255
  ThemeProvider({ theme: 'dark', children: [<App />] });
256
256
  ```
257
257
 
258
+ ### Fonts
259
+
260
+ Declare font families with `font()` and compile them into `@font-face` CSS, custom properties, and preload tags with `compileFonts()`. Only woff2 format is supported.
261
+
262
+ ```tsx
263
+ import { font, compileFonts } from '@vertz/ui/css';
264
+
265
+ const sans = font('DM Sans', {
266
+ weight: '100..1000',
267
+ src: '/fonts/dm-sans.woff2',
268
+ fallback: ['system-ui', 'sans-serif'],
269
+ });
270
+
271
+ const mono = font('JetBrains Mono', {
272
+ weight: '100..800',
273
+ src: '/fonts/jb-mono.woff2',
274
+ fallback: ['monospace'],
275
+ });
276
+
277
+ const compiled = compileFonts({ sans, mono });
278
+
279
+ // compiled.fontFaceCss — @font-face declarations
280
+ // compiled.cssVarsCss — :root { --font-sans: ...; --font-mono: ...; }
281
+ // compiled.cssVarLines — individual lines for merging into an existing :root
282
+ // compiled.preloadTags — <link rel="preload" ...> HTML tags
283
+ ```
284
+
285
+ **Multiple font files** (e.g., normal + italic):
286
+
287
+ ```tsx
288
+ const sans = font('DM Sans', {
289
+ weight: '100..1000',
290
+ src: [
291
+ { path: '/fonts/dm-sans.woff2', weight: '100..1000', style: 'normal' },
292
+ { path: '/fonts/dm-sans-italic.woff2', weight: '100..1000', style: 'italic' },
293
+ ],
294
+ fallback: ['system-ui', 'sans-serif'],
295
+ });
296
+ ```
297
+
298
+ **Font without `src`** (system font with CSS var only):
299
+
300
+ ```tsx
301
+ const system = font('system-ui', { weight: '400' });
302
+ // compileFonts({ system }) generates --font-system: 'system-ui'; but no @font-face
303
+ ```
304
+
258
305
  ---
259
306
 
260
307
  ## Forms
@@ -624,6 +671,8 @@ onMount(() => {
624
671
  | `compileTheme` | Compile a theme to CSS |
625
672
  | `ThemeProvider` | Provide a theme to descendants |
626
673
  | `globalCss` | Inject global CSS |
674
+ | `font` | Declare a font family descriptor |
675
+ | `compileFonts` | Compile font descriptors into CSS, vars, and preload tags |
627
676
 
628
677
  ### Forms
629
678
 
@@ -0,0 +1,10 @@
1
+ import {
2
+ hasSSRResolver
3
+ } from "./chunk-4fwcwxn6.js";
4
+
5
+ // src/env/is-browser.ts
6
+ function isBrowser() {
7
+ return typeof window !== "undefined" && !hasSSRResolver();
8
+ }
9
+
10
+ export { isBrowser };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  domEffect
3
- } from "./chunk-8hsz5y4a.js";
3
+ } from "./chunk-4fwcwxn6.js";
4
4
 
5
5
  // src/dom/attributes.ts
6
6
  function __attr(el, name, fn) {
@@ -39,10 +39,4 @@ function __classList(el, classMap) {
39
39
  };
40
40
  }
41
41
 
42
- // src/dom/events.ts
43
- function __on(el, event, handler) {
44
- el.addEventListener(event, handler);
45
- return () => el.removeEventListener(event, handler);
46
- }
47
-
48
- export { __attr, __show, __classList, __on };
42
+ export { __attr, __show, __classList };
@@ -1,10 +1,20 @@
1
1
  // src/ssr/ssr-render-context.ts
2
- var _ssrResolver = null;
2
+ var RESOLVER_KEY = "__VERTZ_SSR_RESOLVER__";
3
+ function getResolver() {
4
+ return globalThis[RESOLVER_KEY] ?? null;
5
+ }
3
6
  function registerSSRResolver(resolver) {
4
- _ssrResolver = resolver;
7
+ if (resolver === null) {
8
+ delete globalThis[RESOLVER_KEY];
9
+ } else {
10
+ globalThis[RESOLVER_KEY] = resolver;
11
+ }
5
12
  }
6
13
  function getSSRContext() {
7
- return _ssrResolver?.();
14
+ return getResolver()?.();
15
+ }
16
+ function hasSSRResolver() {
17
+ return getResolver() !== null;
8
18
  }
9
19
 
10
20
  // src/component/context.ts
@@ -472,4 +482,4 @@ function lifecycleEffect(fn) {
472
482
  return dispose;
473
483
  }
474
484
 
475
- export { registerSSRResolver, getSSRContext, createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
485
+ export { registerSSRResolver, getSSRContext, hasSSRResolver, createContext, useContext, getContextScope, setContextScope, DisposalScopeError, onCleanup, _tryOnCleanup, pushScope, popScope, runCleanups, setReadValueCallback, untrack, batch, startSignalCollection, stopSignalCollection, signal, computed, domEffect, lifecycleEffect };
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  isNavPrefetchActive
3
3
  } from "./chunk-jrtrk5z4.js";
4
+ import {
5
+ isBrowser
6
+ } from "./chunk-14eqne2a.js";
4
7
  import {
5
8
  getAdapter,
6
9
  isRenderNode
7
- } from "./chunk-h89w580h.js";
10
+ } from "./chunk-afawz764.js";
8
11
  import {
9
12
  _tryOnCleanup,
10
13
  batch,
@@ -18,22 +21,89 @@ import {
18
21
  setReadValueCallback,
19
22
  signal,
20
23
  untrack
21
- } from "./chunk-8hsz5y4a.js";
24
+ } from "./chunk-4fwcwxn6.js";
22
25
 
23
26
  // src/query/cache.ts
24
27
  class MemoryCache {
25
28
  _store = new Map;
29
+ _maxSize;
30
+ _refs = new Map;
31
+ _orphans = new Map;
32
+ constructor(options) {
33
+ const raw = options?.maxSize ?? 1000;
34
+ this._maxSize = Number.isNaN(raw) ? 1000 : Math.max(0, raw);
35
+ }
26
36
  get(key) {
27
- return this._store.get(key);
37
+ if (!this._store.has(key))
38
+ return;
39
+ const value = this._store.get(key);
40
+ this._store.delete(key);
41
+ this._store.set(key, value);
42
+ return value;
28
43
  }
29
44
  set(key, value) {
45
+ if (this._store.has(key))
46
+ this._store.delete(key);
30
47
  this._store.set(key, value);
48
+ while (this._store.size > this._maxSize) {
49
+ const orphan = this._orphans.keys().next();
50
+ if (!orphan.done) {
51
+ if (this._store.has(orphan.value)) {
52
+ this._store.delete(orphan.value);
53
+ this._orphans.delete(orphan.value);
54
+ this._refs.delete(orphan.value);
55
+ continue;
56
+ }
57
+ this._orphans.delete(orphan.value);
58
+ this._refs.delete(orphan.value);
59
+ continue;
60
+ }
61
+ let evicted = false;
62
+ for (const k of this._store.keys()) {
63
+ if (k !== key && !this._refs.has(k)) {
64
+ this._store.delete(k);
65
+ evicted = true;
66
+ break;
67
+ }
68
+ }
69
+ if (evicted)
70
+ continue;
71
+ const oldest = this._store.keys().next();
72
+ if (oldest.done)
73
+ break;
74
+ this._store.delete(oldest.value);
75
+ this._refs.delete(oldest.value);
76
+ this._orphans.delete(oldest.value);
77
+ }
31
78
  }
32
79
  delete(key) {
33
80
  this._store.delete(key);
81
+ this._refs.delete(key);
82
+ this._orphans.delete(key);
34
83
  }
35
84
  clear() {
36
85
  this._store.clear();
86
+ this._refs.clear();
87
+ this._orphans.clear();
88
+ }
89
+ retain(key) {
90
+ const count = (this._refs.get(key) ?? 0) + 1;
91
+ this._refs.set(key, count);
92
+ this._orphans.delete(key);
93
+ }
94
+ release(key) {
95
+ const current = this._refs.get(key);
96
+ if (current === undefined)
97
+ return;
98
+ const count = current - 1;
99
+ if (count <= 0) {
100
+ this._refs.delete(key);
101
+ if (this._store.has(key)) {
102
+ this._orphans.set(key, true);
103
+ }
104
+ } else {
105
+ this._refs.set(key, count);
106
+ }
37
107
  }
38
108
  }
39
109
 
@@ -73,6 +143,93 @@ function hashString(str) {
73
143
  return (hash >>> 0).toString(36);
74
144
  }
75
145
 
146
+ // src/store/field-selection-tracker.ts
147
+ class FieldSelectionTracker {
148
+ _selectInfo = new Map;
149
+ _warned = new Set;
150
+ _onMiss;
151
+ constructor(options) {
152
+ this._onMiss = options?.onMiss;
153
+ }
154
+ registerSelect(type, id, fields, querySource) {
155
+ const key = `${type}:${id}`;
156
+ let info = this._selectInfo.get(key);
157
+ if (!info) {
158
+ info = { fields: new Set, querySources: new Set, fullFetch: false };
159
+ this._selectInfo.set(key, info);
160
+ }
161
+ for (const field of fields) {
162
+ info.fields.add(field);
163
+ }
164
+ info.querySources.add(querySource);
165
+ }
166
+ registerFullFetch(type, id) {
167
+ const key = `${type}:${id}`;
168
+ let info = this._selectInfo.get(key);
169
+ if (!info) {
170
+ info = { fields: new Set, querySources: new Set, fullFetch: true };
171
+ this._selectInfo.set(key, info);
172
+ } else {
173
+ info.fullFetch = true;
174
+ }
175
+ }
176
+ shouldWarn(type, id, field) {
177
+ const key = `${type}:${id}`;
178
+ const info = this._selectInfo.get(key);
179
+ if (!info || info.fullFetch)
180
+ return false;
181
+ return !info.fields.has(field);
182
+ }
183
+ removeEntity(type, id) {
184
+ const key = `${type}:${id}`;
185
+ this._selectInfo.delete(key);
186
+ const prefix = `${key}:`;
187
+ for (const warnKey of this._warned) {
188
+ if (warnKey.startsWith(prefix)) {
189
+ this._warned.delete(warnKey);
190
+ }
191
+ }
192
+ }
193
+ createDevProxy(entity, type, id) {
194
+ const key = `${type}:${id}`;
195
+ const info = this._selectInfo.get(key);
196
+ if (!info || info.fullFetch)
197
+ return entity;
198
+ const tracker = this;
199
+ return new Proxy(entity, {
200
+ get(target, prop, receiver) {
201
+ if (typeof prop === "string" && !INTERNAL_PROPS.has(prop) && tracker.shouldWarn(type, id, prop)) {
202
+ const warnKey = `${key}:${prop}`;
203
+ if (!tracker._warned.has(warnKey)) {
204
+ tracker._warned.add(warnKey);
205
+ const sources = [...info.querySources].join(", ");
206
+ const selectedFields = [...info.fields].sort().join(", ");
207
+ console.warn(`[vertz] Field "${prop}" was accessed on ${type}#${id} ` + `but was not in the select set.
208
+ ` + ` Query: "${sources}"
209
+ ` + ` Selected: ${selectedFields}
210
+ ` + ` Fix: use {entity.${prop}} in JSX, ` + "or add // @vertz-select-all above the query.");
211
+ tracker._onMiss?.(type, id, prop, sources);
212
+ }
213
+ }
214
+ return Reflect.get(target, prop, receiver);
215
+ }
216
+ });
217
+ }
218
+ clear() {
219
+ this._selectInfo.clear();
220
+ this._warned.clear();
221
+ }
222
+ }
223
+ var INTERNAL_PROPS = new Set([
224
+ "then",
225
+ "toJSON",
226
+ "toString",
227
+ "valueOf",
228
+ "constructor",
229
+ "$$typeof",
230
+ "__proto__"
231
+ ]);
232
+
76
233
  // src/store/relation-registry.ts
77
234
  var registry2 = new Map;
78
235
  function registerRelationSchema(entityType, schema) {
@@ -241,10 +398,14 @@ class EntityStore {
241
398
  _entities = new Map;
242
399
  _typeListeners = new Map;
243
400
  _queryIndices = new QueryResultIndex;
401
+ _fieldTracker = null;
244
402
  get queryIndices() {
245
403
  return this._queryIndices;
246
404
  }
247
405
  constructor(options) {
406
+ if (options?.devMode) {
407
+ this._fieldTracker = new FieldSelectionTracker;
408
+ }
248
409
  if (options?.initialData) {
249
410
  this.hydrate(options.initialData);
250
411
  }
@@ -261,7 +422,8 @@ class EntityStore {
261
422
  base: {},
262
423
  layers: new Map,
263
424
  refCount: 0,
264
- orphanedAt: null
425
+ orphanedAt: null,
426
+ lastPlainVisible: null
265
427
  };
266
428
  this._getOrCreateTypeMap(type).set(id, newEntry);
267
429
  return sig;
@@ -275,6 +437,7 @@ class EntityStore {
275
437
  return;
276
438
  }
277
439
  batch(() => {
440
+ this.evictOrphans();
278
441
  for (const item of items) {
279
442
  const { normalized, extracted } = normalizeEntity(type, item);
280
443
  for (const [nestedType, nestedItems] of extracted) {
@@ -286,6 +449,15 @@ class EntityStore {
286
449
  }
287
450
  });
288
451
  }
452
+ mergeWithSelect(type, data, selectOptions) {
453
+ if (this._fieldTracker) {
454
+ const items = Array.isArray(data) ? data : [data];
455
+ for (const item of items) {
456
+ this._fieldTracker.registerSelect(type, item.id, selectOptions.fields, selectOptions.querySource);
457
+ }
458
+ }
459
+ this.merge(type, data);
460
+ }
289
461
  remove(type, id) {
290
462
  const typeMap = this._entities.get(type);
291
463
  if (!typeMap?.has(id)) {
@@ -297,6 +469,7 @@ class EntityStore {
297
469
  }
298
470
  typeMap.delete(id);
299
471
  this._queryIndices.removeEntity(id);
472
+ this._fieldTracker?.removeEntity(type, id);
300
473
  this._notifyTypeChange(type);
301
474
  }
302
475
  onTypeChange(type, callback) {
@@ -368,7 +541,7 @@ class EntityStore {
368
541
  if (!entry)
369
542
  return;
370
543
  entry.layers.set(mutationId, patch);
371
- this._recomputeVisible(entry);
544
+ this._recomputeVisible(entry, type, id);
372
545
  }
373
546
  addRef(type, id) {
374
547
  const entry = this._entities.get(type)?.get(id);
@@ -391,12 +564,13 @@ class EntityStore {
391
564
  evictOrphans(maxAge = 300000) {
392
565
  const now = Date.now();
393
566
  let count = 0;
394
- for (const [_type, typeMap] of this._entities) {
567
+ for (const [type, typeMap] of this._entities) {
395
568
  for (const [id, entry] of typeMap) {
396
569
  if (entry.refCount === 0 && entry.orphanedAt !== null && now - entry.orphanedAt >= maxAge && entry.layers.size === 0) {
397
570
  entry.signal.value = undefined;
398
571
  typeMap.delete(id);
399
572
  this._queryIndices.removeEntity(id);
573
+ this._fieldTracker?.removeEntity(type, id);
400
574
  count++;
401
575
  }
402
576
  }
@@ -420,7 +594,7 @@ class EntityStore {
420
594
  if (!entry)
421
595
  return;
422
596
  entry.layers.delete(mutationId);
423
- this._recomputeVisible(entry);
597
+ this._recomputeVisible(entry, type, id);
424
598
  }
425
599
  removeOptimistic(type, id, _mutationId) {
426
600
  this.remove(type, id);
@@ -445,7 +619,7 @@ class EntityStore {
445
619
  }
446
620
  entry.base = normalized;
447
621
  entry.layers.delete(mutationId);
448
- this._recomputeVisible(entry);
622
+ this._recomputeVisible(entry, type, id);
449
623
  }
450
624
  _mergeOne(type, item) {
451
625
  const id = item.id;
@@ -455,33 +629,42 @@ class EntityStore {
455
629
  const mergedBase = shallowMerge(entry.base, item);
456
630
  if (!shallowEqual(entry.base, mergedBase)) {
457
631
  entry.base = mergedBase;
458
- this._recomputeVisible(entry);
632
+ this._recomputeVisible(entry, type, id);
459
633
  }
460
634
  } else {
461
- const newSignal = signal(item);
635
+ const value = this._wrapWithFieldProxy(item, type, id);
636
+ const newSignal = signal(value);
462
637
  const newEntry = {
463
638
  signal: newSignal,
464
639
  base: item,
465
640
  layers: new Map,
466
641
  refCount: 0,
467
- orphanedAt: null
642
+ orphanedAt: null,
643
+ lastPlainVisible: item
468
644
  };
469
645
  this._getOrCreateTypeMap(type).set(id, newEntry);
470
646
  this._notifyTypeChange(type);
471
647
  }
472
648
  }
473
- _recomputeVisible(entry) {
649
+ _recomputeVisible(entry, type, id) {
474
650
  let visible = { ...entry.base };
475
651
  for (const patch of entry.layers.values()) {
476
652
  visible = shallowMerge(visible, patch);
477
653
  }
478
- const current = entry.signal.peek();
479
- if (current == null || !shallowEqual(current, visible)) {
654
+ const lastPlain = entry.lastPlainVisible;
655
+ if (lastPlain == null || !shallowEqual(lastPlain, visible)) {
656
+ entry.lastPlainVisible = visible;
657
+ const wrapped = type && id ? this._wrapWithFieldProxy(visible, type, id) : visible;
480
658
  untrack(() => {
481
- entry.signal.value = visible;
659
+ entry.signal.value = wrapped;
482
660
  });
483
661
  }
484
662
  }
663
+ _wrapWithFieldProxy(entity, type, id) {
664
+ if (!this._fieldTracker)
665
+ return entity;
666
+ return this._fieldTracker.createDevProxy(entity, type, id);
667
+ }
485
668
  _getOrCreateTypeMap(type) {
486
669
  let typeMap = this._entities.get(type);
487
670
  if (!typeMap) {
@@ -705,6 +888,25 @@ function query(source, options = {}) {
705
888
  function getCacheKey() {
706
889
  return cacheKeyComputed.value;
707
890
  }
891
+ let currentRetainedKey = null;
892
+ const retainable = "retain" in cache && "release" in cache;
893
+ function retainKey(key) {
894
+ if (!retainable)
895
+ return;
896
+ if (currentRetainedKey === key)
897
+ return;
898
+ if (currentRetainedKey !== null) {
899
+ cache.release(currentRetainedKey);
900
+ }
901
+ cache.retain(key);
902
+ currentRetainedKey = key;
903
+ }
904
+ function releaseCurrentKey() {
905
+ if (!retainable || currentRetainedKey === null)
906
+ return;
907
+ cache.release(currentRetainedKey);
908
+ currentRetainedKey = null;
909
+ }
708
910
  function callThunkWithCapture() {
709
911
  const captured = [];
710
912
  const prevCb = setReadValueCallback((v) => captured.push(v));
@@ -782,7 +984,9 @@ function query(source, options = {}) {
782
984
  return raw;
783
985
  }) : rawData;
784
986
  if (initialData !== undefined) {
785
- cache.set(getCacheKey(), initialData);
987
+ const initKey = getCacheKey();
988
+ cache.set(initKey, initialData);
989
+ retainKey(initKey);
786
990
  }
787
991
  const ssrTimeout = options.ssrTimeout ?? getGlobalSSRTimeout() ?? 300;
788
992
  if (isSSR() && enabled && ssrTimeout !== 0 && initialData === undefined) {
@@ -829,6 +1033,7 @@ function query(source, options = {}) {
829
1033
  if (customKey) {
830
1034
  const cached = cache.get(customKey);
831
1035
  if (cached !== undefined) {
1036
+ retainKey(customKey);
832
1037
  normalizeToEntityStore(cached);
833
1038
  rawData.value = cached;
834
1039
  loading.value = false;
@@ -881,7 +1086,7 @@ function query(source, options = {}) {
881
1086
  }, ms);
882
1087
  }
883
1088
  let visibilityHandler;
884
- if (hasInterval && enabled && !isSSR() && typeof document !== "undefined") {
1089
+ if (hasInterval && enabled && isBrowser()) {
885
1090
  visibilityHandler = () => {
886
1091
  if (document.visibilityState === "hidden") {
887
1092
  intervalPaused = true;
@@ -900,6 +1105,7 @@ function query(source, options = {}) {
900
1105
  if (id !== fetchId)
901
1106
  return;
902
1107
  cache.set(key, result);
1108
+ retainKey(key);
903
1109
  normalizeToEntityStore(result);
904
1110
  rawData.value = result;
905
1111
  loading.value = false;
@@ -937,6 +1143,7 @@ function query(source, options = {}) {
937
1143
  }
938
1144
  function refetch() {
939
1145
  const key = getCacheKey();
1146
+ currentRetainedKey = null;
940
1147
  cache.delete(key);
941
1148
  getInflight().delete(key);
942
1149
  refetchTrigger.value = refetchTrigger.peek() + 1;
@@ -954,6 +1161,7 @@ function query(source, options = {}) {
954
1161
  if (customKey) {
955
1162
  const cached = untrack(() => cache.get(customKey));
956
1163
  if (cached !== undefined) {
1164
+ retainKey(customKey);
957
1165
  untrack(() => {
958
1166
  rawData.value = cached;
959
1167
  loading.value = false;
@@ -967,6 +1175,7 @@ function query(source, options = {}) {
967
1175
  const derivedKey = untrack(() => getCacheKey());
968
1176
  const cached = untrack(() => cache.get(derivedKey));
969
1177
  if (cached !== undefined) {
1178
+ retainKey(derivedKey);
970
1179
  untrack(() => {
971
1180
  rawData.value = cached;
972
1181
  loading.value = false;
@@ -1020,6 +1229,7 @@ function query(source, options = {}) {
1020
1229
  if (shouldCheckCache) {
1021
1230
  const cached = untrack(() => cache.get(key));
1022
1231
  if (cached !== undefined) {
1232
+ retainKey(key);
1023
1233
  promise.catch(() => {});
1024
1234
  untrack(() => {
1025
1235
  rawData.value = cached;
@@ -1058,13 +1268,14 @@ function query(source, options = {}) {
1058
1268
  }
1059
1269
  referencedKeys.clear();
1060
1270
  }
1271
+ releaseCurrentKey();
1061
1272
  disposeFn?.();
1062
1273
  unsubscribeBus?.();
1063
1274
  unregisterFromRegistry?.();
1064
1275
  ssrHydrationCleanup?.();
1065
1276
  clearTimeout(debounceTimer);
1066
1277
  clearTimeout(intervalTimer);
1067
- if (visibilityHandler && typeof document !== "undefined") {
1278
+ if (visibilityHandler && isBrowser()) {
1068
1279
  document.removeEventListener("visibilitychange", visibilityHandler);
1069
1280
  }
1070
1281
  fetchId++;
@@ -1209,4 +1420,4 @@ function queryMatch(queryResult, handlers) {
1209
1420
  return wrapper;
1210
1421
  }
1211
1422
 
1212
- export { MemoryCache, invalidate, deriveKey, registerRelationSchema, getRelationSchema, resetRelationSchemas_TEST_ONLY, EntityStore, QueryEnvelopeStore, getEntityStore, getQueryEnvelopeStore, getMutationEventBus, query, queryMatch };
1423
+ export { MemoryCache, invalidate, deriveKey, FieldSelectionTracker, registerRelationSchema, getRelationSchema, resetRelationSchemas_TEST_ONLY, EntityStore, QueryEnvelopeStore, getEntityStore, getQueryEnvelopeStore, getMutationEventBus, query, queryMatch };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  injectCSS
3
- } from "./chunk-c9xxsrat.js";
3
+ } from "./chunk-dhehvmj0.js";
4
4
 
5
5
  // src/dom/animation.ts
6
6
  function onAnimationsComplete(el, callback) {
@@ -479,7 +479,72 @@ function hydrate(registry) {
479
479
  autoStrategy(el, doHydrate);
480
480
  }
481
481
  }
482
+
483
+ // src/hydrate/island-hydrate.ts
484
+ var hydrationQueue = [];
485
+ var isProcessing = false;
486
+ async function processQueue() {
487
+ if (isProcessing)
488
+ return;
489
+ isProcessing = true;
490
+ while (hydrationQueue.length > 0) {
491
+ const task = hydrationQueue.shift();
492
+ await task();
493
+ }
494
+ isProcessing = false;
495
+ }
496
+ function deserializeIslandProps(container) {
497
+ const script = container.querySelector(":scope > script[data-v-island-props]");
498
+ if (!script || !script.textContent) {
499
+ return {};
500
+ }
501
+ try {
502
+ return JSON.parse(script.textContent);
503
+ } catch {
504
+ return {};
505
+ }
506
+ }
507
+ function hydrateIslands(registry) {
508
+ const elements = document.querySelectorAll("[data-v-island]");
509
+ const registryKeys = Object.keys(registry);
510
+ for (const el of elements) {
511
+ if (el.hasAttribute("data-v-hydrated"))
512
+ continue;
513
+ const islandId = el.getAttribute("data-v-island");
514
+ if (!islandId)
515
+ continue;
516
+ const loader = registry[islandId];
517
+ if (!loader) {
518
+ console.error(`[vertz] Island "${islandId}" not found in registry. Available: [${registryKeys.join(", ")}]`);
519
+ continue;
520
+ }
521
+ const props = deserializeIslandProps(el);
522
+ const doHydrate = () => {
523
+ hydrationQueue.push(async () => {
524
+ try {
525
+ const mod = await loader();
526
+ const result = mod.default(props, el);
527
+ if (result instanceof Node) {
528
+ const children = Array.from(el.childNodes);
529
+ for (const child of children) {
530
+ if (child instanceof Element && child.hasAttribute("data-v-island-props")) {
531
+ continue;
532
+ }
533
+ el.removeChild(child);
534
+ }
535
+ el.appendChild(result);
536
+ }
537
+ el.setAttribute("data-v-hydrated", "");
538
+ } catch (error) {
539
+ console.error(`[vertz] Failed to hydrate island "${islandId}":`, error);
540
+ }
541
+ });
542
+ processQueue();
543
+ };
544
+ autoStrategy(el, doHydrate);
545
+ }
546
+ }
482
547
  // src/query/index.ts
483
548
  import { isQueryDescriptor } from "@vertz/fetch";
484
549
 
485
- export { onAnimationsComplete, keyframes, ANIMATION_DURATION, ANIMATION_EASING, fadeIn, fadeOut, zoomIn, zoomOut, slideInFromTop, slideInFromBottom, slideOutToTop, slideOutToBottom, slideInFromLeft, slideInFromRight, slideOutToLeft, slideOutToRight, accordionDown, accordionUp, palettes, resolveComponent, deserializeProps, hydrate, isQueryDescriptor };
550
+ export { onAnimationsComplete, keyframes, ANIMATION_DURATION, ANIMATION_EASING, fadeIn, fadeOut, zoomIn, zoomOut, slideInFromTop, slideInFromBottom, slideOutToTop, slideOutToBottom, slideInFromLeft, slideInFromRight, slideOutToLeft, slideOutToRight, accordionDown, accordionUp, palettes, resolveComponent, deserializeProps, hydrate, hydrateIslands, isQueryDescriptor };
@@ -44,6 +44,7 @@ function defineRoutes(map) {
44
44
  loader: config.loader,
45
45
  params: config.params,
46
46
  pattern,
47
+ prerender: config.prerender,
47
48
  searchParams: config.searchParams
48
49
  };
49
50
  if (config.children) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getSSRContext
3
- } from "./chunk-8hsz5y4a.js";
3
+ } from "./chunk-4fwcwxn6.js";
4
4
 
5
5
  // src/dom/dom-adapter.ts
6
6
  function createDOMAdapter() {