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
@@ -49,11 +49,24 @@ function devWarn(message) {
49
49
 
50
50
  // src/reactivity/track.ts
51
51
  var _isDev2 = isDev();
52
- var subscriberStack = new Array(32);
53
- var stackCapacity = 32;
52
+ var STACK_INITIAL = 32;
53
+ var STACK_SHRINK_THRESHOLD = 128;
54
+ var subscriberStack = new Array(STACK_INITIAL);
55
+ var stackCapacity = STACK_INITIAL;
54
56
  var stackTop = -1;
55
57
  var currentSubscriber = null;
56
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;
68
+ }
69
+ }
57
70
  var notifyDepth = 0;
58
71
  var pendingQueue = [];
59
72
  var pendingSet = /* @__PURE__ */ new Set();
@@ -66,13 +79,45 @@ function safeInvoke(sub) {
66
79
  }
67
80
  }
68
81
  var trackingSuspended = false;
82
+ var subscriberEpochCounter = 0;
69
83
  function retrack(effectFn, subscriber) {
70
84
  const prev = currentSubscriber;
71
85
  currentSubscriber = subscriber;
86
+ const sub = subscriber;
87
+ const epoch = ++subscriberEpochCounter;
88
+ sub._epoch = epoch;
72
89
  try {
73
90
  effectFn();
74
91
  } finally {
75
92
  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);
113
+ }
114
+ }
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);
76
121
  }
77
122
  }
78
123
  function track(effectFn, subscriber) {
@@ -90,36 +135,49 @@ function track(effectFn, subscriber) {
90
135
  } finally {
91
136
  stackTop--;
92
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;
141
+ }
93
142
  }
94
143
  return () => cleanup(subscriber);
95
144
  }
96
145
  function recordDependency(signal2) {
97
146
  if (!currentSubscriber) return;
98
147
  const sub = currentSubscriber;
99
- if (sub._dep === signal2) return;
148
+ const epoch = sub._epoch;
149
+ if (sub._dep === signal2) {
150
+ sub._depEpoch = epoch;
151
+ return;
152
+ }
100
153
  const deps = sub._deps;
101
154
  if (deps) {
102
- if (deps.has(signal2)) return;
103
- deps.add(signal2);
155
+ deps.set(signal2, epoch);
104
156
  } else if (sub._dep !== void 0) {
105
- const set = /* @__PURE__ */ new Set();
106
- set.add(sub._dep);
107
- set.add(signal2);
108
- sub._deps = set;
157
+ const map = /* @__PURE__ */ new Map();
158
+ map.set(sub._dep, sub._depEpoch);
159
+ map.set(signal2, epoch);
160
+ sub._deps = map;
109
161
  sub._dep = void 0;
162
+ sub._depEpoch = void 0;
110
163
  } else {
111
164
  sub._dep = signal2;
165
+ sub._depEpoch = epoch;
112
166
  }
113
- let subs = signal2[SUBS];
167
+ const sig = signal2;
168
+ let subs = sig[SUBS];
114
169
  if (!subs) {
115
170
  subs = /* @__PURE__ */ new Set();
116
- signal2[SUBS] = subs;
171
+ sig[SUBS] = subs;
117
172
  }
173
+ const prevSize = subs.size;
118
174
  subs.add(currentSubscriber);
119
- if (subs.size === 1) {
120
- signal2.__f = currentSubscriber;
121
- } else if (signal2.__f !== void 0) {
122
- signal2.__f = void 0;
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
+ }
123
181
  }
124
182
  }
125
183
  function queueSignalNotification(signal2) {
@@ -134,24 +192,55 @@ function queueSignalNotification(signal2) {
134
192
  }
135
193
  }
136
194
  }
137
- var maxDrainIterations = 1e5;
195
+ var maxSubscriberRepeats = 50;
196
+ var maxDrainIterations = 1e6;
197
+ var drainEpoch = 0;
198
+ function tickRepeat(sub) {
199
+ const s = sub;
200
+ if (s._runEpoch !== drainEpoch) {
201
+ s._runEpoch = drainEpoch;
202
+ s._runs = 1;
203
+ return false;
204
+ }
205
+ return ++s._runs > maxSubscriberRepeats;
206
+ }
207
+ function cycleError(sub) {
208
+ if (typeof console !== "undefined") {
209
+ const name = sub.__name ?? "<unnamed>";
210
+ console.error(
211
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
212
+ );
213
+ }
214
+ }
215
+ function absoluteDrainError() {
216
+ if (typeof console !== "undefined") {
217
+ console.error(
218
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
219
+ );
220
+ }
221
+ }
222
+ function drainQueue() {
223
+ let i = 0;
224
+ while (i < pendingQueue.length) {
225
+ if (i >= maxDrainIterations) {
226
+ absoluteDrainError();
227
+ break;
228
+ }
229
+ const sub = pendingQueue[i++];
230
+ if (tickRepeat(sub)) {
231
+ cycleError(sub);
232
+ break;
233
+ }
234
+ pendingSet.delete(sub);
235
+ safeInvoke(sub);
236
+ }
237
+ }
138
238
  function drainNotificationQueue() {
139
239
  if (notifyDepth > 0) return;
140
240
  notifyDepth++;
241
+ drainEpoch++;
141
242
  try {
142
- let i = 0;
143
- while (i < pendingQueue.length) {
144
- if (i >= maxDrainIterations) {
145
- if (typeof console !== "undefined") {
146
- console.error(
147
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
148
- );
149
- }
150
- break;
151
- }
152
- safeInvoke(pendingQueue[i]);
153
- i++;
154
- }
243
+ drainQueue();
155
244
  } finally {
156
245
  notifyDepth--;
157
246
  if (notifyDepth === 0) {
@@ -214,25 +303,16 @@ function notifySubscribers(signal2) {
214
303
  return;
215
304
  }
216
305
  notifyDepth++;
306
+ drainEpoch++;
217
307
  try {
218
308
  if (first._c) {
219
309
  propagateDirty(first);
310
+ } else if (tickRepeat(first)) {
311
+ cycleError(first);
220
312
  } else {
221
313
  safeInvoke(first);
222
314
  }
223
- let i = 0;
224
- while (i < pendingQueue.length) {
225
- if (i >= maxDrainIterations) {
226
- if (typeof console !== "undefined") {
227
- console.error(
228
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
229
- );
230
- }
231
- break;
232
- }
233
- safeInvoke(pendingQueue[i]);
234
- i++;
235
- }
315
+ drainQueue();
236
316
  } finally {
237
317
  notifyDepth--;
238
318
  if (notifyDepth === 0) {
@@ -256,44 +336,17 @@ function notifySubscribers(signal2) {
256
336
  return;
257
337
  }
258
338
  notifyDepth++;
339
+ drainEpoch++;
259
340
  try {
260
- let directCount = 0;
261
- let hasComputedSub = false;
262
341
  for (const sub of subs) {
263
- if (sub._c) hasComputedSub = true;
264
- pendingQueue[directCount++] = sub;
265
- }
266
- if (!hasComputedSub) {
267
- for (let i2 = 0; i2 < directCount; i2++) {
268
- safeInvoke(pendingQueue[i2]);
269
- }
270
- } else {
271
- for (let i2 = 0; i2 < directCount; i2++) {
272
- if (pendingQueue[i2]._c) {
273
- propagateDirty(pendingQueue[i2]);
274
- }
275
- }
276
- for (let i2 = 0; i2 < directCount; i2++) {
277
- const sub = pendingQueue[i2];
278
- if (!sub._c && !pendingSet.has(sub)) {
279
- pendingSet.add(sub);
280
- safeInvoke(sub);
281
- }
282
- }
283
- }
284
- let i = directCount;
285
- while (i < pendingQueue.length) {
286
- if (i - directCount >= maxDrainIterations) {
287
- if (typeof console !== "undefined") {
288
- console.error(
289
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
290
- );
291
- }
292
- break;
342
+ if (sub._c) {
343
+ propagateDirty(sub);
344
+ } else if (!pendingSet.has(sub)) {
345
+ pendingSet.add(sub);
346
+ pendingQueue.push(sub);
293
347
  }
294
- safeInvoke(pendingQueue[i]);
295
- i++;
296
348
  }
349
+ drainQueue();
297
350
  } finally {
298
351
  notifyDepth--;
299
352
  if (notifyDepth === 0) {
@@ -306,29 +359,22 @@ function cleanup(subscriber) {
306
359
  const sub = subscriber;
307
360
  const singleDep = sub._dep;
308
361
  if (singleDep !== void 0) {
309
- const subs = singleDep[SUBS];
310
- if (subs) {
311
- subs.delete(subscriber);
312
- if (singleDep.__f === subscriber) {
313
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
314
- } else if (subs.size === 1 && singleDep.__f === void 0) {
315
- singleDep.__f = subs.values().next().value;
316
- }
362
+ const sig = singleDep;
363
+ const subs = sig[SUBS];
364
+ if (subs?.delete(subscriber)) {
365
+ syncFastPath(sig, subs);
317
366
  }
318
367
  sub._dep = void 0;
368
+ sub._depEpoch = void 0;
319
369
  return;
320
370
  }
321
371
  const deps = sub._deps;
322
372
  if (!deps || deps.size === 0) return;
323
- for (const signal2 of deps) {
324
- const subs = signal2[SUBS];
325
- if (subs) {
326
- subs.delete(subscriber);
327
- if (signal2.__f === subscriber) {
328
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
329
- } else if (subs.size === 1 && signal2.__f === void 0) {
330
- signal2.__f = subs.values().next().value;
331
- }
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);
332
378
  }
333
379
  }
334
380
  deps.clear();
@@ -395,29 +441,57 @@ function effect(effectFn, options) {
395
441
  let cleanupHandle = () => {
396
442
  };
397
443
  let running = false;
444
+ let rerunPending = false;
445
+ const MAX_RERUNS = 100;
398
446
  const subscriber = () => {
399
447
  if (running) {
400
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
401
- console.warn(
402
- "[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."
403
- );
404
- }
448
+ rerunPending = true;
405
449
  return;
406
450
  }
407
451
  running = true;
408
452
  try {
409
- runUserCleanups();
410
- cleanupHandle();
411
- cleanupHandle = track(wrappedFn, subscriber);
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);
412
469
  } finally {
413
470
  running = false;
471
+ rerunPending = false;
414
472
  }
415
473
  };
416
474
  running = true;
417
475
  try {
418
- cleanupHandle = track(wrappedFn, subscriber);
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);
419
492
  } finally {
420
493
  running = false;
494
+ rerunPending = false;
421
495
  }
422
496
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
423
497
  if (hook) hook.emit("effect:create", { effectFn });
package/dist/ecosystem.js CHANGED
@@ -7,17 +7,17 @@ import {
7
7
  mobXAdapter,
8
8
  reduxAdapter,
9
9
  zustandAdapter
10
- } from "./chunk-WOMYAHHI.js";
10
+ } from "./chunk-WANSMF2L.js";
11
11
  import "./chunk-3JHCYHWN.js";
12
- import "./chunk-KLRMB5ZS.js";
13
- import "./chunk-DFPFITST.js";
14
- import "./chunk-54EDRCEF.js";
12
+ import "./chunk-P2HSJDDN.js";
13
+ import "./chunk-WIPZPFBQ.js";
14
+ import "./chunk-DRUZZAK4.js";
15
15
  import "./chunk-2UPRY23K.js";
16
16
  import "./chunk-UCS6AMJ7.js";
17
- import "./chunk-HB24TBAF.js";
17
+ import "./chunk-WZA53FXU.js";
18
18
  import "./chunk-2RA7SHDA.js";
19
- import "./chunk-CC65Y57T.js";
20
- import "./chunk-VLPPXTYG.js";
19
+ import "./chunk-RDTDJCAB.js";
20
+ import "./chunk-QO3WC6FS.js";
21
21
  import "./chunk-LMLD24FC.js";
22
22
  export {
23
23
  antdAdapter,