sibujs 2.0.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 +369 -276
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +411 -300
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +8 -8
  6. package/dist/{chunk-JA6667UN.js → chunk-2JQUV4Y3.js} +4 -4
  7. package/dist/{chunk-3NSGB5JN.js → chunk-2KM2724A.js} +2 -2
  8. package/dist/{chunk-52YJLLRO.js → chunk-4YTVESDX.js} +1 -1
  9. package/dist/chunk-5WD7BYTZ.js +152 -0
  10. package/dist/{chunk-CC65Y57T.js → chunk-6QZO7MMG.js} +48 -16
  11. package/dist/{chunk-54EDRCEF.js → chunk-DF3GTP4Q.js} +7 -2
  12. package/dist/{chunk-ND2664SF.js → chunk-J63GPPCJ.js} +13 -9
  13. package/dist/{chunk-O2MNQFLP.js → chunk-KH4OE6WY.js} +5 -5
  14. package/dist/{chunk-3LR7GLWQ.js → chunk-KZA7ANXP.js} +3 -3
  15. package/dist/chunk-L4DAT4WU.js +400 -0
  16. package/dist/{chunk-WOMYAHHI.js → chunk-L52H775O.js} +4 -4
  17. package/dist/{chunk-ITX6OO3F.js → chunk-NEWH4O5U.js} +1 -1
  18. package/dist/{chunk-7JDB7I65.js → chunk-RJIRT46U.js} +4 -4
  19. package/dist/{chunk-KLRMB5ZS.js → chunk-STFTTMO2.js} +2 -2
  20. package/dist/{chunk-DFPFITST.js → chunk-UKMXT5T6.js} +1 -1
  21. package/dist/{chunk-SAHNHTFC.js → chunk-V65KTDZW.js} +3 -3
  22. package/dist/{chunk-R73P76YZ.js → chunk-VSNLICTS.js} +1 -1
  23. package/dist/{chunk-MIUAXB7K.js → chunk-XDKP4T7G.js} +2 -2
  24. package/dist/{chunk-JXMMDLBY.js → chunk-XVYB3J6C.js} +27 -29
  25. package/dist/{chunk-GTBNNBJ6.js → chunk-YMOIAHWA.js} +1 -1
  26. package/dist/data.cjs +382 -274
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +398 -284
  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 +382 -274
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +421 -299
  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 +413 -300
  39. package/dist/index.d.cts +16 -11
  40. package/dist/index.d.ts +16 -11
  41. package/dist/index.js +14 -10
  42. package/dist/{introspect-cY2pg9pW.d.ts → introspect-BZWKvQUZ.d.ts} +2 -1
  43. package/dist/{introspect-BWNjNw64.d.cts → introspect-DsJlDD2T.d.cts} +2 -1
  44. package/dist/motion.cjs +189 -149
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +382 -274
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +360 -260
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +376 -257
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +383 -271
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +168 -109
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +373 -258
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +382 -274
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
  61. package/dist/chunk-HB24TBAF.js +0 -121
  62. package/dist/chunk-VLPPXTYG.js +0 -332
package/dist/index.d.cts CHANGED
@@ -762,12 +762,21 @@ declare function effect(effectFn: EffectBody | (() => void), options?: EffectOpt
762
762
  /**
763
763
  * derived creates a derived reactive signal whose value updates when dependencies change.
764
764
  *
765
- * Uses lazy pull-based evaluation with dirty flagging:
765
+ * Uses lazy pull-based evaluation with a single dirty flag:
766
766
  * - When a dependency changes, the computed is marked dirty (no re-evaluation).
767
767
  * - Dirtiness propagates downstream via propagateDirty.
768
768
  * - The getter only re-evaluates when actually read (pull-based).
769
- * - On re-evaluation, dependencies are re-tracked via track() so that
770
- * derived-of-derived chains propagate correctly.
769
+ * - On re-evaluation, dependencies are re-tracked via retrack() so that
770
+ * derived-of-derived chains propagate correctly without paying the full
771
+ * Set-delete + re-add cost of track()'s cleanup phase.
772
+ *
773
+ * NOTE: a previous revision experimented with three-color (CLEAN/CHECK/DIRTY)
774
+ * state for read-side value-change short-circuiting. It regressed every
775
+ * benchmark except Memory (Deep Chain +122%, Component Tree +20%) because
776
+ * the workloads always produce a new downstream value and CHECK had no
777
+ * work to skip — only overhead to add. Keeping the simpler boolean flag
778
+ * here; revisit CHECK propagation when we have benchmarks that exercise
779
+ * stabilisation on diamond / conditional-branch patterns.
771
780
  */
772
781
  declare function derived<T>(getter: () => T, options?: {
773
782
  name?: string;
@@ -1349,14 +1358,10 @@ interface TransitionState {
1349
1358
  */
1350
1359
  declare function transition(): TransitionState;
1351
1360
 
1352
- /**
1353
- * Execute a function without tracking any signal reads as dependencies.
1354
- * Useful for reading signals inside effects without creating subscriptions.
1355
- *
1356
- * @param fn Function to execute without dependency tracking
1357
- * @returns The return value of fn
1358
- */
1361
+ type Subscriber = () => void;
1359
1362
  declare function untracked<T>(fn: () => T): T;
1363
+ declare function retrack(effectFn: () => void, subscriber: Subscriber): void;
1364
+ declare function setMaxDrainIterations(n: number): number;
1360
1365
 
1361
1366
  /**
1362
1367
  * Bind a dynamic attribute where both name and value can change reactively.
@@ -1534,4 +1539,4 @@ interface LoadingProps {
1534
1539
  */
1535
1540
  declare function Loading(props?: LoadingProps): HTMLElement;
1536
1541
 
1537
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
1542
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
package/dist/index.d.ts CHANGED
@@ -762,12 +762,21 @@ declare function effect(effectFn: EffectBody | (() => void), options?: EffectOpt
762
762
  /**
763
763
  * derived creates a derived reactive signal whose value updates when dependencies change.
764
764
  *
765
- * Uses lazy pull-based evaluation with dirty flagging:
765
+ * Uses lazy pull-based evaluation with a single dirty flag:
766
766
  * - When a dependency changes, the computed is marked dirty (no re-evaluation).
767
767
  * - Dirtiness propagates downstream via propagateDirty.
768
768
  * - The getter only re-evaluates when actually read (pull-based).
769
- * - On re-evaluation, dependencies are re-tracked via track() so that
770
- * derived-of-derived chains propagate correctly.
769
+ * - On re-evaluation, dependencies are re-tracked via retrack() so that
770
+ * derived-of-derived chains propagate correctly without paying the full
771
+ * Set-delete + re-add cost of track()'s cleanup phase.
772
+ *
773
+ * NOTE: a previous revision experimented with three-color (CLEAN/CHECK/DIRTY)
774
+ * state for read-side value-change short-circuiting. It regressed every
775
+ * benchmark except Memory (Deep Chain +122%, Component Tree +20%) because
776
+ * the workloads always produce a new downstream value and CHECK had no
777
+ * work to skip — only overhead to add. Keeping the simpler boolean flag
778
+ * here; revisit CHECK propagation when we have benchmarks that exercise
779
+ * stabilisation on diamond / conditional-branch patterns.
771
780
  */
772
781
  declare function derived<T>(getter: () => T, options?: {
773
782
  name?: string;
@@ -1349,14 +1358,10 @@ interface TransitionState {
1349
1358
  */
1350
1359
  declare function transition(): TransitionState;
1351
1360
 
1352
- /**
1353
- * Execute a function without tracking any signal reads as dependencies.
1354
- * Useful for reading signals inside effects without creating subscriptions.
1355
- *
1356
- * @param fn Function to execute without dependency tracking
1357
- * @returns The return value of fn
1358
- */
1361
+ type Subscriber = () => void;
1359
1362
  declare function untracked<T>(fn: () => T): T;
1363
+ declare function retrack(effectFn: () => void, subscriber: Subscriber): void;
1364
+ declare function setMaxDrainIterations(n: number): number;
1360
1365
 
1361
1366
  /**
1362
1367
  * Bind a dynamic attribute where both name and value can change reactively.
@@ -1534,4 +1539,4 @@ interface LoadingProps {
1534
1539
  */
1535
1540
  declare function Loading(props?: LoadingProps): HTMLElement;
1536
1541
 
1537
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
1542
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  unregisterComponent,
45
45
  when,
46
46
  writable
47
- } from "./chunk-ND2664SF.js";
47
+ } from "./chunk-J63GPPCJ.js";
48
48
  import {
49
49
  __resetIdCounter,
50
50
  createId
@@ -186,26 +186,26 @@ import {
186
186
  use,
187
187
  var_,
188
188
  video
189
- } from "./chunk-R73P76YZ.js";
189
+ } from "./chunk-VSNLICTS.js";
190
190
  import {
191
191
  watch
192
- } from "./chunk-ITX6OO3F.js";
192
+ } from "./chunk-NEWH4O5U.js";
193
193
  import {
194
194
  trustHTML
195
195
  } from "./chunk-JYD2PWXH.js";
196
196
  import {
197
197
  context
198
- } from "./chunk-GTBNNBJ6.js";
198
+ } from "./chunk-YMOIAHWA.js";
199
199
  import {
200
200
  SVG_NS,
201
201
  tagFactory
202
- } from "./chunk-KLRMB5ZS.js";
202
+ } from "./chunk-STFTTMO2.js";
203
203
  import {
204
204
  bindDynamic
205
- } from "./chunk-DFPFITST.js";
205
+ } from "./chunk-UKMXT5T6.js";
206
206
  import {
207
207
  derived
208
- } from "./chunk-54EDRCEF.js";
208
+ } from "./chunk-DF3GTP4Q.js";
209
209
  import {
210
210
  checkLeaks,
211
211
  dispose,
@@ -215,7 +215,7 @@ import "./chunk-UCS6AMJ7.js";
215
215
  import {
216
216
  effect,
217
217
  on
218
- } from "./chunk-HB24TBAF.js";
218
+ } from "./chunk-5WD7BYTZ.js";
219
219
  import {
220
220
  disableSSR,
221
221
  enableSSR,
@@ -229,10 +229,12 @@ import {
229
229
  enqueueBatchedSignal,
230
230
  isBatching,
231
231
  signal
232
- } from "./chunk-CC65Y57T.js";
232
+ } from "./chunk-6QZO7MMG.js";
233
233
  import {
234
+ retrack,
235
+ setMaxDrainIterations,
234
236
  untracked
235
- } from "./chunk-VLPPXTYG.js";
237
+ } from "./chunk-L4DAT4WU.js";
236
238
  import "./chunk-LMLD24FC.js";
237
239
  export {
238
240
  DynamicComponent,
@@ -384,6 +386,7 @@ export {
384
386
  registerComponent,
385
387
  registerDisposer,
386
388
  resolveComponent,
389
+ retrack,
387
390
  rp,
388
391
  rt,
389
392
  ruby,
@@ -394,6 +397,7 @@ export {
394
397
  section,
395
398
  select,
396
399
  setGlobalErrorHandler,
400
+ setMaxDrainIterations,
397
401
  show,
398
402
  signal,
399
403
  slot,
@@ -474,7 +474,8 @@ declare function getSubscriberCount(getter: () => unknown): number;
474
474
  * Get the dependency list of an effect or computed subscriber function.
475
475
  * Returns signal references that the subscriber depends on.
476
476
  *
477
- * Note: This reads the _deps Set that track.ts maintains on subscriber functions.
477
+ * Note: This reads the linked-list dep storage maintained by track.ts. Safe
478
+ * to call on any subscriber (effect or computed markDirty).
478
479
  */
479
480
  declare function getDependencies(subscriberFn: () => void): ReactiveSignal[];
480
481
  /**
@@ -474,7 +474,8 @@ declare function getSubscriberCount(getter: () => unknown): number;
474
474
  * Get the dependency list of an effect or computed subscriber function.
475
475
  * Returns signal references that the subscriber depends on.
476
476
  *
477
- * Note: This reads the _deps Set that track.ts maintains on subscriber functions.
477
+ * Note: This reads the linked-list dep storage maintained by track.ts. Safe
478
+ * to call on any subscriber (effect or computed markDirty).
478
479
  */
479
480
  declare function getDependencies(subscriberFn: () => void): ReactiveSignal[];
480
481
  /**
package/dist/motion.cjs CHANGED
@@ -289,9 +289,51 @@ function devWarn(message) {
289
289
 
290
290
  // src/reactivity/track.ts
291
291
  var _isDev2 = isDev();
292
- var subscriberStack = new Array(32);
292
+ var nodePool = [];
293
+ function createNode() {
294
+ return {
295
+ sig: null,
296
+ sub: null,
297
+ epoch: 0,
298
+ sigPrev: null,
299
+ sigNext: null,
300
+ subPrev: null,
301
+ subNext: null,
302
+ prevActive: null
303
+ };
304
+ }
305
+ function allocNode(sig, sub, epoch) {
306
+ const n = nodePool.pop();
307
+ if (n) {
308
+ n.sig = sig;
309
+ n.sub = sub;
310
+ n.epoch = epoch;
311
+ return n;
312
+ }
313
+ const fresh = createNode();
314
+ fresh.sig = sig;
315
+ fresh.sub = sub;
316
+ fresh.epoch = epoch;
317
+ return fresh;
318
+ }
319
+ function linkSignal(sig, node) {
320
+ const oldHead = sig.subsHead ?? null;
321
+ node.sigPrev = null;
322
+ node.sigNext = oldHead;
323
+ if (oldHead) oldHead.sigPrev = node;
324
+ else sig.subsTail = node;
325
+ sig.subsHead = node;
326
+ sig.__sc = (sig.__sc ?? 0) + 1;
327
+ }
328
+ function linkSub(sub, node) {
329
+ const oldTail = sub.depsTail ?? null;
330
+ node.subPrev = oldTail;
331
+ node.subNext = null;
332
+ if (oldTail) oldTail.subNext = node;
333
+ else sub.depsHead = node;
334
+ sub.depsTail = node;
335
+ }
293
336
  var currentSubscriber = null;
294
- var SUBS = "__s";
295
337
  var notifyDepth = 0;
296
338
  var pendingQueue = [];
297
339
  var pendingSet = /* @__PURE__ */ new Set();
@@ -306,33 +348,64 @@ function safeInvoke(sub) {
306
348
  function recordDependency(signal2) {
307
349
  if (!currentSubscriber) return;
308
350
  const sub = currentSubscriber;
309
- if (sub._dep === signal2) return;
310
- const deps = sub._deps;
311
- if (deps) {
312
- if (deps.has(signal2)) return;
313
- deps.add(signal2);
314
- } else if (sub._dep !== void 0) {
315
- const set = /* @__PURE__ */ new Set();
316
- set.add(sub._dep);
317
- set.add(signal2);
318
- sub._deps = set;
319
- sub._dep = void 0;
320
- } else {
321
- sub._dep = signal2;
351
+ const sig = signal2;
352
+ const epoch = sub._epoch ?? 0;
353
+ const active = sig.__activeNode ?? null;
354
+ if (active !== null && active.sub === sub) {
355
+ active.epoch = epoch;
356
+ return;
357
+ }
358
+ const node = allocNode(signal2, sub, epoch);
359
+ node.prevActive = active;
360
+ sig.__activeNode = node;
361
+ linkSub(sub, node);
362
+ linkSignal(sig, node);
363
+ sub._structDirty = true;
364
+ }
365
+ var maxSubscriberRepeats = 50;
366
+ var maxDrainIterations = 1e6;
367
+ var drainEpoch = 0;
368
+ function tickRepeat(sub) {
369
+ const s = sub;
370
+ if (s._runEpoch !== drainEpoch) {
371
+ s._runEpoch = drainEpoch;
372
+ s._runs = 1;
373
+ return false;
322
374
  }
323
- let subs = signal2[SUBS];
324
- if (!subs) {
325
- subs = /* @__PURE__ */ new Set();
326
- signal2[SUBS] = subs;
375
+ s._runs = (s._runs ?? 0) + 1;
376
+ return s._runs > maxSubscriberRepeats;
377
+ }
378
+ function cycleError(sub) {
379
+ if (typeof console !== "undefined") {
380
+ const name = sub.__name ?? "<unnamed>";
381
+ console.error(
382
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
383
+ );
327
384
  }
328
- subs.add(currentSubscriber);
329
- if (subs.size === 1) {
330
- signal2.__f = currentSubscriber;
331
- } else if (signal2.__f !== void 0) {
332
- signal2.__f = void 0;
385
+ }
386
+ function absoluteDrainError() {
387
+ if (typeof console !== "undefined") {
388
+ console.error(
389
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
390
+ );
391
+ }
392
+ }
393
+ function drainQueue() {
394
+ let i = 0;
395
+ while (i < pendingQueue.length) {
396
+ if (i >= maxDrainIterations) {
397
+ absoluteDrainError();
398
+ break;
399
+ }
400
+ const sub = pendingQueue[i++];
401
+ if (tickRepeat(sub)) {
402
+ cycleError(sub);
403
+ break;
404
+ }
405
+ pendingSet.delete(sub);
406
+ safeInvoke(sub);
333
407
  }
334
408
  }
335
- var maxDrainIterations = 1e5;
336
409
  function propagateDirty(sub) {
337
410
  sub();
338
411
  const rootSig = sub._sig;
@@ -342,131 +415,66 @@ function propagateDirty(sub) {
342
415
  stack.push(rootSig);
343
416
  while (stack.length > baseLen) {
344
417
  const sig = stack.pop();
345
- const first = sig.__f;
346
- if (first) {
347
- if (first._c) {
348
- const nSig = first._sig;
349
- if (!nSig._d) {
350
- nSig._d = true;
351
- stack.push(nSig);
352
- }
353
- } else if (!pendingSet.has(first)) {
354
- pendingSet.add(first);
355
- pendingQueue.push(first);
356
- }
357
- continue;
358
- }
359
- const subs = sig[SUBS];
360
- if (!subs) continue;
361
- for (const s of subs) {
362
- if (s._c) {
363
- const nSig = s._sig;
364
- if (nSig && !nSig._d) {
365
- nSig._d = true;
366
- stack.push(nSig);
367
- } else if (!nSig) {
368
- s();
418
+ let node = sig.subsHead ?? null;
419
+ while (node) {
420
+ const s = node.sub;
421
+ if (s) {
422
+ if (s._c) {
423
+ const nSig = s._sig;
424
+ if (nSig) {
425
+ if (!nSig._d) {
426
+ nSig._d = true;
427
+ stack.push(nSig);
428
+ }
429
+ } else {
430
+ s();
431
+ }
432
+ } else if (!pendingSet.has(s)) {
433
+ pendingSet.add(s);
434
+ pendingQueue.push(s);
369
435
  }
370
- } else if (!pendingSet.has(s)) {
371
- pendingSet.add(s);
372
- pendingQueue.push(s);
373
436
  }
437
+ node = node.sigNext;
374
438
  }
375
439
  }
376
440
  }
377
441
  function notifySubscribers(signal2) {
378
- const first = signal2.__f;
379
- if (first) {
380
- if (notifyDepth > 0) {
381
- if (first._c) {
382
- propagateDirty(first);
383
- } else if (!pendingSet.has(first)) {
384
- pendingSet.add(first);
385
- pendingQueue.push(first);
386
- }
387
- return;
388
- }
389
- notifyDepth++;
390
- try {
391
- if (first._c) {
392
- propagateDirty(first);
393
- } else {
394
- safeInvoke(first);
395
- }
396
- let i = 0;
397
- while (i < pendingQueue.length) {
398
- if (i >= maxDrainIterations) {
399
- if (typeof console !== "undefined") {
400
- console.error(
401
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
402
- );
403
- }
404
- break;
405
- }
406
- safeInvoke(pendingQueue[i]);
407
- i++;
408
- }
409
- } finally {
410
- notifyDepth--;
411
- if (notifyDepth === 0) {
412
- pendingQueue.length = 0;
413
- pendingSet.clear();
414
- }
415
- }
416
- return;
417
- }
418
- const subs = signal2[SUBS];
419
- if (!subs || subs.size === 0) return;
442
+ const sig = signal2;
443
+ const head = sig.subsHead;
444
+ if (!head) return;
420
445
  if (notifyDepth > 0) {
421
- for (const sub of subs) {
422
- if (sub._c) {
423
- propagateDirty(sub);
424
- } else if (!pendingSet.has(sub)) {
425
- pendingSet.add(sub);
426
- pendingQueue.push(sub);
446
+ let node = head;
447
+ while (node) {
448
+ const s = node.sub;
449
+ if (s) {
450
+ if (s._c) {
451
+ propagateDirty(s);
452
+ } else if (!pendingSet.has(s)) {
453
+ pendingSet.add(s);
454
+ pendingQueue.push(s);
455
+ }
427
456
  }
457
+ node = node.sigNext;
428
458
  }
429
459
  return;
430
460
  }
431
461
  notifyDepth++;
462
+ drainEpoch++;
432
463
  try {
433
- let directCount = 0;
434
- let hasComputedSub = false;
435
- for (const sub of subs) {
436
- if (sub._c) hasComputedSub = true;
437
- pendingQueue[directCount++] = sub;
438
- }
439
- if (!hasComputedSub) {
440
- for (let i2 = 0; i2 < directCount; i2++) {
441
- safeInvoke(pendingQueue[i2]);
442
- }
443
- } else {
444
- for (let i2 = 0; i2 < directCount; i2++) {
445
- if (pendingQueue[i2]._c) {
446
- propagateDirty(pendingQueue[i2]);
447
- }
448
- }
449
- for (let i2 = 0; i2 < directCount; i2++) {
450
- const sub = pendingQueue[i2];
451
- if (!sub._c && !pendingSet.has(sub)) {
452
- pendingSet.add(sub);
453
- safeInvoke(sub);
454
- }
455
- }
456
- }
457
- let i = directCount;
458
- while (i < pendingQueue.length) {
459
- if (i - directCount >= maxDrainIterations) {
460
- if (typeof console !== "undefined") {
461
- console.error(
462
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
463
- );
464
+ let node = head;
465
+ while (node) {
466
+ const s = node.sub;
467
+ if (s) {
468
+ if (s._c) {
469
+ propagateDirty(s);
470
+ } else if (!pendingSet.has(s)) {
471
+ pendingSet.add(s);
472
+ pendingQueue.push(s);
464
473
  }
465
- break;
466
474
  }
467
- safeInvoke(pendingQueue[i]);
468
- i++;
475
+ node = node.sigNext;
469
476
  }
477
+ drainQueue();
470
478
  } finally {
471
479
  notifyDepth--;
472
480
  if (notifyDepth === 0) {
@@ -489,32 +497,64 @@ function enqueueBatchedSignal(signal2) {
489
497
  var _g = globalThis;
490
498
  var _isDev3 = isDev();
491
499
  function signal(initial, options) {
492
- 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
+ };
493
509
  const debugName = _isDev3 ? options?.name : void 0;
494
510
  const equalsFn = options?.equals;
495
- if (debugName) {
496
- state.__name = debugName;
497
- }
511
+ if (debugName) state.__name = debugName;
498
512
  function get() {
499
513
  recordDependency(state);
500
514
  return state.value;
501
515
  }
502
516
  get.__signal = state;
503
517
  if (debugName) get.__name = debugName;
504
- function set(next) {
505
- const newValue = typeof next === "function" ? next(state.value) : next;
506
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
507
- if (_isDev3) {
508
- 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;
509
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++;
510
541
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
511
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
512
- } 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;
513
552
  state.value = newValue;
514
- }
515
- if (!enqueueBatchedSignal(state)) {
516
- notifySubscribers(state);
517
- }
553
+ state.__v++;
554
+ if (!enqueueBatchedSignal(state)) {
555
+ notifySubscribers(state);
556
+ }
557
+ };
518
558
  }
519
559
  if (_isDev3) {
520
560
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
package/dist/motion.js CHANGED
@@ -19,9 +19,9 @@ import {
19
19
  stagger,
20
20
  transition,
21
21
  viewTransition
22
- } from "./chunk-52YJLLRO.js";
23
- import "./chunk-CC65Y57T.js";
24
- import "./chunk-VLPPXTYG.js";
22
+ } from "./chunk-4YTVESDX.js";
23
+ import "./chunk-6QZO7MMG.js";
24
+ import "./chunk-L4DAT4WU.js";
25
25
  import "./chunk-LMLD24FC.js";
26
26
  export {
27
27
  TransitionGroup,