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/widgets.cjs CHANGED
@@ -50,11 +50,24 @@ function devWarn(message) {
50
50
 
51
51
  // src/reactivity/track.ts
52
52
  var _isDev2 = isDev();
53
- var subscriberStack = new Array(32);
54
- var stackCapacity = 32;
53
+ var STACK_INITIAL = 32;
54
+ var STACK_SHRINK_THRESHOLD = 128;
55
+ var subscriberStack = new Array(STACK_INITIAL);
56
+ var stackCapacity = STACK_INITIAL;
55
57
  var stackTop = -1;
56
58
  var currentSubscriber = null;
57
59
  var SUBS = "__s";
60
+ function syncFastPath(signal2, subs) {
61
+ const size = subs.size;
62
+ if (size === 0) {
63
+ signal2.__f = void 0;
64
+ delete signal2[SUBS];
65
+ } else if (size === 1) {
66
+ signal2.__f = subs.values().next().value;
67
+ } else {
68
+ signal2.__f = void 0;
69
+ }
70
+ }
58
71
  var notifyDepth = 0;
59
72
  var pendingQueue = [];
60
73
  var pendingSet = /* @__PURE__ */ new Set();
@@ -67,13 +80,45 @@ function safeInvoke(sub) {
67
80
  }
68
81
  }
69
82
  var trackingSuspended = false;
83
+ var subscriberEpochCounter = 0;
70
84
  function retrack(effectFn, subscriber) {
71
85
  const prev = currentSubscriber;
72
86
  currentSubscriber = subscriber;
87
+ const sub = subscriber;
88
+ const epoch = ++subscriberEpochCounter;
89
+ sub._epoch = epoch;
73
90
  try {
74
91
  effectFn();
75
92
  } finally {
76
93
  currentSubscriber = prev;
94
+ pruneStaleDeps(sub, epoch);
95
+ }
96
+ }
97
+ function pruneStaleDeps(sub, currentEpoch) {
98
+ if (sub._dep !== void 0) {
99
+ if (sub._depEpoch !== currentEpoch) {
100
+ const sig = sub._dep;
101
+ const subs = sig[SUBS];
102
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
103
+ sub._dep = void 0;
104
+ sub._depEpoch = void 0;
105
+ }
106
+ return;
107
+ }
108
+ const deps = sub._deps;
109
+ if (!deps || deps.size === 0) return;
110
+ let stales;
111
+ for (const [signal2, epoch] of deps) {
112
+ if (epoch !== currentEpoch) {
113
+ (stales ?? (stales = [])).push(signal2);
114
+ }
115
+ }
116
+ if (!stales) return;
117
+ for (const signal2 of stales) {
118
+ deps.delete(signal2);
119
+ const sig = signal2;
120
+ const subs = sig[SUBS];
121
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
77
122
  }
78
123
  }
79
124
  function track(effectFn, subscriber) {
@@ -91,36 +136,49 @@ function track(effectFn, subscriber) {
91
136
  } finally {
92
137
  stackTop--;
93
138
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
139
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
140
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
141
+ subscriberStack.length = stackCapacity;
142
+ }
94
143
  }
95
144
  return () => cleanup(subscriber);
96
145
  }
97
146
  function recordDependency(signal2) {
98
147
  if (!currentSubscriber) return;
99
148
  const sub = currentSubscriber;
100
- if (sub._dep === signal2) return;
149
+ const epoch = sub._epoch;
150
+ if (sub._dep === signal2) {
151
+ sub._depEpoch = epoch;
152
+ return;
153
+ }
101
154
  const deps = sub._deps;
102
155
  if (deps) {
103
- if (deps.has(signal2)) return;
104
- deps.add(signal2);
156
+ deps.set(signal2, epoch);
105
157
  } else if (sub._dep !== void 0) {
106
- const set = /* @__PURE__ */ new Set();
107
- set.add(sub._dep);
108
- set.add(signal2);
109
- sub._deps = set;
158
+ const map = /* @__PURE__ */ new Map();
159
+ map.set(sub._dep, sub._depEpoch);
160
+ map.set(signal2, epoch);
161
+ sub._deps = map;
110
162
  sub._dep = void 0;
163
+ sub._depEpoch = void 0;
111
164
  } else {
112
165
  sub._dep = signal2;
166
+ sub._depEpoch = epoch;
113
167
  }
114
- let subs = signal2[SUBS];
168
+ const sig = signal2;
169
+ let subs = sig[SUBS];
115
170
  if (!subs) {
116
171
  subs = /* @__PURE__ */ new Set();
117
- signal2[SUBS] = subs;
172
+ sig[SUBS] = subs;
118
173
  }
174
+ const prevSize = subs.size;
119
175
  subs.add(currentSubscriber);
120
- if (subs.size === 1) {
121
- signal2.__f = currentSubscriber;
122
- } else if (signal2.__f !== void 0) {
123
- signal2.__f = void 0;
176
+ if (subs.size !== prevSize) {
177
+ if (subs.size === 1) {
178
+ sig.__f = currentSubscriber;
179
+ } else if (sig.__f !== void 0) {
180
+ sig.__f = void 0;
181
+ }
124
182
  }
125
183
  }
126
184
  function queueSignalNotification(signal2) {
@@ -135,24 +193,55 @@ function queueSignalNotification(signal2) {
135
193
  }
136
194
  }
137
195
  }
138
- var maxDrainIterations = 1e5;
196
+ var maxSubscriberRepeats = 50;
197
+ var maxDrainIterations = 1e6;
198
+ var drainEpoch = 0;
199
+ function tickRepeat(sub) {
200
+ const s = sub;
201
+ if (s._runEpoch !== drainEpoch) {
202
+ s._runEpoch = drainEpoch;
203
+ s._runs = 1;
204
+ return false;
205
+ }
206
+ return ++s._runs > maxSubscriberRepeats;
207
+ }
208
+ function cycleError(sub) {
209
+ if (typeof console !== "undefined") {
210
+ const name = sub.__name ?? "<unnamed>";
211
+ console.error(
212
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
213
+ );
214
+ }
215
+ }
216
+ function absoluteDrainError() {
217
+ if (typeof console !== "undefined") {
218
+ console.error(
219
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
220
+ );
221
+ }
222
+ }
223
+ function drainQueue() {
224
+ let i = 0;
225
+ while (i < pendingQueue.length) {
226
+ if (i >= maxDrainIterations) {
227
+ absoluteDrainError();
228
+ break;
229
+ }
230
+ const sub = pendingQueue[i++];
231
+ if (tickRepeat(sub)) {
232
+ cycleError(sub);
233
+ break;
234
+ }
235
+ pendingSet.delete(sub);
236
+ safeInvoke(sub);
237
+ }
238
+ }
139
239
  function drainNotificationQueue() {
140
240
  if (notifyDepth > 0) return;
141
241
  notifyDepth++;
242
+ drainEpoch++;
142
243
  try {
143
- let i = 0;
144
- while (i < pendingQueue.length) {
145
- if (i >= maxDrainIterations) {
146
- if (typeof console !== "undefined") {
147
- console.error(
148
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
149
- );
150
- }
151
- break;
152
- }
153
- safeInvoke(pendingQueue[i]);
154
- i++;
155
- }
244
+ drainQueue();
156
245
  } finally {
157
246
  notifyDepth--;
158
247
  if (notifyDepth === 0) {
@@ -215,25 +304,16 @@ function notifySubscribers(signal2) {
215
304
  return;
216
305
  }
217
306
  notifyDepth++;
307
+ drainEpoch++;
218
308
  try {
219
309
  if (first._c) {
220
310
  propagateDirty(first);
311
+ } else if (tickRepeat(first)) {
312
+ cycleError(first);
221
313
  } else {
222
314
  safeInvoke(first);
223
315
  }
224
- let i = 0;
225
- while (i < pendingQueue.length) {
226
- if (i >= maxDrainIterations) {
227
- if (typeof console !== "undefined") {
228
- console.error(
229
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
230
- );
231
- }
232
- break;
233
- }
234
- safeInvoke(pendingQueue[i]);
235
- i++;
236
- }
316
+ drainQueue();
237
317
  } finally {
238
318
  notifyDepth--;
239
319
  if (notifyDepth === 0) {
@@ -257,44 +337,17 @@ function notifySubscribers(signal2) {
257
337
  return;
258
338
  }
259
339
  notifyDepth++;
340
+ drainEpoch++;
260
341
  try {
261
- let directCount = 0;
262
- let hasComputedSub = false;
263
342
  for (const sub of subs) {
264
- if (sub._c) hasComputedSub = true;
265
- pendingQueue[directCount++] = sub;
266
- }
267
- if (!hasComputedSub) {
268
- for (let i2 = 0; i2 < directCount; i2++) {
269
- safeInvoke(pendingQueue[i2]);
270
- }
271
- } else {
272
- for (let i2 = 0; i2 < directCount; i2++) {
273
- if (pendingQueue[i2]._c) {
274
- propagateDirty(pendingQueue[i2]);
275
- }
276
- }
277
- for (let i2 = 0; i2 < directCount; i2++) {
278
- const sub = pendingQueue[i2];
279
- if (!sub._c && !pendingSet.has(sub)) {
280
- pendingSet.add(sub);
281
- safeInvoke(sub);
282
- }
283
- }
284
- }
285
- let i = directCount;
286
- while (i < pendingQueue.length) {
287
- if (i - directCount >= maxDrainIterations) {
288
- if (typeof console !== "undefined") {
289
- console.error(
290
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
291
- );
292
- }
293
- break;
343
+ if (sub._c) {
344
+ propagateDirty(sub);
345
+ } else if (!pendingSet.has(sub)) {
346
+ pendingSet.add(sub);
347
+ pendingQueue.push(sub);
294
348
  }
295
- safeInvoke(pendingQueue[i]);
296
- i++;
297
349
  }
350
+ drainQueue();
298
351
  } finally {
299
352
  notifyDepth--;
300
353
  if (notifyDepth === 0) {
@@ -307,29 +360,22 @@ function cleanup(subscriber) {
307
360
  const sub = subscriber;
308
361
  const singleDep = sub._dep;
309
362
  if (singleDep !== void 0) {
310
- const subs = singleDep[SUBS];
311
- if (subs) {
312
- subs.delete(subscriber);
313
- if (singleDep.__f === subscriber) {
314
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
315
- } else if (subs.size === 1 && singleDep.__f === void 0) {
316
- singleDep.__f = subs.values().next().value;
317
- }
363
+ const sig = singleDep;
364
+ const subs = sig[SUBS];
365
+ if (subs?.delete(subscriber)) {
366
+ syncFastPath(sig, subs);
318
367
  }
319
368
  sub._dep = void 0;
369
+ sub._depEpoch = void 0;
320
370
  return;
321
371
  }
322
372
  const deps = sub._deps;
323
373
  if (!deps || deps.size === 0) return;
324
- for (const signal2 of deps) {
325
- const subs = signal2[SUBS];
326
- if (subs) {
327
- subs.delete(subscriber);
328
- if (signal2.__f === subscriber) {
329
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
330
- } else if (subs.size === 1 && signal2.__f === void 0) {
331
- signal2.__f = subs.values().next().value;
332
- }
374
+ for (const signal2 of deps.keys()) {
375
+ const sig = signal2;
376
+ const subs = sig[SUBS];
377
+ if (subs?.delete(subscriber)) {
378
+ syncFastPath(sig, subs);
333
379
  }
334
380
  }
335
381
  deps.clear();
@@ -476,29 +522,57 @@ function effect(effectFn, options) {
476
522
  let cleanupHandle = () => {
477
523
  };
478
524
  let running = false;
525
+ let rerunPending = false;
526
+ const MAX_RERUNS = 100;
479
527
  const subscriber = () => {
480
528
  if (running) {
481
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
482
- console.warn(
483
- "[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."
484
- );
485
- }
529
+ rerunPending = true;
486
530
  return;
487
531
  }
488
532
  running = true;
489
533
  try {
490
- runUserCleanups();
491
- cleanupHandle();
492
- cleanupHandle = track(wrappedFn, subscriber);
534
+ let reruns = 0;
535
+ do {
536
+ rerunPending = false;
537
+ runUserCleanups();
538
+ cleanupHandle();
539
+ cleanupHandle = track(wrappedFn, subscriber);
540
+ if (++reruns > MAX_RERUNS) {
541
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
542
+ console.error(
543
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
544
+ );
545
+ }
546
+ rerunPending = false;
547
+ break;
548
+ }
549
+ } while (rerunPending);
493
550
  } finally {
494
551
  running = false;
552
+ rerunPending = false;
495
553
  }
496
554
  };
497
555
  running = true;
498
556
  try {
499
- cleanupHandle = track(wrappedFn, subscriber);
557
+ let reruns = 0;
558
+ do {
559
+ rerunPending = false;
560
+ runUserCleanups();
561
+ cleanupHandle();
562
+ cleanupHandle = track(wrappedFn, subscriber);
563
+ if (++reruns > MAX_RERUNS) {
564
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
565
+ console.error(
566
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
567
+ );
568
+ }
569
+ rerunPending = false;
570
+ break;
571
+ }
572
+ } while (rerunPending);
500
573
  } finally {
501
574
  running = false;
575
+ rerunPending = false;
502
576
  }
503
577
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
504
578
  if (hook) hook.emit("effect:create", { effectFn });
package/dist/widgets.js CHANGED
@@ -8,14 +8,14 @@ import {
8
8
  select,
9
9
  tabs,
10
10
  tooltip
11
- } from "./chunk-7JDB7I65.js";
12
- import "./chunk-ITX6OO3F.js";
13
- import "./chunk-54EDRCEF.js";
11
+ } from "./chunk-ON5MMR2J.js";
12
+ import "./chunk-45YP72ZQ.js";
13
+ import "./chunk-DRUZZAK4.js";
14
14
  import "./chunk-UCS6AMJ7.js";
15
- import "./chunk-HB24TBAF.js";
15
+ import "./chunk-WZA53FXU.js";
16
16
  import "./chunk-2RA7SHDA.js";
17
- import "./chunk-CC65Y57T.js";
18
- import "./chunk-VLPPXTYG.js";
17
+ import "./chunk-RDTDJCAB.js";
18
+ import "./chunk-QO3WC6FS.js";
19
19
  import "./chunk-LMLD24FC.js";
20
20
  export {
21
21
  accordion,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sibujs",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A lightweight, function-based frontend framework that combines the best of React, Svelte, and Vue — with zero VDOM and maximum simplicity. Designed for developers who want fine-grained reactivity and full control without compilation or magic.",
5
5
  "keywords": [
6
6
  "frontend",