sibujs 4.6.0 → 4.8.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 (94) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +62 -8
  3. package/dist/browser.d.cts +23 -0
  4. package/dist/browser.d.ts +23 -0
  5. package/dist/browser.js +6 -5
  6. package/dist/build.cjs +2388 -1004
  7. package/dist/build.d.cts +147 -44
  8. package/dist/build.d.ts +147 -44
  9. package/dist/build.js +1410 -564
  10. package/dist/cdn.dev.global.js +14 -12
  11. package/dist/cdn.full.dev.global.js +15 -13
  12. package/dist/cdn.full.global.js +14 -12
  13. package/dist/cdn.global.js +12 -10
  14. package/dist/chunk-3ACAAQIN.js +224 -0
  15. package/dist/{chunk-7NCARGJW.js → chunk-44MTL7ED.js} +6 -6
  16. package/dist/{chunk-BAAG6ZTI.js → chunk-4WMSTZ7J.js} +8 -5
  17. package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
  18. package/dist/{chunk-DKGBBKOF.js → chunk-C5PEWEGH.js} +20 -5
  19. package/dist/{chunk-3ISI6ACU.js → chunk-C7AA2CZX.js} +1 -1
  20. package/dist/{chunk-UZQ6ALFS.js → chunk-CZPRSWSS.js} +1 -1
  21. package/dist/{chunk-D33YTSX5.js → chunk-DSID757X.js} +5 -4
  22. package/dist/{chunk-OXUY2A6L.js → chunk-EGUQLBHE.js} +22 -8
  23. package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
  24. package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
  25. package/dist/{chunk-HYXDKS4N.js → chunk-GSGNX35Q.js} +5 -5
  26. package/dist/{chunk-7GQCWFOE.js → chunk-J3YQ457H.js} +171 -85
  27. package/dist/{chunk-ORMZXBKQ.js → chunk-JUTFX2RM.js} +18 -10
  28. package/dist/{chunk-QE4TTDU3.js → chunk-KJ3UKABJ.js} +12 -11
  29. package/dist/{chunk-XRRZKZYX.js → chunk-LI4W3U6C.js} +1 -1
  30. package/dist/{chunk-47M47FOM.js → chunk-O6TRVMJI.js} +58 -6
  31. package/dist/{chunk-JSXPZCET.js → chunk-Q5J2LVKH.js} +1 -1
  32. package/dist/{chunk-5MT6SJ3P.js → chunk-QGMSESDD.js} +10 -5
  33. package/dist/{chunk-AIF3Z2T7.js → chunk-RGLPEBDE.js} +13 -10
  34. package/dist/{chunk-5Q4R7HCL.js → chunk-TQQUN4WR.js} +11 -2
  35. package/dist/{chunk-PK6FK2G2.js → chunk-UCNP3HVM.js} +183 -30
  36. package/dist/{chunk-XC4MEKGA.js → chunk-V3LHZSU5.js} +8 -2
  37. package/dist/{chunk-VVWPJ543.js → chunk-VWT3VXPL.js} +55 -20
  38. package/dist/{chunk-SXXVZMKZ.js → chunk-ZVLHNEEL.js} +17 -35
  39. package/dist/{contracts-DRIuclVT.d.cts → contracts-CLqzJnOV.d.ts} +28 -17
  40. package/dist/{contracts-DRIuclVT.d.ts → contracts-CTOJXu-x.d.cts} +28 -17
  41. package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
  42. package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
  43. package/dist/data.cjs +205 -39
  44. package/dist/data.d.cts +119 -3
  45. package/dist/data.d.ts +119 -3
  46. package/dist/data.js +6 -6
  47. package/dist/devtools.cjs +18 -5
  48. package/dist/devtools.js +5 -5
  49. package/dist/ecosystem.cjs +462 -58
  50. package/dist/ecosystem.d.cts +2 -1
  51. package/dist/ecosystem.d.ts +2 -1
  52. package/dist/ecosystem.js +9 -8
  53. package/dist/extras.cjs +852 -296
  54. package/dist/extras.d.cts +5 -4
  55. package/dist/extras.d.ts +5 -4
  56. package/dist/extras.js +24 -23
  57. package/dist/index.cjs +721 -176
  58. package/dist/index.d.cts +220 -169
  59. package/dist/index.d.ts +220 -169
  60. package/dist/index.js +16 -12
  61. package/dist/motion.cjs +12 -4
  62. package/dist/motion.js +3 -3
  63. package/dist/patterns.cjs +37 -21
  64. package/dist/patterns.d.cts +2 -1
  65. package/dist/patterns.d.ts +2 -1
  66. package/dist/patterns.js +6 -6
  67. package/dist/performance.cjs +48 -6
  68. package/dist/performance.d.cts +12 -2
  69. package/dist/performance.d.ts +12 -2
  70. package/dist/performance.js +8 -6
  71. package/dist/plugins.cjs +672 -261
  72. package/dist/plugins.d.cts +131 -13
  73. package/dist/plugins.d.ts +131 -13
  74. package/dist/plugins.js +83 -47
  75. package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
  76. package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.cts} +6 -0
  77. package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.ts} +6 -0
  78. package/dist/ssr.cjs +509 -142
  79. package/dist/ssr.d.cts +55 -5
  80. package/dist/ssr.d.ts +55 -5
  81. package/dist/ssr.js +11 -9
  82. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  83. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  84. package/dist/testing.cjs +17 -4
  85. package/dist/testing.js +4 -4
  86. package/dist/types-CJFViL6Q.d.cts +26 -0
  87. package/dist/types-CJFViL6Q.d.ts +26 -0
  88. package/dist/ui.cjs +635 -254
  89. package/dist/ui.d.cts +3 -2
  90. package/dist/ui.d.ts +3 -2
  91. package/dist/ui.js +9 -8
  92. package/dist/widgets.cjs +22 -14
  93. package/dist/widgets.js +7 -7
  94. package/package.json +8 -5
@@ -1,14 +1,20 @@
1
+ import {
2
+ ContextualRefusal,
3
+ contextualAttributeRefusal,
4
+ settleAttributeProvenance
5
+ } from "./chunk-3ACAAQIN.js";
1
6
  import {
2
7
  isEventHandlerAttr,
3
8
  isHtmlContentAttribute,
4
- sanitizeAttributeString
5
- } from "./chunk-2INLLLMZ.js";
9
+ resolveAttributeValue
10
+ } from "./chunk-EXXSN3BW.js";
6
11
  import {
7
12
  reactiveBinding
8
- } from "./chunk-5Q4R7HCL.js";
13
+ } from "./chunk-TQQUN4WR.js";
9
14
  import {
10
15
  DEV,
11
16
  devWarn,
17
+ devWarnLazy,
12
18
  reportError
13
19
  } from "./chunk-NYNYSPK7.js";
14
20
 
@@ -21,6 +27,19 @@ var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selecte
21
27
  function isHtmlElement(el) {
22
28
  return el.namespaceURI === null || el.namespaceURI === HTML_NS;
23
29
  }
30
+ function warnContextualRefusal(label, name, refusal) {
31
+ devWarnLazy(() => {
32
+ const reasons = [
33
+ "",
34
+ 'the element would become a <meta http-equiv="refresh"> directive the shared refresh policy forbids (a destination outside the URL allowlist, or a directive too malformed to read unambiguously)',
35
+ "a <meta> whose http-equiv or content is reactive may never carry a refresh directive: a browser schedules the navigation as soon as the directive is valid, and nothing can withdraw it when the state changes back",
36
+ "a runtime value may not choose the program a <script> runs; name the script in static template source, or load it through Head({ script }) as an explicit trust decision",
37
+ "an SVG animation may not target a URL, event-handler or nested-document attribute; its to / values would be written into that attribute without passing the URL policy",
38
+ 'a runtime-chosen href may not become an applied <link rel="stylesheet">: a well-formed URL is not a trusted stylesheet. Name the stylesheet in static template source, or load a runtime-chosen one through Head({ link }) as an explicit trust decision'
39
+ ];
40
+ return `${label}: refusing "${name}" in this element's context \u2014 ${reasons[refusal]}. The write was not performed.`;
41
+ });
42
+ }
24
43
  function setProp(el, key, val) {
25
44
  el[key] = val;
26
45
  }
@@ -32,6 +51,11 @@ function namespaceFor(el, name) {
32
51
  return null;
33
52
  }
34
53
  function setSafeAttribute(el, name, value, options = {}) {
54
+ const written = commitSafeAttribute(el, name, value, options);
55
+ if (el.localName === "link") settleAttributeProvenance(el, name, options.reactive ? "reactive" : "runtime");
56
+ return written;
57
+ }
58
+ function commitSafeAttribute(el, name, value, options) {
35
59
  const ns = namespaceFor(el, name);
36
60
  const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
37
61
  if (isEventHandlerAttr(name)) {
@@ -44,6 +68,14 @@ function setSafeAttribute(el, name, value, options = {}) {
44
68
  else el.removeAttribute(name);
45
69
  return false;
46
70
  }
71
+ const pending = value == null || value === false ? null : value === true ? "" : String(value);
72
+ const refusal = contextualAttributeRefusal(el, name, pending, options.reactive ? "reactive" : "runtime");
73
+ if (refusal !== ContextualRefusal.None) {
74
+ warnContextualRefusal(options.label ?? "setSafeAttribute", name, refusal);
75
+ if (ns) el.removeAttributeNS(ns, localName);
76
+ else el.removeAttribute(name);
77
+ return false;
78
+ }
47
79
  if (value == null) {
48
80
  if (ns) el.removeAttributeNS(ns, localName);
49
81
  else el.removeAttribute(name);
@@ -87,13 +119,32 @@ function setSafeAttribute(el, name, value, options = {}) {
87
119
  setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
88
120
  return true;
89
121
  }
90
- const safe = sanitizeAttributeString(name, str, { element: el });
122
+ const safe = str === "" ? "" : resolveAttributeValue(name, str, { element: el });
123
+ if (safe === null) {
124
+ if (ns) el.removeAttributeNS(ns, localName);
125
+ else el.removeAttribute(name);
126
+ return true;
127
+ }
91
128
  const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
92
129
  if (current === safe) return true;
93
130
  if (ns) el.setAttributeNS(ns, name, safe);
94
131
  else el.setAttribute(name, safe);
95
132
  return true;
96
133
  }
134
+ function setTrustedAttribute(el, name, value) {
135
+ if (isHtmlContentAttribute(name)) {
136
+ if (DEV) devWarn(`html: refusing static attribute "${name}" \u2014 the browser parses it as a nested document.`);
137
+ return false;
138
+ }
139
+ const refusal = contextualAttributeRefusal(el, name, value, "static");
140
+ if (refusal !== ContextualRefusal.None) {
141
+ warnContextualRefusal("html (static attribute)", name, refusal);
142
+ return false;
143
+ }
144
+ el.setAttribute(name, value);
145
+ if (el.localName === "link") settleAttributeProvenance(el, name, "static");
146
+ return true;
147
+ }
97
148
 
98
149
  // src/reactivity/bindAttribute.ts
99
150
  function bindAttribute(el, attr, getter) {
@@ -110,7 +161,7 @@ function bindAttribute(el, attr, getter) {
110
161
  reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
111
162
  return;
112
163
  }
113
- setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
164
+ setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")`, reactive: true });
114
165
  }
115
166
  return reactiveBinding(commit, el);
116
167
  }
@@ -135,7 +186,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
135
186
  el.removeAttribute(prevName);
136
187
  prevName = null;
137
188
  }
138
- if (!setSafeAttribute(el, name, value, { label: "bindDynamic" })) return;
189
+ if (!setSafeAttribute(el, name, value, { label: "bindDynamic", reactive: true })) return;
139
190
  prevName = name;
140
191
  }
141
192
  const teardown = reactiveBinding(commit, el);
@@ -149,6 +200,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
149
200
 
150
201
  export {
151
202
  setSafeAttribute,
203
+ setTrustedAttribute,
152
204
  bindAttribute,
153
205
  bindDynamic
154
206
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SVG_NS,
3
3
  tagFactory
4
- } from "./chunk-VVWPJ543.js";
4
+ } from "./chunk-VWT3VXPL.js";
5
5
 
6
6
  // src/core/rendering/html.ts
7
7
  var html = tagFactory("html");
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  renderToString
3
- } from "./chunk-HURREPU2.js";
3
+ } from "./chunk-FC6KCJSA.js";
4
4
  import {
5
5
  globalSingleton
6
6
  } from "./chunk-7ZHH77QA.js";
7
+ import {
8
+ sanitizeUrl
9
+ } from "./chunk-EXXSN3BW.js";
7
10
  import {
8
11
  reportError
9
12
  } from "./chunk-NYNYSPK7.js";
@@ -167,7 +170,7 @@ var CDN_ENTRY_POINTS = {
167
170
  };
168
171
  var packageInfo = {
169
172
  name: "sibujs",
170
- version: true ? "4.6.0" : "dev",
173
+ version: true ? "4.8.0" : "dev",
171
174
  entryPoints: MODULE_ENTRY_POINTS,
172
175
  /**
173
176
  * Generate the package.json `exports` map. Module entries resolve to the
@@ -312,7 +315,7 @@ var env = {
312
315
  };
313
316
 
314
317
  // src/plugins/versioning.ts
315
- var VERSION = true ? "4.6.0" : "dev";
318
+ var VERSION = true ? "4.8.0" : "dev";
316
319
  var NUM = "(?:0|[1-9]\\d*)";
317
320
  var PRE_ID = `(?:${NUM}|\\d*[a-zA-Z-][0-9a-zA-Z-]*)`;
318
321
  var BUILD_ID = "[0-9a-zA-Z-]+";
@@ -595,12 +598,14 @@ function extractRangeTarget(range) {
595
598
  function preloadCritical(resources) {
596
599
  if (typeof document === "undefined") return;
597
600
  for (const resource of resources) {
598
- const safeHref = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(resource.href) : resource.href.replace(/["\\]/g, "\\$&");
601
+ const href = sanitizeUrl(resource.href);
602
+ if (!href) continue;
603
+ const safeHref = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(href) : href.replace(/["\\]/g, "\\$&");
599
604
  const existing = document.querySelector(`link[rel="preload"][href="${safeHref}"]`);
600
605
  if (existing) continue;
601
606
  const link = document.createElement("link");
602
607
  link.rel = "preload";
603
- link.href = resource.href;
608
+ link.href = href;
604
609
  link.setAttribute("as", resource.as);
605
610
  if (resource.type) {
606
611
  link.type = resource.type;
@@ -1,17 +1,17 @@
1
1
  import {
2
2
  isUnsafeKey,
3
3
  stripUnsafeKeys
4
- } from "./chunk-H3SRKIYX.js";
4
+ } from "./chunk-62BDYGAG.js";
5
5
  import {
6
6
  derived
7
- } from "./chunk-QE4TTDU3.js";
7
+ } from "./chunk-KJ3UKABJ.js";
8
8
  import {
9
9
  effect
10
- } from "./chunk-XC4MEKGA.js";
10
+ } from "./chunk-V3LHZSU5.js";
11
11
  import {
12
12
  batch,
13
13
  signal
14
- } from "./chunk-D33YTSX5.js";
14
+ } from "./chunk-DSID757X.js";
15
15
  import {
16
16
  DEV,
17
17
  devWarn,
@@ -19,6 +19,9 @@ import {
19
19
  } from "./chunk-NYNYSPK7.js";
20
20
 
21
21
  // src/patterns/machine.ts
22
+ function ownEntry(table, key) {
23
+ return Object.hasOwn(table, key) ? table[key] : void 0;
24
+ }
22
25
  function machine(config) {
23
26
  const [state, setState] = signal(config.initial);
24
27
  const [context, setContext] = signal(config.context || {});
@@ -40,9 +43,9 @@ function machine(config) {
40
43
  }
41
44
  }
42
45
  function step(event) {
43
- const stateDef = config.states[currentState];
46
+ const stateDef = ownEntry(config.states, currentState);
44
47
  if (!stateDef?.on) return;
45
- const transition = stateDef.on[event];
48
+ const transition = ownEntry(stateDef.on, event);
46
49
  if (!transition) return;
47
50
  let target;
48
51
  let guard;
@@ -70,7 +73,7 @@ function machine(config) {
70
73
  if (action) setContext(nextContext);
71
74
  setState(target);
72
75
  });
73
- const targetDef = config.states[target];
76
+ const targetDef = ownEntry(config.states, target);
74
77
  if (targetDef?.entry) {
75
78
  targetDef.entry(currentContext);
76
79
  }
@@ -80,7 +83,7 @@ function machine(config) {
80
83
  if (processing) return;
81
84
  drain();
82
85
  }
83
- const initialDef = config.states[config.initial];
86
+ const initialDef = ownEntry(config.states, config.initial);
84
87
  if (initialDef?.entry) {
85
88
  processing = true;
86
89
  try {
@@ -98,9 +101,9 @@ function machine(config) {
98
101
  }
99
102
  function can(event) {
100
103
  const currentState2 = state();
101
- const stateDef = config.states[currentState2];
104
+ const stateDef = ownEntry(config.states, currentState2);
102
105
  if (!stateDef?.on) return false;
103
- const transition = stateDef.on[event];
106
+ const transition = ownEntry(stateDef.on, event);
104
107
  if (!transition) return false;
105
108
  if (typeof transition === "string") return true;
106
109
  if (transition.guard) {
@@ -498,7 +498,7 @@ function forEachSubscriber(signal, visit) {
498
498
  }
499
499
 
500
500
  // src/reactivity/track.ts
501
- var _runtimeVersion = true ? "4.6.0" : "dev";
501
+ var _runtimeVersion = true ? "4.8.0" : "dev";
502
502
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
503
503
  function resolveReactiveApi() {
504
504
  const g = globalThis;
@@ -554,6 +554,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
554
554
  var getSubscriberDeps2 = API.getSubscriberDeps;
555
555
  var forEachSubscriber2 = API.forEachSubscriber;
556
556
 
557
+ // src/core/devtoolsHook.ts
558
+ function emitDevtools(hook, event, payload) {
559
+ try {
560
+ hook.emit(event, payload);
561
+ } catch {
562
+ }
563
+ }
564
+
557
565
  export {
558
566
  suspendTracking2 as suspendTracking,
559
567
  resumeTracking2 as resumeTracking,
@@ -570,5 +578,6 @@ export {
570
578
  notifySubscribers2 as notifySubscribers,
571
579
  getSubscriberCount2 as getSubscriberCount,
572
580
  getSubscriberDeps2 as getSubscriberDeps,
573
- forEachSubscriber2 as forEachSubscriber
581
+ forEachSubscriber2 as forEachSubscriber,
582
+ emitDevtools
574
583
  };
@@ -1,22 +1,25 @@
1
1
  import {
2
2
  derived
3
- } from "./chunk-QE4TTDU3.js";
4
- import {
5
- sanitizeUrl
6
- } from "./chunk-2INLLLMZ.js";
3
+ } from "./chunk-KJ3UKABJ.js";
7
4
  import {
8
5
  globalSingleton
9
6
  } from "./chunk-7ZHH77QA.js";
10
7
  import {
11
8
  effect
12
- } from "./chunk-XC4MEKGA.js";
9
+ } from "./chunk-V3LHZSU5.js";
10
+ import {
11
+ sanitizeUrl
12
+ } from "./chunk-EXXSN3BW.js";
13
13
  import {
14
14
  getRequestScopedCache
15
15
  } from "./chunk-7XHATCIH.js";
16
16
  import {
17
17
  batch,
18
18
  signal
19
- } from "./chunk-D33YTSX5.js";
19
+ } from "./chunk-DSID757X.js";
20
+ import {
21
+ untracked
22
+ } from "./chunk-TQQUN4WR.js";
20
23
  import {
21
24
  reportError
22
25
  } from "./chunk-NYNYSPK7.js";
@@ -132,7 +135,87 @@ function waitForRetryDelay(delay, signal2) {
132
135
  });
133
136
  }
134
137
 
138
+ // src/data/structuralSharing.ts
139
+ function isPlainObject(value) {
140
+ if (value === null || typeof value !== "object") return false;
141
+ const proto = Object.getPrototypeOf(value);
142
+ return proto === Object.prototype || proto === null;
143
+ }
144
+ function isPlainArray(value) {
145
+ return Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
146
+ }
147
+ function reconcile(prev, next, depth, walk, newRoot) {
148
+ if (Object.is(prev, next)) return prev;
149
+ const arrays = isPlainArray(prev) && isPlainArray(next);
150
+ if (!arrays && !(isPlainObject(prev) && isPlainObject(next))) return next;
151
+ if (Object.getPrototypeOf(prev) !== Object.getPrototypeOf(next)) return next;
152
+ const nextObject = next;
153
+ const seenAt = walk.path.get(nextObject);
154
+ if (seenAt !== void 0) {
155
+ walk.cycleTo = Math.min(walk.cycleTo, seenAt);
156
+ return next;
157
+ }
158
+ const prevRecord = prev;
159
+ const nextRecord = next;
160
+ const nextKeys = arrays ? Array.from({ length: next.length }, (_, i) => i) : Object.keys(nextRecord);
161
+ const prevSize = arrays ? prev.length : Object.keys(prevRecord).length;
162
+ const copy = arrays ? new Array(nextKeys.length) : Object.create(Object.getPrototypeOf(next));
163
+ let allFromPrev = prevSize === nextKeys.length;
164
+ let allFromNext = true;
165
+ const outerCycleTo = walk.cycleTo;
166
+ walk.cycleTo = Number.POSITIVE_INFINITY;
167
+ walk.path.set(nextObject, depth);
168
+ for (const key of nextKeys) {
169
+ if (arrays && !Object.hasOwn(nextRecord, key)) {
170
+ if (Object.hasOwn(prevRecord, key)) allFromPrev = false;
171
+ continue;
172
+ }
173
+ const nextChild = nextRecord[key];
174
+ const hasPrev = Object.hasOwn(prevRecord, key);
175
+ const shared = hasPrev ? reconcile(prevRecord[key], nextChild, depth + 1, walk, false) : nextChild;
176
+ if (key === "__proto__") {
177
+ Object.defineProperty(copy, key, { value: shared, writable: true, enumerable: true, configurable: true });
178
+ } else {
179
+ copy[key] = shared;
180
+ }
181
+ if (!hasPrev || !Object.is(shared, prevRecord[key])) allFromPrev = false;
182
+ if (!Object.is(shared, nextChild)) allFromNext = false;
183
+ }
184
+ walk.path.delete(nextObject);
185
+ const reached = walk.cycleTo;
186
+ walk.cycleTo = Math.min(outerCycleTo, reached < depth ? reached : Number.POSITIVE_INFINITY);
187
+ if (reached <= depth) return next;
188
+ if (allFromPrev && !newRoot) return prev;
189
+ if (allFromNext) return next;
190
+ return copy;
191
+ }
192
+ function applyStructuralSharing(option, prev, next, explicit = false) {
193
+ if (option === false || prev === void 0) return next;
194
+ const forceNew = explicit && !Object.is(prev, next);
195
+ if (typeof option === "function") {
196
+ const shared = option(prev, next);
197
+ return forceNew && Object.is(shared, prev) ? next : shared;
198
+ }
199
+ return reconcile(prev, next, 0, { path: /* @__PURE__ */ new Map(), cycleTo: Number.POSITIVE_INFINITY }, forceNew);
200
+ }
201
+
135
202
  // src/data/query.ts
203
+ function shareSafely(option, prev, next, explicit) {
204
+ const shared = untracked(
205
+ () => runSelect("query structuralSharing", () => applyStructuralSharing(option, prev, next, explicit))
206
+ );
207
+ return shared.ok ? shared.value : next;
208
+ }
209
+ function sharedView(entry) {
210
+ const raw = entry.data;
211
+ if (raw === void 0) return void 0;
212
+ if (entry.hasShared && Object.is(entry.sharedFrom, raw)) return entry.shared;
213
+ const value = shareSafely(true, entry.hasShared ? entry.shared : void 0, raw, entry.explicit);
214
+ entry.shared = value;
215
+ entry.sharedFrom = raw;
216
+ entry.hasShared = true;
217
+ return value;
218
+ }
136
219
  var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
137
220
  function getActiveQueryCache() {
138
221
  return getRequestScopedCache("query") ?? globalQueryCache;
@@ -158,7 +241,11 @@ function getOrCreateEntry(cache, key, initialData) {
158
241
  listeners: /* @__PURE__ */ new Set(),
159
242
  refetchers: /* @__PURE__ */ new Set(),
160
243
  controller: null,
161
- generation: 0
244
+ generation: 0,
245
+ explicit: false,
246
+ shared: void 0,
247
+ sharedFrom: void 0,
248
+ hasShared: false
162
249
  };
163
250
  cache.set(key, entry);
164
251
  }
@@ -177,13 +264,24 @@ function query(key, fetcher, options = {}) {
177
264
  onSuccess,
178
265
  onError,
179
266
  onSettled,
180
- select
267
+ select,
268
+ structuralSharing = true
181
269
  } = options;
182
270
  const resolveKey = typeof key === "function" ? key : () => key;
183
271
  const cache = getActiveQueryCache();
184
272
  const [data, setData] = signal(initialData);
185
273
  const [isFetching, setIsFetching] = signal(false);
186
274
  const [error, setError] = signal(void 0);
275
+ const [updatedAt, setUpdatedAt] = signal(0);
276
+ const [base, setBase] = signal(void 0);
277
+ let baseEntry = null;
278
+ let baseValue;
279
+ let baseExplicit = false;
280
+ let customEntry = null;
281
+ let customFrom;
282
+ let customValue;
283
+ let selectedEntry = null;
284
+ let selectedValue;
187
285
  let disposed = false;
188
286
  let currentKey = null;
189
287
  let attachedEntry = null;
@@ -192,11 +290,57 @@ function query(key, fetcher, options = {}) {
192
290
  const loading = derived(() => isFetching() && data() === void 0);
193
291
  const isStale = derived(() => {
194
292
  data();
293
+ updatedAt();
195
294
  if (!currentKey) return true;
196
295
  const entry = cache.get(currentKey);
197
296
  if (!entry || entry.dataUpdatedAt === 0) return true;
198
297
  return Date.now() - entry.dataUpdatedAt >= staleTime;
199
298
  });
299
+ function viewOf(entry) {
300
+ const raw = entry.data;
301
+ if (raw === void 0) return void 0;
302
+ if (structuralSharing === false) return raw;
303
+ if (typeof structuralSharing !== "function") return sharedView(entry);
304
+ const sameEntry = customEntry === entry;
305
+ if (sameEntry && Object.is(customFrom, raw)) return customValue;
306
+ const value = shareSafely(structuralSharing, sameEntry ? customValue : void 0, raw, entry.explicit);
307
+ customEntry = entry;
308
+ customFrom = raw;
309
+ customValue = value;
310
+ return value;
311
+ }
312
+ function commitBase(entry, value) {
313
+ const changed = !Object.is(value, baseValue);
314
+ baseEntry = entry;
315
+ baseValue = value;
316
+ if (!select) {
317
+ setData(value);
318
+ return;
319
+ }
320
+ if (!changed) return;
321
+ baseExplicit = entry?.explicit === true;
322
+ setBase(value);
323
+ }
324
+ const stopSelect = select ? effect(() => {
325
+ const raw = base();
326
+ const entry = baseEntry;
327
+ const explicit = baseExplicit;
328
+ baseExplicit = false;
329
+ if (entry === null && selectedEntry === null) return;
330
+ if (raw === void 0) {
331
+ selectedEntry = entry;
332
+ selectedValue = void 0;
333
+ setData(void 0);
334
+ return;
335
+ }
336
+ const selected = runSelect("query select", () => select(raw));
337
+ if (!selected.ok) return;
338
+ const prev = selectedEntry === entry ? selectedValue : void 0;
339
+ const value = shareSafely(structuralSharing, prev, selected.value, explicit);
340
+ selectedEntry = entry;
341
+ selectedValue = value;
342
+ setData(value);
343
+ }) : null;
200
344
  function detachFromEntry() {
201
345
  const entry = attachedEntry;
202
346
  const key2 = attachedKey;
@@ -255,7 +399,7 @@ function query(key, fetcher, options = {}) {
255
399
  const settledError = entry.error;
256
400
  runCallback("query onError", () => onError?.(settledError));
257
401
  } else if (entry.data !== void 0) {
258
- const settledData = entry.data;
402
+ const settledData = viewOf(entry);
259
403
  runCallback("query onSuccess", () => onSuccess?.(settledData));
260
404
  }
261
405
  runCallback("query onSettled", () => onSettled?.());
@@ -286,17 +430,14 @@ function query(key, fetcher, options = {}) {
286
430
  entry.promise = null;
287
431
  entry.controller = null;
288
432
  entry.data = result;
433
+ entry.explicit = false;
289
434
  entry.dataUpdatedAt = Date.now();
290
435
  entry.error = void 0;
291
436
  notifyListeners(entry.listeners, "query cache listener");
292
437
  if (disposed || currentKey !== key2) return;
293
- const selected = runSelect("query select", () => select ? select(result) : result);
294
- batch(() => {
295
- if (selected.ok) setData(selected.value);
296
- setIsFetching(false);
297
- setError(void 0);
298
- });
299
- runCallback("query onSuccess", () => onSuccess?.(result));
438
+ if (!entry.listeners.has(onCacheUpdate)) onCacheUpdate();
439
+ const committedResult = viewOf(entry);
440
+ runCallback("query onSuccess", () => onSuccess?.(committedResult));
300
441
  } catch (err) {
301
442
  if (entry.generation !== generation) return;
302
443
  entry.promise = null;
@@ -326,18 +467,19 @@ function query(key, fetcher, options = {}) {
326
467
  const entry = cache.get(currentKey);
327
468
  if (!entry) {
328
469
  batch(() => {
329
- setData(void 0);
470
+ commitBase(null, void 0);
330
471
  setError(void 0);
331
472
  setIsFetching(false);
473
+ setUpdatedAt(0);
332
474
  });
333
475
  return;
334
476
  }
335
- const raw = entry.data;
336
- const selected = raw !== void 0 && select ? runSelect("query select", () => select(raw)) : { ok: true, value: raw };
477
+ const view = viewOf(entry);
337
478
  batch(() => {
338
- if (selected.ok) setData(selected.value);
479
+ commitBase(entry, view);
339
480
  setError(entry.error);
340
481
  if (!entry.promise) setIsFetching(false);
482
+ setUpdatedAt(entry.dataUpdatedAt);
341
483
  });
342
484
  }
343
485
  const effectCleanup = effect(() => {
@@ -347,11 +489,11 @@ function query(key, fetcher, options = {}) {
347
489
  const entry = getOrCreateEntry(cache, key2, initialData);
348
490
  attachToEntry(entry, key2);
349
491
  if (entry.data !== void 0) {
350
- const raw = entry.data;
351
- const selected = runSelect("query select", () => select ? select(raw) : raw);
492
+ const view = viewOf(entry);
352
493
  batch(() => {
353
- if (selected.ok) setData(selected.value);
494
+ commitBase(entry, view);
354
495
  setError(entry.error);
496
+ setUpdatedAt(entry.dataUpdatedAt);
355
497
  });
356
498
  }
357
499
  if (!keyChanged && currentKey === key2 && entry.data !== void 0) {
@@ -389,6 +531,7 @@ function query(key, fetcher, options = {}) {
389
531
  if (disposed) return;
390
532
  disposed = true;
391
533
  effectCleanup();
534
+ stopSelect?.();
392
535
  loading.dispose();
393
536
  isStale.dispose();
394
537
  if (intervalTimer) clearInterval(intervalTimer);
@@ -420,13 +563,15 @@ function invalidateQueries(keyOrPredicate) {
420
563
  }
421
564
  }
422
565
  function getQueryData(key) {
423
- return getActiveQueryCache().get(key)?.data;
566
+ const entry = getActiveQueryCache().get(key);
567
+ return entry ? sharedView(entry) : void 0;
424
568
  }
425
569
  function setQueryData(key, data) {
426
570
  const entry = getActiveQueryCache().get(key);
427
571
  if (!entry) return;
428
- const newData = typeof data === "function" ? data(entry.data) : data;
572
+ const newData = typeof data === "function" ? data(sharedView(entry)) : data;
429
573
  entry.data = newData;
574
+ entry.explicit = true;
430
575
  entry.dataUpdatedAt = Date.now();
431
576
  notifyListeners(entry.listeners, "query cache listener");
432
577
  }
@@ -810,6 +955,15 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
810
955
  const [loading, setLoading] = signal(false);
811
956
  const [error, setError] = signal(void 0);
812
957
  let currentData = options.initialValue;
958
+ function commitData(next, explicit) {
959
+ const sharing = options.structuralSharing ?? true;
960
+ const shared = untracked(
961
+ () => runSelect("resource structuralSharing", () => applyStructuralSharing(sharing, currentData, next, explicit))
962
+ );
963
+ currentData = shared.ok ? shared.value : next;
964
+ setData(currentData);
965
+ return currentData;
966
+ }
813
967
  let abortController = null;
814
968
  let disposed = false;
815
969
  let effectCleanup = null;
@@ -829,12 +983,12 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
829
983
  try {
830
984
  const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
831
985
  if (version !== fetchVersion || disposed) return;
832
- currentData = result;
986
+ let committed = result;
833
987
  batch(() => {
834
- setData(result);
988
+ committed = commitData(result, false);
835
989
  setLoading(false);
836
990
  });
837
- runCallback("resource onSuccess", () => options.onSuccess?.(result));
991
+ runCallback("resource onSuccess", () => options.onSuccess?.(committed));
838
992
  } catch (err) {
839
993
  if (version !== fetchVersion || disposed) return;
840
994
  if (isAbortError(err)) {
@@ -868,8 +1022,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
868
1022
  refetch: () => doFetch(source ? source() : void 0),
869
1023
  mutate: (value) => {
870
1024
  const newValue = typeof value === "function" ? value(currentData) : value;
871
- currentData = newValue;
872
- setData(newValue);
1025
+ commitData(newValue, true);
873
1026
  },
874
1027
  abort: () => abortController?.abort(),
875
1028
  dispose: () => {
@@ -3,9 +3,10 @@ import {
3
3
  } from "./chunk-7XHATCIH.js";
4
4
  import {
5
5
  cleanup,
6
+ emitDevtools,
6
7
  retrack,
7
8
  untracked
8
- } from "./chunk-5Q4R7HCL.js";
9
+ } from "./chunk-TQQUN4WR.js";
9
10
  import {
10
11
  devAssert,
11
12
  reportError
@@ -142,12 +143,17 @@ function effect(effectFn, options) {
142
143
  try {
143
144
  retrack(ctx.bodyFn, ctx.subscriber);
144
145
  if (ctx.rerunPending) drainReruns(ctx);
146
+ } catch (err) {
147
+ ctx.disposed = true;
148
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
149
+ cleanup(ctx.subscriber);
150
+ throw err;
145
151
  } finally {
146
152
  ctx.running = false;
147
153
  ctx.rerunPending = false;
148
154
  }
149
155
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
150
- if (hook) hook.emit("effect:create", { effectFn });
156
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
151
157
  return () => disposeEffect(ctx);
152
158
  }
153
159