sibujs 4.6.0 → 4.7.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.
Files changed (79) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +18 -5
  3. package/dist/browser.js +4 -4
  4. package/dist/build.cjs +1956 -1007
  5. package/dist/build.d.cts +133 -44
  6. package/dist/build.d.ts +133 -44
  7. package/dist/build.js +1368 -569
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-SXXVZMKZ.js → chunk-2BPG2XDA.js} +5 -5
  13. package/dist/{chunk-BAAG6ZTI.js → chunk-3QWBSL5R.js} +4 -4
  14. package/dist/{chunk-5MT6SJ3P.js → chunk-4AWA2PVD.js} +2 -2
  15. package/dist/{chunk-JSXPZCET.js → chunk-7LUJQAOJ.js} +1 -1
  16. package/dist/{chunk-3ISI6ACU.js → chunk-BC2SECJD.js} +1 -1
  17. package/dist/{chunk-DKGBBKOF.js → chunk-BSY63EM6.js} +2 -2
  18. package/dist/{chunk-7NCARGJW.js → chunk-DKXRACVN.js} +3 -3
  19. package/dist/{chunk-47M47FOM.js → chunk-FNJXNGYZ.js} +1 -1
  20. package/dist/{chunk-D33YTSX5.js → chunk-FQRUXCEE.js} +5 -4
  21. package/dist/{chunk-5Q4R7HCL.js → chunk-JWKYU5GV.js} +11 -2
  22. package/dist/{chunk-QE4TTDU3.js → chunk-RUSSKG6G.js} +12 -11
  23. package/dist/{chunk-UZQ6ALFS.js → chunk-SLM3IA34.js} +1 -1
  24. package/dist/{chunk-XRRZKZYX.js → chunk-UNWRJRKC.js} +1 -1
  25. package/dist/{chunk-VVWPJ543.js → chunk-UOL2ECCS.js} +40 -11
  26. package/dist/{chunk-ORMZXBKQ.js → chunk-VZSG24LS.js} +3 -3
  27. package/dist/{chunk-XC4MEKGA.js → chunk-WEQ3DMVL.js} +8 -2
  28. package/dist/{chunk-PK6FK2G2.js → chunk-WOLJZUFQ.js} +176 -27
  29. package/dist/{chunk-7GQCWFOE.js → chunk-XYV3EDB7.js} +143 -74
  30. package/dist/{chunk-AIF3Z2T7.js → chunk-XZR4PXRE.js} +3 -3
  31. package/dist/{chunk-HYXDKS4N.js → chunk-Z3OHK6QT.js} +4 -4
  32. package/dist/{chunk-OXUY2A6L.js → chunk-ZVL7TY4K.js} +2 -2
  33. package/dist/{contracts-DRIuclVT.d.ts → contracts-CLqzJnOV.d.ts} +28 -17
  34. package/dist/{contracts-DRIuclVT.d.cts → contracts-CTOJXu-x.d.cts} +28 -17
  35. package/dist/data.cjs +198 -39
  36. package/dist/data.d.cts +119 -3
  37. package/dist/data.d.ts +119 -3
  38. package/dist/data.js +5 -5
  39. package/dist/devtools.cjs +18 -5
  40. package/dist/devtools.js +4 -4
  41. package/dist/ecosystem.cjs +64 -23
  42. package/dist/ecosystem.d.cts +2 -1
  43. package/dist/ecosystem.d.ts +2 -1
  44. package/dist/ecosystem.js +7 -7
  45. package/dist/extras.cjs +236 -49
  46. package/dist/extras.d.cts +3 -2
  47. package/dist/extras.d.ts +3 -2
  48. package/dist/extras.js +20 -20
  49. package/dist/index.cjs +259 -108
  50. package/dist/index.d.cts +193 -168
  51. package/dist/index.d.ts +193 -168
  52. package/dist/index.js +10 -10
  53. package/dist/motion.cjs +12 -4
  54. package/dist/motion.js +3 -3
  55. package/dist/patterns.cjs +28 -15
  56. package/dist/patterns.d.cts +2 -1
  57. package/dist/patterns.d.ts +2 -1
  58. package/dist/patterns.js +5 -5
  59. package/dist/performance.cjs +18 -5
  60. package/dist/performance.js +4 -4
  61. package/dist/plugins.cjs +118 -34
  62. package/dist/plugins.d.cts +124 -11
  63. package/dist/plugins.d.ts +124 -11
  64. package/dist/plugins.js +68 -26
  65. package/dist/ssr.cjs +54 -13
  66. package/dist/ssr.js +7 -7
  67. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  68. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  69. package/dist/testing.cjs +15 -2
  70. package/dist/testing.js +2 -2
  71. package/dist/types-CJFViL6Q.d.cts +26 -0
  72. package/dist/types-CJFViL6Q.d.ts +26 -0
  73. package/dist/ui.cjs +28 -15
  74. package/dist/ui.d.cts +2 -1
  75. package/dist/ui.d.ts +2 -1
  76. package/dist/ui.js +7 -7
  77. package/dist/widgets.cjs +22 -14
  78. package/dist/widgets.js +6 -6
  79. package/package.json +4 -2
@@ -1,8 +1,15 @@
1
+ import { C as Component } from './types-CJFViL6Q.cjs';
2
+
1
3
  /**
2
4
  * Higher-order component utilities for SibuJS.
3
5
  * These functions wrap or compose components to add behavior.
6
+ *
7
+ * Each helper is generic over the wrapped component's root type `R`, so a
8
+ * component returning `div()` keeps its `HTMLDivElement` root and an SVG
9
+ * component works as well. `R` defaults to `HTMLElement`, the type these
10
+ * helpers used before, for callers that pass the type arguments explicitly.
4
11
  */
5
- type Component<P = unknown> = (props: P) => HTMLElement;
12
+
6
13
  /**
7
14
  * Wraps a component with additional behavior that runs before/after rendering.
8
15
  *
@@ -18,7 +25,7 @@ type Component<P = unknown> = (props: P) => HTMLElement;
18
25
  * });
19
26
  * ```
20
27
  */
21
- declare function withWrapper<P>(WrappedComponent: Component<P>, wrapper: (component: Component<P>, props: P) => HTMLElement): Component<P>;
28
+ declare function withWrapper<P, R extends Node = HTMLElement, W extends Node = R>(WrappedComponent: Component<P, R>, wrapper: (component: Component<P, R>, props: P) => W): Component<P, W>;
22
29
  /**
23
30
  * Adds default props to a component. Missing props are filled from defaults.
24
31
  *
@@ -38,9 +45,9 @@ declare function withWrapper<P>(WrappedComponent: Component<P>, wrapper: (compon
38
45
  * optional status.
39
46
  */
40
47
  type WithDefaultsProps<P, D> = Omit<P, keyof D> & Partial<Pick<P, Extract<keyof P, keyof D>>>;
41
- declare function withDefaults<P extends object, const D extends Partial<P> = Partial<P>>(component: Component<P>, defaults: D & {
48
+ declare function withDefaults<P extends object, const D extends Partial<P> = Partial<P>, R extends Node = HTMLElement>(component: Component<P, R>, defaults: D & {
42
49
  [K in Exclude<keyof D, keyof P>]: never;
43
- }): Component<WithDefaultsProps<P, D>>;
50
+ }): Component<WithDefaultsProps<P, D>, R>;
44
51
  /**
45
52
  * Composes multiple HOC wrappers into a single wrapper.
46
53
  * Applied from right to left (like function composition).
@@ -54,7 +61,7 @@ declare function withDefaults<P extends object, const D extends Partial<P> = Par
54
61
  * const EnhancedPage = enhance(Page);
55
62
  * ```
56
63
  */
57
- declare function compose(...wrappers: Array<(component: Component) => Component>): (component: Component) => Component;
64
+ declare function compose<C extends Component<never, Node> = Component<unknown, HTMLElement>>(...wrappers: Array<(component: C) => C>): (component: C) => C;
58
65
 
59
66
  /**
60
67
  * RenderProp implements the render-prop pattern.
@@ -83,6 +90,7 @@ declare function createSlots(slots: Record<string, () => HTMLElement | HTMLEleme
83
90
  * Provides utilities to define typed components with prop defaults,
84
91
  * nodes slots, and prop mapping — all with full TypeScript inference.
85
92
  */
93
+
86
94
  /**
87
95
  * Extract the props type from a component defined with defineComponent.
88
96
  *
@@ -94,7 +102,7 @@ declare function createSlots(slots: Record<string, () => HTMLElement | HTMLEleme
94
102
  * type ButtonProps = ComponentProps<typeof Button>; // { label: string }
95
103
  * ```
96
104
  */
97
- type ComponentProps<T> = T extends (props: infer P) => HTMLElement ? P : never;
105
+ type ComponentProps<T> = T extends (props: infer P) => Node ? P : never;
98
106
  /**
99
107
  * Props that include an optional nodes slot.
100
108
  */
@@ -104,11 +112,14 @@ type WithNodes<Props> = Props & {
104
112
  /**
105
113
  * Define a typed component with props inference, defaults, and a setup function.
106
114
  *
107
- * The `setup` function receives merged props (defaults + provided) and must
108
- * return an HTMLElement. TypeScript infers the full props type from the generic.
115
+ * The `setup` function receives merged props (defaults + provided) and
116
+ * returns the component's root. TypeScript infers the full props type from the
117
+ * generic. `R` is the root type; it defaults to `HTMLElement` (every HTML tag
118
+ * factory's result fits) — pass it explicitly for an SVG root
119
+ * (`defineComponent<Props, SVGSVGElement>`).
109
120
  *
110
121
  * @param config Component configuration with optional defaults and a setup function
111
- * @returns A component function that accepts props and returns an HTMLElement
122
+ * @returns A component function that accepts props and returns the root
112
123
  *
113
124
  * @example
114
125
  * ```ts
@@ -125,10 +136,10 @@ type WithNodes<Props> = Props & {
125
136
  * // Usage: Button({ label: 'Click me' }) — TypeScript infers props
126
137
  * ```
127
138
  */
128
- declare function defineComponent<Props extends object>(config: {
139
+ declare function defineComponent<Props extends object, R extends Node = HTMLElement>(config: {
129
140
  defaults?: Partial<Props>;
130
- setup: (props: Props) => HTMLElement;
131
- }): (props: Props) => HTMLElement;
141
+ setup: (props: Props) => R;
142
+ }): Component<Props, R>;
132
143
  /**
133
144
  * Create a component with nodes slot support.
134
145
  *
@@ -137,7 +148,7 @@ declare function defineComponent<Props extends object>(config: {
137
148
  * wraps arbitrary child content.
138
149
  *
139
150
  * @param config Component configuration with optional defaults and a setup function
140
- * @returns A component function that accepts props (including nodes) and returns an HTMLElement
151
+ * @returns A component function that accepts props (including nodes) and returns the root
141
152
  *
142
153
  * @example
143
154
  * ```ts
@@ -156,10 +167,10 @@ declare function defineComponent<Props extends object>(config: {
156
167
  * // Usage: Card({ title: 'Hello', nodes: p('World') })
157
168
  * ```
158
169
  */
159
- declare function defineSlottedComponent<Props extends object>(config: {
170
+ declare function defineSlottedComponent<Props extends object, R extends Node = HTMLElement>(config: {
160
171
  defaults?: Partial<Props>;
161
- setup: (props: WithNodes<Props>) => HTMLElement;
162
- }): (props: WithNodes<Props>) => HTMLElement;
172
+ setup: (props: WithNodes<Props>) => R;
173
+ }): Component<WithNodes<Props>, R>;
163
174
  /**
164
175
  * Higher-order helper to create a component that maps outer props to inner props.
165
176
  *
@@ -185,7 +196,7 @@ declare function defineSlottedComponent<Props extends object>(config: {
185
196
  * // Usage: SmallIconButton({ icon: 'star', label: 'Favorite' })
186
197
  * ```
187
198
  */
188
- declare function withProps<OuterProps extends object, InnerProps extends object>(component: (props: InnerProps) => HTMLElement, mapProps: (outer: OuterProps) => InnerProps): (props: OuterProps) => HTMLElement;
199
+ declare function withProps<OuterProps extends object, InnerProps extends object, R extends Node = HTMLElement>(component: Component<InnerProps, R>, mapProps: (outer: OuterProps) => InnerProps): Component<OuterProps, R>;
189
200
 
190
201
  /**
191
202
  * Runtime prop validation and strict typing contracts for SibuJS.
package/dist/data.cjs CHANGED
@@ -660,7 +660,7 @@ function forEachSubscriber(signal2, visit) {
660
660
  }
661
661
 
662
662
  // src/reactivity/track.ts
663
- var _runtimeVersion = true ? "4.6.0" : "dev";
663
+ var _runtimeVersion = true ? "4.7.0" : "dev";
664
664
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
665
665
  function resolveReactiveApi() {
666
666
  const g = globalThis;
@@ -716,6 +716,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
716
716
  var getSubscriberDeps2 = API.getSubscriberDeps;
717
717
  var forEachSubscriber2 = API.forEachSubscriber;
718
718
 
719
+ // src/core/devtoolsHook.ts
720
+ function emitDevtools(hook, event, payload) {
721
+ try {
722
+ hook.emit(event, payload);
723
+ } catch {
724
+ }
725
+ }
726
+
719
727
  // src/core/signals/derived.ts
720
728
  function derived(getter, options) {
721
729
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
@@ -742,17 +750,17 @@ function derived(getter, options) {
742
750
  cs._d = false;
743
751
  cs._init = true;
744
752
  };
745
- track2(() => {
746
- let threw = true;
747
- try {
753
+ try {
754
+ track2(() => {
748
755
  cs._v = getter();
749
756
  cs._d = false;
750
757
  cs._init = true;
751
- threw = false;
752
- } finally {
753
- if (threw) cs._d = true;
754
- }
755
- }, markDirty);
758
+ }, markDirty);
759
+ } catch (err) {
760
+ disposed = true;
761
+ cleanup2(markDirty);
762
+ throw err;
763
+ }
756
764
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
757
765
  const validate = () => {
758
766
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -771,7 +779,7 @@ function derived(getter, options) {
771
779
  if (disposed) cleanup2(markDirty);
772
780
  }
773
781
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
774
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
782
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
775
783
  }
776
784
  };
777
785
  cs._validate = validate;
@@ -822,7 +830,7 @@ function derived(getter, options) {
822
830
  }
823
831
  }
824
832
  };
825
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
833
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
826
834
  return computedGetter;
827
835
  }
828
836
 
@@ -991,12 +999,17 @@ function effect(effectFn, options) {
991
999
  try {
992
1000
  retrack2(ctx.bodyFn, ctx.subscriber);
993
1001
  if (ctx.rerunPending) drainReruns(ctx);
1002
+ } catch (err) {
1003
+ ctx.disposed = true;
1004
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1005
+ cleanup2(ctx.subscriber);
1006
+ throw err;
994
1007
  } finally {
995
1008
  ctx.running = false;
996
1009
  ctx.rerunPending = false;
997
1010
  }
998
1011
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
999
- if (hook) hook.emit("effect:create", { effectFn });
1012
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
1000
1013
  return () => disposeEffect(ctx);
1001
1014
  }
1002
1015
 
@@ -1083,7 +1096,7 @@ function signal(initial, options) {
1083
1096
  state2.__v++;
1084
1097
  if (DEV) {
1085
1098
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1086
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1099
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1087
1100
  }
1088
1101
  if (!enqueueBatchedSignal(state2)) {
1089
1102
  notifySubscribers2(state2);
@@ -1097,7 +1110,7 @@ function signal(initial, options) {
1097
1110
  state2.value = newValue;
1098
1111
  state2.__v++;
1099
1112
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1100
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1113
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
1101
1114
  if (!enqueueBatchedSignal(state2)) {
1102
1115
  notifySubscribers2(state2);
1103
1116
  }
@@ -1116,7 +1129,7 @@ function signal(initial, options) {
1116
1129
  }
1117
1130
  if (DEV) {
1118
1131
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1119
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
1132
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
1120
1133
  }
1121
1134
  return [get, set];
1122
1135
  }
@@ -1238,7 +1251,83 @@ function waitForRetryDelay(delay, signal2) {
1238
1251
  });
1239
1252
  }
1240
1253
 
1254
+ // src/data/structuralSharing.ts
1255
+ function isPlainObject(value) {
1256
+ if (value === null || typeof value !== "object") return false;
1257
+ const proto = Object.getPrototypeOf(value);
1258
+ return proto === Object.prototype || proto === null;
1259
+ }
1260
+ function isPlainArray(value) {
1261
+ return Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
1262
+ }
1263
+ function reconcile(prev, next, depth, walk, newRoot) {
1264
+ if (Object.is(prev, next)) return prev;
1265
+ const arrays = isPlainArray(prev) && isPlainArray(next);
1266
+ if (!arrays && !(isPlainObject(prev) && isPlainObject(next))) return next;
1267
+ if (Object.getPrototypeOf(prev) !== Object.getPrototypeOf(next)) return next;
1268
+ const nextObject = next;
1269
+ const seenAt = walk.path.get(nextObject);
1270
+ if (seenAt !== void 0) {
1271
+ walk.cycleTo = Math.min(walk.cycleTo, seenAt);
1272
+ return next;
1273
+ }
1274
+ const prevRecord = prev;
1275
+ const nextRecord = next;
1276
+ const nextKeys = arrays ? Array.from({ length: next.length }, (_, i) => i) : Object.keys(nextRecord);
1277
+ const prevSize = arrays ? prev.length : Object.keys(prevRecord).length;
1278
+ const copy = arrays ? new Array(nextKeys.length) : Object.create(Object.getPrototypeOf(next));
1279
+ let allFromPrev = prevSize === nextKeys.length;
1280
+ let allFromNext = true;
1281
+ const outerCycleTo = walk.cycleTo;
1282
+ walk.cycleTo = Number.POSITIVE_INFINITY;
1283
+ walk.path.set(nextObject, depth);
1284
+ for (const key of nextKeys) {
1285
+ if (arrays && !Object.hasOwn(nextRecord, key)) {
1286
+ if (Object.hasOwn(prevRecord, key)) allFromPrev = false;
1287
+ continue;
1288
+ }
1289
+ const nextChild = nextRecord[key];
1290
+ const hasPrev = Object.hasOwn(prevRecord, key);
1291
+ const shared = hasPrev ? reconcile(prevRecord[key], nextChild, depth + 1, walk, false) : nextChild;
1292
+ copy[key] = shared;
1293
+ if (!hasPrev || !Object.is(shared, prevRecord[key])) allFromPrev = false;
1294
+ if (!Object.is(shared, nextChild)) allFromNext = false;
1295
+ }
1296
+ walk.path.delete(nextObject);
1297
+ const reached = walk.cycleTo;
1298
+ walk.cycleTo = Math.min(outerCycleTo, reached < depth ? reached : Number.POSITIVE_INFINITY);
1299
+ if (reached <= depth) return next;
1300
+ if (allFromPrev && !newRoot) return prev;
1301
+ if (allFromNext) return next;
1302
+ return copy;
1303
+ }
1304
+ function applyStructuralSharing(option, prev, next, explicit = false) {
1305
+ if (option === false || prev === void 0) return next;
1306
+ const forceNew = explicit && !Object.is(prev, next);
1307
+ if (typeof option === "function") {
1308
+ const shared = option(prev, next);
1309
+ return forceNew && Object.is(shared, prev) ? next : shared;
1310
+ }
1311
+ return reconcile(prev, next, 0, { path: /* @__PURE__ */ new Map(), cycleTo: Number.POSITIVE_INFINITY }, forceNew);
1312
+ }
1313
+
1241
1314
  // src/data/query.ts
1315
+ function shareSafely(option, prev, next, explicit) {
1316
+ const shared = untracked2(
1317
+ () => runSelect("query structuralSharing", () => applyStructuralSharing(option, prev, next, explicit))
1318
+ );
1319
+ return shared.ok ? shared.value : next;
1320
+ }
1321
+ function sharedView(entry) {
1322
+ const raw = entry.data;
1323
+ if (raw === void 0) return void 0;
1324
+ if (entry.hasShared && Object.is(entry.sharedFrom, raw)) return entry.shared;
1325
+ const value = shareSafely(true, entry.hasShared ? entry.shared : void 0, raw, entry.explicit);
1326
+ entry.shared = value;
1327
+ entry.sharedFrom = raw;
1328
+ entry.hasShared = true;
1329
+ return value;
1330
+ }
1242
1331
  var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
1243
1332
  function getActiveQueryCache() {
1244
1333
  return getRequestScopedCache("query") ?? globalQueryCache;
@@ -1264,7 +1353,11 @@ function getOrCreateEntry(cache, key, initialData) {
1264
1353
  listeners: /* @__PURE__ */ new Set(),
1265
1354
  refetchers: /* @__PURE__ */ new Set(),
1266
1355
  controller: null,
1267
- generation: 0
1356
+ generation: 0,
1357
+ explicit: false,
1358
+ shared: void 0,
1359
+ sharedFrom: void 0,
1360
+ hasShared: false
1268
1361
  };
1269
1362
  cache.set(key, entry);
1270
1363
  }
@@ -1283,13 +1376,24 @@ function query(key, fetcher, options = {}) {
1283
1376
  onSuccess,
1284
1377
  onError,
1285
1378
  onSettled,
1286
- select
1379
+ select,
1380
+ structuralSharing = true
1287
1381
  } = options;
1288
1382
  const resolveKey = typeof key === "function" ? key : () => key;
1289
1383
  const cache = getActiveQueryCache();
1290
1384
  const [data, setData] = signal(initialData);
1291
1385
  const [isFetching, setIsFetching] = signal(false);
1292
1386
  const [error, setError] = signal(void 0);
1387
+ const [updatedAt, setUpdatedAt] = signal(0);
1388
+ const [base, setBase] = signal(void 0);
1389
+ let baseEntry = null;
1390
+ let baseValue;
1391
+ let baseExplicit = false;
1392
+ let customEntry = null;
1393
+ let customFrom;
1394
+ let customValue;
1395
+ let selectedEntry = null;
1396
+ let selectedValue;
1293
1397
  let disposed = false;
1294
1398
  let currentKey = null;
1295
1399
  let attachedEntry = null;
@@ -1298,11 +1402,57 @@ function query(key, fetcher, options = {}) {
1298
1402
  const loading = derived(() => isFetching() && data() === void 0);
1299
1403
  const isStale = derived(() => {
1300
1404
  data();
1405
+ updatedAt();
1301
1406
  if (!currentKey) return true;
1302
1407
  const entry = cache.get(currentKey);
1303
1408
  if (!entry || entry.dataUpdatedAt === 0) return true;
1304
1409
  return Date.now() - entry.dataUpdatedAt >= staleTime;
1305
1410
  });
1411
+ function viewOf(entry) {
1412
+ const raw = entry.data;
1413
+ if (raw === void 0) return void 0;
1414
+ if (structuralSharing === false) return raw;
1415
+ if (typeof structuralSharing !== "function") return sharedView(entry);
1416
+ const sameEntry = customEntry === entry;
1417
+ if (sameEntry && Object.is(customFrom, raw)) return customValue;
1418
+ const value = shareSafely(structuralSharing, sameEntry ? customValue : void 0, raw, entry.explicit);
1419
+ customEntry = entry;
1420
+ customFrom = raw;
1421
+ customValue = value;
1422
+ return value;
1423
+ }
1424
+ function commitBase(entry, value) {
1425
+ const changed = !Object.is(value, baseValue);
1426
+ baseEntry = entry;
1427
+ baseValue = value;
1428
+ if (!select) {
1429
+ setData(value);
1430
+ return;
1431
+ }
1432
+ if (!changed) return;
1433
+ baseExplicit = entry?.explicit === true;
1434
+ setBase(value);
1435
+ }
1436
+ const stopSelect = select ? effect(() => {
1437
+ const raw = base();
1438
+ const entry = baseEntry;
1439
+ const explicit = baseExplicit;
1440
+ baseExplicit = false;
1441
+ if (entry === null && selectedEntry === null) return;
1442
+ if (raw === void 0) {
1443
+ selectedEntry = entry;
1444
+ selectedValue = void 0;
1445
+ setData(void 0);
1446
+ return;
1447
+ }
1448
+ const selected = runSelect("query select", () => select(raw));
1449
+ if (!selected.ok) return;
1450
+ const prev = selectedEntry === entry ? selectedValue : void 0;
1451
+ const value = shareSafely(structuralSharing, prev, selected.value, explicit);
1452
+ selectedEntry = entry;
1453
+ selectedValue = value;
1454
+ setData(value);
1455
+ }) : null;
1306
1456
  function detachFromEntry() {
1307
1457
  const entry = attachedEntry;
1308
1458
  const key2 = attachedKey;
@@ -1361,7 +1511,7 @@ function query(key, fetcher, options = {}) {
1361
1511
  const settledError = entry.error;
1362
1512
  runCallback("query onError", () => onError?.(settledError));
1363
1513
  } else if (entry.data !== void 0) {
1364
- const settledData = entry.data;
1514
+ const settledData = viewOf(entry);
1365
1515
  runCallback("query onSuccess", () => onSuccess?.(settledData));
1366
1516
  }
1367
1517
  runCallback("query onSettled", () => onSettled?.());
@@ -1392,17 +1542,14 @@ function query(key, fetcher, options = {}) {
1392
1542
  entry.promise = null;
1393
1543
  entry.controller = null;
1394
1544
  entry.data = result;
1545
+ entry.explicit = false;
1395
1546
  entry.dataUpdatedAt = Date.now();
1396
1547
  entry.error = void 0;
1397
1548
  notifyListeners(entry.listeners, "query cache listener");
1398
1549
  if (disposed || currentKey !== key2) return;
1399
- const selected = runSelect("query select", () => select ? select(result) : result);
1400
- batch(() => {
1401
- if (selected.ok) setData(selected.value);
1402
- setIsFetching(false);
1403
- setError(void 0);
1404
- });
1405
- runCallback("query onSuccess", () => onSuccess?.(result));
1550
+ if (!entry.listeners.has(onCacheUpdate)) onCacheUpdate();
1551
+ const committedResult = viewOf(entry);
1552
+ runCallback("query onSuccess", () => onSuccess?.(committedResult));
1406
1553
  } catch (err) {
1407
1554
  if (entry.generation !== generation) return;
1408
1555
  entry.promise = null;
@@ -1432,18 +1579,19 @@ function query(key, fetcher, options = {}) {
1432
1579
  const entry = cache.get(currentKey);
1433
1580
  if (!entry) {
1434
1581
  batch(() => {
1435
- setData(void 0);
1582
+ commitBase(null, void 0);
1436
1583
  setError(void 0);
1437
1584
  setIsFetching(false);
1585
+ setUpdatedAt(0);
1438
1586
  });
1439
1587
  return;
1440
1588
  }
1441
- const raw = entry.data;
1442
- const selected = raw !== void 0 && select ? runSelect("query select", () => select(raw)) : { ok: true, value: raw };
1589
+ const view = viewOf(entry);
1443
1590
  batch(() => {
1444
- if (selected.ok) setData(selected.value);
1591
+ commitBase(entry, view);
1445
1592
  setError(entry.error);
1446
1593
  if (!entry.promise) setIsFetching(false);
1594
+ setUpdatedAt(entry.dataUpdatedAt);
1447
1595
  });
1448
1596
  }
1449
1597
  const effectCleanup = effect(() => {
@@ -1453,11 +1601,11 @@ function query(key, fetcher, options = {}) {
1453
1601
  const entry = getOrCreateEntry(cache, key2, initialData);
1454
1602
  attachToEntry(entry, key2);
1455
1603
  if (entry.data !== void 0) {
1456
- const raw = entry.data;
1457
- const selected = runSelect("query select", () => select ? select(raw) : raw);
1604
+ const view = viewOf(entry);
1458
1605
  batch(() => {
1459
- if (selected.ok) setData(selected.value);
1606
+ commitBase(entry, view);
1460
1607
  setError(entry.error);
1608
+ setUpdatedAt(entry.dataUpdatedAt);
1461
1609
  });
1462
1610
  }
1463
1611
  if (!keyChanged && currentKey === key2 && entry.data !== void 0) {
@@ -1495,6 +1643,7 @@ function query(key, fetcher, options = {}) {
1495
1643
  if (disposed) return;
1496
1644
  disposed = true;
1497
1645
  effectCleanup();
1646
+ stopSelect?.();
1498
1647
  loading.dispose();
1499
1648
  isStale.dispose();
1500
1649
  if (intervalTimer) clearInterval(intervalTimer);
@@ -1526,13 +1675,15 @@ function invalidateQueries(keyOrPredicate) {
1526
1675
  }
1527
1676
  }
1528
1677
  function getQueryData(key) {
1529
- return getActiveQueryCache().get(key)?.data;
1678
+ const entry = getActiveQueryCache().get(key);
1679
+ return entry ? sharedView(entry) : void 0;
1530
1680
  }
1531
1681
  function setQueryData(key, data) {
1532
1682
  const entry = getActiveQueryCache().get(key);
1533
1683
  if (!entry) return;
1534
- const newData = typeof data === "function" ? data(entry.data) : data;
1684
+ const newData = typeof data === "function" ? data(sharedView(entry)) : data;
1535
1685
  entry.data = newData;
1686
+ entry.explicit = true;
1536
1687
  entry.dataUpdatedAt = Date.now();
1537
1688
  notifyListeners(entry.listeners, "query cache listener");
1538
1689
  }
@@ -1916,6 +2067,15 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
1916
2067
  const [loading, setLoading] = signal(false);
1917
2068
  const [error, setError] = signal(void 0);
1918
2069
  let currentData = options.initialValue;
2070
+ function commitData(next, explicit) {
2071
+ const sharing = options.structuralSharing ?? true;
2072
+ const shared = untracked2(
2073
+ () => runSelect("resource structuralSharing", () => applyStructuralSharing(sharing, currentData, next, explicit))
2074
+ );
2075
+ currentData = shared.ok ? shared.value : next;
2076
+ setData(currentData);
2077
+ return currentData;
2078
+ }
1919
2079
  let abortController = null;
1920
2080
  let disposed = false;
1921
2081
  let effectCleanup = null;
@@ -1935,12 +2095,12 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
1935
2095
  try {
1936
2096
  const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
1937
2097
  if (version !== fetchVersion || disposed) return;
1938
- currentData = result;
2098
+ let committed = result;
1939
2099
  batch(() => {
1940
- setData(result);
2100
+ committed = commitData(result, false);
1941
2101
  setLoading(false);
1942
2102
  });
1943
- runCallback("resource onSuccess", () => options.onSuccess?.(result));
2103
+ runCallback("resource onSuccess", () => options.onSuccess?.(committed));
1944
2104
  } catch (err) {
1945
2105
  if (version !== fetchVersion || disposed) return;
1946
2106
  if (isAbortError(err)) {
@@ -1974,8 +2134,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
1974
2134
  refetch: () => doFetch(source ? source() : void 0),
1975
2135
  mutate: (value) => {
1976
2136
  const newValue = typeof value === "function" ? value(currentData) : value;
1977
- currentData = newValue;
1978
- setData(newValue);
2137
+ commitData(newValue, true);
1979
2138
  },
1980
2139
  abort: () => abortController?.abort(),
1981
2140
  dispose: () => {