sibujs 2.0.0 → 2.1.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 (60) hide show
  1. package/dist/browser.cjs +144 -102
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +183 -102
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-MIUAXB7K.js → chunk-3DZP6OIT.js} +2 -2
  7. package/dist/{chunk-ITX6OO3F.js → chunk-45YP72ZQ.js} +1 -1
  8. package/dist/{chunk-ND2664SF.js → chunk-AMK2TYNW.js} +13 -9
  9. package/dist/{chunk-R73P76YZ.js → chunk-CWBVQML6.js} +1 -1
  10. package/dist/{chunk-54EDRCEF.js → chunk-DRUZZAK4.js} +1 -1
  11. package/dist/{chunk-3NSGB5JN.js → chunk-GWWURC5M.js} +2 -2
  12. package/dist/{chunk-SAHNHTFC.js → chunk-KGYT6UO6.js} +3 -3
  13. package/dist/{chunk-52YJLLRO.js → chunk-NASX6ST2.js} +1 -1
  14. package/dist/{chunk-O2MNQFLP.js → chunk-O6EFQ3KT.js} +5 -5
  15. package/dist/{chunk-GTBNNBJ6.js → chunk-OJ3P4ECI.js} +1 -1
  16. package/dist/{chunk-7JDB7I65.js → chunk-ON5MMR2J.js} +4 -4
  17. package/dist/{chunk-KLRMB5ZS.js → chunk-P2HSJDDN.js} +2 -2
  18. package/dist/{chunk-VLPPXTYG.js → chunk-QO3WC6FS.js} +145 -93
  19. package/dist/{chunk-CC65Y57T.js → chunk-RDTDJCAB.js} +1 -1
  20. package/dist/{chunk-JXMMDLBY.js → chunk-TH2ILCYW.js} +8 -4
  21. package/dist/{chunk-3LR7GLWQ.js → chunk-V6C4FADE.js} +3 -3
  22. package/dist/{chunk-WOMYAHHI.js → chunk-WANSMF2L.js} +4 -4
  23. package/dist/{chunk-DFPFITST.js → chunk-WIPZPFBQ.js} +1 -1
  24. package/dist/{chunk-HB24TBAF.js → chunk-WZA53FXU.js} +38 -10
  25. package/dist/{chunk-JA6667UN.js → chunk-ZAQSMOED.js} +4 -4
  26. package/dist/data.cjs +176 -102
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +148 -91
  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 +176 -102
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +182 -104
  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 +185 -102
  39. package/dist/index.d.cts +15 -1
  40. package/dist/index.d.ts +15 -1
  41. package/dist/index.js +14 -10
  42. package/dist/{introspect-BWNjNw64.d.cts → introspect-2TOlQ7oa.d.cts} +3 -1
  43. package/dist/{introspect-cY2pg9pW.d.ts → introspect-DnIpHQQz.d.ts} +3 -1
  44. package/dist/motion.cjs +78 -62
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +176 -102
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +142 -89
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +142 -89
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +144 -102
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +66 -28
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +174 -89
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +176 -102
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
package/dist/data.cjs CHANGED
@@ -62,11 +62,24 @@ function devWarn(message) {
62
62
 
63
63
  // src/reactivity/track.ts
64
64
  var _isDev2 = isDev();
65
- var subscriberStack = new Array(32);
66
- var stackCapacity = 32;
65
+ var STACK_INITIAL = 32;
66
+ var STACK_SHRINK_THRESHOLD = 128;
67
+ var subscriberStack = new Array(STACK_INITIAL);
68
+ var stackCapacity = STACK_INITIAL;
67
69
  var stackTop = -1;
68
70
  var currentSubscriber = null;
69
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;
81
+ }
82
+ }
70
83
  var notifyDepth = 0;
71
84
  var pendingQueue = [];
72
85
  var pendingSet = /* @__PURE__ */ new Set();
@@ -79,13 +92,45 @@ function safeInvoke(sub) {
79
92
  }
80
93
  }
81
94
  var trackingSuspended = false;
95
+ var subscriberEpochCounter = 0;
82
96
  function retrack(effectFn, subscriber) {
83
97
  const prev = currentSubscriber;
84
98
  currentSubscriber = subscriber;
99
+ const sub = subscriber;
100
+ const epoch = ++subscriberEpochCounter;
101
+ sub._epoch = epoch;
85
102
  try {
86
103
  effectFn();
87
104
  } finally {
88
105
  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);
126
+ }
127
+ }
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);
89
134
  }
90
135
  }
91
136
  function track(effectFn, subscriber) {
@@ -103,36 +148,49 @@ function track(effectFn, subscriber) {
103
148
  } finally {
104
149
  stackTop--;
105
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;
154
+ }
106
155
  }
107
156
  return () => cleanup(subscriber);
108
157
  }
109
158
  function recordDependency(signal2) {
110
159
  if (!currentSubscriber) return;
111
160
  const sub = currentSubscriber;
112
- if (sub._dep === signal2) return;
161
+ const epoch = sub._epoch;
162
+ if (sub._dep === signal2) {
163
+ sub._depEpoch = epoch;
164
+ return;
165
+ }
113
166
  const deps = sub._deps;
114
167
  if (deps) {
115
- if (deps.has(signal2)) return;
116
- deps.add(signal2);
168
+ deps.set(signal2, epoch);
117
169
  } else if (sub._dep !== void 0) {
118
- const set = /* @__PURE__ */ new Set();
119
- set.add(sub._dep);
120
- set.add(signal2);
121
- sub._deps = set;
170
+ const map = /* @__PURE__ */ new Map();
171
+ map.set(sub._dep, sub._depEpoch);
172
+ map.set(signal2, epoch);
173
+ sub._deps = map;
122
174
  sub._dep = void 0;
175
+ sub._depEpoch = void 0;
123
176
  } else {
124
177
  sub._dep = signal2;
178
+ sub._depEpoch = epoch;
125
179
  }
126
- let subs = signal2[SUBS];
180
+ const sig = signal2;
181
+ let subs = sig[SUBS];
127
182
  if (!subs) {
128
183
  subs = /* @__PURE__ */ new Set();
129
- signal2[SUBS] = subs;
184
+ sig[SUBS] = subs;
130
185
  }
186
+ const prevSize = subs.size;
131
187
  subs.add(currentSubscriber);
132
- if (subs.size === 1) {
133
- signal2.__f = currentSubscriber;
134
- } else if (signal2.__f !== void 0) {
135
- signal2.__f = void 0;
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
+ }
136
194
  }
137
195
  }
138
196
  function queueSignalNotification(signal2) {
@@ -147,24 +205,55 @@ function queueSignalNotification(signal2) {
147
205
  }
148
206
  }
149
207
  }
150
- var maxDrainIterations = 1e5;
208
+ var maxSubscriberRepeats = 50;
209
+ var maxDrainIterations = 1e6;
210
+ var drainEpoch = 0;
211
+ function tickRepeat(sub) {
212
+ const s = sub;
213
+ if (s._runEpoch !== drainEpoch) {
214
+ s._runEpoch = drainEpoch;
215
+ s._runs = 1;
216
+ return false;
217
+ }
218
+ return ++s._runs > maxSubscriberRepeats;
219
+ }
220
+ function cycleError(sub) {
221
+ if (typeof console !== "undefined") {
222
+ const name = sub.__name ?? "<unnamed>";
223
+ console.error(
224
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
225
+ );
226
+ }
227
+ }
228
+ function absoluteDrainError() {
229
+ if (typeof console !== "undefined") {
230
+ console.error(
231
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
232
+ );
233
+ }
234
+ }
235
+ function drainQueue() {
236
+ let i = 0;
237
+ while (i < pendingQueue.length) {
238
+ if (i >= maxDrainIterations) {
239
+ absoluteDrainError();
240
+ break;
241
+ }
242
+ const sub = pendingQueue[i++];
243
+ if (tickRepeat(sub)) {
244
+ cycleError(sub);
245
+ break;
246
+ }
247
+ pendingSet.delete(sub);
248
+ safeInvoke(sub);
249
+ }
250
+ }
151
251
  function drainNotificationQueue() {
152
252
  if (notifyDepth > 0) return;
153
253
  notifyDepth++;
254
+ drainEpoch++;
154
255
  try {
155
- let i = 0;
156
- while (i < pendingQueue.length) {
157
- if (i >= maxDrainIterations) {
158
- if (typeof console !== "undefined") {
159
- console.error(
160
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
161
- );
162
- }
163
- break;
164
- }
165
- safeInvoke(pendingQueue[i]);
166
- i++;
167
- }
256
+ drainQueue();
168
257
  } finally {
169
258
  notifyDepth--;
170
259
  if (notifyDepth === 0) {
@@ -227,25 +316,16 @@ function notifySubscribers(signal2) {
227
316
  return;
228
317
  }
229
318
  notifyDepth++;
319
+ drainEpoch++;
230
320
  try {
231
321
  if (first._c) {
232
322
  propagateDirty(first);
323
+ } else if (tickRepeat(first)) {
324
+ cycleError(first);
233
325
  } else {
234
326
  safeInvoke(first);
235
327
  }
236
- let i = 0;
237
- while (i < pendingQueue.length) {
238
- if (i >= maxDrainIterations) {
239
- if (typeof console !== "undefined") {
240
- console.error(
241
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
242
- );
243
- }
244
- break;
245
- }
246
- safeInvoke(pendingQueue[i]);
247
- i++;
248
- }
328
+ drainQueue();
249
329
  } finally {
250
330
  notifyDepth--;
251
331
  if (notifyDepth === 0) {
@@ -269,44 +349,17 @@ function notifySubscribers(signal2) {
269
349
  return;
270
350
  }
271
351
  notifyDepth++;
352
+ drainEpoch++;
272
353
  try {
273
- let directCount = 0;
274
- let hasComputedSub = false;
275
354
  for (const sub of subs) {
276
- if (sub._c) hasComputedSub = true;
277
- pendingQueue[directCount++] = sub;
278
- }
279
- if (!hasComputedSub) {
280
- for (let i2 = 0; i2 < directCount; i2++) {
281
- safeInvoke(pendingQueue[i2]);
282
- }
283
- } else {
284
- for (let i2 = 0; i2 < directCount; i2++) {
285
- if (pendingQueue[i2]._c) {
286
- propagateDirty(pendingQueue[i2]);
287
- }
288
- }
289
- for (let i2 = 0; i2 < directCount; i2++) {
290
- const sub = pendingQueue[i2];
291
- if (!sub._c && !pendingSet.has(sub)) {
292
- pendingSet.add(sub);
293
- safeInvoke(sub);
294
- }
295
- }
296
- }
297
- let i = directCount;
298
- while (i < pendingQueue.length) {
299
- if (i - directCount >= maxDrainIterations) {
300
- if (typeof console !== "undefined") {
301
- console.error(
302
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
303
- );
304
- }
305
- break;
355
+ if (sub._c) {
356
+ propagateDirty(sub);
357
+ } else if (!pendingSet.has(sub)) {
358
+ pendingSet.add(sub);
359
+ pendingQueue.push(sub);
306
360
  }
307
- safeInvoke(pendingQueue[i]);
308
- i++;
309
361
  }
362
+ drainQueue();
310
363
  } finally {
311
364
  notifyDepth--;
312
365
  if (notifyDepth === 0) {
@@ -319,29 +372,22 @@ function cleanup(subscriber) {
319
372
  const sub = subscriber;
320
373
  const singleDep = sub._dep;
321
374
  if (singleDep !== void 0) {
322
- const subs = singleDep[SUBS];
323
- if (subs) {
324
- subs.delete(subscriber);
325
- if (singleDep.__f === subscriber) {
326
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
327
- } else if (subs.size === 1 && singleDep.__f === void 0) {
328
- singleDep.__f = subs.values().next().value;
329
- }
375
+ const sig = singleDep;
376
+ const subs = sig[SUBS];
377
+ if (subs?.delete(subscriber)) {
378
+ syncFastPath(sig, subs);
330
379
  }
331
380
  sub._dep = void 0;
381
+ sub._depEpoch = void 0;
332
382
  return;
333
383
  }
334
384
  const deps = sub._deps;
335
385
  if (!deps || deps.size === 0) return;
336
- for (const signal2 of deps) {
337
- const subs = signal2[SUBS];
338
- if (subs) {
339
- subs.delete(subscriber);
340
- if (signal2.__f === subscriber) {
341
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
342
- } else if (subs.size === 1 && signal2.__f === void 0) {
343
- signal2.__f = subs.values().next().value;
344
- }
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);
345
391
  }
346
392
  }
347
393
  deps.clear();
@@ -488,29 +534,57 @@ function effect(effectFn, options) {
488
534
  let cleanupHandle = () => {
489
535
  };
490
536
  let running = false;
537
+ let rerunPending = false;
538
+ const MAX_RERUNS = 100;
491
539
  const subscriber = () => {
492
540
  if (running) {
493
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
494
- console.warn(
495
- "[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
496
- );
497
- }
541
+ rerunPending = true;
498
542
  return;
499
543
  }
500
544
  running = true;
501
545
  try {
502
- runUserCleanups();
503
- cleanupHandle();
504
- cleanupHandle = track(wrappedFn, subscriber);
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);
505
562
  } finally {
506
563
  running = false;
564
+ rerunPending = false;
507
565
  }
508
566
  };
509
567
  running = true;
510
568
  try {
511
- cleanupHandle = track(wrappedFn, subscriber);
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);
512
585
  } finally {
513
586
  running = false;
587
+ rerunPending = false;
514
588
  }
515
589
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
516
590
  if (hook) hook.emit("effect:create", { effectFn });
package/dist/data.js CHANGED
@@ -20,14 +20,14 @@ import {
20
20
  syncAdapter,
21
21
  throttle,
22
22
  withRetry
23
- } from "./chunk-JA6667UN.js";
24
- import "./chunk-GTBNNBJ6.js";
25
- import "./chunk-54EDRCEF.js";
23
+ } from "./chunk-ZAQSMOED.js";
24
+ import "./chunk-OJ3P4ECI.js";
25
+ import "./chunk-DRUZZAK4.js";
26
26
  import "./chunk-UCS6AMJ7.js";
27
- import "./chunk-HB24TBAF.js";
27
+ import "./chunk-WZA53FXU.js";
28
28
  import "./chunk-2RA7SHDA.js";
29
- import "./chunk-CC65Y57T.js";
30
- import "./chunk-VLPPXTYG.js";
29
+ import "./chunk-RDTDJCAB.js";
30
+ import "./chunk-QO3WC6FS.js";
31
31
  import "./chunk-LMLD24FC.js";
32
32
  export {
33
33
  __resetQueryCache,