sibujs 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/browser.cjs +238 -69
  2. package/dist/browser.d.cts +5 -0
  3. package/dist/browser.d.ts +5 -0
  4. package/dist/browser.js +6 -6
  5. package/dist/build.cjs +916 -292
  6. package/dist/build.js +15 -13
  7. package/dist/cdn.global.js +17 -16
  8. package/dist/chunk-2RA7SHDA.js +65 -0
  9. package/dist/chunk-2UPRY23K.js +80 -0
  10. package/dist/chunk-3JHCYHWN.js +125 -0
  11. package/dist/{chunk-VAPYJN4X.js → chunk-3LR7GLWQ.js} +93 -23
  12. package/dist/{chunk-RJ46C3CS.js → chunk-3NSGB5JN.js} +71 -20
  13. package/dist/{chunk-XUEEGU5O.js → chunk-52YJLLRO.js} +16 -4
  14. package/dist/{chunk-XHK6BDAJ.js → chunk-54EDRCEF.js} +25 -8
  15. package/dist/chunk-7JDB7I65.js +1327 -0
  16. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  17. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  18. package/dist/{chunk-BGN5ZMP4.js → chunk-GTBNNBJ6.js} +14 -2
  19. package/dist/chunk-HB24TBAF.js +121 -0
  20. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  21. package/dist/{chunk-BGTHZHJ5.js → chunk-JA6667UN.js} +188 -44
  22. package/dist/{chunk-7GRNSCFT.js → chunk-JXMMDLBY.js} +306 -183
  23. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  24. package/dist/{chunk-SFKNRVCU.js → chunk-KLRMB5ZS.js} +135 -79
  25. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  26. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  27. package/dist/{chunk-BMPL52BF.js → chunk-MIUAXB7K.js} +118 -66
  28. package/dist/{chunk-JCDUJN2F.js → chunk-ND2664SF.js} +486 -153
  29. package/dist/{chunk-VQDZK23A.js → chunk-O2MNQFLP.js} +181 -66
  30. package/dist/{chunk-NHUC2QWH.js → chunk-R73P76YZ.js} +1 -1
  31. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  32. package/dist/chunk-UCS6AMJ7.js +79 -0
  33. package/dist/{chunk-K4G4ZQNR.js → chunk-VLPPXTYG.js} +84 -38
  34. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  35. package/dist/{customElement-BL3Uo8dL.d.cts → customElement-CPfIrbvg.d.cts} +14 -10
  36. package/dist/{customElement-BL3Uo8dL.d.ts → customElement-CPfIrbvg.d.ts} +14 -10
  37. package/dist/data.cjs +410 -99
  38. package/dist/data.d.cts +20 -2
  39. package/dist/data.d.ts +20 -2
  40. package/dist/data.js +11 -9
  41. package/dist/devtools.cjs +513 -223
  42. package/dist/devtools.d.cts +1 -1
  43. package/dist/devtools.d.ts +1 -1
  44. package/dist/devtools.js +12 -6
  45. package/dist/ecosystem.cjs +475 -144
  46. package/dist/ecosystem.d.cts +9 -7
  47. package/dist/ecosystem.d.ts +9 -7
  48. package/dist/ecosystem.js +12 -11
  49. package/dist/extras.cjs +3355 -1541
  50. package/dist/extras.d.cts +9 -9
  51. package/dist/extras.d.ts +9 -9
  52. package/dist/extras.js +58 -45
  53. package/dist/index.cjs +920 -292
  54. package/dist/index.d.cts +71 -8
  55. package/dist/index.d.ts +71 -8
  56. package/dist/index.js +28 -16
  57. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  58. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  59. package/dist/motion.cjs +77 -34
  60. package/dist/motion.js +4 -4
  61. package/dist/patterns.cjs +335 -69
  62. package/dist/patterns.d.cts +11 -12
  63. package/dist/patterns.d.ts +11 -12
  64. package/dist/patterns.js +7 -7
  65. package/dist/performance.cjs +279 -108
  66. package/dist/performance.d.cts +23 -16
  67. package/dist/performance.d.ts +23 -16
  68. package/dist/performance.js +13 -8
  69. package/dist/plugin-D30wlGW5.d.cts +71 -0
  70. package/dist/plugin-D30wlGW5.d.ts +71 -0
  71. package/dist/plugins.cjs +635 -260
  72. package/dist/plugins.d.cts +10 -3
  73. package/dist/plugins.d.ts +10 -3
  74. package/dist/plugins.js +106 -38
  75. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  76. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  77. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  78. package/dist/ssr.cjs +642 -222
  79. package/dist/ssr.d.cts +26 -6
  80. package/dist/ssr.d.ts +26 -6
  81. package/dist/ssr.js +12 -11
  82. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.cts} +9 -1
  83. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.ts} +9 -1
  84. package/dist/testing.cjs +252 -63
  85. package/dist/testing.d.cts +17 -4
  86. package/dist/testing.d.ts +17 -4
  87. package/dist/testing.js +100 -44
  88. package/dist/ui.cjs +463 -137
  89. package/dist/ui.d.cts +1 -1
  90. package/dist/ui.d.ts +1 -1
  91. package/dist/ui.js +20 -17
  92. package/dist/widgets.cjs +977 -94
  93. package/dist/widgets.d.cts +104 -2
  94. package/dist/widgets.d.ts +104 -2
  95. package/dist/widgets.js +9 -7
  96. package/package.json +8 -2
  97. package/dist/chunk-32DY64NT.js +0 -282
  98. package/dist/chunk-3AIRKM3B.js +0 -1263
  99. package/dist/chunk-3ARAQO7B.js +0 -398
  100. package/dist/chunk-3CRQALYP.js +0 -877
  101. package/dist/chunk-4EI4AG32.js +0 -482
  102. package/dist/chunk-4MYMUBRS.js +0 -21
  103. package/dist/chunk-5ZYQ6KDD.js +0 -154
  104. package/dist/chunk-6BMPXPUW.js +0 -26
  105. package/dist/chunk-6HLLIF3K.js +0 -398
  106. package/dist/chunk-6LSNVCS2.js +0 -937
  107. package/dist/chunk-6SA3QQES.js +0 -61
  108. package/dist/chunk-77L6NL3X.js +0 -1097
  109. package/dist/chunk-7BF6TK55.js +0 -1097
  110. package/dist/chunk-7TQKR4PP.js +0 -294
  111. package/dist/chunk-7V26P53V.js +0 -712
  112. package/dist/chunk-AZ3ISID5.js +0 -298
  113. package/dist/chunk-B7SWRFUT.js +0 -332
  114. package/dist/chunk-BTU3TJDS.js +0 -365
  115. package/dist/chunk-BW3WT46K.js +0 -937
  116. package/dist/chunk-C6KFWOFV.js +0 -616
  117. package/dist/chunk-CHF5OHIA.js +0 -61
  118. package/dist/chunk-CHJ27IGK.js +0 -26
  119. package/dist/chunk-CMBFNA7L.js +0 -27
  120. package/dist/chunk-DAHRH4ON.js +0 -331
  121. package/dist/chunk-DKOHBI74.js +0 -924
  122. package/dist/chunk-DTCOOBMX.js +0 -725
  123. package/dist/chunk-EBGIRKQY.js +0 -616
  124. package/dist/chunk-EUZND3CB.js +0 -27
  125. package/dist/chunk-EVCZO745.js +0 -365
  126. package/dist/chunk-EWFVA3TJ.js +0 -282
  127. package/dist/chunk-F3FA4F32.js +0 -292
  128. package/dist/chunk-FGOEVHY3.js +0 -60
  129. package/dist/chunk-G3BOQPVO.js +0 -365
  130. package/dist/chunk-GCOK2LC3.js +0 -282
  131. package/dist/chunk-GJPXRJ45.js +0 -37
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-JAKHTMQU.js +0 -1000
  134. package/dist/chunk-JCI5M6U6.js +0 -956
  135. package/dist/chunk-K5ZUMYVS.js +0 -89
  136. package/dist/chunk-KQPDEVVS.js +0 -398
  137. package/dist/chunk-L6JRBDNS.js +0 -60
  138. package/dist/chunk-LA6KQEDU.js +0 -712
  139. package/dist/chunk-MB6QFH3I.js +0 -2776
  140. package/dist/chunk-MDVXJWFN.js +0 -304
  141. package/dist/chunk-MEZVEBPN.js +0 -2008
  142. package/dist/chunk-MK4ERFYL.js +0 -2249
  143. package/dist/chunk-MLKGABMK.js +0 -9
  144. package/dist/chunk-MQ5GOYPH.js +0 -2249
  145. package/dist/chunk-MYRV7VDM.js +0 -742
  146. package/dist/chunk-N6IZB6KJ.js +0 -567
  147. package/dist/chunk-NEKUBFPT.js +0 -60
  148. package/dist/chunk-NMRUZALC.js +0 -1097
  149. package/dist/chunk-NYVAC6P5.js +0 -37
  150. package/dist/chunk-NZIIMDWI.js +0 -84
  151. package/dist/chunk-OF7UZIVB.js +0 -725
  152. package/dist/chunk-P3XWXJZU.js +0 -282
  153. package/dist/chunk-P6W3STU4.js +0 -2249
  154. package/dist/chunk-PBHF5WKN.js +0 -616
  155. package/dist/chunk-PDZQY43A.js +0 -616
  156. package/dist/chunk-PTQJDMRT.js +0 -146
  157. package/dist/chunk-PZEGYCF5.js +0 -61
  158. package/dist/chunk-QBMDLBU2.js +0 -975
  159. package/dist/chunk-QWZG56ET.js +0 -2744
  160. package/dist/chunk-RQGQSLQK.js +0 -725
  161. package/dist/chunk-SDLZDHKP.js +0 -107
  162. package/dist/chunk-TDGZL5CU.js +0 -365
  163. package/dist/chunk-TNQWPPE6.js +0 -37
  164. package/dist/chunk-TSOKIX5Z.js +0 -654
  165. package/dist/chunk-UHNL42EF.js +0 -2730
  166. package/dist/chunk-UNXCEF6S.js +0 -21
  167. package/dist/chunk-V2XTI523.js +0 -347
  168. package/dist/chunk-VAU366PN.js +0 -2241
  169. package/dist/chunk-VMVDTCXB.js +0 -712
  170. package/dist/chunk-VQNQZCWJ.js +0 -61
  171. package/dist/chunk-VRW3FULF.js +0 -725
  172. package/dist/chunk-WADYRCO2.js +0 -304
  173. package/dist/chunk-WILQZRO4.js +0 -282
  174. package/dist/chunk-WR5D4EGH.js +0 -26
  175. package/dist/chunk-WUHJISPP.js +0 -298
  176. package/dist/chunk-XYU6TZOW.js +0 -182
  177. package/dist/chunk-Y6GP4QGG.js +0 -276
  178. package/dist/chunk-YECR7UIA.js +0 -347
  179. package/dist/chunk-YUTWTI4B.js +0 -654
  180. package/dist/chunk-Z65KYU7I.js +0 -26
  181. package/dist/chunk-Z6POF5YC.js +0 -975
  182. package/dist/chunk-ZBJP6WFL.js +0 -482
  183. package/dist/chunk-ZD6OAMTH.js +0 -277
  184. package/dist/chunk-ZWKZCBO6.js +0 -317
  185. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  186. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  187. package/dist/contracts-DOrhwbke.d.cts +0 -245
  188. package/dist/contracts-DOrhwbke.d.ts +0 -245
  189. package/dist/contracts-xo5ckdRP.d.cts +0 -240
  190. package/dist/contracts-xo5ckdRP.d.ts +0 -240
  191. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  192. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  193. package/dist/customElement-D2DJp_xn.d.cts +0 -313
  194. package/dist/customElement-D2DJp_xn.d.ts +0 -313
  195. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  196. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  197. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  198. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  199. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  200. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  201. package/dist/ssr-3RXHP5ES.js +0 -38
  202. package/dist/ssr-6GIMY5MX.js +0 -38
  203. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  204. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  205. package/dist/ssr-WKUPVSSK.js +0 -36
  206. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  207. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/data.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // data.ts
21
21
  var data_exports = {};
22
22
  __export(data_exports, {
23
+ __resetQueryCache: () => __resetQueryCache,
23
24
  calculateDelay: () => calculateDelay,
24
25
  clearQueryCache: () => clearQueryCache,
25
26
  debounce: () => debounce,
@@ -50,12 +51,12 @@ function isDev() {
50
51
  var _isDev = isDev();
51
52
  function devAssert(condition, message) {
52
53
  if (_isDev && !condition) {
53
- throw new Error(`[Sibu] ${message}`);
54
+ throw new Error(`[SibuJS] ${message}`);
54
55
  }
55
56
  }
56
57
  function devWarn(message) {
57
58
  if (_isDev) {
58
- console.warn(`[Sibu] ${message}`);
59
+ console.warn(`[SibuJS] ${message}`);
59
60
  }
60
61
  }
61
62
 
@@ -65,11 +66,11 @@ var subscriberStack = new Array(32);
65
66
  var stackCapacity = 32;
66
67
  var stackTop = -1;
67
68
  var currentSubscriber = null;
68
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
69
69
  var SUBS = "__s";
70
70
  var notifyDepth = 0;
71
71
  var pendingQueue = [];
72
72
  var pendingSet = /* @__PURE__ */ new Set();
73
+ var propagateStack = [];
73
74
  function safeInvoke(sub) {
74
75
  try {
75
76
  sub();
@@ -78,6 +79,15 @@ function safeInvoke(sub) {
78
79
  }
79
80
  }
80
81
  var trackingSuspended = false;
82
+ function retrack(effectFn, subscriber) {
83
+ const prev = currentSubscriber;
84
+ currentSubscriber = subscriber;
85
+ try {
86
+ effectFn();
87
+ } finally {
88
+ currentSubscriber = prev;
89
+ }
90
+ }
81
91
  function track(effectFn, subscriber) {
82
92
  if (!subscriber) subscriber = effectFn;
83
93
  cleanup(subscriber);
@@ -116,7 +126,6 @@ function recordDependency(signal2) {
116
126
  let subs = signal2[SUBS];
117
127
  if (!subs) {
118
128
  subs = /* @__PURE__ */ new Set();
119
- signalSubscribers.set(signal2, subs);
120
129
  signal2[SUBS] = subs;
121
130
  }
122
131
  subs.add(currentSubscriber);
@@ -138,17 +147,17 @@ function queueSignalNotification(signal2) {
138
147
  }
139
148
  }
140
149
  }
141
- var MAX_DRAIN_ITERATIONS = 1e3;
150
+ var maxDrainIterations = 1e5;
142
151
  function drainNotificationQueue() {
143
152
  if (notifyDepth > 0) return;
144
153
  notifyDepth++;
145
154
  try {
146
155
  let i = 0;
147
156
  while (i < pendingQueue.length) {
148
- if (i >= MAX_DRAIN_ITERATIONS) {
157
+ if (i >= maxDrainIterations) {
149
158
  if (typeof console !== "undefined") {
150
159
  console.error(
151
- `[SibuJS] Notification queue exceeded ${MAX_DRAIN_ITERATIONS} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
160
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
152
161
  );
153
162
  }
154
163
  break;
@@ -157,47 +166,52 @@ function drainNotificationQueue() {
157
166
  i++;
158
167
  }
159
168
  } finally {
160
- pendingQueue.length = 0;
161
- pendingSet.clear();
162
169
  notifyDepth--;
170
+ if (notifyDepth === 0) {
171
+ pendingQueue.length = 0;
172
+ pendingSet.clear();
173
+ }
163
174
  }
164
175
  }
165
176
  function propagateDirty(sub) {
166
177
  sub();
167
- let sig = sub._sig;
168
- while (sig) {
178
+ const rootSig = sub._sig;
179
+ if (!rootSig) return;
180
+ const stack = propagateStack;
181
+ const baseLen = stack.length;
182
+ stack.push(rootSig);
183
+ while (stack.length > baseLen) {
184
+ const sig = stack.pop();
169
185
  const first = sig.__f;
170
186
  if (first) {
171
187
  if (first._c) {
172
188
  const nSig = first._sig;
173
- nSig._d = true;
174
- sig = nSig;
175
- continue;
176
- }
177
- if (!pendingSet.has(first)) {
189
+ if (!nSig._d) {
190
+ nSig._d = true;
191
+ stack.push(nSig);
192
+ }
193
+ } else if (!pendingSet.has(first)) {
178
194
  pendingSet.add(first);
179
195
  pendingQueue.push(first);
180
196
  }
181
- break;
197
+ continue;
182
198
  }
183
199
  const subs = sig[SUBS];
184
- if (!subs) break;
185
- let nextSig;
200
+ if (!subs) continue;
186
201
  for (const s of subs) {
187
202
  if (s._c) {
188
- s();
189
203
  const nSig = s._sig;
190
- if (nSig && !nextSig) {
191
- nextSig = nSig;
192
- } else if (nSig) {
193
- propagateDirty(s);
204
+ if (nSig && !nSig._d) {
205
+ nSig._d = true;
206
+ stack.push(nSig);
207
+ } else if (!nSig) {
208
+ s();
194
209
  }
195
210
  } else if (!pendingSet.has(s)) {
196
211
  pendingSet.add(s);
197
212
  pendingQueue.push(s);
198
213
  }
199
214
  }
200
- sig = nextSig;
201
215
  }
202
216
  }
203
217
  function notifySubscribers(signal2) {
@@ -221,13 +235,23 @@ function notifySubscribers(signal2) {
221
235
  }
222
236
  let i = 0;
223
237
  while (i < pendingQueue.length) {
238
+ if (i >= maxDrainIterations) {
239
+ if (typeof console !== "undefined") {
240
+ console.error(
241
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
242
+ );
243
+ }
244
+ break;
245
+ }
224
246
  safeInvoke(pendingQueue[i]);
225
247
  i++;
226
248
  }
227
249
  } finally {
228
- pendingQueue.length = 0;
229
- pendingSet.clear();
230
250
  notifyDepth--;
251
+ if (notifyDepth === 0) {
252
+ pendingQueue.length = 0;
253
+ pendingSet.clear();
254
+ }
231
255
  }
232
256
  return;
233
257
  }
@@ -247,30 +271,48 @@ function notifySubscribers(signal2) {
247
271
  notifyDepth++;
248
272
  try {
249
273
  let directCount = 0;
274
+ let hasComputedSub = false;
250
275
  for (const sub of subs) {
276
+ if (sub._c) hasComputedSub = true;
251
277
  pendingQueue[directCount++] = sub;
252
278
  }
253
- for (let i2 = 0; i2 < directCount; i2++) {
254
- if (pendingQueue[i2]._c) {
255
- propagateDirty(pendingQueue[i2]);
279
+ if (!hasComputedSub) {
280
+ for (let i2 = 0; i2 < directCount; i2++) {
281
+ safeInvoke(pendingQueue[i2]);
256
282
  }
257
- }
258
- for (let i2 = 0; i2 < directCount; i2++) {
259
- if (!pendingQueue[i2]._c) {
260
- if (!pendingSet.has(pendingQueue[i2])) {
261
- safeInvoke(pendingQueue[i2]);
283
+ } else {
284
+ for (let i2 = 0; i2 < directCount; i2++) {
285
+ if (pendingQueue[i2]._c) {
286
+ propagateDirty(pendingQueue[i2]);
287
+ }
288
+ }
289
+ for (let i2 = 0; i2 < directCount; i2++) {
290
+ const sub = pendingQueue[i2];
291
+ if (!sub._c && !pendingSet.has(sub)) {
292
+ pendingSet.add(sub);
293
+ safeInvoke(sub);
262
294
  }
263
295
  }
264
296
  }
265
297
  let i = directCount;
266
298
  while (i < pendingQueue.length) {
299
+ if (i - directCount >= maxDrainIterations) {
300
+ if (typeof console !== "undefined") {
301
+ console.error(
302
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
303
+ );
304
+ }
305
+ break;
306
+ }
267
307
  safeInvoke(pendingQueue[i]);
268
308
  i++;
269
309
  }
270
310
  } finally {
271
- pendingQueue.length = 0;
272
- pendingSet.clear();
273
311
  notifyDepth--;
312
+ if (notifyDepth === 0) {
313
+ pendingQueue.length = 0;
314
+ pendingSet.clear();
315
+ }
274
316
  }
275
317
  }
276
318
  function cleanup(subscriber) {
@@ -281,7 +323,9 @@ function cleanup(subscriber) {
281
323
  if (subs) {
282
324
  subs.delete(subscriber);
283
325
  if (singleDep.__f === subscriber) {
284
- singleDep.__f = void 0;
326
+ singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
327
+ } else if (subs.size === 1 && singleDep.__f === void 0) {
328
+ singleDep.__f = subs.values().next().value;
285
329
  }
286
330
  }
287
331
  sub._dep = void 0;
@@ -294,7 +338,9 @@ function cleanup(subscriber) {
294
338
  if (subs) {
295
339
  subs.delete(subscriber);
296
340
  if (signal2.__f === subscriber) {
297
- signal2.__f = void 0;
341
+ signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
342
+ } else if (subs.size === 1 && signal2.__f === void 0) {
343
+ signal2.__f = subs.values().next().value;
298
344
  }
299
345
  }
300
346
  }
@@ -305,6 +351,7 @@ function cleanup(subscriber) {
305
351
  function derived(getter, options) {
306
352
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
307
353
  const debugName = options?.name;
354
+ const equals = options?.equals;
308
355
  const cs = {};
309
356
  cs._d = false;
310
357
  cs._g = getter;
@@ -315,8 +362,14 @@ function derived(getter, options) {
315
362
  markDirty._c = 1;
316
363
  markDirty._sig = cs;
317
364
  track(() => {
318
- cs._d = false;
319
- cs._v = getter();
365
+ let threw = true;
366
+ try {
367
+ cs._v = getter();
368
+ cs._d = false;
369
+ threw = false;
370
+ } finally {
371
+ if (threw) cs._d = true;
372
+ }
320
373
  }, markDirty);
321
374
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
322
375
  let evaluating = false;
@@ -329,11 +382,16 @@ function derived(getter, options) {
329
382
  if (trackingSuspended) {
330
383
  if (cs._d) {
331
384
  evaluating = true;
385
+ let threw = true;
332
386
  try {
333
- cs._d = false;
334
- cs._v = getter();
387
+ retrack(() => {
388
+ cs._v = getter();
389
+ cs._d = false;
390
+ threw = false;
391
+ }, markDirty);
335
392
  } finally {
336
393
  evaluating = false;
394
+ if (threw) cs._d = true;
337
395
  }
338
396
  }
339
397
  return cs._v;
@@ -342,13 +400,17 @@ function derived(getter, options) {
342
400
  if (cs._d) {
343
401
  const oldValue = cs._v;
344
402
  evaluating = true;
403
+ let threw = true;
345
404
  try {
346
- track(() => {
405
+ retrack(() => {
406
+ const next = getter();
407
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
347
408
  cs._d = false;
348
- cs._v = getter();
409
+ threw = false;
349
410
  }, markDirty);
350
411
  } finally {
351
412
  evaluating = false;
413
+ if (threw) cs._d = true;
352
414
  }
353
415
  if (hook && oldValue !== cs._v) {
354
416
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
@@ -366,9 +428,28 @@ function derived(getter, options) {
366
428
  }
367
429
 
368
430
  // src/core/ssr-context.ts
369
- var ssrMode = false;
431
+ var als = null;
432
+ try {
433
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
434
+ const req = Function("return typeof require==='function'?require:null")();
435
+ if (req) {
436
+ const mod = req("node:async_hooks");
437
+ als = new mod.AsyncLocalStorage();
438
+ }
439
+ }
440
+ } catch {
441
+ als = null;
442
+ }
443
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
444
+ function getSSRStore() {
445
+ if (als) {
446
+ const s = als.getStore();
447
+ if (s) return s;
448
+ }
449
+ return fallbackStore;
450
+ }
370
451
  function isSSR() {
371
- return ssrMode;
452
+ return getSSRStore().ssr;
372
453
  }
373
454
 
374
455
  // src/core/signals/effect.ts
@@ -378,26 +459,86 @@ function effect(effectFn, options) {
378
459
  if (isSSR()) return () => {
379
460
  };
380
461
  const onError = options?.onError;
462
+ let userCleanups = [];
463
+ const onCleanup = (fn) => {
464
+ userCleanups.push(fn);
465
+ };
466
+ const runUserCleanups = () => {
467
+ if (userCleanups.length === 0) return;
468
+ const list = userCleanups;
469
+ userCleanups = [];
470
+ for (let i = list.length - 1; i >= 0; i--) {
471
+ try {
472
+ list[i]();
473
+ } catch (err) {
474
+ if (typeof console !== "undefined") {
475
+ console.warn("[SibuJS effect] onCleanup threw:", err);
476
+ }
477
+ }
478
+ }
479
+ };
480
+ const invokeBody = () => effectFn(onCleanup);
381
481
  const wrappedFn = onError ? () => {
382
482
  try {
383
- effectFn();
483
+ invokeBody();
384
484
  } catch (err) {
385
485
  onError(err);
386
486
  }
387
- } : effectFn;
487
+ } : invokeBody;
388
488
  let cleanupHandle = () => {
389
489
  };
490
+ let running = false;
390
491
  const subscriber = () => {
391
- cleanupHandle();
392
- cleanupHandle = track(wrappedFn, subscriber);
492
+ if (running) {
493
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
494
+ console.warn(
495
+ "[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
496
+ );
497
+ }
498
+ return;
499
+ }
500
+ running = true;
501
+ try {
502
+ runUserCleanups();
503
+ cleanupHandle();
504
+ cleanupHandle = track(wrappedFn, subscriber);
505
+ } finally {
506
+ running = false;
507
+ }
393
508
  };
394
- cleanupHandle = track(wrappedFn, subscriber);
509
+ running = true;
510
+ try {
511
+ cleanupHandle = track(wrappedFn, subscriber);
512
+ } finally {
513
+ running = false;
514
+ }
395
515
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
396
516
  if (hook) hook.emit("effect:create", { effectFn });
517
+ let disposed = false;
397
518
  return () => {
519
+ if (disposed) return;
520
+ disposed = true;
398
521
  const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
399
- if (h) h.emit("effect:destroy", { effectFn });
400
- cleanupHandle();
522
+ if (h) {
523
+ try {
524
+ h.emit("effect:destroy", { effectFn });
525
+ } catch {
526
+ }
527
+ }
528
+ try {
529
+ runUserCleanups();
530
+ } catch (err) {
531
+ if (typeof console !== "undefined") {
532
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
533
+ }
534
+ }
535
+ try {
536
+ cleanupHandle();
537
+ } catch (err) {
538
+ if (typeof console !== "undefined") {
539
+ console.warn("[SibuJS effect] dispose threw:", err);
540
+ }
541
+ }
401
542
  };
402
543
  }
403
544
 
@@ -421,10 +562,13 @@ function enqueueBatchedSignal(signal2) {
421
562
  return true;
422
563
  }
423
564
  function flushBatch() {
424
- for (const signal2 of pendingSignals) {
425
- queueSignalNotification(signal2);
565
+ try {
566
+ for (const signal2 of pendingSignals) {
567
+ queueSignalNotification(signal2);
568
+ }
569
+ } finally {
570
+ pendingSignals.clear();
426
571
  }
427
- pendingSignals.clear();
428
572
  drainNotificationQueue();
429
573
  }
430
574
 
@@ -481,10 +625,12 @@ function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
481
625
  break;
482
626
  }
483
627
  delay = Math.min(delay, maxDelay);
628
+ if (!Number.isFinite(delay)) delay = Number.MAX_SAFE_INTEGER;
484
629
  if (jitter > 0) {
485
630
  const jitterRange = delay * jitter;
486
631
  delay += (Math.random() * 2 - 1) * jitterRange;
487
632
  }
633
+ if (!Number.isFinite(delay) || Number.isNaN(delay)) delay = 0;
488
634
  return Math.max(0, delay);
489
635
  }
490
636
  async function withRetry(fn, options, onRetry, signal2) {
@@ -498,6 +644,7 @@ async function withRetry(fn, options, onRetry, signal2) {
498
644
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
499
645
  if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
500
646
  try {
647
+ if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
501
648
  return await fn();
502
649
  } catch (error) {
503
650
  lastError = error;
@@ -579,24 +726,46 @@ function query(key, fetcher, options = {}) {
579
726
  let entry = queryCache.get(key2);
580
727
  if (!entry) {
581
728
  entry = getOrCreateEntry(key2);
582
- entry.subscribers++;
583
729
  entry.listeners.add(onCacheUpdate);
584
730
  entry.refetchers.add(doFetch);
585
731
  }
586
732
  if (entry.promise) {
587
733
  setIsFetching(true);
734
+ const captured = entry.promise;
588
735
  try {
589
- await entry.promise;
736
+ await captured;
737
+ if (disposed || currentKey !== key2) return;
738
+ if (entry.promise === captured) {
739
+ onCacheUpdate();
740
+ if (entry.error) onError?.(entry.error);
741
+ else if (entry.data !== void 0) onSuccess?.(entry.data);
742
+ }
590
743
  } catch {
744
+ if (disposed || currentKey !== key2) return;
745
+ if (entry.promise === captured) {
746
+ onCacheUpdate();
747
+ if (entry.error) onError?.(entry.error);
748
+ }
749
+ } finally {
750
+ if (!disposed && currentKey === key2) onSettled?.();
591
751
  }
592
- onCacheUpdate();
593
752
  return;
594
753
  }
595
754
  abortController?.abort();
596
755
  abortController = new AbortController();
597
756
  const signal2 = abortController.signal;
598
757
  setIsFetching(true);
599
- const promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
758
+ let promise;
759
+ try {
760
+ promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
761
+ } catch (err) {
762
+ setIsFetching(false);
763
+ const errorObj = err instanceof Error ? err : new Error(String(err));
764
+ entry.error = errorObj;
765
+ onError?.(errorObj);
766
+ onSettled?.();
767
+ return;
768
+ }
600
769
  entry.promise = promise;
601
770
  try {
602
771
  const result = await promise;
@@ -658,6 +827,7 @@ function query(key, fetcher, options = {}) {
658
827
  oldEntry.subscribers--;
659
828
  if (oldEntry.subscribers <= 0 && cacheTime >= 0) {
660
829
  const oldKey = currentKey;
830
+ if (oldEntry.gcTimer !== null) clearTimeout(oldEntry.gcTimer);
661
831
  oldEntry.gcTimer = setTimeout(() => queryCache.delete(oldKey), cacheTime);
662
832
  }
663
833
  }
@@ -665,7 +835,7 @@ function query(key, fetcher, options = {}) {
665
835
  const keyChanged = currentKey !== key2;
666
836
  currentKey = key2;
667
837
  const entry = getOrCreateEntry(key2, initialData);
668
- if (keyChanged || entry.subscribers === 0) entry.subscribers++;
838
+ if (keyChanged) entry.subscribers++;
669
839
  if (entry.gcTimer !== null) {
670
840
  clearTimeout(entry.gcTimer);
671
841
  entry.gcTimer = null;
@@ -680,6 +850,11 @@ function query(key, fetcher, options = {}) {
680
850
  setError(entry.error);
681
851
  });
682
852
  }
853
+ if (!keyChanged && currentKey === key2 && entry.data !== void 0) {
854
+ const isDataStale2 = entry.dataUpdatedAt === 0 || Date.now() - entry.dataUpdatedAt >= staleTime;
855
+ if (enabled && isDataStale2 && !entry.promise) doFetch();
856
+ return;
857
+ }
683
858
  const isDataStale = entry.dataUpdatedAt === 0 || Date.now() - entry.dataUpdatedAt >= staleTime;
684
859
  if (enabled && (entry.data === void 0 || isDataStale)) {
685
860
  doFetch();
@@ -707,6 +882,7 @@ function query(key, fetcher, options = {}) {
707
882
  }
708
883
  }
709
884
  function dispose() {
885
+ if (disposed) return;
710
886
  disposed = true;
711
887
  abortController?.abort();
712
888
  effectCleanup();
@@ -719,12 +895,17 @@ function query(key, fetcher, options = {}) {
719
895
  entry.subscribers--;
720
896
  if (entry.subscribers <= 0 && cacheTime >= 0) {
721
897
  const key2 = currentKey;
898
+ if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
722
899
  entry.gcTimer = setTimeout(() => queryCache.delete(key2), cacheTime);
723
900
  }
724
901
  }
725
902
  }
726
- if (focusHandler) globalThis.removeEventListener("focus", focusHandler);
727
- if (onlineHandler) globalThis.removeEventListener("online", onlineHandler);
903
+ if (focusHandler && typeof globalThis.removeEventListener === "function") {
904
+ globalThis.removeEventListener("focus", focusHandler);
905
+ }
906
+ if (onlineHandler && typeof globalThis.removeEventListener === "function") {
907
+ globalThis.removeEventListener("online", onlineHandler);
908
+ }
728
909
  }
729
910
  return {
730
911
  data,
@@ -768,7 +949,19 @@ function clearQueryCache() {
768
949
  }
769
950
  queryCache.clear();
770
951
  for (const listener of activeListeners) listener();
771
- for (const refetcher of activeRefetchers) refetcher();
952
+ for (const refetcher of activeRefetchers) {
953
+ refetcher().catch((err) => {
954
+ if (typeof console !== "undefined") {
955
+ console.warn("[SibuJS query] refetch after clearQueryCache failed:", err);
956
+ }
957
+ });
958
+ }
959
+ }
960
+ function __resetQueryCache() {
961
+ for (const entry of queryCache.values()) {
962
+ if (entry.gcTimer) clearTimeout(entry.gcTimer);
963
+ }
964
+ queryCache.clear();
772
965
  }
773
966
 
774
967
  // src/data/mutation.ts
@@ -831,7 +1024,10 @@ function mutation(mutationFn, options = {}) {
831
1024
  isSuccess,
832
1025
  isIdle,
833
1026
  mutate: (variables) => {
834
- execute(variables).catch(() => {
1027
+ execute(variables).catch((err) => {
1028
+ if (typeof console !== "undefined") {
1029
+ console.warn("[SibuJS mutation] mutate() failed; check `.error()` signal or onError option.", err);
1030
+ }
835
1031
  });
836
1032
  },
837
1033
  mutateAsync: execute,
@@ -867,11 +1063,13 @@ function infiniteQuery(key, fetcher, options) {
867
1063
  const hasPreviousPage = derived(() => prevPageParam() !== void 0);
868
1064
  let abortController = null;
869
1065
  let disposed = false;
1066
+ let runId = 0;
870
1067
  async function fetchPage(pageParam, direction) {
871
1068
  if (disposed) return;
872
1069
  abortController?.abort();
873
1070
  abortController = new AbortController();
874
1071
  const signal2 = abortController.signal;
1072
+ const myRun = ++runId;
875
1073
  batch(() => {
876
1074
  setIsFetching(true);
877
1075
  if (direction === "next") setIsFetchingNext(true);
@@ -880,7 +1078,7 @@ function infiniteQuery(key, fetcher, options) {
880
1078
  });
881
1079
  try {
882
1080
  const page = await withRetry(() => fetcher({ signal: signal2, pageParam }), retryOptions, void 0, signal2);
883
- if (disposed) return;
1081
+ if (disposed || myRun !== runId) return;
884
1082
  const currentPages = pages();
885
1083
  let newPages;
886
1084
  if (direction === "prev") {
@@ -900,7 +1098,7 @@ function infiniteQuery(key, fetcher, options) {
900
1098
  });
901
1099
  onSuccess?.(newPages);
902
1100
  } catch (err) {
903
- if (disposed) return;
1101
+ if (disposed || myRun !== runId) return;
904
1102
  if (err instanceof DOMException && err.name === "AbortError") return;
905
1103
  const errorObj = err instanceof Error ? err : new Error(String(err));
906
1104
  batch(() => {
@@ -915,9 +1113,12 @@ function infiniteQuery(key, fetcher, options) {
915
1113
  const effectCleanup = effect(() => {
916
1114
  resolveKey();
917
1115
  if (enabled) {
918
- setPages([]);
919
- setNextPageParam(initialPageParam);
920
- setPrevPageParam(void 0);
1116
+ abortController?.abort();
1117
+ batch(() => {
1118
+ setPages([]);
1119
+ setNextPageParam(initialPageParam);
1120
+ setPrevPageParam(void 0);
1121
+ });
921
1122
  fetchPage(initialPageParam, "initial");
922
1123
  }
923
1124
  });
@@ -1154,18 +1355,57 @@ function idbPut(db, store, item) {
1154
1355
  tx.onerror = () => reject(tx.error);
1155
1356
  });
1156
1357
  }
1157
- function idbDelete(db, store, key) {
1358
+ function idbPutWithChange(db, item, change) {
1359
+ return new Promise((resolve, reject) => {
1360
+ const tx = db.transaction(["items", "_changes"], "readwrite");
1361
+ tx.objectStore("items").put(item);
1362
+ tx.objectStore("_changes").put(change);
1363
+ tx.oncomplete = () => resolve();
1364
+ tx.onerror = () => reject(tx.error);
1365
+ });
1366
+ }
1367
+ function idbDeleteWithChange(db, key, change) {
1368
+ return new Promise((resolve, reject) => {
1369
+ const tx = db.transaction(["items", "_changes"], "readwrite");
1370
+ tx.objectStore("items").delete(key);
1371
+ tx.objectStore("_changes").put(change);
1372
+ tx.oncomplete = () => resolve();
1373
+ tx.onerror = () => reject(tx.error);
1374
+ });
1375
+ }
1376
+ function idbGetAllWithKeys(db, store) {
1377
+ return new Promise((resolve, reject) => {
1378
+ const tx = db.transaction(store, "readonly");
1379
+ const out = [];
1380
+ const req = tx.objectStore(store).openCursor();
1381
+ req.onsuccess = () => {
1382
+ const cursor = req.result;
1383
+ if (cursor) {
1384
+ out.push({ key: cursor.primaryKey, value: cursor.value });
1385
+ cursor.continue();
1386
+ } else {
1387
+ resolve(out);
1388
+ }
1389
+ };
1390
+ req.onerror = () => reject(req.error);
1391
+ });
1392
+ }
1393
+ function idbDeleteKeys(db, store, keys) {
1394
+ if (keys.length === 0) return Promise.resolve();
1158
1395
  return new Promise((resolve, reject) => {
1159
1396
  const tx = db.transaction(store, "readwrite");
1160
- tx.objectStore(store).delete(key);
1397
+ const objStore = tx.objectStore(store);
1398
+ for (const k of keys) objStore.delete(k);
1161
1399
  tx.oncomplete = () => resolve();
1162
1400
  tx.onerror = () => reject(tx.error);
1163
1401
  });
1164
1402
  }
1165
- function idbClear(db, store) {
1403
+ function idbPutMany(db, store, items) {
1404
+ if (items.length === 0) return Promise.resolve();
1166
1405
  return new Promise((resolve, reject) => {
1167
1406
  const tx = db.transaction(store, "readwrite");
1168
- tx.objectStore(store).clear();
1407
+ const objStore = tx.objectStore(store);
1408
+ for (const item of items) objStore.put(item);
1169
1409
  tx.oncomplete = () => resolve();
1170
1410
  tx.onerror = () => reject(tx.error);
1171
1411
  });
@@ -1188,15 +1428,13 @@ async function offlineStore(options) {
1188
1428
  setPendingCount(changes.length);
1189
1429
  }
1190
1430
  async function put(item) {
1191
- await idbPut(db, "items", item);
1192
- await idbPut(db, "_changes", { type: "put", item, timestamp: Date.now() });
1431
+ await idbPutWithChange(db, item, { type: "put", item, timestamp: Date.now() });
1193
1432
  await refreshData();
1194
1433
  }
1195
1434
  async function remove(key) {
1196
1435
  const existing = await idbGet(db, "items", key);
1197
1436
  if (existing) {
1198
- await idbDelete(db, "items", key);
1199
- await idbPut(db, "_changes", { type: "delete", item: existing, timestamp: Date.now() });
1437
+ await idbDeleteWithChange(db, key, { type: "delete", item: existing, timestamp: Date.now() });
1200
1438
  await refreshData();
1201
1439
  }
1202
1440
  }
@@ -1207,25 +1445,45 @@ async function offlineStore(options) {
1207
1445
  return data().filter(filter);
1208
1446
  }
1209
1447
  async function sync() {
1210
- if (!adapter || isSyncing()) return;
1448
+ if (!adapter || isSyncing() || closed) return;
1211
1449
  setIsSyncing(true);
1212
1450
  try {
1213
- const changes = await idbGetAll(db, "_changes");
1214
- if (changes.length > 0) {
1215
- const result = await adapter.push(changes);
1451
+ const snapshot = await idbGetAllWithKeys(db, "_changes");
1452
+ if (closed) return;
1453
+ if (snapshot.length > 0) {
1454
+ const result = await adapter.push(snapshot.map((e) => e.value));
1455
+ if (closed) return;
1216
1456
  if (result.ok) {
1217
- await idbClear(db, "_changes");
1457
+ await idbDeleteKeys(
1458
+ db,
1459
+ "_changes",
1460
+ snapshot.map((e) => e.key)
1461
+ );
1462
+ if (closed) return;
1218
1463
  }
1219
1464
  }
1220
1465
  const remoteItems = await adapter.pull(lastSynced());
1221
- for (const item of remoteItems) {
1222
- await idbPut(db, "items", item);
1466
+ if (closed) return;
1467
+ const pendingChanges = await idbGetAll(db, "_changes");
1468
+ if (closed) return;
1469
+ const pendingKeys = /* @__PURE__ */ new Set();
1470
+ for (const c of pendingChanges) {
1471
+ const k = c.item[keyPath];
1472
+ if (k != null) pendingKeys.add(k);
1223
1473
  }
1474
+ const safeRemote = remoteItems.filter((item) => {
1475
+ const k = item[keyPath];
1476
+ return k == null || !pendingKeys.has(k);
1477
+ });
1478
+ await idbPutMany(db, "items", safeRemote);
1479
+ if (closed) return;
1224
1480
  const now = Date.now();
1225
1481
  await idbPut(db, "_meta", now);
1482
+ if (closed) return;
1226
1483
  setLastSynced(now);
1227
1484
  await refreshData();
1228
- } catch {
1485
+ } catch (err) {
1486
+ if (typeof console !== "undefined") console.warn("[offlineStore] sync failed", err);
1229
1487
  } finally {
1230
1488
  setIsSyncing(false);
1231
1489
  }
@@ -1233,13 +1491,21 @@ async function offlineStore(options) {
1233
1491
  function attach(newAdapter) {
1234
1492
  adapter = newAdapter;
1235
1493
  }
1494
+ let onlineHandler = null;
1495
+ let closed = false;
1236
1496
  function close() {
1497
+ closed = true;
1498
+ if (onlineHandler && typeof window !== "undefined") {
1499
+ window.removeEventListener("online", onlineHandler);
1500
+ onlineHandler = null;
1501
+ }
1237
1502
  db.close();
1238
1503
  }
1239
1504
  if (autoSync && typeof window !== "undefined") {
1240
- window.addEventListener("online", () => {
1505
+ onlineHandler = () => {
1241
1506
  sync();
1242
- });
1507
+ };
1508
+ window.addEventListener("online", onlineHandler);
1243
1509
  }
1244
1510
  return {
1245
1511
  data,
@@ -1262,9 +1528,11 @@ function syncAdapter(config) {
1262
1528
  // src/core/rendering/context.ts
1263
1529
  function context(defaultValue) {
1264
1530
  const [getValue, setValue] = signal(defaultValue);
1265
- return {
1531
+ const ctx = {
1266
1532
  provide(value) {
1533
+ const previous2 = getValue();
1267
1534
  setValue(value);
1535
+ return () => setValue(previous2);
1268
1536
  },
1269
1537
  use() {
1270
1538
  return getValue;
@@ -1274,8 +1542,18 @@ function context(defaultValue) {
1274
1542
  },
1275
1543
  set(value) {
1276
1544
  setValue(value);
1545
+ },
1546
+ withContext(value, fn) {
1547
+ const previous2 = getValue();
1548
+ setValue(value);
1549
+ try {
1550
+ return fn();
1551
+ } finally {
1552
+ setValue(previous2);
1553
+ }
1277
1554
  }
1278
1555
  };
1556
+ return ctx;
1279
1557
  }
1280
1558
 
1281
1559
  // src/data/routeLoader.ts
@@ -1296,9 +1574,16 @@ function loaderData() {
1296
1574
  error: resource2.error
1297
1575
  };
1298
1576
  }
1299
- async function preloadRoute(route, context2) {
1577
+ async function preloadRoute(route, context2, callerSignal) {
1300
1578
  if (!route.loader) return void 0;
1301
1579
  const controller = new AbortController();
1580
+ if (callerSignal) {
1581
+ if (callerSignal.aborted) {
1582
+ controller.abort();
1583
+ } else {
1584
+ callerSignal.addEventListener("abort", () => controller.abort(), { once: true });
1585
+ }
1586
+ }
1302
1587
  return route.loader(context2, { signal: controller.signal });
1303
1588
  }
1304
1589
 
@@ -1313,7 +1598,7 @@ function validateWsUrl(raw) {
1313
1598
  function socket(url, options) {
1314
1599
  const autoReconnect = options?.autoReconnect ?? false;
1315
1600
  const reconnectDelay = options?.reconnectDelay ?? 1e3;
1316
- const maxReconnects = options?.maxReconnects ?? Infinity;
1601
+ const maxReconnects = options?.maxReconnects ?? 10;
1317
1602
  const heartbeat = options?.heartbeat;
1318
1603
  const protocols = options?.protocols;
1319
1604
  const [data, setData] = signal(null);
@@ -1347,13 +1632,19 @@ function socket(url, options) {
1347
1632
  ws.onclose = () => {
1348
1633
  setStatus("closed");
1349
1634
  stopHeartbeat();
1350
- if (autoReconnect && !disposed && !manuallyClosed && reconnectCount < maxReconnects) {
1635
+ const wasManual = manuallyClosed;
1636
+ manuallyClosed = false;
1637
+ if (autoReconnect && !disposed && !wasManual && reconnectCount < maxReconnects) {
1638
+ const cap = 3e4;
1639
+ const delay = Math.min(cap, reconnectDelay * 2 ** reconnectCount);
1640
+ const jittered = delay * (0.5 + Math.random() * 0.5);
1351
1641
  reconnectCount++;
1352
1642
  reconnectTimer = setTimeout(() => {
1643
+ reconnectTimer = null;
1644
+ if (disposed || manuallyClosed) return;
1353
1645
  connect();
1354
- }, reconnectDelay);
1646
+ }, jittered);
1355
1647
  }
1356
- manuallyClosed = false;
1357
1648
  };
1358
1649
  ws.onerror = () => {
1359
1650
  };
@@ -1399,13 +1690,24 @@ function socket(url, options) {
1399
1690
  }
1400
1691
 
1401
1692
  // src/utils/sanitize.ts
1693
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1402
1694
  function sanitizeUrl(url) {
1403
1695
  const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1404
1696
  if (!trimmed) return "";
1405
1697
  const lower = trimmed.toLowerCase();
1406
- if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
1407
- return "";
1698
+ let schemeEnd = -1;
1699
+ for (let i = 0; i < lower.length; i++) {
1700
+ const ch = lower.charCodeAt(i);
1701
+ if (ch === 58) {
1702
+ schemeEnd = i;
1703
+ break;
1704
+ }
1705
+ if (ch === 47 || ch === 63 || ch === 35) break;
1408
1706
  }
1707
+ if (schemeEnd === -1) return trimmed;
1708
+ const scheme = lower.slice(0, schemeEnd + 1);
1709
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
1710
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1409
1711
  return trimmed;
1410
1712
  }
1411
1713
 
@@ -1417,12 +1719,16 @@ function validateSseUrl(raw) {
1417
1719
  }
1418
1720
  function stream(url, options) {
1419
1721
  const autoReconnect = options?.autoReconnect ?? false;
1722
+ const maxReconnects = options?.maxReconnects ?? 10;
1723
+ const baseMs = options?.reconnectBaseMs ?? 1e3;
1724
+ const maxMs = options?.reconnectMaxMs ?? 3e4;
1420
1725
  const [data, setData] = signal(null);
1421
1726
  const [event, setEvent] = signal(null);
1422
1727
  const [status, setStatus] = signal("connecting");
1423
1728
  let source = null;
1424
1729
  let disposed = false;
1425
1730
  let reconnectTimer = null;
1731
+ let attempts = 0;
1426
1732
  function connect() {
1427
1733
  if (disposed) return;
1428
1734
  const safeUrl = validateSseUrl(url);
@@ -1436,6 +1742,7 @@ function stream(url, options) {
1436
1742
  });
1437
1743
  source.onopen = () => {
1438
1744
  setStatus("open");
1745
+ attempts = 0;
1439
1746
  };
1440
1747
  source.onmessage = (evt) => {
1441
1748
  setData(evt.data);
@@ -1445,11 +1752,14 @@ function stream(url, options) {
1445
1752
  if (source && source.readyState === EventSource.CLOSED) {
1446
1753
  setStatus("closed");
1447
1754
  source = null;
1448
- if (autoReconnect && !disposed) {
1755
+ if (autoReconnect && !disposed && attempts < maxReconnects) {
1756
+ const delay = Math.min(maxMs, baseMs * 2 ** attempts);
1757
+ const jittered = delay * (0.5 + Math.random() * 0.5);
1758
+ attempts++;
1449
1759
  reconnectTimer = setTimeout(() => {
1450
1760
  reconnectTimer = null;
1451
1761
  connect();
1452
- }, 1e3);
1762
+ }, jittered);
1453
1763
  }
1454
1764
  }
1455
1765
  };
@@ -1474,6 +1784,7 @@ function stream(url, options) {
1474
1784
  }
1475
1785
  // Annotate the CommonJS export names for ESM import in node:
1476
1786
  0 && (module.exports = {
1787
+ __resetQueryCache,
1477
1788
  calculateDelay,
1478
1789
  clearQueryCache,
1479
1790
  debounce,