sibujs 4.14.0 → 4.15.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 (93) hide show
  1. package/dist/browser.cjs +297 -45
  2. package/dist/browser.js +6 -7
  3. package/dist/build.cjs +2189 -573
  4. package/dist/build.js +16 -15
  5. package/dist/cdn.dev.global.js +13 -12
  6. package/dist/cdn.full.dev.global.js +14 -13
  7. package/dist/cdn.full.global.js +12 -12
  8. package/dist/cdn.global.js +12 -12
  9. package/dist/{chunk-RVR5NPFL.js → chunk-2VKTFA7C.js} +1 -1
  10. package/dist/{chunk-7ILEHTVX.js → chunk-4AEFGRYW.js} +1 -1
  11. package/dist/{chunk-4TQDTWWN.js → chunk-6IV5ZRG6.js} +5 -7
  12. package/dist/{chunk-GVAMIU6K.js → chunk-75EAQH3U.js} +2 -2
  13. package/dist/{chunk-TLS4NVIB.js → chunk-7JO7DPSS.js} +4 -4
  14. package/dist/{chunk-SVULV7O4.js → chunk-AQH7WDSE.js} +6 -8
  15. package/dist/{chunk-S37PZD6I.js → chunk-BF3WBJJQ.js} +60 -10
  16. package/dist/{chunk-LJMX4SQ7.js → chunk-BG4BP44S.js} +3 -3
  17. package/dist/{chunk-N7A6FKWW.js → chunk-D44TAGCT.js} +2 -2
  18. package/dist/{chunk-CCKUNHVU.js → chunk-DAEXQJQA.js} +1 -1
  19. package/dist/chunk-DNCQDFJJ.js +22 -0
  20. package/dist/{chunk-BKZ5N6NK.js → chunk-DNCT2UXB.js} +4 -4
  21. package/dist/{chunk-BOYKGGVD.js → chunk-DRWOD7RZ.js} +4 -4
  22. package/dist/chunk-DS5V2PRE.js +64 -0
  23. package/dist/{chunk-VM2U5LEL.js → chunk-EHSJESRN.js} +595 -123
  24. package/dist/{chunk-MIRKTVH4.js → chunk-GFM5WWPU.js} +252 -5
  25. package/dist/{chunk-52XFPGSN.js → chunk-HTPOIAAL.js} +1 -1
  26. package/dist/{chunk-3HEX6QJQ.js → chunk-I2R5QALY.js} +2 -4
  27. package/dist/{chunk-4FJSAUKB.js → chunk-IUBV5WO5.js} +1 -1
  28. package/dist/{chunk-A454RAEK.js → chunk-KIDETDMU.js} +5 -7
  29. package/dist/{chunk-JTFQ7IL4.js → chunk-KPAXG5IR.js} +7 -9
  30. package/dist/{chunk-AE32RGWX.js → chunk-LGUXYHTZ.js} +3 -3
  31. package/dist/{chunk-Y4LUM2XS.js → chunk-LQWLPQOK.js} +6 -8
  32. package/dist/{chunk-CNPT6NKV.js → chunk-MINLMJUY.js} +48 -9
  33. package/dist/{chunk-BNXRWZZV.js → chunk-OURTGOJT.js} +8 -10
  34. package/dist/{chunk-F67F77IA.js → chunk-TQ24Y3IV.js} +2 -2
  35. package/dist/{chunk-2TTRW2TE.js → chunk-UM3M5PXS.js} +8 -8
  36. package/dist/{chunk-CJ7KS3DF.js → chunk-VSTX3XSX.js} +205 -15
  37. package/dist/{chunk-HQIAL7F2.js → chunk-XPDZ2LDR.js} +2 -2
  38. package/dist/{chunk-UUIKJJIK.js → chunk-Y7JM7GY2.js} +5 -7
  39. package/dist/{chunk-QS6GMWDD.js → chunk-YAFITBOK.js} +1269 -162
  40. package/dist/data.cjs +285 -33
  41. package/dist/data.js +6 -7
  42. package/dist/devtools.cjs +289 -39
  43. package/dist/devtools.js +5 -6
  44. package/dist/{dispose-EIHHHDC7.js → dispose-3WDBYJMH.js} +3 -1
  45. package/dist/ecosystem.cjs +345 -61
  46. package/dist/ecosystem.d.cts +1 -1
  47. package/dist/ecosystem.d.ts +1 -1
  48. package/dist/ecosystem.js +10 -10
  49. package/dist/extras.cjs +568 -248
  50. package/dist/extras.d.cts +1 -1
  51. package/dist/extras.d.ts +1 -1
  52. package/dist/extras.js +27 -27
  53. package/dist/index.cjs +2134 -515
  54. package/dist/index.d.cts +628 -211
  55. package/dist/index.d.ts +628 -211
  56. package/dist/index.js +22 -15
  57. package/dist/motion.cjs +286 -30
  58. package/dist/motion.js +4 -4
  59. package/dist/patterns.cjs +301 -43
  60. package/dist/patterns.js +6 -6
  61. package/dist/performance.cjs +286 -36
  62. package/dist/performance.js +9 -9
  63. package/dist/plugins.cjs +1237 -291
  64. package/dist/plugins.d.cts +3 -1
  65. package/dist/plugins.d.ts +3 -1
  66. package/dist/plugins.js +20 -19
  67. package/dist/{renderRoute-fxgsm2Ir.d.ts → renderRoute-B84MONaO.d.cts} +2 -2
  68. package/dist/{renderRoute-DP20XwMS.d.cts → renderRoute-_yxPuEQY.d.ts} +2 -2
  69. package/dist/{routeHead-CE9SV-cH.d.cts → routeHead-DLxlq15W.d.cts} +94 -0
  70. package/dist/{routeHead-DzVxPviY.d.ts → routeHead-w8nANZ3M.d.ts} +94 -0
  71. package/dist/{ssr-NECEU4X4.js → ssr-EF3IUHTE.js} +6 -6
  72. package/dist/ssr-dom.cjs +64 -9
  73. package/dist/ssr-dom.d.cts +2 -0
  74. package/dist/ssr-dom.d.ts +2 -0
  75. package/dist/ssr-dom.js +2 -1
  76. package/dist/ssr.cjs +850 -170
  77. package/dist/ssr.d.cts +5 -3
  78. package/dist/ssr.d.ts +5 -3
  79. package/dist/ssr.js +16 -15
  80. package/dist/{tagFactory-DkytnJws.d.cts → tagFactory-BfEh69VF.d.cts} +7 -0
  81. package/dist/{tagFactory-Bnd_Nph9.d.ts → tagFactory-pSYiLYyh.d.ts} +7 -0
  82. package/dist/testing.cjs +817 -132
  83. package/dist/testing.d.cts +4 -2
  84. package/dist/testing.d.ts +4 -2
  85. package/dist/testing.js +18 -17
  86. package/dist/track-core-0edROAF5.d.ts +187 -0
  87. package/dist/track-core-DN5fYLWi.d.cts +187 -0
  88. package/dist/ui.cjs +293 -43
  89. package/dist/ui.js +10 -12
  90. package/dist/widgets.cjs +285 -27
  91. package/dist/widgets.js +7 -7
  92. package/package.json +1 -1
  93. package/dist/chunk-7ZHH77QA.js +0 -9
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { a as NodeChild, C as Component, N as NodeChildren } from './types-CJFViL6Q.cjs';
2
2
  export { D as Dispose } from './types-CJFViL6Q.cjs';
3
- import { T as TagProps, a as TagFunction, D as DerivedAccessor } from './tagFactory-DkytnJws.cjs';
4
- export { S as SVG_NS, b as StyleMap, c as StyleObject, d as TagEventHandlers, e as derived, t as tagFactory } from './tagFactory-DkytnJws.cjs';
5
3
  import { A as Accessor } from './signal-EotCj4hS.cjs';
6
4
  export { D as DisposableAccessor, E as ExternalSource, S as SignalOptions, e as external, s as signal } from './signal-EotCj4hS.cjs';
5
+ import { T as TagProps, a as TagFunction, D as DerivedAccessor } from './tagFactory-BfEh69VF.cjs';
6
+ export { S as SVG_NS, b as StyleMap, c as StyleObject, d as TagEventHandlers, e as derived, t as tagFactory } from './tagFactory-BfEh69VF.cjs';
7
+ import { s as suspendTracking, r as resumeTracking, i as isTrackingSuspended, u as untracked$1, a as retrack$1, t as track$1, b as reactiveBinding, c as recordDependency, d as cleanup, e as setMaxSubscriberRepeats, f as setMaxDrainIterations$1, g as drainNotificationQueue, q as queueSignalNotification, n as notifySubscribers, h as getSubscriberCount, j as getSubscriberDeps, k as forEachSubscriber, l as getOwnerScope, m as runWithOwnerScope, o as bindOwnerScope, p as structuralBinding, v as captureFrozen, w as holdSubtree, H as HeldCheck } from './track-core-DN5fYLWi.cjs';
7
8
  export { V as EachBindings, W as EachEventBindings, X as EnhanceContext, Y as EnhanceSetup, Z as IslandLoader, _ as IslandRegistration, $ as IslandStrategy, a0 as LazyIslandLoader, a1 as MountIslandsOptions, T as TrustedHTML, a2 as enhance, a3 as enhanceAll, a4 as lazyIsland, a5 as mountIslands, a6 as registerIsland, u as trustHTML, a7 as unregisterIsland } from './ssr-JpGpHnpC.cjs';
8
9
  import { R as ReactiveSignal } from './signal-BnWpq6WB.cjs';
9
10
 
@@ -476,6 +477,92 @@ declare function createId(prefix?: string): string;
476
477
  */
477
478
  declare function __resetIdCounter(): void;
478
479
 
480
+ /**
481
+ * Animate the branches of a `when()` / `match()`. See `DirectiveOptions.transition`.
482
+ *
483
+ * `enterClass` and `leaveClass` are the option names (and types) of
484
+ * `transition()` in `sibujs/motion`.
485
+ */
486
+ interface DirectiveTransition {
487
+ /** Class added to the branch's elements when it is inserted, removed when its animation / transition ends. */
488
+ enterClass?: string;
489
+ /** Class added to the outgoing branch's elements; the branch is removed when their animation / transition ends. */
490
+ leaveClass?: string;
491
+ /**
492
+ * How a branch switch is sequenced:
493
+ * - `"both"` (default): the new branch is inserted (and enters) immediately,
494
+ * while the old one leaves alongside it.
495
+ * - `"out-in"`: the new branch is built and inserted only once every
496
+ * outgoing branch has finished leaving.
497
+ * - `"in-out"`: the new branch is inserted and enters first; the old one
498
+ * starts leaving when the enter has finished.
499
+ */
500
+ mode?: "both" | "out-in" | "in-out";
501
+ /**
502
+ * Custom JS leave (Web Animations, a library). Receives the branch's
503
+ * top-level nodes; the branch is removed when the returned promise settles,
504
+ * or right away when it returns nothing. Takes precedence over `leaveClass`.
505
+ */
506
+ leave?: (nodes: Node[]) => Promise<void> | void;
507
+ /** Custom JS enter; see `leave`. Takes precedence over `enterClass`. */
508
+ enter?: (nodes: Node[]) => Promise<void> | void;
509
+ /**
510
+ * Safety cap for one enter or leave, in ms. By default a phase waits for its
511
+ * end events, or for the longest computed animation / transition (delay +
512
+ * duration x iteration count) + 50 ms. Never more than 5000 ms.
513
+ */
514
+ maxDuration?: number;
515
+ /** Also play the enter on the directive's first render. Default `false`. */
516
+ appear?: boolean;
517
+ /**
518
+ * Opt-in, in ms: let content that arrives later enter WITH the branch, if
519
+ * it is committed within this time, counted from the switch and never more
520
+ * than 5000 ms. Default: not waited for. The content waited for is the
521
+ * PENDING content inside the entering branch, wherever it sits: the initial
522
+ * content (or error view) of every `Suspense` boundary (the router's and
523
+ * the core one), `lazy()` component and route outlet (`Route`, `Outlet`,
524
+ * `KeepAliveRoute`), and what the application registers with
525
+ * `pendingContent()`. An outlet is pending until its CURRENT navigation
526
+ * commits a page, an error view or nothing at all — a superseded pass, or
527
+ * one waiting for its anchor to be attached, settles nothing.
528
+ *
529
+ * - A class-based enter starts at once and stays open for that content
530
+ * until it has settled or the time is up: each piece joins the enter when
531
+ * it is committed, and the enter ends when the animations do, within the
532
+ * enter's own `maxDuration` / 5 s cap. Content that reveals more pending
533
+ * content (an outer boundary whose result holds an inner one, a layout
534
+ * holding an outlet) keeps the enter open for that too.
535
+ * - A custom `enter` is called once the content has settled — the content
536
+ * it revealed included — or when the time is up, with the nodes present
537
+ * then.
538
+ * - What is pending is taken from the branch as it is: again whenever a
539
+ * registration begins or settles, and whenever a node moves into or out
540
+ * of the branch while the enter waits. Content joins only if it is inside
541
+ * the branch when it settles, and only if it was committed — a cancelled
542
+ * registration (or one dropped with its location) enrolls nothing.
543
+ * - Nothing moves the deadline; it ends when its own timer fires, whatever
544
+ * the wall clock does. Content committed after it, or after the enter
545
+ * ended, appears without an enter: an enter never replays, and nothing is
546
+ * loaded again for it. An enclosing branch that is paused when it ends
547
+ * still holds a custom `enter` until it resumes.
548
+ *
549
+ * What joins a class-based enter, automatically: elements a top-level range
550
+ * of the branch (an `each()`, a nested directive) adds while it runs —
551
+ * whatever this setting — and the content committed for a pending
552
+ * registration. Either is skipped when it lies inside an element of the
553
+ * enter whose own animation is still running: it already moves with that
554
+ * element, and a second copy of the animation would compound (twice the
555
+ * offset, the opacity squared). Once that animation has finished, content
556
+ * arriving inside it enters on its own. A nested directive's own
557
+ * `transition` is never affected.
558
+ *
559
+ * With `mode: "in-out"` the outgoing branch starts leaving when this enter
560
+ * ends, so it waits as long. The first render takes part only with
561
+ * `appear: true`.
562
+ */
563
+ waitForContent?: number;
564
+ }
565
+
479
566
  /**
480
567
  * What a rebuild does with focus it discarded. Set per directive through the
481
568
  * `focus` option of `when()` / `match()`.
@@ -531,7 +618,127 @@ interface DirectiveOptions {
531
618
  * `<body>` when the rebuilt branch has no counterpart.
532
619
  */
533
620
  focus?: FocusPolicy;
621
+ /**
622
+ * Animate branch changes. Without it (the default) the outgoing branch is
623
+ * removed and disposed at the moment of the switch.
624
+ *
625
+ * ```ts
626
+ * when(() => step() === "done", () => Done(), () => Generating(), {
627
+ * transition: { enterClass: "fade-in", leaveClass: "fade-out" },
628
+ * });
629
+ * ```
630
+ *
631
+ * With a transition, at the switch the outgoing branch:
632
+ * - is FROZEN: its reactive bindings and effects stop, so it keeps showing
633
+ * its last state instead of reacting to state the new branch now owns;
634
+ * - gets `inert` and `aria-hidden="true"`, so it cannot be clicked or
635
+ * focused while it fades (focus inside it follows the `focus` policy, as
636
+ * on any rebuild);
637
+ * - plays its leave (`leaveClass`, or a custom `leave`) where it is in the
638
+ * DOM, and is removed and disposed when that ends: once every animation
639
+ * and transition its elements actually started has finished (read with
640
+ * `getAnimations()`; end events where it is missing), when the custom
641
+ * `leave` promise settles, or at a timeout computed from its styles,
642
+ * capped by `maxDuration` and never more than 5 s. Zero durations (no animation, a
643
+ * reduced-motion stylesheet, `display: none`) remove it synchronously.
644
+ *
645
+ * Its `onUnmount` callbacks and `registerDisposer` teardowns run when it is
646
+ * actually removed, so a component can keep a canvas or a
647
+ * `requestAnimationFrame` loop running until it has faded out.
648
+ *
649
+ * Switching again while a branch is leaving does not interrupt it; several
650
+ * branches may be leaving at once. Switching back to a bare element (or
651
+ * fragment) branch that is still leaving cancels its leave and re-attaches
652
+ * it. The first render plays no enter unless `appear: true`. An enter waits
653
+ * for the first render of a top-level `each()` or nested directive (a few
654
+ * microtask turns at most), so those rows enter with the branch; rows such a
655
+ * range adds while a class-based enter runs join it, and rows it adds while
656
+ * the branch leaves join the leave. Content that arrives later (a
657
+ * `Suspense` boundary, a route outlet, a `pendingContent()` registration)
658
+ * is not waited for unless `waitForContent` says how long to wait.
659
+ * Disposing the directive removes leaving branches at once. Server renders
660
+ * ignore transitions entirely.
661
+ *
662
+ * A branch the directive built has its own lifecycle either way (see
663
+ * `lifecycleScope`): without a transition it ends at the switch, with one it
664
+ * pauses for the leave and ends when the branch is released.
665
+ */
666
+ transition?: DirectiveTransition;
534
667
  }
668
+ /**
669
+ * Conditional rendering directive. Renders content only when condition is true.
670
+ * When false, renders nothing (comment placeholder). Re-evaluates reactively.
671
+ *
672
+ * Each branch may be a THUNK (rebuilt on every condition change) or a bare
673
+ * element/value (attached as-is, and re-attached unchanged on every later
674
+ * change). Both shapes are accepted so that `when` and {@link show} take the
675
+ * same arguments; a bare element used to be accepted and then silently render
676
+ * nothing, because the directive called it as a function only.
677
+ *
678
+ * A branch may be SEVERAL sibling nodes: return a `Fragment([...])` (or any
679
+ * `DocumentFragment`). No wrapper element is needed, and on every switch all of
680
+ * its nodes — including what its reactive children, an `each()` or a nested
681
+ * `when` / `match` at its top level render now — are removed and disposed
682
+ * together. Cleanup registered on the fragment itself runs then too.
683
+ *
684
+ * The branch is rebuilt whenever the condition's VALUE changes (`Object.is`),
685
+ * not only when its truthiness flips, so
686
+ * `when(() => selectedUser(), () => UserCard(selectedUser()!))` rebuilds the
687
+ * card for each different user. To rebuild only when truthiness flips, return
688
+ * a boolean: `when(() => !!x(), …)`. For rebuilds keyed on something other
689
+ * than the value itself (an id, a mode), use {@link match}.
690
+ *
691
+ * @param condition Reactive getter; its value is compared to decide the branch
692
+ * @param thenBranch Element, value, fragment, or function returning one, used when truthy
693
+ * @param elseBranch Optional counterpart used when falsy. May be omitted in
694
+ * favour of `options` (`when(cond, then, { focus: "discard" })`).
695
+ * @param options Optional {@link DirectiveOptions}: focus policy and transitions
696
+ * @returns A Comment anchor that manages the conditional content
697
+ *
698
+ * @example
699
+ * ```ts
700
+ * when(
701
+ * () => isLoggedIn(),
702
+ * () => div("Welcome!"),
703
+ * () => div("Please log in")
704
+ * );
705
+ * when(() => isLoggedIn(), div("Welcome!")); // also valid
706
+ *
707
+ * // Two sibling buttons, no wrapper element:
708
+ * when(() => confirming(), () => Fragment([button("Yes"), button("No")]));
709
+ *
710
+ * // Fade the outgoing branch out before it is removed:
711
+ * when(() => ready(), () => Result(), () => Spinner(), {
712
+ * transition: { leaveClass: "fade-out" },
713
+ * });
714
+ * ```
715
+ *
716
+ * GOTCHA — branch factories rebuild only when `condition` changes. A branch
717
+ * factory runs untracked, so a signal read *eagerly* inside it is captured once
718
+ * and never updates:
719
+ * ```ts
720
+ * when(() => show(), () => div(`Count: ${count()}`)); // ✗ frozen at first count
721
+ * when(() => show(), () => div(() => `Count: ${count()}`)); // ✓ reactive text child
722
+ * ```
723
+ * Drive per-branch reactivity with a nested getter (or a reactive child), not a
724
+ * bare read in the factory body.
725
+ *
726
+ * TRAP — a rebuild DESTROYS the outgoing branch, so focus, selection and any
727
+ * in-progress IME composition inside it are lost. The runtime restores focus
728
+ * when the rebuilt branch contains an element with the same `id`, `name`, or
729
+ * `data-focus-key`, and warns in dev when it cannot. For a subtree holding a
730
+ * live edit, prefer {@link match} keyed on a shape key so the subtree is
731
+ * rebuilt only when the shape genuinely changes. When dropping focus is the
732
+ * point (Escape closes a panel), say so with `{ focus: "discard" }`, or route
733
+ * it with `{ focus: { restoreTo: triggerEl } }`.
734
+ *
735
+ * A branch switch disposes the outgoing branch BEFORE any binding inside it can
736
+ * re-run in the same flush, so a binding that is only valid while its branch is
737
+ * mounted (`() => selected()!.name` under `when(() => selected(), …)`) never
738
+ * observes the state that removed it. With a `transition`, the outgoing branch
739
+ * is frozen at that same point instead, and disposed when its leave ends — see
740
+ * {@link DirectiveOptions.transition}.
741
+ */
535
742
  declare function when<T>(condition: () => T, thenBranch: NodeChild, elseBranch?: NodeChild | DirectiveOptions, options?: DirectiveOptions): Comment;
536
743
  /**
537
744
  * Pattern matching directive. Renders content based on matching a reactive value
@@ -541,7 +748,8 @@ declare function when<T>(condition: () => T, thenBranch: NodeChild, elseBranch?:
541
748
  * @param cases Object mapping values to render functions
542
749
  * @param fallback Optional default case if no match found. May be omitted in
543
750
  * favour of `options`.
544
- * @param options Optional {@link DirectiveOptions} (`focus` policy, as in `when`)
751
+ * @param options Optional {@link DirectiveOptions} (`focus` policy and
752
+ * `transition`, as in `when`)
545
753
  * @returns A Comment anchor that manages the matched content
546
754
  *
547
755
  * @example
@@ -576,7 +784,17 @@ declare function when<T>(condition: () => T, thenBranch: NodeChild, elseBranch?:
576
784
  *
577
785
  * Cases and the fallback accept a bare element as well as a factory, matching
578
786
  * {@link when} and {@link show}. A bare element is re-attached rather than
579
- * rebuilt, so it keeps whatever state it accumulated.
787
+ * rebuilt, so it keeps whatever state it accumulated. A case may return a
788
+ * `Fragment([...])` to render several sibling nodes, and `transition` animates
789
+ * case changes exactly as in `when`:
790
+ *
791
+ * ```ts
792
+ * match(() => screen(), {
793
+ * attract: () => Attract(),
794
+ * draw: () => Fragment([Canvas(), Toolbar()]),
795
+ * done: () => Done(),
796
+ * }, { transition: { enterClass: "screen-in", leaveClass: "screen-out", mode: "out-in" } });
797
+ * ```
580
798
  */
581
799
  declare function match<T extends string | number>(value: () => T, cases: Record<string, NodeChild>, fallback?: NodeChild | DirectiveOptions, options?: DirectiveOptions): Comment;
582
800
 
@@ -713,6 +931,31 @@ declare function resolveComponent<El extends Element = HTMLElement>(name: string
713
931
  */
714
932
  declare function DynamicComponent(is: () => string | Component): HTMLDivElement;
715
933
 
934
+ /**
935
+ * The item getter `each()` passes to `render`: a reactive accessor for the
936
+ * row's CURRENT item, plus `peek()`.
937
+ *
938
+ * Calling it inside a binding (`() => item().name`) or an effect subscribes to
939
+ * the row, so the binding re-runs when reconciliation assigns the row's key a
940
+ * replacement item. Calling it directly in the body of `render` is a one-time
941
+ * read: `render` runs once per key, untracked, so what it read goes stale when
942
+ * the item behind the key is replaced (and dev builds warn when that happens).
943
+ */
944
+ type EachItem<T> = Accessor<T> & {
945
+ /**
946
+ * Read the current item WITHOUT subscribing and without the dev staleness
947
+ * warning: the explicit form of an intentional one-time read in `render`
948
+ * (an initial value, an id that is part of the key). `untracked(() => item())`
949
+ * does not serve here: `render` is already untracked, so that read is still
950
+ * reported as one that may go stale.
951
+ */
952
+ peek(): T;
953
+ };
954
+ /** The index getter `each()` passes to `render`; see {@link EachItem}. */
955
+ type EachIndex = Accessor<number> & {
956
+ /** Read the current index without subscribing and without the dev staleness warning. */
957
+ peek(): number;
958
+ };
716
959
  /**
717
960
  * Renders a list of nodes efficiently with key-based diffing and
718
961
  * LIS-based move minimization.
@@ -746,6 +989,21 @@ declare function DynamicComponent(is: () => string | Component): HTMLDivElement;
746
989
  * down (`Row({ item, index })`) or read them inside bindings
747
990
  * (`() => item().name`) and effects created by the row.
748
991
  *
992
+ * ```ts
993
+ * each(products, (p) => img({ src: p().url }), { key: (p) => p.name }); // ✗ src frozen per key
994
+ * each(products, (p) => img({ src: () => p().url }), { key: (p) => p.name }); // ✓ follows the item
995
+ * each(products, (p) => Row({ id: p.peek().id }), { key: (p) => p.id }); // ✓ deliberate one-time read
996
+ * ```
997
+ *
998
+ * In development, a row whose `render` read `item()` (or `index()`) directly
999
+ * warns once per call site when that read actually goes stale: when its key is
1000
+ * handed a different item (`!Object.is`), or the row moves. A list whose items
1001
+ * are never replaced never warns. `item.peek()` / `index.peek()` read the
1002
+ * current value without subscribing and are never reported; use them to mark a
1003
+ * one-time read as intentional. (`untracked(() => item())` is not an
1004
+ * alternative here: `render` is already untracked.) The check and its message
1005
+ * are compiled out of production builds.
1006
+ *
749
1007
  * A binding that reads `item()` subscribes to the ROW's cell, not to the
750
1008
  * whole-array signal. `render` never re-runs for an existing key; only the
751
1009
  * bindings and effects that read that row's `item()` / `index()` re-run, and
@@ -753,11 +1011,11 @@ declare function DynamicComponent(is: () => string | Component): HTMLDivElement;
753
1011
  * row does not disturb it.
754
1012
  *
755
1013
  * @param getArray A reactive getter returning an array.
756
- * @param render A function that receives reactive item and index getters and returns a NodeChild.
1014
+ * @param render Receives the row's reactive item and index getters ({@link EachItem}, {@link EachIndex}) and returns a NodeChild. Runs once per key.
757
1015
  * @param options A key function for unique identity of items.
758
1016
  * @returns A Comment node serving as the anchor for the list.
759
1017
  */
760
- declare function each<T>(getArray: () => T[], render: (item: () => T, index: () => number) => NodeChild, options: {
1018
+ declare function each<T>(getArray: () => T[], render: (item: EachItem<T>, index: EachIndex) => NodeChild, options: {
761
1019
  key: (item: T) => string | number;
762
1020
  }): Comment;
763
1021
 
@@ -770,6 +1028,12 @@ declare function each<T>(getArray: () => T[], render: (item: () => T, index: ()
770
1028
  * change. The binding is owned by that placeholder, so disposing the parent the
771
1029
  * fragment was appended to (or unmounting a mounted fragment) stops it.
772
1030
  *
1031
+ * A fragment is a valid `when()` / `match()` branch: the directive records the
1032
+ * fragment's nodes before inserting it (insertion empties a DocumentFragment)
1033
+ * and removes and disposes all of them on the next switch, including whatever
1034
+ * its reactive children have rendered since. Several sibling nodes need no
1035
+ * wrapper element (and no `display: contents` span).
1036
+ *
773
1037
  * @example
774
1038
  * ```ts
775
1039
  * div([
@@ -778,6 +1042,8 @@ declare function each<T>(getArray: () => T[], render: (item: () => T, index: ()
778
1042
  * () => `Count: ${count()}`,
779
1043
  * ])
780
1044
  * ]);
1045
+ *
1046
+ * when(() => editing(), () => Fragment([button("Save"), button("Cancel")]));
781
1047
  * ```
782
1048
  *
783
1049
  * @param nodes Array of child nodes to include in the fragment
@@ -1006,7 +1272,7 @@ declare const area: TagFunction<HTMLAreaElement, TagProps<HTMLAreaElement>>;
1006
1272
  declare const audio: TypedTagFunction<AudioProps, HTMLAudioElement>;
1007
1273
  declare const img: TypedTagFunction<ImgProps, HTMLImageElement>;
1008
1274
  declare const map: TagFunction<HTMLMapElement, TagProps<HTMLMapElement>>;
1009
- declare const track$1: TagFunction<HTMLTrackElement, TagProps<HTMLTrackElement>>;
1275
+ declare const track: TagFunction<HTMLTrackElement, TagProps<HTMLTrackElement>>;
1010
1276
  declare const video: TypedTagFunction<VideoProps, HTMLVideoElement>;
1011
1277
  declare const embed: TagFunction<HTMLEmbedElement, TagProps<HTMLEmbedElement>>;
1012
1278
  declare const iframe: TagFunction<HTMLIFrameElement, TagProps<HTMLIFrameElement>>;
@@ -1157,7 +1423,30 @@ declare function takePendingError(node: Element): Error | undefined;
1157
1423
  *
1158
1424
  * The returned component renders either the loading container (a `div`) or,
1159
1425
  * once loaded, the component's own root — its return type is the union, so an
1160
- * SVG component stays usable without a cast.
1426
+ * SVG component stays usable without a cast. A component returning a
1427
+ * `DocumentFragment` is rendered in the container either way: a fragment is
1428
+ * no root its instance could be disposed with. Cleanup registered on the
1429
+ * fragment itself still runs, once, when the instance is disposed — after its
1430
+ * children's.
1431
+ *
1432
+ * The loaded component belongs to the lifecycle the placeholder was rendered
1433
+ * in (a `when()` branch, a route instance, a `Suspense`'s content): while that
1434
+ * lifecycle is paused — a branch playing its leave, a
1435
+ * `KeepAliveRoute({ suspendInactive: true })` page that is hidden — the module
1436
+ * is kept and nothing is built or committed until it resumes; if it ends,
1437
+ * nothing is. The loaded module is cached either way.
1438
+ *
1439
+ * Every instance has the same lifetime, whether its module was loaded already
1440
+ * or not: the component is built under its own lifecycle, nested in the one
1441
+ * it renders in. A `lifecycleScope()` it captures pauses with that one, and
1442
+ * ends — its scoped effects and watches disposed — when the instance is
1443
+ * disposed: the container, or the component's own root when a cached module
1444
+ * renders it directly.
1445
+ *
1446
+ * A synchronous throw from `importFn` propagates to the caller; nothing is
1447
+ * left registered for the container that was never returned. So does a throw
1448
+ * from a cached component, rendered synchronously: what it registered is
1449
+ * rolled back and its lifecycle ended.
1161
1450
  *
1162
1451
  * @param importFn Dynamic import function returning `{ default: Component }`
1163
1452
  * @returns A component function that lazy-loads on first call
@@ -1190,6 +1479,15 @@ interface SuspenseProps {
1190
1479
  * Render `fallback` until the async `nodes` factory resolves, then swap it for
1191
1480
  * the resolved content.
1192
1481
  *
1482
+ * `fallback()` and `nodes()` each run under their own lifecycle, nested in the
1483
+ * one the boundary renders in: a `lifecycleScope()` captured in the fallback
1484
+ * ends — its scoped effects and watches disposed — when the content replaces
1485
+ * the fallback, or with the boundary; one captured in the content ends with
1486
+ * the boundary — or as soon as the content is abandoned: `nodes()` threw, or
1487
+ * the content it built could not be inserted (the boundary and its fallback
1488
+ * stay). A throwing `fallback()` propagates, with what it registered rolled
1489
+ * back.
1490
+ *
1193
1491
  * @param props `nodes` is the async content factory; `fallback` builds the
1194
1492
  * placeholder shown while it is pending.
1195
1493
  * @returns The live container element, already holding the fallback.
@@ -1300,6 +1598,327 @@ declare function mount(component: Component<void, Node> | Node, container: Eleme
1300
1598
  unmount: () => void;
1301
1599
  };
1302
1600
 
1601
+ interface ReactiveApi {
1602
+ suspendTracking: typeof suspendTracking;
1603
+ resumeTracking: typeof resumeTracking;
1604
+ isTrackingSuspended: typeof isTrackingSuspended;
1605
+ untracked: typeof untracked$1;
1606
+ retrack: typeof retrack$1;
1607
+ track: typeof track$1;
1608
+ reactiveBinding: typeof reactiveBinding;
1609
+ recordDependency: typeof recordDependency;
1610
+ cleanup: typeof cleanup;
1611
+ setMaxSubscriberRepeats: typeof setMaxSubscriberRepeats;
1612
+ setMaxDrainIterations: typeof setMaxDrainIterations$1;
1613
+ drainNotificationQueue: typeof drainNotificationQueue;
1614
+ queueSignalNotification: typeof queueSignalNotification;
1615
+ notifySubscribers: typeof notifySubscribers;
1616
+ getSubscriberCount: typeof getSubscriberCount;
1617
+ getSubscriberDeps: typeof getSubscriberDeps;
1618
+ forEachSubscriber: typeof forEachSubscriber;
1619
+ getOwnerScope?: typeof getOwnerScope;
1620
+ runWithOwnerScope?: typeof runWithOwnerScope;
1621
+ bindOwnerScope?: typeof bindOwnerScope;
1622
+ structuralBinding?: typeof structuralBinding;
1623
+ captureFrozen?: typeof captureFrozen;
1624
+ holdSubtree?: typeof holdSubtree;
1625
+ version: string;
1626
+ __dupWarned?: boolean;
1627
+ }
1628
+ declare const untracked: ReactiveApi["untracked"];
1629
+ declare const retrack: ReactiveApi["retrack"];
1630
+ declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
1631
+
1632
+ /** Options for effect */
1633
+ interface EffectOptions {
1634
+ /** Error handler for exceptions thrown during effect execution. */
1635
+ onError?: (error: unknown) => void;
1636
+ /**
1637
+ * Also run during a server render. By default an effect is a client-only
1638
+ * side effect and does nothing under SSR. With `server: true` it runs on the
1639
+ * server exactly as in the browser — reactively, until the render is
1640
+ * disposed — which is what a data primitive needs to start a fetch the server
1641
+ * render then awaits. Browser APIs are still off limits inside it.
1642
+ */
1643
+ server?: boolean;
1644
+ }
1645
+ /**
1646
+ * Creates a callback that only tracks the specified dependencies,
1647
+ * running the handler in an untracked context. Use with `effect()`
1648
+ * to control exactly which signals trigger re-execution.
1649
+ *
1650
+ * @param deps Getter(s) whose return values are tracked as dependencies
1651
+ * @param handler Called with the current dependency value(s) whenever they change
1652
+ * @returns A function suitable for passing to `effect()`
1653
+ *
1654
+ * @example
1655
+ * ```ts
1656
+ * const [count, setCount] = signal(0);
1657
+ * const [label, setLabel] = signal("clicks");
1658
+ *
1659
+ * // Only re-runs when count changes, NOT when label changes
1660
+ * effect(on(() => count(), (c) => {
1661
+ * console.log(`${c} ${label()}`); // label() read but not tracked
1662
+ * }));
1663
+ * ```
1664
+ */
1665
+ declare function on<T>(deps: () => T, handler: (value: T, prev: T | undefined) => void): () => void;
1666
+ /** Registers a function to run before the effect re-runs or is disposed.
1667
+ * Called with the same signature inside every invocation. */
1668
+ type OnCleanup = (fn: () => void) => void;
1669
+ /** The user's effect body — may accept an `onCleanup` callback to register
1670
+ * teardown that runs before the next re-run or on dispose. */
1671
+ type EffectBody = (onCleanup: OnCleanup) => void;
1672
+ /**
1673
+ * effect runs the provided effectFn immediately and re-runs it whenever
1674
+ * any reactive dependency changes.
1675
+ * Returns a cleanup function to stop further executions.
1676
+ *
1677
+ * In SSR mode, effect is a no-op — side effects should not run on the server.
1678
+ *
1679
+ * @example addEventListener pattern with built-in teardown:
1680
+ * ```ts
1681
+ * effect((onCleanup) => {
1682
+ * const handler = (e: Event) => { ... };
1683
+ * window.addEventListener("resize", handler);
1684
+ * onCleanup(() => window.removeEventListener("resize", handler));
1685
+ * });
1686
+ * ```
1687
+ *
1688
+ * @returns A dispose function that stops the effect and releases its
1689
+ * dependencies. Idempotent.
1690
+ */
1691
+ declare function effect(effectFn: EffectBody | (() => void), options?: EffectOptions): () => void;
1692
+
1693
+ /** Where a {@link LifecycleScope} is in its life. */
1694
+ type LifecycleState = "active" | "paused" | "ended";
1695
+ /**
1696
+ * The lifecycle a component was rendered in, captured so work created LATER —
1697
+ * after an `await` — can still belong to it. See {@link lifecycleScope}.
1698
+ *
1699
+ * Every registration made through it follows the same contract:
1700
+ *
1701
+ * - active: it behaves exactly like the plain primitive;
1702
+ * - paused: a new registration is created only when the lifecycle resumes; an
1703
+ * existing one does not run, and what changed meanwhile is caught up in ONE
1704
+ * run on resume, against the current state;
1705
+ * - ended: it is disposed — subscriptions released, cleanups run once — in
1706
+ * the disposal that ends the lifecycle, before any other reactive work can
1707
+ * run, and never runs again. A registration made after the end does nothing.
1708
+ *
1709
+ * Each method returns an idempotent disposer that works at any point: before
1710
+ * the registration was created (it then never is), while paused, or after.
1711
+ */
1712
+ interface LifecycleScope {
1713
+ /**
1714
+ * `"active"`: the subtree renders normally. `"paused"`: it is leaving (a
1715
+ * `when()` / `match()` branch playing its leave transition) and may still be
1716
+ * taken back. `"ended"`: it was released.
1717
+ */
1718
+ state(): LifecycleState;
1719
+ /**
1720
+ * `effect()`, owned by the captured lifecycle. Paused, changes are
1721
+ * coalesced into one run on resume (its cleanups first, as always).
1722
+ */
1723
+ effect(fn: EffectBody | (() => void), options?: EffectOptions): () => void;
1724
+ /**
1725
+ * `watch()`, owned by the captured lifecycle. It keeps `watch`'s contract —
1726
+ * `callback(current, previous)` when the getter's value changed, compared
1727
+ * with `Object.is` — so while paused the values in between are not
1728
+ * delivered: on resume the callback runs once, with the current value and
1729
+ * the last one it delivered, and not at all if the value came back to it.
1730
+ * Created while paused, it reads its initial value when the lifecycle
1731
+ * resumes.
1732
+ */
1733
+ watch<T>(getter: () => T, callback: (value: T, prev: T | undefined) => void): () => void;
1734
+ /**
1735
+ * The general form: run `register` as owned by the captured lifecycle and
1736
+ * dispose what it returns when the lifecycle ends. Every effect, watcher or
1737
+ * binding `register` creates synchronously is paused and resumed with the
1738
+ * lifecycle; the disposer it returns is called once — at the end, or by the
1739
+ * disposer this returns. Paused, `register` itself waits for the resume.
1740
+ *
1741
+ * If `register` throws, nothing is kept for it: the registration is rolled
1742
+ * back (its lifecycle listeners dropped) and the error is rethrown — or,
1743
+ * for a registration deferred to a resume, reported like an effect error.
1744
+ * Resources `register` acquired before throwing are its own to release:
1745
+ * it never returned the disposer that would have done it.
1746
+ */
1747
+ own(register: () => () => void): () => void;
1748
+ /**
1749
+ * Call `callback` each time the lifecycle pauses (a branch begins its leave,
1750
+ * a `KeepAliveRoute({ suspendInactive: true })` page is cached), a
1751
+ * microtask after it does. Reactive work owned by the scope pauses on its
1752
+ * own; this is for what does not — timers, sockets, media, requests — so it
1753
+ * can be suspended explicitly. Dropped when the lifecycle ends; returns a
1754
+ * function that drops it sooner.
1755
+ *
1756
+ * The callback runs in the context the scope captured — `route()` is the
1757
+ * route of the page that is pausing, not of the page navigated to — and
1758
+ * with the captured lifecycle as owner of what it creates. A pause and a
1759
+ * resume in the same task deliver neither. A callback that throws is
1760
+ * reported; the others still run.
1761
+ */
1762
+ onPause(callback: () => void): () => void;
1763
+ /** Call `callback` each time the lifecycle resumes after a pause; see {@link onPause}. */
1764
+ onResume(callback: () => void): () => void;
1765
+ }
1766
+ /** The owner check a scope captured. @internal */
1767
+ declare function scopeOwner(scope: LifecycleScope): HeldCheck | undefined;
1768
+ /**
1769
+ * Capture the current lifecycle — the structural owner and route scope the
1770
+ * calling code renders in — for work created after an `await`.
1771
+ *
1772
+ * An `effect()` or `watch()` called after an `await` runs outside the render
1773
+ * that started the work, so it belongs to no lifecycle: it keeps running while
1774
+ * its component's branch is paused for a leave transition, and only a
1775
+ * disposer you registered yourself stops it. Nothing can find it from the
1776
+ * outside either: it has no DOM node, so holding a detached subtree does not
1777
+ * reach it, and registering its disposer on a node gives teardown, not
1778
+ * ownership. Capture the lifecycle synchronously, before the first `await`,
1779
+ * and create the work through it:
1780
+ *
1781
+ * ```ts
1782
+ * function Profile(id: string) {
1783
+ * const scope = lifecycleScope(); // during render
1784
+ * const el = div("loading…");
1785
+ * load(id).then((user) => {
1786
+ * scope.effect(() => { // owned by Profile's lifecycle
1787
+ * el.textContent = `${user.name} (${status()})`;
1788
+ * });
1789
+ * scope.watch(() => user.online(), (online) => notify(online));
1790
+ * });
1791
+ * return el;
1792
+ * }
1793
+ * ```
1794
+ *
1795
+ * Nothing is installed globally across the `await`: each scope is an explicit
1796
+ * object, so concurrent work never inherits another's lifecycle.
1797
+ *
1798
+ * Which lifecycle is captured:
1799
+ * - in a `when()` / `match()` branch the directive built, the branch's: it
1800
+ * ends when the branch is replaced — at the switch, or with a `transition`
1801
+ * when its leave ends; it pauses while the branch leaves and resumes if the
1802
+ * branch is taken back. A bare node handed to the directive is the
1803
+ * caller's, so the scope is that of the code that built it;
1804
+ * - in a route component (cached, lazy, unwrapped from a module), the route
1805
+ * instance's: it ends when the instance is replaced or disposed, and pauses
1806
+ * with the branch holding the outlet — and, with
1807
+ * `KeepAliveRoute({ suspendInactive: true })`, while the page is cached;
1808
+ * - in `Suspense`'s `nodes()` (or `fallback`), the boundary's content (or
1809
+ * fallback): it ends with the boundary (or when the fallback is replaced),
1810
+ * and the content's as soon as that content is abandoned — `nodes()` threw
1811
+ * or rejected, or what it returned could not be inserted;
1812
+ * - outside any render: none. The scope is always active and never ends; its
1813
+ * registrations behave like the plain primitives.
1814
+ *
1815
+ * Only what goes through the scope is owned. An `effect()` created after an
1816
+ * `await` without it is unowned, and stays so: nothing is installed across
1817
+ * the `await`, and ownership is never inferred from what rendered last.
1818
+ * `registerDisposer(node, stop)` gives such work teardown with that node, not
1819
+ * pause and resume.
1820
+ *
1821
+ * @returns The captured {@link LifecycleScope}.
1822
+ */
1823
+ declare function lifecycleScope(): LifecycleScope;
1824
+
1825
+ /** A registration made by {@link pendingContent}. */
1826
+ interface PendingContent {
1827
+ /**
1828
+ * The content is committed: settle the registration. Call it AFTER the
1829
+ * content is in the DOM — readiness means "on the page", not "the request
1830
+ * returned". `committed` names what joins a running enter; by default it is
1831
+ * what the location renders then (the children of an element, the range
1832
+ * after an anchor). A failure that commits an error view settles the same
1833
+ * way, with that view.
1834
+ */
1835
+ done(committed?: Node | readonly Node[]): void;
1836
+ /**
1837
+ * The operation was cancelled or superseded: settle WITHOUT content.
1838
+ * Nothing joins a running enter for it — not even what its location shows
1839
+ * now (a loading placeholder). Disposing the location, or the end of the
1840
+ * lifecycle it was registered on, cancels it the same way.
1841
+ */
1842
+ cancel(): void;
1843
+ /** Whether it has settled (done, cancelled, or dropped with its location). */
1844
+ readonly settled: boolean;
1845
+ }
1846
+ /**
1847
+ * Register content that will arrive later, so a `when()` / `match()` enter
1848
+ * with `waitForContent` waits for it. Nothing becomes pending on its own — a
1849
+ * `fetch`, a promise or an `asyncDerived` is not tracked until the application
1850
+ * says so here — and each call is one independent registration: several at
1851
+ * the same location stay pending until each one settles, and settling one
1852
+ * (a stale request completing late) never settles another.
1853
+ *
1854
+ * `at` places it:
1855
+ * - a node (an element, or a range anchor): it takes part in an enter while
1856
+ * the node lies inside the entering branch. Moving it into the branch, or
1857
+ * out of it, while the enter waits is noticed when it happens: attached, it
1858
+ * holds the enter (and its content joins when it is done); removed, it no
1859
+ * longer does. Disposing the node cancels it;
1860
+ * - a {@link LifecycleScope}: it takes part when that lifecycle lies inside
1861
+ * the entering branch (captured in it, or in a route instance or `Suspense`
1862
+ * below it). The lifecycle ending cancels it. This is the form to use when
1863
+ * the content is pending BEFORE the node it will render into exists — a
1864
+ * component that starts a request, then builds its DOM when it returns:
1865
+ * capture the scope while rendering and register on it. A scope captured
1866
+ * outside any render belongs to no branch and never ends: a registration
1867
+ * on it holds no enter, and stays until it is settled.
1868
+ *
1869
+ * Settle it exactly when the outcome is committed: `done()` after inserting
1870
+ * the content (or an error view), `cancel()` when the operation is abandoned
1871
+ * or superseded — a cancelled registration enrolls nothing in the enter. Both
1872
+ * are idempotent; the first call wins, so a stale `done()` after a
1873
+ * `cancel()` (or after the location was disposed) does nothing. A registration
1874
+ * made while an enter is waiting is picked up by it, but the enter's
1875
+ * deadline, fixed at the switch, never moves: content committed later
1876
+ * appears without an enter.
1877
+ *
1878
+ * Server renders play no transitions: there it registers nothing and returns
1879
+ * an inert handle (already settled), so the same code runs in both places.
1880
+ *
1881
+ * ```ts
1882
+ * function Results(query: string) {
1883
+ * const list = ul({ class: "results" }, li("Loading…"));
1884
+ * const pending = pendingContent(list);
1885
+ * fetch(`/api/search?q=${query}`)
1886
+ * .then((r) => r.json())
1887
+ * .then((rows) => {
1888
+ * list.replaceChildren(...rows.map((row) => li(row.title)));
1889
+ * pending.done(); // committed: the rows join the enter
1890
+ * })
1891
+ * .catch((err) => {
1892
+ * list.replaceChildren(li({ class: "error" }, String(err)));
1893
+ * pending.done(); // the error view is the outcome
1894
+ * });
1895
+ * return list;
1896
+ * }
1897
+ * ```
1898
+ *
1899
+ * Content driven by reactive state is committed by its bindings; settle once
1900
+ * the state that feeds them has arrived. Readiness is read a microtask after
1901
+ * `done()`, after the bindings of the same update have run:
1902
+ *
1903
+ * ```ts
1904
+ * function Profile() {
1905
+ * const scope = lifecycleScope();
1906
+ * const user = asyncDerived(() => loadUser(), null);
1907
+ * const card = div({ class: "card" }, () => user.value()?.name ?? "Loading…");
1908
+ * const pending = pendingContent(card);
1909
+ * scope.watch(() => user.loading(), (loading) => {
1910
+ * if (!loading) pending.done(); // value (or error) rendered
1911
+ * });
1912
+ * scope.own(() => user.dispose);
1913
+ * return card;
1914
+ * }
1915
+ * ```
1916
+ *
1917
+ * @param at The node, range anchor or captured lifecycle the content belongs to.
1918
+ * @returns The registration's handle.
1919
+ */
1920
+ declare function pendingContent(at: Node | LifecycleScope): PendingContent;
1921
+
1303
1922
  /**
1304
1923
  * Portal renders nodes into a DOM node outside the parent component hierarchy.
1305
1924
  * Useful for modals, tooltips, dropdowns, and overlays.
@@ -1537,67 +2156,6 @@ declare function deepEqual(a: unknown, b: unknown, seen?: Map<object, Set<object
1537
2156
  */
1538
2157
  declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T) => T)) => void];
1539
2158
 
1540
- /** Options for effect */
1541
- interface EffectOptions {
1542
- /** Error handler for exceptions thrown during effect execution. */
1543
- onError?: (error: unknown) => void;
1544
- /**
1545
- * Also run during a server render. By default an effect is a client-only
1546
- * side effect and does nothing under SSR. With `server: true` it runs on the
1547
- * server exactly as in the browser — reactively, until the render is
1548
- * disposed — which is what a data primitive needs to start a fetch the server
1549
- * render then awaits. Browser APIs are still off limits inside it.
1550
- */
1551
- server?: boolean;
1552
- }
1553
- /**
1554
- * Creates a callback that only tracks the specified dependencies,
1555
- * running the handler in an untracked context. Use with `effect()`
1556
- * to control exactly which signals trigger re-execution.
1557
- *
1558
- * @param deps Getter(s) whose return values are tracked as dependencies
1559
- * @param handler Called with the current dependency value(s) whenever they change
1560
- * @returns A function suitable for passing to `effect()`
1561
- *
1562
- * @example
1563
- * ```ts
1564
- * const [count, setCount] = signal(0);
1565
- * const [label, setLabel] = signal("clicks");
1566
- *
1567
- * // Only re-runs when count changes, NOT when label changes
1568
- * effect(on(() => count(), (c) => {
1569
- * console.log(`${c} ${label()}`); // label() read but not tracked
1570
- * }));
1571
- * ```
1572
- */
1573
- declare function on<T>(deps: () => T, handler: (value: T, prev: T | undefined) => void): () => void;
1574
- /** Registers a function to run before the effect re-runs or is disposed.
1575
- * Called with the same signature inside every invocation. */
1576
- type OnCleanup = (fn: () => void) => void;
1577
- /** The user's effect body — may accept an `onCleanup` callback to register
1578
- * teardown that runs before the next re-run or on dispose. */
1579
- type EffectBody = (onCleanup: OnCleanup) => void;
1580
- /**
1581
- * effect runs the provided effectFn immediately and re-runs it whenever
1582
- * any reactive dependency changes.
1583
- * Returns a cleanup function to stop further executions.
1584
- *
1585
- * In SSR mode, effect is a no-op — side effects should not run on the server.
1586
- *
1587
- * @example addEventListener pattern with built-in teardown:
1588
- * ```ts
1589
- * effect((onCleanup) => {
1590
- * const handler = (e: Event) => { ... };
1591
- * window.addEventListener("resize", handler);
1592
- * onCleanup(() => window.removeEventListener("resize", handler));
1593
- * });
1594
- * ```
1595
- *
1596
- * @returns A dispose function that stops the effect and releases its
1597
- * dependencies. Idempotent.
1598
- */
1599
- declare function effect(effectFn: EffectBody | (() => void), options?: EffectOptions): () => void;
1600
-
1601
2159
  /**
1602
2160
  * ref creates a mutable reference object with a reactive `current` property.
1603
2161
  *
@@ -2005,145 +2563,4 @@ declare function transition(): TransitionState;
2005
2563
  */
2006
2564
  declare function nextTick(): Promise<void>;
2007
2565
 
2008
- type Subscriber = () => void;
2009
- /**
2010
- * Stop recording dependency edges until the matching {@link resumeTracking}.
2011
- * Nestable — the depth is counted, so an inner pair does not resume early.
2012
- *
2013
- * Prefer {@link untracked}, which pairs the two calls for you even when the
2014
- * body throws. Reach for the raw pair only when the suspended region cannot be
2015
- * expressed as a single function.
2016
- *
2017
- * @returns Nothing.
2018
- */
2019
- declare function suspendTracking(): void;
2020
- /**
2021
- * Undo one {@link suspendTracking}. Dependency recording resumes only when the
2022
- * outermost pair closes.
2023
- *
2024
- * @returns Nothing.
2025
- */
2026
- declare function resumeTracking(): void;
2027
- /** Read the "tracking suspended" flag (used by derived's lazy path). */
2028
- declare function isTrackingSuspended(): boolean;
2029
- /**
2030
- * Run `fn` without recording anything it reads as a dependency.
2031
- *
2032
- * The suspension is released even if `fn` throws, which is the reason to prefer
2033
- * this over a manual suspend/resume pair.
2034
- *
2035
- * @param fn Function to run outside dependency tracking.
2036
- * @returns Whatever `fn` returns.
2037
- */
2038
- declare function untracked$1<T>(fn: () => T): T;
2039
- /** The owner scope in effect right now (`null` when there is none). @internal */
2040
- declare function getOwnerScope(): unknown;
2041
- /**
2042
- * Run `fn` with `scope` as the current owner scope, restoring the previous
2043
- * scope afterwards (even if `fn` throws). @internal
2044
- */
2045
- declare function runWithOwnerScope<T>(scope: unknown, fn: () => T): T;
2046
- /**
2047
- * Capture the current owner scope into `fn`, for work that runs later (a
2048
- * deferred first render, a lifecycle callback). Returns `fn` itself when there
2049
- * is no scope to carry. @internal
2050
- */
2051
- declare function bindOwnerScope<A extends unknown[], R>(fn: (...args: A) => R): (...args: A) => R;
2052
- declare function retrack$1(effectFn: () => void, subscriber: Subscriber): void;
2053
- declare function track(effectFn: () => void, subscriber?: Subscriber): () => void;
2054
- declare function reactiveBinding(commit: () => void, ownerNode?: unknown, structural?: boolean): () => void;
2055
- /**
2056
- * Handle returned by {@link structuralBinding}.
2057
- * @internal
2058
- */
2059
- interface StructuralBinding {
2060
- /** Dispose the binding. Idempotent. */
2061
- stop: () => void;
2062
- /**
2063
- * Run `fn` with this binding as the structural owner, for a render that
2064
- * happens outside the binding's own run (a directive's deferred first
2065
- * render), so what it builds is owned exactly as a later rebuild would be.
2066
- */
2067
- run: <T>(fn: () => T) => T;
2068
- }
2069
- /**
2070
- * A {@link reactiveBinding} whose run may REPLACE the subtree it renders —
2071
- * the binding behind `when`, `match`, `each` and `KeepAlive`. Subscribers
2072
- * created while it runs are owned by it, and the drain runs it before any of
2073
- * them when both are pending (see "Structural ownership").
2074
- * @internal
2075
- */
2076
- declare function structuralBinding(commit: () => void, ownerNode?: unknown): StructuralBinding;
2077
- declare function recordDependency(signal: ReactiveSignal): void;
2078
- declare function cleanup(subscriber: Subscriber): void;
2079
- /**
2080
- * Cap how many times one subscriber may re-run within a single drain before it
2081
- * is quarantined as a runaway cycle. A safety valve, not a tuning knob.
2082
- *
2083
- * @param n New limit; ignored unless finite and positive.
2084
- * @returns The previous limit, so a caller can restore it.
2085
- */
2086
- declare function setMaxSubscriberRepeats(n: number): number;
2087
- /**
2088
- * Cap how many passes one notification drain may make before giving up on a
2089
- * self-refeeding update cycle.
2090
- *
2091
- * @param n New limit; ignored unless finite and positive.
2092
- * @returns The previous limit, so a caller can restore it.
2093
- */
2094
- declare function setMaxDrainIterations$1(n: number): number;
2095
- /**
2096
- * Flush queued signal notifications, running every dirty subscriber.
2097
- *
2098
- * Re-entrant calls are ignored: a write performed from inside a subscriber adds
2099
- * to the queue the current drain is already working through, rather than
2100
- * starting a nested one.
2101
- *
2102
- * @returns Nothing.
2103
- */
2104
- declare function drainNotificationQueue(): void;
2105
- declare function queueSignalNotification(signal: ReactiveSignal): void;
2106
- /**
2107
- * Mark every subscriber of `signal` dirty and queue them for the next drain.
2108
- *
2109
- * @param signal The signal that changed.
2110
- * @returns Nothing.
2111
- */
2112
- declare function notifySubscribers(signal: ReactiveSignal): void;
2113
- /** O(1) subscriber count for devtools / introspection. */
2114
- declare function getSubscriberCount(signal: ReactiveSignal): number;
2115
- /** Return the signals a subscriber currently depends on, in record order. */
2116
- declare function getSubscriberDeps(subscriber: Subscriber): ReactiveSignal[];
2117
- /** Iterate subscribers of a signal (devtools graph walk). */
2118
- declare function forEachSubscriber(signal: ReactiveSignal, visit: (sub: Subscriber) => void): void;
2119
-
2120
- interface ReactiveApi {
2121
- suspendTracking: typeof suspendTracking;
2122
- resumeTracking: typeof resumeTracking;
2123
- isTrackingSuspended: typeof isTrackingSuspended;
2124
- untracked: typeof untracked$1;
2125
- retrack: typeof retrack$1;
2126
- track: typeof track;
2127
- reactiveBinding: typeof reactiveBinding;
2128
- recordDependency: typeof recordDependency;
2129
- cleanup: typeof cleanup;
2130
- setMaxSubscriberRepeats: typeof setMaxSubscriberRepeats;
2131
- setMaxDrainIterations: typeof setMaxDrainIterations$1;
2132
- drainNotificationQueue: typeof drainNotificationQueue;
2133
- queueSignalNotification: typeof queueSignalNotification;
2134
- notifySubscribers: typeof notifySubscribers;
2135
- getSubscriberCount: typeof getSubscriberCount;
2136
- getSubscriberDeps: typeof getSubscriberDeps;
2137
- forEachSubscriber: typeof forEachSubscriber;
2138
- getOwnerScope?: typeof getOwnerScope;
2139
- runWithOwnerScope?: typeof runWithOwnerScope;
2140
- bindOwnerScope?: typeof bindOwnerScope;
2141
- structuralBinding?: typeof structuralBinding;
2142
- version: string;
2143
- __dupWarned?: boolean;
2144
- }
2145
- declare const untracked: ReactiveApi["untracked"];
2146
- declare const retrack: ReactiveApi["retrack"];
2147
- declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
2148
-
2149
- export { Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, ClientOnly, type ClientOnlyOptions, Component, type Context, DerivedAccessor, type DirectiveOptions, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FocusPolicy, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, MAX_DRAIN_TEARDOWNS, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type RuntimeErrorContext, type RuntimeErrorHandler, type RuntimeErrorPhase, type SSRStore, type SelectProps, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagFunction, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __renderParsedTemplate, __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, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getAction, getRequestScopedCache, getRuntimeErrorHandler, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isClient, isSSR, isServer, 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, registerAction, registerComponent, registerDisposer, replaceChildrenSafely, reportDrainRunaway, reportError, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, setRuntimeErrorHandler, show, 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$1 as track, transition, trapFocus, tspan, u, ul, unregisterComponent, unregisterDisposer, untracked, use, var_, video, watch, when, withSSR, writable };
2566
+ export { Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, ClientOnly, type ClientOnlyOptions, Component, type Context, DerivedAccessor, type DirectiveOptions, type DirectiveTransition, DynamicComponent, type EachIndex, type EachItem, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FocusPolicy, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, type LifecycleScope, type LifecycleState, Loading, type LoadingProps, type LongPressOptions, MAX_DRAIN_TEARDOWNS, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, type PendingContent, Portal, type Ref, type RuntimeErrorContext, type RuntimeErrorHandler, type RuntimeErrorPhase, type SSRStore, type SelectProps, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagFunction, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __renderParsedTemplate, __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, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getAction, getRequestScopedCache, getRuntimeErrorHandler, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isClient, isSSR, isServer, kbd, label, lazy, legend, li, lifecycleScope, 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, pendingContent, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerAction, registerComponent, registerDisposer, replaceChildrenSafely, reportDrainRunaway, reportError, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, scopeOwner, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, setRuntimeErrorHandler, show, 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, unregisterDisposer, untracked, use, var_, video, watch, when, withSSR, writable };