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
@@ -49,24 +49,88 @@ function devWarn(message) {
49
49
 
50
50
  // src/reactivity/track.ts
51
51
  var _isDev2 = isDev();
52
- var STACK_INITIAL = 32;
53
- var STACK_SHRINK_THRESHOLD = 128;
54
- var subscriberStack = new Array(STACK_INITIAL);
55
- var stackCapacity = STACK_INITIAL;
56
- var stackTop = -1;
57
- var currentSubscriber = null;
58
- var SUBS = "__s";
59
- function syncFastPath(signal2, subs) {
60
- const size = subs.size;
61
- if (size === 0) {
62
- signal2.__f = void 0;
63
- delete signal2[SUBS];
64
- } else if (size === 1) {
65
- signal2.__f = subs.values().next().value;
66
- } else {
67
- signal2.__f = void 0;
52
+ var POOL_MAX = 4096;
53
+ var nodePool = [];
54
+ function createNode() {
55
+ return {
56
+ sig: null,
57
+ sub: null,
58
+ epoch: 0,
59
+ sigPrev: null,
60
+ sigNext: null,
61
+ subPrev: null,
62
+ subNext: null,
63
+ prevActive: null
64
+ };
65
+ }
66
+ function allocNode(sig, sub, epoch) {
67
+ const n = nodePool.pop();
68
+ if (n) {
69
+ n.sig = sig;
70
+ n.sub = sub;
71
+ n.epoch = epoch;
72
+ return n;
73
+ }
74
+ const fresh = createNode();
75
+ fresh.sig = sig;
76
+ fresh.sub = sub;
77
+ fresh.epoch = epoch;
78
+ return fresh;
79
+ }
80
+ function freeNode(node) {
81
+ node.sig = null;
82
+ node.sub = null;
83
+ node.sigPrev = null;
84
+ node.sigNext = null;
85
+ node.subPrev = null;
86
+ node.subNext = null;
87
+ node.prevActive = null;
88
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
89
+ }
90
+ function linkSignal(sig, node) {
91
+ const oldHead = sig.subsHead ?? null;
92
+ node.sigPrev = null;
93
+ node.sigNext = oldHead;
94
+ if (oldHead) oldHead.sigPrev = node;
95
+ else sig.subsTail = node;
96
+ sig.subsHead = node;
97
+ sig.__sc = (sig.__sc ?? 0) + 1;
98
+ }
99
+ function unlinkSignal(node) {
100
+ const sig = node.sig;
101
+ if (!sig) return;
102
+ const prev = node.sigPrev;
103
+ const next = node.sigNext;
104
+ if (prev) prev.sigNext = next;
105
+ else sig.subsHead = next;
106
+ if (next) next.sigPrev = prev;
107
+ else sig.subsTail = prev;
108
+ sig.__sc = (sig.__sc ?? 1) - 1;
109
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
110
+ if (sig.__sc === 0) {
111
+ sig.subsHead = null;
112
+ sig.subsTail = null;
68
113
  }
69
114
  }
115
+ function linkSub(sub, node) {
116
+ const oldTail = sub.depsTail ?? null;
117
+ node.subPrev = oldTail;
118
+ node.subNext = null;
119
+ if (oldTail) oldTail.subNext = node;
120
+ else sub.depsHead = node;
121
+ sub.depsTail = node;
122
+ }
123
+ function unlinkSub(node) {
124
+ const sub = node.sub;
125
+ if (!sub) return;
126
+ const prev = node.subPrev;
127
+ const next = node.subNext;
128
+ if (prev) prev.subNext = next;
129
+ else sub.depsHead = next;
130
+ if (next) next.subPrev = prev;
131
+ else sub.depsTail = prev;
132
+ }
133
+ var currentSubscriber = null;
70
134
  var notifyDepth = 0;
71
135
  var pendingQueue = [];
72
136
  var pendingSet = /* @__PURE__ */ new Set();
@@ -86,110 +150,77 @@ function retrack(effectFn, subscriber) {
86
150
  const sub = subscriber;
87
151
  const epoch = ++subscriberEpochCounter;
88
152
  sub._epoch = epoch;
153
+ sub._structDirty = false;
154
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
155
+ const sig = n.sig;
156
+ n.prevActive = sig.__activeNode ?? null;
157
+ sig.__activeNode = n;
158
+ }
89
159
  try {
90
160
  effectFn();
91
161
  } finally {
92
162
  currentSubscriber = prev;
93
- pruneStaleDeps(sub, epoch);
94
- }
95
- }
96
- function pruneStaleDeps(sub, currentEpoch) {
97
- if (sub._dep !== void 0) {
98
- if (sub._depEpoch !== currentEpoch) {
99
- const sig = sub._dep;
100
- const subs = sig[SUBS];
101
- if (subs?.delete(sub)) syncFastPath(sig, subs);
102
- sub._dep = void 0;
103
- sub._depEpoch = void 0;
104
- }
105
- return;
106
- }
107
- const deps = sub._deps;
108
- if (!deps || deps.size === 0) return;
109
- let stales;
110
- for (const [signal2, epoch] of deps) {
111
- if (epoch !== currentEpoch) {
112
- (stales ?? (stales = [])).push(signal2);
163
+ let node = sub.depsHead ?? null;
164
+ while (node !== null) {
165
+ const next = node.subNext;
166
+ const sig = node.sig;
167
+ sig.__activeNode = node.prevActive;
168
+ node.prevActive = null;
169
+ if (node.epoch !== epoch) {
170
+ unlinkSub(node);
171
+ unlinkSignal(node);
172
+ freeNode(node);
173
+ }
174
+ node = next;
113
175
  }
114
176
  }
115
- if (!stales) return;
116
- for (const signal2 of stales) {
117
- deps.delete(signal2);
118
- const sig = signal2;
119
- const subs = sig[SUBS];
120
- if (subs?.delete(sub)) syncFastPath(sig, subs);
121
- }
122
177
  }
123
178
  function track(effectFn, subscriber) {
124
179
  if (!subscriber) subscriber = effectFn;
125
180
  cleanup(subscriber);
126
- ++stackTop;
127
- if (stackTop >= stackCapacity) {
128
- stackCapacity *= 2;
129
- subscriberStack.length = stackCapacity;
130
- }
131
- subscriberStack[stackTop] = subscriber;
181
+ const prev = currentSubscriber;
132
182
  currentSubscriber = subscriber;
133
183
  try {
134
184
  effectFn();
135
185
  } finally {
136
- stackTop--;
137
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
138
- if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
139
- stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
140
- subscriberStack.length = stackCapacity;
186
+ currentSubscriber = prev;
187
+ const sub2 = subscriber;
188
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
189
+ const sig = n.sig;
190
+ sig.__activeNode = n.prevActive;
191
+ n.prevActive = null;
141
192
  }
142
193
  }
143
- return () => cleanup(subscriber);
194
+ const sub = subscriber;
195
+ return sub._dispose ?? (sub._dispose = () => cleanup(subscriber));
144
196
  }
145
197
  function recordDependency(signal2) {
146
198
  if (!currentSubscriber) return;
147
199
  const sub = currentSubscriber;
148
- const epoch = sub._epoch;
149
- if (sub._dep === signal2) {
150
- sub._depEpoch = epoch;
151
- return;
152
- }
153
- const deps = sub._deps;
154
- if (deps) {
155
- deps.set(signal2, epoch);
156
- } else if (sub._dep !== void 0) {
157
- const map = /* @__PURE__ */ new Map();
158
- map.set(sub._dep, sub._depEpoch);
159
- map.set(signal2, epoch);
160
- sub._deps = map;
161
- sub._dep = void 0;
162
- sub._depEpoch = void 0;
163
- } else {
164
- sub._dep = signal2;
165
- sub._depEpoch = epoch;
166
- }
167
200
  const sig = signal2;
168
- let subs = sig[SUBS];
169
- if (!subs) {
170
- subs = /* @__PURE__ */ new Set();
171
- sig[SUBS] = subs;
172
- }
173
- const prevSize = subs.size;
174
- subs.add(currentSubscriber);
175
- if (subs.size !== prevSize) {
176
- if (subs.size === 1) {
177
- sig.__f = currentSubscriber;
178
- } else if (sig.__f !== void 0) {
179
- sig.__f = void 0;
180
- }
201
+ const epoch = sub._epoch ?? 0;
202
+ const active = sig.__activeNode ?? null;
203
+ if (active !== null && active.sub === sub) {
204
+ active.epoch = epoch;
205
+ return;
181
206
  }
207
+ const node = allocNode(signal2, sub, epoch);
208
+ node.prevActive = active;
209
+ sig.__activeNode = node;
210
+ linkSub(sub, node);
211
+ linkSignal(sig, node);
212
+ sub._structDirty = true;
182
213
  }
183
- function queueSignalNotification(signal2) {
184
- const subs = signal2[SUBS];
185
- if (!subs) return;
186
- for (const sub of subs) {
187
- if (sub._c) {
188
- propagateDirty(sub);
189
- } else if (!pendingSet.has(sub)) {
190
- pendingSet.add(sub);
191
- pendingQueue.push(sub);
192
- }
214
+ function cleanup(subscriber) {
215
+ const sub = subscriber;
216
+ let node = sub.depsHead ?? null;
217
+ sub.depsHead = null;
218
+ sub.depsTail = null;
219
+ while (node) {
220
+ const next = node.subNext;
221
+ unlinkSignal(node);
222
+ freeNode(node);
223
+ node = next;
193
224
  }
194
225
  }
195
226
  var maxSubscriberRepeats = 50;
@@ -202,7 +233,8 @@ function tickRepeat(sub) {
202
233
  s._runs = 1;
203
234
  return false;
204
235
  }
205
- return ++s._runs > maxSubscriberRepeats;
236
+ s._runs = (s._runs ?? 0) + 1;
237
+ return s._runs > maxSubscriberRepeats;
206
238
  }
207
239
  function cycleError(sub) {
208
240
  if (typeof console !== "undefined") {
@@ -258,93 +290,80 @@ function propagateDirty(sub) {
258
290
  stack.push(rootSig);
259
291
  while (stack.length > baseLen) {
260
292
  const sig = stack.pop();
261
- const first = sig.__f;
262
- if (first) {
263
- if (first._c) {
264
- const nSig = first._sig;
265
- if (!nSig._d) {
266
- nSig._d = true;
267
- stack.push(nSig);
293
+ let node = sig.subsHead ?? null;
294
+ while (node) {
295
+ const s = node.sub;
296
+ if (s) {
297
+ if (s._c) {
298
+ const nSig = s._sig;
299
+ if (nSig) {
300
+ if (!nSig._d) {
301
+ nSig._d = true;
302
+ stack.push(nSig);
303
+ }
304
+ } else {
305
+ s();
306
+ }
307
+ } else if (!pendingSet.has(s)) {
308
+ pendingSet.add(s);
309
+ pendingQueue.push(s);
268
310
  }
269
- } else if (!pendingSet.has(first)) {
270
- pendingSet.add(first);
271
- pendingQueue.push(first);
272
311
  }
273
- continue;
312
+ node = node.sigNext;
274
313
  }
275
- const subs = sig[SUBS];
276
- if (!subs) continue;
277
- for (const s of subs) {
314
+ }
315
+ }
316
+ function queueSignalNotification(signal2) {
317
+ const sig = signal2;
318
+ let node = sig.subsHead ?? null;
319
+ while (node) {
320
+ const s = node.sub;
321
+ if (s) {
278
322
  if (s._c) {
279
- const nSig = s._sig;
280
- if (nSig && !nSig._d) {
281
- nSig._d = true;
282
- stack.push(nSig);
283
- } else if (!nSig) {
284
- s();
285
- }
323
+ propagateDirty(s);
286
324
  } else if (!pendingSet.has(s)) {
287
325
  pendingSet.add(s);
288
326
  pendingQueue.push(s);
289
327
  }
290
328
  }
329
+ node = node.sigNext;
291
330
  }
292
331
  }
293
332
  function notifySubscribers(signal2) {
294
- const first = signal2.__f;
295
- if (first) {
296
- if (notifyDepth > 0) {
297
- if (first._c) {
298
- propagateDirty(first);
299
- } else if (!pendingSet.has(first)) {
300
- pendingSet.add(first);
301
- pendingQueue.push(first);
302
- }
303
- return;
304
- }
305
- notifyDepth++;
306
- drainEpoch++;
307
- try {
308
- if (first._c) {
309
- propagateDirty(first);
310
- } else if (tickRepeat(first)) {
311
- cycleError(first);
312
- } else {
313
- safeInvoke(first);
314
- }
315
- drainQueue();
316
- } finally {
317
- notifyDepth--;
318
- if (notifyDepth === 0) {
319
- pendingQueue.length = 0;
320
- pendingSet.clear();
321
- }
322
- }
323
- return;
324
- }
325
- const subs = signal2[SUBS];
326
- if (!subs || subs.size === 0) return;
333
+ const sig = signal2;
334
+ const head = sig.subsHead;
335
+ if (!head) return;
327
336
  if (notifyDepth > 0) {
328
- for (const sub of subs) {
329
- if (sub._c) {
330
- propagateDirty(sub);
331
- } else if (!pendingSet.has(sub)) {
332
- pendingSet.add(sub);
333
- pendingQueue.push(sub);
337
+ let node = head;
338
+ while (node) {
339
+ const s = node.sub;
340
+ if (s) {
341
+ if (s._c) {
342
+ propagateDirty(s);
343
+ } else if (!pendingSet.has(s)) {
344
+ pendingSet.add(s);
345
+ pendingQueue.push(s);
346
+ }
334
347
  }
348
+ node = node.sigNext;
335
349
  }
336
350
  return;
337
351
  }
338
352
  notifyDepth++;
339
353
  drainEpoch++;
340
354
  try {
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);
355
+ let node = head;
356
+ while (node) {
357
+ const s = node.sub;
358
+ if (s) {
359
+ if (s._c) {
360
+ propagateDirty(s);
361
+ } else if (!pendingSet.has(s)) {
362
+ pendingSet.add(s);
363
+ pendingQueue.push(s);
364
+ }
347
365
  }
366
+ node = node.sigNext;
348
367
  }
349
368
  drainQueue();
350
369
  } finally {
@@ -355,30 +374,6 @@ function notifySubscribers(signal2) {
355
374
  }
356
375
  }
357
376
  }
358
- function cleanup(subscriber) {
359
- const sub = subscriber;
360
- const singleDep = sub._dep;
361
- if (singleDep !== void 0) {
362
- const sig = singleDep;
363
- const subs = sig[SUBS];
364
- if (subs?.delete(subscriber)) {
365
- syncFastPath(sig, subs);
366
- }
367
- sub._dep = void 0;
368
- sub._depEpoch = void 0;
369
- return;
370
- }
371
- const deps = sub._deps;
372
- if (!deps || deps.size === 0) return;
373
- for (const signal2 of deps.keys()) {
374
- const sig = signal2;
375
- const subs = sig[SUBS];
376
- if (subs?.delete(subscriber)) {
377
- syncFastPath(sig, subs);
378
- }
379
- }
380
- deps.clear();
381
- }
382
377
 
383
378
  // src/core/ssr-context.ts
384
379
  var als = null;
@@ -407,120 +402,122 @@ function isSSR() {
407
402
 
408
403
  // src/core/signals/effect.ts
409
404
  var _g = globalThis;
405
+ var MAX_RERUNS = 100;
406
+ function flushUserCleanups(ctx) {
407
+ const list = ctx.userCleanups;
408
+ if (list.length === 0) return;
409
+ ctx.userCleanups = [];
410
+ for (let i = list.length - 1; i >= 0; i--) {
411
+ try {
412
+ list[i]();
413
+ } catch (err) {
414
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
415
+ }
416
+ }
417
+ }
418
+ function drainReruns(ctx) {
419
+ let reruns = 1;
420
+ do {
421
+ ctx.rerunPending = false;
422
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
423
+ retrack(ctx.bodyFn, ctx.subscriber);
424
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
425
+ if (ctx.rerunPending) {
426
+ ctx.rerunPending = false;
427
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
428
+ console.error(
429
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
430
+ );
431
+ }
432
+ }
433
+ }
434
+ function disposeEffect(ctx) {
435
+ if (ctx.disposed) return;
436
+ ctx.disposed = true;
437
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
438
+ if (h) {
439
+ try {
440
+ h.emit("effect:destroy", { effectFn: ctx.fn });
441
+ } catch {
442
+ }
443
+ }
444
+ try {
445
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
446
+ } catch (err) {
447
+ if (typeof console !== "undefined") {
448
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
449
+ }
450
+ }
451
+ try {
452
+ cleanup(ctx.subscriber);
453
+ } catch (err) {
454
+ if (typeof console !== "undefined") {
455
+ console.warn("[SibuJS effect] dispose threw:", err);
456
+ }
457
+ }
458
+ }
410
459
  function effect(effectFn, options) {
411
460
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
412
461
  if (isSSR()) return () => {
413
462
  };
414
- const onError = options?.onError;
415
- let userCleanups = [];
416
- const onCleanup = (fn) => {
417
- userCleanups.push(fn);
463
+ const ctx = {
464
+ fn: effectFn,
465
+ onError: options?.onError,
466
+ userCleanups: [],
467
+ running: false,
468
+ rerunPending: false,
469
+ disposed: false,
470
+ onCleanup: null,
471
+ subscriber: null,
472
+ bodyFn: null
418
473
  };
419
- const runUserCleanups = () => {
420
- if (userCleanups.length === 0) return;
421
- const list = userCleanups;
422
- userCleanups = [];
423
- for (let i = list.length - 1; i >= 0; i--) {
424
- try {
425
- list[i]();
426
- } catch (err) {
427
- if (typeof console !== "undefined") {
428
- console.warn("[SibuJS effect] onCleanup threw:", err);
429
- }
430
- }
431
- }
474
+ ctx.onCleanup = (fn) => {
475
+ ctx.userCleanups.push(fn);
432
476
  };
433
- const invokeBody = () => effectFn(onCleanup);
434
- const wrappedFn = onError ? () => {
477
+ const onErrorCaptured = ctx.onError;
478
+ ctx.bodyFn = onErrorCaptured ? () => {
435
479
  try {
436
- invokeBody();
480
+ ctx.fn(ctx.onCleanup);
437
481
  } catch (err) {
438
- onError(err);
482
+ onErrorCaptured(err);
439
483
  }
440
- } : invokeBody;
441
- let cleanupHandle = () => {
484
+ } : () => {
485
+ ctx.fn(ctx.onCleanup);
442
486
  };
443
- let running = false;
444
- let rerunPending = false;
445
- const MAX_RERUNS = 100;
446
- const subscriber = () => {
447
- if (running) {
448
- rerunPending = true;
487
+ const sub = (() => {
488
+ if (ctx.running) {
489
+ ctx.rerunPending = true;
449
490
  return;
450
491
  }
451
- running = true;
492
+ ctx.running = true;
452
493
  try {
453
- let reruns = 0;
454
- do {
455
- rerunPending = false;
456
- runUserCleanups();
457
- cleanupHandle();
458
- cleanupHandle = track(wrappedFn, subscriber);
459
- if (++reruns > MAX_RERUNS) {
460
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
461
- console.error(
462
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
463
- );
464
- }
465
- rerunPending = false;
466
- break;
467
- }
468
- } while (rerunPending);
494
+ ctx.rerunPending = false;
495
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
496
+ retrack(ctx.bodyFn, sub);
497
+ if (ctx.rerunPending) drainReruns(ctx);
469
498
  } finally {
470
- running = false;
471
- rerunPending = false;
499
+ ctx.running = false;
500
+ ctx.rerunPending = false;
472
501
  }
473
- };
474
- running = true;
502
+ });
503
+ sub.depsHead = null;
504
+ sub.depsTail = null;
505
+ sub._epoch = 0;
506
+ sub._structDirty = false;
507
+ sub._runEpoch = 0;
508
+ sub._runs = 0;
509
+ ctx.subscriber = sub;
510
+ ctx.running = true;
475
511
  try {
476
- let reruns = 0;
477
- do {
478
- rerunPending = false;
479
- runUserCleanups();
480
- cleanupHandle();
481
- cleanupHandle = track(wrappedFn, subscriber);
482
- if (++reruns > MAX_RERUNS) {
483
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
484
- console.error(
485
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
486
- );
487
- }
488
- rerunPending = false;
489
- break;
490
- }
491
- } while (rerunPending);
512
+ retrack(ctx.bodyFn, ctx.subscriber);
513
+ if (ctx.rerunPending) drainReruns(ctx);
492
514
  } finally {
493
- running = false;
494
- rerunPending = false;
515
+ ctx.running = false;
516
+ ctx.rerunPending = false;
495
517
  }
496
518
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
497
519
  if (hook) hook.emit("effect:create", { effectFn });
498
- let disposed = false;
499
- return () => {
500
- if (disposed) return;
501
- disposed = true;
502
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
503
- if (h) {
504
- try {
505
- h.emit("effect:destroy", { effectFn });
506
- } catch {
507
- }
508
- }
509
- try {
510
- runUserCleanups();
511
- } catch (err) {
512
- if (typeof console !== "undefined") {
513
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
514
- }
515
- }
516
- try {
517
- cleanupHandle();
518
- } catch (err) {
519
- if (typeof console !== "undefined") {
520
- console.warn("[SibuJS effect] dispose threw:", err);
521
- }
522
- }
523
- };
520
+ return () => disposeEffect(ctx);
524
521
  }
525
522
 
526
523
  // src/reactivity/batch.ts
@@ -557,32 +554,64 @@ function flushBatch() {
557
554
  var _g2 = globalThis;
558
555
  var _isDev3 = isDev();
559
556
  function signal(initial, options) {
560
- const state = { value: initial };
557
+ const state = {
558
+ value: initial,
559
+ __v: 0,
560
+ __sc: 0,
561
+ subsHead: null,
562
+ subsTail: null,
563
+ __activeNode: null,
564
+ __name: void 0
565
+ };
561
566
  const debugName = _isDev3 ? options?.name : void 0;
562
567
  const equalsFn = options?.equals;
563
- if (debugName) {
564
- state.__name = debugName;
565
- }
568
+ if (debugName) state.__name = debugName;
566
569
  function get() {
567
570
  recordDependency(state);
568
571
  return state.value;
569
572
  }
570
573
  get.__signal = state;
571
574
  if (debugName) get.__name = debugName;
572
- function set(next) {
573
- const newValue = typeof next === "function" ? next(state.value) : next;
574
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
575
- if (_isDev3) {
576
- const oldValue = state.value;
575
+ let set;
576
+ if (equalsFn) {
577
+ set = (next) => {
578
+ const prev = state.value;
579
+ const newValue = typeof next === "function" ? next(prev) : next;
580
+ if (equalsFn(prev, newValue)) return;
581
+ state.value = newValue;
582
+ state.__v++;
583
+ if (_isDev3) {
584
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
585
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
586
+ }
587
+ if (!enqueueBatchedSignal(state)) {
588
+ notifySubscribers(state);
589
+ }
590
+ };
591
+ } else if (_isDev3) {
592
+ set = (next) => {
593
+ const prev = state.value;
594
+ const newValue = typeof next === "function" ? next(prev) : next;
595
+ if (Object.is(newValue, prev)) return;
577
596
  state.value = newValue;
597
+ state.__v++;
578
598
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
579
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
580
- } else {
599
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
600
+ if (!enqueueBatchedSignal(state)) {
601
+ notifySubscribers(state);
602
+ }
603
+ };
604
+ } else {
605
+ set = (next) => {
606
+ const prev = state.value;
607
+ const newValue = typeof next === "function" ? next(prev) : next;
608
+ if (Object.is(newValue, prev)) return;
581
609
  state.value = newValue;
582
- }
583
- if (!enqueueBatchedSignal(state)) {
584
- notifySubscribers(state);
585
- }
610
+ state.__v++;
611
+ if (!enqueueBatchedSignal(state)) {
612
+ notifySubscribers(state);
613
+ }
614
+ };
586
615
  }
587
616
  if (_isDev3) {
588
617
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -721,6 +750,7 @@ function derived(getter, options) {
721
750
  const cs = {};
722
751
  cs._d = false;
723
752
  cs._g = getter;
753
+ cs.__v = 0;
724
754
  const markDirty = () => {
725
755
  if (cs._d) return;
726
756
  cs._d = true;
@@ -750,11 +780,14 @@ function derived(getter, options) {
750
780
  evaluating = true;
751
781
  let threw = true;
752
782
  try {
783
+ const prev = cs._v;
753
784
  retrack(() => {
754
- cs._v = getter();
785
+ const next = getter();
786
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
755
787
  cs._d = false;
756
788
  threw = false;
757
789
  }, markDirty);
790
+ if (!Object.is(prev, cs._v)) cs.__v++;
758
791
  } finally {
759
792
  evaluating = false;
760
793
  if (threw) cs._d = true;
@@ -774,6 +807,7 @@ function derived(getter, options) {
774
807
  cs._d = false;
775
808
  threw = false;
776
809
  }, markDirty);
810
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
777
811
  } finally {
778
812
  evaluating = false;
779
813
  if (threw) cs._d = true;