sibujs 2.1.0 → 2.2.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 (62) hide show
  1. package/dist/browser.cjs +324 -273
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +358 -328
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +8 -8
  6. package/dist/{chunk-ZAQSMOED.js → chunk-2JQUV4Y3.js} +4 -4
  7. package/dist/{chunk-GWWURC5M.js → chunk-2KM2724A.js} +2 -2
  8. package/dist/{chunk-NASX6ST2.js → chunk-4YTVESDX.js} +1 -1
  9. package/dist/chunk-5WD7BYTZ.js +152 -0
  10. package/dist/{chunk-RDTDJCAB.js → chunk-6QZO7MMG.js} +48 -16
  11. package/dist/{chunk-DRUZZAK4.js → chunk-DF3GTP4Q.js} +7 -2
  12. package/dist/{chunk-AMK2TYNW.js → chunk-J63GPPCJ.js} +9 -9
  13. package/dist/{chunk-O6EFQ3KT.js → chunk-KH4OE6WY.js} +5 -5
  14. package/dist/{chunk-V6C4FADE.js → chunk-KZA7ANXP.js} +3 -3
  15. package/dist/chunk-L4DAT4WU.js +400 -0
  16. package/dist/{chunk-WANSMF2L.js → chunk-L52H775O.js} +4 -4
  17. package/dist/{chunk-45YP72ZQ.js → chunk-NEWH4O5U.js} +1 -1
  18. package/dist/{chunk-ON5MMR2J.js → chunk-RJIRT46U.js} +4 -4
  19. package/dist/{chunk-P2HSJDDN.js → chunk-STFTTMO2.js} +2 -2
  20. package/dist/{chunk-WIPZPFBQ.js → chunk-UKMXT5T6.js} +1 -1
  21. package/dist/{chunk-KGYT6UO6.js → chunk-V65KTDZW.js} +3 -3
  22. package/dist/{chunk-CWBVQML6.js → chunk-VSNLICTS.js} +1 -1
  23. package/dist/{chunk-3DZP6OIT.js → chunk-XDKP4T7G.js} +2 -2
  24. package/dist/{chunk-TH2ILCYW.js → chunk-XVYB3J6C.js} +27 -33
  25. package/dist/{chunk-OJ3P4ECI.js → chunk-YMOIAHWA.js} +1 -1
  26. package/dist/data.cjs +332 -298
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +353 -296
  29. package/dist/devtools.d.cts +1 -1
  30. package/dist/devtools.d.ts +1 -1
  31. package/dist/devtools.js +4 -4
  32. package/dist/ecosystem.cjs +332 -298
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +372 -328
  35. package/dist/extras.d.cts +1 -1
  36. package/dist/extras.d.ts +1 -1
  37. package/dist/extras.js +19 -19
  38. package/dist/index.cjs +358 -328
  39. package/dist/index.d.cts +13 -22
  40. package/dist/index.d.ts +13 -22
  41. package/dist/index.js +10 -10
  42. package/dist/{introspect-DnIpHQQz.d.ts → introspect-BZWKvQUZ.d.ts} +2 -3
  43. package/dist/{introspect-2TOlQ7oa.d.cts → introspect-DsJlDD2T.d.cts} +2 -3
  44. package/dist/motion.cjs +147 -123
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +332 -298
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +315 -268
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +332 -266
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +340 -270
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +167 -146
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +324 -294
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +332 -298
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
  61. package/dist/chunk-QO3WC6FS.js +0 -384
  62. package/dist/chunk-WZA53FXU.js +0 -149
package/dist/widgets.cjs CHANGED
@@ -50,24 +50,88 @@ function devWarn(message) {
50
50
 
51
51
  // src/reactivity/track.ts
52
52
  var _isDev2 = isDev();
53
- var STACK_INITIAL = 32;
54
- var STACK_SHRINK_THRESHOLD = 128;
55
- var subscriberStack = new Array(STACK_INITIAL);
56
- var stackCapacity = STACK_INITIAL;
57
- var stackTop = -1;
58
- var currentSubscriber = null;
59
- var SUBS = "__s";
60
- function syncFastPath(signal2, subs) {
61
- const size = subs.size;
62
- if (size === 0) {
63
- signal2.__f = void 0;
64
- delete signal2[SUBS];
65
- } else if (size === 1) {
66
- signal2.__f = subs.values().next().value;
67
- } else {
68
- signal2.__f = void 0;
53
+ var POOL_MAX = 4096;
54
+ var nodePool = [];
55
+ function createNode() {
56
+ return {
57
+ sig: null,
58
+ sub: null,
59
+ epoch: 0,
60
+ sigPrev: null,
61
+ sigNext: null,
62
+ subPrev: null,
63
+ subNext: null,
64
+ prevActive: null
65
+ };
66
+ }
67
+ function allocNode(sig, sub, epoch) {
68
+ const n = nodePool.pop();
69
+ if (n) {
70
+ n.sig = sig;
71
+ n.sub = sub;
72
+ n.epoch = epoch;
73
+ return n;
74
+ }
75
+ const fresh = createNode();
76
+ fresh.sig = sig;
77
+ fresh.sub = sub;
78
+ fresh.epoch = epoch;
79
+ return fresh;
80
+ }
81
+ function freeNode(node) {
82
+ node.sig = null;
83
+ node.sub = null;
84
+ node.sigPrev = null;
85
+ node.sigNext = null;
86
+ node.subPrev = null;
87
+ node.subNext = null;
88
+ node.prevActive = null;
89
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
90
+ }
91
+ function linkSignal(sig, node) {
92
+ const oldHead = sig.subsHead ?? null;
93
+ node.sigPrev = null;
94
+ node.sigNext = oldHead;
95
+ if (oldHead) oldHead.sigPrev = node;
96
+ else sig.subsTail = node;
97
+ sig.subsHead = node;
98
+ sig.__sc = (sig.__sc ?? 0) + 1;
99
+ }
100
+ function unlinkSignal(node) {
101
+ const sig = node.sig;
102
+ if (!sig) return;
103
+ const prev = node.sigPrev;
104
+ const next = node.sigNext;
105
+ if (prev) prev.sigNext = next;
106
+ else sig.subsHead = next;
107
+ if (next) next.sigPrev = prev;
108
+ else sig.subsTail = prev;
109
+ sig.__sc = (sig.__sc ?? 1) - 1;
110
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
111
+ if (sig.__sc === 0) {
112
+ sig.subsHead = null;
113
+ sig.subsTail = null;
69
114
  }
70
115
  }
116
+ function linkSub(sub, node) {
117
+ const oldTail = sub.depsTail ?? null;
118
+ node.subPrev = oldTail;
119
+ node.subNext = null;
120
+ if (oldTail) oldTail.subNext = node;
121
+ else sub.depsHead = node;
122
+ sub.depsTail = node;
123
+ }
124
+ function unlinkSub(node) {
125
+ const sub = node.sub;
126
+ if (!sub) return;
127
+ const prev = node.subPrev;
128
+ const next = node.subNext;
129
+ if (prev) prev.subNext = next;
130
+ else sub.depsHead = next;
131
+ if (next) next.subPrev = prev;
132
+ else sub.depsTail = prev;
133
+ }
134
+ var currentSubscriber = null;
71
135
  var notifyDepth = 0;
72
136
  var pendingQueue = [];
73
137
  var pendingSet = /* @__PURE__ */ new Set();
@@ -87,110 +151,77 @@ function retrack(effectFn, subscriber) {
87
151
  const sub = subscriber;
88
152
  const epoch = ++subscriberEpochCounter;
89
153
  sub._epoch = epoch;
154
+ sub._structDirty = false;
155
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
156
+ const sig = n.sig;
157
+ n.prevActive = sig.__activeNode ?? null;
158
+ sig.__activeNode = n;
159
+ }
90
160
  try {
91
161
  effectFn();
92
162
  } finally {
93
163
  currentSubscriber = prev;
94
- pruneStaleDeps(sub, epoch);
95
- }
96
- }
97
- function pruneStaleDeps(sub, currentEpoch) {
98
- if (sub._dep !== void 0) {
99
- if (sub._depEpoch !== currentEpoch) {
100
- const sig = sub._dep;
101
- const subs = sig[SUBS];
102
- if (subs?.delete(sub)) syncFastPath(sig, subs);
103
- sub._dep = void 0;
104
- sub._depEpoch = void 0;
105
- }
106
- return;
107
- }
108
- const deps = sub._deps;
109
- if (!deps || deps.size === 0) return;
110
- let stales;
111
- for (const [signal2, epoch] of deps) {
112
- if (epoch !== currentEpoch) {
113
- (stales ?? (stales = [])).push(signal2);
164
+ let node = sub.depsHead ?? null;
165
+ while (node !== null) {
166
+ const next = node.subNext;
167
+ const sig = node.sig;
168
+ sig.__activeNode = node.prevActive;
169
+ node.prevActive = null;
170
+ if (node.epoch !== epoch) {
171
+ unlinkSub(node);
172
+ unlinkSignal(node);
173
+ freeNode(node);
174
+ }
175
+ node = next;
114
176
  }
115
177
  }
116
- if (!stales) return;
117
- for (const signal2 of stales) {
118
- deps.delete(signal2);
119
- const sig = signal2;
120
- const subs = sig[SUBS];
121
- if (subs?.delete(sub)) syncFastPath(sig, subs);
122
- }
123
178
  }
124
179
  function track(effectFn, subscriber) {
125
180
  if (!subscriber) subscriber = effectFn;
126
181
  cleanup(subscriber);
127
- ++stackTop;
128
- if (stackTop >= stackCapacity) {
129
- stackCapacity *= 2;
130
- subscriberStack.length = stackCapacity;
131
- }
132
- subscriberStack[stackTop] = subscriber;
182
+ const prev = currentSubscriber;
133
183
  currentSubscriber = subscriber;
134
184
  try {
135
185
  effectFn();
136
186
  } finally {
137
- stackTop--;
138
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
139
- if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
140
- stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
141
- subscriberStack.length = stackCapacity;
187
+ currentSubscriber = prev;
188
+ const sub2 = subscriber;
189
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
190
+ const sig = n.sig;
191
+ sig.__activeNode = n.prevActive;
192
+ n.prevActive = null;
142
193
  }
143
194
  }
144
- return () => cleanup(subscriber);
195
+ const sub = subscriber;
196
+ return sub._dispose ?? (sub._dispose = () => cleanup(subscriber));
145
197
  }
146
198
  function recordDependency(signal2) {
147
199
  if (!currentSubscriber) return;
148
200
  const sub = currentSubscriber;
149
- const epoch = sub._epoch;
150
- if (sub._dep === signal2) {
151
- sub._depEpoch = epoch;
152
- return;
153
- }
154
- const deps = sub._deps;
155
- if (deps) {
156
- deps.set(signal2, epoch);
157
- } else if (sub._dep !== void 0) {
158
- const map = /* @__PURE__ */ new Map();
159
- map.set(sub._dep, sub._depEpoch);
160
- map.set(signal2, epoch);
161
- sub._deps = map;
162
- sub._dep = void 0;
163
- sub._depEpoch = void 0;
164
- } else {
165
- sub._dep = signal2;
166
- sub._depEpoch = epoch;
167
- }
168
201
  const sig = signal2;
169
- let subs = sig[SUBS];
170
- if (!subs) {
171
- subs = /* @__PURE__ */ new Set();
172
- sig[SUBS] = subs;
173
- }
174
- const prevSize = subs.size;
175
- subs.add(currentSubscriber);
176
- if (subs.size !== prevSize) {
177
- if (subs.size === 1) {
178
- sig.__f = currentSubscriber;
179
- } else if (sig.__f !== void 0) {
180
- sig.__f = void 0;
181
- }
202
+ const epoch = sub._epoch ?? 0;
203
+ const active = sig.__activeNode ?? null;
204
+ if (active !== null && active.sub === sub) {
205
+ active.epoch = epoch;
206
+ return;
182
207
  }
208
+ const node = allocNode(signal2, sub, epoch);
209
+ node.prevActive = active;
210
+ sig.__activeNode = node;
211
+ linkSub(sub, node);
212
+ linkSignal(sig, node);
213
+ sub._structDirty = true;
183
214
  }
184
- function queueSignalNotification(signal2) {
185
- const subs = signal2[SUBS];
186
- if (!subs) return;
187
- for (const sub of subs) {
188
- if (sub._c) {
189
- propagateDirty(sub);
190
- } else if (!pendingSet.has(sub)) {
191
- pendingSet.add(sub);
192
- pendingQueue.push(sub);
193
- }
215
+ function cleanup(subscriber) {
216
+ const sub = subscriber;
217
+ let node = sub.depsHead ?? null;
218
+ sub.depsHead = null;
219
+ sub.depsTail = null;
220
+ while (node) {
221
+ const next = node.subNext;
222
+ unlinkSignal(node);
223
+ freeNode(node);
224
+ node = next;
194
225
  }
195
226
  }
196
227
  var maxSubscriberRepeats = 50;
@@ -203,7 +234,8 @@ function tickRepeat(sub) {
203
234
  s._runs = 1;
204
235
  return false;
205
236
  }
206
- return ++s._runs > maxSubscriberRepeats;
237
+ s._runs = (s._runs ?? 0) + 1;
238
+ return s._runs > maxSubscriberRepeats;
207
239
  }
208
240
  function cycleError(sub) {
209
241
  if (typeof console !== "undefined") {
@@ -259,93 +291,80 @@ function propagateDirty(sub) {
259
291
  stack.push(rootSig);
260
292
  while (stack.length > baseLen) {
261
293
  const sig = stack.pop();
262
- const first = sig.__f;
263
- if (first) {
264
- if (first._c) {
265
- const nSig = first._sig;
266
- if (!nSig._d) {
267
- nSig._d = true;
268
- stack.push(nSig);
294
+ let node = sig.subsHead ?? null;
295
+ while (node) {
296
+ const s = node.sub;
297
+ if (s) {
298
+ if (s._c) {
299
+ const nSig = s._sig;
300
+ if (nSig) {
301
+ if (!nSig._d) {
302
+ nSig._d = true;
303
+ stack.push(nSig);
304
+ }
305
+ } else {
306
+ s();
307
+ }
308
+ } else if (!pendingSet.has(s)) {
309
+ pendingSet.add(s);
310
+ pendingQueue.push(s);
269
311
  }
270
- } else if (!pendingSet.has(first)) {
271
- pendingSet.add(first);
272
- pendingQueue.push(first);
273
312
  }
274
- continue;
313
+ node = node.sigNext;
275
314
  }
276
- const subs = sig[SUBS];
277
- if (!subs) continue;
278
- for (const s of subs) {
315
+ }
316
+ }
317
+ function queueSignalNotification(signal2) {
318
+ const sig = signal2;
319
+ let node = sig.subsHead ?? null;
320
+ while (node) {
321
+ const s = node.sub;
322
+ if (s) {
279
323
  if (s._c) {
280
- const nSig = s._sig;
281
- if (nSig && !nSig._d) {
282
- nSig._d = true;
283
- stack.push(nSig);
284
- } else if (!nSig) {
285
- s();
286
- }
324
+ propagateDirty(s);
287
325
  } else if (!pendingSet.has(s)) {
288
326
  pendingSet.add(s);
289
327
  pendingQueue.push(s);
290
328
  }
291
329
  }
330
+ node = node.sigNext;
292
331
  }
293
332
  }
294
333
  function notifySubscribers(signal2) {
295
- const first = signal2.__f;
296
- if (first) {
297
- if (notifyDepth > 0) {
298
- if (first._c) {
299
- propagateDirty(first);
300
- } else if (!pendingSet.has(first)) {
301
- pendingSet.add(first);
302
- pendingQueue.push(first);
303
- }
304
- return;
305
- }
306
- notifyDepth++;
307
- drainEpoch++;
308
- try {
309
- if (first._c) {
310
- propagateDirty(first);
311
- } else if (tickRepeat(first)) {
312
- cycleError(first);
313
- } else {
314
- safeInvoke(first);
315
- }
316
- drainQueue();
317
- } finally {
318
- notifyDepth--;
319
- if (notifyDepth === 0) {
320
- pendingQueue.length = 0;
321
- pendingSet.clear();
322
- }
323
- }
324
- return;
325
- }
326
- const subs = signal2[SUBS];
327
- if (!subs || subs.size === 0) return;
334
+ const sig = signal2;
335
+ const head = sig.subsHead;
336
+ if (!head) return;
328
337
  if (notifyDepth > 0) {
329
- for (const sub of subs) {
330
- if (sub._c) {
331
- propagateDirty(sub);
332
- } else if (!pendingSet.has(sub)) {
333
- pendingSet.add(sub);
334
- pendingQueue.push(sub);
338
+ let node = head;
339
+ while (node) {
340
+ const s = node.sub;
341
+ if (s) {
342
+ if (s._c) {
343
+ propagateDirty(s);
344
+ } else if (!pendingSet.has(s)) {
345
+ pendingSet.add(s);
346
+ pendingQueue.push(s);
347
+ }
335
348
  }
349
+ node = node.sigNext;
336
350
  }
337
351
  return;
338
352
  }
339
353
  notifyDepth++;
340
354
  drainEpoch++;
341
355
  try {
342
- for (const sub of subs) {
343
- if (sub._c) {
344
- propagateDirty(sub);
345
- } else if (!pendingSet.has(sub)) {
346
- pendingSet.add(sub);
347
- pendingQueue.push(sub);
356
+ let node = head;
357
+ while (node) {
358
+ const s = node.sub;
359
+ if (s) {
360
+ if (s._c) {
361
+ propagateDirty(s);
362
+ } else if (!pendingSet.has(s)) {
363
+ pendingSet.add(s);
364
+ pendingQueue.push(s);
365
+ }
348
366
  }
367
+ node = node.sigNext;
349
368
  }
350
369
  drainQueue();
351
370
  } finally {
@@ -356,30 +375,6 @@ function notifySubscribers(signal2) {
356
375
  }
357
376
  }
358
377
  }
359
- function cleanup(subscriber) {
360
- const sub = subscriber;
361
- const singleDep = sub._dep;
362
- if (singleDep !== void 0) {
363
- const sig = singleDep;
364
- const subs = sig[SUBS];
365
- if (subs?.delete(subscriber)) {
366
- syncFastPath(sig, subs);
367
- }
368
- sub._dep = void 0;
369
- sub._depEpoch = void 0;
370
- return;
371
- }
372
- const deps = sub._deps;
373
- if (!deps || deps.size === 0) return;
374
- for (const signal2 of deps.keys()) {
375
- const sig = signal2;
376
- const subs = sig[SUBS];
377
- if (subs?.delete(subscriber)) {
378
- syncFastPath(sig, subs);
379
- }
380
- }
381
- deps.clear();
382
- }
383
378
 
384
379
  // src/core/signals/derived.ts
385
380
  function derived(getter, options) {
@@ -389,6 +384,7 @@ function derived(getter, options) {
389
384
  const cs = {};
390
385
  cs._d = false;
391
386
  cs._g = getter;
387
+ cs.__v = 0;
392
388
  const markDirty = () => {
393
389
  if (cs._d) return;
394
390
  cs._d = true;
@@ -418,11 +414,14 @@ function derived(getter, options) {
418
414
  evaluating = true;
419
415
  let threw = true;
420
416
  try {
417
+ const prev = cs._v;
421
418
  retrack(() => {
422
- cs._v = getter();
419
+ const next = getter();
420
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
423
421
  cs._d = false;
424
422
  threw = false;
425
423
  }, markDirty);
424
+ if (!Object.is(prev, cs._v)) cs.__v++;
426
425
  } finally {
427
426
  evaluating = false;
428
427
  if (threw) cs._d = true;
@@ -442,6 +441,7 @@ function derived(getter, options) {
442
441
  cs._d = false;
443
442
  threw = false;
444
443
  }, markDirty);
444
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
445
445
  } finally {
446
446
  evaluating = false;
447
447
  if (threw) cs._d = true;
@@ -488,120 +488,122 @@ function isSSR() {
488
488
 
489
489
  // src/core/signals/effect.ts
490
490
  var _g = globalThis;
491
+ var MAX_RERUNS = 100;
492
+ function flushUserCleanups(ctx) {
493
+ const list = ctx.userCleanups;
494
+ if (list.length === 0) return;
495
+ ctx.userCleanups = [];
496
+ for (let i = list.length - 1; i >= 0; i--) {
497
+ try {
498
+ list[i]();
499
+ } catch (err) {
500
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
501
+ }
502
+ }
503
+ }
504
+ function drainReruns(ctx) {
505
+ let reruns = 1;
506
+ do {
507
+ ctx.rerunPending = false;
508
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
509
+ retrack(ctx.bodyFn, ctx.subscriber);
510
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
511
+ if (ctx.rerunPending) {
512
+ ctx.rerunPending = false;
513
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
514
+ console.error(
515
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
516
+ );
517
+ }
518
+ }
519
+ }
520
+ function disposeEffect(ctx) {
521
+ if (ctx.disposed) return;
522
+ ctx.disposed = true;
523
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
524
+ if (h) {
525
+ try {
526
+ h.emit("effect:destroy", { effectFn: ctx.fn });
527
+ } catch {
528
+ }
529
+ }
530
+ try {
531
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
532
+ } catch (err) {
533
+ if (typeof console !== "undefined") {
534
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
535
+ }
536
+ }
537
+ try {
538
+ cleanup(ctx.subscriber);
539
+ } catch (err) {
540
+ if (typeof console !== "undefined") {
541
+ console.warn("[SibuJS effect] dispose threw:", err);
542
+ }
543
+ }
544
+ }
491
545
  function effect(effectFn, options) {
492
546
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
493
547
  if (isSSR()) return () => {
494
548
  };
495
- const onError = options?.onError;
496
- let userCleanups = [];
497
- const onCleanup = (fn) => {
498
- userCleanups.push(fn);
549
+ const ctx = {
550
+ fn: effectFn,
551
+ onError: options?.onError,
552
+ userCleanups: [],
553
+ running: false,
554
+ rerunPending: false,
555
+ disposed: false,
556
+ onCleanup: null,
557
+ subscriber: null,
558
+ bodyFn: null
499
559
  };
500
- const runUserCleanups = () => {
501
- if (userCleanups.length === 0) return;
502
- const list = userCleanups;
503
- userCleanups = [];
504
- for (let i = list.length - 1; i >= 0; i--) {
505
- try {
506
- list[i]();
507
- } catch (err) {
508
- if (typeof console !== "undefined") {
509
- console.warn("[SibuJS effect] onCleanup threw:", err);
510
- }
511
- }
512
- }
560
+ ctx.onCleanup = (fn) => {
561
+ ctx.userCleanups.push(fn);
513
562
  };
514
- const invokeBody = () => effectFn(onCleanup);
515
- const wrappedFn = onError ? () => {
563
+ const onErrorCaptured = ctx.onError;
564
+ ctx.bodyFn = onErrorCaptured ? () => {
516
565
  try {
517
- invokeBody();
566
+ ctx.fn(ctx.onCleanup);
518
567
  } catch (err) {
519
- onError(err);
568
+ onErrorCaptured(err);
520
569
  }
521
- } : invokeBody;
522
- let cleanupHandle = () => {
570
+ } : () => {
571
+ ctx.fn(ctx.onCleanup);
523
572
  };
524
- let running = false;
525
- let rerunPending = false;
526
- const MAX_RERUNS = 100;
527
- const subscriber = () => {
528
- if (running) {
529
- rerunPending = true;
573
+ const sub = (() => {
574
+ if (ctx.running) {
575
+ ctx.rerunPending = true;
530
576
  return;
531
577
  }
532
- running = true;
578
+ ctx.running = true;
533
579
  try {
534
- let reruns = 0;
535
- do {
536
- rerunPending = false;
537
- runUserCleanups();
538
- cleanupHandle();
539
- cleanupHandle = track(wrappedFn, subscriber);
540
- if (++reruns > MAX_RERUNS) {
541
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
542
- console.error(
543
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
544
- );
545
- }
546
- rerunPending = false;
547
- break;
548
- }
549
- } while (rerunPending);
580
+ ctx.rerunPending = false;
581
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
582
+ retrack(ctx.bodyFn, sub);
583
+ if (ctx.rerunPending) drainReruns(ctx);
550
584
  } finally {
551
- running = false;
552
- rerunPending = false;
585
+ ctx.running = false;
586
+ ctx.rerunPending = false;
553
587
  }
554
- };
555
- running = true;
588
+ });
589
+ sub.depsHead = null;
590
+ sub.depsTail = null;
591
+ sub._epoch = 0;
592
+ sub._structDirty = false;
593
+ sub._runEpoch = 0;
594
+ sub._runs = 0;
595
+ ctx.subscriber = sub;
596
+ ctx.running = true;
556
597
  try {
557
- let reruns = 0;
558
- do {
559
- rerunPending = false;
560
- runUserCleanups();
561
- cleanupHandle();
562
- cleanupHandle = track(wrappedFn, subscriber);
563
- if (++reruns > MAX_RERUNS) {
564
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
565
- console.error(
566
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
567
- );
568
- }
569
- rerunPending = false;
570
- break;
571
- }
572
- } while (rerunPending);
598
+ retrack(ctx.bodyFn, ctx.subscriber);
599
+ if (ctx.rerunPending) drainReruns(ctx);
573
600
  } finally {
574
- running = false;
575
- rerunPending = false;
601
+ ctx.running = false;
602
+ ctx.rerunPending = false;
576
603
  }
577
604
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
578
605
  if (hook) hook.emit("effect:create", { effectFn });
579
- let disposed = false;
580
- return () => {
581
- if (disposed) return;
582
- disposed = true;
583
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
584
- if (h) {
585
- try {
586
- h.emit("effect:destroy", { effectFn });
587
- } catch {
588
- }
589
- }
590
- try {
591
- runUserCleanups();
592
- } catch (err) {
593
- if (typeof console !== "undefined") {
594
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
595
- }
596
- }
597
- try {
598
- cleanupHandle();
599
- } catch (err) {
600
- if (typeof console !== "undefined") {
601
- console.warn("[SibuJS effect] dispose threw:", err);
602
- }
603
- }
604
- };
606
+ return () => disposeEffect(ctx);
605
607
  }
606
608
 
607
609
  // src/reactivity/batch.ts
@@ -638,32 +640,64 @@ function flushBatch() {
638
640
  var _g2 = globalThis;
639
641
  var _isDev3 = isDev();
640
642
  function signal(initial, options) {
641
- const state = { value: initial };
643
+ const state = {
644
+ value: initial,
645
+ __v: 0,
646
+ __sc: 0,
647
+ subsHead: null,
648
+ subsTail: null,
649
+ __activeNode: null,
650
+ __name: void 0
651
+ };
642
652
  const debugName = _isDev3 ? options?.name : void 0;
643
653
  const equalsFn = options?.equals;
644
- if (debugName) {
645
- state.__name = debugName;
646
- }
654
+ if (debugName) state.__name = debugName;
647
655
  function get() {
648
656
  recordDependency(state);
649
657
  return state.value;
650
658
  }
651
659
  get.__signal = state;
652
660
  if (debugName) get.__name = debugName;
653
- function set(next) {
654
- const newValue = typeof next === "function" ? next(state.value) : next;
655
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
656
- if (_isDev3) {
657
- const oldValue = state.value;
661
+ let set;
662
+ if (equalsFn) {
663
+ set = (next) => {
664
+ const prev = state.value;
665
+ const newValue = typeof next === "function" ? next(prev) : next;
666
+ if (equalsFn(prev, newValue)) return;
667
+ state.value = newValue;
668
+ state.__v++;
669
+ if (_isDev3) {
670
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
671
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
672
+ }
673
+ if (!enqueueBatchedSignal(state)) {
674
+ notifySubscribers(state);
675
+ }
676
+ };
677
+ } else if (_isDev3) {
678
+ set = (next) => {
679
+ const prev = state.value;
680
+ const newValue = typeof next === "function" ? next(prev) : next;
681
+ if (Object.is(newValue, prev)) return;
658
682
  state.value = newValue;
683
+ state.__v++;
659
684
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
660
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
661
- } else {
685
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
686
+ if (!enqueueBatchedSignal(state)) {
687
+ notifySubscribers(state);
688
+ }
689
+ };
690
+ } else {
691
+ set = (next) => {
692
+ const prev = state.value;
693
+ const newValue = typeof next === "function" ? next(prev) : next;
694
+ if (Object.is(newValue, prev)) return;
662
695
  state.value = newValue;
663
- }
664
- if (!enqueueBatchedSignal(state)) {
665
- notifySubscribers(state);
666
- }
696
+ state.__v++;
697
+ if (!enqueueBatchedSignal(state)) {
698
+ notifySubscribers(state);
699
+ }
700
+ };
667
701
  }
668
702
  if (_isDev3) {
669
703
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;