@vertz/ui 0.2.15 → 0.2.17

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 (33) hide show
  1. package/README.md +49 -0
  2. package/dist/shared/{chunk-dksg08fq.js → chunk-07bh4m1e.js} +1 -1
  3. package/dist/shared/chunk-14eqne2a.js +10 -0
  4. package/dist/shared/{chunk-nn9v1zmk.js → chunk-2wtb9x81.js} +83 -20
  5. package/dist/shared/{chunk-8hsz5y4a.js → chunk-4fwcwxn6.js} +14 -4
  6. package/dist/shared/{chunk-4txc67nd.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-1wby7nex.js → chunk-dhehvmj0.js} +161 -9
  10. package/dist/shared/{chunk-wymw818z.js → chunk-fkbgbf3n.js} +48 -9
  11. package/dist/shared/{chunk-hw67ckr3.js → chunk-fs3eec4b.js} +230 -19
  12. package/dist/shared/{chunk-5dbq8jp9.js → chunk-j09yyh34.js} +72 -6
  13. package/dist/shared/chunk-mtsvrj9e.js +23 -0
  14. package/dist/shared/{chunk-j6qyxfdc.js → chunk-vndfjfdy.js} +3 -3
  15. package/dist/src/auth/public.d.ts +40 -24
  16. package/dist/src/auth/public.js +110 -52
  17. package/dist/src/css/public.d.ts +110 -2
  18. package/dist/src/css/public.js +8 -4
  19. package/dist/src/form/public.d.ts +29 -6
  20. package/dist/src/form/public.js +2 -2
  21. package/dist/src/index.d.ts +284 -13
  22. package/dist/src/index.js +160 -14
  23. package/dist/src/internals.d.ts +168 -5
  24. package/dist/src/internals.js +14 -8
  25. package/dist/src/jsx-runtime/index.d.ts +5 -0
  26. package/dist/src/jsx-runtime/index.js +8 -1
  27. package/dist/src/query/public.js +4 -3
  28. package/dist/src/router/public.d.ts +17 -4
  29. package/dist/src/router/public.js +16 -11
  30. package/dist/src/test/index.d.ts +5 -0
  31. package/dist/src/test/index.js +4 -3
  32. package/package.json +3 -3
  33. package/reactivity.json +1 -11
@@ -1,14 +1,17 @@
1
1
  import {
2
2
  executeLoaders,
3
3
  matchRoute
4
- } from "./chunk-83g4h38e.js";
4
+ } from "./chunk-6wd36w21.js";
5
5
  import {
6
6
  prefetchNavData
7
7
  } from "./chunk-jrtrk5z4.js";
8
+ import {
9
+ isBrowser
10
+ } from "./chunk-14eqne2a.js";
8
11
  import {
9
12
  getSSRContext,
10
13
  signal
11
- } from "./chunk-8hsz5y4a.js";
14
+ } from "./chunk-4fwcwxn6.js";
12
15
 
13
16
  // src/router/navigate.ts
14
17
  var DEFAULT_NAV_THRESHOLD_MS = 500;
@@ -59,24 +62,34 @@ function buildSearch(search) {
59
62
  function buildNavigationUrl(to, options) {
60
63
  return `${interpolatePath(to, options?.params)}${buildSearch(options?.search)}`;
61
64
  }
62
- function createRouter(routes, initialUrl, options) {
65
+ function createRouter(routes, initialUrlOrOptions, maybeOptions) {
66
+ const initialUrl = typeof initialUrlOrOptions === "string" ? initialUrlOrOptions : undefined;
67
+ const options = typeof initialUrlOrOptions === "object" ? initialUrlOrOptions : maybeOptions;
63
68
  const ssrCtx = getSSRContext();
64
- const isSSR = ssrCtx !== undefined;
65
- if (isSSR || typeof window === "undefined") {
69
+ if (!isBrowser()) {
70
+ let registerRoutesForDiscovery = function(ctx) {
71
+ if (!ctx.discoveredRoutes) {
72
+ ctx.discoveredRoutes = collectRoutePatterns(routes);
73
+ }
74
+ };
66
75
  const ssrUrl = initialUrl ?? ssrCtx?.url ?? "/";
67
76
  const fallbackMatch = matchRoute(routes, ssrUrl);
68
77
  return {
69
78
  current: {
70
79
  get value() {
71
80
  const ctx = getSSRContext();
72
- if (ctx)
81
+ if (ctx) {
82
+ registerRoutesForDiscovery(ctx);
73
83
  return matchRoute(routes, ctx.url);
84
+ }
74
85
  return fallbackMatch;
75
86
  },
76
87
  peek() {
77
88
  const ctx = getSSRContext();
78
- if (ctx)
89
+ if (ctx) {
90
+ registerRoutesForDiscovery(ctx);
79
91
  return matchRoute(routes, ctx.url);
92
+ }
80
93
  return fallbackMatch;
81
94
  },
82
95
  notify() {}
@@ -129,8 +142,12 @@ function createRouter(routes, initialUrl, options) {
129
142
  const current = {
130
143
  get value() {
131
144
  const ctx = getSSRContext();
132
- if (ctx)
145
+ if (ctx) {
146
+ if (!ctx.discoveredRoutes) {
147
+ ctx.discoveredRoutes = collectRoutePatterns(routes);
148
+ }
133
149
  return matchRoute(routes, ctx.url);
150
+ }
134
151
  return _current.value;
135
152
  },
136
153
  set value(v) {
@@ -138,8 +155,12 @@ function createRouter(routes, initialUrl, options) {
138
155
  },
139
156
  peek() {
140
157
  const ctx = getSSRContext();
141
- if (ctx)
158
+ if (ctx) {
159
+ if (!ctx.discoveredRoutes) {
160
+ ctx.discoveredRoutes = collectRoutePatterns(routes);
161
+ }
142
162
  return matchRoute(routes, ctx.url);
163
+ }
143
164
  return _current.peek();
144
165
  },
145
166
  notify() {
@@ -298,5 +319,23 @@ function createRouter(routes, initialUrl, options) {
298
319
  searchParams
299
320
  };
300
321
  }
322
+ function collectRoutePatterns(routes, prefix = "") {
323
+ const patterns = [];
324
+ for (const route of routes) {
325
+ const fullPattern = joinPatterns(prefix, route.pattern);
326
+ patterns.push(fullPattern);
327
+ if (route.children) {
328
+ patterns.push(...collectRoutePatterns(route.children, fullPattern));
329
+ }
330
+ }
331
+ return patterns;
332
+ }
333
+ function joinPatterns(parent, child) {
334
+ if (!parent || parent === "/")
335
+ return child;
336
+ if (child === "/")
337
+ return parent;
338
+ return `${parent.replace(/\/$/, "")}/${child.replace(/^\//, "")}`;
339
+ }
301
340
 
302
341
  export { createRouter };
@@ -4,7 +4,10 @@ import {
4
4
  import {
5
5
  getAdapter,
6
6
  isRenderNode
7
- } from "./chunk-h89w580h.js";
7
+ } from "./chunk-afawz764.js";
8
+ import {
9
+ isBrowser
10
+ } from "./chunk-14eqne2a.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,7 +1,7 @@
1
1
  import {
2
2
  computed,
3
3
  signal
4
- } from "./chunk-8hsz5y4a.js";
4
+ } from "./chunk-4fwcwxn6.js";
5
5
 
6
6
  // src/form/field-state.ts
7
7
  function createFieldState(_name, initialValue) {
@@ -31,14 +31,40 @@ function createFieldState(_name, initialValue) {
31
31
  function formDataToObject(formData, options) {
32
32
  const result = {};
33
33
  const coerce = options?.coerce ?? false;
34
+ const nested = options?.nested ?? false;
34
35
  for (const [key, value] of formData.entries()) {
35
36
  if (typeof value !== "string") {
36
37
  continue;
37
38
  }
38
- result[key] = coerce ? coerceValue(value) : value;
39
+ const coerced = coerce ? coerceValue(value) : value;
40
+ if (nested && key.includes(".")) {
41
+ setNestedValue(result, key, coerced);
42
+ } else {
43
+ result[key] = coerced;
44
+ }
39
45
  }
40
46
  return result;
41
47
  }
48
+ var DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
49
+ function setNestedValue(obj, dotPath, value) {
50
+ const segments = dotPath.split(".");
51
+ let current = obj;
52
+ for (let i = 0;i < segments.length - 1; i++) {
53
+ const segment = segments[i];
54
+ if (DANGEROUS_KEYS.has(segment))
55
+ return;
56
+ const nextSegment = segments[i + 1];
57
+ const isNextArray = /^\d+$/.test(nextSegment);
58
+ if (!(segment in current)) {
59
+ current[segment] = isNextArray ? [] : {};
60
+ }
61
+ current = current[segment];
62
+ }
63
+ const lastSegment = segments[segments.length - 1];
64
+ if (DANGEROUS_KEYS.has(lastSegment))
65
+ return;
66
+ current[lastSegment] = value;
67
+ }
42
68
  function coerceValue(value) {
43
69
  if (value === "true")
44
70
  return true;
@@ -79,8 +105,11 @@ function validate(schema, data) {
79
105
  }
80
106
 
81
107
  // src/form/form.ts
108
+ var FIELD_STATE_SIGNALS = new Set(["error", "dirty", "touched", "value"]);
109
+ var FIELD_STATE_METHODS = new Set(["setValue", "reset"]);
82
110
  function form(sdkMethod, options) {
83
111
  const fieldCache = new Map;
112
+ const chainProxyCache = new Map;
84
113
  const submitting = signal(false);
85
114
  const fieldGeneration = signal(0);
86
115
  const dirty = computed(() => {
@@ -99,20 +128,57 @@ function form(sdkMethod, options) {
99
128
  }
100
129
  return true;
101
130
  });
131
+ function resolveNestedInitial(dotPath) {
132
+ const initialObj = typeof options?.initial === "function" ? options.initial() : options?.initial;
133
+ if (!initialObj)
134
+ return;
135
+ const segments = dotPath.split(".");
136
+ let current = initialObj;
137
+ for (const segment of segments) {
138
+ if (current == null || typeof current !== "object")
139
+ return;
140
+ current = current[segment];
141
+ }
142
+ return current;
143
+ }
102
144
  function getOrCreateField(name) {
103
145
  let field = fieldCache.get(name);
104
146
  if (!field) {
105
- const initialObj = typeof options?.initial === "function" ? options.initial() : options?.initial;
106
- const initialValue = initialObj?.[name];
147
+ const initialValue = name.includes(".") ? resolveNestedInitial(name) : (() => {
148
+ const initialObj = typeof options?.initial === "function" ? options.initial() : options?.initial;
149
+ return initialObj?.[name];
150
+ })();
107
151
  field = createFieldState(name, initialValue);
108
152
  fieldCache.set(name, field);
109
153
  fieldGeneration.value++;
110
154
  }
111
155
  return field;
112
156
  }
157
+ function getOrCreateChainProxy(dotPath) {
158
+ let proxy = chainProxyCache.get(dotPath);
159
+ if (proxy)
160
+ return proxy;
161
+ proxy = new Proxy(Object.create(null), {
162
+ get(_target, prop) {
163
+ if (typeof prop === "string") {
164
+ if (FIELD_STATE_SIGNALS.has(prop)) {
165
+ return getOrCreateField(dotPath)[prop];
166
+ }
167
+ if (FIELD_STATE_METHODS.has(prop)) {
168
+ return getOrCreateField(dotPath)[prop];
169
+ }
170
+ const childPath = `${dotPath}.${prop}`;
171
+ return getOrCreateChainProxy(childPath);
172
+ }
173
+ return;
174
+ }
175
+ });
176
+ chainProxyCache.set(dotPath, proxy);
177
+ return proxy;
178
+ }
113
179
  const resolvedSchema = options?.schema ?? sdkMethod.meta?.bodySchema;
114
180
  async function submitPipeline(formData) {
115
- const data = formDataToObject(formData);
181
+ const data = formDataToObject(formData, { nested: true });
116
182
  if (resolvedSchema) {
117
183
  const result2 = validate(resolvedSchema, data);
118
184
  if (!result2.success) {
@@ -227,7 +293,7 @@ function form(sdkMethod, options) {
227
293
  if (knownProperties.has(prop)) {
228
294
  return target[prop];
229
295
  }
230
- return getOrCreateField(prop);
296
+ return getOrCreateChainProxy(prop);
231
297
  }
232
298
  return Reflect.get(target, prop, receiver);
233
299
  }