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/ui.cjs CHANGED
@@ -103,24 +103,88 @@ function devWarn(message) {
103
103
 
104
104
  // src/reactivity/track.ts
105
105
  var _isDev2 = isDev();
106
- var STACK_INITIAL = 32;
107
- var STACK_SHRINK_THRESHOLD = 128;
108
- var subscriberStack = new Array(STACK_INITIAL);
109
- var stackCapacity = STACK_INITIAL;
110
- var stackTop = -1;
111
- var currentSubscriber = null;
112
- var SUBS = "__s";
113
- function syncFastPath(signal2, subs) {
114
- const size = subs.size;
115
- if (size === 0) {
116
- signal2.__f = void 0;
117
- delete signal2[SUBS];
118
- } else if (size === 1) {
119
- signal2.__f = subs.values().next().value;
120
- } else {
121
- signal2.__f = void 0;
122
- }
106
+ var POOL_MAX = 4096;
107
+ var nodePool = [];
108
+ function createNode() {
109
+ return {
110
+ sig: null,
111
+ sub: null,
112
+ epoch: 0,
113
+ sigPrev: null,
114
+ sigNext: null,
115
+ subPrev: null,
116
+ subNext: null,
117
+ prevActive: null
118
+ };
119
+ }
120
+ function allocNode(sig, sub, epoch) {
121
+ const n = nodePool.pop();
122
+ if (n) {
123
+ n.sig = sig;
124
+ n.sub = sub;
125
+ n.epoch = epoch;
126
+ return n;
127
+ }
128
+ const fresh = createNode();
129
+ fresh.sig = sig;
130
+ fresh.sub = sub;
131
+ fresh.epoch = epoch;
132
+ return fresh;
133
+ }
134
+ function freeNode(node) {
135
+ node.sig = null;
136
+ node.sub = null;
137
+ node.sigPrev = null;
138
+ node.sigNext = null;
139
+ node.subPrev = null;
140
+ node.subNext = null;
141
+ node.prevActive = null;
142
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
143
+ }
144
+ function linkSignal(sig, node) {
145
+ const oldHead = sig.subsHead ?? null;
146
+ node.sigPrev = null;
147
+ node.sigNext = oldHead;
148
+ if (oldHead) oldHead.sigPrev = node;
149
+ else sig.subsTail = node;
150
+ sig.subsHead = node;
151
+ sig.__sc = (sig.__sc ?? 0) + 1;
152
+ }
153
+ function unlinkSignal(node) {
154
+ const sig = node.sig;
155
+ if (!sig) return;
156
+ const prev = node.sigPrev;
157
+ const next = node.sigNext;
158
+ if (prev) prev.sigNext = next;
159
+ else sig.subsHead = next;
160
+ if (next) next.sigPrev = prev;
161
+ else sig.subsTail = prev;
162
+ sig.__sc = (sig.__sc ?? 1) - 1;
163
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
164
+ if (sig.__sc === 0) {
165
+ sig.subsHead = null;
166
+ sig.subsTail = null;
167
+ }
168
+ }
169
+ function linkSub(sub, node) {
170
+ const oldTail = sub.depsTail ?? null;
171
+ node.subPrev = oldTail;
172
+ node.subNext = null;
173
+ if (oldTail) oldTail.subNext = node;
174
+ else sub.depsHead = node;
175
+ sub.depsTail = node;
176
+ }
177
+ function unlinkSub(node) {
178
+ const sub = node.sub;
179
+ if (!sub) return;
180
+ const prev = node.subPrev;
181
+ const next = node.subNext;
182
+ if (prev) prev.subNext = next;
183
+ else sub.depsHead = next;
184
+ if (next) next.subPrev = prev;
185
+ else sub.depsTail = prev;
123
186
  }
187
+ var currentSubscriber = null;
124
188
  var notifyDepth = 0;
125
189
  var pendingQueue = [];
126
190
  var pendingSet = /* @__PURE__ */ new Set();
@@ -140,98 +204,77 @@ function retrack(effectFn, subscriber) {
140
204
  const sub = subscriber;
141
205
  const epoch = ++subscriberEpochCounter;
142
206
  sub._epoch = epoch;
207
+ sub._structDirty = false;
208
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
209
+ const sig = n.sig;
210
+ n.prevActive = sig.__activeNode ?? null;
211
+ sig.__activeNode = n;
212
+ }
143
213
  try {
144
214
  effectFn();
145
215
  } finally {
146
216
  currentSubscriber = prev;
147
- pruneStaleDeps(sub, epoch);
148
- }
149
- }
150
- function pruneStaleDeps(sub, currentEpoch) {
151
- if (sub._dep !== void 0) {
152
- if (sub._depEpoch !== currentEpoch) {
153
- const sig = sub._dep;
154
- const subs = sig[SUBS];
155
- if (subs?.delete(sub)) syncFastPath(sig, subs);
156
- sub._dep = void 0;
157
- sub._depEpoch = void 0;
158
- }
159
- return;
160
- }
161
- const deps = sub._deps;
162
- if (!deps || deps.size === 0) return;
163
- let stales;
164
- for (const [signal2, epoch] of deps) {
165
- if (epoch !== currentEpoch) {
166
- (stales ?? (stales = [])).push(signal2);
217
+ let node = sub.depsHead ?? null;
218
+ while (node !== null) {
219
+ const next = node.subNext;
220
+ const sig = node.sig;
221
+ sig.__activeNode = node.prevActive;
222
+ node.prevActive = null;
223
+ if (node.epoch !== epoch) {
224
+ unlinkSub(node);
225
+ unlinkSignal(node);
226
+ freeNode(node);
227
+ }
228
+ node = next;
167
229
  }
168
230
  }
169
- if (!stales) return;
170
- for (const signal2 of stales) {
171
- deps.delete(signal2);
172
- const sig = signal2;
173
- const subs = sig[SUBS];
174
- if (subs?.delete(sub)) syncFastPath(sig, subs);
175
- }
176
231
  }
177
232
  function track(effectFn, subscriber) {
178
233
  if (!subscriber) subscriber = effectFn;
179
234
  cleanup(subscriber);
180
- ++stackTop;
181
- if (stackTop >= stackCapacity) {
182
- stackCapacity *= 2;
183
- subscriberStack.length = stackCapacity;
184
- }
185
- subscriberStack[stackTop] = subscriber;
235
+ const prev = currentSubscriber;
186
236
  currentSubscriber = subscriber;
187
237
  try {
188
238
  effectFn();
189
239
  } finally {
190
- stackTop--;
191
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
192
- if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
193
- stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
194
- subscriberStack.length = stackCapacity;
240
+ currentSubscriber = prev;
241
+ const sub2 = subscriber;
242
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
243
+ const sig = n.sig;
244
+ sig.__activeNode = n.prevActive;
245
+ n.prevActive = null;
195
246
  }
196
247
  }
197
- return () => cleanup(subscriber);
248
+ const sub = subscriber;
249
+ return sub._dispose ?? (sub._dispose = () => cleanup(subscriber));
198
250
  }
199
251
  function recordDependency(signal2) {
200
252
  if (!currentSubscriber) return;
201
253
  const sub = currentSubscriber;
202
- const epoch = sub._epoch;
203
- if (sub._dep === signal2) {
204
- sub._depEpoch = epoch;
205
- return;
206
- }
207
- const deps = sub._deps;
208
- if (deps) {
209
- deps.set(signal2, epoch);
210
- } else if (sub._dep !== void 0) {
211
- const map = /* @__PURE__ */ new Map();
212
- map.set(sub._dep, sub._depEpoch);
213
- map.set(signal2, epoch);
214
- sub._deps = map;
215
- sub._dep = void 0;
216
- sub._depEpoch = void 0;
217
- } else {
218
- sub._dep = signal2;
219
- sub._depEpoch = epoch;
220
- }
221
254
  const sig = signal2;
222
- let subs = sig[SUBS];
223
- if (!subs) {
224
- subs = /* @__PURE__ */ new Set();
225
- sig[SUBS] = subs;
255
+ const epoch = sub._epoch ?? 0;
256
+ const active = sig.__activeNode ?? null;
257
+ if (active !== null && active.sub === sub) {
258
+ active.epoch = epoch;
259
+ return;
226
260
  }
227
- const prevSize = subs.size;
228
- subs.add(currentSubscriber);
229
- if (subs.size !== prevSize) {
230
- if (subs.size === 1) {
231
- sig.__f = currentSubscriber;
232
- } else if (sig.__f !== void 0) {
233
- sig.__f = void 0;
234
- }
261
+ const node = allocNode(signal2, sub, epoch);
262
+ node.prevActive = active;
263
+ sig.__activeNode = node;
264
+ linkSub(sub, node);
265
+ linkSignal(sig, node);
266
+ sub._structDirty = true;
267
+ }
268
+ function cleanup(subscriber) {
269
+ const sub = subscriber;
270
+ let node = sub.depsHead ?? null;
271
+ sub.depsHead = null;
272
+ sub.depsTail = null;
273
+ while (node) {
274
+ const next = node.subNext;
275
+ unlinkSignal(node);
276
+ freeNode(node);
277
+ node = next;
235
278
  }
236
279
  }
237
280
  var maxSubscriberRepeats = 50;
@@ -244,7 +287,8 @@ function tickRepeat(sub) {
244
287
  s._runs = 1;
245
288
  return false;
246
289
  }
247
- return ++s._runs > maxSubscriberRepeats;
290
+ s._runs = (s._runs ?? 0) + 1;
291
+ return s._runs > maxSubscriberRepeats;
248
292
  }
249
293
  function cycleError(sub) {
250
294
  if (typeof console !== "undefined") {
@@ -286,93 +330,64 @@ function propagateDirty(sub) {
286
330
  stack.push(rootSig);
287
331
  while (stack.length > baseLen) {
288
332
  const sig = stack.pop();
289
- const first = sig.__f;
290
- if (first) {
291
- if (first._c) {
292
- const nSig = first._sig;
293
- if (!nSig._d) {
294
- nSig._d = true;
295
- stack.push(nSig);
296
- }
297
- } else if (!pendingSet.has(first)) {
298
- pendingSet.add(first);
299
- pendingQueue.push(first);
300
- }
301
- continue;
302
- }
303
- const subs = sig[SUBS];
304
- if (!subs) continue;
305
- for (const s of subs) {
306
- if (s._c) {
307
- const nSig = s._sig;
308
- if (nSig && !nSig._d) {
309
- nSig._d = true;
310
- stack.push(nSig);
311
- } else if (!nSig) {
312
- s();
333
+ let node = sig.subsHead ?? null;
334
+ while (node) {
335
+ const s = node.sub;
336
+ if (s) {
337
+ if (s._c) {
338
+ const nSig = s._sig;
339
+ if (nSig) {
340
+ if (!nSig._d) {
341
+ nSig._d = true;
342
+ stack.push(nSig);
343
+ }
344
+ } else {
345
+ s();
346
+ }
347
+ } else if (!pendingSet.has(s)) {
348
+ pendingSet.add(s);
349
+ pendingQueue.push(s);
313
350
  }
314
- } else if (!pendingSet.has(s)) {
315
- pendingSet.add(s);
316
- pendingQueue.push(s);
317
351
  }
352
+ node = node.sigNext;
318
353
  }
319
354
  }
320
355
  }
321
356
  function notifySubscribers(signal2) {
322
- const first = signal2.__f;
323
- if (first) {
324
- if (notifyDepth > 0) {
325
- if (first._c) {
326
- propagateDirty(first);
327
- } else if (!pendingSet.has(first)) {
328
- pendingSet.add(first);
329
- pendingQueue.push(first);
330
- }
331
- return;
332
- }
333
- notifyDepth++;
334
- drainEpoch++;
335
- try {
336
- if (first._c) {
337
- propagateDirty(first);
338
- } else if (tickRepeat(first)) {
339
- cycleError(first);
340
- } else {
341
- safeInvoke(first);
342
- }
343
- drainQueue();
344
- } finally {
345
- notifyDepth--;
346
- if (notifyDepth === 0) {
347
- pendingQueue.length = 0;
348
- pendingSet.clear();
349
- }
350
- }
351
- return;
352
- }
353
- const subs = signal2[SUBS];
354
- if (!subs || subs.size === 0) return;
357
+ const sig = signal2;
358
+ const head = sig.subsHead;
359
+ if (!head) return;
355
360
  if (notifyDepth > 0) {
356
- for (const sub of subs) {
357
- if (sub._c) {
358
- propagateDirty(sub);
359
- } else if (!pendingSet.has(sub)) {
360
- pendingSet.add(sub);
361
- pendingQueue.push(sub);
361
+ let node = head;
362
+ while (node) {
363
+ const s = node.sub;
364
+ if (s) {
365
+ if (s._c) {
366
+ propagateDirty(s);
367
+ } else if (!pendingSet.has(s)) {
368
+ pendingSet.add(s);
369
+ pendingQueue.push(s);
370
+ }
362
371
  }
372
+ node = node.sigNext;
363
373
  }
364
374
  return;
365
375
  }
366
376
  notifyDepth++;
367
377
  drainEpoch++;
368
378
  try {
369
- for (const sub of subs) {
370
- if (sub._c) {
371
- propagateDirty(sub);
372
- } else if (!pendingSet.has(sub)) {
373
- pendingSet.add(sub);
374
- pendingQueue.push(sub);
379
+ let node = head;
380
+ while (node) {
381
+ const s = node.sub;
382
+ if (s) {
383
+ if (s._c) {
384
+ propagateDirty(s);
385
+ } else if (!pendingSet.has(s)) {
386
+ pendingSet.add(s);
387
+ pendingQueue.push(s);
388
+ }
375
389
  }
390
+ node = node.sigNext;
376
391
  }
377
392
  drainQueue();
378
393
  } finally {
@@ -383,30 +398,6 @@ function notifySubscribers(signal2) {
383
398
  }
384
399
  }
385
400
  }
386
- function cleanup(subscriber) {
387
- const sub = subscriber;
388
- const singleDep = sub._dep;
389
- if (singleDep !== void 0) {
390
- const sig = singleDep;
391
- const subs = sig[SUBS];
392
- if (subs?.delete(subscriber)) {
393
- syncFastPath(sig, subs);
394
- }
395
- sub._dep = void 0;
396
- sub._depEpoch = void 0;
397
- return;
398
- }
399
- const deps = sub._deps;
400
- if (!deps || deps.size === 0) return;
401
- for (const signal2 of deps.keys()) {
402
- const sig = signal2;
403
- const subs = sig[SUBS];
404
- if (subs?.delete(subscriber)) {
405
- syncFastPath(sig, subs);
406
- }
407
- }
408
- deps.clear();
409
- }
410
401
 
411
402
  // src/core/signals/derived.ts
412
403
  function derived(getter, options) {
@@ -416,6 +407,7 @@ function derived(getter, options) {
416
407
  const cs = {};
417
408
  cs._d = false;
418
409
  cs._g = getter;
410
+ cs.__v = 0;
419
411
  const markDirty = () => {
420
412
  if (cs._d) return;
421
413
  cs._d = true;
@@ -445,11 +437,14 @@ function derived(getter, options) {
445
437
  evaluating = true;
446
438
  let threw = true;
447
439
  try {
440
+ const prev = cs._v;
448
441
  retrack(() => {
449
- cs._v = getter();
442
+ const next = getter();
443
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
450
444
  cs._d = false;
451
445
  threw = false;
452
446
  }, markDirty);
447
+ if (!Object.is(prev, cs._v)) cs.__v++;
453
448
  } finally {
454
449
  evaluating = false;
455
450
  if (threw) cs._d = true;
@@ -469,6 +464,7 @@ function derived(getter, options) {
469
464
  cs._d = false;
470
465
  threw = false;
471
466
  }, markDirty);
467
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
472
468
  } finally {
473
469
  evaluating = false;
474
470
  if (threw) cs._d = true;
@@ -501,32 +497,64 @@ function enqueueBatchedSignal(signal2) {
501
497
  var _g = globalThis;
502
498
  var _isDev3 = isDev();
503
499
  function signal(initial, options) {
504
- const state = { value: initial };
500
+ const state = {
501
+ value: initial,
502
+ __v: 0,
503
+ __sc: 0,
504
+ subsHead: null,
505
+ subsTail: null,
506
+ __activeNode: null,
507
+ __name: void 0
508
+ };
505
509
  const debugName = _isDev3 ? options?.name : void 0;
506
510
  const equalsFn = options?.equals;
507
- if (debugName) {
508
- state.__name = debugName;
509
- }
511
+ if (debugName) state.__name = debugName;
510
512
  function get() {
511
513
  recordDependency(state);
512
514
  return state.value;
513
515
  }
514
516
  get.__signal = state;
515
517
  if (debugName) get.__name = debugName;
516
- function set(next) {
517
- const newValue = typeof next === "function" ? next(state.value) : next;
518
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
519
- if (_isDev3) {
520
- const oldValue = state.value;
518
+ let set;
519
+ if (equalsFn) {
520
+ set = (next) => {
521
+ const prev = state.value;
522
+ const newValue = typeof next === "function" ? next(prev) : next;
523
+ if (equalsFn(prev, newValue)) return;
521
524
  state.value = newValue;
525
+ state.__v++;
526
+ if (_isDev3) {
527
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
528
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
529
+ }
530
+ if (!enqueueBatchedSignal(state)) {
531
+ notifySubscribers(state);
532
+ }
533
+ };
534
+ } else if (_isDev3) {
535
+ set = (next) => {
536
+ const prev = state.value;
537
+ const newValue = typeof next === "function" ? next(prev) : next;
538
+ if (Object.is(newValue, prev)) return;
539
+ state.value = newValue;
540
+ state.__v++;
522
541
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
523
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
524
- } else {
542
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
543
+ if (!enqueueBatchedSignal(state)) {
544
+ notifySubscribers(state);
545
+ }
546
+ };
547
+ } else {
548
+ set = (next) => {
549
+ const prev = state.value;
550
+ const newValue = typeof next === "function" ? next(prev) : next;
551
+ if (Object.is(newValue, prev)) return;
525
552
  state.value = newValue;
526
- }
527
- if (!enqueueBatchedSignal(state)) {
528
- notifySubscribers(state);
529
- }
553
+ state.__v++;
554
+ if (!enqueueBatchedSignal(state)) {
555
+ notifySubscribers(state);
556
+ }
557
+ };
530
558
  }
531
559
  if (_isDev3) {
532
560
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -856,120 +884,122 @@ function isSSR() {
856
884
 
857
885
  // src/core/signals/effect.ts
858
886
  var _g2 = globalThis;
887
+ var MAX_RERUNS = 100;
888
+ function flushUserCleanups(ctx) {
889
+ const list = ctx.userCleanups;
890
+ if (list.length === 0) return;
891
+ ctx.userCleanups = [];
892
+ for (let i = list.length - 1; i >= 0; i--) {
893
+ try {
894
+ list[i]();
895
+ } catch (err) {
896
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
897
+ }
898
+ }
899
+ }
900
+ function drainReruns(ctx) {
901
+ let reruns = 1;
902
+ do {
903
+ ctx.rerunPending = false;
904
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
905
+ retrack(ctx.bodyFn, ctx.subscriber);
906
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
907
+ if (ctx.rerunPending) {
908
+ ctx.rerunPending = false;
909
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
910
+ console.error(
911
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
912
+ );
913
+ }
914
+ }
915
+ }
916
+ function disposeEffect(ctx) {
917
+ if (ctx.disposed) return;
918
+ ctx.disposed = true;
919
+ const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
920
+ if (h) {
921
+ try {
922
+ h.emit("effect:destroy", { effectFn: ctx.fn });
923
+ } catch {
924
+ }
925
+ }
926
+ try {
927
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
928
+ } catch (err) {
929
+ if (typeof console !== "undefined") {
930
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
931
+ }
932
+ }
933
+ try {
934
+ cleanup(ctx.subscriber);
935
+ } catch (err) {
936
+ if (typeof console !== "undefined") {
937
+ console.warn("[SibuJS effect] dispose threw:", err);
938
+ }
939
+ }
940
+ }
859
941
  function effect(effectFn, options) {
860
942
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
861
943
  if (isSSR()) return () => {
862
944
  };
863
- const onError = options?.onError;
864
- let userCleanups = [];
865
- const onCleanup = (fn) => {
866
- userCleanups.push(fn);
945
+ const ctx = {
946
+ fn: effectFn,
947
+ onError: options?.onError,
948
+ userCleanups: [],
949
+ running: false,
950
+ rerunPending: false,
951
+ disposed: false,
952
+ onCleanup: null,
953
+ subscriber: null,
954
+ bodyFn: null
867
955
  };
868
- const runUserCleanups = () => {
869
- if (userCleanups.length === 0) return;
870
- const list = userCleanups;
871
- userCleanups = [];
872
- for (let i = list.length - 1; i >= 0; i--) {
873
- try {
874
- list[i]();
875
- } catch (err) {
876
- if (typeof console !== "undefined") {
877
- console.warn("[SibuJS effect] onCleanup threw:", err);
878
- }
879
- }
880
- }
956
+ ctx.onCleanup = (fn) => {
957
+ ctx.userCleanups.push(fn);
881
958
  };
882
- const invokeBody = () => effectFn(onCleanup);
883
- const wrappedFn = onError ? () => {
959
+ const onErrorCaptured = ctx.onError;
960
+ ctx.bodyFn = onErrorCaptured ? () => {
884
961
  try {
885
- invokeBody();
962
+ ctx.fn(ctx.onCleanup);
886
963
  } catch (err) {
887
- onError(err);
964
+ onErrorCaptured(err);
888
965
  }
889
- } : invokeBody;
890
- let cleanupHandle = () => {
966
+ } : () => {
967
+ ctx.fn(ctx.onCleanup);
891
968
  };
892
- let running = false;
893
- let rerunPending = false;
894
- const MAX_RERUNS = 100;
895
- const subscriber = () => {
896
- if (running) {
897
- rerunPending = true;
969
+ const sub = (() => {
970
+ if (ctx.running) {
971
+ ctx.rerunPending = true;
898
972
  return;
899
973
  }
900
- running = true;
974
+ ctx.running = true;
901
975
  try {
902
- let reruns = 0;
903
- do {
904
- rerunPending = false;
905
- runUserCleanups();
906
- cleanupHandle();
907
- cleanupHandle = track(wrappedFn, subscriber);
908
- if (++reruns > MAX_RERUNS) {
909
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
910
- console.error(
911
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
912
- );
913
- }
914
- rerunPending = false;
915
- break;
916
- }
917
- } while (rerunPending);
976
+ ctx.rerunPending = false;
977
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
978
+ retrack(ctx.bodyFn, sub);
979
+ if (ctx.rerunPending) drainReruns(ctx);
918
980
  } finally {
919
- running = false;
920
- rerunPending = false;
981
+ ctx.running = false;
982
+ ctx.rerunPending = false;
921
983
  }
922
- };
923
- running = true;
984
+ });
985
+ sub.depsHead = null;
986
+ sub.depsTail = null;
987
+ sub._epoch = 0;
988
+ sub._structDirty = false;
989
+ sub._runEpoch = 0;
990
+ sub._runs = 0;
991
+ ctx.subscriber = sub;
992
+ ctx.running = true;
924
993
  try {
925
- let reruns = 0;
926
- do {
927
- rerunPending = false;
928
- runUserCleanups();
929
- cleanupHandle();
930
- cleanupHandle = track(wrappedFn, subscriber);
931
- if (++reruns > MAX_RERUNS) {
932
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
933
- console.error(
934
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
935
- );
936
- }
937
- rerunPending = false;
938
- break;
939
- }
940
- } while (rerunPending);
994
+ retrack(ctx.bodyFn, ctx.subscriber);
995
+ if (ctx.rerunPending) drainReruns(ctx);
941
996
  } finally {
942
- running = false;
943
- rerunPending = false;
997
+ ctx.running = false;
998
+ ctx.rerunPending = false;
944
999
  }
945
1000
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
946
1001
  if (hook) hook.emit("effect:create", { effectFn });
947
- let disposed = false;
948
- return () => {
949
- if (disposed) return;
950
- disposed = true;
951
- const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
952
- if (h) {
953
- try {
954
- h.emit("effect:destroy", { effectFn });
955
- } catch {
956
- }
957
- }
958
- try {
959
- runUserCleanups();
960
- } catch (err) {
961
- if (typeof console !== "undefined") {
962
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
963
- }
964
- }
965
- try {
966
- cleanupHandle();
967
- } catch (err) {
968
- if (typeof console !== "undefined") {
969
- console.warn("[SibuJS effect] dispose threw:", err);
970
- }
971
- }
972
- };
1002
+ return () => disposeEffect(ctx);
973
1003
  }
974
1004
 
975
1005
  // src/ui/virtualList.ts