sibujs 4.13.0 → 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-7K7W4AA5.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-GNSRXK34.js → chunk-4FJSAUKB.js} +11 -3
  16. package/dist/{chunk-IV74C2YX.js → chunk-4TQDTWWN.js} +6 -5
  17. package/dist/{chunk-L3QHS4O4.js → chunk-7ILEHTVX.js} +1 -1
  18. package/dist/{chunk-CK5F2IR5.js → chunk-A454RAEK.js} +4 -4
  19. package/dist/{chunk-BRRRVLHQ.js → chunk-AE32RGWX.js} +10 -6
  20. package/dist/{chunk-EXXSN3BW.js → chunk-B3HOT4TK.js} +1 -1
  21. package/dist/{chunk-F4W7WYXY.js → chunk-BKZ5N6NK.js} +8 -6
  22. package/dist/{chunk-MNEITZ27.js → chunk-BNXRWZZV.js} +49 -8
  23. package/dist/chunk-BOYKGGVD.js +2015 -0
  24. package/dist/{chunk-G6366I2A.js → chunk-CCKUNHVU.js} +2 -2
  25. package/dist/{chunk-346BYZL2.js → chunk-CJ7KS3DF.js} +16 -10
  26. package/dist/chunk-CNPT6NKV.js +1859 -0
  27. package/dist/{chunk-ENFRHCMK.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-HDKDBC6V.js → chunk-HQIAL7F2.js} +8 -6
  31. package/dist/{chunk-ZE5LF44R.js → chunk-JTFQ7IL4.js} +8 -8
  32. package/dist/{chunk-AM6DZLBM.js → chunk-LJMX4SQ7.js} +2 -2
  33. package/dist/{chunk-HVVRXRGO.js → chunk-MIRKTVH4.js} +7 -14
  34. package/dist/{chunk-YJ57VR6Y.js → chunk-N7A6FKWW.js} +5 -3
  35. package/dist/chunk-NT3ZYB5S.js +167 -0
  36. package/dist/{chunk-Z3EZX6DZ.js → chunk-QS6GMWDD.js} +151 -86
  37. package/dist/chunk-R5U7XKVJ.js +16 -0
  38. package/dist/{chunk-3C56VNCK.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-QV5WEOFD.js → chunk-SVULV7O4.js} +119 -26
  42. package/dist/{chunk-C2FAO6VQ.js → chunk-TLS4NVIB.js} +5 -5
  43. package/dist/{chunk-M25RIH6Y.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-24L5RDPS.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
@@ -0,0 +1,841 @@
1
+ /** Event handlers for {@link EachBindings}, typed per event name. */
2
+ type EachEventBindings = {
3
+ [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K], el: HTMLElement) => void;
4
+ };
5
+ /**
6
+ * What {@link EnhanceContext.each} attaches to one element.
7
+ *
8
+ * Every field maps one-to-one onto an existing `ctx.*` helper and is committed
9
+ * through it — this is a shorthand for calls you could write by hand, not a
10
+ * template language and not a second binding engine. There is no expression
11
+ * parsing, no string interpolation and no `eval`: every value is a plain
12
+ * function you wrote, so it stays CSP-safe and fully type-checked.
13
+ *
14
+ * Anything not covered here (two-way `model()`, listener options, a nested
15
+ * `enhance`) is written imperatively in the same callback — it receives the
16
+ * element, so `ctx.model(el, …)` beside a returned descriptor is normal.
17
+ */
18
+ interface EachBindings {
19
+ /** Reactive `textContent` — same as `ctx.text(el, value)`. */
20
+ text?: () => unknown;
21
+ /** Reactive attributes by name — same as `ctx.attr(el, name, value)`. */
22
+ attr?: Record<string, () => unknown>;
23
+ /** Reactive class toggles by class name — same as `ctx.classed(el, name, on)`. */
24
+ class?: Record<string, () => boolean>;
25
+ /** Reactive visibility — same as `ctx.show(el, when)`. */
26
+ show?: () => boolean;
27
+ /** Event listeners by event name — same as `ctx.on(el, event, handler)`. */
28
+ on?: EachEventBindings;
29
+ /** Per-element teardown, run with the rest of the enhancement's cleanups. */
30
+ cleanup?: () => void;
31
+ }
32
+ /**
33
+ * Helpers handed to an `enhance` setup. Every binding is fine-grained (its own
34
+ * effect) and auto-disposed when the root element (or the returned dispose) is
35
+ * torn down.
36
+ *
37
+ * Target resolution for every helper:
38
+ * - `"@name"` → a descendant marked `data-ref="name"` (the ergonomic form).
39
+ * - any other string → a raw CSS selector, queried within the root.
40
+ * - an `Element` → used as-is.
41
+ * - `null` / omitted (where allowed) → the root element itself.
42
+ */
43
+ interface EnhanceContext {
44
+ /** The enhanced root element (the server-rendered node). */
45
+ root: HTMLElement;
46
+ /** First descendant matching a `@ref`/selector (or the root for `null`). */
47
+ ref<T extends Element = HTMLElement>(target: string | null): T | null;
48
+ /** All descendants matching a `@ref`/selector. */
49
+ refs<T extends Element = HTMLElement>(target: string): T[];
50
+ /** Attach an auto-removed event listener to a target (root if `null`). */
51
+ on<K extends keyof HTMLElementEventMap>(target: string | Element | null, event: K, handler: (event: HTMLElementEventMap[K], el: HTMLElement) => void, options?: AddEventListenerOptions): void;
52
+ /** Reactively drive `textContent` of an existing node. */
53
+ text(target: string | Element | null, value: () => unknown): void;
54
+ /** Reactively drive an attribute; a `null`/`false`/`undefined` value removes it. */
55
+ attr(target: string | Element | null, name: string, value: () => unknown): void;
56
+ /** Reactively toggle a class on a node. */
57
+ classed(target: string | Element | null, name: string, on: () => boolean): void;
58
+ /** Reactively toggle visibility (sets `display:none` when `false`). */
59
+ show(target: string | Element | null, when: () => boolean): void;
60
+ /** Two-way bind a form control to a `[get, set]` signal tuple. */
61
+ model<T>(target: string | Element, state: readonly [() => T, (value: T) => void], options?: {
62
+ event?: string;
63
+ }): void;
64
+ /**
65
+ * Bind a set of elements the server already rendered — a board, a table, a
66
+ * keyboard, a timeline, a legend — one descriptor at a time.
67
+ *
68
+ * The callback receives each element and its index and returns what to
69
+ * attach; every field is committed through the matching `ctx.*` helper, so
70
+ * ownership, disposal, write elision, attribute sanitization and error
71
+ * routing are byte-for-byte the same as writing the calls out by hand. No
72
+ * element is created, replaced, moved or re-parented — node identity is
73
+ * preserved, which is the entire point of enhancing existing markup.
74
+ *
75
+ * ```ts
76
+ * ctx.each<HTMLButtonElement>("@square", (el) => {
77
+ * const square = el.dataset.square as Square;
78
+ * return {
79
+ * text: () => pieceAt(square),
80
+ * class: { selected: () => selected() === square },
81
+ * attr: { "aria-label": () => describe(square) },
82
+ * on: { click: () => choose(square) },
83
+ * };
84
+ * });
85
+ * ```
86
+ *
87
+ * The callback may also return nothing and wire the element imperatively —
88
+ * `ctx.model(el, …)`, `ctx.on(el, "click", h, { passive: true })` — for the
89
+ * cases the descriptor deliberately does not cover.
90
+ *
91
+ * Zero matches is a silent no-op. Calling `each` twice over the same
92
+ * elements creates two independent sets of bindings, exactly as calling
93
+ * `ctx.text()` twice on one node does; the helper is sugar over those calls
94
+ * and does not track what a previous call attached.
95
+ *
96
+ * @param target A `@ref`/CSS selector resolved with {@link EnhanceContext.refs},
97
+ * or any iterable of elements (an array, a `NodeList`, an `HTMLCollection`).
98
+ * @param describe Called once per element, in document order.
99
+ */
100
+ each<T extends Element = HTMLElement>(target: string | Iterable<Element>, describe: (element: T, index: number) => EachBindings | void): void;
101
+ /** Register arbitrary teardown to run on disposal. */
102
+ cleanup(fn: () => void): void;
103
+ }
104
+ /** A setup function for `enhance` — wire reactivity, optionally return cleanup.
105
+ * The `void | (() => void)` shape mirrors `useEffect`'s "return a teardown, or
106
+ * nothing". */
107
+ type EnhanceSetup = (ctx: EnhanceContext) => void | (() => void);
108
+ /**
109
+ * Attach reactivity to an existing element (typically server-rendered) without
110
+ * replacing it. Returns a dispose function; disposal is also wired to the
111
+ * element, so removing its subtree cleans everything up.
112
+ *
113
+ * **Setup is a transaction.** If `setup` throws, every binding, listener and
114
+ * cleanup it registered through the {@link EnhanceContext} is torn down and the
115
+ * original error is rethrown unchanged — the element is left exactly as
116
+ * unenhanced as it started, so a retry is legal. The rollback covers
117
+ * framework-owned resources only (`ctx.on`, `ctx.text`, `ctx.attr`,
118
+ * `ctx.classed`, `ctx.show`, `ctx.model`, `ctx.cleanup`, and a cleanup returned
119
+ * from setup); work the setup performed outside those helpers — `innerHTML`
120
+ * writes, network calls, global mutation — cannot be reversed generically, so
121
+ * register its undo with `ctx.cleanup()` as you go.
122
+ *
123
+ * **Ownership.** A successfully enhanced root is marked
124
+ * `data-sibu-enhanced="true"`; disposal removes it. The marker tracks *current*
125
+ * ownership rather than history, so a **disposed root can be enhanced again**,
126
+ * while enhancing a root that is still active is refused (dev-warns) to prevent
127
+ * two competing sets of bindings. See `docs/architecture/enhancement-lifecycle.md`.
128
+ *
129
+ * @param target An `Element` or a CSS selector resolved against `document`
130
+ * (the first match is used; see {@link enhanceAll} for many).
131
+ * @param setup Wires reactivity via the {@link EnhanceContext}.
132
+ * @throws Whatever `setup` throws, after rolling its resources back.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * // server HTML: <div data-counter><b data-ref="n">0</b><button data-ref="inc">+1</button></div>
137
+ * const [n, setN] = signal(0);
138
+ * enhance("[data-counter]", (ctx) => {
139
+ * ctx.text("@n", () => n());
140
+ * ctx.on("@inc", "click", () => setN((v) => v + 1));
141
+ * });
142
+ * ```
143
+ */
144
+ declare function enhance(target: Element | string, setup: EnhanceSetup): () => void;
145
+ /**
146
+ * Enhance every element matching a selector. Returns a single dispose that
147
+ * tears down all of them.
148
+ *
149
+ * The whole collection is one transaction: if any element's setup throws, the
150
+ * elements already enhanced are rolled back (newest first, mirroring stack
151
+ * unwinding) and the original error is rethrown. A caller that never received
152
+ * the aggregate disposer is therefore never left holding live enhancements it
153
+ * has no way to release. A teardown that fails during that rollback is reported
154
+ * and skipped — it neither aborts the remaining rollback nor replaces the
155
+ * original setup error.
156
+ *
157
+ * The returned disposer is idempotent, and the collection may be enhanced again
158
+ * afterwards (see {@link enhance} on marker lifetime).
159
+ *
160
+ * @throws Whatever the failing element's setup threw, after rolling back.
161
+ */
162
+ declare function enhanceAll(selector: string, setup: EnhanceSetup): () => void;
163
+
164
+ /** When an island activates. Declared per-element via `data-sibu-load`. */
165
+ type IslandStrategy = "load" | "idle" | "visible" | "interaction" | "media";
166
+ /** A lazy code loader: resolves to an island setup (or a module whose `default`
167
+ * is one). Only fetched when the island activates. Wrap with {@link lazyIsland}. */
168
+ type IslandLoader = () => Promise<EnhanceSetup | {
169
+ default: EnhanceSetup;
170
+ }>;
171
+ /**
172
+ * A loader that has been through {@link lazyIsland}.
173
+ *
174
+ * Exported for callers that want to be explicit about having wrapped one. It is
175
+ * NOT required by {@link IslandRegistration}, and that is deliberate.
176
+ *
177
+ * Requiring the brand would catch a forgotten `lazyIsland(...)` at compile time,
178
+ * which is where a mistake is cheapest to find — but it also rejects code that
179
+ * compiles today, and this package's contract is that the existing public API
180
+ * keeps working, widening rather than replacing, with a codemod for anything
181
+ * that cannot be widened. There is no codemod infrastructure here to ship one
182
+ * through, so the narrowing is not taken: the mistake is caught at mount time
183
+ * instead, by the thenable guard in `enhance()`, which throws immediately with
184
+ * an actionable message and leaves the element unenhanced. Loud and instant at
185
+ * runtime beats a type error that arrives with a breaking change attached.
186
+ *
187
+ * The brand is a phantom STRING-keyed property, never a `unique symbol`. A
188
+ * `unique symbol` has nominal identity per declaration, so two copies of this
189
+ * package's `.d.ts` in one dependency tree would produce two incompatible
190
+ * types. That is not hypothetical: the registry is deliberately shared through
191
+ * `Symbol.for` precisely because duplicate copies are expected. The property
192
+ * exists only in the type — the runtime marker is the global symbol below.
193
+ */
194
+ type LazyIslandLoader = IslandLoader & {
195
+ readonly __sibujsLazyIsland: true;
196
+ };
197
+ /**
198
+ * Either an inline setup, or a loader.
199
+ *
200
+ * Unchanged from 4.2: an unbranded `IslandLoader` is still accepted, so no
201
+ * previously-compiling code breaks. Wrapping with {@link lazyIsland} is what
202
+ * makes it *work* — see {@link LazyIslandLoader} for why this is not narrowed.
203
+ */
204
+ type IslandRegistration = EnhanceSetup | IslandLoader | LazyIslandLoader;
205
+ /**
206
+ * Mark a loader as lazy island code — its module is fetched only when the island
207
+ * activates, so a page ships ~0 JS for islands that never trigger.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * registerIsland("chart", lazyIsland(() => import("./islands/chart.js")));
212
+ * ```
213
+ */
214
+ declare function lazyIsland(loader: IslandLoader): LazyIslandLoader;
215
+ /**
216
+ * Register an island by name. The setup is applied via `enhance()` when a
217
+ * matching `[data-sibu-island="name"]` element activates.
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * registerIsland("counter", (ctx) => {
222
+ * const [n, setN] = signal(0);
223
+ * ctx.text("@n", () => n());
224
+ * ctx.on("@inc", "click", () => setN((v) => v + 1));
225
+ * });
226
+ * ```
227
+ */
228
+ declare function registerIsland(name: string, setup: IslandRegistration): void;
229
+ /** Remove a registration (mainly for tests / HMR). */
230
+ declare function unregisterIsland(name: string): void;
231
+ interface MountIslandsOptions {
232
+ /** IntersectionObserver options for the `visible` strategy. */
233
+ rootMargin?: string;
234
+ threshold?: number | number[];
235
+ }
236
+ /**
237
+ * Scan a root for `[data-sibu-island]` elements and activate each according to
238
+ * its `data-sibu-load` strategy (default `load`). Returns a cleanup function
239
+ * that cancels pending schedulers and disposes every mounted island.
240
+ *
241
+ * Strategies (`data-sibu-load`):
242
+ * - `load` — activate immediately (next microtask).
243
+ * - `idle` — `requestIdleCallback` (falls back to a timeout).
244
+ * - `visible` — when the element scrolls into view (IntersectionObserver).
245
+ * - `interaction` — on first pointer/focus/key/touch interaction.
246
+ * - `media` — when `data-sibu-media` (a media query) matches.
247
+ *
248
+ * **Error isolation is lifecycle isolation.** Each island is one `enhance()`
249
+ * transaction, so an island whose setup throws is reported and leaves zero live
250
+ * bindings and zero live listeners; siblings activate normally, and the failed
251
+ * island can be mounted again once its setup is fixed.
252
+ *
253
+ * **Remounting is supported.** Cleanup disposes each island and releases its
254
+ * `data-sibu-enhanced` marker, so the same server markup can be mounted again
255
+ * with a fresh generation. Mounting twice *without* cleanup is also safe:
256
+ * islands that currently own an enhancement are skipped, never double-wired.
257
+ *
258
+ * Activation can never outlive its owner. Cleanup landing before a lazy chunk
259
+ * resolves prevents activation entirely, and cleanup landing *during* a setup
260
+ * disposes the island rather than stranding it — DISPOSED never becomes ACTIVE.
261
+ * `data-sibu-hydrated` is hydration provenance rather than ownership, and is
262
+ * therefore not cleared on disposal; see
263
+ * `docs/architecture/enhancement-lifecycle.md`.
264
+ */
265
+ declare function mountIslands(root?: ParentNode | null, options?: MountIslandsOptions): () => void;
266
+
267
+ /** The attribute carrying an island's serialized props. */
268
+ declare const ISLAND_PROPS_ATTR = "data-sibu-props";
269
+ /** Options of a server-rendered component island. */
270
+ interface ComponentIslandOptions {
271
+ /** When the browser activates it (`data-sibu-load`). Default `"load"`. */
272
+ load?: IslandStrategy;
273
+ /** Media query for `load: "media"`. */
274
+ media?: string;
275
+ }
276
+ /**
277
+ * Render `Component(props)` as an island: on the server the markup carries the
278
+ * island marker, its props (JSON) and adoption ids; in the browser, outside an
279
+ * island runtime, the component simply renders.
280
+ *
281
+ * @param name Island name, matched by `registerIsland(name, islandComponent(Component))`.
282
+ * @param component The island's component.
283
+ * @param props JSON-serializable props, sent to the browser in the markup.
284
+ * @param options Load strategy.
285
+ * @returns The island's wrapper element (`display: contents`).
286
+ */
287
+ declare function componentIsland<P>(name: string, component: (props: P) => Node, props: P, options?: ComponentIslandOptions): HTMLElement;
288
+ /**
289
+ * The browser side of a server-rendered component island: an island setup
290
+ * (for `registerIsland` / `lazyIsland`) that reads the props from the markup
291
+ * and adopts the island's server nodes with `Component(props)`.
292
+ *
293
+ * @param component The same component the server rendered.
294
+ * @param validate Optional guard for the props read from the markup (they come
295
+ * from the page, so treat them as input).
296
+ * @returns An island setup.
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * registerIsland("cart", islandComponent(Cart));
301
+ * registerIsland("reviews", lazyIsland(() => import("./Reviews").then((m) => islandComponent(m.Reviews))));
302
+ * mountIslands();
303
+ * ```
304
+ */
305
+ declare function islandComponent<P>(component: (props: P) => Node, validate?: (props: unknown) => props is P): EnhanceSetup;
306
+
307
+ /** Attribute carrying an element's creation-scope id in adopt-marked server output. */
308
+ declare const ADOPT_ID_ATTR = "data-sibu-h";
309
+ /** One divergence between the server markup and the client tree. */
310
+ interface AdoptMismatch {
311
+ kind: "tag" | "missing" | "extra" | "text";
312
+ /** Where: the DOM path of the server node (or of the parent the client node went into). */
313
+ path: string;
314
+ /** The creation-scope id involved, when there is one. */
315
+ id: string | null;
316
+ serverValue: string;
317
+ clientValue: string;
318
+ /** What usually causes this divergence. */
319
+ cause: string;
320
+ message: string;
321
+ }
322
+ /** Options of an adopting `hydrate()`. */
323
+ interface AdoptOptions {
324
+ /** Called for every divergence instead of the development warning. */
325
+ onMismatch?: (report: AdoptMismatch) => void;
326
+ /**
327
+ * How long to wait for lazy chunks and route loads before releasing the
328
+ * server markup nothing claimed. Default 10 000 ms.
329
+ */
330
+ timeout?: number;
331
+ }
332
+ /** What an adopting hydration did. */
333
+ interface AdoptResult {
334
+ /** Server elements reused as client elements. */
335
+ adopted: number;
336
+ /** Elements the client had to create (0 on matching markup). */
337
+ created: number;
338
+ /** Server nodes removed because nothing claimed them. */
339
+ removed: number;
340
+ /** Every divergence found. */
341
+ mismatches: AdoptMismatch[];
342
+ /** True when the root itself did not match and the whole tree was replaced. */
343
+ replacedRoot: boolean;
344
+ }
345
+
346
+ /** Default time a server render waits for pending work before serializing anyway. */
347
+ declare const DEFAULT_SETTLE_TIMEOUT = 5000;
348
+ /** Default cap on the transferred state, in UTF-8 bytes (the `serializeState` default). */
349
+ declare const DEFAULT_TRANSFER_MAX_BYTES: number;
350
+ /** Outcome of {@link settled}. */
351
+ interface SettleResult {
352
+ /** True when the timeout elapsed with work still pending. */
353
+ timedOut: boolean;
354
+ /** Labels of the work still pending at the timeout (`query:/api/catalog`). Empty when settled. */
355
+ pending: string[];
356
+ }
357
+ /** Options of {@link settled}. */
358
+ interface SettleOptions {
359
+ /** Milliseconds to wait for pending work. Default 5000. */
360
+ timeout?: number;
361
+ }
362
+ /**
363
+ * Wait until the current request's render has settled: every deferred
364
+ * directive commit has run and every registered async unit (lazy chunks,
365
+ * `query()` / `resource()` fetches, router navigation and route loads,
366
+ * `prerenderWait()` promises) has settled — or until `timeout`.
367
+ *
368
+ * Must be awaited inside the request's scope (`runInSSRContext`); outside one
369
+ * there is no registry, and it only drains the microtask queue.
370
+ *
371
+ * @param _root The tree being rendered. Accepted for symmetry with test
372
+ * helpers; settling is decided by the request's registry, not by the DOM.
373
+ * @param options `timeout` in milliseconds (default 5000).
374
+ * @returns Whether it timed out, and what was still pending.
375
+ */
376
+ declare function settled(_root?: Node | null, options?: SettleOptions): Promise<SettleResult>;
377
+ /** Diagnostics of one async server render. */
378
+ interface RenderDiagnostics {
379
+ /** True when the settle timeout elapsed; the output shows fallbacks for what was pending. */
380
+ timedOut: boolean;
381
+ /** Work still pending at the timeout. */
382
+ pending: string[];
383
+ /** Browser-only APIs read during the render (see `installServerDom`), with the reading site. */
384
+ browserApis: string[];
385
+ /** Size of the transferred state in UTF-8 bytes (0 when nothing was transferred). */
386
+ stateBytes: number;
387
+ /** The largest transferred entries, biggest first: `q:/api/catalog` → bytes. */
388
+ largestState: Array<{
389
+ key: string;
390
+ bytes: number;
391
+ }>;
392
+ /** Entries left out of the transferred state because it exceeded `maxStateBytes`. */
393
+ droppedState: string[];
394
+ /** Wall-clock render time in milliseconds. */
395
+ durationMs: number;
396
+ }
397
+ /** Result of {@link renderToStringAsync} and {@link renderToDocumentAsync}. */
398
+ interface RenderResult {
399
+ /** The rendered markup: the body content, or the whole document for `renderToDocumentAsync`. */
400
+ html: string;
401
+ /** Head tags collected during the render (`<title>`, meta, link, JSON-LD), escaped. */
402
+ head: string;
403
+ /** The transfer `<script>` restoring server data in the browser, or `""`. */
404
+ state: string;
405
+ /** The transferred data itself (what `state` assigns), or `null`. */
406
+ stateData: Record<string, unknown> | null;
407
+ /** HTTP status: 200 unless a router render decided otherwise (see `renderRoute`). */
408
+ status: number;
409
+ /** Diagnostics: pending work at the timeout, browser-API reads, state size. */
410
+ diagnostics: RenderDiagnostics;
411
+ }
412
+ /** Options of {@link renderToStringAsync}. */
413
+ interface AsyncRenderOptions {
414
+ /** Milliseconds to wait for pending work before serializing anyway. Default 5000. */
415
+ timeout?: number;
416
+ /** Embed the data the render fetched so the browser does not fetch it again. Default `true`. */
417
+ transfer?: boolean;
418
+ /** CSP nonce for the transfer script. */
419
+ nonce?: string;
420
+ /** Cap on the transferred state in bytes. Larger entries are dropped (largest first). Default 1 MB. */
421
+ maxStateBytes?: number;
422
+ /**
423
+ * Where the `data-sibu-ssr="true"` provenance marker goes. Default `"root"`:
424
+ * top-level elements only (`renderToString` stamps every element, ~20 bytes
425
+ * each, and nothing reads it beneath the root).
426
+ */
427
+ marker?: "all" | "root" | "none";
428
+ /**
429
+ * `"adopt"` marks the output for `hydrate(App, root, { strategy: "adopt" })`:
430
+ * every element built by a tag factory or `html` carries its creation id
431
+ * (`data-sibu-h`), so the browser binds to these nodes instead of replacing
432
+ * them. Default: no markers (the output `hydrate()`'s replace strategy uses).
433
+ */
434
+ hydration?: "adopt" | "replace";
435
+ /**
436
+ * Log a one-block summary of the render in development (time, settle
437
+ * outcome, what was pending, browser-API reads, transferred state and its
438
+ * largest entries). A string labels it (`renderRoute` passes the URL).
439
+ */
440
+ log?: boolean | string;
441
+ }
442
+ /**
443
+ * The diagnostics of one server render as readable text — what `log: true`
444
+ * prints, for a custom logger or a test failure message.
445
+ *
446
+ * @param d The render's `diagnostics`.
447
+ * @param label What was rendered (a URL).
448
+ * @returns A multi-line summary.
449
+ */
450
+ declare function formatRenderDiagnostics(d: RenderDiagnostics, label?: string): string;
451
+ /**
452
+ * Describe where a node sits, for an error message: `main > section.grid > div.card`.
453
+ * The runtime has no component stack, so the DOM position is the component path.
454
+ */
455
+ declare function describeNodePath(node: unknown): string;
456
+ /**
457
+ * The error an async server render rejects with for an error no
458
+ * `ErrorBoundary` claimed: the original as `cause`, the component name and DOM
459
+ * path of the failing node in the message.
460
+ * @internal
461
+ */
462
+ declare function renderFailure(entry: {
463
+ error: unknown;
464
+ context: {
465
+ name?: string;
466
+ node?: unknown;
467
+ };
468
+ }): Error;
469
+ /**
470
+ * Render a tree to HTML once it has settled — one `await`, no manual
471
+ * microtask waits.
472
+ *
473
+ * Pass a FUNCTION so the tree is built inside the request scope: its queries
474
+ * then use the request's cache and its work is awaited. Called outside
475
+ * `runInSSRContext`, it opens a scope of its own.
476
+ *
477
+ * Rejects when an error escapes the tree (no `ErrorBoundary` claimed it), with
478
+ * the DOM path of the failing node in the message. A settle timeout does not
479
+ * reject: what is ready is serialized, `Suspense` and `lazy()` keep their
480
+ * fallbacks, and `diagnostics.pending` names what was still running.
481
+ *
482
+ * @param input The tree, or a function building it.
483
+ * @param options Timeout, state transfer, nonce, state cap.
484
+ * @returns `{ html, head, state, stateData, status, diagnostics }`.
485
+ *
486
+ * @example
487
+ * ```ts
488
+ * const { html, head, state } = await renderToStringAsync(() => App());
489
+ * ```
490
+ */
491
+ declare function renderToStringAsync(input: Node | (() => Node), options?: AsyncRenderOptions): Promise<RenderResult>;
492
+ /**
493
+ * {@link renderToStringAsync}, wrapped in the document `renderToDocument`
494
+ * produces. The collected head tags go into `<head>` and the transfer script
495
+ * at the end of `<body>`, before `options.scripts`.
496
+ *
497
+ * @param input The tree, or a function building it.
498
+ * @param options Document options plus the async render options.
499
+ * @returns The render result; `html` is the whole document.
500
+ */
501
+ declare function renderToDocumentAsync(input: Node | (() => Node), options?: DocumentOptions & AsyncRenderOptions): Promise<RenderResult>;
502
+
503
+ /** Options of the streaming `renderToReadableStream(() => App(), options)`. */
504
+ interface StreamRenderOptions extends AsyncRenderOptions {
505
+ /**
506
+ * Wrap the stream in `renderToDocument`'s document (head tags flushed with
507
+ * the shell, the transfer script and `scripts` at the end). Default `{}`;
508
+ * `false` streams the body markup only.
509
+ */
510
+ document?: DocumentOptions | false;
511
+ /** Total time for every boundary (default 10 000 ms); a boundary still pending keeps its fallback. */
512
+ timeout?: number;
513
+ /** Time for the shell itself (default 5000 ms); past it the shell flushes with what is ready. */
514
+ shellTimeout?: number;
515
+ }
516
+
517
+ /**
518
+ * Serialize an element's open tag — `<tag attr="…" …` without the closing `>`
519
+ * — or return `null` when the element must not be emitted at all.
520
+ *
521
+ * THE single attribute serializer for server-emitted element trees, shared by
522
+ * `renderToString` and `renderToStream`:
523
+ *
524
+ * - names are validated, `on*` and `srcdoc` dropped, policy sinks sanitized
525
+ * through the same `sanitizeAttributeString` the client writers use;
526
+ * - a `<meta>` whose EMITTED attributes form a refresh directive the shared
527
+ * policy forbids is dropped whole, exactly as `renderToDocument`'s `meta`
528
+ * option and `Head()` drop such an entry. DOM built through the
529
+ * framework's writers can never reach this state; DOM built any other way
530
+ * (application code, a third-party library) could, and rendering target is
531
+ * not a security boundary.
532
+ * - on a `foreign` (SVG / MathML) element, names keep their case and their
533
+ * namespace prefix, and an SVG animation may not target a URL or handler
534
+ * attribute — its `attributeName` is omitted, as the client writers omit it.
535
+ * - an `<iframe>` built by `embed()` is re-judged against what `embed()`
536
+ * wrote (`embedSerializedAttribute`): only the attributes it writes (plus
537
+ * `id`, `class`, `style`, `width`, `height`, `data-*`, `aria-*`) are
538
+ * served, `src` must match the allowlist the frame was built under,
539
+ * `sandbox` / `allow` can only be narrower than what it wrote,
540
+ * `allowfullscreen` follows the served `allow`, and a frame without a
541
+ * non-blank `title` is not served — so a frame changed behind the
542
+ * framework's back is never served wider than `embed()` built it.
543
+ */
544
+ /**
545
+ * How a render path marks its output.
546
+ * @internal
547
+ */
548
+ interface SerializeOptions {
549
+ /**
550
+ * Where `data-sibu-ssr="true"` goes: on every element (`renderToString`'s
551
+ * historical output), on top-level elements only, or nowhere.
552
+ */
553
+ marker: "all" | "root" | "none";
554
+ /** Adoption ids (`data-sibu-h`) of elements built under an adopt render. */
555
+ ids: WeakMap<Element, string> | null;
556
+ }
557
+ /**
558
+ * Converts an element tree to an HTML string for server-side rendering.
559
+ * HTML, SVG and MathML elements are serialized; SVG and MathML keep the case
560
+ * of their tag and attribute names and their `xlink:` / `xml:` prefixes.
561
+ *
562
+ * Security: attribute names are validated, `on*` handlers are dropped,
563
+ * URL-bearing attributes are routed through `sanitizeUrl`, attribute
564
+ * values are escaped against both `"` and `'`, and an SVG animation may not
565
+ * target a URL or handler attribute. `<script>` and `<style>` tags are
566
+ * stripped from the serialized output.
567
+ */
568
+ declare function renderToString(element: HTMLElement | DocumentFragment | Node): string;
569
+ /**
570
+ * `renderToString` with explicit marker options — the async renderers' entry.
571
+ * @internal
572
+ */
573
+ declare function renderToStringWith(element: Node, opts: SerializeOptions): string;
574
+ interface HydrateOptions {
575
+ /**
576
+ * Enable dev-mode hydration diagnostics. When true, the walker
577
+ * compares the server-rendered tree against the client tree and
578
+ * logs the first tag mismatch, attribute difference, or child count
579
+ * mismatch to the console. Disabled by default.
580
+ */
581
+ diagnostics?: boolean;
582
+ /**
583
+ * Called for each detected mismatch instead of the default console
584
+ * warning. Useful for routing diagnostics to a telemetry system.
585
+ */
586
+ onMismatch?: (report: HydrationMismatch) => void;
587
+ }
588
+ interface HydrationMismatch {
589
+ kind: "tag" | "attribute" | "child-count" | "text";
590
+ path: string;
591
+ serverValue: string;
592
+ clientValue: string;
593
+ message: string;
594
+ }
595
+ /**
596
+ * Activates a server-rendered SibuJS application by building the live client
597
+ * tree and **replacing** the inert server-rendered subtree with it.
598
+ *
599
+ * SibuJS uses replacement hydration rather than DOM adoption: the server markup
600
+ * is discarded, not attached to. It is not the case that listeners and bindings
601
+ * are wired onto the existing server nodes — reactive bindings belong to the
602
+ * nodes `component()` produced, and adopting server nodes would leave the
603
+ * visible DOM permanently frozen.
604
+ *
605
+ * Consequences worth knowing before deploying SSR:
606
+ *
607
+ * - DOM node identity is not preserved.
608
+ * - Pre-hydration user input, checkbox state, and focus are discarded.
609
+ * - Conversely, partial-adoption mismatch bugs are avoided by this design —
610
+ * the client tree is always authoritative.
611
+ *
612
+ * Re-hydrating the same container disposes the previous client tree first.
613
+ *
614
+ * When `options.diagnostics` is true, the walker reports the first
615
+ * server/client mismatch it finds. This is a dev-mode tool — pass
616
+ * `diagnostics: false` (or omit it) in production.
617
+ *
618
+ * @see docs/architecture/hydration.md
619
+ */
620
+ declare function hydrate(component: () => Node, container: Element, options: AdoptOptions & {
621
+ strategy: "adopt";
622
+ }): Promise<AdoptResult>;
623
+ declare function hydrate(component: () => HTMLElement, container: HTMLElement, options?: HydrateOptions & {
624
+ strategy?: "replace";
625
+ }): void;
626
+ /**
627
+ * Branded type for raw HTML that has been explicitly marked as trusted.
628
+ * Use `trustHTML()` to create a value of this type. This prevents
629
+ * accidental injection of unsanitized user input into `headExtra`.
630
+ */
631
+ type TrustedHTML = string & {
632
+ readonly __brand: "TrustedHTML";
633
+ };
634
+ /**
635
+ * Mark an HTML string as trusted for use in contexts that accept raw HTML
636
+ * (e.g. `renderToDocument({ headExtra })`). Only call this on
637
+ * developer-controlled strings — never on user input.
638
+ *
639
+ * @example
640
+ * ```ts
641
+ * const extra = trustHTML('<link rel="preconnect" href="https://fonts.googleapis.com">');
642
+ * renderToDocument(App, { headExtra: extra });
643
+ * ```
644
+ */
645
+ declare function trustHTML(html: string): TrustedHTML;
646
+ /**
647
+ * Should this meta entry be dropped entirely?
648
+ *
649
+ * Delegates to the SHARED meta-refresh policy. This used to be a private copy
650
+ * of four `includes()` checks that happened to match the client's private copy
651
+ * — two implementations of one rule, so a fix to either would silently diverge
652
+ * from the other. The exported name is kept for back-compat; the policy is no
653
+ * longer local.
654
+ *
655
+ * Returns true for a forbidden refresh directive AND for an entry carrying
656
+ * duplicate case-insensitive attribute names, since there the value validated
657
+ * would not be the value the browser ends up honouring.
658
+ */
659
+ declare function isDangerousMetaRefresh(metaProps: Record<string, string>): boolean;
660
+ /**
661
+ * Renders a component to a full HTML document string.
662
+ *
663
+ * `headExtra` requires a `TrustedHTML` value created via `trustHTML()`.
664
+ * This prevents accidental injection of unsanitized user input.
665
+ *
666
+ * Security: meta/link/bodyAttrs keys are validated against
667
+ * `SAFE_ATTR_NAME` (rejecting crafted keys that would break out of the
668
+ * attribute context). URL attributes in meta/link/scripts/bodyAttrs pass
669
+ * through `sanitizeUrl`. The page `title` is HTML-escaped.
670
+ */
671
+ /** Document options shared by `renderToDocument` and `renderToDocumentAsync`. */
672
+ interface DocumentOptions {
673
+ title?: string;
674
+ meta?: Record<string, string>[];
675
+ links?: Record<string, string>[];
676
+ /**
677
+ * External script URLs appended to `<body>`. Each passes the scheme
678
+ * allowlist; an allowed URL is still code that runs with the page's
679
+ * authority, so this option is an explicit trust decision — first-party
680
+ * or pinned URLs only, never request-derived values.
681
+ */
682
+ scripts?: string[];
683
+ bodyAttrs?: Record<string, string>;
684
+ /**
685
+ * Raw HTML injected into `<head>`. Must be wrapped in `trustHTML()`
686
+ * to confirm the content is developer-controlled.
687
+ */
688
+ headExtra?: TrustedHTML;
689
+ }
690
+ declare function renderToDocument(component: () => HTMLElement, options?: DocumentOptions): string;
691
+ /**
692
+ * The document `renderToDocument` emits, around already-rendered `content`.
693
+ * `head` and `bodyEnd` are framework-generated markup (collected head tags,
694
+ * the transfer script), escaped by their producers.
695
+ * @internal
696
+ */
697
+ declare function assembleDocument(content: string, options: DocumentOptions, head: string, bodyEnd: string): string;
698
+ /**
699
+ * Renders a component tree to an async iterable of HTML chunks.
700
+ * Enables progressive server-side rendering — the consumer can write
701
+ * each chunk to a response stream as it becomes available.
702
+ *
703
+ * Same security posture as `renderToString`.
704
+ */
705
+ declare function renderToStream(element: HTMLElement | DocumentFragment | Node): AsyncGenerator<string>;
706
+ /**
707
+ * Collects the full output of renderToStream into a string.
708
+ */
709
+ declare function collectStream(stream: AsyncGenerator<string> | AsyncIterable<string>): Promise<string>;
710
+ /**
711
+ * Renders a component tree to a Web `ReadableStream<string>`.
712
+ *
713
+ * Uses pull-based backpressure — chunks are produced on demand.
714
+ *
715
+ * **Environment.** Supported on SibuJS's declared Node runtime (`>=22.3.0`) and
716
+ * on any other environment providing both the Web Streams API *and* a DOM
717
+ * implementation. Streaming SSR requires the same server DOM as every other
718
+ * SibuJS SSR API: this function takes real `Node` objects and walks them, so a
719
+ * DOM-less runtime cannot produce the input, let alone render it. Do not read
720
+ * "returns a Web stream" as "runs anywhere Web streams exist".
721
+ *
722
+ * **Semantics.** Suspense boundaries encountered during the walk are resolved
723
+ * as a batch before the affected chunk is emitted — the shell is not flushed
724
+ * ahead of individually-completing boundaries. See
725
+ * `docs/architecture/ssr.md` for the precise streaming and failure semantics.
726
+ */
727
+ declare function renderToReadableStream(element: HTMLElement | DocumentFragment | Node): ReadableStream<string>;
728
+ /**
729
+ * Stream a page built by `input` with out-of-order `Suspense`: the head and the
730
+ * shell (with each boundary's fallback) flush as soon as everything outside the
731
+ * boundaries has settled, then each boundary streams when it is ready, with a
732
+ * CSP-nonce-aware swap script, then the transferred state and the scripts.
733
+ * Backpressure: the next chunk is produced once the previous one was read.
734
+ * See `ssrStream.ts`.
735
+ */
736
+ declare function renderToReadableStream(input: () => Node, options?: StreamRenderOptions): ReadableStream<string>;
737
+ declare function island(id: string, component: () => HTMLElement): HTMLElement;
738
+ /**
739
+ * A component island rendered on the server: the markup carries the island
740
+ * marker, its serialized `props` and adoption ids, and the browser adopts just
741
+ * this subtree with `registerIsland(name, islandComponent(Component))` +
742
+ * `mountIslands()`. See `componentIsland`.
743
+ */
744
+ declare function island<P>(name: string, component: (props: P) => Node, props: P, options?: ComponentIslandOptions): HTMLElement;
745
+ declare function hydrateIslands(container: HTMLElement, islands: Record<string, () => HTMLElement>): void;
746
+ /**
747
+ * Progressively hydrate islands only when they enter the viewport.
748
+ * Uses IntersectionObserver to defer hydration of off-screen islands,
749
+ * reducing initial JavaScript execution cost.
750
+ *
751
+ * Returns a cleanup function that disconnects all observers.
752
+ */
753
+ declare function hydrateProgressively(container: HTMLElement, islands: Record<string, () => HTMLElement>, options?: IntersectionObserverInit): () => void;
754
+ /**
755
+ * Reset SSR state between requests. Call at the start of each SSR render
756
+ * to prevent ID drift in long-lived server processes.
757
+ *
758
+ * When running inside `runInSSRContext`, each request already owns its
759
+ * own counter via AsyncLocalStorage — this hook only touches the
760
+ * module-global fallback used outside that context.
761
+ */
762
+ declare function resetSSRState(): void;
763
+ /**
764
+ * Create a suspense boundary for SSR streaming.
765
+ * Renders fallback HTML inline and returns a promise for the resolved content.
766
+ *
767
+ * The returned element contains the fallback UI with a `data-sibu-suspense-id`
768
+ * marker. The promise resolves to `{ id, html }` once async content is ready.
769
+ *
770
+ * The promise never rejects. Any failure to produce the content HTML — the
771
+ * content factory throwing synchronously, its promise rejecting, the timeout,
772
+ * or rendering the resolved element throwing — resolves it with the fallback
773
+ * HTML, so the stream still produces a deterministic swap payload.
774
+ */
775
+ declare function ssrSuspense(props: {
776
+ fallback: () => HTMLElement;
777
+ content: () => Promise<HTMLElement>;
778
+ /** Milliseconds before the content promise is rejected. Defaults to 30000. */
779
+ timeoutMs?: number;
780
+ }): {
781
+ element: HTMLElement;
782
+ promise: Promise<{
783
+ id: string;
784
+ html: string;
785
+ }>;
786
+ };
787
+ /**
788
+ * Generate an inline script that swaps a suspense fallback with resolved content.
789
+ *
790
+ * Security: the `id` is validated against a strict allowlist. Values that
791
+ * do not match throw, so an attacker-controlled id cannot inject context
792
+ * breakers into the selector or the JS string. `ssrSuspense()`'s internal
793
+ * generator always produces allowlist-safe ids.
794
+ */
795
+ declare function suspenseSwapScript(id: string, nonce?: string): string;
796
+ /**
797
+ * Renders a component tree with suspense boundaries as a stream.
798
+ * Yields the main tree HTML first (including fallback content for suspended
799
+ * boundaries), then flushes resolved content with inline swap scripts.
800
+ *
801
+ * Supports an optional `nonce` that is propagated to the resolved-content
802
+ * swap scripts so the stream works with strict CSP.
803
+ */
804
+ declare function renderToSuspenseStream(element: HTMLElement | DocumentFragment | Node, pendingBoundaries?: Promise<{
805
+ id: string;
806
+ html: string;
807
+ }>[], options?: {
808
+ nonce?: string;
809
+ }): AsyncGenerator<string>;
810
+ /**
811
+ * Escape a JSON string for safe embedding inside a `<script>` tag. This
812
+ * goes beyond the usual `</script>` concern:
813
+ *
814
+ * - `<`, `>`, `&` are unicode-escaped so nothing inside a string literal
815
+ * can close the script tag or start a new one.
816
+ * - `U+2028` (LINE SEPARATOR) and `U+2029` (PARAGRAPH SEPARATOR) are
817
+ * unicode-escaped. Before ES2019 these were illegal inside a JS string
818
+ * literal, so including them verbatim would cause a SyntaxError on
819
+ * older engines and could break out of string context.
820
+ */
821
+ declare function escapeScriptJson(json: string): string;
822
+ declare function serializeState(state: Record<string, unknown>, nonce?: string, options?: {
823
+ maxBytes?: number;
824
+ /**
825
+ * The window global to assign (default `__SIBU_SSR_DATA__`). Must be a
826
+ * plain identifier; anything else throws.
827
+ */
828
+ global?: string;
829
+ }): string;
830
+ /**
831
+ * Retrieve state that was embedded by `serializeState()` during SSR.
832
+ *
833
+ * When a `validate` function is provided, it acts as a type guard —
834
+ * only data that passes validation is returned. This prevents
835
+ * tampered SSR payloads from being trusted by the client.
836
+ *
837
+ * @param validate Optional type guard to verify data integrity
838
+ */
839
+ declare function deserializeState<T = Record<string, unknown>>(validate?: (data: unknown) => data is T): T | undefined;
840
+
841
+ export { type IslandStrategy as $, type AsyncRenderOptions as A, DEFAULT_TRANSFER_MAX_BYTES as B, type ComponentIslandOptions as C, type DocumentOptions as D, type RenderDiagnostics as E, type SettleOptions as F, type SettleResult as G, type HydrateOptions as H, ISLAND_PROPS_ATTR as I, type StreamRenderOptions as J, componentIsland as K, describeNodePath as L, formatRenderDiagnostics as M, islandComponent as N, renderFailure as O, renderToDocumentAsync as P, renderToStringAsync as Q, type RenderResult as R, type SerializeOptions as S, type TrustedHTML as T, settled as U, type EachBindings as V, type EachEventBindings as W, type EnhanceContext as X, type EnhanceSetup as Y, type IslandLoader as Z, type IslandRegistration as _, type HydrationMismatch as a, type LazyIslandLoader as a0, type MountIslandsOptions as a1, enhance as a2, enhanceAll as a3, lazyIsland as a4, mountIslands as a5, registerIsland as a6, unregisterIsland as a7, assembleDocument as b, collectStream as c, deserializeState as d, escapeScriptJson as e, hydrateIslands as f, hydrateProgressively as g, hydrate as h, isDangerousMetaRefresh as i, island as j, renderToReadableStream as k, renderToStream as l, renderToString as m, renderToStringWith as n, renderToSuspenseStream as o, resetSSRState as p, ssrSuspense as q, renderToDocument as r, serializeState as s, suspenseSwapScript as t, trustHTML as u, ADOPT_ID_ATTR as v, type AdoptMismatch as w, type AdoptOptions as x, type AdoptResult as y, DEFAULT_SETTLE_TIMEOUT as z };