sibujs 1.4.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 (190) hide show
  1. package/README.md +105 -119
  2. package/dist/browser.cjs +288 -80
  3. package/dist/browser.d.cts +19 -9
  4. package/dist/browser.d.ts +19 -9
  5. package/dist/browser.js +6 -6
  6. package/dist/build.cjs +1019 -313
  7. package/dist/build.d.cts +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.js +15 -13
  10. package/dist/cdn.global.js +17 -16
  11. package/dist/chunk-2RA7SHDA.js +65 -0
  12. package/dist/chunk-2UPRY23K.js +80 -0
  13. package/dist/chunk-3JHCYHWN.js +125 -0
  14. package/dist/{chunk-ZWKZCBO6.js → chunk-3LR7GLWQ.js} +154 -33
  15. package/dist/{chunk-3AIRKM3B.js → chunk-3NSGB5JN.js} +115 -34
  16. package/dist/{chunk-3ARAQO7B.js → chunk-52YJLLRO.js} +29 -6
  17. package/dist/chunk-54EDRCEF.js +93 -0
  18. package/dist/chunk-7JDB7I65.js +1327 -0
  19. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  20. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  21. package/dist/{chunk-WR5D4EGH.js → chunk-GTBNNBJ6.js} +14 -2
  22. package/dist/chunk-HB24TBAF.js +121 -0
  23. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  24. package/dist/{chunk-JAKHTMQU.js → chunk-JA6667UN.js} +206 -46
  25. package/dist/{chunk-77L6NL3X.js → chunk-JXMMDLBY.js} +306 -183
  26. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  27. package/dist/{chunk-F3FA4F32.js → chunk-KLRMB5ZS.js} +135 -79
  28. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  29. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  30. package/dist/{chunk-TSOKIX5Z.js → chunk-MIUAXB7K.js} +126 -74
  31. package/dist/{chunk-QWZG56ET.js → chunk-ND2664SF.js} +558 -190
  32. package/dist/{chunk-JCI5M6U6.js → chunk-O2MNQFLP.js} +261 -79
  33. package/dist/{chunk-EWFVA3TJ.js → chunk-R73P76YZ.js} +1 -1
  34. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  35. package/dist/chunk-UCS6AMJ7.js +79 -0
  36. package/dist/{chunk-ZD6OAMTH.js → chunk-VLPPXTYG.js} +90 -35
  37. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  38. package/dist/{contracts-xo5ckdRP.d.cts → contracts-ey_Qh8ef.d.cts} +7 -8
  39. package/dist/{contracts-xo5ckdRP.d.ts → contracts-ey_Qh8ef.d.ts} +7 -8
  40. package/dist/{customElement-D2DJp_xn.d.cts → customElement-CPfIrbvg.d.cts} +18 -9
  41. package/dist/{customElement-D2DJp_xn.d.ts → customElement-CPfIrbvg.d.ts} +18 -9
  42. package/dist/data.cjs +452 -100
  43. package/dist/data.d.cts +20 -2
  44. package/dist/data.d.ts +20 -2
  45. package/dist/data.js +11 -9
  46. package/dist/devtools.cjs +535 -247
  47. package/dist/devtools.d.cts +1 -1
  48. package/dist/devtools.d.ts +1 -1
  49. package/dist/devtools.js +34 -30
  50. package/dist/ecosystem.cjs +499 -143
  51. package/dist/ecosystem.d.cts +13 -11
  52. package/dist/ecosystem.d.ts +13 -11
  53. package/dist/ecosystem.js +12 -11
  54. package/dist/extras.cjs +3639 -1629
  55. package/dist/extras.d.cts +11 -11
  56. package/dist/extras.d.ts +11 -11
  57. package/dist/extras.js +58 -45
  58. package/dist/index.cjs +1023 -313
  59. package/dist/index.d.cts +128 -55
  60. package/dist/index.d.ts +128 -55
  61. package/dist/index.js +28 -16
  62. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  63. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  64. package/dist/motion.cjs +90 -36
  65. package/dist/motion.d.cts +1 -1
  66. package/dist/motion.d.ts +1 -1
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +414 -81
  69. package/dist/patterns.d.cts +53 -20
  70. package/dist/patterns.d.ts +53 -20
  71. package/dist/patterns.js +7 -7
  72. package/dist/performance.cjs +364 -108
  73. package/dist/performance.d.cts +29 -17
  74. package/dist/performance.d.ts +29 -17
  75. package/dist/performance.js +13 -6
  76. package/dist/plugin-D30wlGW5.d.cts +71 -0
  77. package/dist/plugin-D30wlGW5.d.ts +71 -0
  78. package/dist/plugins.cjs +652 -271
  79. package/dist/plugins.d.cts +13 -6
  80. package/dist/plugins.d.ts +13 -6
  81. package/dist/plugins.js +116 -50
  82. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  83. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  84. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  85. package/dist/ssr.cjs +648 -219
  86. package/dist/ssr.d.cts +27 -7
  87. package/dist/ssr.d.ts +27 -7
  88. package/dist/ssr.js +12 -11
  89. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.cts} +9 -1
  90. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.ts} +9 -1
  91. package/dist/testing.cjs +252 -63
  92. package/dist/testing.d.cts +17 -4
  93. package/dist/testing.d.ts +17 -4
  94. package/dist/testing.js +100 -44
  95. package/dist/ui.cjs +576 -168
  96. package/dist/ui.d.cts +13 -16
  97. package/dist/ui.d.ts +13 -16
  98. package/dist/ui.js +20 -17
  99. package/dist/widgets.cjs +1001 -93
  100. package/dist/widgets.d.cts +104 -2
  101. package/dist/widgets.d.ts +104 -2
  102. package/dist/widgets.js +9 -7
  103. package/package.json +8 -2
  104. package/dist/chunk-32DY64NT.js +0 -282
  105. package/dist/chunk-3CRQALYP.js +0 -877
  106. package/dist/chunk-4EI4AG32.js +0 -482
  107. package/dist/chunk-4MYMUBRS.js +0 -21
  108. package/dist/chunk-6HLLIF3K.js +0 -398
  109. package/dist/chunk-6LSNVCS2.js +0 -937
  110. package/dist/chunk-6SA3QQES.js +0 -61
  111. package/dist/chunk-7BF6TK55.js +0 -1097
  112. package/dist/chunk-7TQKR4PP.js +0 -294
  113. package/dist/chunk-7V26P53V.js +0 -712
  114. package/dist/chunk-AZ3ISID5.js +0 -298
  115. package/dist/chunk-B7SWRFUT.js +0 -332
  116. package/dist/chunk-BGN5ZMP4.js +0 -26
  117. package/dist/chunk-BTU3TJDS.js +0 -365
  118. package/dist/chunk-BW3WT46K.js +0 -937
  119. package/dist/chunk-C6KFWOFV.js +0 -616
  120. package/dist/chunk-CHF5OHIA.js +0 -61
  121. package/dist/chunk-CHJ27IGK.js +0 -26
  122. package/dist/chunk-CMBFNA7L.js +0 -27
  123. package/dist/chunk-DAHRH4ON.js +0 -331
  124. package/dist/chunk-DKOHBI74.js +0 -924
  125. package/dist/chunk-DTCOOBMX.js +0 -725
  126. package/dist/chunk-EBGIRKQY.js +0 -616
  127. package/dist/chunk-EUZND3CB.js +0 -27
  128. package/dist/chunk-EVCZO745.js +0 -365
  129. package/dist/chunk-FGOEVHY3.js +0 -60
  130. package/dist/chunk-G3BOQPVO.js +0 -365
  131. package/dist/chunk-GCOK2LC3.js +0 -282
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-K5ZUMYVS.js +0 -89
  134. package/dist/chunk-KQPDEVVS.js +0 -398
  135. package/dist/chunk-L6JRBDNS.js +0 -60
  136. package/dist/chunk-LA6KQEDU.js +0 -712
  137. package/dist/chunk-MDVXJWFN.js +0 -304
  138. package/dist/chunk-MEZVEBPN.js +0 -2008
  139. package/dist/chunk-MK4ERFYL.js +0 -2249
  140. package/dist/chunk-MLKGABMK.js +0 -9
  141. package/dist/chunk-MQ5GOYPH.js +0 -2249
  142. package/dist/chunk-N6IZB6KJ.js +0 -567
  143. package/dist/chunk-NEKUBFPT.js +0 -60
  144. package/dist/chunk-NHUC2QWH.js +0 -282
  145. package/dist/chunk-NMRUZALC.js +0 -1097
  146. package/dist/chunk-NYVAC6P5.js +0 -37
  147. package/dist/chunk-OF7UZIVB.js +0 -725
  148. package/dist/chunk-P6W3STU4.js +0 -2249
  149. package/dist/chunk-PBHF5WKN.js +0 -616
  150. package/dist/chunk-PTQJDMRT.js +0 -146
  151. package/dist/chunk-PZEGYCF5.js +0 -61
  152. package/dist/chunk-QBMDLBU2.js +0 -975
  153. package/dist/chunk-RQGQSLQK.js +0 -725
  154. package/dist/chunk-SDLZDHKP.js +0 -107
  155. package/dist/chunk-TNQWPPE6.js +0 -37
  156. package/dist/chunk-UHNL42EF.js +0 -2730
  157. package/dist/chunk-UNXCEF6S.js +0 -21
  158. package/dist/chunk-V2XTI523.js +0 -347
  159. package/dist/chunk-VAU366PN.js +0 -2241
  160. package/dist/chunk-VMVDTCXB.js +0 -712
  161. package/dist/chunk-VRW3FULF.js +0 -725
  162. package/dist/chunk-WADYRCO2.js +0 -304
  163. package/dist/chunk-WILQZRO4.js +0 -282
  164. package/dist/chunk-WUHJISPP.js +0 -298
  165. package/dist/chunk-XYU6TZOW.js +0 -182
  166. package/dist/chunk-Y6GP4QGG.js +0 -276
  167. package/dist/chunk-YECR7UIA.js +0 -347
  168. package/dist/chunk-YUTWTI4B.js +0 -654
  169. package/dist/chunk-Z65KYU7I.js +0 -26
  170. package/dist/chunk-Z6POF5YC.js +0 -975
  171. package/dist/chunk-ZBJP6WFL.js +0 -482
  172. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  173. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  174. package/dist/contracts-DOrhwbke.d.cts +0 -245
  175. package/dist/contracts-DOrhwbke.d.ts +0 -245
  176. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  177. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  178. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  179. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  180. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  181. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  182. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  183. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  184. package/dist/ssr-3RXHP5ES.js +0 -38
  185. package/dist/ssr-6GIMY5MX.js +0 -38
  186. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  187. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  188. package/dist/ssr-WKUPVSSK.js +0 -36
  189. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  190. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/patterns.cjs CHANGED
@@ -50,12 +50,12 @@ function isDev() {
50
50
  var _isDev = isDev();
51
51
  function devAssert(condition, message) {
52
52
  if (_isDev && !condition) {
53
- throw new Error(`[Sibu] ${message}`);
53
+ throw new Error(`[SibuJS] ${message}`);
54
54
  }
55
55
  }
56
56
  function devWarn(message) {
57
57
  if (_isDev) {
58
- console.warn(`[Sibu] ${message}`);
58
+ console.warn(`[SibuJS] ${message}`);
59
59
  }
60
60
  }
61
61
 
@@ -65,11 +65,11 @@ var subscriberStack = new Array(32);
65
65
  var stackCapacity = 32;
66
66
  var stackTop = -1;
67
67
  var currentSubscriber = null;
68
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
69
68
  var SUBS = "__s";
70
69
  var notifyDepth = 0;
71
70
  var pendingQueue = [];
72
71
  var pendingSet = /* @__PURE__ */ new Set();
72
+ var propagateStack = [];
73
73
  function safeInvoke(sub) {
74
74
  try {
75
75
  sub();
@@ -78,6 +78,15 @@ function safeInvoke(sub) {
78
78
  }
79
79
  }
80
80
  var trackingSuspended = false;
81
+ function retrack(effectFn, subscriber) {
82
+ const prev = currentSubscriber;
83
+ currentSubscriber = subscriber;
84
+ try {
85
+ effectFn();
86
+ } finally {
87
+ currentSubscriber = prev;
88
+ }
89
+ }
81
90
  function track(effectFn, subscriber) {
82
91
  if (!subscriber) subscriber = effectFn;
83
92
  cleanup(subscriber);
@@ -116,7 +125,6 @@ function recordDependency(signal2) {
116
125
  let subs = signal2[SUBS];
117
126
  if (!subs) {
118
127
  subs = /* @__PURE__ */ new Set();
119
- signalSubscribers.set(signal2, subs);
120
128
  signal2[SUBS] = subs;
121
129
  }
122
130
  subs.add(currentSubscriber);
@@ -126,42 +134,83 @@ function recordDependency(signal2) {
126
134
  signal2.__f = void 0;
127
135
  }
128
136
  }
137
+ function queueSignalNotification(signal2) {
138
+ const subs = signal2[SUBS];
139
+ if (!subs) return;
140
+ for (const sub of subs) {
141
+ if (sub._c) {
142
+ propagateDirty(sub);
143
+ } else if (!pendingSet.has(sub)) {
144
+ pendingSet.add(sub);
145
+ pendingQueue.push(sub);
146
+ }
147
+ }
148
+ }
149
+ var maxDrainIterations = 1e5;
150
+ function drainNotificationQueue() {
151
+ if (notifyDepth > 0) return;
152
+ notifyDepth++;
153
+ try {
154
+ let i = 0;
155
+ while (i < pendingQueue.length) {
156
+ if (i >= maxDrainIterations) {
157
+ if (typeof console !== "undefined") {
158
+ console.error(
159
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
160
+ );
161
+ }
162
+ break;
163
+ }
164
+ safeInvoke(pendingQueue[i]);
165
+ i++;
166
+ }
167
+ } finally {
168
+ notifyDepth--;
169
+ if (notifyDepth === 0) {
170
+ pendingQueue.length = 0;
171
+ pendingSet.clear();
172
+ }
173
+ }
174
+ }
129
175
  function propagateDirty(sub) {
130
176
  sub();
131
- let sig = sub._sig;
132
- while (sig) {
177
+ const rootSig = sub._sig;
178
+ if (!rootSig) return;
179
+ const stack = propagateStack;
180
+ const baseLen = stack.length;
181
+ stack.push(rootSig);
182
+ while (stack.length > baseLen) {
183
+ const sig = stack.pop();
133
184
  const first = sig.__f;
134
185
  if (first) {
135
186
  if (first._c) {
136
187
  const nSig = first._sig;
137
- nSig._d = true;
138
- sig = nSig;
139
- continue;
140
- }
141
- if (!pendingSet.has(first)) {
188
+ if (!nSig._d) {
189
+ nSig._d = true;
190
+ stack.push(nSig);
191
+ }
192
+ } else if (!pendingSet.has(first)) {
142
193
  pendingSet.add(first);
143
194
  pendingQueue.push(first);
144
195
  }
145
- break;
196
+ continue;
146
197
  }
147
198
  const subs = sig[SUBS];
148
- if (!subs) break;
149
- let nextSig;
199
+ if (!subs) continue;
150
200
  for (const s of subs) {
151
201
  if (s._c) {
152
- s();
153
202
  const nSig = s._sig;
154
- if (nSig && !nextSig) {
155
- nextSig = nSig;
156
- } else if (nSig) {
157
- propagateDirty(s);
203
+ if (nSig && !nSig._d) {
204
+ nSig._d = true;
205
+ stack.push(nSig);
206
+ } else if (!nSig) {
207
+ s();
158
208
  }
159
209
  } else if (!pendingSet.has(s)) {
160
210
  pendingSet.add(s);
161
211
  pendingQueue.push(s);
162
212
  }
163
213
  }
164
- sig = nextSig;
165
214
  }
166
215
  }
167
216
  function notifySubscribers(signal2) {
@@ -185,13 +234,23 @@ function notifySubscribers(signal2) {
185
234
  }
186
235
  let i = 0;
187
236
  while (i < pendingQueue.length) {
237
+ if (i >= maxDrainIterations) {
238
+ if (typeof console !== "undefined") {
239
+ console.error(
240
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
241
+ );
242
+ }
243
+ break;
244
+ }
188
245
  safeInvoke(pendingQueue[i]);
189
246
  i++;
190
247
  }
191
248
  } finally {
192
- pendingQueue.length = 0;
193
- pendingSet.clear();
194
249
  notifyDepth--;
250
+ if (notifyDepth === 0) {
251
+ pendingQueue.length = 0;
252
+ pendingSet.clear();
253
+ }
195
254
  }
196
255
  return;
197
256
  }
@@ -211,30 +270,48 @@ function notifySubscribers(signal2) {
211
270
  notifyDepth++;
212
271
  try {
213
272
  let directCount = 0;
273
+ let hasComputedSub = false;
214
274
  for (const sub of subs) {
275
+ if (sub._c) hasComputedSub = true;
215
276
  pendingQueue[directCount++] = sub;
216
277
  }
217
- for (let i2 = 0; i2 < directCount; i2++) {
218
- if (pendingQueue[i2]._c) {
219
- propagateDirty(pendingQueue[i2]);
278
+ if (!hasComputedSub) {
279
+ for (let i2 = 0; i2 < directCount; i2++) {
280
+ safeInvoke(pendingQueue[i2]);
220
281
  }
221
- }
222
- for (let i2 = 0; i2 < directCount; i2++) {
223
- if (!pendingQueue[i2]._c) {
224
- if (!pendingSet.has(pendingQueue[i2])) {
225
- safeInvoke(pendingQueue[i2]);
282
+ } else {
283
+ for (let i2 = 0; i2 < directCount; i2++) {
284
+ if (pendingQueue[i2]._c) {
285
+ propagateDirty(pendingQueue[i2]);
286
+ }
287
+ }
288
+ for (let i2 = 0; i2 < directCount; i2++) {
289
+ const sub = pendingQueue[i2];
290
+ if (!sub._c && !pendingSet.has(sub)) {
291
+ pendingSet.add(sub);
292
+ safeInvoke(sub);
226
293
  }
227
294
  }
228
295
  }
229
296
  let i = directCount;
230
297
  while (i < pendingQueue.length) {
298
+ if (i - directCount >= maxDrainIterations) {
299
+ if (typeof console !== "undefined") {
300
+ console.error(
301
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
302
+ );
303
+ }
304
+ break;
305
+ }
231
306
  safeInvoke(pendingQueue[i]);
232
307
  i++;
233
308
  }
234
309
  } finally {
235
- pendingQueue.length = 0;
236
- pendingSet.clear();
237
310
  notifyDepth--;
311
+ if (notifyDepth === 0) {
312
+ pendingQueue.length = 0;
313
+ pendingSet.clear();
314
+ }
238
315
  }
239
316
  }
240
317
  function cleanup(subscriber) {
@@ -245,7 +322,9 @@ function cleanup(subscriber) {
245
322
  if (subs) {
246
323
  subs.delete(subscriber);
247
324
  if (singleDep.__f === subscriber) {
248
- singleDep.__f = void 0;
325
+ singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
326
+ } else if (subs.size === 1 && singleDep.__f === void 0) {
327
+ singleDep.__f = subs.values().next().value;
249
328
  }
250
329
  }
251
330
  sub._dep = void 0;
@@ -258,7 +337,9 @@ function cleanup(subscriber) {
258
337
  if (subs) {
259
338
  subs.delete(subscriber);
260
339
  if (signal2.__f === subscriber) {
261
- signal2.__f = void 0;
340
+ signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
341
+ } else if (subs.size === 1 && signal2.__f === void 0) {
342
+ signal2.__f = subs.values().next().value;
262
343
  }
263
344
  }
264
345
  }
@@ -268,11 +349,32 @@ function cleanup(subscriber) {
268
349
  // src/reactivity/batch.ts
269
350
  var batchDepth = 0;
270
351
  var pendingSignals = /* @__PURE__ */ new Set();
352
+ function batch(fn) {
353
+ batchDepth++;
354
+ try {
355
+ return fn();
356
+ } finally {
357
+ batchDepth--;
358
+ if (batchDepth === 0) {
359
+ flushBatch();
360
+ }
361
+ }
362
+ }
271
363
  function enqueueBatchedSignal(signal2) {
272
364
  if (batchDepth === 0) return false;
273
365
  pendingSignals.add(signal2);
274
366
  return true;
275
367
  }
368
+ function flushBatch() {
369
+ try {
370
+ for (const signal2 of pendingSignals) {
371
+ queueSignalNotification(signal2);
372
+ }
373
+ } finally {
374
+ pendingSignals.clear();
375
+ }
376
+ drainNotificationQueue();
377
+ }
276
378
 
277
379
  // src/core/signals/signal.ts
278
380
  var _g = globalThis;
@@ -375,9 +477,28 @@ function machine(config) {
375
477
  }
376
478
 
377
479
  // src/core/ssr-context.ts
378
- var ssrMode = false;
480
+ var als = null;
481
+ try {
482
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
483
+ const req = Function("return typeof require==='function'?require:null")();
484
+ if (req) {
485
+ const mod = req("node:async_hooks");
486
+ als = new mod.AsyncLocalStorage();
487
+ }
488
+ }
489
+ } catch {
490
+ als = null;
491
+ }
492
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
493
+ function getSSRStore() {
494
+ if (als) {
495
+ const s = als.getStore();
496
+ if (s) return s;
497
+ }
498
+ return fallbackStore;
499
+ }
379
500
  function isSSR() {
380
- return ssrMode;
501
+ return getSSRStore().ssr;
381
502
  }
382
503
 
383
504
  // src/core/signals/effect.ts
@@ -387,26 +508,86 @@ function effect(effectFn, options) {
387
508
  if (isSSR()) return () => {
388
509
  };
389
510
  const onError = options?.onError;
511
+ let userCleanups = [];
512
+ const onCleanup = (fn) => {
513
+ userCleanups.push(fn);
514
+ };
515
+ const runUserCleanups = () => {
516
+ if (userCleanups.length === 0) return;
517
+ const list = userCleanups;
518
+ userCleanups = [];
519
+ for (let i = list.length - 1; i >= 0; i--) {
520
+ try {
521
+ list[i]();
522
+ } catch (err) {
523
+ if (typeof console !== "undefined") {
524
+ console.warn("[SibuJS effect] onCleanup threw:", err);
525
+ }
526
+ }
527
+ }
528
+ };
529
+ const invokeBody = () => effectFn(onCleanup);
390
530
  const wrappedFn = onError ? () => {
391
531
  try {
392
- effectFn();
532
+ invokeBody();
393
533
  } catch (err) {
394
534
  onError(err);
395
535
  }
396
- } : effectFn;
536
+ } : invokeBody;
397
537
  let cleanupHandle = () => {
398
538
  };
539
+ let running = false;
399
540
  const subscriber = () => {
400
- cleanupHandle();
401
- cleanupHandle = track(wrappedFn, subscriber);
541
+ if (running) {
542
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
543
+ console.warn(
544
+ "[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."
545
+ );
546
+ }
547
+ return;
548
+ }
549
+ running = true;
550
+ try {
551
+ runUserCleanups();
552
+ cleanupHandle();
553
+ cleanupHandle = track(wrappedFn, subscriber);
554
+ } finally {
555
+ running = false;
556
+ }
402
557
  };
403
- cleanupHandle = track(wrappedFn, subscriber);
558
+ running = true;
559
+ try {
560
+ cleanupHandle = track(wrappedFn, subscriber);
561
+ } finally {
562
+ running = false;
563
+ }
404
564
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
405
565
  if (hook) hook.emit("effect:create", { effectFn });
566
+ let disposed = false;
406
567
  return () => {
568
+ if (disposed) return;
569
+ disposed = true;
407
570
  const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
408
- if (h) h.emit("effect:destroy", { effectFn });
409
- cleanupHandle();
571
+ if (h) {
572
+ try {
573
+ h.emit("effect:destroy", { effectFn });
574
+ } catch {
575
+ }
576
+ }
577
+ try {
578
+ runUserCleanups();
579
+ } catch (err) {
580
+ if (typeof console !== "undefined") {
581
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
582
+ }
583
+ }
584
+ try {
585
+ cleanupHandle();
586
+ } catch (err) {
587
+ if (typeof console !== "undefined") {
588
+ console.warn("[SibuJS effect] dispose threw:", err);
589
+ }
590
+ }
410
591
  };
411
592
  }
412
593
 
@@ -414,7 +595,11 @@ function effect(effectFn, options) {
414
595
  function persisted(key, initial, options = {}) {
415
596
  const storage = options.session ? sessionStorage : localStorage;
416
597
  const serialize = options.serialize || JSON.stringify;
417
- const deserialize = options.deserialize || JSON.parse;
598
+ const safeReviver = (k, v) => {
599
+ if (k === "__proto__" || k === "constructor" || k === "prototype") return void 0;
600
+ return v;
601
+ };
602
+ const deserialize = options.deserialize || ((raw) => JSON.parse(raw, safeReviver));
418
603
  const encrypt = options.encrypt;
419
604
  const decrypt = options.decrypt;
420
605
  const syncTabs = options.session ? false : options.syncTabs ?? true;
@@ -430,7 +615,7 @@ function persisted(key, initial, options = {}) {
430
615
  }
431
616
  const [value, setValue] = signal(restored);
432
617
  let applyingFromStorage = false;
433
- effect(() => {
618
+ const stopPersistEffect = effect(() => {
434
619
  const current = value();
435
620
  if (applyingFromStorage) return;
436
621
  try {
@@ -470,6 +655,7 @@ function persisted(key, initial, options = {}) {
470
655
  window.addEventListener("storage", storageListener);
471
656
  }
472
657
  const dispose = () => {
658
+ stopPersistEffect();
473
659
  if (storageListener && typeof window !== "undefined") {
474
660
  window.removeEventListener("storage", storageListener);
475
661
  storageListener = null;
@@ -487,68 +673,151 @@ function persisted(key, initial, options = {}) {
487
673
  // src/patterns/optimistic.ts
488
674
  function optimistic(initialValue) {
489
675
  const [value, setValue] = signal(initialValue);
490
- const [_pending, setPending] = signal(false);
491
- async function addOptimistic(optimisticValue, asyncAction) {
676
+ const [pending, setPending] = signal(false);
677
+ let inflightCount = 0;
678
+ let version = 0;
679
+ async function update(optimisticValue, asyncAction) {
680
+ const myVersion = ++version;
492
681
  const previousValue = value();
493
682
  setValue(optimisticValue);
683
+ inflightCount++;
494
684
  setPending(true);
495
685
  try {
496
686
  const result = await asyncAction();
497
- setValue(result);
687
+ if (version === myVersion) {
688
+ setValue(result);
689
+ }
498
690
  } catch {
499
- setValue(previousValue);
691
+ if (version === myVersion) {
692
+ setValue(previousValue);
693
+ }
500
694
  } finally {
501
- setPending(false);
695
+ inflightCount--;
696
+ if (inflightCount === 0) setPending(false);
502
697
  }
503
698
  }
504
- return [value, addOptimistic];
699
+ return { value, pending, update };
505
700
  }
506
701
  function optimisticList(initialValue) {
507
702
  const [items, setItems] = signal([...initialValue]);
508
- async function addOptimistic(item, asyncAction) {
703
+ const [pending, setPending] = signal(false);
704
+ let inflightCount = 0;
705
+ let version = 0;
706
+ let tempIdCounter = 0;
707
+ const itemIds = /* @__PURE__ */ new WeakMap();
708
+ const idToItem = /* @__PURE__ */ new Map();
709
+ function tagItem(item) {
710
+ const id = ++tempIdCounter;
711
+ if (item !== null && typeof item === "object") {
712
+ itemIds.set(item, id);
713
+ }
714
+ idToItem.set(id, item);
715
+ return id;
716
+ }
717
+ function findIndexById(list, id) {
718
+ for (let i = 0; i < list.length; i++) {
719
+ const it = list[i];
720
+ if (it !== null && typeof it === "object" && itemIds.get(it) === id) {
721
+ return i;
722
+ }
723
+ if (Object.is(it, idToItem.get(id))) return i;
724
+ }
725
+ return -1;
726
+ }
727
+ function begin() {
728
+ const v = ++version;
729
+ inflightCount++;
730
+ setPending(true);
731
+ return v;
732
+ }
733
+ function end(myVersion, revertFn) {
734
+ if (revertFn && version === myVersion) {
735
+ revertFn();
736
+ }
737
+ inflightCount--;
738
+ if (inflightCount === 0) setPending(false);
739
+ }
740
+ async function add(item, asyncAction) {
509
741
  const prev = items();
742
+ const id = tagItem(item);
510
743
  setItems([...prev, item]);
744
+ const myVersion = begin();
511
745
  try {
512
746
  const result = await asyncAction();
513
747
  setItems((current) => {
514
- const idx = current.lastIndexOf(item);
748
+ const idx = findIndexById(current, id);
515
749
  if (idx >= 0) {
516
750
  const next = [...current];
517
751
  next[idx] = result;
518
752
  return next;
519
753
  }
520
- return [...current.filter((i) => i !== item), result];
754
+ return [...current, result];
521
755
  });
756
+ idToItem.delete(id);
757
+ end(myVersion);
522
758
  } catch {
523
- setItems(prev);
759
+ idToItem.delete(id);
760
+ end(myVersion, () => setItems(prev));
524
761
  }
525
762
  }
526
- async function removeOptimistic(predicate, asyncAction) {
763
+ async function remove(predicate, asyncAction) {
527
764
  const prev = items();
528
765
  setItems(prev.filter((item) => !predicate(item)));
766
+ const myVersion = begin();
529
767
  try {
530
768
  await asyncAction();
769
+ end(myVersion);
531
770
  } catch {
532
- setItems(prev);
771
+ end(myVersion, () => setItems(prev));
533
772
  }
534
773
  }
535
- async function updateOptimistic(predicate, update, asyncAction) {
774
+ async function updateItem(predicate, patch, asyncAction) {
536
775
  const prev = items();
537
- setItems(prev.map((item) => predicate(item) ? { ...item, ...update } : item));
776
+ const patchedIds = [];
777
+ setItems(
778
+ prev.map((item) => {
779
+ if (predicate(item)) {
780
+ const patched = { ...item, ...patch };
781
+ const id = tagItem(patched);
782
+ patchedIds.push(id);
783
+ return patched;
784
+ }
785
+ return item;
786
+ })
787
+ );
788
+ const myVersion = begin();
538
789
  try {
539
790
  const result = await asyncAction();
540
- setItems((current) => current.map((item) => predicate(item) ? result : item));
791
+ setItems(
792
+ (current) => current.map((item) => {
793
+ if (item !== null && typeof item === "object") {
794
+ const existingId = itemIds.get(item);
795
+ if (existingId !== void 0 && patchedIds.includes(existingId)) return result;
796
+ }
797
+ return item;
798
+ })
799
+ );
800
+ for (const id of patchedIds) idToItem.delete(id);
801
+ end(myVersion);
541
802
  } catch {
542
- setItems(prev);
803
+ for (const id of patchedIds) idToItem.delete(id);
804
+ end(myVersion, () => setItems(prev));
543
805
  }
544
806
  }
545
- return { items, addOptimistic, removeOptimistic, updateOptimistic };
807
+ return {
808
+ items,
809
+ pending,
810
+ add,
811
+ remove,
812
+ update: updateItem
813
+ };
546
814
  }
547
815
 
548
816
  // src/core/signals/derived.ts
549
817
  function derived(getter, options) {
550
818
  devAssert(typeof getter === "function", "derived: argument must be a getter function.");
551
819
  const debugName = options?.name;
820
+ const equals = options?.equals;
552
821
  const cs = {};
553
822
  cs._d = false;
554
823
  cs._g = getter;
@@ -559,25 +828,56 @@ function derived(getter, options) {
559
828
  markDirty._c = 1;
560
829
  markDirty._sig = cs;
561
830
  track(() => {
562
- cs._d = false;
563
- cs._v = getter();
831
+ let threw = true;
832
+ try {
833
+ cs._v = getter();
834
+ cs._d = false;
835
+ threw = false;
836
+ } finally {
837
+ if (threw) cs._d = true;
838
+ }
564
839
  }, markDirty);
565
840
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
841
+ let evaluating = false;
566
842
  function computedGetter() {
843
+ if (evaluating) {
844
+ throw new Error(
845
+ `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
846
+ );
847
+ }
567
848
  if (trackingSuspended) {
568
849
  if (cs._d) {
569
- cs._d = false;
570
- cs._v = getter();
850
+ evaluating = true;
851
+ let threw = true;
852
+ try {
853
+ retrack(() => {
854
+ cs._v = getter();
855
+ cs._d = false;
856
+ threw = false;
857
+ }, markDirty);
858
+ } finally {
859
+ evaluating = false;
860
+ if (threw) cs._d = true;
861
+ }
571
862
  }
572
863
  return cs._v;
573
864
  }
574
865
  recordDependency(cs);
575
866
  if (cs._d) {
576
867
  const oldValue = cs._v;
577
- track(() => {
578
- cs._d = false;
579
- cs._v = getter();
580
- }, markDirty);
868
+ evaluating = true;
869
+ let threw = true;
870
+ try {
871
+ retrack(() => {
872
+ const next = getter();
873
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
874
+ cs._d = false;
875
+ threw = false;
876
+ }, markDirty);
877
+ } finally {
878
+ evaluating = false;
879
+ if (threw) cs._d = true;
880
+ }
581
881
  if (hook && oldValue !== cs._v) {
582
882
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
583
883
  }
@@ -608,14 +908,16 @@ function timeline(initial, maxHistory = 100) {
608
908
  const idx = index();
609
909
  const newHistory = hist.slice(0, idx + 1);
610
910
  newHistory.push(newValue);
611
- if (newHistory.length > maxHistory) {
612
- newHistory.shift();
613
- setHistory(newHistory);
614
- setIndex(newHistory.length - 1);
615
- } else {
616
- setHistory(newHistory);
617
- setIndex(idx + 1);
618
- }
911
+ batch(() => {
912
+ if (newHistory.length > maxHistory) {
913
+ newHistory.shift();
914
+ setHistory(newHistory);
915
+ setIndex(newHistory.length - 1);
916
+ } else {
917
+ setHistory(newHistory);
918
+ setIndex(idx + 1);
919
+ }
920
+ });
619
921
  }
620
922
  function undo() {
621
923
  if (canUndo()) {
@@ -628,8 +930,10 @@ function timeline(initial, maxHistory = 100) {
628
930
  }
629
931
  }
630
932
  function reset() {
631
- setHistory([initial]);
632
- setIndex(0);
933
+ batch(() => {
934
+ setHistory([initial]);
935
+ setIndex(0);
936
+ });
633
937
  }
634
938
  function jumpTo(targetIndex) {
635
939
  const hist = history();
@@ -641,8 +945,37 @@ function timeline(initial, maxHistory = 100) {
641
945
  }
642
946
 
643
947
  // src/patterns/globalStore.ts
948
+ function deepClone(value) {
949
+ if (typeof structuredClone === "function") {
950
+ return structuredClone(value);
951
+ }
952
+ const seen = /* @__PURE__ */ new WeakSet();
953
+ const clone = (v) => {
954
+ if (v === null || typeof v !== "object") return v;
955
+ if (seen.has(v)) throw new Error("deepClone: circular reference");
956
+ seen.add(v);
957
+ if (v instanceof Date) return new Date(v.getTime());
958
+ if (v instanceof Map) {
959
+ const out2 = /* @__PURE__ */ new Map();
960
+ for (const [k, val] of v) out2.set(clone(k), clone(val));
961
+ return out2;
962
+ }
963
+ if (v instanceof Set) {
964
+ const out2 = /* @__PURE__ */ new Set();
965
+ for (const val of v) out2.add(clone(val));
966
+ return out2;
967
+ }
968
+ if (Array.isArray(v)) return v.map(clone);
969
+ const out = {};
970
+ for (const k of Object.keys(v)) {
971
+ out[k] = clone(v[k]);
972
+ }
973
+ return out;
974
+ };
975
+ return clone(value);
976
+ }
644
977
  function globalStore(config) {
645
- const initialState = { ...config.state };
978
+ const initialState = deepClone(config.state);
646
979
  const [getState, setState] = signal({ ...initialState });
647
980
  const listeners = /* @__PURE__ */ new Set();
648
981
  const middlewares = config.middleware || [];