sibujs 4.4.0 → 4.5.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 (74) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +31 -2
  3. package/dist/browser.d.cts +15 -2
  4. package/dist/browser.d.ts +15 -2
  5. package/dist/browser.js +4 -4
  6. package/dist/build.cjs +92 -13
  7. package/dist/build.js +10 -10
  8. package/dist/cdn.dev.global.js +9 -9
  9. package/dist/cdn.full.dev.global.js +10 -10
  10. package/dist/cdn.full.global.js +9 -9
  11. package/dist/cdn.global.js +9 -9
  12. package/dist/{chunk-XLO7SLIX.js → chunk-2DCGACUU.js} +16 -9
  13. package/dist/{chunk-6GSIXTWX.js → chunk-3JZ4L5TJ.js} +10 -4
  14. package/dist/{chunk-UCALUKB5.js → chunk-4PMLNECI.js} +4 -4
  15. package/dist/{chunk-RR7M3FHM.js → chunk-7LN645I6.js} +1 -1
  16. package/dist/{chunk-SKPTERHP.js → chunk-B3WHI2QA.js} +1 -1
  17. package/dist/{chunk-4Z3SPQ67.js → chunk-FTIR4QW2.js} +13 -3
  18. package/dist/{chunk-HFCOH2GN.js → chunk-GW3SCCZG.js} +4 -4
  19. package/dist/{chunk-PNIRUQ4C.js → chunk-KEISJXBU.js} +2 -2
  20. package/dist/{chunk-LYVUX7NT.js → chunk-OMJJM3KM.js} +2 -2
  21. package/dist/{chunk-VCTAEPSB.js → chunk-ONOHFDLG.js} +9 -1
  22. package/dist/{chunk-36S2YPP4.js → chunk-PCT43HW3.js} +1 -1
  23. package/dist/{chunk-W2EQ7X2L.js → chunk-RBTPLM32.js} +46 -8
  24. package/dist/{chunk-7GIHSAWB.js → chunk-RIXRAYIU.js} +1 -1
  25. package/dist/{chunk-HQSEH5F6.js → chunk-RJE2BNI4.js} +3 -3
  26. package/dist/{chunk-ADM46X22.js → chunk-S373NSMK.js} +18 -13
  27. package/dist/{chunk-M2F7TZHH.js → chunk-TBYTO6BS.js} +2 -2
  28. package/dist/{chunk-3BTTCZ5J.js → chunk-UGRX3S57.js} +31 -2
  29. package/dist/{chunk-7NBDXVHS.js → chunk-VUF4ALSW.js} +1 -1
  30. package/dist/{chunk-LQFGQNMV.js → chunk-VZKNK2V7.js} +9 -4
  31. package/dist/{chunk-NVNJH22U.js → chunk-XZZOBQAY.js} +1 -1
  32. package/dist/{customElement-CNZxEB9G.d.ts → customElement-OB9CIsc5.d.cts} +38 -10
  33. package/dist/{customElement-CNZxEB9G.d.cts → customElement-OB9CIsc5.d.ts} +38 -10
  34. package/dist/data.cjs +81 -9
  35. package/dist/data.js +6 -6
  36. package/dist/devtools.cjs +41 -2
  37. package/dist/devtools.js +4 -4
  38. package/dist/ecosystem.cjs +83 -9
  39. package/dist/ecosystem.d.cts +13 -5
  40. package/dist/ecosystem.d.ts +13 -5
  41. package/dist/ecosystem.js +7 -7
  42. package/dist/extras.cjs +115 -14
  43. package/dist/extras.d.cts +2 -2
  44. package/dist/extras.d.ts +2 -2
  45. package/dist/extras.js +19 -19
  46. package/dist/index.cjs +92 -13
  47. package/dist/index.d.cts +24 -151
  48. package/dist/index.d.ts +24 -151
  49. package/dist/index.js +10 -10
  50. package/dist/motion.cjs +31 -2
  51. package/dist/motion.js +3 -3
  52. package/dist/patterns.cjs +81 -10
  53. package/dist/patterns.d.cts +5 -0
  54. package/dist/patterns.d.ts +5 -0
  55. package/dist/patterns.js +5 -5
  56. package/dist/performance.cjs +31 -2
  57. package/dist/performance.js +4 -4
  58. package/dist/plugins.cjs +39 -2
  59. package/dist/plugins.js +6 -6
  60. package/dist/ssr.cjs +39 -2
  61. package/dist/ssr.js +7 -7
  62. package/dist/tagFactory-DVoDpHye.d.cts +215 -0
  63. package/dist/tagFactory-DVoDpHye.d.ts +215 -0
  64. package/dist/testing.cjs +31 -2
  65. package/dist/testing.js +2 -2
  66. package/dist/ui.cjs +93 -13
  67. package/dist/ui.d.cts +1 -1
  68. package/dist/ui.d.ts +1 -1
  69. package/dist/ui.js +6 -6
  70. package/dist/widgets.cjs +75 -9
  71. package/dist/widgets.js +6 -6
  72. package/package.json +2 -2
  73. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  74. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { N as NodeChild, a as NodeChildren, T as TagProps } from './tagFactory-Bzupt4Pj.cjs';
2
- export { D as Dispose, S as SVG_NS, t as tagFactory } from './tagFactory-Bzupt4Pj.cjs';
1
+ import { N as NodeChild, a as NodeChildren, T as TagProps, A as Accessor, D as DerivedAccessor } from './tagFactory-DVoDpHye.cjs';
2
+ export { b as Dispose, E as ExternalSource, S as SVG_NS, c as SignalOptions, d as derived, e as external, s as signal, t as tagFactory } from './tagFactory-DVoDpHye.cjs';
3
3
  export { T as TrustedHTML, t as trustHTML } from './ssr-BiPRdZ6n.cjs';
4
4
  import { R as ReactiveSignal } from './signal-BnWpq6WB.cjs';
5
5
 
@@ -667,12 +667,22 @@ declare function DynamicComponent(is: () => string | Component$1): HTMLElement;
667
667
  * assigns that row a new item or position.
668
668
  *
669
669
  * Identity is not value freshness: a row keeps its DOM node when its key is
670
- * unchanged, and still updates its contents when the item behind that key is
671
- * replaced.
672
- *
673
- * Reading `item()` subscribes to the ROW's cell, not to the whole-array signal,
674
- * so a row only re-renders when its own item/index actually changes — mutating
675
- * an unrelated row does not disturb it.
670
+ * unchanged, and its bindings still update when the item behind that key is
671
+ * replaced — provided they read the getter rather than a value captured when
672
+ * `render` ran.
673
+ *
674
+ * `render` runs untracked, whether the row appears in the first pass or in a
675
+ * later update: a signal read directly in its body — `item()` and `index()`
676
+ * included — is a one-time read that never subscribes anything and goes stale
677
+ * when the key later receives a replacement item or moves. Pass the getters
678
+ * down (`Row({ item, index })`) or read them inside bindings
679
+ * (`() => item().name`) and effects created by the row.
680
+ *
681
+ * A binding that reads `item()` subscribes to the ROW's cell, not to the
682
+ * whole-array signal. `render` never re-runs for an existing key; only the
683
+ * bindings and effects that read that row's `item()` / `index()` re-run, and
684
+ * only when its own item or position actually changes — mutating an unrelated
685
+ * row does not disturb it.
676
686
  *
677
687
  * @param getArray A reactive getter returning an array.
678
688
  * @param render A function that receives reactive item and index getters and returns a NodeChild.
@@ -1199,106 +1209,6 @@ type Slots = Record<string, SlotFn>;
1199
1209
  */
1200
1210
  declare function getSlot(slots: Slots | undefined, name?: string): SlotFn | undefined;
1201
1211
 
1202
- declare const __accessor: unique symbol;
1203
- /**
1204
- * A reactive signal getter returned by signal(), derived(), and similar primitives.
1205
- *
1206
- * Pass an Accessor directly into reactive prop positions — never call it there:
1207
- * ```ts
1208
- * const [count, setCount] = signal(0);
1209
- *
1210
- * div(count) // ✓ reactive — Accessor passed directly
1211
- * div(() => count()) // ✓ reactive — explicit arrow wrapper
1212
- * div(count()) // ✗ static — evaluated once, not reactive
1213
- * ```
1214
- */
1215
- type Accessor<T> = (() => T) & {
1216
- readonly [__accessor]?: never;
1217
- };
1218
- type SetState<T> = (next: T | ((prev: T) => T)) => void;
1219
- type StateTuple<T> = [Accessor<T>, SetState<T>];
1220
- /** Options for signal */
1221
- interface SignalOptions<T = unknown> {
1222
- /** Debug name for devtools inspection. Only used in development. */
1223
- name?: string;
1224
- /** Custom equality function. Defaults to Object.is(). */
1225
- equals?: (prev: T, next: T) => boolean;
1226
- }
1227
- /**
1228
- * signal creates a reactive signal that holds a value of type T.
1229
- * Returns a tuple: [getter, setter].
1230
- *
1231
- * @param initial Initial value
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.
1235
- */
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;
1301
-
1302
1212
  /**
1303
1213
  * Reactive array hook. Provides common array operations that
1304
1214
  * automatically trigger reactive updates.
@@ -1498,45 +1408,6 @@ declare function deepEqual(a: unknown, b: unknown, seen?: Map<object, Set<object
1498
1408
  */
1499
1409
  declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T) => T)) => void];
1500
1410
 
1501
- /**
1502
- * derived creates a derived reactive signal whose value updates when dependencies change.
1503
- *
1504
- * Uses lazy pull-based evaluation with a single dirty flag:
1505
- * - When a dependency changes, the computed is marked dirty (no re-evaluation).
1506
- * - Dirtiness propagates downstream via propagateDirty.
1507
- * - The getter only re-evaluates when actually read (pull-based).
1508
- * - On re-evaluation, dependencies are re-tracked via retrack() so that
1509
- * derived-of-derived chains propagate correctly without paying the full
1510
- * Set-delete + re-add cost of track()'s cleanup phase.
1511
- *
1512
- * STABILIZATION — why a dirty flag is enough:
1513
- *
1514
- * A dirty computed does NOT imply a changed value. Downstream effects are
1515
- * enqueued by `propagateDirty` at write time, before this computed has had a
1516
- * chance to recompute and compare. Rather than adding a three-color
1517
- * (CLEAN/CHECK/DIRTY) propagation pass — which an earlier revision measured as
1518
- * a regression on every benchmark, because the extra state has nothing to skip
1519
- * when values genuinely change — the engine settles the question lazily at
1520
- * DRAIN time: `cs._validate` recomputes a dirty computed and `cs.__v` is bumped
1521
- * ONLY when the new value differs under this computed's comparator. The
1522
- * scheduler compares that version against what each subscriber last observed
1523
- * and suppresses the run when nothing changed (see `depsChanged` in
1524
- * ../../reactivity/track-core.ts).
1525
- *
1526
- * That keeps the cheap boolean dirty flag AND makes `equals` actually stop
1527
- * propagation, with recomputation still fully lazy: `_validate` only ever runs
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.
1532
- */
1533
- declare function derived<T>(getter: () => T, options?: {
1534
- name?: string;
1535
- /** Custom equality — when the recomputed value equals the previous,
1536
- * downstream subscribers are not notified. Defaults to `Object.is`. */
1537
- equals?: (a: T, b: T) => boolean;
1538
- }): Accessor<T>;
1539
-
1540
1411
  /** Options for effect */
1541
1412
  interface EffectOptions {
1542
1413
  /** Error handler for exceptions thrown during effect execution. */
@@ -1668,7 +1539,9 @@ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefi
1668
1539
  * dependency-tracked). The setter typically updates upstream signals,
1669
1540
  * and the derived value recomputes on the next read.
1670
1541
  *
1671
- * Returns a `[getter, setter]` tuple, matching the `signal()` API.
1542
+ * Returns a `[getter, setter]` tuple, matching the `signal()` API. The getter
1543
+ * is the underlying `derived()` accessor, so `getter.dispose()` releases its
1544
+ * upstream subscriptions when the writable is discarded before its sources.
1672
1545
  *
1673
1546
  * @param get Computed getter — reads reactive dependencies
1674
1547
  * @param set Setter — called with the new value, typically updates upstream signals
@@ -1695,11 +1568,11 @@ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefi
1695
1568
  * fullName(); // "Jane Smith"
1696
1569
  * ```
1697
1570
  *
1698
- * @returns A store handle with `subscribe`, `set` and `update`.
1571
+ * @returns A `[getter, setter]` tuple; the getter carries `dispose()`.
1699
1572
  */
1700
1573
  declare function writable<T>(get: () => T, set: (value: T) => void, options?: {
1701
1574
  name?: string;
1702
- }): [Accessor<T>, (value: T) => void];
1575
+ }): [DerivedAccessor<T>, (value: T) => void];
1703
1576
 
1704
1577
  /**
1705
1578
  * SSR context for SibuJS.
@@ -2306,4 +2179,4 @@ declare const untracked: ReactiveApi["untracked"];
2306
2179
  declare const retrack: ReactiveApi["retrack"];
2307
2180
  declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
2308
2181
 
2309
- 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, type LazyIslandLoader, 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 };
2182
+ export { Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DerivedAccessor, DynamicComponent, type EachBindings, type EachEventBindings, 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, type LazyIslandLoader, 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 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, 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, 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 };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { N as NodeChild, a as NodeChildren, T as TagProps } from './tagFactory-Bzupt4Pj.js';
2
- export { D as Dispose, S as SVG_NS, t as tagFactory } from './tagFactory-Bzupt4Pj.js';
1
+ import { N as NodeChild, a as NodeChildren, T as TagProps, A as Accessor, D as DerivedAccessor } from './tagFactory-DVoDpHye.js';
2
+ export { b as Dispose, E as ExternalSource, S as SVG_NS, c as SignalOptions, d as derived, e as external, s as signal, t as tagFactory } from './tagFactory-DVoDpHye.js';
3
3
  export { T as TrustedHTML, t as trustHTML } from './ssr-BiPRdZ6n.js';
4
4
  import { R as ReactiveSignal } from './signal-BnWpq6WB.js';
5
5
 
@@ -667,12 +667,22 @@ declare function DynamicComponent(is: () => string | Component$1): HTMLElement;
667
667
  * assigns that row a new item or position.
668
668
  *
669
669
  * Identity is not value freshness: a row keeps its DOM node when its key is
670
- * unchanged, and still updates its contents when the item behind that key is
671
- * replaced.
672
- *
673
- * Reading `item()` subscribes to the ROW's cell, not to the whole-array signal,
674
- * so a row only re-renders when its own item/index actually changes — mutating
675
- * an unrelated row does not disturb it.
670
+ * unchanged, and its bindings still update when the item behind that key is
671
+ * replaced — provided they read the getter rather than a value captured when
672
+ * `render` ran.
673
+ *
674
+ * `render` runs untracked, whether the row appears in the first pass or in a
675
+ * later update: a signal read directly in its body — `item()` and `index()`
676
+ * included — is a one-time read that never subscribes anything and goes stale
677
+ * when the key later receives a replacement item or moves. Pass the getters
678
+ * down (`Row({ item, index })`) or read them inside bindings
679
+ * (`() => item().name`) and effects created by the row.
680
+ *
681
+ * A binding that reads `item()` subscribes to the ROW's cell, not to the
682
+ * whole-array signal. `render` never re-runs for an existing key; only the
683
+ * bindings and effects that read that row's `item()` / `index()` re-run, and
684
+ * only when its own item or position actually changes — mutating an unrelated
685
+ * row does not disturb it.
676
686
  *
677
687
  * @param getArray A reactive getter returning an array.
678
688
  * @param render A function that receives reactive item and index getters and returns a NodeChild.
@@ -1199,106 +1209,6 @@ type Slots = Record<string, SlotFn>;
1199
1209
  */
1200
1210
  declare function getSlot(slots: Slots | undefined, name?: string): SlotFn | undefined;
1201
1211
 
1202
- declare const __accessor: unique symbol;
1203
- /**
1204
- * A reactive signal getter returned by signal(), derived(), and similar primitives.
1205
- *
1206
- * Pass an Accessor directly into reactive prop positions — never call it there:
1207
- * ```ts
1208
- * const [count, setCount] = signal(0);
1209
- *
1210
- * div(count) // ✓ reactive — Accessor passed directly
1211
- * div(() => count()) // ✓ reactive — explicit arrow wrapper
1212
- * div(count()) // ✗ static — evaluated once, not reactive
1213
- * ```
1214
- */
1215
- type Accessor<T> = (() => T) & {
1216
- readonly [__accessor]?: never;
1217
- };
1218
- type SetState<T> = (next: T | ((prev: T) => T)) => void;
1219
- type StateTuple<T> = [Accessor<T>, SetState<T>];
1220
- /** Options for signal */
1221
- interface SignalOptions<T = unknown> {
1222
- /** Debug name for devtools inspection. Only used in development. */
1223
- name?: string;
1224
- /** Custom equality function. Defaults to Object.is(). */
1225
- equals?: (prev: T, next: T) => boolean;
1226
- }
1227
- /**
1228
- * signal creates a reactive signal that holds a value of type T.
1229
- * Returns a tuple: [getter, setter].
1230
- *
1231
- * @param initial Initial value
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.
1235
- */
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;
1301
-
1302
1212
  /**
1303
1213
  * Reactive array hook. Provides common array operations that
1304
1214
  * automatically trigger reactive updates.
@@ -1498,45 +1408,6 @@ declare function deepEqual(a: unknown, b: unknown, seen?: Map<object, Set<object
1498
1408
  */
1499
1409
  declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T) => T)) => void];
1500
1410
 
1501
- /**
1502
- * derived creates a derived reactive signal whose value updates when dependencies change.
1503
- *
1504
- * Uses lazy pull-based evaluation with a single dirty flag:
1505
- * - When a dependency changes, the computed is marked dirty (no re-evaluation).
1506
- * - Dirtiness propagates downstream via propagateDirty.
1507
- * - The getter only re-evaluates when actually read (pull-based).
1508
- * - On re-evaluation, dependencies are re-tracked via retrack() so that
1509
- * derived-of-derived chains propagate correctly without paying the full
1510
- * Set-delete + re-add cost of track()'s cleanup phase.
1511
- *
1512
- * STABILIZATION — why a dirty flag is enough:
1513
- *
1514
- * A dirty computed does NOT imply a changed value. Downstream effects are
1515
- * enqueued by `propagateDirty` at write time, before this computed has had a
1516
- * chance to recompute and compare. Rather than adding a three-color
1517
- * (CLEAN/CHECK/DIRTY) propagation pass — which an earlier revision measured as
1518
- * a regression on every benchmark, because the extra state has nothing to skip
1519
- * when values genuinely change — the engine settles the question lazily at
1520
- * DRAIN time: `cs._validate` recomputes a dirty computed and `cs.__v` is bumped
1521
- * ONLY when the new value differs under this computed's comparator. The
1522
- * scheduler compares that version against what each subscriber last observed
1523
- * and suppresses the run when nothing changed (see `depsChanged` in
1524
- * ../../reactivity/track-core.ts).
1525
- *
1526
- * That keeps the cheap boolean dirty flag AND makes `equals` actually stop
1527
- * propagation, with recomputation still fully lazy: `_validate` only ever runs
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.
1532
- */
1533
- declare function derived<T>(getter: () => T, options?: {
1534
- name?: string;
1535
- /** Custom equality — when the recomputed value equals the previous,
1536
- * downstream subscribers are not notified. Defaults to `Object.is`. */
1537
- equals?: (a: T, b: T) => boolean;
1538
- }): Accessor<T>;
1539
-
1540
1411
  /** Options for effect */
1541
1412
  interface EffectOptions {
1542
1413
  /** Error handler for exceptions thrown during effect execution. */
@@ -1668,7 +1539,9 @@ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefi
1668
1539
  * dependency-tracked). The setter typically updates upstream signals,
1669
1540
  * and the derived value recomputes on the next read.
1670
1541
  *
1671
- * Returns a `[getter, setter]` tuple, matching the `signal()` API.
1542
+ * Returns a `[getter, setter]` tuple, matching the `signal()` API. The getter
1543
+ * is the underlying `derived()` accessor, so `getter.dispose()` releases its
1544
+ * upstream subscriptions when the writable is discarded before its sources.
1672
1545
  *
1673
1546
  * @param get Computed getter — reads reactive dependencies
1674
1547
  * @param set Setter — called with the new value, typically updates upstream signals
@@ -1695,11 +1568,11 @@ declare function watch<T>(getter: () => T, callback: (value: T, prev: T | undefi
1695
1568
  * fullName(); // "Jane Smith"
1696
1569
  * ```
1697
1570
  *
1698
- * @returns A store handle with `subscribe`, `set` and `update`.
1571
+ * @returns A `[getter, setter]` tuple; the getter carries `dispose()`.
1699
1572
  */
1700
1573
  declare function writable<T>(get: () => T, set: (value: T) => void, options?: {
1701
1574
  name?: string;
1702
- }): [Accessor<T>, (value: T) => void];
1575
+ }): [DerivedAccessor<T>, (value: T) => void];
1703
1576
 
1704
1577
  /**
1705
1578
  * SSR context for SibuJS.
@@ -2306,4 +2179,4 @@ declare const untracked: ReactiveApi["untracked"];
2306
2179
  declare const retrack: ReactiveApi["retrack"];
2307
2180
  declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
2308
2181
 
2309
- 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, type LazyIslandLoader, 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 };
2182
+ export { Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DerivedAccessor, DynamicComponent, type EachBindings, type EachEventBindings, 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, type LazyIslandLoader, 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 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, 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, 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 };
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  unregisterIsland,
53
53
  when,
54
54
  writable
55
- } from "./chunk-ADM46X22.js";
55
+ } from "./chunk-S373NSMK.js";
56
56
  import {
57
57
  a,
58
58
  abbr,
@@ -190,36 +190,36 @@ import {
190
190
  use,
191
191
  var_,
192
192
  video
193
- } from "./chunk-36S2YPP4.js";
193
+ } from "./chunk-PCT43HW3.js";
194
194
  import {
195
195
  trustHTML
196
196
  } from "./chunk-NIOYEGBQ.js";
197
197
  import {
198
198
  watch
199
- } from "./chunk-RR7M3FHM.js";
199
+ } from "./chunk-7LN645I6.js";
200
200
  import {
201
201
  __resetIdCounter,
202
202
  createId
203
203
  } from "./chunk-CCSJMTRN.js";
204
204
  import {
205
205
  context
206
- } from "./chunk-7NBDXVHS.js";
206
+ } from "./chunk-VUF4ALSW.js";
207
207
  import {
208
208
  SVG_NS,
209
209
  tagFactory
210
- } from "./chunk-PNIRUQ4C.js";
210
+ } from "./chunk-KEISJXBU.js";
211
211
  import {
212
212
  bindDynamic
213
- } from "./chunk-VCTAEPSB.js";
213
+ } from "./chunk-ONOHFDLG.js";
214
214
  import {
215
215
  derived
216
- } from "./chunk-W2EQ7X2L.js";
216
+ } from "./chunk-RBTPLM32.js";
217
217
  import "./chunk-IXKSNWV5.js";
218
218
  import "./chunk-7ZHH77QA.js";
219
219
  import {
220
220
  effect,
221
221
  on
222
- } from "./chunk-7GIHSAWB.js";
222
+ } from "./chunk-RIXRAYIU.js";
223
223
  import {
224
224
  disableSSR,
225
225
  enableSSR,
@@ -244,12 +244,12 @@ import {
244
244
  external,
245
245
  isBatching,
246
246
  signal
247
- } from "./chunk-NVNJH22U.js";
247
+ } from "./chunk-XZZOBQAY.js";
248
248
  import {
249
249
  retrack,
250
250
  setMaxDrainIterations,
251
251
  untracked
252
- } from "./chunk-3BTTCZ5J.js";
252
+ } from "./chunk-UGRX3S57.js";
253
253
  import {
254
254
  getRuntimeErrorHandler,
255
255
  reportError,
package/dist/motion.cjs CHANGED
@@ -391,6 +391,15 @@ function untracked(fn) {
391
391
  var subscriberEpochCounter = 0;
392
392
  function retrack(effectFn, subscriber) {
393
393
  const prev = currentSubscriber;
394
+ let savedDepth = 0;
395
+ let savedSuspendSub = null;
396
+ if (trackingSuspended) {
397
+ savedDepth = suspendDepth;
398
+ savedSuspendSub = suspendSavedSub;
399
+ suspendDepth = 0;
400
+ suspendSavedSub = null;
401
+ trackingSuspended = false;
402
+ }
394
403
  currentSubscriber = subscriber;
395
404
  const sub = subscriber;
396
405
  const epoch = ++subscriberEpochCounter;
@@ -405,6 +414,11 @@ function retrack(effectFn, subscriber) {
405
414
  effectFn();
406
415
  } finally {
407
416
  currentSubscriber = prev;
417
+ if (savedDepth !== 0) {
418
+ suspendDepth = savedDepth;
419
+ suspendSavedSub = savedSuspendSub;
420
+ trackingSuspended = true;
421
+ }
408
422
  let node = sub.depsHead ?? null;
409
423
  while (node !== null) {
410
424
  const next = node.subNext;
@@ -424,11 +438,25 @@ function track(effectFn, subscriber) {
424
438
  if (!subscriber) return reactiveBinding(effectFn);
425
439
  cleanup(subscriber);
426
440
  const prev = currentSubscriber;
441
+ let savedDepth = 0;
442
+ let savedSuspendSub = null;
443
+ if (trackingSuspended) {
444
+ savedDepth = suspendDepth;
445
+ savedSuspendSub = suspendSavedSub;
446
+ suspendDepth = 0;
447
+ suspendSavedSub = null;
448
+ trackingSuspended = false;
449
+ }
427
450
  currentSubscriber = subscriber;
428
451
  try {
429
452
  effectFn();
430
453
  } finally {
431
454
  currentSubscriber = prev;
455
+ if (savedDepth !== 0) {
456
+ suspendDepth = savedDepth;
457
+ suspendSavedSub = savedSuspendSub;
458
+ trackingSuspended = true;
459
+ }
432
460
  const sub2 = subscriber;
433
461
  for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
434
462
  const sig = n.sig;
@@ -599,8 +627,9 @@ function propagateDirty(sub) {
599
627
  if (s._c) {
600
628
  const nSig = s._sig;
601
629
  if (nSig) {
602
- if (!nSig._d) {
630
+ if (!nSig._d || nSig._f === true) {
603
631
  nSig._d = true;
632
+ nSig._f = false;
604
633
  stack.push(nSig);
605
634
  }
606
635
  } else {
@@ -700,7 +729,7 @@ function forEachSubscriber(signal2, visit) {
700
729
  }
701
730
 
702
731
  // src/reactivity/track.ts
703
- var _runtimeVersion = true ? "4.4.0" : "dev";
732
+ var _runtimeVersion = true ? "4.5.0" : "dev";
704
733
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
705
734
  function resolveReactiveApi() {
706
735
  const g = globalThis;
package/dist/motion.js CHANGED
@@ -20,10 +20,10 @@ import {
20
20
  stagger,
21
21
  transition,
22
22
  viewTransition
23
- } from "./chunk-SKPTERHP.js";
23
+ } from "./chunk-B3WHI2QA.js";
24
24
  import "./chunk-QKRPLZ2V.js";
25
- import "./chunk-NVNJH22U.js";
26
- import "./chunk-3BTTCZ5J.js";
25
+ import "./chunk-XZZOBQAY.js";
26
+ import "./chunk-UGRX3S57.js";
27
27
  import "./chunk-2WLZ6757.js";
28
28
  export {
29
29
  TransitionGroup,