sibujs 2.1.0 → 3.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 (62) hide show
  1. package/dist/browser.cjs +324 -273
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +363 -330
  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-INBOWHQ3.js} +14 -11
  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 +363 -330
  39. package/dist/index.d.cts +26 -36
  40. package/dist/index.d.ts +26 -36
  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/data.cjs CHANGED
@@ -62,24 +62,88 @@ function devWarn(message) {
62
62
 
63
63
  // src/reactivity/track.ts
64
64
  var _isDev2 = isDev();
65
- var STACK_INITIAL = 32;
66
- var STACK_SHRINK_THRESHOLD = 128;
67
- var subscriberStack = new Array(STACK_INITIAL);
68
- var stackCapacity = STACK_INITIAL;
69
- var stackTop = -1;
70
- var currentSubscriber = null;
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;
65
+ var POOL_MAX = 4096;
66
+ var nodePool = [];
67
+ function createNode() {
68
+ return {
69
+ sig: null,
70
+ sub: null,
71
+ epoch: 0,
72
+ sigPrev: null,
73
+ sigNext: null,
74
+ subPrev: null,
75
+ subNext: null,
76
+ prevActive: null
77
+ };
78
+ }
79
+ function allocNode(sig, sub, epoch) {
80
+ const n = nodePool.pop();
81
+ if (n) {
82
+ n.sig = sig;
83
+ n.sub = sub;
84
+ n.epoch = epoch;
85
+ return n;
86
+ }
87
+ const fresh = createNode();
88
+ fresh.sig = sig;
89
+ fresh.sub = sub;
90
+ fresh.epoch = epoch;
91
+ return fresh;
92
+ }
93
+ function freeNode(node) {
94
+ node.sig = null;
95
+ node.sub = null;
96
+ node.sigPrev = null;
97
+ node.sigNext = null;
98
+ node.subPrev = null;
99
+ node.subNext = null;
100
+ node.prevActive = null;
101
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
102
+ }
103
+ function linkSignal(sig, node) {
104
+ const oldHead = sig.subsHead ?? null;
105
+ node.sigPrev = null;
106
+ node.sigNext = oldHead;
107
+ if (oldHead) oldHead.sigPrev = node;
108
+ else sig.subsTail = node;
109
+ sig.subsHead = node;
110
+ sig.__sc = (sig.__sc ?? 0) + 1;
111
+ }
112
+ function unlinkSignal(node) {
113
+ const sig = node.sig;
114
+ if (!sig) return;
115
+ const prev = node.sigPrev;
116
+ const next = node.sigNext;
117
+ if (prev) prev.sigNext = next;
118
+ else sig.subsHead = next;
119
+ if (next) next.sigPrev = prev;
120
+ else sig.subsTail = prev;
121
+ sig.__sc = (sig.__sc ?? 1) - 1;
122
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
123
+ if (sig.__sc === 0) {
124
+ sig.subsHead = null;
125
+ sig.subsTail = null;
81
126
  }
82
127
  }
128
+ function linkSub(sub, node) {
129
+ const oldTail = sub.depsTail ?? null;
130
+ node.subPrev = oldTail;
131
+ node.subNext = null;
132
+ if (oldTail) oldTail.subNext = node;
133
+ else sub.depsHead = node;
134
+ sub.depsTail = node;
135
+ }
136
+ function unlinkSub(node) {
137
+ const sub = node.sub;
138
+ if (!sub) return;
139
+ const prev = node.subPrev;
140
+ const next = node.subNext;
141
+ if (prev) prev.subNext = next;
142
+ else sub.depsHead = next;
143
+ if (next) next.subPrev = prev;
144
+ else sub.depsTail = prev;
145
+ }
146
+ var currentSubscriber = null;
83
147
  var notifyDepth = 0;
84
148
  var pendingQueue = [];
85
149
  var pendingSet = /* @__PURE__ */ new Set();
@@ -99,110 +163,77 @@ function retrack(effectFn, subscriber) {
99
163
  const sub = subscriber;
100
164
  const epoch = ++subscriberEpochCounter;
101
165
  sub._epoch = epoch;
166
+ sub._structDirty = false;
167
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
168
+ const sig = n.sig;
169
+ n.prevActive = sig.__activeNode ?? null;
170
+ sig.__activeNode = n;
171
+ }
102
172
  try {
103
173
  effectFn();
104
174
  } finally {
105
175
  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);
176
+ let node = sub.depsHead ?? null;
177
+ while (node !== null) {
178
+ const next = node.subNext;
179
+ const sig = node.sig;
180
+ sig.__activeNode = node.prevActive;
181
+ node.prevActive = null;
182
+ if (node.epoch !== epoch) {
183
+ unlinkSub(node);
184
+ unlinkSignal(node);
185
+ freeNode(node);
186
+ }
187
+ node = next;
126
188
  }
127
189
  }
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
190
  }
136
191
  function track(effectFn, subscriber) {
137
192
  if (!subscriber) subscriber = effectFn;
138
193
  cleanup(subscriber);
139
- ++stackTop;
140
- if (stackTop >= stackCapacity) {
141
- stackCapacity *= 2;
142
- subscriberStack.length = stackCapacity;
143
- }
144
- subscriberStack[stackTop] = subscriber;
194
+ const prev = currentSubscriber;
145
195
  currentSubscriber = subscriber;
146
196
  try {
147
197
  effectFn();
148
198
  } finally {
149
- stackTop--;
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;
199
+ currentSubscriber = prev;
200
+ const sub2 = subscriber;
201
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
202
+ const sig = n.sig;
203
+ sig.__activeNode = n.prevActive;
204
+ n.prevActive = null;
154
205
  }
155
206
  }
156
- return () => cleanup(subscriber);
207
+ const sub = subscriber;
208
+ return sub._dispose ?? (sub._dispose = () => cleanup(subscriber));
157
209
  }
158
210
  function recordDependency(signal2) {
159
211
  if (!currentSubscriber) return;
160
212
  const sub = currentSubscriber;
161
- const epoch = sub._epoch;
162
- if (sub._dep === signal2) {
163
- sub._depEpoch = epoch;
164
- return;
165
- }
166
- const deps = sub._deps;
167
- if (deps) {
168
- deps.set(signal2, epoch);
169
- } else if (sub._dep !== void 0) {
170
- const map = /* @__PURE__ */ new Map();
171
- map.set(sub._dep, sub._depEpoch);
172
- map.set(signal2, epoch);
173
- sub._deps = map;
174
- sub._dep = void 0;
175
- sub._depEpoch = void 0;
176
- } else {
177
- sub._dep = signal2;
178
- sub._depEpoch = epoch;
179
- }
180
213
  const sig = signal2;
181
- let subs = sig[SUBS];
182
- if (!subs) {
183
- subs = /* @__PURE__ */ new Set();
184
- sig[SUBS] = subs;
185
- }
186
- const prevSize = subs.size;
187
- subs.add(currentSubscriber);
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
- }
214
+ const epoch = sub._epoch ?? 0;
215
+ const active = sig.__activeNode ?? null;
216
+ if (active !== null && active.sub === sub) {
217
+ active.epoch = epoch;
218
+ return;
194
219
  }
220
+ const node = allocNode(signal2, sub, epoch);
221
+ node.prevActive = active;
222
+ sig.__activeNode = node;
223
+ linkSub(sub, node);
224
+ linkSignal(sig, node);
225
+ sub._structDirty = true;
195
226
  }
196
- function queueSignalNotification(signal2) {
197
- const subs = signal2[SUBS];
198
- if (!subs) return;
199
- for (const sub of subs) {
200
- if (sub._c) {
201
- propagateDirty(sub);
202
- } else if (!pendingSet.has(sub)) {
203
- pendingSet.add(sub);
204
- pendingQueue.push(sub);
205
- }
227
+ function cleanup(subscriber) {
228
+ const sub = subscriber;
229
+ let node = sub.depsHead ?? null;
230
+ sub.depsHead = null;
231
+ sub.depsTail = null;
232
+ while (node) {
233
+ const next = node.subNext;
234
+ unlinkSignal(node);
235
+ freeNode(node);
236
+ node = next;
206
237
  }
207
238
  }
208
239
  var maxSubscriberRepeats = 50;
@@ -215,7 +246,8 @@ function tickRepeat(sub) {
215
246
  s._runs = 1;
216
247
  return false;
217
248
  }
218
- return ++s._runs > maxSubscriberRepeats;
249
+ s._runs = (s._runs ?? 0) + 1;
250
+ return s._runs > maxSubscriberRepeats;
219
251
  }
220
252
  function cycleError(sub) {
221
253
  if (typeof console !== "undefined") {
@@ -271,93 +303,80 @@ function propagateDirty(sub) {
271
303
  stack.push(rootSig);
272
304
  while (stack.length > baseLen) {
273
305
  const sig = stack.pop();
274
- const first = sig.__f;
275
- if (first) {
276
- if (first._c) {
277
- const nSig = first._sig;
278
- if (!nSig._d) {
279
- nSig._d = true;
280
- stack.push(nSig);
306
+ let node = sig.subsHead ?? null;
307
+ while (node) {
308
+ const s = node.sub;
309
+ if (s) {
310
+ if (s._c) {
311
+ const nSig = s._sig;
312
+ if (nSig) {
313
+ if (!nSig._d) {
314
+ nSig._d = true;
315
+ stack.push(nSig);
316
+ }
317
+ } else {
318
+ s();
319
+ }
320
+ } else if (!pendingSet.has(s)) {
321
+ pendingSet.add(s);
322
+ pendingQueue.push(s);
281
323
  }
282
- } else if (!pendingSet.has(first)) {
283
- pendingSet.add(first);
284
- pendingQueue.push(first);
285
324
  }
286
- continue;
325
+ node = node.sigNext;
287
326
  }
288
- const subs = sig[SUBS];
289
- if (!subs) continue;
290
- for (const s of subs) {
327
+ }
328
+ }
329
+ function queueSignalNotification(signal2) {
330
+ const sig = signal2;
331
+ let node = sig.subsHead ?? null;
332
+ while (node) {
333
+ const s = node.sub;
334
+ if (s) {
291
335
  if (s._c) {
292
- const nSig = s._sig;
293
- if (nSig && !nSig._d) {
294
- nSig._d = true;
295
- stack.push(nSig);
296
- } else if (!nSig) {
297
- s();
298
- }
336
+ propagateDirty(s);
299
337
  } else if (!pendingSet.has(s)) {
300
338
  pendingSet.add(s);
301
339
  pendingQueue.push(s);
302
340
  }
303
341
  }
342
+ node = node.sigNext;
304
343
  }
305
344
  }
306
345
  function notifySubscribers(signal2) {
307
- const first = signal2.__f;
308
- if (first) {
309
- if (notifyDepth > 0) {
310
- if (first._c) {
311
- propagateDirty(first);
312
- } else if (!pendingSet.has(first)) {
313
- pendingSet.add(first);
314
- pendingQueue.push(first);
315
- }
316
- return;
317
- }
318
- notifyDepth++;
319
- drainEpoch++;
320
- try {
321
- if (first._c) {
322
- propagateDirty(first);
323
- } else if (tickRepeat(first)) {
324
- cycleError(first);
325
- } else {
326
- safeInvoke(first);
327
- }
328
- drainQueue();
329
- } finally {
330
- notifyDepth--;
331
- if (notifyDepth === 0) {
332
- pendingQueue.length = 0;
333
- pendingSet.clear();
334
- }
335
- }
336
- return;
337
- }
338
- const subs = signal2[SUBS];
339
- if (!subs || subs.size === 0) return;
346
+ const sig = signal2;
347
+ const head = sig.subsHead;
348
+ if (!head) return;
340
349
  if (notifyDepth > 0) {
341
- for (const sub of subs) {
342
- if (sub._c) {
343
- propagateDirty(sub);
344
- } else if (!pendingSet.has(sub)) {
345
- pendingSet.add(sub);
346
- pendingQueue.push(sub);
350
+ let node = head;
351
+ while (node) {
352
+ const s = node.sub;
353
+ if (s) {
354
+ if (s._c) {
355
+ propagateDirty(s);
356
+ } else if (!pendingSet.has(s)) {
357
+ pendingSet.add(s);
358
+ pendingQueue.push(s);
359
+ }
347
360
  }
361
+ node = node.sigNext;
348
362
  }
349
363
  return;
350
364
  }
351
365
  notifyDepth++;
352
366
  drainEpoch++;
353
367
  try {
354
- for (const sub of subs) {
355
- if (sub._c) {
356
- propagateDirty(sub);
357
- } else if (!pendingSet.has(sub)) {
358
- pendingSet.add(sub);
359
- pendingQueue.push(sub);
368
+ let node = head;
369
+ while (node) {
370
+ const s = node.sub;
371
+ if (s) {
372
+ if (s._c) {
373
+ propagateDirty(s);
374
+ } else if (!pendingSet.has(s)) {
375
+ pendingSet.add(s);
376
+ pendingQueue.push(s);
377
+ }
360
378
  }
379
+ node = node.sigNext;
361
380
  }
362
381
  drainQueue();
363
382
  } finally {
@@ -368,30 +387,6 @@ function notifySubscribers(signal2) {
368
387
  }
369
388
  }
370
389
  }
371
- function cleanup(subscriber) {
372
- const sub = subscriber;
373
- const singleDep = sub._dep;
374
- if (singleDep !== void 0) {
375
- const sig = singleDep;
376
- const subs = sig[SUBS];
377
- if (subs?.delete(subscriber)) {
378
- syncFastPath(sig, subs);
379
- }
380
- sub._dep = void 0;
381
- sub._depEpoch = void 0;
382
- return;
383
- }
384
- const deps = sub._deps;
385
- if (!deps || deps.size === 0) return;
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);
391
- }
392
- }
393
- deps.clear();
394
- }
395
390
 
396
391
  // src/core/signals/derived.ts
397
392
  function derived(getter, options) {
@@ -401,6 +396,7 @@ function derived(getter, options) {
401
396
  const cs = {};
402
397
  cs._d = false;
403
398
  cs._g = getter;
399
+ cs.__v = 0;
404
400
  const markDirty = () => {
405
401
  if (cs._d) return;
406
402
  cs._d = true;
@@ -430,11 +426,14 @@ function derived(getter, options) {
430
426
  evaluating = true;
431
427
  let threw = true;
432
428
  try {
429
+ const prev = cs._v;
433
430
  retrack(() => {
434
- cs._v = getter();
431
+ const next = getter();
432
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
435
433
  cs._d = false;
436
434
  threw = false;
437
435
  }, markDirty);
436
+ if (!Object.is(prev, cs._v)) cs.__v++;
438
437
  } finally {
439
438
  evaluating = false;
440
439
  if (threw) cs._d = true;
@@ -454,6 +453,7 @@ function derived(getter, options) {
454
453
  cs._d = false;
455
454
  threw = false;
456
455
  }, markDirty);
456
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
457
457
  } finally {
458
458
  evaluating = false;
459
459
  if (threw) cs._d = true;
@@ -500,120 +500,122 @@ function isSSR() {
500
500
 
501
501
  // src/core/signals/effect.ts
502
502
  var _g = globalThis;
503
+ var MAX_RERUNS = 100;
504
+ function flushUserCleanups(ctx) {
505
+ const list = ctx.userCleanups;
506
+ if (list.length === 0) return;
507
+ ctx.userCleanups = [];
508
+ for (let i = list.length - 1; i >= 0; i--) {
509
+ try {
510
+ list[i]();
511
+ } catch (err) {
512
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
513
+ }
514
+ }
515
+ }
516
+ function drainReruns(ctx) {
517
+ let reruns = 1;
518
+ do {
519
+ ctx.rerunPending = false;
520
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
521
+ retrack(ctx.bodyFn, ctx.subscriber);
522
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
523
+ if (ctx.rerunPending) {
524
+ ctx.rerunPending = false;
525
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
526
+ console.error(
527
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
528
+ );
529
+ }
530
+ }
531
+ }
532
+ function disposeEffect(ctx) {
533
+ if (ctx.disposed) return;
534
+ ctx.disposed = true;
535
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
536
+ if (h) {
537
+ try {
538
+ h.emit("effect:destroy", { effectFn: ctx.fn });
539
+ } catch {
540
+ }
541
+ }
542
+ try {
543
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
544
+ } catch (err) {
545
+ if (typeof console !== "undefined") {
546
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
547
+ }
548
+ }
549
+ try {
550
+ cleanup(ctx.subscriber);
551
+ } catch (err) {
552
+ if (typeof console !== "undefined") {
553
+ console.warn("[SibuJS effect] dispose threw:", err);
554
+ }
555
+ }
556
+ }
503
557
  function effect(effectFn, options) {
504
558
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
505
559
  if (isSSR()) return () => {
506
560
  };
507
- const onError = options?.onError;
508
- let userCleanups = [];
509
- const onCleanup = (fn) => {
510
- userCleanups.push(fn);
561
+ const ctx = {
562
+ fn: effectFn,
563
+ onError: options?.onError,
564
+ userCleanups: [],
565
+ running: false,
566
+ rerunPending: false,
567
+ disposed: false,
568
+ onCleanup: null,
569
+ subscriber: null,
570
+ bodyFn: null
511
571
  };
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
- }
572
+ ctx.onCleanup = (fn) => {
573
+ ctx.userCleanups.push(fn);
525
574
  };
526
- const invokeBody = () => effectFn(onCleanup);
527
- const wrappedFn = onError ? () => {
575
+ const onErrorCaptured = ctx.onError;
576
+ ctx.bodyFn = onErrorCaptured ? () => {
528
577
  try {
529
- invokeBody();
578
+ ctx.fn(ctx.onCleanup);
530
579
  } catch (err) {
531
- onError(err);
580
+ onErrorCaptured(err);
532
581
  }
533
- } : invokeBody;
534
- let cleanupHandle = () => {
582
+ } : () => {
583
+ ctx.fn(ctx.onCleanup);
535
584
  };
536
- let running = false;
537
- let rerunPending = false;
538
- const MAX_RERUNS = 100;
539
- const subscriber = () => {
540
- if (running) {
541
- rerunPending = true;
585
+ const sub = (() => {
586
+ if (ctx.running) {
587
+ ctx.rerunPending = true;
542
588
  return;
543
589
  }
544
- running = true;
590
+ ctx.running = true;
545
591
  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);
592
+ ctx.rerunPending = false;
593
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
594
+ retrack(ctx.bodyFn, sub);
595
+ if (ctx.rerunPending) drainReruns(ctx);
562
596
  } finally {
563
- running = false;
564
- rerunPending = false;
597
+ ctx.running = false;
598
+ ctx.rerunPending = false;
565
599
  }
566
- };
567
- running = true;
600
+ });
601
+ sub.depsHead = null;
602
+ sub.depsTail = null;
603
+ sub._epoch = 0;
604
+ sub._structDirty = false;
605
+ sub._runEpoch = 0;
606
+ sub._runs = 0;
607
+ ctx.subscriber = sub;
608
+ ctx.running = true;
568
609
  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);
610
+ retrack(ctx.bodyFn, ctx.subscriber);
611
+ if (ctx.rerunPending) drainReruns(ctx);
585
612
  } finally {
586
- running = false;
587
- rerunPending = false;
613
+ ctx.running = false;
614
+ ctx.rerunPending = false;
588
615
  }
589
616
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
590
617
  if (hook) hook.emit("effect:create", { effectFn });
591
- let disposed = false;
592
- return () => {
593
- if (disposed) return;
594
- disposed = true;
595
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
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
- }
616
- };
618
+ return () => disposeEffect(ctx);
617
619
  }
618
620
 
619
621
  // src/reactivity/batch.ts
@@ -650,32 +652,64 @@ function flushBatch() {
650
652
  var _g2 = globalThis;
651
653
  var _isDev3 = isDev();
652
654
  function signal(initial, options) {
653
- const state = { value: initial };
655
+ const state = {
656
+ value: initial,
657
+ __v: 0,
658
+ __sc: 0,
659
+ subsHead: null,
660
+ subsTail: null,
661
+ __activeNode: null,
662
+ __name: void 0
663
+ };
654
664
  const debugName = _isDev3 ? options?.name : void 0;
655
665
  const equalsFn = options?.equals;
656
- if (debugName) {
657
- state.__name = debugName;
658
- }
666
+ if (debugName) state.__name = debugName;
659
667
  function get() {
660
668
  recordDependency(state);
661
669
  return state.value;
662
670
  }
663
671
  get.__signal = state;
664
672
  if (debugName) get.__name = debugName;
665
- function set(next) {
666
- const newValue = typeof next === "function" ? next(state.value) : next;
667
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
668
- if (_isDev3) {
669
- const oldValue = state.value;
673
+ let set;
674
+ if (equalsFn) {
675
+ set = (next) => {
676
+ const prev = state.value;
677
+ const newValue = typeof next === "function" ? next(prev) : next;
678
+ if (equalsFn(prev, newValue)) return;
679
+ state.value = newValue;
680
+ state.__v++;
681
+ if (_isDev3) {
682
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
683
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
684
+ }
685
+ if (!enqueueBatchedSignal(state)) {
686
+ notifySubscribers(state);
687
+ }
688
+ };
689
+ } else if (_isDev3) {
690
+ set = (next) => {
691
+ const prev = state.value;
692
+ const newValue = typeof next === "function" ? next(prev) : next;
693
+ if (Object.is(newValue, prev)) return;
670
694
  state.value = newValue;
695
+ state.__v++;
671
696
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
672
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
673
- } else {
697
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
698
+ if (!enqueueBatchedSignal(state)) {
699
+ notifySubscribers(state);
700
+ }
701
+ };
702
+ } else {
703
+ set = (next) => {
704
+ const prev = state.value;
705
+ const newValue = typeof next === "function" ? next(prev) : next;
706
+ if (Object.is(newValue, prev)) return;
674
707
  state.value = newValue;
675
- }
676
- if (!enqueueBatchedSignal(state)) {
677
- notifySubscribers(state);
678
- }
708
+ state.__v++;
709
+ if (!enqueueBatchedSignal(state)) {
710
+ notifySubscribers(state);
711
+ }
712
+ };
679
713
  }
680
714
  if (_isDev3) {
681
715
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;