sibujs 4.0.1 → 4.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 (80) hide show
  1. package/README.md +29 -1
  2. package/dist/browser.cjs +21 -22
  3. package/dist/browser.js +6 -6
  4. package/dist/build.cjs +363 -110
  5. package/dist/build.js +15 -15
  6. package/dist/cdn.dev.global.js +398 -0
  7. package/dist/cdn.global.js +10 -10
  8. package/dist/{chunk-AD5YSZAN.js → chunk-2LOYNGBY.js} +2 -2
  9. package/dist/{chunk-VPP2FONR.js → chunk-2WLZ6757.js} +17 -10
  10. package/dist/{chunk-UNAYBND2.js → chunk-3VTSPNC5.js} +2 -2
  11. package/dist/{chunk-TIRZCERI.js → chunk-5DXA2J44.js} +1 -1
  12. package/dist/{chunk-QD4I7ZGZ.js → chunk-6ZGI3RBE.js} +149 -33
  13. package/dist/{chunk-JYXQCELN.js → chunk-7ALC7KGE.js} +13 -7
  14. package/dist/{chunk-XDOMTQHM.js → chunk-7VEC3UD2.js} +3 -3
  15. package/dist/{chunk-PTLJ7NQX.js → chunk-BMYYZWHX.js} +3 -3
  16. package/dist/{chunk-PHB3MCK4.js → chunk-BNKV4REK.js} +7 -8
  17. package/dist/{chunk-XET2RUJF.js → chunk-FOWBJZDL.js} +7 -8
  18. package/dist/{chunk-5BVKQX2F.js → chunk-GDUNQCCW.js} +4 -4
  19. package/dist/{chunk-TVURNWDK.js → chunk-HECVW343.js} +20 -9
  20. package/dist/{chunk-PHRH66Y7.js → chunk-HJEPKZ2O.js} +7 -8
  21. package/dist/{chunk-PJDSI2K7.js → chunk-HZEBO22J.js} +125 -42
  22. package/dist/{chunk-5INI7D2L.js → chunk-IXKSNWV5.js} +74 -11
  23. package/dist/{chunk-IKVUUE7D.js → chunk-KE4KWRDY.js} +3 -3
  24. package/dist/{chunk-AW6HPJ3L.js → chunk-KKLW7YWL.js} +1 -1
  25. package/dist/{chunk-XZMCVZW6.js → chunk-NEYWS4EX.js} +6 -6
  26. package/dist/{chunk-HYCCIYNS.js → chunk-NIOYEGBQ.js} +13 -14
  27. package/dist/{chunk-YLV26GAJ.js → chunk-OUWAIXIK.js} +3 -3
  28. package/dist/{chunk-E72G5JSK.js → chunk-P3DHXBR6.js} +1 -1
  29. package/dist/{chunk-J4WZMCIW.js → chunk-QCFLDD7C.js} +5 -5
  30. package/dist/{chunk-DHDZ7IVN.js → chunk-QKRPLZ2V.js} +7 -8
  31. package/dist/{chunk-52ERIVFD.js → chunk-QL4UREZO.js} +6 -6
  32. package/dist/{chunk-TNEHT3NG.js → chunk-SF3TB4K6.js} +8 -8
  33. package/dist/{chunk-HPXTUPJD.js → chunk-XZ2N525U.js} +6 -6
  34. package/dist/{customElement-CKLoO63w.d.ts → customElement-CNZxEB9G.d.cts} +77 -0
  35. package/dist/{customElement-CKLoO63w.d.cts → customElement-CNZxEB9G.d.ts} +77 -0
  36. package/dist/data.cjs +23 -25
  37. package/dist/data.js +9 -9
  38. package/dist/devtools.cjs +26 -23
  39. package/dist/devtools.d.cts +1 -1
  40. package/dist/devtools.d.ts +1 -1
  41. package/dist/devtools.js +8 -8
  42. package/dist/{dispose-4FZ6OFXR.js → dispose-46BOMMQJ.js} +2 -2
  43. package/dist/ecosystem.cjs +230 -68
  44. package/dist/ecosystem.js +11 -11
  45. package/dist/extras.cjs +251 -87
  46. package/dist/extras.d.cts +2 -2
  47. package/dist/extras.d.ts +2 -2
  48. package/dist/extras.js +25 -25
  49. package/dist/index.cjs +358 -104
  50. package/dist/index.d.cts +287 -34
  51. package/dist/index.d.ts +287 -34
  52. package/dist/index.js +17 -15
  53. package/dist/{introspect-RjLfIFpL.d.cts → introspect-D5xF-0Es.d.cts} +7 -0
  54. package/dist/{introspect-DOZfmC-4.d.ts → introspect-rSBIxEQi.d.ts} +7 -0
  55. package/dist/motion.cjs +21 -23
  56. package/dist/motion.js +5 -5
  57. package/dist/patterns.cjs +24 -25
  58. package/dist/patterns.d.cts +8 -0
  59. package/dist/patterns.d.ts +8 -0
  60. package/dist/patterns.js +7 -7
  61. package/dist/performance.cjs +24 -28
  62. package/dist/performance.js +9 -9
  63. package/dist/plugins.cjs +268 -87
  64. package/dist/plugins.d.cts +92 -0
  65. package/dist/plugins.d.ts +92 -0
  66. package/dist/plugins.js +18 -17
  67. package/dist/{ssr-JFP5535P.js → ssr-Y7XOEPEN.js} +5 -5
  68. package/dist/ssr.cjs +241 -80
  69. package/dist/ssr.js +12 -12
  70. package/dist/testing.cjs +19 -20
  71. package/dist/testing.js +5 -5
  72. package/dist/ui.cjs +106 -44
  73. package/dist/ui.d.cts +7 -1
  74. package/dist/ui.d.ts +7 -1
  75. package/dist/ui.js +10 -10
  76. package/dist/widgets.cjs +21 -22
  77. package/dist/widgets.d.cts +8 -0
  78. package/dist/widgets.d.ts +8 -0
  79. package/dist/widgets.js +9 -9
  80. package/package.json +11 -2
package/dist/index.d.cts CHANGED
@@ -193,9 +193,15 @@ type RuntimeErrorHandler = (error: unknown, context: RuntimeErrorContext) => voi
193
193
  * This is application/runtime-global state, NOT request-scoped: under SSR it is
194
194
  * shared by every concurrent request in the process. Install it once at startup
195
195
  * and derive any request-specific detail from the error/context instead.
196
+ *
197
+ * @returns The previously installed handler, so a caller can restore it.
196
198
  */
197
199
  declare function setRuntimeErrorHandler(handler: RuntimeErrorHandler | null): RuntimeErrorHandler | null;
198
- /** The currently installed runtime error handler, if any. */
200
+ /**
201
+ * Read the currently installed runtime error handler.
202
+ *
203
+ * @returns The installed handler, or `null` when none is set.
204
+ */
199
205
  declare function getRuntimeErrorHandler(): RuntimeErrorHandler | null;
200
206
  /**
201
207
  * Report an error the runtime caught and contained.
@@ -440,45 +446,30 @@ declare function __resetIdCounter(): void;
440
446
  * Conditional rendering directive. Shows or hides an element reactively.
441
447
  * Unlike `when()`, the element is always created — it just toggles display.
442
448
  *
449
+ * Accepts the element either directly or as a thunk, so the same call shape
450
+ * works here and in {@link when}. Which one you pass changes nothing: `show`
451
+ * never rebuilds, so a thunk is invoked exactly once, immediately.
452
+ *
443
453
  * @param condition Reactive getter returning boolean
444
- * @param element The element to show/hide
445
- * @returns The element with reactive display binding
454
+ * @param element The element to show/hide, or a function returning it
455
+ * @returns The element itself (not a wrapper), with a reactive display binding.
456
+ * The caller keeps the live node and can mutate it or attach listeners to it.
446
457
  *
447
458
  * @example
448
459
  * ```ts
449
460
  * const [visible, setVisible] = signal(true);
450
461
  * div([show(() => visible(), span("I toggle!"))]);
451
- * ```
452
- */
453
- declare function show<T extends Element>(condition: () => boolean, element: T): T;
454
- /**
455
- * Conditional rendering directive. Renders content only when condition is true.
456
- * When false, renders nothing (comment placeholder). Re-evaluates reactively.
457
- *
458
- * @param condition Reactive getter returning boolean
459
- * @param thenBranch Function returning element when true
460
- * @param elseBranch Optional function returning element when false
461
- * @returns A Comment anchor that manages the conditional content
462
- *
463
- * @example
464
- * ```ts
465
- * when(
466
- * () => isLoggedIn(),
467
- * () => div("Welcome!"),
468
- * () => div("Please log in")
469
- * );
462
+ * div([show(() => visible(), () => span("also fine"))]);
470
463
  * ```
471
464
  *
472
- * GOTCHAbranch factories rebuild only when `condition` changes. A signal
473
- * read *eagerly* inside a branch is captured once and never updates:
474
- * ```ts
475
- * when(() => show(), () => div(`Count: ${count()}`)); // frozen at first count
476
- * when(() => show(), () => div(() => `Count: ${count()}`)); // ✓ reactive text child
477
- * ```
478
- * Drive per-branch reactivity with a nested getter (or a reactive child), not a
479
- * bare read in the factory body.
465
+ * TRAP`show` keeps the element in the DOM and toggles `display`. Use
466
+ * {@link when} when the content must not exist at all while hidden (an
467
+ * expensive subtree, or one whose mere presence is observable). Note that
468
+ * `when` REBUILDS on every condition change, which discards focus and
469
+ * selection inside the branch see the note on `when`.
480
470
  */
481
- declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBranch?: () => NodeChild): Comment;
471
+ declare function show<T extends Element>(condition: () => boolean, element: T | (() => T)): T;
472
+ declare function when<T>(condition: () => T, thenBranch: NodeChild, elseBranch?: NodeChild): Comment;
482
473
  /**
483
474
  * Pattern matching directive. Renders content based on matching a reactive value
484
475
  * against multiple cases. Similar to a switch statement.
@@ -504,8 +495,25 @@ declare function when<T>(condition: () => T, thenBranch: () => NodeChild, elseBr
504
495
  * GOTCHA — like `when()`, a case factory rebuilds only when the matched key
505
496
  * changes. A signal read eagerly inside a case is frozen at build time; use a
506
497
  * nested getter (`() => div(() => label())`) for reactive per-case content.
498
+ *
499
+ * THIS IS THE KEYING PATTERN for subtrees that hold a live edit. Because the
500
+ * subtree is rebuilt only when the KEY changes — not on every read of every
501
+ * signal inside it — a form can update its contents through reactive attributes
502
+ * and text children while its inputs keep focus, selection and IME state:
503
+ *
504
+ * ```ts
505
+ * // Rebuilds only when the form's shape changes, not on every keystroke.
506
+ * match(
507
+ * () => `${entity()}:${mode()}`,
508
+ * { "user:edit": () => UserForm(), "user:view": () => UserCard() },
509
+ * );
510
+ * ```
511
+ *
512
+ * Cases and the fallback accept a bare element as well as a factory, matching
513
+ * {@link when} and {@link show}. A bare element is re-attached rather than
514
+ * rebuilt, so it keeps whatever state it accumulated.
507
515
  */
508
- declare function match<T extends string | number>(value: () => T, cases: Record<string, () => NodeChild>, fallback?: () => NodeChild): Comment;
516
+ declare function match<T extends string | number>(value: () => T, cases: Record<string, NodeChild>, fallback?: NodeChild): Comment;
509
517
 
510
518
  /**
511
519
  * Safety ceiling on the **total number of teardowns a single drain may execute**.
@@ -582,7 +590,9 @@ declare function replaceChildrenSafely(parent: ParentNode, ...next: Node[]): voi
582
590
  /**
583
591
  * Check for potential binding leaks. Returns the number of active DOM bindings.
584
592
  * In dev mode, logs a warning if the count exceeds the threshold.
585
- * In production, _isDev is false so the counter is always 0.
593
+ * In production, DEV is false so the counter is always 0.
594
+ *
595
+ * @returns Diagnostic counts of nodes still holding registered disposers.
586
596
  */
587
597
  declare function checkLeaks(warnThreshold?: number): number;
588
598
 
@@ -699,6 +709,9 @@ declare function Fragment(nodes: NodeChildren[]): DocumentFragment;
699
709
  * Templates are parsed once per call site and cached. Subsequent calls at the
700
710
  * same source location skip parsing entirely and only replay the cached
701
711
  * structure with fresh expression values.
712
+ *
713
+ * @returns The live DOM node (or fragment) the template describes — not a
714
+ * string, and not a virtual node.
702
715
  */
703
716
  declare function html(strings: TemplateStringsArray, ...values: unknown[]): Element;
704
717
 
@@ -1004,6 +1017,15 @@ interface KeepAliveOptions {
1004
1017
  */
1005
1018
  declare function KeepAlive(activeKey: () => string, cases: Record<string, () => Node>, options?: KeepAliveOptions): Comment;
1006
1019
 
1020
+ /**
1021
+ * Read and CLEAR an error stashed on a node by an async boundary.
1022
+ *
1023
+ * Consuming is the point: an error handed to one boundary must not also reach
1024
+ * an outer one, so this is deliberately not a plain getter.
1025
+ *
1026
+ * @param node Element the error was stashed on.
1027
+ * @returns The pending error, or `undefined` when there is none.
1028
+ */
1007
1029
  declare function takePendingError(node: Element): Error | undefined;
1008
1030
  type Component = () => HTMLElement;
1009
1031
  type LazyImport = () => Promise<{
@@ -1051,6 +1073,18 @@ interface SuspenseProps {
1051
1073
  nodes: () => HTMLElement;
1052
1074
  fallback: () => HTMLElement;
1053
1075
  }
1076
+ /**
1077
+ * Render `fallback` until the async `nodes` factory resolves, then swap it for
1078
+ * the resolved content.
1079
+ *
1080
+ * @param props `nodes` is the async content factory; `fallback` builds the
1081
+ * placeholder shown while it is pending.
1082
+ * @returns The live container element, already holding the fallback.
1083
+ *
1084
+ * TRAP — the swap REPLACES the container's children, so focus and selection in
1085
+ * the fallback are discarded. That is usually harmless (a spinner rarely holds
1086
+ * focus) but matters for a skeleton form; see the note on `when`.
1087
+ */
1054
1088
  declare function Suspense({ nodes, fallback }: SuspenseProps): HTMLElement;
1055
1089
 
1056
1090
  /**
@@ -1119,6 +1153,11 @@ declare function onCleanup(callback: CleanupFn, element: Node): void;
1119
1153
  /**
1120
1154
  * Mounts a root component into a DOM element.
1121
1155
  * Supports both function components and pre-created HTMLElements.
1156
+ *
1157
+ * @param component Component function, or an already-built Element/Node.
1158
+ * @param container Element to mount into.
1159
+ * @returns `{ node, unmount }` — the live root node, and a teardown that
1160
+ * disposes the tree and removes it.
1122
1161
  */
1123
1162
  declare function mount(component: (() => Element) | Element | Node, container: Element | null): {
1124
1163
  node: Node;
@@ -1151,6 +1190,13 @@ declare function Portal(nodes: () => HTMLElement, target?: HTMLElement): Comment
1151
1190
 
1152
1191
  type SlotFn = () => Element | string | number | null | undefined;
1153
1192
  type Slots = Record<string, SlotFn>;
1193
+ /**
1194
+ * Look up a named slot factory.
1195
+ *
1196
+ * @param slots The slot map, which may be undefined.
1197
+ * @param name Slot name; defaults to `"default"`.
1198
+ * @returns The slot factory, or `undefined` when the slot was not provided.
1199
+ */
1154
1200
  declare function getSlot(slots: Slots | undefined, name?: string): SlotFn | undefined;
1155
1201
 
1156
1202
  declare const __accessor: unique symbol;
@@ -1184,8 +1230,74 @@ interface SignalOptions<T = unknown> {
1184
1230
  *
1185
1231
  * @param initial Initial value
1186
1232
  * @param options Optional config: `{ name: "count" }` for devtools labeling
1233
+ * @returns A `[getter, setter]` tuple. Calling the getter inside a reactive
1234
+ * context subscribes to the signal; the setter accepts a value or an updater.
1187
1235
  */
1188
1236
  declare function signal<T>(initial: T, options?: SignalOptions<T>): StateTuple<T>;
1237
+ /**
1238
+ * A valueless reactive token standing in for state SibuJS does not own.
1239
+ *
1240
+ * See {@link external}.
1241
+ */
1242
+ interface ExternalSource {
1243
+ /**
1244
+ * Declare, from inside a reactive computation, that it reads the external
1245
+ * state this source represents. Call it in the same places you would read a
1246
+ * signal — the top of a binding getter, a `derived()` body, an `effect()`.
1247
+ *
1248
+ * Outside a tracking context it is a no-op, exactly like reading a signal.
1249
+ */
1250
+ track(): void;
1251
+ /**
1252
+ * Declare that the external state changed. Every consumer that called
1253
+ * {@link ExternalSource.track} is invalidated.
1254
+ *
1255
+ * Participates in `batch()` like any signal write: inside a batch, consumers
1256
+ * are notified once when the outermost batch flushes.
1257
+ */
1258
+ invalidate(): void;
1259
+ }
1260
+ /**
1261
+ * Create a reactive source for state that lives outside SibuJS — a domain
1262
+ * engine, a media element, a canvas scene, an editor document, a cache a
1263
+ * socket writes into.
1264
+ *
1265
+ * The pattern is two lines: `track()` where you read, `invalidate()` after you
1266
+ * mutate.
1267
+ *
1268
+ * ```ts
1269
+ * import { Chess } from "chess.js";
1270
+ * import { external } from "sibujs";
1271
+ *
1272
+ * const game = new Chess(); // owns the rules and the mutable state
1273
+ * const moved = external(); // owns "something changed"
1274
+ *
1275
+ * ctx.text("@status", () => {
1276
+ * moved.track(); // this binding reads the engine
1277
+ * return game.isCheckmate() ? "Checkmate" : `${game.turn()} to move`;
1278
+ * });
1279
+ *
1280
+ * game.move({ from: "e2", to: "e4" });
1281
+ * moved.invalidate(); // every consumer above re-reads
1282
+ * ```
1283
+ *
1284
+ * **One source is one invalidation domain.** Every consumer of a source
1285
+ * re-runs on every `invalidate()`, so the granularity of your updates is
1286
+ * exactly the granularity of your sources: one for a whole engine is the
1287
+ * cheapest to write, several (`board`, `clock`, `history`) let an update touch
1288
+ * only what it affects. See `docs/architecture/external-state.md` for the
1289
+ * trade-offs and when subdividing is worth it.
1290
+ *
1291
+ * Ownership, disposal and error routing are the consumer's, not the source's:
1292
+ * a disposed binding or effect is never invalidated, and a consumer that
1293
+ * throws is reported through the normal runtime error pipeline with its own
1294
+ * phase and node.
1295
+ *
1296
+ * @param options `name` labels the source in devtools (development only).
1297
+ */
1298
+ declare function external(options?: {
1299
+ name?: string;
1300
+ }): ExternalSource;
1189
1301
 
1190
1302
  /**
1191
1303
  * Reactive array hook. Provides common array operations that
@@ -1234,6 +1346,14 @@ interface ArrayActions<T> {
1234
1346
  /** Clear all items */
1235
1347
  clear(): void;
1236
1348
  }
1349
+ /**
1350
+ * A signal holding an array, plus mutation helpers that write a NEW array each
1351
+ * time so the signal actually notifies.
1352
+ *
1353
+ * @param initial Starting items; copied, so the caller's array is not aliased.
1354
+ * @returns A `[accessor, actions]` pair — `actions` carries `push`, `remove`,
1355
+ * `insert`, `move`, `clear` and friends.
1356
+ */
1237
1357
  declare function array<T>(initial?: T[]): [Accessor<T[]>, ArrayActions<T>];
1238
1358
  /**
1239
1359
  * Optimized reactive array hook. Uses in-place mutations with a version
@@ -1342,6 +1462,9 @@ interface AsyncDerivedContext {
1342
1462
  * return r.json();
1343
1463
  * }, []);
1344
1464
  * ```
1465
+ *
1466
+ * @returns An accessor exposing the async result along with its loading and
1467
+ * error state.
1345
1468
  */
1346
1469
  declare function asyncDerived<T>(factory: (context: AsyncDerivedContext) => Promise<T>, initial: T): AsyncDerivedState<T>;
1347
1470
 
@@ -1403,6 +1526,9 @@ declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T)
1403
1526
  * That keeps the cheap boolean dirty flag AND makes `equals` actually stop
1404
1527
  * propagation, with recomputation still fully lazy: `_validate` only ever runs
1405
1528
  * when an effect is genuinely about to observe the value.
1529
+ *
1530
+ * @returns An accessor for the computed value. It recomputes lazily on read
1531
+ * after any dependency changes.
1406
1532
  */
1407
1533
  declare function derived<T>(getter: () => T, options?: {
1408
1534
  name?: string;
@@ -1458,6 +1584,9 @@ type EffectBody = (onCleanup: OnCleanup) => void;
1458
1584
  * onCleanup(() => window.removeEventListener("resize", handler));
1459
1585
  * });
1460
1586
  * ```
1587
+ *
1588
+ * @returns A dispose function that stops the effect and releases its
1589
+ * dependencies. Idempotent.
1461
1590
  */
1462
1591
  declare function effect(effectFn: EffectBody | (() => void), options?: EffectOptions): () => void;
1463
1592
 
@@ -1565,6 +1694,8 @@ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefi
1565
1694
  * lastName(); // "Smith"
1566
1695
  * fullName(); // "Jane Smith"
1567
1696
  * ```
1697
+ *
1698
+ * @returns A store handle with `subscribe`, `set` and `update`.
1568
1699
  */
1569
1700
  declare function writable<T>(get: () => T, set: (value: T) => void, options?: {
1570
1701
  name?: string;
@@ -1683,6 +1814,37 @@ declare function strict<T>(fn: () => T): T;
1683
1814
  */
1684
1815
  declare function strictEffect(fn: () => void): () => void;
1685
1816
 
1817
+ /** Event handlers for {@link EachBindings}, typed per event name. */
1818
+ type EachEventBindings = {
1819
+ [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K], el: HTMLElement) => void;
1820
+ };
1821
+ /**
1822
+ * What {@link EnhanceContext.each} attaches to one element.
1823
+ *
1824
+ * Every field maps one-to-one onto an existing `ctx.*` helper and is committed
1825
+ * through it — this is a shorthand for calls you could write by hand, not a
1826
+ * template language and not a second binding engine. There is no expression
1827
+ * parsing, no string interpolation and no `eval`: every value is a plain
1828
+ * function you wrote, so it stays CSP-safe and fully type-checked.
1829
+ *
1830
+ * Anything not covered here (two-way `model()`, listener options, a nested
1831
+ * `enhance`) is written imperatively in the same callback — it receives the
1832
+ * element, so `ctx.model(el, …)` beside a returned descriptor is normal.
1833
+ */
1834
+ interface EachBindings {
1835
+ /** Reactive `textContent` — same as `ctx.text(el, value)`. */
1836
+ text?: () => unknown;
1837
+ /** Reactive attributes by name — same as `ctx.attr(el, name, value)`. */
1838
+ attr?: Record<string, () => unknown>;
1839
+ /** Reactive class toggles by class name — same as `ctx.classed(el, name, on)`. */
1840
+ class?: Record<string, () => boolean>;
1841
+ /** Reactive visibility — same as `ctx.show(el, when)`. */
1842
+ show?: () => boolean;
1843
+ /** Event listeners by event name — same as `ctx.on(el, event, handler)`. */
1844
+ on?: EachEventBindings;
1845
+ /** Per-element teardown, run with the rest of the enhancement's cleanups. */
1846
+ cleanup?: () => void;
1847
+ }
1686
1848
  /**
1687
1849
  * Helpers handed to an `enhance` setup. Every binding is fine-grained (its own
1688
1850
  * effect) and auto-disposed when the root element (or the returned dispose) is
@@ -1715,6 +1877,43 @@ interface EnhanceContext {
1715
1877
  model<T>(target: string | Element, state: readonly [() => T, (value: T) => void], options?: {
1716
1878
  event?: string;
1717
1879
  }): void;
1880
+ /**
1881
+ * Bind a set of elements the server already rendered — a board, a table, a
1882
+ * keyboard, a timeline, a legend — one descriptor at a time.
1883
+ *
1884
+ * The callback receives each element and its index and returns what to
1885
+ * attach; every field is committed through the matching `ctx.*` helper, so
1886
+ * ownership, disposal, write elision, attribute sanitization and error
1887
+ * routing are byte-for-byte the same as writing the calls out by hand. No
1888
+ * element is created, replaced, moved or re-parented — node identity is
1889
+ * preserved, which is the entire point of enhancing existing markup.
1890
+ *
1891
+ * ```ts
1892
+ * ctx.each<HTMLButtonElement>("@square", (el) => {
1893
+ * const square = el.dataset.square as Square;
1894
+ * return {
1895
+ * text: () => pieceAt(square),
1896
+ * class: { selected: () => selected() === square },
1897
+ * attr: { "aria-label": () => describe(square) },
1898
+ * on: { click: () => choose(square) },
1899
+ * };
1900
+ * });
1901
+ * ```
1902
+ *
1903
+ * The callback may also return nothing and wire the element imperatively —
1904
+ * `ctx.model(el, …)`, `ctx.on(el, "click", h, { passive: true })` — for the
1905
+ * cases the descriptor deliberately does not cover.
1906
+ *
1907
+ * Zero matches is a silent no-op. Calling `each` twice over the same
1908
+ * elements creates two independent sets of bindings, exactly as calling
1909
+ * `ctx.text()` twice on one node does; the helper is sugar over those calls
1910
+ * and does not track what a previous call attached.
1911
+ *
1912
+ * @param target A `@ref`/CSS selector resolved with {@link EnhanceContext.refs},
1913
+ * or any iterable of elements (an array, a `NodeList`, an `HTMLCollection`).
1914
+ * @param describe Called once per element, in document order.
1915
+ */
1916
+ each<T extends Element = HTMLElement>(target: string | Iterable<Element>, describe: (element: T, index: number) => EachBindings | void): void;
1718
1917
  /** Register arbitrary teardown to run on disposal. */
1719
1918
  cleanup(fn: () => void): void;
1720
1919
  }
@@ -1974,20 +2173,74 @@ declare function transition(): TransitionState;
1974
2173
  declare function nextTick(): Promise<void>;
1975
2174
 
1976
2175
  type Subscriber = () => void;
2176
+ /**
2177
+ * Stop recording dependency edges until the matching {@link resumeTracking}.
2178
+ * Nestable — the depth is counted, so an inner pair does not resume early.
2179
+ *
2180
+ * Prefer {@link untracked}, which pairs the two calls for you even when the
2181
+ * body throws. Reach for the raw pair only when the suspended region cannot be
2182
+ * expressed as a single function.
2183
+ *
2184
+ * @returns Nothing.
2185
+ */
1977
2186
  declare function suspendTracking(): void;
2187
+ /**
2188
+ * Undo one {@link suspendTracking}. Dependency recording resumes only when the
2189
+ * outermost pair closes.
2190
+ *
2191
+ * @returns Nothing.
2192
+ */
1978
2193
  declare function resumeTracking(): void;
1979
2194
  /** Read the "tracking suspended" flag (used by derived's lazy path). */
1980
2195
  declare function isTrackingSuspended(): boolean;
2196
+ /**
2197
+ * Run `fn` without recording anything it reads as a dependency.
2198
+ *
2199
+ * The suspension is released even if `fn` throws, which is the reason to prefer
2200
+ * this over a manual suspend/resume pair.
2201
+ *
2202
+ * @param fn Function to run outside dependency tracking.
2203
+ * @returns Whatever `fn` returns.
2204
+ */
1981
2205
  declare function untracked$1<T>(fn: () => T): T;
1982
2206
  declare function retrack$1(effectFn: () => void, subscriber: Subscriber): void;
1983
2207
  declare function track(effectFn: () => void, subscriber?: Subscriber): () => void;
1984
2208
  declare function reactiveBinding(commit: () => void, ownerNode?: unknown): () => void;
1985
2209
  declare function recordDependency(signal: ReactiveSignal): void;
1986
2210
  declare function cleanup(subscriber: Subscriber): void;
2211
+ /**
2212
+ * Cap how many times one subscriber may re-run within a single drain before it
2213
+ * is quarantined as a runaway cycle. A safety valve, not a tuning knob.
2214
+ *
2215
+ * @param n New limit; ignored unless finite and positive.
2216
+ * @returns The previous limit, so a caller can restore it.
2217
+ */
1987
2218
  declare function setMaxSubscriberRepeats(n: number): number;
2219
+ /**
2220
+ * Cap how many passes one notification drain may make before giving up on a
2221
+ * self-refeeding update cycle.
2222
+ *
2223
+ * @param n New limit; ignored unless finite and positive.
2224
+ * @returns The previous limit, so a caller can restore it.
2225
+ */
1988
2226
  declare function setMaxDrainIterations$1(n: number): number;
2227
+ /**
2228
+ * Flush queued signal notifications, running every dirty subscriber.
2229
+ *
2230
+ * Re-entrant calls are ignored: a write performed from inside a subscriber adds
2231
+ * to the queue the current drain is already working through, rather than
2232
+ * starting a nested one.
2233
+ *
2234
+ * @returns Nothing.
2235
+ */
1989
2236
  declare function drainNotificationQueue(): void;
1990
2237
  declare function queueSignalNotification(signal: ReactiveSignal): void;
2238
+ /**
2239
+ * Mark every subscriber of `signal` dirty and queue them for the next drain.
2240
+ *
2241
+ * @param signal The signal that changed.
2242
+ * @returns Nothing.
2243
+ */
1991
2244
  declare function notifySubscribers(signal: ReactiveSignal): void;
1992
2245
  /** O(1) subscriber count for devtools / introspection. */
1993
2246
  declare function getSubscriberCount(signal: ReactiveSignal): number;
@@ -2021,4 +2274,4 @@ declare const untracked: ReactiveApi["untracked"];
2021
2274
  declare const retrack: ReactiveApi["retrack"];
2022
2275
  declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
2023
2276
 
2024
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, type EnhanceContext, type EnhanceSetup, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, type IslandLoader, type IslandRegistration, type IslandStrategy, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, MAX_DRAIN_TEARDOWNS, type MediaProps, type MountIslandsOptions, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type RuntimeErrorContext, type RuntimeErrorHandler, type RuntimeErrorPhase, 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, enhance, enhanceAll, 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, isSSR, kbd, label, lazy, lazyIsland, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, mountIslands, 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, registerIsland, replaceChildrenSafely, reportDrainRunaway, reportError, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, setRuntimeErrorHandler, 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$1 as track, transition, trapFocus, tspan, u, ul, unregisterComponent, unregisterDisposer, unregisterIsland, untracked, use, var_, video, watch, when, withSSR, writable };
2277
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EachBindings, type EachEventBindings, type EffectBody, type EffectOptions, type EnhanceContext, type EnhanceSetup, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type ExternalSource, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, type IslandLoader, type IslandRegistration, type IslandStrategy, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, MAX_DRAIN_TEARDOWNS, type MediaProps, type MountIslandsOptions, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type RuntimeErrorContext, type RuntimeErrorHandler, type RuntimeErrorPhase, 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, enhance, enhanceAll, enqueueBatchedSignal, external, 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, isSSR, kbd, label, lazy, lazyIsland, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, mountIslands, 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, registerIsland, replaceChildrenSafely, reportDrainRunaway, reportError, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, setRuntimeErrorHandler, 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$1 as track, transition, trapFocus, tspan, u, ul, unregisterComponent, unregisterDisposer, unregisterIsland, untracked, use, var_, video, watch, when, withSSR, writable };