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
@@ -5,11 +5,24 @@ import {
5
5
 
6
6
  // src/reactivity/track.ts
7
7
  var _isDev = isDev();
8
- var subscriberStack = new Array(32);
9
- var stackCapacity = 32;
8
+ var STACK_INITIAL = 32;
9
+ var STACK_SHRINK_THRESHOLD = 128;
10
+ var subscriberStack = new Array(STACK_INITIAL);
11
+ var stackCapacity = STACK_INITIAL;
10
12
  var stackTop = -1;
11
13
  var currentSubscriber = null;
12
14
  var SUBS = "__s";
15
+ function syncFastPath(signal, subs) {
16
+ const size = subs.size;
17
+ if (size === 0) {
18
+ signal.__f = void 0;
19
+ delete signal[SUBS];
20
+ } else if (size === 1) {
21
+ signal.__f = subs.values().next().value;
22
+ } else {
23
+ signal.__f = void 0;
24
+ }
25
+ }
13
26
  var notifyDepth = 0;
14
27
  var pendingQueue = [];
15
28
  var pendingSet = /* @__PURE__ */ new Set();
@@ -23,13 +36,45 @@ function safeInvoke(sub) {
23
36
  }
24
37
  var suspendDepth = 0;
25
38
  var trackingSuspended = false;
39
+ var subscriberEpochCounter = 0;
26
40
  function retrack(effectFn, subscriber) {
27
41
  const prev = currentSubscriber;
28
42
  currentSubscriber = subscriber;
43
+ const sub = subscriber;
44
+ const epoch = ++subscriberEpochCounter;
45
+ sub._epoch = epoch;
29
46
  try {
30
47
  effectFn();
31
48
  } finally {
32
49
  currentSubscriber = prev;
50
+ pruneStaleDeps(sub, epoch);
51
+ }
52
+ }
53
+ function pruneStaleDeps(sub, currentEpoch) {
54
+ if (sub._dep !== void 0) {
55
+ if (sub._depEpoch !== currentEpoch) {
56
+ const sig = sub._dep;
57
+ const subs = sig[SUBS];
58
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
59
+ sub._dep = void 0;
60
+ sub._depEpoch = void 0;
61
+ }
62
+ return;
63
+ }
64
+ const deps = sub._deps;
65
+ if (!deps || deps.size === 0) return;
66
+ let stales;
67
+ for (const [signal, epoch] of deps) {
68
+ if (epoch !== currentEpoch) {
69
+ (stales ?? (stales = [])).push(signal);
70
+ }
71
+ }
72
+ if (!stales) return;
73
+ for (const signal of stales) {
74
+ deps.delete(signal);
75
+ const sig = signal;
76
+ const subs = sig[SUBS];
77
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
33
78
  }
34
79
  }
35
80
  function track(effectFn, subscriber) {
@@ -47,6 +92,10 @@ function track(effectFn, subscriber) {
47
92
  } finally {
48
93
  stackTop--;
49
94
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
95
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
96
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
97
+ subscriberStack.length = stackCapacity;
98
+ }
50
99
  }
51
100
  return () => cleanup(subscriber);
52
101
  }
@@ -82,30 +131,39 @@ function untracked(fn) {
82
131
  function recordDependency(signal) {
83
132
  if (!currentSubscriber) return;
84
133
  const sub = currentSubscriber;
85
- if (sub._dep === signal) return;
134
+ const epoch = sub._epoch;
135
+ if (sub._dep === signal) {
136
+ sub._depEpoch = epoch;
137
+ return;
138
+ }
86
139
  const deps = sub._deps;
87
140
  if (deps) {
88
- if (deps.has(signal)) return;
89
- deps.add(signal);
141
+ deps.set(signal, epoch);
90
142
  } else if (sub._dep !== void 0) {
91
- const set = /* @__PURE__ */ new Set();
92
- set.add(sub._dep);
93
- set.add(signal);
94
- sub._deps = set;
143
+ const map = /* @__PURE__ */ new Map();
144
+ map.set(sub._dep, sub._depEpoch);
145
+ map.set(signal, epoch);
146
+ sub._deps = map;
95
147
  sub._dep = void 0;
148
+ sub._depEpoch = void 0;
96
149
  } else {
97
150
  sub._dep = signal;
151
+ sub._depEpoch = epoch;
98
152
  }
99
- let subs = signal[SUBS];
153
+ const sig = signal;
154
+ let subs = sig[SUBS];
100
155
  if (!subs) {
101
156
  subs = /* @__PURE__ */ new Set();
102
- signal[SUBS] = subs;
157
+ sig[SUBS] = subs;
103
158
  }
159
+ const prevSize = subs.size;
104
160
  subs.add(currentSubscriber);
105
- if (subs.size === 1) {
106
- signal.__f = currentSubscriber;
107
- } else if (signal.__f !== void 0) {
108
- signal.__f = void 0;
161
+ if (subs.size !== prevSize) {
162
+ if (subs.size === 1) {
163
+ sig.__f = currentSubscriber;
164
+ } else if (sig.__f !== void 0) {
165
+ sig.__f = void 0;
166
+ }
109
167
  }
110
168
  }
111
169
  function queueSignalNotification(signal) {
@@ -120,24 +178,60 @@ function queueSignalNotification(signal) {
120
178
  }
121
179
  }
122
180
  }
123
- var maxDrainIterations = 1e5;
181
+ var maxSubscriberRepeats = 50;
182
+ var maxDrainIterations = 1e6;
183
+ var drainEpoch = 0;
184
+ function setMaxDrainIterations(n) {
185
+ const prev = maxDrainIterations;
186
+ if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
187
+ return prev;
188
+ }
189
+ function tickRepeat(sub) {
190
+ const s = sub;
191
+ if (s._runEpoch !== drainEpoch) {
192
+ s._runEpoch = drainEpoch;
193
+ s._runs = 1;
194
+ return false;
195
+ }
196
+ return ++s._runs > maxSubscriberRepeats;
197
+ }
198
+ function cycleError(sub) {
199
+ if (typeof console !== "undefined") {
200
+ const name = sub.__name ?? "<unnamed>";
201
+ console.error(
202
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
203
+ );
204
+ }
205
+ }
206
+ function absoluteDrainError() {
207
+ if (typeof console !== "undefined") {
208
+ console.error(
209
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
210
+ );
211
+ }
212
+ }
213
+ function drainQueue() {
214
+ let i = 0;
215
+ while (i < pendingQueue.length) {
216
+ if (i >= maxDrainIterations) {
217
+ absoluteDrainError();
218
+ break;
219
+ }
220
+ const sub = pendingQueue[i++];
221
+ if (tickRepeat(sub)) {
222
+ cycleError(sub);
223
+ break;
224
+ }
225
+ pendingSet.delete(sub);
226
+ safeInvoke(sub);
227
+ }
228
+ }
124
229
  function drainNotificationQueue() {
125
230
  if (notifyDepth > 0) return;
126
231
  notifyDepth++;
232
+ drainEpoch++;
127
233
  try {
128
- let i = 0;
129
- while (i < pendingQueue.length) {
130
- if (i >= maxDrainIterations) {
131
- if (typeof console !== "undefined") {
132
- console.error(
133
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
134
- );
135
- }
136
- break;
137
- }
138
- safeInvoke(pendingQueue[i]);
139
- i++;
140
- }
234
+ drainQueue();
141
235
  } finally {
142
236
  notifyDepth--;
143
237
  if (notifyDepth === 0) {
@@ -200,25 +294,16 @@ function notifySubscribers(signal) {
200
294
  return;
201
295
  }
202
296
  notifyDepth++;
297
+ drainEpoch++;
203
298
  try {
204
299
  if (first._c) {
205
300
  propagateDirty(first);
301
+ } else if (tickRepeat(first)) {
302
+ cycleError(first);
206
303
  } else {
207
304
  safeInvoke(first);
208
305
  }
209
- let i = 0;
210
- while (i < pendingQueue.length) {
211
- if (i >= maxDrainIterations) {
212
- if (typeof console !== "undefined") {
213
- console.error(
214
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
215
- );
216
- }
217
- break;
218
- }
219
- safeInvoke(pendingQueue[i]);
220
- i++;
221
- }
306
+ drainQueue();
222
307
  } finally {
223
308
  notifyDepth--;
224
309
  if (notifyDepth === 0) {
@@ -242,44 +327,17 @@ function notifySubscribers(signal) {
242
327
  return;
243
328
  }
244
329
  notifyDepth++;
330
+ drainEpoch++;
245
331
  try {
246
- let directCount = 0;
247
- let hasComputedSub = false;
248
332
  for (const sub of subs) {
249
- if (sub._c) hasComputedSub = true;
250
- pendingQueue[directCount++] = sub;
251
- }
252
- if (!hasComputedSub) {
253
- for (let i2 = 0; i2 < directCount; i2++) {
254
- safeInvoke(pendingQueue[i2]);
255
- }
256
- } else {
257
- for (let i2 = 0; i2 < directCount; i2++) {
258
- if (pendingQueue[i2]._c) {
259
- propagateDirty(pendingQueue[i2]);
260
- }
261
- }
262
- for (let i2 = 0; i2 < directCount; i2++) {
263
- const sub = pendingQueue[i2];
264
- if (!sub._c && !pendingSet.has(sub)) {
265
- pendingSet.add(sub);
266
- safeInvoke(sub);
267
- }
268
- }
269
- }
270
- let i = directCount;
271
- while (i < pendingQueue.length) {
272
- if (i - directCount >= maxDrainIterations) {
273
- if (typeof console !== "undefined") {
274
- console.error(
275
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
276
- );
277
- }
278
- break;
333
+ if (sub._c) {
334
+ propagateDirty(sub);
335
+ } else if (!pendingSet.has(sub)) {
336
+ pendingSet.add(sub);
337
+ pendingQueue.push(sub);
279
338
  }
280
- safeInvoke(pendingQueue[i]);
281
- i++;
282
339
  }
340
+ drainQueue();
283
341
  } finally {
284
342
  notifyDepth--;
285
343
  if (notifyDepth === 0) {
@@ -292,29 +350,22 @@ function cleanup(subscriber) {
292
350
  const sub = subscriber;
293
351
  const singleDep = sub._dep;
294
352
  if (singleDep !== void 0) {
295
- const subs = singleDep[SUBS];
296
- if (subs) {
297
- subs.delete(subscriber);
298
- if (singleDep.__f === subscriber) {
299
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
300
- } else if (subs.size === 1 && singleDep.__f === void 0) {
301
- singleDep.__f = subs.values().next().value;
302
- }
353
+ const sig = singleDep;
354
+ const subs = sig[SUBS];
355
+ if (subs?.delete(subscriber)) {
356
+ syncFastPath(sig, subs);
303
357
  }
304
358
  sub._dep = void 0;
359
+ sub._depEpoch = void 0;
305
360
  return;
306
361
  }
307
362
  const deps = sub._deps;
308
363
  if (!deps || deps.size === 0) return;
309
- for (const signal of deps) {
310
- const subs = signal[SUBS];
311
- if (subs) {
312
- subs.delete(subscriber);
313
- if (signal.__f === subscriber) {
314
- signal.__f = subs.size === 1 ? subs.values().next().value : void 0;
315
- } else if (subs.size === 1 && signal.__f === void 0) {
316
- signal.__f = subs.values().next().value;
317
- }
364
+ for (const signal of deps.keys()) {
365
+ const sig = signal;
366
+ const subs = sig[SUBS];
367
+ if (subs?.delete(subscriber)) {
368
+ syncFastPath(sig, subs);
318
369
  }
319
370
  }
320
371
  deps.clear();
@@ -327,6 +378,7 @@ export {
327
378
  untracked,
328
379
  recordDependency,
329
380
  queueSignalNotification,
381
+ setMaxDrainIterations,
330
382
  drainNotificationQueue,
331
383
  notifySubscribers
332
384
  };
@@ -3,7 +3,7 @@ import {
3
3
  notifySubscribers,
4
4
  queueSignalNotification,
5
5
  recordDependency
6
- } from "./chunk-VLPPXTYG.js";
6
+ } from "./chunk-QO3WC6FS.js";
7
7
  import {
8
8
  isDev
9
9
  } from "./chunk-LMLD24FC.js";
@@ -6,10 +6,10 @@ import {
6
6
  } from "./chunk-UCS6AMJ7.js";
7
7
  import {
8
8
  effect
9
- } from "./chunk-HB24TBAF.js";
9
+ } from "./chunk-WZA53FXU.js";
10
10
  import {
11
11
  signal
12
- } from "./chunk-CC65Y57T.js";
12
+ } from "./chunk-RDTDJCAB.js";
13
13
  import {
14
14
  isDev
15
15
  } from "./chunk-LMLD24FC.js";
@@ -1139,8 +1139,12 @@ function getSubscriberCount(getter) {
1139
1139
  return subs ? subs.size : 0;
1140
1140
  }
1141
1141
  function getDependencies(subscriberFn) {
1142
- const deps = subscriberFn._deps;
1143
- return deps ? Array.from(deps) : [];
1142
+ const fn = subscriberFn;
1143
+ const singleDep = fn._dep;
1144
+ if (singleDep !== void 0) return [singleDep];
1145
+ const deps = fn._deps;
1146
+ if (!deps) return [];
1147
+ return deps instanceof Map ? Array.from(deps.keys()) : Array.from(deps);
1144
1148
  }
1145
1149
  function inspectSignal(getter) {
1146
1150
  const signal2 = getter.__signal;
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  derived
3
- } from "./chunk-54EDRCEF.js";
3
+ } from "./chunk-DRUZZAK4.js";
4
4
  import {
5
5
  effect
6
- } from "./chunk-HB24TBAF.js";
6
+ } from "./chunk-WZA53FXU.js";
7
7
  import {
8
8
  batch,
9
9
  signal
10
- } from "./chunk-CC65Y57T.js";
10
+ } from "./chunk-RDTDJCAB.js";
11
11
 
12
12
  // src/patterns/machine.ts
13
13
  function machine(config) {
@@ -3,17 +3,17 @@ import {
3
3
  } from "./chunk-3JHCYHWN.js";
4
4
  import {
5
5
  tagFactory
6
- } from "./chunk-KLRMB5ZS.js";
6
+ } from "./chunk-P2HSJDDN.js";
7
7
  import {
8
8
  derived
9
- } from "./chunk-54EDRCEF.js";
9
+ } from "./chunk-DRUZZAK4.js";
10
10
  import {
11
11
  effect
12
- } from "./chunk-HB24TBAF.js";
12
+ } from "./chunk-WZA53FXU.js";
13
13
  import {
14
14
  batch,
15
15
  signal
16
- } from "./chunk-CC65Y57T.js";
16
+ } from "./chunk-RDTDJCAB.js";
17
17
 
18
18
  // src/ecosystem/adapters/mobx.ts
19
19
  function mobXAdapter(options) {
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-UCS6AMJ7.js";
5
5
  import {
6
6
  track
7
- } from "./chunk-VLPPXTYG.js";
7
+ } from "./chunk-QO3WC6FS.js";
8
8
  import {
9
9
  devWarn,
10
10
  isDev
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  track,
6
6
  untracked
7
- } from "./chunk-VLPPXTYG.js";
7
+ } from "./chunk-QO3WC6FS.js";
8
8
  import {
9
9
  devAssert
10
10
  } from "./chunk-LMLD24FC.js";
@@ -61,29 +61,57 @@ function effect(effectFn, options) {
61
61
  let cleanupHandle = () => {
62
62
  };
63
63
  let running = false;
64
+ let rerunPending = false;
65
+ const MAX_RERUNS = 100;
64
66
  const subscriber = () => {
65
67
  if (running) {
66
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
67
- console.warn(
68
- "[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."
69
- );
70
- }
68
+ rerunPending = true;
71
69
  return;
72
70
  }
73
71
  running = true;
74
72
  try {
75
- runUserCleanups();
76
- cleanupHandle();
77
- cleanupHandle = track(wrappedFn, subscriber);
73
+ let reruns = 0;
74
+ do {
75
+ rerunPending = false;
76
+ runUserCleanups();
77
+ cleanupHandle();
78
+ cleanupHandle = track(wrappedFn, subscriber);
79
+ if (++reruns > MAX_RERUNS) {
80
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
81
+ console.error(
82
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
83
+ );
84
+ }
85
+ rerunPending = false;
86
+ break;
87
+ }
88
+ } while (rerunPending);
78
89
  } finally {
79
90
  running = false;
91
+ rerunPending = false;
80
92
  }
81
93
  };
82
94
  running = true;
83
95
  try {
84
- cleanupHandle = track(wrappedFn, subscriber);
96
+ let reruns = 0;
97
+ do {
98
+ rerunPending = false;
99
+ runUserCleanups();
100
+ cleanupHandle();
101
+ cleanupHandle = track(wrappedFn, subscriber);
102
+ if (++reruns > MAX_RERUNS) {
103
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
104
+ console.error(
105
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
106
+ );
107
+ }
108
+ rerunPending = false;
109
+ break;
110
+ }
111
+ } while (rerunPending);
85
112
  } finally {
86
113
  running = false;
114
+ rerunPending = false;
87
115
  }
88
116
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
89
117
  if (hook) hook.emit("effect:create", { effectFn });
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  context
3
- } from "./chunk-GTBNNBJ6.js";
3
+ } from "./chunk-OJ3P4ECI.js";
4
4
  import {
5
5
  derived
6
- } from "./chunk-54EDRCEF.js";
6
+ } from "./chunk-DRUZZAK4.js";
7
7
  import {
8
8
  sanitizeUrl
9
9
  } from "./chunk-UCS6AMJ7.js";
10
10
  import {
11
11
  effect
12
- } from "./chunk-HB24TBAF.js";
12
+ } from "./chunk-WZA53FXU.js";
13
13
  import {
14
14
  batch,
15
15
  signal
16
- } from "./chunk-CC65Y57T.js";
16
+ } from "./chunk-RDTDJCAB.js";
17
17
 
18
18
  // src/data/retry.ts
19
19
  function calculateDelay(attempt, strategy, baseDelay, maxDelay, jitter) {