sibujs 4.13.1 → 4.14.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 (107) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +15 -2
  3. package/dist/browser.js +9 -7
  4. package/dist/build.cjs +880 -281
  5. package/dist/build.d.cts +68 -3
  6. package/dist/build.d.ts +68 -3
  7. package/dist/build.js +88 -30
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +13 -13
  10. package/dist/cdn.full.global.js +12 -12
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-5RIBLY2N.js → chunk-2TTRW2TE.js} +6 -6
  13. package/dist/{chunk-J6FW5TV6.js → chunk-3HEX6QJQ.js} +5 -3
  14. package/dist/chunk-3HZOSMPX.js +90 -0
  15. package/dist/{chunk-QKFVMIAV.js → chunk-4FJSAUKB.js} +11 -3
  16. package/dist/{chunk-OIKD7YDY.js → chunk-4TQDTWWN.js} +6 -5
  17. package/dist/{chunk-5XUGJM3J.js → chunk-7ILEHTVX.js} +1 -1
  18. package/dist/{chunk-EU7T7EB3.js → chunk-A454RAEK.js} +4 -4
  19. package/dist/{chunk-QC3NP47A.js → chunk-AE32RGWX.js} +10 -6
  20. package/dist/{chunk-EXXSN3BW.js → chunk-B3HOT4TK.js} +1 -1
  21. package/dist/{chunk-QZ47OZ25.js → chunk-BKZ5N6NK.js} +8 -6
  22. package/dist/{chunk-2O6JJCUR.js → chunk-BNXRWZZV.js} +49 -8
  23. package/dist/chunk-BOYKGGVD.js +2015 -0
  24. package/dist/{chunk-22OPSWMF.js → chunk-CCKUNHVU.js} +2 -2
  25. package/dist/{chunk-CPQKWCJ2.js → chunk-CJ7KS3DF.js} +16 -10
  26. package/dist/chunk-CNPT6NKV.js +1859 -0
  27. package/dist/{chunk-2NC56RGY.js → chunk-F67F77IA.js} +2 -2
  28. package/dist/{chunk-TUCPL2HB.js → chunk-GIJ33UJZ.js} +1 -1
  29. package/dist/chunk-GVAMIU6K.js +195 -0
  30. package/dist/{chunk-K4LNUT5L.js → chunk-HQIAL7F2.js} +8 -6
  31. package/dist/{chunk-C73VBPEF.js → chunk-JTFQ7IL4.js} +8 -8
  32. package/dist/{chunk-AM6DZLBM.js → chunk-LJMX4SQ7.js} +2 -2
  33. package/dist/{chunk-4UTGDVSA.js → chunk-MIRKTVH4.js} +7 -14
  34. package/dist/{chunk-YFTBCCVH.js → chunk-N7A6FKWW.js} +5 -3
  35. package/dist/chunk-NT3ZYB5S.js +167 -0
  36. package/dist/{chunk-VFPLWUT7.js → chunk-QS6GMWDD.js} +151 -86
  37. package/dist/chunk-R5U7XKVJ.js +16 -0
  38. package/dist/{chunk-ADZHPN2B.js → chunk-RVR5NPFL.js} +3 -3
  39. package/dist/{chunk-F2UWOZ6J.js → chunk-RVWJGWCL.js} +2 -2
  40. package/dist/{chunk-NYNYSPK7.js → chunk-S37PZD6I.js} +30 -45
  41. package/dist/{chunk-G3Z2FEBR.js → chunk-SVULV7O4.js} +119 -26
  42. package/dist/{chunk-PBJYCE2Z.js → chunk-TLS4NVIB.js} +5 -5
  43. package/dist/{chunk-KC62VURU.js → chunk-UUIKJJIK.js} +10 -8
  44. package/dist/chunk-UYTYNCMZ.js +199 -0
  45. package/dist/chunk-VM2U5LEL.js +2596 -0
  46. package/dist/{chunk-J4X7ICH4.js → chunk-Y4LUM2XS.js} +8 -6
  47. package/dist/data.cjs +265 -35
  48. package/dist/data.d.cts +31 -1
  49. package/dist/data.d.ts +31 -1
  50. package/dist/data.js +16 -10
  51. package/dist/devtools.cjs +15 -2
  52. package/dist/devtools.js +9 -7
  53. package/dist/{dispose-GEIG2KOF.js → dispose-EIHHHDC7.js} +3 -1
  54. package/dist/ecosystem.cjs +62 -23
  55. package/dist/ecosystem.js +14 -12
  56. package/dist/extras.cjs +3520 -270
  57. package/dist/extras.d.cts +3 -3
  58. package/dist/extras.d.ts +3 -3
  59. package/dist/extras.js +39 -29
  60. package/dist/index.cjs +442 -253
  61. package/dist/index.d.cts +93 -283
  62. package/dist/index.d.ts +93 -283
  63. package/dist/index.js +26 -16
  64. package/dist/motion.cjs +12 -1
  65. package/dist/motion.js +6 -4
  66. package/dist/patterns.cjs +15 -2
  67. package/dist/patterns.js +10 -8
  68. package/dist/performance.cjs +36 -2
  69. package/dist/performance.d.cts +2 -2
  70. package/dist/performance.d.ts +2 -2
  71. package/dist/performance.js +14 -10
  72. package/dist/plugins.cjs +5225 -1807
  73. package/dist/plugins.d.cts +8 -834
  74. package/dist/plugins.d.ts +8 -834
  75. package/dist/plugins.js +64 -2329
  76. package/dist/prerenderServer-MXJEVWA3.js +350 -0
  77. package/dist/renderRoute-DP20XwMS.d.cts +86 -0
  78. package/dist/renderRoute-fxgsm2Ir.d.ts +86 -0
  79. package/dist/routeHead-CE9SV-cH.d.cts +995 -0
  80. package/dist/routeHead-DzVxPviY.d.ts +995 -0
  81. package/dist/{routeMiddleware-B1sp4P7a.d.ts → routeMiddleware-BySbueoE.d.ts} +0 -25
  82. package/dist/{routeMiddleware-CedQTsLK.d.cts → routeMiddleware-CijZD5eW.d.cts} +0 -25
  83. package/dist/ssr-JpGpHnpC.d.cts +841 -0
  84. package/dist/ssr-JpGpHnpC.d.ts +841 -0
  85. package/dist/{ssr-VBM6J5SJ.js → ssr-NECEU4X4.js} +15 -6
  86. package/dist/ssr-dom.cjs +1862 -0
  87. package/dist/ssr-dom.d.cts +341 -0
  88. package/dist/ssr-dom.d.ts +341 -0
  89. package/dist/ssr-dom.js +19 -0
  90. package/dist/ssr.cjs +7630 -2064
  91. package/dist/ssr.d.cts +63 -5
  92. package/dist/ssr.d.ts +63 -5
  93. package/dist/ssr.js +136 -14
  94. package/dist/testing.cjs +6543 -87
  95. package/dist/testing.d.cts +80 -1
  96. package/dist/testing.d.ts +80 -1
  97. package/dist/testing.js +117 -4
  98. package/dist/ui.cjs +36 -2
  99. package/dist/ui.js +17 -14
  100. package/dist/widgets.cjs +14 -1
  101. package/dist/widgets.js +11 -9
  102. package/package.json +9 -2
  103. package/dist/chunk-2F4PSJ5Z.js +0 -765
  104. package/dist/chunk-7XHATCIH.js +0 -121
  105. package/dist/chunk-B36ADEIR.js +0 -21
  106. package/dist/ssr-Db0Sgdu3.d.cts +0 -245
  107. package/dist/ssr-Db0Sgdu3.d.ts +0 -245
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { T as TagProps, a as TagFunction, D as DerivedAccessor } from './tagFact
4
4
  export { S as SVG_NS, b as StyleMap, c as StyleObject, d as TagEventHandlers, e as derived, t as tagFactory } from './tagFactory-Bnd_Nph9.js';
5
5
  import { A as Accessor } from './signal-EotCj4hS.js';
6
6
  export { D as DisposableAccessor, E as ExternalSource, S as SignalOptions, e as external, s as signal } from './signal-EotCj4hS.js';
7
- export { T as TrustedHTML, t as trustHTML } from './ssr-Db0Sgdu3.js';
7
+ 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.js';
8
8
  import { R as ReactiveSignal } from './signal-BnWpq6WB.js';
9
9
 
10
10
  interface ErrorBoundaryOptions {
@@ -207,21 +207,6 @@ declare function setRuntimeErrorHandler(handler: RuntimeErrorHandler | null): Ru
207
207
  * @returns The installed handler, or `null` when none is set.
208
208
  */
209
209
  declare function getRuntimeErrorHandler(): RuntimeErrorHandler | null;
210
- /**
211
- * Report an error the runtime caught and contained.
212
- *
213
- * Resolution order: nearest claiming `ErrorBoundary` → configured runtime
214
- * handler → `console.error`. Exactly one of those runs for a given report.
215
- *
216
- * Never throws — callers are inside `catch`/`finally` blocks where a secondary
217
- * failure would corrupt teardown or abort a drain.
218
- *
219
- * @public Framework primitive, also usable by plugin/integration code that
220
- * catches an application exception on SibuJS's behalf (custom rendering
221
- * primitives, adapters) and wants it to follow the same boundary/handler
222
- * resolution as a built-in one. Ordinary application code should prefer
223
- * `ErrorBoundary` or `setRuntimeErrorHandler`.
224
- */
225
210
  declare function reportError(error: unknown, context: RuntimeErrorContext): void;
226
211
 
227
212
  /**
@@ -360,6 +345,40 @@ declare function catchErrorAsync<T>(fn: () => Promise<T>, onError?: ErrorHandler
360
345
  */
361
346
  declare function setGlobalErrorHandler(handler: ErrorHandler): void;
362
347
 
348
+ /** Options of {@link ClientOnly}. */
349
+ interface ClientOnlyOptions {
350
+ /**
351
+ * What the server renders (and what the browser shows until it takes over).
352
+ * Give it the same size as the content to avoid a layout jump when it is
353
+ * swapped. Omitted, nothing is rendered on the server.
354
+ */
355
+ fallback?: () => NodeChild;
356
+ }
357
+ /**
358
+ * Render content that only works in a browser — a canvas, `document.fonts`,
359
+ * `matchMedia`, storage, a third-party widget.
360
+ *
361
+ * - **On the server** (an SSR render or the build-time pre-renderer), only
362
+ * `fallback` is rendered, so the content's browser APIs never run there.
363
+ * - **In the browser**, the content renders directly. During an adopting
364
+ * hydration the fallback is rendered first, so it binds to the server's
365
+ * fallback markup, and the content replaces it in one synchronous swap as
366
+ * soon as the hydration ends.
367
+ *
368
+ * @param content Builds the browser-only content.
369
+ * @param options `fallback` for the server and for the first paint.
370
+ * @returns A directive anchor (like `when`), to place as a child.
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * // The card ships a plain <img>; the browser recolors it on a <canvas>.
375
+ * ClientOnly(() => RecoloredCanvas(product), {
376
+ * fallback: () => img({ src: product.image, width: 160, height: 160 }),
377
+ * });
378
+ * ```
379
+ */
380
+ declare function ClientOnly(content: () => NodeChild, options?: ClientOnlyOptions): Comment;
381
+
363
382
  /**
364
383
  * Context API for SibuJS — a reactive global value that any component
365
384
  * can read without prop drilling.
@@ -1522,6 +1541,14 @@ declare function deepSignal<T>(initial: T): [Accessor<T>, (next: T | ((prev: T)
1522
1541
  interface EffectOptions {
1523
1542
  /** Error handler for exceptions thrown during effect execution. */
1524
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;
1525
1552
  }
1526
1553
  /**
1527
1554
  * Creates a callback that only tracks the specified dependencies,
@@ -1731,6 +1758,39 @@ interface SSRStore {
1731
1758
  * will ever use. See `plugins/i18n.ts`.
1732
1759
  */
1733
1760
  locale?: string;
1761
+ /**
1762
+ * Async work started inside this request that a server render must wait for
1763
+ * before it serializes: `lazy()` chunks, `query()` / `resource()` fetches, the
1764
+ * router's initial navigation and route loads, `prerenderWait()` promises.
1765
+ * Created by `runInSSRContext`; `settled()` drains it. See `trackPending`.
1766
+ */
1767
+ pending?: Set<PendingWork>;
1768
+ /** Called whenever an entry leaves `pending`, so a waiting `settled()` re-checks. */
1769
+ wake?: (() => void) | null;
1770
+ /**
1771
+ * Errors no `ErrorBoundary` claimed during this request. When present,
1772
+ * `reportError` records them here instead of the console, and an async
1773
+ * server render rejects with the first one.
1774
+ */
1775
+ errors?: Array<{
1776
+ error: Error;
1777
+ context: {
1778
+ phase: string;
1779
+ name?: string;
1780
+ node?: unknown;
1781
+ };
1782
+ }>;
1783
+ /** Request-scoped slots of other subsystems (router, head collector, transferred state). */
1784
+ slots?: Map<string, unknown>;
1785
+ }
1786
+ /** One unit of async work a server render waits for. */
1787
+ interface PendingWork {
1788
+ /** What the work is, for diagnostics: `query:/api/catalog`, `lazy`, `router:navigation`. */
1789
+ readonly label: string;
1790
+ /** `Date.now()` when the work was registered. */
1791
+ readonly since: number;
1792
+ /** The streaming `Suspense` boundary it was started in, if any (see `streamBoundary`). */
1793
+ readonly boundary?: unknown;
1734
1794
  }
1735
1795
  /** Returns the active store (ALS or fallback). */
1736
1796
  declare function getSSRStore(): SSRStore;
@@ -1743,6 +1803,22 @@ declare function isSSR(): boolean;
1743
1803
  * AsyncLocalStorage, giving each request its own caches.
1744
1804
  */
1745
1805
  declare function getRequestScopedCache<V>(name: string): Map<string, V> | null;
1806
+ /**
1807
+ * True while the code runs on a server: inside an SSR scope (`runInSSRContext`,
1808
+ * `withSSR`, `enableSSR`) or in the build-time pre-renderer. Use it to keep
1809
+ * browser-only work (canvas, `document.fonts`, storage, `matchMedia`) out of a
1810
+ * server render; `ClientOnly` and `onMount` are usually the better tools.
1811
+ *
1812
+ * @returns `true` on the server, `false` in a browser.
1813
+ */
1814
+ declare function isServer(): boolean;
1815
+ /**
1816
+ * The opposite of {@link isServer}: true in a browser (or a browser-like test
1817
+ * environment) that is not rendering for a server.
1818
+ *
1819
+ * @returns `true` in a browser, `false` on the server.
1820
+ */
1821
+ declare function isClient(): boolean;
1746
1822
  /** Enable SSR mode. Side effects (effect, watch, onMount) become no-ops. */
1747
1823
  declare function enableSSR(): void;
1748
1824
  /** Disable SSR mode. Side effects resume normal behavior. */
@@ -1804,272 +1880,6 @@ declare function strict<T>(fn: () => T): T;
1804
1880
  */
1805
1881
  declare function strictEffect(fn: () => void): () => void;
1806
1882
 
1807
- /** Event handlers for {@link EachBindings}, typed per event name. */
1808
- type EachEventBindings = {
1809
- [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K], el: HTMLElement) => void;
1810
- };
1811
- /**
1812
- * What {@link EnhanceContext.each} attaches to one element.
1813
- *
1814
- * Every field maps one-to-one onto an existing `ctx.*` helper and is committed
1815
- * through it — this is a shorthand for calls you could write by hand, not a
1816
- * template language and not a second binding engine. There is no expression
1817
- * parsing, no string interpolation and no `eval`: every value is a plain
1818
- * function you wrote, so it stays CSP-safe and fully type-checked.
1819
- *
1820
- * Anything not covered here (two-way `model()`, listener options, a nested
1821
- * `enhance`) is written imperatively in the same callback — it receives the
1822
- * element, so `ctx.model(el, …)` beside a returned descriptor is normal.
1823
- */
1824
- interface EachBindings {
1825
- /** Reactive `textContent` — same as `ctx.text(el, value)`. */
1826
- text?: () => unknown;
1827
- /** Reactive attributes by name — same as `ctx.attr(el, name, value)`. */
1828
- attr?: Record<string, () => unknown>;
1829
- /** Reactive class toggles by class name — same as `ctx.classed(el, name, on)`. */
1830
- class?: Record<string, () => boolean>;
1831
- /** Reactive visibility — same as `ctx.show(el, when)`. */
1832
- show?: () => boolean;
1833
- /** Event listeners by event name — same as `ctx.on(el, event, handler)`. */
1834
- on?: EachEventBindings;
1835
- /** Per-element teardown, run with the rest of the enhancement's cleanups. */
1836
- cleanup?: () => void;
1837
- }
1838
- /**
1839
- * Helpers handed to an `enhance` setup. Every binding is fine-grained (its own
1840
- * effect) and auto-disposed when the root element (or the returned dispose) is
1841
- * torn down.
1842
- *
1843
- * Target resolution for every helper:
1844
- * - `"@name"` → a descendant marked `data-ref="name"` (the ergonomic form).
1845
- * - any other string → a raw CSS selector, queried within the root.
1846
- * - an `Element` → used as-is.
1847
- * - `null` / omitted (where allowed) → the root element itself.
1848
- */
1849
- interface EnhanceContext {
1850
- /** The enhanced root element (the server-rendered node). */
1851
- root: HTMLElement;
1852
- /** First descendant matching a `@ref`/selector (or the root for `null`). */
1853
- ref<T extends Element = HTMLElement>(target: string | null): T | null;
1854
- /** All descendants matching a `@ref`/selector. */
1855
- refs<T extends Element = HTMLElement>(target: string): T[];
1856
- /** Attach an auto-removed event listener to a target (root if `null`). */
1857
- on<K extends keyof HTMLElementEventMap>(target: string | Element | null, event: K, handler: (event: HTMLElementEventMap[K], el: HTMLElement) => void, options?: AddEventListenerOptions): void;
1858
- /** Reactively drive `textContent` of an existing node. */
1859
- text(target: string | Element | null, value: () => unknown): void;
1860
- /** Reactively drive an attribute; a `null`/`false`/`undefined` value removes it. */
1861
- attr(target: string | Element | null, name: string, value: () => unknown): void;
1862
- /** Reactively toggle a class on a node. */
1863
- classed(target: string | Element | null, name: string, on: () => boolean): void;
1864
- /** Reactively toggle visibility (sets `display:none` when `false`). */
1865
- show(target: string | Element | null, when: () => boolean): void;
1866
- /** Two-way bind a form control to a `[get, set]` signal tuple. */
1867
- model<T>(target: string | Element, state: readonly [() => T, (value: T) => void], options?: {
1868
- event?: string;
1869
- }): void;
1870
- /**
1871
- * Bind a set of elements the server already rendered — a board, a table, a
1872
- * keyboard, a timeline, a legend — one descriptor at a time.
1873
- *
1874
- * The callback receives each element and its index and returns what to
1875
- * attach; every field is committed through the matching `ctx.*` helper, so
1876
- * ownership, disposal, write elision, attribute sanitization and error
1877
- * routing are byte-for-byte the same as writing the calls out by hand. No
1878
- * element is created, replaced, moved or re-parented — node identity is
1879
- * preserved, which is the entire point of enhancing existing markup.
1880
- *
1881
- * ```ts
1882
- * ctx.each<HTMLButtonElement>("@square", (el) => {
1883
- * const square = el.dataset.square as Square;
1884
- * return {
1885
- * text: () => pieceAt(square),
1886
- * class: { selected: () => selected() === square },
1887
- * attr: { "aria-label": () => describe(square) },
1888
- * on: { click: () => choose(square) },
1889
- * };
1890
- * });
1891
- * ```
1892
- *
1893
- * The callback may also return nothing and wire the element imperatively —
1894
- * `ctx.model(el, …)`, `ctx.on(el, "click", h, { passive: true })` — for the
1895
- * cases the descriptor deliberately does not cover.
1896
- *
1897
- * Zero matches is a silent no-op. Calling `each` twice over the same
1898
- * elements creates two independent sets of bindings, exactly as calling
1899
- * `ctx.text()` twice on one node does; the helper is sugar over those calls
1900
- * and does not track what a previous call attached.
1901
- *
1902
- * @param target A `@ref`/CSS selector resolved with {@link EnhanceContext.refs},
1903
- * or any iterable of elements (an array, a `NodeList`, an `HTMLCollection`).
1904
- * @param describe Called once per element, in document order.
1905
- */
1906
- each<T extends Element = HTMLElement>(target: string | Iterable<Element>, describe: (element: T, index: number) => EachBindings | void): void;
1907
- /** Register arbitrary teardown to run on disposal. */
1908
- cleanup(fn: () => void): void;
1909
- }
1910
- /** A setup function for `enhance` — wire reactivity, optionally return cleanup.
1911
- * The `void | (() => void)` shape mirrors `useEffect`'s "return a teardown, or
1912
- * nothing". */
1913
- type EnhanceSetup = (ctx: EnhanceContext) => void | (() => void);
1914
- /**
1915
- * Attach reactivity to an existing element (typically server-rendered) without
1916
- * replacing it. Returns a dispose function; disposal is also wired to the
1917
- * element, so removing its subtree cleans everything up.
1918
- *
1919
- * **Setup is a transaction.** If `setup` throws, every binding, listener and
1920
- * cleanup it registered through the {@link EnhanceContext} is torn down and the
1921
- * original error is rethrown unchanged — the element is left exactly as
1922
- * unenhanced as it started, so a retry is legal. The rollback covers
1923
- * framework-owned resources only (`ctx.on`, `ctx.text`, `ctx.attr`,
1924
- * `ctx.classed`, `ctx.show`, `ctx.model`, `ctx.cleanup`, and a cleanup returned
1925
- * from setup); work the setup performed outside those helpers — `innerHTML`
1926
- * writes, network calls, global mutation — cannot be reversed generically, so
1927
- * register its undo with `ctx.cleanup()` as you go.
1928
- *
1929
- * **Ownership.** A successfully enhanced root is marked
1930
- * `data-sibu-enhanced="true"`; disposal removes it. The marker tracks *current*
1931
- * ownership rather than history, so a **disposed root can be enhanced again**,
1932
- * while enhancing a root that is still active is refused (dev-warns) to prevent
1933
- * two competing sets of bindings. See `docs/architecture/enhancement-lifecycle.md`.
1934
- *
1935
- * @param target An `Element` or a CSS selector resolved against `document`
1936
- * (the first match is used; see {@link enhanceAll} for many).
1937
- * @param setup Wires reactivity via the {@link EnhanceContext}.
1938
- * @throws Whatever `setup` throws, after rolling its resources back.
1939
- *
1940
- * @example
1941
- * ```ts
1942
- * // server HTML: <div data-counter><b data-ref="n">0</b><button data-ref="inc">+1</button></div>
1943
- * const [n, setN] = signal(0);
1944
- * enhance("[data-counter]", (ctx) => {
1945
- * ctx.text("@n", () => n());
1946
- * ctx.on("@inc", "click", () => setN((v) => v + 1));
1947
- * });
1948
- * ```
1949
- */
1950
- declare function enhance(target: Element | string, setup: EnhanceSetup): () => void;
1951
- /**
1952
- * Enhance every element matching a selector. Returns a single dispose that
1953
- * tears down all of them.
1954
- *
1955
- * The whole collection is one transaction: if any element's setup throws, the
1956
- * elements already enhanced are rolled back (newest first, mirroring stack
1957
- * unwinding) and the original error is rethrown. A caller that never received
1958
- * the aggregate disposer is therefore never left holding live enhancements it
1959
- * has no way to release. A teardown that fails during that rollback is reported
1960
- * and skipped — it neither aborts the remaining rollback nor replaces the
1961
- * original setup error.
1962
- *
1963
- * The returned disposer is idempotent, and the collection may be enhanced again
1964
- * afterwards (see {@link enhance} on marker lifetime).
1965
- *
1966
- * @throws Whatever the failing element's setup threw, after rolling back.
1967
- */
1968
- declare function enhanceAll(selector: string, setup: EnhanceSetup): () => void;
1969
-
1970
- /** When an island activates. Declared per-element via `data-sibu-load`. */
1971
- type IslandStrategy = "load" | "idle" | "visible" | "interaction" | "media";
1972
- /** A lazy code loader: resolves to an island setup (or a module whose `default`
1973
- * is one). Only fetched when the island activates. Wrap with {@link lazyIsland}. */
1974
- type IslandLoader = () => Promise<EnhanceSetup | {
1975
- default: EnhanceSetup;
1976
- }>;
1977
- /**
1978
- * A loader that has been through {@link lazyIsland}.
1979
- *
1980
- * Exported for callers that want to be explicit about having wrapped one. It is
1981
- * NOT required by {@link IslandRegistration}, and that is deliberate.
1982
- *
1983
- * Requiring the brand would catch a forgotten `lazyIsland(...)` at compile time,
1984
- * which is where a mistake is cheapest to find — but it also rejects code that
1985
- * compiles today, and this package's contract is that the existing public API
1986
- * keeps working, widening rather than replacing, with a codemod for anything
1987
- * that cannot be widened. There is no codemod infrastructure here to ship one
1988
- * through, so the narrowing is not taken: the mistake is caught at mount time
1989
- * instead, by the thenable guard in `enhance()`, which throws immediately with
1990
- * an actionable message and leaves the element unenhanced. Loud and instant at
1991
- * runtime beats a type error that arrives with a breaking change attached.
1992
- *
1993
- * The brand is a phantom STRING-keyed property, never a `unique symbol`. A
1994
- * `unique symbol` has nominal identity per declaration, so two copies of this
1995
- * package's `.d.ts` in one dependency tree would produce two incompatible
1996
- * types. That is not hypothetical: the registry is deliberately shared through
1997
- * `Symbol.for` precisely because duplicate copies are expected. The property
1998
- * exists only in the type — the runtime marker is the global symbol below.
1999
- */
2000
- type LazyIslandLoader = IslandLoader & {
2001
- readonly __sibujsLazyIsland: true;
2002
- };
2003
- /**
2004
- * Either an inline setup, or a loader.
2005
- *
2006
- * Unchanged from 4.2: an unbranded `IslandLoader` is still accepted, so no
2007
- * previously-compiling code breaks. Wrapping with {@link lazyIsland} is what
2008
- * makes it *work* — see {@link LazyIslandLoader} for why this is not narrowed.
2009
- */
2010
- type IslandRegistration = EnhanceSetup | IslandLoader | LazyIslandLoader;
2011
- /**
2012
- * Mark a loader as lazy island code — its module is fetched only when the island
2013
- * activates, so a page ships ~0 JS for islands that never trigger.
2014
- *
2015
- * @example
2016
- * ```ts
2017
- * registerIsland("chart", lazyIsland(() => import("./islands/chart.js")));
2018
- * ```
2019
- */
2020
- declare function lazyIsland(loader: IslandLoader): LazyIslandLoader;
2021
- /**
2022
- * Register an island by name. The setup is applied via `enhance()` when a
2023
- * matching `[data-sibu-island="name"]` element activates.
2024
- *
2025
- * @example
2026
- * ```ts
2027
- * registerIsland("counter", (ctx) => {
2028
- * const [n, setN] = signal(0);
2029
- * ctx.text("@n", () => n());
2030
- * ctx.on("@inc", "click", () => setN((v) => v + 1));
2031
- * });
2032
- * ```
2033
- */
2034
- declare function registerIsland(name: string, setup: IslandRegistration): void;
2035
- /** Remove a registration (mainly for tests / HMR). */
2036
- declare function unregisterIsland(name: string): void;
2037
- interface MountIslandsOptions {
2038
- /** IntersectionObserver options for the `visible` strategy. */
2039
- rootMargin?: string;
2040
- threshold?: number | number[];
2041
- }
2042
- /**
2043
- * Scan a root for `[data-sibu-island]` elements and activate each according to
2044
- * its `data-sibu-load` strategy (default `load`). Returns a cleanup function
2045
- * that cancels pending schedulers and disposes every mounted island.
2046
- *
2047
- * Strategies (`data-sibu-load`):
2048
- * - `load` — activate immediately (next microtask).
2049
- * - `idle` — `requestIdleCallback` (falls back to a timeout).
2050
- * - `visible` — when the element scrolls into view (IntersectionObserver).
2051
- * - `interaction` — on first pointer/focus/key/touch interaction.
2052
- * - `media` — when `data-sibu-media` (a media query) matches.
2053
- *
2054
- * **Error isolation is lifecycle isolation.** Each island is one `enhance()`
2055
- * transaction, so an island whose setup throws is reported and leaves zero live
2056
- * bindings and zero live listeners; siblings activate normally, and the failed
2057
- * island can be mounted again once its setup is fixed.
2058
- *
2059
- * **Remounting is supported.** Cleanup disposes each island and releases its
2060
- * `data-sibu-enhanced` marker, so the same server markup can be mounted again
2061
- * with a fresh generation. Mounting twice *without* cleanup is also safe:
2062
- * islands that currently own an enhancement are skipped, never double-wired.
2063
- *
2064
- * Activation can never outlive its owner. Cleanup landing before a lazy chunk
2065
- * resolves prevents activation entirely, and cleanup landing *during* a setup
2066
- * disposes the island rather than stranding it — DISPOSED never becomes ACTIVE.
2067
- * `data-sibu-hydrated` is hydration provenance rather than ownership, and is
2068
- * therefore not cleared on disposal; see
2069
- * `docs/architecture/enhancement-lifecycle.md`.
2070
- */
2071
- declare function mountIslands(root?: ParentNode | null, options?: MountIslandsOptions): () => void;
2072
-
2073
1883
  /**
2074
1884
  * Batch multiple state updates into a single notification pass.
2075
1885
  * Subscribers are only notified once after the batch completes,
@@ -2336,4 +2146,4 @@ declare const untracked: ReactiveApi["untracked"];
2336
2146
  declare const retrack: ReactiveApi["retrack"];
2337
2147
  declare const setMaxDrainIterations: ReactiveApi["setMaxDrainIterations"];
2338
2148
 
2339
- export { Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedContext, type AsyncDerivedState, type AudioProps, type ButtonProps, Component, type Context, DerivedAccessor, type DirectiveOptions, DynamicComponent, type EachBindings, type EachEventBindings, type EffectBody, type EffectOptions, type EnhanceContext, type EnhanceSetup, ErrorBoundary, type ErrorBoundaryOptions, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FocusPolicy, 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, 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, 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 };
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 };
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ ClientOnly,
2
3
  DynamicComponent,
3
4
  ErrorBoundary,
4
5
  ErrorDisplay,
@@ -54,7 +55,7 @@ import {
54
55
  unregisterIsland,
55
56
  when,
56
57
  writable
57
- } from "./chunk-VFPLWUT7.js";
58
+ } from "./chunk-QS6GMWDD.js";
58
59
  import {
59
60
  a,
60
61
  abbr,
@@ -192,45 +193,49 @@ import {
192
193
  use,
193
194
  var_,
194
195
  video
195
- } from "./chunk-5XUGJM3J.js";
196
+ } from "./chunk-7ILEHTVX.js";
196
197
  import {
197
198
  trustHTML
198
- } from "./chunk-2F4PSJ5Z.js";
199
- import "./chunk-AM6DZLBM.js";
199
+ } from "./chunk-BOYKGGVD.js";
200
+ import "./chunk-CNPT6NKV.js";
201
+ import "./chunk-LJMX4SQ7.js";
200
202
  import {
201
203
  watch
202
- } from "./chunk-ADZHPN2B.js";
204
+ } from "./chunk-RVR5NPFL.js";
203
205
  import {
204
206
  __resetIdCounter,
205
207
  createId
206
- } from "./chunk-22OPSWMF.js";
208
+ } from "./chunk-CCKUNHVU.js";
209
+ import "./chunk-3HZOSMPX.js";
207
210
  import "./chunk-62BDYGAG.js";
208
211
  import {
209
212
  SVG_NS,
210
213
  tagFactory
211
- } from "./chunk-CPQKWCJ2.js";
214
+ } from "./chunk-CJ7KS3DF.js";
212
215
  import {
213
216
  bindDynamic
214
- } from "./chunk-K4LNUT5L.js";
215
- import "./chunk-F2UWOZ6J.js";
217
+ } from "./chunk-HQIAL7F2.js";
218
+ import "./chunk-RVWJGWCL.js";
216
219
  import {
217
220
  derived
218
- } from "./chunk-YFTBCCVH.js";
221
+ } from "./chunk-N7A6FKWW.js";
219
222
  import "./chunk-7ZHH77QA.js";
220
223
  import {
221
224
  effect,
222
225
  on
223
- } from "./chunk-QC3NP47A.js";
224
- import "./chunk-EXXSN3BW.js";
226
+ } from "./chunk-AE32RGWX.js";
227
+ import "./chunk-B3HOT4TK.js";
225
228
  import {
226
229
  disableSSR,
227
230
  enableSSR,
228
231
  getRequestScopedCache,
229
232
  getSSRStore,
233
+ isClient,
230
234
  isSSR,
235
+ isServer,
231
236
  runInSSRContext,
232
237
  withSSR
233
- } from "./chunk-7XHATCIH.js";
238
+ } from "./chunk-UYTYNCMZ.js";
234
239
  import "./chunk-5HZXGZ6T.js";
235
240
  import {
236
241
  batch,
@@ -238,12 +243,12 @@ import {
238
243
  external,
239
244
  isBatching,
240
245
  signal
241
- } from "./chunk-QKFVMIAV.js";
246
+ } from "./chunk-4FJSAUKB.js";
242
247
  import {
243
248
  retrack,
244
249
  setMaxDrainIterations,
245
250
  untracked
246
- } from "./chunk-4UTGDVSA.js";
251
+ } from "./chunk-MIRKTVH4.js";
247
252
  import {
248
253
  MAX_DRAIN_TEARDOWNS,
249
254
  checkLeaks,
@@ -255,8 +260,11 @@ import {
255
260
  reportError,
256
261
  setRuntimeErrorHandler,
257
262
  unregisterDisposer
258
- } from "./chunk-NYNYSPK7.js";
263
+ } from "./chunk-S37PZD6I.js";
264
+ import "./chunk-NT3ZYB5S.js";
265
+ import "./chunk-R5U7XKVJ.js";
259
266
  export {
267
+ ClientOnly,
260
268
  DynamicComponent,
261
269
  ErrorBoundary,
262
270
  ErrorDisplay,
@@ -362,7 +370,9 @@ export {
362
370
  input,
363
371
  ins,
364
372
  isBatching,
373
+ isClient,
365
374
  isSSR,
375
+ isServer,
366
376
  kbd,
367
377
  label,
368
378
  lazy,
package/dist/motion.cjs CHANGED
@@ -108,10 +108,21 @@ function defaultReport(error, context) {
108
108
  if (typeof console === "undefined") return;
109
109
  console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
110
110
  }
111
+ function requestErrorSink() {
112
+ const shared = globalThis[/* @__PURE__ */ Symbol.for("sibujs.ssr.v1")];
113
+ if (!shared) return void 0;
114
+ const store = shared.als ? shared.als.getStore() : shared.fallbackDepth > 0 ? shared.fallbackStore : void 0;
115
+ return store?.errors;
116
+ }
111
117
  function reportError(error, context) {
112
118
  const err = normalizeError(error);
113
119
  untrackedRun(() => {
114
120
  if (claimedByBoundary(err, context)) return;
121
+ const sink = requestErrorSink();
122
+ if (sink) {
123
+ sink.push({ error: err, context });
124
+ return;
125
+ }
115
126
  const s = state();
116
127
  const handler = s.handler;
117
128
  if (handler && s.handlerDepth === 0) {
@@ -838,7 +849,7 @@ function forEachSubscriber(signal2, visit) {
838
849
  }
839
850
 
840
851
  // src/reactivity/track.ts
841
- var _runtimeVersion = true ? "4.13.1" : "dev";
852
+ var _runtimeVersion = true ? "4.14.0" : "dev";
842
853
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
843
854
  function resolveReactiveApi() {
844
855
  const g = globalThis;
package/dist/motion.js CHANGED
@@ -20,11 +20,13 @@ import {
20
20
  stagger,
21
21
  transition,
22
22
  viewTransition
23
- } from "./chunk-2NC56RGY.js";
23
+ } from "./chunk-F67F77IA.js";
24
24
  import "./chunk-5HZXGZ6T.js";
25
- import "./chunk-QKFVMIAV.js";
26
- import "./chunk-4UTGDVSA.js";
27
- import "./chunk-NYNYSPK7.js";
25
+ import "./chunk-4FJSAUKB.js";
26
+ import "./chunk-MIRKTVH4.js";
27
+ import "./chunk-S37PZD6I.js";
28
+ import "./chunk-NT3ZYB5S.js";
29
+ import "./chunk-R5U7XKVJ.js";
28
30
  export {
29
31
  TransitionGroup,
30
32
  animate,
package/dist/patterns.cjs CHANGED
@@ -123,10 +123,21 @@ function defaultReport(error, context) {
123
123
  if (typeof console === "undefined") return;
124
124
  console.error(`[SibuJS] Uncaught error during ${describe(context)}: ${error.message}`, error);
125
125
  }
126
+ function requestErrorSink() {
127
+ const shared = globalThis[/* @__PURE__ */ Symbol.for("sibujs.ssr.v1")];
128
+ if (!shared) return void 0;
129
+ const store = shared.als ? shared.als.getStore() : shared.fallbackDepth > 0 ? shared.fallbackStore : void 0;
130
+ return store?.errors;
131
+ }
126
132
  function reportError(error, context) {
127
133
  const err = normalizeError(error);
128
134
  untrackedRun(() => {
129
135
  if (claimedByBoundary(err, context)) return;
136
+ const sink = requestErrorSink();
137
+ if (sink) {
138
+ sink.push({ error: err, context });
139
+ return;
140
+ }
130
141
  const s = state();
131
142
  const handler = s.handler;
132
143
  if (handler && s.handlerDepth === 0) {
@@ -734,7 +745,7 @@ function forEachSubscriber(signal2, visit) {
734
745
  }
735
746
 
736
747
  // src/reactivity/track.ts
737
- var _runtimeVersion = true ? "4.13.1" : "dev";
748
+ var _runtimeVersion = true ? "4.14.0" : "dev";
738
749
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
739
750
  function resolveReactiveApi() {
740
751
  const g = globalThis;
@@ -1051,6 +1062,8 @@ function detectSSRShared() {
1051
1062
  }
1052
1063
  if (mod) detected = new mod.AsyncLocalStorage();
1053
1064
  }
1065
+ const globalAls = globalThis.AsyncLocalStorage;
1066
+ if (!detected && typeof globalAls === "function") detected = new globalAls();
1054
1067
  } catch {
1055
1068
  detected = null;
1056
1069
  }
@@ -1139,7 +1152,7 @@ function disposeEffect(ctx) {
1139
1152
  }
1140
1153
  function effect(effectFn, options) {
1141
1154
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
1142
- if (isSSR()) return () => {
1155
+ if (isSSR() && !options?.server) return () => {
1143
1156
  };
1144
1157
  const ctx = {
1145
1158
  fn: effectFn,