sibujs 1.5.0 → 2.1.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 (208) hide show
  1. package/dist/browser.cjs +332 -121
  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 +1049 -344
  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-BMPL52BF.js → chunk-3DZP6OIT.js} +118 -66
  11. package/dist/chunk-3JHCYHWN.js +125 -0
  12. package/dist/{chunk-CZUGLNJS.js → chunk-45YP72ZQ.js} +3 -3
  13. package/dist/{chunk-JCDUJN2F.js → chunk-AMK2TYNW.js} +490 -153
  14. package/dist/{chunk-NHUC2QWH.js → chunk-CWBVQML6.js} +1 -1
  15. package/dist/{chunk-XHK6BDAJ.js → chunk-DRUZZAK4.js} +25 -8
  16. package/dist/{chunk-RJ46C3CS.js → chunk-GWWURC5M.js} +71 -20
  17. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  18. package/dist/{chunk-2BYQDGN3.js → chunk-KGYT6UO6.js} +234 -63
  19. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  20. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  21. package/dist/{chunk-XUEEGU5O.js → chunk-NASX6ST2.js} +16 -4
  22. package/dist/{chunk-VQDZK23A.js → chunk-O6EFQ3KT.js} +181 -66
  23. package/dist/{chunk-BGN5ZMP4.js → chunk-OJ3P4ECI.js} +14 -2
  24. package/dist/chunk-ON5MMR2J.js +1327 -0
  25. package/dist/{chunk-SFKNRVCU.js → chunk-P2HSJDDN.js} +135 -79
  26. package/dist/chunk-QO3WC6FS.js +384 -0
  27. package/dist/{chunk-WZSPOOER.js → chunk-RDTDJCAB.js} +8 -5
  28. package/dist/{chunk-7GRNSCFT.js → chunk-TH2ILCYW.js} +312 -185
  29. package/dist/chunk-UCS6AMJ7.js +79 -0
  30. package/dist/{chunk-VAPYJN4X.js → chunk-V6C4FADE.js} +93 -23
  31. package/dist/{chunk-OUZZEE4S.js → chunk-WANSMF2L.js} +17 -11
  32. package/dist/{chunk-23VV7YD3.js → chunk-WIPZPFBQ.js} +25 -30
  33. package/dist/chunk-WZA53FXU.js +149 -0
  34. package/dist/{chunk-BGTHZHJ5.js → chunk-ZAQSMOED.js} +188 -44
  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 +536 -151
  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 +613 -266
  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 +602 -197
  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 +3500 -1608
  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 +1055 -344
  54. package/dist/index.d.cts +85 -8
  55. package/dist/index.d.ts +85 -8
  56. package/dist/index.js +32 -16
  57. package/dist/{introspect-BumjnBKr.d.cts → introspect-2TOlQ7oa.d.cts} +25 -3
  58. package/dist/{introspect-CZrlcaYy.d.ts → introspect-DnIpHQQz.d.ts} +25 -3
  59. package/dist/motion.cjs +122 -63
  60. package/dist/motion.js +4 -4
  61. package/dist/patterns.cjs +450 -110
  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 +373 -149
  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 +729 -301
  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 +736 -274
  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 +303 -76
  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 +589 -178
  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 +1103 -146
  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-K4G4ZQNR.js +0 -286
  136. package/dist/chunk-K5ZUMYVS.js +0 -89
  137. package/dist/chunk-KQPDEVVS.js +0 -398
  138. package/dist/chunk-L6JRBDNS.js +0 -60
  139. package/dist/chunk-LA6KQEDU.js +0 -712
  140. package/dist/chunk-MB6QFH3I.js +0 -2776
  141. package/dist/chunk-MDVXJWFN.js +0 -304
  142. package/dist/chunk-MEZVEBPN.js +0 -2008
  143. package/dist/chunk-MK4ERFYL.js +0 -2249
  144. package/dist/chunk-MLKGABMK.js +0 -9
  145. package/dist/chunk-MQ5GOYPH.js +0 -2249
  146. package/dist/chunk-MYRV7VDM.js +0 -742
  147. package/dist/chunk-N6IZB6KJ.js +0 -567
  148. package/dist/chunk-NEKUBFPT.js +0 -60
  149. package/dist/chunk-NMRUZALC.js +0 -1097
  150. package/dist/chunk-NYVAC6P5.js +0 -37
  151. package/dist/chunk-NZIIMDWI.js +0 -84
  152. package/dist/chunk-OF7UZIVB.js +0 -725
  153. package/dist/chunk-P3XWXJZU.js +0 -282
  154. package/dist/chunk-P6W3STU4.js +0 -2249
  155. package/dist/chunk-PBHF5WKN.js +0 -616
  156. package/dist/chunk-PDZQY43A.js +0 -616
  157. package/dist/chunk-PTQJDMRT.js +0 -146
  158. package/dist/chunk-PZEGYCF5.js +0 -61
  159. package/dist/chunk-QBMDLBU2.js +0 -975
  160. package/dist/chunk-QWZG56ET.js +0 -2744
  161. package/dist/chunk-RQGQSLQK.js +0 -725
  162. package/dist/chunk-SDLZDHKP.js +0 -107
  163. package/dist/chunk-TDGZL5CU.js +0 -365
  164. package/dist/chunk-TNQWPPE6.js +0 -37
  165. package/dist/chunk-TSOKIX5Z.js +0 -654
  166. package/dist/chunk-UHNL42EF.js +0 -2730
  167. package/dist/chunk-UNXCEF6S.js +0 -21
  168. package/dist/chunk-V2XTI523.js +0 -347
  169. package/dist/chunk-VAU366PN.js +0 -2241
  170. package/dist/chunk-VMVDTCXB.js +0 -712
  171. package/dist/chunk-VQNQZCWJ.js +0 -61
  172. package/dist/chunk-VRW3FULF.js +0 -725
  173. package/dist/chunk-WADYRCO2.js +0 -304
  174. package/dist/chunk-WILQZRO4.js +0 -282
  175. package/dist/chunk-WR5D4EGH.js +0 -26
  176. package/dist/chunk-WUHJISPP.js +0 -298
  177. package/dist/chunk-XYU6TZOW.js +0 -182
  178. package/dist/chunk-Y6GP4QGG.js +0 -276
  179. package/dist/chunk-YECR7UIA.js +0 -347
  180. package/dist/chunk-YUTWTI4B.js +0 -654
  181. package/dist/chunk-Z65KYU7I.js +0 -26
  182. package/dist/chunk-Z6POF5YC.js +0 -975
  183. package/dist/chunk-ZBJP6WFL.js +0 -482
  184. package/dist/chunk-ZD6OAMTH.js +0 -277
  185. package/dist/chunk-ZWKZCBO6.js +0 -317
  186. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  187. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  188. package/dist/contracts-DOrhwbke.d.cts +0 -245
  189. package/dist/contracts-DOrhwbke.d.ts +0 -245
  190. package/dist/contracts-xo5ckdRP.d.cts +0 -240
  191. package/dist/contracts-xo5ckdRP.d.ts +0 -240
  192. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  193. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  194. package/dist/customElement-D2DJp_xn.d.cts +0 -313
  195. package/dist/customElement-D2DJp_xn.d.ts +0 -313
  196. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  197. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  198. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  199. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  200. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  201. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  202. package/dist/ssr-3RXHP5ES.js +0 -38
  203. package/dist/ssr-6GIMY5MX.js +0 -38
  204. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  205. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  206. package/dist/ssr-WKUPVSSK.js +0 -36
  207. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  208. 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,26 +51,39 @@ 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
 
62
63
  // src/reactivity/track.ts
63
64
  var _isDev2 = isDev();
64
- var subscriberStack = new Array(32);
65
- var stackCapacity = 32;
65
+ var STACK_INITIAL = 32;
66
+ var STACK_SHRINK_THRESHOLD = 128;
67
+ var subscriberStack = new Array(STACK_INITIAL);
68
+ var stackCapacity = STACK_INITIAL;
66
69
  var stackTop = -1;
67
70
  var currentSubscriber = null;
68
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
69
71
  var SUBS = "__s";
72
+ function syncFastPath(signal2, subs) {
73
+ const size = subs.size;
74
+ if (size === 0) {
75
+ signal2.__f = void 0;
76
+ delete signal2[SUBS];
77
+ } else if (size === 1) {
78
+ signal2.__f = subs.values().next().value;
79
+ } else {
80
+ signal2.__f = void 0;
81
+ }
82
+ }
70
83
  var notifyDepth = 0;
71
84
  var pendingQueue = [];
72
85
  var pendingSet = /* @__PURE__ */ new Set();
86
+ var propagateStack = [];
73
87
  function safeInvoke(sub) {
74
88
  try {
75
89
  sub();
@@ -78,6 +92,47 @@ function safeInvoke(sub) {
78
92
  }
79
93
  }
80
94
  var trackingSuspended = false;
95
+ var subscriberEpochCounter = 0;
96
+ function retrack(effectFn, subscriber) {
97
+ const prev = currentSubscriber;
98
+ currentSubscriber = subscriber;
99
+ const sub = subscriber;
100
+ const epoch = ++subscriberEpochCounter;
101
+ sub._epoch = epoch;
102
+ try {
103
+ effectFn();
104
+ } finally {
105
+ currentSubscriber = prev;
106
+ pruneStaleDeps(sub, epoch);
107
+ }
108
+ }
109
+ function pruneStaleDeps(sub, currentEpoch) {
110
+ if (sub._dep !== void 0) {
111
+ if (sub._depEpoch !== currentEpoch) {
112
+ const sig = sub._dep;
113
+ const subs = sig[SUBS];
114
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
115
+ sub._dep = void 0;
116
+ sub._depEpoch = void 0;
117
+ }
118
+ return;
119
+ }
120
+ const deps = sub._deps;
121
+ if (!deps || deps.size === 0) return;
122
+ let stales;
123
+ for (const [signal2, epoch] of deps) {
124
+ if (epoch !== currentEpoch) {
125
+ (stales ?? (stales = [])).push(signal2);
126
+ }
127
+ }
128
+ if (!stales) return;
129
+ for (const signal2 of stales) {
130
+ deps.delete(signal2);
131
+ const sig = signal2;
132
+ const subs = sig[SUBS];
133
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
134
+ }
135
+ }
81
136
  function track(effectFn, subscriber) {
82
137
  if (!subscriber) subscriber = effectFn;
83
138
  cleanup(subscriber);
@@ -93,37 +148,49 @@ function track(effectFn, subscriber) {
93
148
  } finally {
94
149
  stackTop--;
95
150
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
151
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
152
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
153
+ subscriberStack.length = stackCapacity;
154
+ }
96
155
  }
97
156
  return () => cleanup(subscriber);
98
157
  }
99
158
  function recordDependency(signal2) {
100
159
  if (!currentSubscriber) return;
101
160
  const sub = currentSubscriber;
102
- if (sub._dep === signal2) return;
161
+ const epoch = sub._epoch;
162
+ if (sub._dep === signal2) {
163
+ sub._depEpoch = epoch;
164
+ return;
165
+ }
103
166
  const deps = sub._deps;
104
167
  if (deps) {
105
- if (deps.has(signal2)) return;
106
- deps.add(signal2);
168
+ deps.set(signal2, epoch);
107
169
  } else if (sub._dep !== void 0) {
108
- const set = /* @__PURE__ */ new Set();
109
- set.add(sub._dep);
110
- set.add(signal2);
111
- sub._deps = set;
170
+ const map = /* @__PURE__ */ new Map();
171
+ map.set(sub._dep, sub._depEpoch);
172
+ map.set(signal2, epoch);
173
+ sub._deps = map;
112
174
  sub._dep = void 0;
175
+ sub._depEpoch = void 0;
113
176
  } else {
114
177
  sub._dep = signal2;
178
+ sub._depEpoch = epoch;
115
179
  }
116
- let subs = signal2[SUBS];
180
+ const sig = signal2;
181
+ let subs = sig[SUBS];
117
182
  if (!subs) {
118
183
  subs = /* @__PURE__ */ new Set();
119
- signalSubscribers.set(signal2, subs);
120
- signal2[SUBS] = subs;
184
+ sig[SUBS] = subs;
121
185
  }
186
+ const prevSize = subs.size;
122
187
  subs.add(currentSubscriber);
123
- if (subs.size === 1) {
124
- signal2.__f = currentSubscriber;
125
- } else if (signal2.__f !== void 0) {
126
- signal2.__f = void 0;
188
+ if (subs.size !== prevSize) {
189
+ if (subs.size === 1) {
190
+ sig.__f = currentSubscriber;
191
+ } else if (sig.__f !== void 0) {
192
+ sig.__f = void 0;
193
+ }
127
194
  }
128
195
  }
129
196
  function queueSignalNotification(signal2) {
@@ -138,66 +205,102 @@ function queueSignalNotification(signal2) {
138
205
  }
139
206
  }
140
207
  }
141
- var MAX_DRAIN_ITERATIONS = 1e3;
208
+ var maxSubscriberRepeats = 50;
209
+ var maxDrainIterations = 1e6;
210
+ var drainEpoch = 0;
211
+ function tickRepeat(sub) {
212
+ const s = sub;
213
+ if (s._runEpoch !== drainEpoch) {
214
+ s._runEpoch = drainEpoch;
215
+ s._runs = 1;
216
+ return false;
217
+ }
218
+ return ++s._runs > maxSubscriberRepeats;
219
+ }
220
+ function cycleError(sub) {
221
+ if (typeof console !== "undefined") {
222
+ const name = sub.__name ?? "<unnamed>";
223
+ console.error(
224
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
225
+ );
226
+ }
227
+ }
228
+ function absoluteDrainError() {
229
+ if (typeof console !== "undefined") {
230
+ console.error(
231
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
232
+ );
233
+ }
234
+ }
235
+ function drainQueue() {
236
+ let i = 0;
237
+ while (i < pendingQueue.length) {
238
+ if (i >= maxDrainIterations) {
239
+ absoluteDrainError();
240
+ break;
241
+ }
242
+ const sub = pendingQueue[i++];
243
+ if (tickRepeat(sub)) {
244
+ cycleError(sub);
245
+ break;
246
+ }
247
+ pendingSet.delete(sub);
248
+ safeInvoke(sub);
249
+ }
250
+ }
142
251
  function drainNotificationQueue() {
143
252
  if (notifyDepth > 0) return;
144
253
  notifyDepth++;
254
+ drainEpoch++;
145
255
  try {
146
- let i = 0;
147
- while (i < pendingQueue.length) {
148
- if (i >= MAX_DRAIN_ITERATIONS) {
149
- if (typeof console !== "undefined") {
150
- 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.`
152
- );
153
- }
154
- break;
155
- }
156
- safeInvoke(pendingQueue[i]);
157
- i++;
158
- }
256
+ drainQueue();
159
257
  } finally {
160
- pendingQueue.length = 0;
161
- pendingSet.clear();
162
258
  notifyDepth--;
259
+ if (notifyDepth === 0) {
260
+ pendingQueue.length = 0;
261
+ pendingSet.clear();
262
+ }
163
263
  }
164
264
  }
165
265
  function propagateDirty(sub) {
166
266
  sub();
167
- let sig = sub._sig;
168
- while (sig) {
267
+ const rootSig = sub._sig;
268
+ if (!rootSig) return;
269
+ const stack = propagateStack;
270
+ const baseLen = stack.length;
271
+ stack.push(rootSig);
272
+ while (stack.length > baseLen) {
273
+ const sig = stack.pop();
169
274
  const first = sig.__f;
170
275
  if (first) {
171
276
  if (first._c) {
172
277
  const nSig = first._sig;
173
- nSig._d = true;
174
- sig = nSig;
175
- continue;
176
- }
177
- if (!pendingSet.has(first)) {
278
+ if (!nSig._d) {
279
+ nSig._d = true;
280
+ stack.push(nSig);
281
+ }
282
+ } else if (!pendingSet.has(first)) {
178
283
  pendingSet.add(first);
179
284
  pendingQueue.push(first);
180
285
  }
181
- break;
286
+ continue;
182
287
  }
183
288
  const subs = sig[SUBS];
184
- if (!subs) break;
185
- let nextSig;
289
+ if (!subs) continue;
186
290
  for (const s of subs) {
187
291
  if (s._c) {
188
- s();
189
292
  const nSig = s._sig;
190
- if (nSig && !nextSig) {
191
- nextSig = nSig;
192
- } else if (nSig) {
193
- propagateDirty(s);
293
+ if (nSig && !nSig._d) {
294
+ nSig._d = true;
295
+ stack.push(nSig);
296
+ } else if (!nSig) {
297
+ s();
194
298
  }
195
299
  } else if (!pendingSet.has(s)) {
196
300
  pendingSet.add(s);
197
301
  pendingQueue.push(s);
198
302
  }
199
303
  }
200
- sig = nextSig;
201
304
  }
202
305
  }
203
306
  function notifySubscribers(signal2) {
@@ -213,21 +316,22 @@ function notifySubscribers(signal2) {
213
316
  return;
214
317
  }
215
318
  notifyDepth++;
319
+ drainEpoch++;
216
320
  try {
217
321
  if (first._c) {
218
322
  propagateDirty(first);
323
+ } else if (tickRepeat(first)) {
324
+ cycleError(first);
219
325
  } else {
220
326
  safeInvoke(first);
221
327
  }
222
- let i = 0;
223
- while (i < pendingQueue.length) {
224
- safeInvoke(pendingQueue[i]);
225
- i++;
226
- }
328
+ drainQueue();
227
329
  } finally {
228
- pendingQueue.length = 0;
229
- pendingSet.clear();
230
330
  notifyDepth--;
331
+ if (notifyDepth === 0) {
332
+ pendingQueue.length = 0;
333
+ pendingSet.clear();
334
+ }
231
335
  }
232
336
  return;
233
337
  }
@@ -245,57 +349,45 @@ function notifySubscribers(signal2) {
245
349
  return;
246
350
  }
247
351
  notifyDepth++;
352
+ drainEpoch++;
248
353
  try {
249
- let directCount = 0;
250
354
  for (const sub of subs) {
251
- pendingQueue[directCount++] = sub;
252
- }
253
- for (let i2 = 0; i2 < directCount; i2++) {
254
- if (pendingQueue[i2]._c) {
255
- propagateDirty(pendingQueue[i2]);
256
- }
257
- }
258
- for (let i2 = 0; i2 < directCount; i2++) {
259
- if (!pendingQueue[i2]._c) {
260
- if (!pendingSet.has(pendingQueue[i2])) {
261
- safeInvoke(pendingQueue[i2]);
262
- }
355
+ if (sub._c) {
356
+ propagateDirty(sub);
357
+ } else if (!pendingSet.has(sub)) {
358
+ pendingSet.add(sub);
359
+ pendingQueue.push(sub);
263
360
  }
264
361
  }
265
- let i = directCount;
266
- while (i < pendingQueue.length) {
267
- safeInvoke(pendingQueue[i]);
268
- i++;
269
- }
362
+ drainQueue();
270
363
  } finally {
271
- pendingQueue.length = 0;
272
- pendingSet.clear();
273
364
  notifyDepth--;
365
+ if (notifyDepth === 0) {
366
+ pendingQueue.length = 0;
367
+ pendingSet.clear();
368
+ }
274
369
  }
275
370
  }
276
371
  function cleanup(subscriber) {
277
372
  const sub = subscriber;
278
373
  const singleDep = sub._dep;
279
374
  if (singleDep !== void 0) {
280
- const subs = singleDep[SUBS];
281
- if (subs) {
282
- subs.delete(subscriber);
283
- if (singleDep.__f === subscriber) {
284
- singleDep.__f = void 0;
285
- }
375
+ const sig = singleDep;
376
+ const subs = sig[SUBS];
377
+ if (subs?.delete(subscriber)) {
378
+ syncFastPath(sig, subs);
286
379
  }
287
380
  sub._dep = void 0;
381
+ sub._depEpoch = void 0;
288
382
  return;
289
383
  }
290
384
  const deps = sub._deps;
291
385
  if (!deps || deps.size === 0) return;
292
- for (const signal2 of deps) {
293
- const subs = signal2[SUBS];
294
- if (subs) {
295
- subs.delete(subscriber);
296
- if (signal2.__f === subscriber) {
297
- signal2.__f = void 0;
298
- }
386
+ for (const signal2 of deps.keys()) {
387
+ const sig = signal2;
388
+ const subs = sig[SUBS];
389
+ if (subs?.delete(subscriber)) {
390
+ syncFastPath(sig, subs);
299
391
  }
300
392
  }
301
393
  deps.clear();
@@ -305,6 +397,7 @@ function cleanup(subscriber) {
305
397
  function derived(getter, options) {
306
398
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
307
399
  const debugName = options?.name;
400
+ const equals = options?.equals;
308
401
  const cs = {};
309
402
  cs._d = false;
310
403
  cs._g = getter;
@@ -315,8 +408,14 @@ function derived(getter, options) {
315
408
  markDirty._c = 1;
316
409
  markDirty._sig = cs;
317
410
  track(() => {
318
- cs._d = false;
319
- cs._v = getter();
411
+ let threw = true;
412
+ try {
413
+ cs._v = getter();
414
+ cs._d = false;
415
+ threw = false;
416
+ } finally {
417
+ if (threw) cs._d = true;
418
+ }
320
419
  }, markDirty);
321
420
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
322
421
  let evaluating = false;
@@ -329,11 +428,16 @@ function derived(getter, options) {
329
428
  if (trackingSuspended) {
330
429
  if (cs._d) {
331
430
  evaluating = true;
431
+ let threw = true;
332
432
  try {
333
- cs._d = false;
334
- cs._v = getter();
433
+ retrack(() => {
434
+ cs._v = getter();
435
+ cs._d = false;
436
+ threw = false;
437
+ }, markDirty);
335
438
  } finally {
336
439
  evaluating = false;
440
+ if (threw) cs._d = true;
337
441
  }
338
442
  }
339
443
  return cs._v;
@@ -342,13 +446,17 @@ function derived(getter, options) {
342
446
  if (cs._d) {
343
447
  const oldValue = cs._v;
344
448
  evaluating = true;
449
+ let threw = true;
345
450
  try {
346
- track(() => {
451
+ retrack(() => {
452
+ const next = getter();
453
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
347
454
  cs._d = false;
348
- cs._v = getter();
455
+ threw = false;
349
456
  }, markDirty);
350
457
  } finally {
351
458
  evaluating = false;
459
+ if (threw) cs._d = true;
352
460
  }
353
461
  if (hook && oldValue !== cs._v) {
354
462
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
@@ -366,9 +474,28 @@ function derived(getter, options) {
366
474
  }
367
475
 
368
476
  // src/core/ssr-context.ts
369
- var ssrMode = false;
477
+ var als = null;
478
+ try {
479
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
480
+ const req = Function("return typeof require==='function'?require:null")();
481
+ if (req) {
482
+ const mod = req("node:async_hooks");
483
+ als = new mod.AsyncLocalStorage();
484
+ }
485
+ }
486
+ } catch {
487
+ als = null;
488
+ }
489
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
490
+ function getSSRStore() {
491
+ if (als) {
492
+ const s = als.getStore();
493
+ if (s) return s;
494
+ }
495
+ return fallbackStore;
496
+ }
370
497
  function isSSR() {
371
- return ssrMode;
498
+ return getSSRStore().ssr;
372
499
  }
373
500
 
374
501
  // src/core/signals/effect.ts
@@ -378,26 +505,114 @@ function effect(effectFn, options) {
378
505
  if (isSSR()) return () => {
379
506
  };
380
507
  const onError = options?.onError;
508
+ let userCleanups = [];
509
+ const onCleanup = (fn) => {
510
+ userCleanups.push(fn);
511
+ };
512
+ const runUserCleanups = () => {
513
+ if (userCleanups.length === 0) return;
514
+ const list = userCleanups;
515
+ userCleanups = [];
516
+ for (let i = list.length - 1; i >= 0; i--) {
517
+ try {
518
+ list[i]();
519
+ } catch (err) {
520
+ if (typeof console !== "undefined") {
521
+ console.warn("[SibuJS effect] onCleanup threw:", err);
522
+ }
523
+ }
524
+ }
525
+ };
526
+ const invokeBody = () => effectFn(onCleanup);
381
527
  const wrappedFn = onError ? () => {
382
528
  try {
383
- effectFn();
529
+ invokeBody();
384
530
  } catch (err) {
385
531
  onError(err);
386
532
  }
387
- } : effectFn;
533
+ } : invokeBody;
388
534
  let cleanupHandle = () => {
389
535
  };
536
+ let running = false;
537
+ let rerunPending = false;
538
+ const MAX_RERUNS = 100;
390
539
  const subscriber = () => {
391
- cleanupHandle();
392
- cleanupHandle = track(wrappedFn, subscriber);
540
+ if (running) {
541
+ rerunPending = true;
542
+ return;
543
+ }
544
+ running = true;
545
+ try {
546
+ let reruns = 0;
547
+ do {
548
+ rerunPending = false;
549
+ runUserCleanups();
550
+ cleanupHandle();
551
+ cleanupHandle = track(wrappedFn, subscriber);
552
+ if (++reruns > MAX_RERUNS) {
553
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
554
+ console.error(
555
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
556
+ );
557
+ }
558
+ rerunPending = false;
559
+ break;
560
+ }
561
+ } while (rerunPending);
562
+ } finally {
563
+ running = false;
564
+ rerunPending = false;
565
+ }
393
566
  };
394
- cleanupHandle = track(wrappedFn, subscriber);
567
+ running = true;
568
+ try {
569
+ let reruns = 0;
570
+ do {
571
+ rerunPending = false;
572
+ runUserCleanups();
573
+ cleanupHandle();
574
+ cleanupHandle = track(wrappedFn, subscriber);
575
+ if (++reruns > MAX_RERUNS) {
576
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
577
+ console.error(
578
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
579
+ );
580
+ }
581
+ rerunPending = false;
582
+ break;
583
+ }
584
+ } while (rerunPending);
585
+ } finally {
586
+ running = false;
587
+ rerunPending = false;
588
+ }
395
589
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
396
590
  if (hook) hook.emit("effect:create", { effectFn });
591
+ let disposed = false;
397
592
  return () => {
593
+ if (disposed) return;
594
+ disposed = true;
398
595
  const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
399
- if (h) h.emit("effect:destroy", { effectFn });
400
- cleanupHandle();
596
+ if (h) {
597
+ try {
598
+ h.emit("effect:destroy", { effectFn });
599
+ } catch {
600
+ }
601
+ }
602
+ try {
603
+ runUserCleanups();
604
+ } catch (err) {
605
+ if (typeof console !== "undefined") {
606
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
607
+ }
608
+ }
609
+ try {
610
+ cleanupHandle();
611
+ } catch (err) {
612
+ if (typeof console !== "undefined") {
613
+ console.warn("[SibuJS effect] dispose threw:", err);
614
+ }
615
+ }
401
616
  };
402
617
  }
403
618
 
@@ -421,10 +636,13 @@ function enqueueBatchedSignal(signal2) {
421
636
  return true;
422
637
  }
423
638
  function flushBatch() {
424
- for (const signal2 of pendingSignals) {
425
- queueSignalNotification(signal2);
639
+ try {
640
+ for (const signal2 of pendingSignals) {
641
+ queueSignalNotification(signal2);
642
+ }
643
+ } finally {
644
+ pendingSignals.clear();
426
645
  }
427
- pendingSignals.clear();
428
646
  drainNotificationQueue();
429
647
  }
430
648
 
@@ -481,10 +699,12 @@ function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {
481
699
  break;
482
700
  }
483
701
  delay = Math.min(delay, maxDelay);
702
+ if (!Number.isFinite(delay)) delay = Number.MAX_SAFE_INTEGER;
484
703
  if (jitter > 0) {
485
704
  const jitterRange = delay * jitter;
486
705
  delay += (Math.random() * 2 - 1) * jitterRange;
487
706
  }
707
+ if (!Number.isFinite(delay) || Number.isNaN(delay)) delay = 0;
488
708
  return Math.max(0, delay);
489
709
  }
490
710
  async function withRetry(fn, options, onRetry, signal2) {
@@ -498,6 +718,7 @@ async function withRetry(fn, options, onRetry, signal2) {
498
718
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
499
719
  if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
500
720
  try {
721
+ if (signal2?.aborted) throw new DOMException("Aborted", "AbortError");
501
722
  return await fn();
502
723
  } catch (error) {
503
724
  lastError = error;
@@ -579,24 +800,46 @@ function query(key, fetcher, options = {}) {
579
800
  let entry = queryCache.get(key2);
580
801
  if (!entry) {
581
802
  entry = getOrCreateEntry(key2);
582
- entry.subscribers++;
583
803
  entry.listeners.add(onCacheUpdate);
584
804
  entry.refetchers.add(doFetch);
585
805
  }
586
806
  if (entry.promise) {
587
807
  setIsFetching(true);
808
+ const captured = entry.promise;
588
809
  try {
589
- await entry.promise;
810
+ await captured;
811
+ if (disposed || currentKey !== key2) return;
812
+ if (entry.promise === captured) {
813
+ onCacheUpdate();
814
+ if (entry.error) onError?.(entry.error);
815
+ else if (entry.data !== void 0) onSuccess?.(entry.data);
816
+ }
590
817
  } catch {
818
+ if (disposed || currentKey !== key2) return;
819
+ if (entry.promise === captured) {
820
+ onCacheUpdate();
821
+ if (entry.error) onError?.(entry.error);
822
+ }
823
+ } finally {
824
+ if (!disposed && currentKey === key2) onSettled?.();
591
825
  }
592
- onCacheUpdate();
593
826
  return;
594
827
  }
595
828
  abortController?.abort();
596
829
  abortController = new AbortController();
597
830
  const signal2 = abortController.signal;
598
831
  setIsFetching(true);
599
- const promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
832
+ let promise;
833
+ try {
834
+ promise = withRetry(() => fetcher({ signal: signal2, key: key2 }), retryOptions, void 0, signal2);
835
+ } catch (err) {
836
+ setIsFetching(false);
837
+ const errorObj = err instanceof Error ? err : new Error(String(err));
838
+ entry.error = errorObj;
839
+ onError?.(errorObj);
840
+ onSettled?.();
841
+ return;
842
+ }
600
843
  entry.promise = promise;
601
844
  try {
602
845
  const result = await promise;
@@ -658,6 +901,7 @@ function query(key, fetcher, options = {}) {
658
901
  oldEntry.subscribers--;
659
902
  if (oldEntry.subscribers <= 0 && cacheTime >= 0) {
660
903
  const oldKey = currentKey;
904
+ if (oldEntry.gcTimer !== null) clearTimeout(oldEntry.gcTimer);
661
905
  oldEntry.gcTimer = setTimeout(() => queryCache.delete(oldKey), cacheTime);
662
906
  }
663
907
  }
@@ -665,7 +909,7 @@ function query(key, fetcher, options = {}) {
665
909
  const keyChanged = currentKey !== key2;
666
910
  currentKey = key2;
667
911
  const entry = getOrCreateEntry(key2, initialData);
668
- if (keyChanged || entry.subscribers === 0) entry.subscribers++;
912
+ if (keyChanged) entry.subscribers++;
669
913
  if (entry.gcTimer !== null) {
670
914
  clearTimeout(entry.gcTimer);
671
915
  entry.gcTimer = null;
@@ -680,6 +924,11 @@ function query(key, fetcher, options = {}) {
680
924
  setError(entry.error);
681
925
  });
682
926
  }
927
+ if (!keyChanged && currentKey === key2 && entry.data !== void 0) {
928
+ const isDataStale2 = entry.dataUpdatedAt === 0 || Date.now() - entry.dataUpdatedAt >= staleTime;
929
+ if (enabled && isDataStale2 && !entry.promise) doFetch();
930
+ return;
931
+ }
683
932
  const isDataStale = entry.dataUpdatedAt === 0 || Date.now() - entry.dataUpdatedAt >= staleTime;
684
933
  if (enabled && (entry.data === void 0 || isDataStale)) {
685
934
  doFetch();
@@ -707,6 +956,7 @@ function query(key, fetcher, options = {}) {
707
956
  }
708
957
  }
709
958
  function dispose() {
959
+ if (disposed) return;
710
960
  disposed = true;
711
961
  abortController?.abort();
712
962
  effectCleanup();
@@ -719,12 +969,17 @@ function query(key, fetcher, options = {}) {
719
969
  entry.subscribers--;
720
970
  if (entry.subscribers <= 0 && cacheTime >= 0) {
721
971
  const key2 = currentKey;
972
+ if (entry.gcTimer !== null) clearTimeout(entry.gcTimer);
722
973
  entry.gcTimer = setTimeout(() => queryCache.delete(key2), cacheTime);
723
974
  }
724
975
  }
725
976
  }
726
- if (focusHandler) globalThis.removeEventListener("focus", focusHandler);
727
- if (onlineHandler) globalThis.removeEventListener("online", onlineHandler);
977
+ if (focusHandler && typeof globalThis.removeEventListener === "function") {
978
+ globalThis.removeEventListener("focus", focusHandler);
979
+ }
980
+ if (onlineHandler && typeof globalThis.removeEventListener === "function") {
981
+ globalThis.removeEventListener("online", onlineHandler);
982
+ }
728
983
  }
729
984
  return {
730
985
  data,
@@ -768,7 +1023,19 @@ function clearQueryCache() {
768
1023
  }
769
1024
  queryCache.clear();
770
1025
  for (const listener of activeListeners) listener();
771
- for (const refetcher of activeRefetchers) refetcher();
1026
+ for (const refetcher of activeRefetchers) {
1027
+ refetcher().catch((err) => {
1028
+ if (typeof console !== "undefined") {
1029
+ console.warn("[SibuJS query] refetch after clearQueryCache failed:", err);
1030
+ }
1031
+ });
1032
+ }
1033
+ }
1034
+ function __resetQueryCache() {
1035
+ for (const entry of queryCache.values()) {
1036
+ if (entry.gcTimer) clearTimeout(entry.gcTimer);
1037
+ }
1038
+ queryCache.clear();
772
1039
  }
773
1040
 
774
1041
  // src/data/mutation.ts
@@ -831,7 +1098,10 @@ function mutation(mutationFn, options = {}) {
831
1098
  isSuccess,
832
1099
  isIdle,
833
1100
  mutate: (variables) => {
834
- execute(variables).catch(() => {
1101
+ execute(variables).catch((err) => {
1102
+ if (typeof console !== "undefined") {
1103
+ console.warn("[SibuJS mutation] mutate() failed; check `.error()` signal or onError option.", err);
1104
+ }
835
1105
  });
836
1106
  },
837
1107
  mutateAsync: execute,
@@ -867,11 +1137,13 @@ function infiniteQuery(key, fetcher, options) {
867
1137
  const hasPreviousPage = derived(() => prevPageParam() !== void 0);
868
1138
  let abortController = null;
869
1139
  let disposed = false;
1140
+ let runId = 0;
870
1141
  async function fetchPage(pageParam, direction) {
871
1142
  if (disposed) return;
872
1143
  abortController?.abort();
873
1144
  abortController = new AbortController();
874
1145
  const signal2 = abortController.signal;
1146
+ const myRun = ++runId;
875
1147
  batch(() => {
876
1148
  setIsFetching(true);
877
1149
  if (direction === "next") setIsFetchingNext(true);
@@ -880,7 +1152,7 @@ function infiniteQuery(key, fetcher, options) {
880
1152
  });
881
1153
  try {
882
1154
  const page = await withRetry(() => fetcher({ signal: signal2, pageParam }), retryOptions, void 0, signal2);
883
- if (disposed) return;
1155
+ if (disposed || myRun !== runId) return;
884
1156
  const currentPages = pages();
885
1157
  let newPages;
886
1158
  if (direction === "prev") {
@@ -900,7 +1172,7 @@ function infiniteQuery(key, fetcher, options) {
900
1172
  });
901
1173
  onSuccess?.(newPages);
902
1174
  } catch (err) {
903
- if (disposed) return;
1175
+ if (disposed || myRun !== runId) return;
904
1176
  if (err instanceof DOMException && err.name === "AbortError") return;
905
1177
  const errorObj = err instanceof Error ? err : new Error(String(err));
906
1178
  batch(() => {
@@ -915,9 +1187,12 @@ function infiniteQuery(key, fetcher, options) {
915
1187
  const effectCleanup = effect(() => {
916
1188
  resolveKey();
917
1189
  if (enabled) {
918
- setPages([]);
919
- setNextPageParam(initialPageParam);
920
- setPrevPageParam(void 0);
1190
+ abortController?.abort();
1191
+ batch(() => {
1192
+ setPages([]);
1193
+ setNextPageParam(initialPageParam);
1194
+ setPrevPageParam(void 0);
1195
+ });
921
1196
  fetchPage(initialPageParam, "initial");
922
1197
  }
923
1198
  });
@@ -1154,18 +1429,57 @@ function idbPut(db, store, item) {
1154
1429
  tx.onerror = () => reject(tx.error);
1155
1430
  });
1156
1431
  }
1157
- function idbDelete(db, store, key) {
1432
+ function idbPutWithChange(db, item, change) {
1433
+ return new Promise((resolve, reject) => {
1434
+ const tx = db.transaction(["items", "_changes"], "readwrite");
1435
+ tx.objectStore("items").put(item);
1436
+ tx.objectStore("_changes").put(change);
1437
+ tx.oncomplete = () => resolve();
1438
+ tx.onerror = () => reject(tx.error);
1439
+ });
1440
+ }
1441
+ function idbDeleteWithChange(db, key, change) {
1442
+ return new Promise((resolve, reject) => {
1443
+ const tx = db.transaction(["items", "_changes"], "readwrite");
1444
+ tx.objectStore("items").delete(key);
1445
+ tx.objectStore("_changes").put(change);
1446
+ tx.oncomplete = () => resolve();
1447
+ tx.onerror = () => reject(tx.error);
1448
+ });
1449
+ }
1450
+ function idbGetAllWithKeys(db, store) {
1451
+ return new Promise((resolve, reject) => {
1452
+ const tx = db.transaction(store, "readonly");
1453
+ const out = [];
1454
+ const req = tx.objectStore(store).openCursor();
1455
+ req.onsuccess = () => {
1456
+ const cursor = req.result;
1457
+ if (cursor) {
1458
+ out.push({ key: cursor.primaryKey, value: cursor.value });
1459
+ cursor.continue();
1460
+ } else {
1461
+ resolve(out);
1462
+ }
1463
+ };
1464
+ req.onerror = () => reject(req.error);
1465
+ });
1466
+ }
1467
+ function idbDeleteKeys(db, store, keys) {
1468
+ if (keys.length === 0) return Promise.resolve();
1158
1469
  return new Promise((resolve, reject) => {
1159
1470
  const tx = db.transaction(store, "readwrite");
1160
- tx.objectStore(store).delete(key);
1471
+ const objStore = tx.objectStore(store);
1472
+ for (const k of keys) objStore.delete(k);
1161
1473
  tx.oncomplete = () => resolve();
1162
1474
  tx.onerror = () => reject(tx.error);
1163
1475
  });
1164
1476
  }
1165
- function idbClear(db, store) {
1477
+ function idbPutMany(db, store, items) {
1478
+ if (items.length === 0) return Promise.resolve();
1166
1479
  return new Promise((resolve, reject) => {
1167
1480
  const tx = db.transaction(store, "readwrite");
1168
- tx.objectStore(store).clear();
1481
+ const objStore = tx.objectStore(store);
1482
+ for (const item of items) objStore.put(item);
1169
1483
  tx.oncomplete = () => resolve();
1170
1484
  tx.onerror = () => reject(tx.error);
1171
1485
  });
@@ -1188,15 +1502,13 @@ async function offlineStore(options) {
1188
1502
  setPendingCount(changes.length);
1189
1503
  }
1190
1504
  async function put(item) {
1191
- await idbPut(db, "items", item);
1192
- await idbPut(db, "_changes", { type: "put", item, timestamp: Date.now() });
1505
+ await idbPutWithChange(db, item, { type: "put", item, timestamp: Date.now() });
1193
1506
  await refreshData();
1194
1507
  }
1195
1508
  async function remove(key) {
1196
1509
  const existing = await idbGet(db, "items", key);
1197
1510
  if (existing) {
1198
- await idbDelete(db, "items", key);
1199
- await idbPut(db, "_changes", { type: "delete", item: existing, timestamp: Date.now() });
1511
+ await idbDeleteWithChange(db, key, { type: "delete", item: existing, timestamp: Date.now() });
1200
1512
  await refreshData();
1201
1513
  }
1202
1514
  }
@@ -1207,25 +1519,45 @@ async function offlineStore(options) {
1207
1519
  return data().filter(filter);
1208
1520
  }
1209
1521
  async function sync() {
1210
- if (!adapter || isSyncing()) return;
1522
+ if (!adapter || isSyncing() || closed) return;
1211
1523
  setIsSyncing(true);
1212
1524
  try {
1213
- const changes = await idbGetAll(db, "_changes");
1214
- if (changes.length > 0) {
1215
- const result = await adapter.push(changes);
1525
+ const snapshot = await idbGetAllWithKeys(db, "_changes");
1526
+ if (closed) return;
1527
+ if (snapshot.length > 0) {
1528
+ const result = await adapter.push(snapshot.map((e) => e.value));
1529
+ if (closed) return;
1216
1530
  if (result.ok) {
1217
- await idbClear(db, "_changes");
1531
+ await idbDeleteKeys(
1532
+ db,
1533
+ "_changes",
1534
+ snapshot.map((e) => e.key)
1535
+ );
1536
+ if (closed) return;
1218
1537
  }
1219
1538
  }
1220
1539
  const remoteItems = await adapter.pull(lastSynced());
1221
- for (const item of remoteItems) {
1222
- await idbPut(db, "items", item);
1540
+ if (closed) return;
1541
+ const pendingChanges = await idbGetAll(db, "_changes");
1542
+ if (closed) return;
1543
+ const pendingKeys = /* @__PURE__ */ new Set();
1544
+ for (const c of pendingChanges) {
1545
+ const k = c.item[keyPath];
1546
+ if (k != null) pendingKeys.add(k);
1223
1547
  }
1548
+ const safeRemote = remoteItems.filter((item) => {
1549
+ const k = item[keyPath];
1550
+ return k == null || !pendingKeys.has(k);
1551
+ });
1552
+ await idbPutMany(db, "items", safeRemote);
1553
+ if (closed) return;
1224
1554
  const now = Date.now();
1225
1555
  await idbPut(db, "_meta", now);
1556
+ if (closed) return;
1226
1557
  setLastSynced(now);
1227
1558
  await refreshData();
1228
- } catch {
1559
+ } catch (err) {
1560
+ if (typeof console !== "undefined") console.warn("[offlineStore] sync failed", err);
1229
1561
  } finally {
1230
1562
  setIsSyncing(false);
1231
1563
  }
@@ -1233,13 +1565,21 @@ async function offlineStore(options) {
1233
1565
  function attach(newAdapter) {
1234
1566
  adapter = newAdapter;
1235
1567
  }
1568
+ let onlineHandler = null;
1569
+ let closed = false;
1236
1570
  function close() {
1571
+ closed = true;
1572
+ if (onlineHandler && typeof window !== "undefined") {
1573
+ window.removeEventListener("online", onlineHandler);
1574
+ onlineHandler = null;
1575
+ }
1237
1576
  db.close();
1238
1577
  }
1239
1578
  if (autoSync && typeof window !== "undefined") {
1240
- window.addEventListener("online", () => {
1579
+ onlineHandler = () => {
1241
1580
  sync();
1242
- });
1581
+ };
1582
+ window.addEventListener("online", onlineHandler);
1243
1583
  }
1244
1584
  return {
1245
1585
  data,
@@ -1262,9 +1602,11 @@ function syncAdapter(config) {
1262
1602
  // src/core/rendering/context.ts
1263
1603
  function context(defaultValue) {
1264
1604
  const [getValue, setValue] = signal(defaultValue);
1265
- return {
1605
+ const ctx = {
1266
1606
  provide(value) {
1607
+ const previous2 = getValue();
1267
1608
  setValue(value);
1609
+ return () => setValue(previous2);
1268
1610
  },
1269
1611
  use() {
1270
1612
  return getValue;
@@ -1274,8 +1616,18 @@ function context(defaultValue) {
1274
1616
  },
1275
1617
  set(value) {
1276
1618
  setValue(value);
1619
+ },
1620
+ withContext(value, fn) {
1621
+ const previous2 = getValue();
1622
+ setValue(value);
1623
+ try {
1624
+ return fn();
1625
+ } finally {
1626
+ setValue(previous2);
1627
+ }
1277
1628
  }
1278
1629
  };
1630
+ return ctx;
1279
1631
  }
1280
1632
 
1281
1633
  // src/data/routeLoader.ts
@@ -1296,9 +1648,16 @@ function loaderData() {
1296
1648
  error: resource2.error
1297
1649
  };
1298
1650
  }
1299
- async function preloadRoute(route, context2) {
1651
+ async function preloadRoute(route, context2, callerSignal) {
1300
1652
  if (!route.loader) return void 0;
1301
1653
  const controller = new AbortController();
1654
+ if (callerSignal) {
1655
+ if (callerSignal.aborted) {
1656
+ controller.abort();
1657
+ } else {
1658
+ callerSignal.addEventListener("abort", () => controller.abort(), { once: true });
1659
+ }
1660
+ }
1302
1661
  return route.loader(context2, { signal: controller.signal });
1303
1662
  }
1304
1663
 
@@ -1313,7 +1672,7 @@ function validateWsUrl(raw) {
1313
1672
  function socket(url, options) {
1314
1673
  const autoReconnect = options?.autoReconnect ?? false;
1315
1674
  const reconnectDelay = options?.reconnectDelay ?? 1e3;
1316
- const maxReconnects = options?.maxReconnects ?? Infinity;
1675
+ const maxReconnects = options?.maxReconnects ?? 10;
1317
1676
  const heartbeat = options?.heartbeat;
1318
1677
  const protocols = options?.protocols;
1319
1678
  const [data, setData] = signal(null);
@@ -1347,13 +1706,19 @@ function socket(url, options) {
1347
1706
  ws.onclose = () => {
1348
1707
  setStatus("closed");
1349
1708
  stopHeartbeat();
1350
- if (autoReconnect && !disposed && !manuallyClosed && reconnectCount < maxReconnects) {
1709
+ const wasManual = manuallyClosed;
1710
+ manuallyClosed = false;
1711
+ if (autoReconnect && !disposed && !wasManual && reconnectCount < maxReconnects) {
1712
+ const cap = 3e4;
1713
+ const delay = Math.min(cap, reconnectDelay * 2 ** reconnectCount);
1714
+ const jittered = delay * (0.5 + Math.random() * 0.5);
1351
1715
  reconnectCount++;
1352
1716
  reconnectTimer = setTimeout(() => {
1717
+ reconnectTimer = null;
1718
+ if (disposed || manuallyClosed) return;
1353
1719
  connect();
1354
- }, reconnectDelay);
1720
+ }, jittered);
1355
1721
  }
1356
- manuallyClosed = false;
1357
1722
  };
1358
1723
  ws.onerror = () => {
1359
1724
  };
@@ -1399,13 +1764,24 @@ function socket(url, options) {
1399
1764
  }
1400
1765
 
1401
1766
  // src/utils/sanitize.ts
1767
+ var SAFE_URL_PROTOCOLS = ["http:", "https:", "mailto:", "tel:", "ftp:"];
1402
1768
  function sanitizeUrl(url) {
1403
1769
  const trimmed = url.replace(/[\x00-\x20\x7f-\x9f]+/g, "").trim();
1404
1770
  if (!trimmed) return "";
1405
1771
  const lower = trimmed.toLowerCase();
1406
- if (lower.startsWith("javascript:") || lower.startsWith("data:") || lower.startsWith("vbscript:") || lower.startsWith("blob:")) {
1407
- return "";
1772
+ let schemeEnd = -1;
1773
+ for (let i = 0; i < lower.length; i++) {
1774
+ const ch = lower.charCodeAt(i);
1775
+ if (ch === 58) {
1776
+ schemeEnd = i;
1777
+ break;
1778
+ }
1779
+ if (ch === 47 || ch === 63 || ch === 35) break;
1408
1780
  }
1781
+ if (schemeEnd === -1) return trimmed;
1782
+ const scheme = lower.slice(0, schemeEnd + 1);
1783
+ if (!/^[a-z][a-z0-9+.-]*:$/.test(scheme)) return trimmed;
1784
+ if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
1409
1785
  return trimmed;
1410
1786
  }
1411
1787
 
@@ -1417,12 +1793,16 @@ function validateSseUrl(raw) {
1417
1793
  }
1418
1794
  function stream(url, options) {
1419
1795
  const autoReconnect = options?.autoReconnect ?? false;
1796
+ const maxReconnects = options?.maxReconnects ?? 10;
1797
+ const baseMs = options?.reconnectBaseMs ?? 1e3;
1798
+ const maxMs = options?.reconnectMaxMs ?? 3e4;
1420
1799
  const [data, setData] = signal(null);
1421
1800
  const [event, setEvent] = signal(null);
1422
1801
  const [status, setStatus] = signal("connecting");
1423
1802
  let source = null;
1424
1803
  let disposed = false;
1425
1804
  let reconnectTimer = null;
1805
+ let attempts = 0;
1426
1806
  function connect() {
1427
1807
  if (disposed) return;
1428
1808
  const safeUrl = validateSseUrl(url);
@@ -1436,6 +1816,7 @@ function stream(url, options) {
1436
1816
  });
1437
1817
  source.onopen = () => {
1438
1818
  setStatus("open");
1819
+ attempts = 0;
1439
1820
  };
1440
1821
  source.onmessage = (evt) => {
1441
1822
  setData(evt.data);
@@ -1445,11 +1826,14 @@ function stream(url, options) {
1445
1826
  if (source && source.readyState === EventSource.CLOSED) {
1446
1827
  setStatus("closed");
1447
1828
  source = null;
1448
- if (autoReconnect && !disposed) {
1829
+ if (autoReconnect && !disposed && attempts < maxReconnects) {
1830
+ const delay = Math.min(maxMs, baseMs * 2 ** attempts);
1831
+ const jittered = delay * (0.5 + Math.random() * 0.5);
1832
+ attempts++;
1449
1833
  reconnectTimer = setTimeout(() => {
1450
1834
  reconnectTimer = null;
1451
1835
  connect();
1452
- }, 1e3);
1836
+ }, jittered);
1453
1837
  }
1454
1838
  }
1455
1839
  };
@@ -1474,6 +1858,7 @@ function stream(url, options) {
1474
1858
  }
1475
1859
  // Annotate the CommonJS export names for ESM import in node:
1476
1860
  0 && (module.exports = {
1861
+ __resetQueryCache,
1477
1862
  calculateDelay,
1478
1863
  clearQueryCache,
1479
1864
  debounce,