@elurjs/kit 2.4.10 → 2.5.1

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 (188) hide show
  1. package/CHANGELOG.md +313 -0
  2. package/README.md +248 -15
  3. package/dist/lib/action/index.cjs +1 -1
  4. package/dist/lib/action/index.js +1 -1
  5. package/dist/lib/{action-DZHihU3Z.cjs → action-BiE4tEty.cjs} +2 -2
  6. package/dist/lib/{action-DZHihU3Z.cjs.map → action-BiE4tEty.cjs.map} +1 -1
  7. package/dist/lib/{action-BB6N5yD-.js → action-CXVk8rWt.js} +3 -3
  8. package/dist/lib/{action-BB6N5yD-.js.map → action-CXVk8rWt.js.map} +1 -1
  9. package/dist/lib/adapter-DNsjXvq5.js +160 -0
  10. package/dist/lib/adapter-DNsjXvq5.js.map +1 -0
  11. package/dist/lib/adapter-Dz2hzR36.cjs +3 -0
  12. package/dist/lib/adapter-Dz2hzR36.cjs.map +1 -0
  13. package/dist/lib/adapters/bun.cjs +2 -2
  14. package/dist/lib/adapters/bun.cjs.map +1 -1
  15. package/dist/lib/adapters/bun.js +17 -9
  16. package/dist/lib/adapters/bun.js.map +1 -1
  17. package/dist/lib/adapters/index.d.cts +34 -0
  18. package/dist/lib/adapters/index.d.ts +34 -0
  19. package/dist/lib/adapters/netlify.cjs +1 -1
  20. package/dist/lib/adapters/netlify.js +7 -7
  21. package/dist/lib/adapters/node.cjs +5 -5
  22. package/dist/lib/adapters/node.cjs.map +1 -1
  23. package/dist/lib/adapters/node.js +20 -12
  24. package/dist/lib/adapters/node.js.map +1 -1
  25. package/dist/lib/adapters/vercel.cjs +1 -1
  26. package/dist/lib/adapters/vercel.js +8 -8
  27. package/dist/lib/build/build.d.cts +40 -0
  28. package/dist/lib/build/build.d.ts +40 -0
  29. package/dist/lib/build/document-shell.d.cts +47 -1
  30. package/dist/lib/build/document-shell.d.ts +47 -1
  31. package/dist/lib/build/vite-build.d.cts +25 -2
  32. package/dist/lib/build/vite-build.d.ts +25 -2
  33. package/dist/lib/cache/adapter.d.cts +41 -0
  34. package/dist/lib/cache/adapter.d.ts +41 -0
  35. package/dist/lib/cache/index.cjs +2 -0
  36. package/dist/lib/cache/index.cjs.map +1 -0
  37. package/dist/lib/cache/index.d.cts +5 -0
  38. package/dist/lib/cache/index.d.ts +5 -0
  39. package/dist/lib/cache/index.js +113 -0
  40. package/dist/lib/cache/index.js.map +1 -0
  41. package/dist/lib/cache/invalidation.d.cts +36 -0
  42. package/dist/lib/cache/invalidation.d.ts +36 -0
  43. package/dist/lib/cache/redis-adapter.d.cts +49 -0
  44. package/dist/lib/cache/redis-adapter.d.ts +49 -0
  45. package/dist/lib/cache-DGhTYyqh.js +50 -0
  46. package/dist/lib/cache-DGhTYyqh.js.map +1 -0
  47. package/dist/lib/cache-X1n4Ossu.cjs +2 -0
  48. package/dist/lib/cache-X1n4Ossu.cjs.map +1 -0
  49. package/dist/lib/cli/output.d.cts +58 -0
  50. package/dist/lib/cli/output.d.ts +58 -0
  51. package/dist/lib/cli/ports.d.cts +20 -0
  52. package/dist/lib/cli/ports.d.ts +20 -0
  53. package/dist/lib/cli.cjs +2559 -1488
  54. package/dist/lib/cli.cjs.map +1 -1
  55. package/dist/lib/cli.d.cts +21 -0
  56. package/dist/lib/cli.d.ts +21 -0
  57. package/dist/lib/cli.js +2558 -1488
  58. package/dist/lib/cli.js.map +1 -1
  59. package/dist/lib/config/index.cjs +1 -1
  60. package/dist/lib/config/index.cjs.map +1 -1
  61. package/dist/lib/config/index.d.cts +79 -0
  62. package/dist/lib/config/index.d.ts +79 -0
  63. package/dist/lib/config/index.js +21 -14
  64. package/dist/lib/config/index.js.map +1 -1
  65. package/dist/lib/{errors-ePYxDlhU.js → errors-j4cg9VN3.js} +24 -30
  66. package/dist/lib/{errors-ePYxDlhU.js.map → errors-j4cg9VN3.js.map} +1 -1
  67. package/dist/lib/errors-kSl9yLkg.cjs +2 -0
  68. package/dist/lib/{errors-0X7pCFNU.cjs.map → errors-kSl9yLkg.cjs.map} +1 -1
  69. package/dist/lib/hydrate-BSLuI0Nh.cjs +2 -0
  70. package/dist/lib/hydrate-BSLuI0Nh.cjs.map +1 -0
  71. package/dist/lib/hydrate-BaS7-d8C.js +133 -0
  72. package/dist/lib/hydrate-BaS7-d8C.js.map +1 -0
  73. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs +2 -0
  74. package/dist/lib/idiomorph.esm-BOOTOg1r.cjs.map +1 -0
  75. package/dist/lib/idiomorph.esm-CXuY-EHB.js +371 -0
  76. package/dist/lib/idiomorph.esm-CXuY-EHB.js.map +1 -0
  77. package/dist/lib/index.cjs +1 -1
  78. package/dist/lib/index.cjs.map +1 -1
  79. package/dist/lib/index.d.cts +8 -6
  80. package/dist/lib/index.d.ts +8 -6
  81. package/dist/lib/index.js +1 -1
  82. package/dist/lib/index.js.map +1 -1
  83. package/dist/lib/interpolation-plugin-C0dTd69H.cjs +58 -0
  84. package/dist/lib/interpolation-plugin-C0dTd69H.cjs.map +1 -0
  85. package/dist/lib/{interpolation-plugin-Wgb1j4pT.js → interpolation-plugin-K2qE69tF.js} +95 -126
  86. package/dist/lib/interpolation-plugin-K2qE69tF.js.map +1 -0
  87. package/dist/lib/invalidation-CFpNQUv6.cjs +2 -0
  88. package/dist/lib/invalidation-CFpNQUv6.cjs.map +1 -0
  89. package/dist/lib/invalidation-DXhdE9wj.js +64 -0
  90. package/dist/lib/invalidation-DXhdE9wj.js.map +1 -0
  91. package/dist/lib/island/generate-entry.d.cts +40 -2
  92. package/dist/lib/island/generate-entry.d.ts +40 -2
  93. package/dist/lib/island/hydrate.d.cts +19 -6
  94. package/dist/lib/island/hydrate.d.ts +19 -6
  95. package/dist/lib/island/index.cjs +1 -1
  96. package/dist/lib/island/index.d.cts +2 -1
  97. package/dist/lib/island/index.d.ts +2 -1
  98. package/dist/lib/island/index.js +1 -1
  99. package/dist/lib/island/island.d.cts +15 -0
  100. package/dist/lib/island/island.d.ts +15 -0
  101. package/dist/lib/island-CTBnsOhI.js +59 -0
  102. package/dist/lib/island-CTBnsOhI.js.map +1 -0
  103. package/dist/lib/island-kfQEKRe_.cjs +2 -0
  104. package/dist/lib/island-kfQEKRe_.cjs.map +1 -0
  105. package/dist/lib/node-http-DVFcYeBS.js +723 -0
  106. package/dist/lib/node-http-DVFcYeBS.js.map +1 -0
  107. package/dist/lib/node-http-Uct7DhmN.cjs +13 -0
  108. package/dist/lib/node-http-Uct7DhmN.cjs.map +1 -0
  109. package/dist/lib/rolldown-runtime-BIInC43l.cjs +1 -0
  110. package/dist/lib/rolldown-runtime-Dy4uBu1J.js +11 -0
  111. package/dist/lib/router/client.cjs +1 -1
  112. package/dist/lib/router/client.cjs.map +1 -1
  113. package/dist/lib/router/client.d.cts +55 -8
  114. package/dist/lib/router/client.d.ts +55 -8
  115. package/dist/lib/router/client.js +298 -97
  116. package/dist/lib/router/client.js.map +1 -1
  117. package/dist/lib/router/redirects.d.cts +34 -0
  118. package/dist/lib/router/redirects.d.ts +34 -0
  119. package/dist/lib/runtime/handler.d.cts +59 -0
  120. package/dist/lib/runtime/handler.d.ts +59 -0
  121. package/dist/lib/runtime/index.cjs +1 -1
  122. package/dist/lib/runtime/index.d.cts +2 -1
  123. package/dist/lib/runtime/index.d.ts +2 -1
  124. package/dist/lib/runtime/index.js +4 -4
  125. package/dist/lib/runtime/logger.d.cts +47 -0
  126. package/dist/lib/runtime/logger.d.ts +47 -0
  127. package/dist/lib/runtime/node-http.d.cts +11 -1
  128. package/dist/lib/runtime/node-http.d.ts +11 -1
  129. package/dist/lib/runtime-ChE4flX3.cjs +2 -0
  130. package/dist/lib/runtime-ChE4flX3.cjs.map +1 -0
  131. package/dist/lib/runtime-OMyz5yE-.js +905 -0
  132. package/dist/lib/runtime-OMyz5yE-.js.map +1 -0
  133. package/dist/lib/seo/index.cjs +1 -10
  134. package/dist/lib/seo/index.d.cts +1 -0
  135. package/dist/lib/seo/index.d.ts +1 -0
  136. package/dist/lib/seo/index.js +2 -41
  137. package/dist/lib/seo/sitemap-from-routes.d.cts +24 -0
  138. package/dist/lib/seo/sitemap-from-routes.d.ts +24 -0
  139. package/dist/lib/{shared-CfqOLsKj.cjs → shared-CP0Dc3cp.cjs} +72 -47
  140. package/dist/lib/shared-CP0Dc3cp.cjs.map +1 -0
  141. package/dist/lib/{shared-DU3iQgr7.js → shared-D8w-neq2.js} +87 -58
  142. package/dist/lib/shared-D8w-neq2.js.map +1 -0
  143. package/dist/lib/sitemap-from-routes-CaBoiGvE.js +77 -0
  144. package/dist/lib/sitemap-from-routes-CaBoiGvE.js.map +1 -0
  145. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs +13 -0
  146. package/dist/lib/sitemap-from-routes-DbcLLL4G.cjs.map +1 -0
  147. package/dist/lib/ssr/render.d.cts +8 -2
  148. package/dist/lib/ssr/render.d.ts +8 -2
  149. package/dist/lib/ssr/server.d.cts +7 -0
  150. package/dist/lib/ssr/server.d.ts +7 -0
  151. package/dist/lib/ssr/stream-response.d.cts +30 -0
  152. package/dist/lib/ssr/stream-response.d.ts +30 -0
  153. package/dist/lib/ssr/stream.d.cts +16 -1
  154. package/dist/lib/ssr/stream.d.ts +16 -1
  155. package/dist/lib/{stream-DmZjYqHm.cjs → stream-Cg-xYG82.cjs} +3 -3
  156. package/dist/lib/stream-Cg-xYG82.cjs.map +1 -0
  157. package/dist/lib/stream-D-ytogdu.js +106 -0
  158. package/dist/lib/stream-D-ytogdu.js.map +1 -0
  159. package/dist/lib/vite/index.cjs +1 -1
  160. package/dist/lib/vite/index.cjs.map +1 -1
  161. package/dist/lib/vite/index.d.cts +11 -0
  162. package/dist/lib/vite/index.d.ts +11 -0
  163. package/dist/lib/vite/index.js +101 -94
  164. package/dist/lib/vite/index.js.map +1 -1
  165. package/package.json +25 -8
  166. package/dist/lib/errors-0X7pCFNU.cjs +0 -2
  167. package/dist/lib/hydrate-CJbUWdXu.js +0 -126
  168. package/dist/lib/hydrate-CJbUWdXu.js.map +0 -1
  169. package/dist/lib/hydrate-D6MR24mz.cjs +0 -2
  170. package/dist/lib/hydrate-D6MR24mz.cjs.map +0 -1
  171. package/dist/lib/interpolation-plugin-TWDch8nn.cjs +0 -39
  172. package/dist/lib/interpolation-plugin-TWDch8nn.cjs.map +0 -1
  173. package/dist/lib/interpolation-plugin-Wgb1j4pT.js.map +0 -1
  174. package/dist/lib/node-http-DRAUhO0c.js +0 -617
  175. package/dist/lib/node-http-DRAUhO0c.js.map +0 -1
  176. package/dist/lib/node-http-DhxguYyz.cjs +0 -13
  177. package/dist/lib/node-http-DhxguYyz.cjs.map +0 -1
  178. package/dist/lib/runtime-CrH5X-Es.cjs +0 -2
  179. package/dist/lib/runtime-CrH5X-Es.cjs.map +0 -1
  180. package/dist/lib/runtime-Ds2-wH6P.js +0 -520
  181. package/dist/lib/runtime-Ds2-wH6P.js.map +0 -1
  182. package/dist/lib/seo/index.cjs.map +0 -1
  183. package/dist/lib/seo/index.js.map +0 -1
  184. package/dist/lib/shared-CfqOLsKj.cjs.map +0 -1
  185. package/dist/lib/shared-DU3iQgr7.js.map +0 -1
  186. package/dist/lib/stream-CpOG052V.js +0 -104
  187. package/dist/lib/stream-CpOG052V.js.map +0 -1
  188. package/dist/lib/stream-DmZjYqHm.cjs.map +0 -1
@@ -1,4 +1,28 @@
1
1
  import type { IslandModule } from "./scan.js";
2
+ /**
3
+ * Router options for the generated client entry (Fase 8.3).
4
+ */
5
+ export interface RouterEntryOptions {
6
+ /**
7
+ * Include SPA navigation code. When `false` the generated entry is
8
+ * hydrate-only and no router file is emitted. Default: `true`.
9
+ */
10
+ enabled?: boolean;
11
+ /** Forwarded to `startClientRouter({ prefetch })`. Default: `true`. */
12
+ prefetch?: boolean;
13
+ /** Forwarded to `startClientRouter({ morph })` (idiomorph swap). */
14
+ morph?: boolean;
15
+ /** Forwarded to `startClientRouter({ loadingIndicator })`. */
16
+ loadingIndicator?: boolean;
17
+ /**
18
+ * Split mode: the entry hydrates islands only and the router lives in a
19
+ * separate generated module (`router.ts` next to the entry, emitted as its
20
+ * own chunk). This is what lets pages without islands load only the router.
21
+ */
22
+ separate?: boolean;
23
+ /** Absolute path of the generated router module when `separate` is set. */
24
+ outFile?: string;
25
+ }
2
26
  /** Options for generating the client entry module. */
3
27
  export interface GenerateEntryOptions {
4
28
  /** Islands to register, from `scanIslands`. */
@@ -15,11 +39,25 @@ export interface GenerateEntryOptions {
15
39
  * Defaults to the published subpath `@elurjs/kit/router`.
16
40
  */
17
41
  routerImport?: string;
42
+ /**
43
+ * Router inclusion. Omit for the legacy combined entry (router embedded).
44
+ * With `separate: true`, a standalone router module is also generated at
45
+ * `router.outFile`.
46
+ */
47
+ router?: RouterEntryOptions;
18
48
  }
49
+ /**
50
+ * Source for the standalone router module emitted in split builds. Router
51
+ * options are baked in so the public module takes no arguments.
52
+ */
53
+ export declare function buildRouterEntrySource(routerImport?: string, options?: Omit<RouterEntryOptions, "enabled" | "separate" | "outFile">): string;
19
54
  /** Builds the source code of the client entry module. */
20
- export declare function buildEntrySource(islands: IslandModule[], outFile: string, hydrateImport?: string, routerImport?: string): string;
55
+ export declare function buildEntrySource(islands: IslandModule[], outFile: string, hydrateImport?: string, routerImport?: string, router?: RouterEntryOptions): string;
21
56
  /**
22
- * Generates and writes the client entry module for the given islands.
57
+ * Generates and writes the client entry module for the given islands. When
58
+ * `options.router.separate` is set (and the router is enabled), a standalone
59
+ * router module is written alongside it (default: `router.ts` next to the
60
+ * entry — bundle it as a second input to emit `/_elur/router.js`).
23
61
  *
24
62
  * @param options Generation options.
25
63
  * @returns The absolute path of the written entry file.
@@ -21,13 +21,26 @@ export type IslandRegistry = Record<string, IslandRegistryEntry<any>>;
21
21
  */
22
22
  export declare function lazyIsland<TProps = unknown>(loader: () => Promise<IslandComponent<TProps>>): IslandLoader<TProps>;
23
23
  /**
24
- * Hydrates all islands on the page using the provided registry.
25
- *
26
- * @param registry Map from island name to component factory.
24
+ * Options for {@link cleanupHydratedIslands}.
27
25
  */
26
+ export interface CleanupIslandsOptions {
27
+ /**
28
+ * Root elements whose islands must NOT be disposed — used by the SPA router
29
+ * to keep the live state of islands inside `data-elur-persist` nodes that
30
+ * are moved (not re-rendered) into the new page. The router passes them via
31
+ * the `elur:before-render` event detail (`detail.persisted`).
32
+ */
33
+ except?: Iterable<Element>;
34
+ }
28
35
  /**
29
- * Dispose all currently hydrated islands. Called by the client router before
30
- * swapping the page body to prevent leaked effects and stale DOM writes.
36
+ * Dispose all currently hydrated islands. Runs on the `elur:before-render`
37
+ * event — dispatched by the client router BEFORE it swaps the page body so
38
+ * effects are cleaned up while their DOM is still attached, and islands inside
39
+ * `except` roots keep running untouched.
40
+ *
41
+ * Hosts that only dispatch the legacy `elur:rendered` event keep working: the
42
+ * generated entry runs this cleanup on `elur:rendered` as a compat fallback
43
+ * when no `elur:before-render` preceded it.
31
44
  */
32
- export declare function cleanupHydratedIslands(): void;
45
+ export declare function cleanupHydratedIslands(options?: CleanupIslandsOptions): void;
33
46
  export declare function hydrateIslands(registry: IslandRegistry): void;
@@ -21,13 +21,26 @@ export type IslandRegistry = Record<string, IslandRegistryEntry<any>>;
21
21
  */
22
22
  export declare function lazyIsland<TProps = unknown>(loader: () => Promise<IslandComponent<TProps>>): IslandLoader<TProps>;
23
23
  /**
24
- * Hydrates all islands on the page using the provided registry.
25
- *
26
- * @param registry Map from island name to component factory.
24
+ * Options for {@link cleanupHydratedIslands}.
27
25
  */
26
+ export interface CleanupIslandsOptions {
27
+ /**
28
+ * Root elements whose islands must NOT be disposed — used by the SPA router
29
+ * to keep the live state of islands inside `data-elur-persist` nodes that
30
+ * are moved (not re-rendered) into the new page. The router passes them via
31
+ * the `elur:before-render` event detail (`detail.persisted`).
32
+ */
33
+ except?: Iterable<Element>;
34
+ }
28
35
  /**
29
- * Dispose all currently hydrated islands. Called by the client router before
30
- * swapping the page body to prevent leaked effects and stale DOM writes.
36
+ * Dispose all currently hydrated islands. Runs on the `elur:before-render`
37
+ * event — dispatched by the client router BEFORE it swaps the page body so
38
+ * effects are cleaned up while their DOM is still attached, and islands inside
39
+ * `except` roots keep running untouched.
40
+ *
41
+ * Hosts that only dispatch the legacy `elur:rendered` event keep working: the
42
+ * generated entry runs this cleanup on `elur:rendered` as a compat fallback
43
+ * when no `elur:before-render` preceded it.
31
44
  */
32
- export declare function cleanupHydratedIslands(): void;
45
+ export declare function cleanupHydratedIslands(options?: CleanupIslandsOptions): void;
33
46
  export declare function hydrateIslands(registry: IslandRegistry): void;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../hydrate-D6MR24mz.cjs");exports.cleanupHydratedIslands=e.t,exports.hydrateIslands=e.n,exports.lazyIsland=e.r;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../island-kfQEKRe_.cjs"),t=require("../hydrate-BSLuI0Nh.cjs");exports.ISLAND_MARKER_ATTR=e.t,exports.PERSIST_ATTR=e.n,exports.cleanupHydratedIslands=t.t,exports.hydrateIslands=t.n,exports.lazyIsland=t.r;
@@ -1,2 +1,3 @@
1
- export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry } from "./hydrate.js";
1
+ export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry, type CleanupIslandsOptions } from "./hydrate.js";
2
2
  export type { IslandComponent, IslandDirective, IslandOptions } from "./island.js";
3
+ export { ISLAND_MARKER_ATTR, PERSIST_ATTR } from "./island.js";
@@ -1,2 +1,3 @@
1
- export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry } from "./hydrate.js";
1
+ export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry, type CleanupIslandsOptions } from "./hydrate.js";
2
2
  export type { IslandComponent, IslandDirective, IslandOptions } from "./island.js";
3
+ export { ISLAND_MARKER_ATTR, PERSIST_ATTR } from "./island.js";
@@ -1 +1 @@
1
- import{n as e,r as t,t as n}from"../hydrate-CJbUWdXu.js";export{n as cleanupHydratedIslands,e as hydrateIslands,t as lazyIsland};
1
+ import{n as e,t}from"../island-CTBnsOhI.js";import{n,r,t as i}from"../hydrate-BaS7-d8C.js";export{t as ISLAND_MARKER_ATTR,e as PERSIST_ATTR,i as cleanupHydratedIslands,n as hydrateIslands,r as lazyIsland};
@@ -1,5 +1,20 @@
1
1
  import { type ElurTemplate } from "@elurjs/core";
2
2
  export type IslandDirective = "load" | "idle" | "visible" | "only";
3
+ /**
4
+ * HTML attribute that marks an island marker element. The renderer scans the
5
+ * rendered body for this attribute to decide whether the page needs the
6
+ * hydration entry at all (0% JS gating); the client hydrator queries the DOM
7
+ * with `[data-elur-island]`.
8
+ */
9
+ export declare const ISLAND_MARKER_ATTR = "data-elur-island";
10
+ /**
11
+ * HTML attribute that marks an element whose live DOM node is moved (not
12
+ * re-rendered) across SPA navigations — the Astro `transition:persist` /
13
+ * Turbo `data-turbo-permanent` pattern. The client router matches nodes by
14
+ * the attribute value (`data-elur-persist="key"`) between the old and new
15
+ * page, preserving component state, media playback, scroll position, etc.
16
+ */
17
+ export declare const PERSIST_ATTR = "data-elur-persist";
3
18
  export interface IslandComponent<TProps = unknown> {
4
19
  (props: TProps): ElurTemplate | null | false | undefined;
5
20
  }
@@ -1,5 +1,20 @@
1
1
  import { type ElurTemplate } from "@elurjs/core";
2
2
  export type IslandDirective = "load" | "idle" | "visible" | "only";
3
+ /**
4
+ * HTML attribute that marks an island marker element. The renderer scans the
5
+ * rendered body for this attribute to decide whether the page needs the
6
+ * hydration entry at all (0% JS gating); the client hydrator queries the DOM
7
+ * with `[data-elur-island]`.
8
+ */
9
+ export declare const ISLAND_MARKER_ATTR = "data-elur-island";
10
+ /**
11
+ * HTML attribute that marks an element whose live DOM node is moved (not
12
+ * re-rendered) across SPA navigations — the Astro `transition:persist` /
13
+ * Turbo `data-turbo-permanent` pattern. The client router matches nodes by
14
+ * the attribute value (`data-elur-persist="key"`) between the old and new
15
+ * page, preserving component state, media playback, scroll position, etc.
16
+ */
17
+ export declare const PERSIST_ATTR = "data-elur-persist";
3
18
  export interface IslandComponent<TProps = unknown> {
4
19
  (props: TProps): ElurTemplate | null | false | undefined;
5
20
  }
@@ -0,0 +1,59 @@
1
+ import { ELUR_RENDER_PROTOCOL as e } from "@elurjs/core";
2
+ //#region src/island/island.ts
3
+ var t = "data-elur-island", n = "data-elur-persist";
4
+ function r(n, r, l, u = "load", d) {
5
+ let f = u === "only" ? !1 : d?.ssr ?? !0, p = d?.fallback, m = (e) => `<div ${t}="${s(n)}" data-directive="${u}" data-props='${c(l)}'>${e}</div>`;
6
+ return {
7
+ __isElurTemplate: !0,
8
+ [e]: { async renderServer(e) {
9
+ let t = "";
10
+ if (f) try {
11
+ let n = r(l);
12
+ t = n !== null && n !== !1 && n !== void 0 ? await e.render(n, { markers: !0 }) : await a(p, e);
13
+ } catch (e) {
14
+ throw i(n, e);
15
+ }
16
+ else t = await a(p, e);
17
+ return m(t);
18
+ } },
19
+ _render(e, t) {
20
+ let n = document.createElement("div"), i = "";
21
+ if (f) {
22
+ let e = r(l);
23
+ if (e !== null && e !== !1 && e !== void 0) {
24
+ let t = e._render(n, null);
25
+ i = n.innerHTML, t();
26
+ } else i = o(p, n);
27
+ } else i = o(p, n);
28
+ let a = document.createElement("template");
29
+ a.innerHTML = m(i);
30
+ let s = a.content, c = s.firstChild;
31
+ return e.insertBefore(s, t), () => {
32
+ c?.parentNode && c.parentNode.removeChild(c);
33
+ };
34
+ }
35
+ };
36
+ }
37
+ function i(e, t) {
38
+ let n = t instanceof Error ? t : Error(String(t)), r = t instanceof Error ? t.message : String(t);
39
+ return Error(`[elur-kit] Island "${e}" threw during SSR: ${r}\n If the component accesses browser-only globals (document, window, etc.),\n use directive: "only" or options: { ssr: false } to skip server rendering.\n For environment reads (matchMedia, localStorage, navigator) you may guard\n the access with isSSR() from "@elurjs/kit".`, { cause: n });
40
+ }
41
+ async function a(e, t) {
42
+ return e == null || e === "" ? "" : typeof e == "string" ? e : t.render(e, { markers: !1 });
43
+ }
44
+ function o(e, t) {
45
+ if (e == null || e === "") return "";
46
+ if (typeof e == "string") return e;
47
+ let n = e._render(t, null), r = t.innerHTML;
48
+ return n(), r;
49
+ }
50
+ function s(e) {
51
+ return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
52
+ }
53
+ function c(e) {
54
+ return JSON.stringify(e ?? null).replace(/</g, "\\u003c").replace(/'/g, "\\u0027");
55
+ }
56
+ //#endregion
57
+ export { n, r, t };
58
+
59
+ //# sourceMappingURL=island-CTBnsOhI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-CTBnsOhI.js","names":[],"sources":["../../src/island/island.ts"],"sourcesContent":["import { ELUR_RENDER_PROTOCOL, type ElurTemplate, type ServerRenderProtocolContext } from \"@elurjs/core\";\n\n// --- Islands helper ---\n//\n// Marks a component as an island. During server-side rendering it emits a\n// static placeholder with `data-elur-island` attributes. The client entry finds\n// these markers and hydrates them with the real component + reactive signals.\n//\n// SSR strategy\n// ------------\n// By default the component is executed on the server to produce a fallback HTML\n// fragment (better first paint, SEO, less layout shift). Components that access\n// browser-only globals (`document`, `window`, `navigator`, ...) in their body\n// cannot run on the server. Two opt-out mechanisms are provided, mirroring the\n// industry standard (Astro `client:only`, Next.js `dynamic(..., { ssr: false })`):\n//\n// 1. directive: \"only\" — shortcut for client-only with `load` scheduling.\n// 2. options: { ssr: false } — client-only with any directive (load/idle/visible).\n//\n// When SSR is skipped, only `options.fallback` (a ElurTemplate or string) is\n// rendered into the marker. The client hydrates from scratch.\n//\n// When SSR runs and the component throws, the error is NOT swallowed: it is\n// re-thrown wrapped with an actionable message naming the island and suggesting\n// `directive: \"only\"` / `{ ssr: false }` / `isSSR()`. This matches Astro and\n// Next.js, which never try/catch to \"auto-detect\" client-only components.\n\nexport type IslandDirective = \"load\" | \"idle\" | \"visible\" | \"only\";\n\n/**\n * HTML attribute that marks an island marker element. The renderer scans the\n * rendered body for this attribute to decide whether the page needs the\n * hydration entry at all (0% JS gating); the client hydrator queries the DOM\n * with `[data-elur-island]`.\n */\nexport const ISLAND_MARKER_ATTR = \"data-elur-island\";\n\n/**\n * HTML attribute that marks an element whose live DOM node is moved (not\n * re-rendered) across SPA navigations — the Astro `transition:persist` /\n * Turbo `data-turbo-permanent` pattern. The client router matches nodes by\n * the attribute value (`data-elur-persist=\"key\"`) between the old and new\n * page, preserving component state, media playback, scroll position, etc.\n */\nexport const PERSIST_ATTR = \"data-elur-persist\";\n\nexport interface IslandComponent<TProps = unknown> {\n (props: TProps): ElurTemplate | null | false | undefined;\n}\n\n/**\n * Options for {@link island}.\n *\n * - `ssr`: Whether to execute the component on the server. Defaults to `true`\n * unless `directive === \"only\"` (then `false`). When `false`, the component\n * is never called during SSR; only `fallback` is rendered.\n * - `fallback`: HTML to render inside the island marker when SSR is skipped or\n * the component returns null/false. Accepts a `ElurTemplate` (reactive, with\n * signals) or a plain string. Defaults to an empty string.\n */\nexport interface IslandOptions {\n ssr?: boolean;\n fallback?: ElurTemplate | string;\n}\n\n/**\n * Renders a component to a static HTML string with island markers.\n *\n * @param name Unique island name used by the client entry to look up the module.\n * @param component Island component. Executed on the server unless `directive`\n * is `\"only\"` or `options.ssr` is `false`.\n * @param props Props passed to the component and serialized for hydration.\n * @param directive When to hydrate on the client. Use `\"only\"` to skip SSR\n * entirely (client-only island).\n * @param options SSR strategy and fallback content.\n * @returns A ElurTemplate that renders the island placeholder.\n */\nexport function island<TProps>(\n name: string,\n component: IslandComponent<TProps>,\n props: TProps,\n directive: IslandDirective = \"load\",\n options?: IslandOptions,\n): ElurTemplate {\n // `directive: \"only\"` forces ssr off; explicit `options.ssr` wins otherwise.\n const ssr = directive === \"only\" ? false : (options?.ssr ?? true);\n const fallback = options?.fallback;\n\n const markerHtml = (innerHtml: string) =>\n `<div ${ISLAND_MARKER_ATTR}=\"${escapeHtml(name)}\" data-directive=\"${directive}\" data-props='${serializeProps(props)}'>${innerHtml}</div>`;\n\n return {\n __isElurTemplate: true as const,\n [ELUR_RENDER_PROTOCOL]: {\n async renderServer(context: ServerRenderProtocolContext) {\n let innerHtml = \"\";\n if (ssr) {\n try {\n const template = component(props);\n if (template !== null && template !== false && template !== undefined) {\n innerHtml = await context.render(template, { markers: true });\n } else {\n // Component returned null/false/undefined — render fallback if any.\n innerHtml = await renderFallback(fallback, context);\n }\n } catch (error) {\n throw wrapIslandSSRError(name, error);\n }\n } else {\n innerHtml = await renderFallback(fallback, context);\n }\n return markerHtml(innerHtml);\n },\n },\n _render(parent: Node, before: Node | null): () => void {\n const container = document.createElement(\"div\");\n let innerHtml = \"\";\n if (ssr) {\n const template = component(props);\n if (template !== null && template !== false && template !== undefined) {\n const dispose = template._render(container, null);\n innerHtml = container.innerHTML;\n dispose();\n } else {\n // null/false/undefined — render fallback if any.\n innerHtml = renderFallbackSync(fallback, container);\n }\n } else {\n innerHtml = renderFallbackSync(fallback, container);\n }\n const wrapper = document.createElement(\"template\");\n wrapper.innerHTML = markerHtml(innerHtml);\n const fragment = wrapper.content;\n const inserted = fragment.firstChild;\n parent.insertBefore(fragment, before);\n return () => {\n if (inserted?.parentNode) inserted.parentNode.removeChild(inserted);\n };\n },\n } as unknown as ElurTemplate;\n}\n\n/**\n * Wraps an SSR error from an island component with an actionable message.\n *\n * Following the Astro/Next.js convention, SSR errors are never silently\n * swallowed — they propagate so real bugs surface. The wrapper adds the island\n * name and three concrete remediation paths.\n */\nfunction wrapIslandSSRError(name: string, error: unknown): Error {\n const cause = error instanceof Error ? error : new Error(String(error));\n const msg = error instanceof Error ? error.message : String(error);\n return new Error(\n `[elur-kit] Island \"${name}\" threw during SSR: ${msg}\\n` +\n ` If the component accesses browser-only globals (document, window, etc.),\\n` +\n ` use directive: \"only\" or options: { ssr: false } to skip server rendering.\\n` +\n ` For environment reads (matchMedia, localStorage, navigator) you may guard\\n` +\n ` the access with isSSR() from \"@elurjs/kit\".`,\n { cause },\n );\n}\n\n/** Renders the fallback (ElurTemplate or string) to an HTML string on the server. */\nasync function renderFallback(\n fallback: ElurTemplate | string | undefined,\n context: ServerRenderProtocolContext,\n): Promise<string> {\n if (fallback == null || fallback === \"\") return \"\";\n if (typeof fallback === \"string\") return fallback;\n return context.render(fallback, { markers: false });\n}\n\n/** Renders the fallback into a container and returns its innerHTML (client path). */\nfunction renderFallbackSync(fallback: ElurTemplate | string | undefined, container: HTMLElement): string {\n if (fallback == null || fallback === \"\") return \"\";\n if (typeof fallback === \"string\") return fallback;\n const dispose = fallback._render(container, null);\n const html = container.innerHTML;\n dispose();\n return html;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction serializeProps(props: unknown): string {\n return JSON.stringify(props ?? null)\n .replace(/</g, \"\\\\u003c\")\n .replace(/'/g, \"\\\\u0027\");\n}\n"],"mappings":";;AAmCA,IAAa,IAAqB,oBASrB,IAAe;AAiC5B,SAAgB,EACd,GACA,GACA,GACA,IAA6B,QAC7B,GACc;CAEd,IAAM,IAAM,MAAc,SAAS,KAAS,GAAS,OAAO,IACtD,IAAW,GAAS,UAEpB,KAAc,MAClB,QAAQ,EAAmB,IAAI,EAAW,CAAI,EAAE,oBAAoB,EAAU,gBAAgB,EAAe,CAAK,EAAE,IAAI,EAAU;CAEpI,OAAO;EACL,kBAAkB;GACjB,IAAuB,EACtB,MAAM,aAAa,GAAsC;GACvD,IAAI,IAAY;GAChB,IAAI,GACF,IAAI;IACF,IAAM,IAAW,EAAU,CAAK;IAChC,AAIE,IAJE,MAAa,QAAQ,MAAa,MAAS,MAAa,KAAA,IAC9C,MAAM,EAAQ,OAAO,GAAU,EAAE,SAAS,GAAK,CAAC,IAGhD,MAAM,EAAe,GAAU,CAAO;GAEtD,SAAS,GAAO;IACd,MAAM,EAAmB,GAAM,CAAK;GACtC;QAEA,IAAY,MAAM,EAAe,GAAU,CAAO;GAEpD,OAAO,EAAW,CAAS;EAC7B,EACF;EACA,QAAQ,GAAc,GAAiC;GACrD,IAAM,IAAY,SAAS,cAAc,KAAK,GAC1C,IAAY;GAChB,IAAI,GAAK;IACP,IAAM,IAAW,EAAU,CAAK;IAChC,IAAI,MAAa,QAAQ,MAAa,MAAS,MAAa,KAAA,GAAW;KACrE,IAAM,IAAU,EAAS,QAAQ,GAAW,IAAI;KAEhD,AADA,IAAY,EAAU,WACtB,EAAQ;IACV,OAEE,IAAY,EAAmB,GAAU,CAAS;GAEtD,OACE,IAAY,EAAmB,GAAU,CAAS;GAEpD,IAAM,IAAU,SAAS,cAAc,UAAU;GACjD,EAAQ,YAAY,EAAW,CAAS;GACxC,IAAM,IAAW,EAAQ,SACnB,IAAW,EAAS;GAE1B,OADA,EAAO,aAAa,GAAU,CAAM,SACvB;IACX,AAAI,GAAU,cAAY,EAAS,WAAW,YAAY,CAAQ;GACpE;EACF;CACF;AACF;AASA,SAAS,EAAmB,GAAc,GAAuB;CAC/D,IAAM,IAAQ,aAAiB,QAAQ,IAAY,MAAM,OAAO,CAAK,CAAC,GAChE,IAAM,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;CACjE,OAAW,MACT,sBAAsB,EAAK,sBAAsB,EAAI,yRAKrD,EAAE,SAAM,CACV;AACF;AAGA,eAAe,EACb,GACA,GACiB;CAGjB,OAFI,KAAY,QAAQ,MAAa,KAAW,KAC5C,OAAO,KAAa,WAAiB,IAClC,EAAQ,OAAO,GAAU,EAAE,SAAS,GAAM,CAAC;AACpD;AAGA,SAAS,EAAmB,GAA6C,GAAgC;CACvG,IAAI,KAAY,QAAQ,MAAa,IAAI,OAAO;CAChD,IAAI,OAAO,KAAa,UAAU,OAAO;CACzC,IAAM,IAAU,EAAS,QAAQ,GAAW,IAAI,GAC1C,IAAO,EAAU;CAEvB,OADA,EAAQ,GACD;AACT;AAEA,SAAS,EAAW,GAAuB;CACzC,OAAO,EACJ,QAAQ,MAAM,OAAO,CAAC,CACtB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,MAAM,CAAC,CACrB,QAAQ,MAAM,QAAQ,CAAC,CACvB,QAAQ,MAAM,OAAO;AAC1B;AAEA,SAAS,EAAe,GAAwB;CAC9C,OAAO,KAAK,UAAU,KAAS,IAAI,CAAC,CACjC,QAAQ,MAAM,SAAS,CAAC,CACxB,QAAQ,MAAM,SAAS;AAC5B"}
@@ -0,0 +1,2 @@
1
+ let e=require("@elurjs/core");var t=`data-elur-island`,n=`data-elur-persist`;function r(n,r,l,u=`load`,d){let f=u===`only`?!1:d?.ssr??!0,p=d?.fallback,m=e=>`<div ${t}="${s(n)}" data-directive="${u}" data-props='${c(l)}'>${e}</div>`;return{__isElurTemplate:!0,[e.ELUR_RENDER_PROTOCOL]:{async renderServer(e){let t=``;if(f)try{let n=r(l);t=n!==null&&n!==!1&&n!==void 0?await e.render(n,{markers:!0}):await a(p,e)}catch(e){throw i(n,e)}else t=await a(p,e);return m(t)}},_render(e,t){let n=document.createElement(`div`),i=``;if(f){let e=r(l);if(e!==null&&e!==!1&&e!==void 0){let t=e._render(n,null);i=n.innerHTML,t()}else i=o(p,n)}else i=o(p,n);let a=document.createElement(`template`);a.innerHTML=m(i);let s=a.content,c=s.firstChild;return e.insertBefore(s,t),()=>{c?.parentNode&&c.parentNode.removeChild(c)}}}}function i(e,t){let n=t instanceof Error?t:Error(String(t)),r=t instanceof Error?t.message:String(t);return Error(`[elur-kit] Island "${e}" threw during SSR: ${r}\n If the component accesses browser-only globals (document, window, etc.),\n use directive: "only" or options: { ssr: false } to skip server rendering.\n For environment reads (matchMedia, localStorage, navigator) you may guard\n the access with isSSR() from "@elurjs/kit".`,{cause:n})}async function a(e,t){return e==null||e===``?``:typeof e==`string`?e:t.render(e,{markers:!1})}function o(e,t){if(e==null||e===``)return``;if(typeof e==`string`)return e;let n=e._render(t,null),r=t.innerHTML;return n(),r}function s(e){return e.replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`).replace(/"/g,`&quot;`).replace(/'/g,`&#39;`)}function c(e){return JSON.stringify(e??null).replace(/</g,`\\u003c`).replace(/'/g,`\\u0027`)}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return t}});
2
+ //# sourceMappingURL=island-kfQEKRe_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"island-kfQEKRe_.cjs","names":[],"sources":["../../src/island/island.ts"],"sourcesContent":["import { ELUR_RENDER_PROTOCOL, type ElurTemplate, type ServerRenderProtocolContext } from \"@elurjs/core\";\n\n// --- Islands helper ---\n//\n// Marks a component as an island. During server-side rendering it emits a\n// static placeholder with `data-elur-island` attributes. The client entry finds\n// these markers and hydrates them with the real component + reactive signals.\n//\n// SSR strategy\n// ------------\n// By default the component is executed on the server to produce a fallback HTML\n// fragment (better first paint, SEO, less layout shift). Components that access\n// browser-only globals (`document`, `window`, `navigator`, ...) in their body\n// cannot run on the server. Two opt-out mechanisms are provided, mirroring the\n// industry standard (Astro `client:only`, Next.js `dynamic(..., { ssr: false })`):\n//\n// 1. directive: \"only\" — shortcut for client-only with `load` scheduling.\n// 2. options: { ssr: false } — client-only with any directive (load/idle/visible).\n//\n// When SSR is skipped, only `options.fallback` (a ElurTemplate or string) is\n// rendered into the marker. The client hydrates from scratch.\n//\n// When SSR runs and the component throws, the error is NOT swallowed: it is\n// re-thrown wrapped with an actionable message naming the island and suggesting\n// `directive: \"only\"` / `{ ssr: false }` / `isSSR()`. This matches Astro and\n// Next.js, which never try/catch to \"auto-detect\" client-only components.\n\nexport type IslandDirective = \"load\" | \"idle\" | \"visible\" | \"only\";\n\n/**\n * HTML attribute that marks an island marker element. The renderer scans the\n * rendered body for this attribute to decide whether the page needs the\n * hydration entry at all (0% JS gating); the client hydrator queries the DOM\n * with `[data-elur-island]`.\n */\nexport const ISLAND_MARKER_ATTR = \"data-elur-island\";\n\n/**\n * HTML attribute that marks an element whose live DOM node is moved (not\n * re-rendered) across SPA navigations — the Astro `transition:persist` /\n * Turbo `data-turbo-permanent` pattern. The client router matches nodes by\n * the attribute value (`data-elur-persist=\"key\"`) between the old and new\n * page, preserving component state, media playback, scroll position, etc.\n */\nexport const PERSIST_ATTR = \"data-elur-persist\";\n\nexport interface IslandComponent<TProps = unknown> {\n (props: TProps): ElurTemplate | null | false | undefined;\n}\n\n/**\n * Options for {@link island}.\n *\n * - `ssr`: Whether to execute the component on the server. Defaults to `true`\n * unless `directive === \"only\"` (then `false`). When `false`, the component\n * is never called during SSR; only `fallback` is rendered.\n * - `fallback`: HTML to render inside the island marker when SSR is skipped or\n * the component returns null/false. Accepts a `ElurTemplate` (reactive, with\n * signals) or a plain string. Defaults to an empty string.\n */\nexport interface IslandOptions {\n ssr?: boolean;\n fallback?: ElurTemplate | string;\n}\n\n/**\n * Renders a component to a static HTML string with island markers.\n *\n * @param name Unique island name used by the client entry to look up the module.\n * @param component Island component. Executed on the server unless `directive`\n * is `\"only\"` or `options.ssr` is `false`.\n * @param props Props passed to the component and serialized for hydration.\n * @param directive When to hydrate on the client. Use `\"only\"` to skip SSR\n * entirely (client-only island).\n * @param options SSR strategy and fallback content.\n * @returns A ElurTemplate that renders the island placeholder.\n */\nexport function island<TProps>(\n name: string,\n component: IslandComponent<TProps>,\n props: TProps,\n directive: IslandDirective = \"load\",\n options?: IslandOptions,\n): ElurTemplate {\n // `directive: \"only\"` forces ssr off; explicit `options.ssr` wins otherwise.\n const ssr = directive === \"only\" ? false : (options?.ssr ?? true);\n const fallback = options?.fallback;\n\n const markerHtml = (innerHtml: string) =>\n `<div ${ISLAND_MARKER_ATTR}=\"${escapeHtml(name)}\" data-directive=\"${directive}\" data-props='${serializeProps(props)}'>${innerHtml}</div>`;\n\n return {\n __isElurTemplate: true as const,\n [ELUR_RENDER_PROTOCOL]: {\n async renderServer(context: ServerRenderProtocolContext) {\n let innerHtml = \"\";\n if (ssr) {\n try {\n const template = component(props);\n if (template !== null && template !== false && template !== undefined) {\n innerHtml = await context.render(template, { markers: true });\n } else {\n // Component returned null/false/undefined — render fallback if any.\n innerHtml = await renderFallback(fallback, context);\n }\n } catch (error) {\n throw wrapIslandSSRError(name, error);\n }\n } else {\n innerHtml = await renderFallback(fallback, context);\n }\n return markerHtml(innerHtml);\n },\n },\n _render(parent: Node, before: Node | null): () => void {\n const container = document.createElement(\"div\");\n let innerHtml = \"\";\n if (ssr) {\n const template = component(props);\n if (template !== null && template !== false && template !== undefined) {\n const dispose = template._render(container, null);\n innerHtml = container.innerHTML;\n dispose();\n } else {\n // null/false/undefined — render fallback if any.\n innerHtml = renderFallbackSync(fallback, container);\n }\n } else {\n innerHtml = renderFallbackSync(fallback, container);\n }\n const wrapper = document.createElement(\"template\");\n wrapper.innerHTML = markerHtml(innerHtml);\n const fragment = wrapper.content;\n const inserted = fragment.firstChild;\n parent.insertBefore(fragment, before);\n return () => {\n if (inserted?.parentNode) inserted.parentNode.removeChild(inserted);\n };\n },\n } as unknown as ElurTemplate;\n}\n\n/**\n * Wraps an SSR error from an island component with an actionable message.\n *\n * Following the Astro/Next.js convention, SSR errors are never silently\n * swallowed — they propagate so real bugs surface. The wrapper adds the island\n * name and three concrete remediation paths.\n */\nfunction wrapIslandSSRError(name: string, error: unknown): Error {\n const cause = error instanceof Error ? error : new Error(String(error));\n const msg = error instanceof Error ? error.message : String(error);\n return new Error(\n `[elur-kit] Island \"${name}\" threw during SSR: ${msg}\\n` +\n ` If the component accesses browser-only globals (document, window, etc.),\\n` +\n ` use directive: \"only\" or options: { ssr: false } to skip server rendering.\\n` +\n ` For environment reads (matchMedia, localStorage, navigator) you may guard\\n` +\n ` the access with isSSR() from \"@elurjs/kit\".`,\n { cause },\n );\n}\n\n/** Renders the fallback (ElurTemplate or string) to an HTML string on the server. */\nasync function renderFallback(\n fallback: ElurTemplate | string | undefined,\n context: ServerRenderProtocolContext,\n): Promise<string> {\n if (fallback == null || fallback === \"\") return \"\";\n if (typeof fallback === \"string\") return fallback;\n return context.render(fallback, { markers: false });\n}\n\n/** Renders the fallback into a container and returns its innerHTML (client path). */\nfunction renderFallbackSync(fallback: ElurTemplate | string | undefined, container: HTMLElement): string {\n if (fallback == null || fallback === \"\") return \"\";\n if (typeof fallback === \"string\") return fallback;\n const dispose = fallback._render(container, null);\n const html = container.innerHTML;\n dispose();\n return html;\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\nfunction serializeProps(props: unknown): string {\n return JSON.stringify(props ?? null)\n .replace(/</g, \"\\\\u003c\")\n .replace(/'/g, \"\\\\u0027\");\n}\n"],"mappings":"8BAmCA,IAAa,EAAqB,mBASrB,EAAe,oBAiC5B,SAAgB,EACd,EACA,EACA,EACA,EAA6B,OAC7B,EACc,CAEd,IAAM,EAAM,IAAc,OAAS,GAAS,GAAS,KAAO,GACtD,EAAW,GAAS,SAEpB,EAAc,GAClB,QAAQ,EAAmB,IAAI,EAAW,CAAI,EAAE,oBAAoB,EAAU,gBAAgB,EAAe,CAAK,EAAE,IAAI,EAAU,QAEpI,MAAO,CACL,iBAAkB,IACjB,EAAA,sBAAuB,CACtB,MAAM,aAAa,EAAsC,CACvD,IAAI,EAAY,GAChB,GAAI,EACF,GAAI,CACF,IAAM,EAAW,EAAU,CAAK,EAChC,AAIE,EAJE,IAAa,MAAQ,IAAa,IAAS,IAAa,IAAA,GAC9C,MAAM,EAAQ,OAAO,EAAU,CAAE,QAAS,EAAK,CAAC,EAGhD,MAAM,EAAe,EAAU,CAAO,CAEtD,OAAS,EAAO,CACd,MAAM,EAAmB,EAAM,CAAK,CACtC,KAEA,GAAY,MAAM,EAAe,EAAU,CAAO,EAEpD,OAAO,EAAW,CAAS,CAC7B,CACF,EACA,QAAQ,EAAc,EAAiC,CACrD,IAAM,EAAY,SAAS,cAAc,KAAK,EAC1C,EAAY,GAChB,GAAI,EAAK,CACP,IAAM,EAAW,EAAU,CAAK,EAChC,GAAI,IAAa,MAAQ,IAAa,IAAS,IAAa,IAAA,GAAW,CACrE,IAAM,EAAU,EAAS,QAAQ,EAAW,IAAI,EAChD,EAAY,EAAU,UACtB,EAAQ,CACV,KAEE,GAAY,EAAmB,EAAU,CAAS,CAEtD,KACE,GAAY,EAAmB,EAAU,CAAS,EAEpD,IAAM,EAAU,SAAS,cAAc,UAAU,EACjD,EAAQ,UAAY,EAAW,CAAS,EACxC,IAAM,EAAW,EAAQ,QACnB,EAAW,EAAS,WAE1B,OADA,EAAO,aAAa,EAAU,CAAM,MACvB,CACP,GAAU,YAAY,EAAS,WAAW,YAAY,CAAQ,CACpE,CACF,CACF,CACF,CASA,SAAS,EAAmB,EAAc,EAAuB,CAC/D,IAAM,EAAQ,aAAiB,MAAQ,EAAY,MAAM,OAAO,CAAK,CAAC,EAChE,EAAM,aAAiB,MAAQ,EAAM,QAAU,OAAO,CAAK,EACjE,OAAW,MACT,sBAAsB,EAAK,sBAAsB,EAAI,wRAKrD,CAAE,OAAM,CACV,CACF,CAGA,eAAe,EACb,EACA,EACiB,CAGjB,OAFI,GAAY,MAAQ,IAAa,GAAW,GAC5C,OAAO,GAAa,SAAiB,EAClC,EAAQ,OAAO,EAAU,CAAE,QAAS,EAAM,CAAC,CACpD,CAGA,SAAS,EAAmB,EAA6C,EAAgC,CACvG,GAAI,GAAY,MAAQ,IAAa,GAAI,MAAO,GAChD,GAAI,OAAO,GAAa,SAAU,OAAO,EACzC,IAAM,EAAU,EAAS,QAAQ,EAAW,IAAI,EAC1C,EAAO,EAAU,UAEvB,OADA,EAAQ,EACD,CACT,CAEA,SAAS,EAAW,EAAuB,CACzC,OAAO,EACJ,QAAQ,KAAM,OAAO,CAAC,CACtB,QAAQ,KAAM,MAAM,CAAC,CACrB,QAAQ,KAAM,MAAM,CAAC,CACrB,QAAQ,KAAM,QAAQ,CAAC,CACvB,QAAQ,KAAM,OAAO,CAC1B,CAEA,SAAS,EAAe,EAAwB,CAC9C,OAAO,KAAK,UAAU,GAAS,IAAI,CAAC,CACjC,QAAQ,KAAM,SAAS,CAAC,CACxB,QAAQ,KAAM,SAAS,CAC5B"}