@elurjs/kit 2.4.4

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 (255) hide show
  1. package/CHANGELOG.md +874 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1330 -0
  4. package/bin/elur-kit.js +54 -0
  5. package/dist/lib/action/define.d.cts +72 -0
  6. package/dist/lib/action/define.d.ts +72 -0
  7. package/dist/lib/action/error-store.d.cts +29 -0
  8. package/dist/lib/action/error-store.d.ts +29 -0
  9. package/dist/lib/action/index.cjs +1 -0
  10. package/dist/lib/action/index.d.cts +64 -0
  11. package/dist/lib/action/index.d.ts +64 -0
  12. package/dist/lib/action/index.js +2 -0
  13. package/dist/lib/action/origin.d.cts +20 -0
  14. package/dist/lib/action/origin.d.ts +20 -0
  15. package/dist/lib/action/scan.d.cts +28 -0
  16. package/dist/lib/action/scan.d.ts +28 -0
  17. package/dist/lib/action/server.d.cts +30 -0
  18. package/dist/lib/action/server.d.ts +30 -0
  19. package/dist/lib/action-BB6N5yD-.js +111 -0
  20. package/dist/lib/action-BB6N5yD-.js.map +1 -0
  21. package/dist/lib/action-DZHihU3Z.cjs +2 -0
  22. package/dist/lib/action-DZHihU3Z.cjs.map +1 -0
  23. package/dist/lib/adapters/bun.cjs +30 -0
  24. package/dist/lib/adapters/bun.cjs.map +1 -0
  25. package/dist/lib/adapters/bun.d.cts +13 -0
  26. package/dist/lib/adapters/bun.d.ts +13 -0
  27. package/dist/lib/adapters/bun.js +65 -0
  28. package/dist/lib/adapters/bun.js.map +1 -0
  29. package/dist/lib/adapters/index.cjs +0 -0
  30. package/dist/lib/adapters/index.d.cts +30 -0
  31. package/dist/lib/adapters/index.d.ts +30 -0
  32. package/dist/lib/adapters/index.js +0 -0
  33. package/dist/lib/adapters/netlify.cjs +10 -0
  34. package/dist/lib/adapters/netlify.cjs.map +1 -0
  35. package/dist/lib/adapters/netlify.d.cts +13 -0
  36. package/dist/lib/adapters/netlify.d.ts +13 -0
  37. package/dist/lib/adapters/netlify.js +51 -0
  38. package/dist/lib/adapters/netlify.js.map +1 -0
  39. package/dist/lib/adapters/node.cjs +46 -0
  40. package/dist/lib/adapters/node.cjs.map +1 -0
  41. package/dist/lib/adapters/node.d.cts +13 -0
  42. package/dist/lib/adapters/node.d.ts +13 -0
  43. package/dist/lib/adapters/node.js +105 -0
  44. package/dist/lib/adapters/node.js.map +1 -0
  45. package/dist/lib/adapters/shared.d.cts +17 -0
  46. package/dist/lib/adapters/shared.d.ts +17 -0
  47. package/dist/lib/adapters/vercel.cjs +2 -0
  48. package/dist/lib/adapters/vercel.cjs.map +1 -0
  49. package/dist/lib/adapters/vercel.d.cts +9 -0
  50. package/dist/lib/adapters/vercel.d.ts +9 -0
  51. package/dist/lib/adapters/vercel.js +62 -0
  52. package/dist/lib/adapters/vercel.js.map +1 -0
  53. package/dist/lib/build/build.d.cts +83 -0
  54. package/dist/lib/build/build.d.ts +83 -0
  55. package/dist/lib/build/document-shell.d.cts +48 -0
  56. package/dist/lib/build/document-shell.d.ts +48 -0
  57. package/dist/lib/build/transform-source.d.cts +26 -0
  58. package/dist/lib/build/transform-source.d.ts +26 -0
  59. package/dist/lib/build/vite-build.d.cts +78 -0
  60. package/dist/lib/build/vite-build.d.ts +78 -0
  61. package/dist/lib/cache/policy.d.cts +25 -0
  62. package/dist/lib/cache/policy.d.ts +25 -0
  63. package/dist/lib/cache.d.cts +13 -0
  64. package/dist/lib/cache.d.ts +13 -0
  65. package/dist/lib/capabilities-C0TDQoMY.cjs +2 -0
  66. package/dist/lib/capabilities-C0TDQoMY.cjs.map +1 -0
  67. package/dist/lib/capabilities-C7txofmY.js +45 -0
  68. package/dist/lib/capabilities-C7txofmY.js.map +1 -0
  69. package/dist/lib/cli/commands.d.cts +15 -0
  70. package/dist/lib/cli/commands.d.ts +15 -0
  71. package/dist/lib/cli.cjs +5132 -0
  72. package/dist/lib/cli.cjs.map +1 -0
  73. package/dist/lib/cli.d.cts +30 -0
  74. package/dist/lib/cli.d.ts +30 -0
  75. package/dist/lib/cli.js +5131 -0
  76. package/dist/lib/cli.js.map +1 -0
  77. package/dist/lib/collections-Dbc8aRLf.js +233 -0
  78. package/dist/lib/collections-Dbc8aRLf.js.map +1 -0
  79. package/dist/lib/collections-VDy5g9p8.cjs +13 -0
  80. package/dist/lib/collections-VDy5g9p8.cjs.map +1 -0
  81. package/dist/lib/config/index.cjs +2 -0
  82. package/dist/lib/config/index.cjs.map +1 -0
  83. package/dist/lib/config/index.d.cts +100 -0
  84. package/dist/lib/config/index.d.ts +100 -0
  85. package/dist/lib/config/index.js +122 -0
  86. package/dist/lib/config/index.js.map +1 -0
  87. package/dist/lib/content/collections.d.cts +69 -0
  88. package/dist/lib/content/collections.d.ts +69 -0
  89. package/dist/lib/content/frontmatter.d.cts +20 -0
  90. package/dist/lib/content/frontmatter.d.ts +20 -0
  91. package/dist/lib/content/index.cjs +2 -0
  92. package/dist/lib/content/index.cjs.map +1 -0
  93. package/dist/lib/content/index.d.cts +5 -0
  94. package/dist/lib/content/index.d.ts +5 -0
  95. package/dist/lib/content/index.js +28 -0
  96. package/dist/lib/content/index.js.map +1 -0
  97. package/dist/lib/content/markdown.d.cts +6 -0
  98. package/dist/lib/content/markdown.d.ts +6 -0
  99. package/dist/lib/content/raw.d.cts +9 -0
  100. package/dist/lib/content/raw.d.ts +9 -0
  101. package/dist/lib/content/schema.d.cts +14 -0
  102. package/dist/lib/content/schema.d.ts +14 -0
  103. package/dist/lib/errors-0X7pCFNU.cjs +2 -0
  104. package/dist/lib/errors-0X7pCFNU.cjs.map +1 -0
  105. package/dist/lib/errors-ePYxDlhU.js +70 -0
  106. package/dist/lib/errors-ePYxDlhU.js.map +1 -0
  107. package/dist/lib/errors.d.cts +84 -0
  108. package/dist/lib/errors.d.ts +84 -0
  109. package/dist/lib/hydrate-8a3TbQbs.cjs +2 -0
  110. package/dist/lib/hydrate-8a3TbQbs.cjs.map +1 -0
  111. package/dist/lib/hydrate-CgZGrTxF.js +126 -0
  112. package/dist/lib/hydrate-CgZGrTxF.js.map +1 -0
  113. package/dist/lib/image/index.cjs +2 -0
  114. package/dist/lib/image/index.cjs.map +1 -0
  115. package/dist/lib/image/index.d.cts +37 -0
  116. package/dist/lib/image/index.d.ts +37 -0
  117. package/dist/lib/image/index.js +49 -0
  118. package/dist/lib/image/index.js.map +1 -0
  119. package/dist/lib/image/pipeline.d.cts +37 -0
  120. package/dist/lib/image/pipeline.d.ts +37 -0
  121. package/dist/lib/image/registry.cjs +2 -0
  122. package/dist/lib/image/registry.cjs.map +1 -0
  123. package/dist/lib/image/registry.d.cts +26 -0
  124. package/dist/lib/image/registry.d.ts +26 -0
  125. package/dist/lib/image/registry.js +23 -0
  126. package/dist/lib/image/registry.js.map +1 -0
  127. package/dist/lib/image/service.cjs +2 -0
  128. package/dist/lib/image/service.cjs.map +1 -0
  129. package/dist/lib/image/service.d.cts +164 -0
  130. package/dist/lib/image/service.d.ts +164 -0
  131. package/dist/lib/image/service.js +308 -0
  132. package/dist/lib/image/service.js.map +1 -0
  133. package/dist/lib/index.cjs +1 -0
  134. package/dist/lib/index.cjs.map +1 -0
  135. package/dist/lib/index.d.cts +38 -0
  136. package/dist/lib/index.d.ts +38 -0
  137. package/dist/lib/index.js +1 -0
  138. package/dist/lib/index.js.map +1 -0
  139. package/dist/lib/integrations/hooks.d.cts +58 -0
  140. package/dist/lib/integrations/hooks.d.ts +58 -0
  141. package/dist/lib/integrations/index.cjs +1 -0
  142. package/dist/lib/integrations/index.d.cts +18 -0
  143. package/dist/lib/integrations/index.d.ts +18 -0
  144. package/dist/lib/integrations/index.js +2 -0
  145. package/dist/lib/integrations-7dpN1RYB.js +35 -0
  146. package/dist/lib/integrations-7dpN1RYB.js.map +1 -0
  147. package/dist/lib/integrations-DDqfWjvH.cjs +2 -0
  148. package/dist/lib/integrations-DDqfWjvH.cjs.map +1 -0
  149. package/dist/lib/interpolation-plugin-Bt-KRKVY.js +365 -0
  150. package/dist/lib/interpolation-plugin-Bt-KRKVY.js.map +1 -0
  151. package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs +31 -0
  152. package/dist/lib/interpolation-plugin-CMPCDlVJ.cjs.map +1 -0
  153. package/dist/lib/island/generate-entry.d.cts +27 -0
  154. package/dist/lib/island/generate-entry.d.ts +27 -0
  155. package/dist/lib/island/hydrate.d.cts +33 -0
  156. package/dist/lib/island/hydrate.d.ts +33 -0
  157. package/dist/lib/island/index.cjs +1 -0
  158. package/dist/lib/island/index.d.cts +2 -0
  159. package/dist/lib/island/index.d.ts +2 -0
  160. package/dist/lib/island/index.js +1 -0
  161. package/dist/lib/island/island.d.cts +32 -0
  162. package/dist/lib/island/island.d.ts +32 -0
  163. package/dist/lib/island/scan.d.cts +14 -0
  164. package/dist/lib/island/scan.d.ts +14 -0
  165. package/dist/lib/manifest/index.cjs +3 -0
  166. package/dist/lib/manifest/index.cjs.map +1 -0
  167. package/dist/lib/manifest/index.d.cts +18 -0
  168. package/dist/lib/manifest/index.d.ts +18 -0
  169. package/dist/lib/manifest/index.js +110 -0
  170. package/dist/lib/manifest/index.js.map +1 -0
  171. package/dist/lib/middleware/index.d.cts +56 -0
  172. package/dist/lib/middleware/index.d.ts +56 -0
  173. package/dist/lib/middleware/stream-boundary.d.cts +53 -0
  174. package/dist/lib/middleware/stream-boundary.d.ts +53 -0
  175. package/dist/lib/node-http-DRAUhO0c.js +617 -0
  176. package/dist/lib/node-http-DRAUhO0c.js.map +1 -0
  177. package/dist/lib/node-http-DhxguYyz.cjs +13 -0
  178. package/dist/lib/node-http-DhxguYyz.cjs.map +1 -0
  179. package/dist/lib/render/render-to-string.d.cts +12 -0
  180. package/dist/lib/render/render-to-string.d.ts +12 -0
  181. package/dist/lib/render/ssr-flag.d.cts +4 -0
  182. package/dist/lib/render/ssr-flag.d.ts +4 -0
  183. package/dist/lib/route-scanner-JY6YrNH5.cjs +2 -0
  184. package/dist/lib/route-scanner-JY6YrNH5.cjs.map +1 -0
  185. package/dist/lib/route-scanner-XcLId7lK.js +100 -0
  186. package/dist/lib/route-scanner-XcLId7lK.js.map +1 -0
  187. package/dist/lib/router/client.cjs +2 -0
  188. package/dist/lib/router/client.cjs.map +1 -0
  189. package/dist/lib/router/client.d.cts +47 -0
  190. package/dist/lib/router/client.d.ts +47 -0
  191. package/dist/lib/router/client.js +298 -0
  192. package/dist/lib/router/client.js.map +1 -0
  193. package/dist/lib/router/route-scanner.d.cts +50 -0
  194. package/dist/lib/router/route-scanner.d.ts +50 -0
  195. package/dist/lib/runtime/capabilities.d.cts +47 -0
  196. package/dist/lib/runtime/capabilities.d.ts +47 -0
  197. package/dist/lib/runtime/context.d.cts +140 -0
  198. package/dist/lib/runtime/context.d.ts +140 -0
  199. package/dist/lib/runtime/handler.d.cts +39 -0
  200. package/dist/lib/runtime/handler.d.ts +39 -0
  201. package/dist/lib/runtime/index.cjs +1 -0
  202. package/dist/lib/runtime/index.d.cts +6 -0
  203. package/dist/lib/runtime/index.d.ts +6 -0
  204. package/dist/lib/runtime/index.js +4 -0
  205. package/dist/lib/runtime/node-http.d.cts +2 -0
  206. package/dist/lib/runtime/node-http.d.ts +2 -0
  207. package/dist/lib/runtime/security-headers.d.cts +13 -0
  208. package/dist/lib/runtime/security-headers.d.ts +13 -0
  209. package/dist/lib/runtime/static.d.cts +1 -0
  210. package/dist/lib/runtime/static.d.ts +1 -0
  211. package/dist/lib/runtime-CrH5X-Es.cjs +2 -0
  212. package/dist/lib/runtime-CrH5X-Es.cjs.map +1 -0
  213. package/dist/lib/runtime-Ds2-wH6P.js +520 -0
  214. package/dist/lib/runtime-Ds2-wH6P.js.map +1 -0
  215. package/dist/lib/scan-CY2O9ty6.cjs +2 -0
  216. package/dist/lib/scan-CY2O9ty6.cjs.map +1 -0
  217. package/dist/lib/scan-CjMUgnsd.js +22 -0
  218. package/dist/lib/scan-CjMUgnsd.js.map +1 -0
  219. package/dist/lib/scan-DXzYtmRQ.cjs +2 -0
  220. package/dist/lib/scan-DXzYtmRQ.cjs.map +1 -0
  221. package/dist/lib/scan-Dn93ycXG.js +33 -0
  222. package/dist/lib/scan-Dn93ycXG.js.map +1 -0
  223. package/dist/lib/seo/index.cjs +10 -0
  224. package/dist/lib/seo/index.cjs.map +1 -0
  225. package/dist/lib/seo/index.d.cts +99 -0
  226. package/dist/lib/seo/index.d.ts +99 -0
  227. package/dist/lib/seo/index.js +41 -0
  228. package/dist/lib/seo/index.js.map +1 -0
  229. package/dist/lib/shared-CfqOLsKj.cjs +183 -0
  230. package/dist/lib/shared-CfqOLsKj.cjs.map +1 -0
  231. package/dist/lib/shared-DU3iQgr7.js +236 -0
  232. package/dist/lib/shared-DU3iQgr7.js.map +1 -0
  233. package/dist/lib/ssr/match.d.cts +25 -0
  234. package/dist/lib/ssr/match.d.ts +25 -0
  235. package/dist/lib/ssr/render.d.cts +62 -0
  236. package/dist/lib/ssr/render.d.ts +62 -0
  237. package/dist/lib/ssr/server.d.cts +41 -0
  238. package/dist/lib/ssr/server.d.ts +41 -0
  239. package/dist/lib/ssr/stream.d.cts +48 -0
  240. package/dist/lib/ssr/stream.d.ts +48 -0
  241. package/dist/lib/stream-CpOG052V.js +104 -0
  242. package/dist/lib/stream-CpOG052V.js.map +1 -0
  243. package/dist/lib/stream-DmZjYqHm.cjs +17 -0
  244. package/dist/lib/stream-DmZjYqHm.cjs.map +1 -0
  245. package/dist/lib/types.d.cts +97 -0
  246. package/dist/lib/types.d.ts +97 -0
  247. package/dist/lib/vite/index.cjs +2 -0
  248. package/dist/lib/vite/index.cjs.map +1 -0
  249. package/dist/lib/vite/index.d.cts +41 -0
  250. package/dist/lib/vite/index.d.ts +41 -0
  251. package/dist/lib/vite/index.js +229 -0
  252. package/dist/lib/vite/index.js.map +1 -0
  253. package/dist/lib/vite/interpolation-plugin.d.cts +64 -0
  254. package/dist/lib/vite/interpolation-plugin.d.ts +64 -0
  255. package/package.json +292 -0
@@ -0,0 +1,33 @@
1
+ import type { ElurTemplate } from "@elurjs/core";
2
+ export type IslandComponent<TProps = unknown> = (props: TProps) => ElurTemplate | null | false | undefined;
3
+ export type { IslandDirective } from "./island.js";
4
+ /** Lazy island loader in a discriminated form (no probe required to detect). */
5
+ export interface IslandLoader<TProps = unknown> {
6
+ load: () => Promise<IslandComponent<TProps>>;
7
+ }
8
+ /**
9
+ * Island registry entry. Two unambiguous shapes:
10
+ * - `IslandComponent` (eager component function).
11
+ * - `IslandLoader` `{ load }` (lazy, code-split loader).
12
+ *
13
+ * Legacy async loader functions are also accepted for backwards compatibility
14
+ * and detected *without invoking them* (via the `AsyncFunction` tag), so no
15
+ * side effects or duplicate signal creation happen during detection.
16
+ */
17
+ export type IslandRegistryEntry<TProps = unknown> = IslandComponent<TProps> | IslandLoader<TProps> | (() => Promise<IslandComponent<TProps>>);
18
+ export type IslandRegistry = Record<string, IslandRegistryEntry<any>>;
19
+ /**
20
+ * Wraps a lazy island loader in the discriminated `{ load }` form.
21
+ */
22
+ export declare function lazyIsland<TProps = unknown>(loader: () => Promise<IslandComponent<TProps>>): IslandLoader<TProps>;
23
+ /**
24
+ * Hydrates all islands on the page using the provided registry.
25
+ *
26
+ * @param registry Map from island name to component factory.
27
+ */
28
+ /**
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.
31
+ */
32
+ export declare function cleanupHydratedIslands(): void;
33
+ export declare function hydrateIslands(registry: IslandRegistry): void;
@@ -0,0 +1,33 @@
1
+ import type { ElurTemplate } from "@elurjs/core";
2
+ export type IslandComponent<TProps = unknown> = (props: TProps) => ElurTemplate | null | false | undefined;
3
+ export type { IslandDirective } from "./island.js";
4
+ /** Lazy island loader in a discriminated form (no probe required to detect). */
5
+ export interface IslandLoader<TProps = unknown> {
6
+ load: () => Promise<IslandComponent<TProps>>;
7
+ }
8
+ /**
9
+ * Island registry entry. Two unambiguous shapes:
10
+ * - `IslandComponent` (eager component function).
11
+ * - `IslandLoader` `{ load }` (lazy, code-split loader).
12
+ *
13
+ * Legacy async loader functions are also accepted for backwards compatibility
14
+ * and detected *without invoking them* (via the `AsyncFunction` tag), so no
15
+ * side effects or duplicate signal creation happen during detection.
16
+ */
17
+ export type IslandRegistryEntry<TProps = unknown> = IslandComponent<TProps> | IslandLoader<TProps> | (() => Promise<IslandComponent<TProps>>);
18
+ export type IslandRegistry = Record<string, IslandRegistryEntry<any>>;
19
+ /**
20
+ * Wraps a lazy island loader in the discriminated `{ load }` form.
21
+ */
22
+ export declare function lazyIsland<TProps = unknown>(loader: () => Promise<IslandComponent<TProps>>): IslandLoader<TProps>;
23
+ /**
24
+ * Hydrates all islands on the page using the provided registry.
25
+ *
26
+ * @param registry Map from island name to component factory.
27
+ */
28
+ /**
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.
31
+ */
32
+ export declare function cleanupHydratedIslands(): void;
33
+ export declare function hydrateIslands(registry: IslandRegistry): void;
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../hydrate-8a3TbQbs.cjs");exports.cleanupHydratedIslands=e.t,exports.hydrateIslands=e.n,exports.lazyIsland=e.r;
@@ -0,0 +1,2 @@
1
+ export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry } from "./hydrate.js";
2
+ export type { IslandComponent, IslandDirective, IslandOptions } from "./island.js";
@@ -0,0 +1,2 @@
1
+ export { hydrateIslands, cleanupHydratedIslands, lazyIsland, type IslandRegistry, type IslandLoader, type IslandRegistryEntry } from "./hydrate.js";
2
+ export type { IslandComponent, IslandDirective, IslandOptions } from "./island.js";
@@ -0,0 +1 @@
1
+ import{n as e,r as t,t as n}from"../hydrate-CgZGrTxF.js";export{n as cleanupHydratedIslands,e as hydrateIslands,t as lazyIsland};
@@ -0,0 +1,32 @@
1
+ import { type ElurTemplate } from "@elurjs/core";
2
+ export type IslandDirective = "load" | "idle" | "visible" | "only";
3
+ export interface IslandComponent<TProps = unknown> {
4
+ (props: TProps): ElurTemplate | null | false | undefined;
5
+ }
6
+ /**
7
+ * Options for {@link island}.
8
+ *
9
+ * - `ssr`: Whether to execute the component on the server. Defaults to `true`
10
+ * unless `directive === "only"` (then `false`). When `false`, the component
11
+ * is never called during SSR; only `fallback` is rendered.
12
+ * - `fallback`: HTML to render inside the island marker when SSR is skipped or
13
+ * the component returns null/false. Accepts a `ElurTemplate` (reactive, with
14
+ * signals) or a plain string. Defaults to an empty string.
15
+ */
16
+ export interface IslandOptions {
17
+ ssr?: boolean;
18
+ fallback?: ElurTemplate | string;
19
+ }
20
+ /**
21
+ * Renders a component to a static HTML string with island markers.
22
+ *
23
+ * @param name Unique island name used by the client entry to look up the module.
24
+ * @param component Island component. Executed on the server unless `directive`
25
+ * is `"only"` or `options.ssr` is `false`.
26
+ * @param props Props passed to the component and serialized for hydration.
27
+ * @param directive When to hydrate on the client. Use `"only"` to skip SSR
28
+ * entirely (client-only island).
29
+ * @param options SSR strategy and fallback content.
30
+ * @returns A ElurTemplate that renders the island placeholder.
31
+ */
32
+ export declare function island<TProps>(name: string, component: IslandComponent<TProps>, props: TProps, directive?: IslandDirective, options?: IslandOptions): ElurTemplate;
@@ -0,0 +1,32 @@
1
+ import { type ElurTemplate } from "@elurjs/core";
2
+ export type IslandDirective = "load" | "idle" | "visible" | "only";
3
+ export interface IslandComponent<TProps = unknown> {
4
+ (props: TProps): ElurTemplate | null | false | undefined;
5
+ }
6
+ /**
7
+ * Options for {@link island}.
8
+ *
9
+ * - `ssr`: Whether to execute the component on the server. Defaults to `true`
10
+ * unless `directive === "only"` (then `false`). When `false`, the component
11
+ * is never called during SSR; only `fallback` is rendered.
12
+ * - `fallback`: HTML to render inside the island marker when SSR is skipped or
13
+ * the component returns null/false. Accepts a `ElurTemplate` (reactive, with
14
+ * signals) or a plain string. Defaults to an empty string.
15
+ */
16
+ export interface IslandOptions {
17
+ ssr?: boolean;
18
+ fallback?: ElurTemplate | string;
19
+ }
20
+ /**
21
+ * Renders a component to a static HTML string with island markers.
22
+ *
23
+ * @param name Unique island name used by the client entry to look up the module.
24
+ * @param component Island component. Executed on the server unless `directive`
25
+ * is `"only"` or `options.ssr` is `false`.
26
+ * @param props Props passed to the component and serialized for hydration.
27
+ * @param directive When to hydrate on the client. Use `"only"` to skip SSR
28
+ * entirely (client-only island).
29
+ * @param options SSR strategy and fallback content.
30
+ * @returns A ElurTemplate that renders the island placeholder.
31
+ */
32
+ export declare function island<TProps>(name: string, component: IslandComponent<TProps>, props: TProps, directive?: IslandDirective, options?: IslandOptions): ElurTemplate;
@@ -0,0 +1,14 @@
1
+ /** A single island component discovered by the scanner. */
2
+ export interface IslandModule {
3
+ /** Registry name, derived from the path relative to the islands dir. */
4
+ name: string;
5
+ /** Absolute file system path to the island module. */
6
+ filePath: string;
7
+ }
8
+ /**
9
+ * Scans an islands directory for island component modules.
10
+ *
11
+ * @param islandsDir Absolute path to the islands directory (e.g. "src/islands").
12
+ * @returns Discovered island modules, sorted by name.
13
+ */
14
+ export declare function scanIslands(islandsDir: string): Promise<IslandModule[]>;
@@ -0,0 +1,14 @@
1
+ /** A single island component discovered by the scanner. */
2
+ export interface IslandModule {
3
+ /** Registry name, derived from the path relative to the islands dir. */
4
+ name: string;
5
+ /** Absolute file system path to the island module. */
6
+ filePath: string;
7
+ }
8
+ /**
9
+ * Scans an islands directory for island component modules.
10
+ *
11
+ * @param islandsDir Absolute path to the islands directory (e.g. "src/islands").
12
+ * @returns Discovered island modules, sorted by name.
13
+ */
14
+ export declare function scanIslands(islandsDir: string): Promise<IslandModule[]>;
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../route-scanner-JY6YrNH5.cjs"),t=require("../scan-CY2O9ty6.cjs"),n=require("../scan-DXzYtmRQ.cjs"),r=require("../integrations-DDqfWjvH.cjs");let i=require("node:fs/promises"),a=require("node:path");async function o(i){let[a,o,s]=await Promise.all([e.t(i.appDir),n.n(i.appDir),t.t(i.islandsDir)]);l(a),p(s);let c={version:1,root:i.root,routes:a,actions:o,islands:s,base:i.base,output:i.output};return await r.t(i.integrations,`routes`,[c,{root:i.root,command:`build`}]),c}async function s(e,t){await(0,i.mkdir)((0,a.dirname)(t),{recursive:!0}),await(0,i.writeFile)(t,JSON.stringify(m(e),null,2),`utf8`)}async function c(e,t){let n=e.routes.pages.map(e=>JSON.stringify(e.path)),r=Object.values(e.actions).flatMap(e=>Object.keys(e)).filter((e,t,n)=>n.indexOf(e)===t).map(e=>JSON.stringify(e)),o=[`export type ElurRoutePath = ${n.length?n.join(` | `):`never`};`,`export type ElurActionName = ${r.length?r.join(` | `):`never`};`,`export interface ElurRouteParams { [name: string]: string | string[] | undefined }`,``].join(`
2
+ `);await(0,i.mkdir)((0,a.dirname)(t),{recursive:!0}),await(0,i.writeFile)(t,o,`utf8`)}function l(e){let t=new Map;for(let n of e.pages)d(t,n.path,n.pagePath,`page`),f(n.path,n.pagePath);for(let n of e.api)d(t,n.path,n.routePath,`API`),f(n.path,n.routePath)}function u(e,t){if(/\.server\.[cm]?[jt]sx?$/.test(e))throw Error(`[elur-kit] Server-only module imported by client${t?` from ${t}`:``}: ${e}`)}function d(e,t,n,r){let i=e.get(t);if(i)throw Error(`[elur-kit] Duplicate ${r} route "${t}": ${i} and ${n}`);e.set(t,n)}function f(e,t){if(e===`/__elur-js`||e.startsWith(`/__elur-js/`)||e===`/_elur`||e.startsWith(`/_elur/`))throw Error(`[elur-kit] Reserved route "${e}" declared by ${t}`)}function p(e){let t=new Set;for(let n of e){if(t.has(n.name))throw Error(`[elur-kit] Duplicate island name: ${n.name}`);t.add(n.name)}}function m(e){let t=t=>t?(0,a.relative)(e.root,t).split(`\\`).join(`/`):void 0,n={pages:e.routes.pages.map(e=>({...e,pagePath:t(e.pagePath),dataPath:t(e.dataPath),actionPath:t(e.actionPath),loadingPath:t(e.loadingPath),layouts:e.layouts.map(e=>t(e))})),api:e.routes.api.map(e=>({...e,routePath:t(e.routePath)})),error404:e.routes.error404?{...e.routes.error404,pagePath:t(e.routes.error404.pagePath),dataPath:t(e.routes.error404.dataPath),actionPath:t(e.routes.error404.actionPath),loadingPath:t(e.routes.error404.loadingPath),layouts:e.routes.error404.layouts.map(e=>t(e))}:void 0,error500:e.routes.error500?{...e.routes.error500,pagePath:t(e.routes.error500.pagePath),dataPath:t(e.routes.error500.dataPath),actionPath:t(e.routes.error500.actionPath),loadingPath:t(e.routes.error500.loadingPath),layouts:e.routes.error500.layouts.map(e=>t(e))}:void 0},r={};for(let[n,i]of Object.entries(e.actions))r[n]=Object.fromEntries(Object.entries(i).map(([e,n])=>[e,t(n)]));return{...e,root:`.`,routes:n,actions:r,islands:e.islands.map(e=>({...e,filePath:t(e.filePath)}))}}exports.assertClientImportAllowed=u,exports.createAppManifest=o,exports.validateManifestRoutes=l,exports.writeAppManifest=s,exports.writeRouteTypes=c;
3
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/manifest/index.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, relative } from \"node:path\";\nimport { scanActions, type ActionRegistry } from \"../action/scan.js\";\nimport type { ResolvedElurConfig } from \"../config/index.js\";\nimport { runIntegrationHook } from \"../integrations/index.js\";\nimport { scanIslands, type IslandModule } from \"../island/scan.js\";\nimport { scanRoutes, type ScannedRoutes } from \"../router/route-scanner.js\";\n\nexport interface AppManifest {\n version: 1;\n root: string;\n routes: ScannedRoutes;\n actions: ActionRegistry;\n islands: IslandModule[];\n base: string;\n output: ResolvedElurConfig[\"output\"];\n}\n\nexport async function createAppManifest(config: ResolvedElurConfig): Promise<AppManifest> {\n const [routes, actions, islands] = await Promise.all([\n scanRoutes(config.appDir),\n scanActions(config.appDir),\n scanIslands(config.islandsDir),\n ]);\n validateManifestRoutes(routes);\n validateIslands(islands);\n const manifest: AppManifest = {\n version: 1,\n root: config.root,\n routes,\n actions,\n islands,\n base: config.base,\n output: config.output,\n };\n await runIntegrationHook(config.integrations, \"routes\", [\n manifest,\n { root: config.root, command: \"build\" },\n ]);\n return manifest;\n}\n\nexport async function writeAppManifest(manifest: AppManifest, path: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(toPortableManifest(manifest), null, 2), \"utf8\");\n}\n\nexport async function writeRouteTypes(manifest: AppManifest, path: string): Promise<void> {\n const routePaths = manifest.routes.pages.map((route) => JSON.stringify(route.path));\n const actionNames = Object.values(manifest.actions)\n .flatMap((actions) => Object.keys(actions))\n .filter((name, index, names) => names.indexOf(name) === index)\n .map((name) => JSON.stringify(name));\n const source = [\n `export type ElurRoutePath = ${routePaths.length ? routePaths.join(\" | \") : \"never\"};`,\n `export type ElurActionName = ${actionNames.length ? actionNames.join(\" | \") : \"never\"};`,\n \"export interface ElurRouteParams { [name: string]: string | string[] | undefined }\",\n \"\",\n ].join(\"\\n\");\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, source, \"utf8\");\n}\n\nexport function validateManifestRoutes(routes: ScannedRoutes): void {\n const seen = new Map<string, string>();\n for (const route of routes.pages) {\n registerRoute(seen, route.path, route.pagePath, \"page\");\n assertNotReserved(route.path, route.pagePath);\n }\n for (const route of routes.api) {\n registerRoute(seen, route.path, route.routePath, \"API\");\n assertNotReserved(route.path, route.routePath);\n }\n}\n\nexport function assertClientImportAllowed(id: string, importer?: string): void {\n if (/\\.server\\.[cm]?[jt]sx?$/.test(id)) {\n throw new Error(`[elur-kit] Server-only module imported by client${importer ? ` from ${importer}` : \"\"}: ${id}`);\n }\n}\n\nfunction registerRoute(seen: Map<string, string>, path: string, file: string, kind: string): void {\n const existing = seen.get(path);\n if (existing) {\n throw new Error(`[elur-kit] Duplicate ${kind} route \"${path}\": ${existing} and ${file}`);\n }\n seen.set(path, file);\n}\n\nfunction assertNotReserved(path: string, file: string): void {\n if (path === \"/__elur-js\" || path.startsWith(\"/__elur-js/\") || path === \"/_elur\" || path.startsWith(\"/_elur/\")) {\n throw new Error(`[elur-kit] Reserved route \"${path}\" declared by ${file}`);\n }\n}\n\nfunction validateIslands(islands: readonly IslandModule[]): void {\n const names = new Set<string>();\n for (const island of islands) {\n if (names.has(island.name)) throw new Error(`[elur-kit] Duplicate island name: ${island.name}`);\n names.add(island.name);\n }\n}\n\nfunction toPortableManifest(manifest: AppManifest): AppManifest {\n const relativePath = (path: string | undefined) => path ? relative(manifest.root, path).split(\"\\\\\").join(\"/\") : undefined;\n const routes: ScannedRoutes = {\n pages: manifest.routes.pages.map((route) => ({\n ...route,\n pagePath: relativePath(route.pagePath)!,\n dataPath: relativePath(route.dataPath),\n actionPath: relativePath(route.actionPath),\n loadingPath: relativePath(route.loadingPath),\n layouts: route.layouts.map((layout) => relativePath(layout)!),\n })),\n api: manifest.routes.api.map((route) => ({ ...route, routePath: relativePath(route.routePath)! })),\n error404: manifest.routes.error404 ? {\n ...manifest.routes.error404,\n pagePath: relativePath(manifest.routes.error404.pagePath)!,\n dataPath: relativePath(manifest.routes.error404.dataPath),\n actionPath: relativePath(manifest.routes.error404.actionPath),\n loadingPath: relativePath(manifest.routes.error404.loadingPath),\n layouts: manifest.routes.error404.layouts.map((layout) => relativePath(layout)!),\n } : undefined,\n error500: manifest.routes.error500 ? {\n ...manifest.routes.error500,\n pagePath: relativePath(manifest.routes.error500.pagePath)!,\n dataPath: relativePath(manifest.routes.error500.dataPath),\n actionPath: relativePath(manifest.routes.error500.actionPath),\n loadingPath: relativePath(manifest.routes.error500.loadingPath),\n layouts: manifest.routes.error500.layouts.map((layout) => relativePath(layout)!),\n } : undefined,\n };\n const actions: ActionRegistry = {};\n for (const [page, pageActions] of Object.entries(manifest.actions)) {\n actions[page] = Object.fromEntries(\n Object.entries(pageActions).map(([name, path]) => [name, relativePath(path)!]),\n );\n }\n return {\n ...manifest,\n root: \".\",\n routes,\n actions,\n islands: manifest.islands.map((island) => ({ ...island, filePath: relativePath(island.filePath)! })),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { type ActionRegistry } from "../action/scan.js";
2
+ import type { ResolvedElurConfig } from "../config/index.js";
3
+ import { type IslandModule } from "../island/scan.js";
4
+ import { type ScannedRoutes } from "../router/route-scanner.js";
5
+ export interface AppManifest {
6
+ version: 1;
7
+ root: string;
8
+ routes: ScannedRoutes;
9
+ actions: ActionRegistry;
10
+ islands: IslandModule[];
11
+ base: string;
12
+ output: ResolvedElurConfig["output"];
13
+ }
14
+ export declare function createAppManifest(config: ResolvedElurConfig): Promise<AppManifest>;
15
+ export declare function writeAppManifest(manifest: AppManifest, path: string): Promise<void>;
16
+ export declare function writeRouteTypes(manifest: AppManifest, path: string): Promise<void>;
17
+ export declare function validateManifestRoutes(routes: ScannedRoutes): void;
18
+ export declare function assertClientImportAllowed(id: string, importer?: string): void;
@@ -0,0 +1,18 @@
1
+ import { type ActionRegistry } from "../action/scan.js";
2
+ import type { ResolvedElurConfig } from "../config/index.js";
3
+ import { type IslandModule } from "../island/scan.js";
4
+ import { type ScannedRoutes } from "../router/route-scanner.js";
5
+ export interface AppManifest {
6
+ version: 1;
7
+ root: string;
8
+ routes: ScannedRoutes;
9
+ actions: ActionRegistry;
10
+ islands: IslandModule[];
11
+ base: string;
12
+ output: ResolvedElurConfig["output"];
13
+ }
14
+ export declare function createAppManifest(config: ResolvedElurConfig): Promise<AppManifest>;
15
+ export declare function writeAppManifest(manifest: AppManifest, path: string): Promise<void>;
16
+ export declare function writeRouteTypes(manifest: AppManifest, path: string): Promise<void>;
17
+ export declare function validateManifestRoutes(routes: ScannedRoutes): void;
18
+ export declare function assertClientImportAllowed(id: string, importer?: string): void;
@@ -0,0 +1,110 @@
1
+ import { t as e } from "../route-scanner-XcLId7lK.js";
2
+ import { t } from "../scan-Dn93ycXG.js";
3
+ import { n } from "../scan-CjMUgnsd.js";
4
+ import { t as r } from "../integrations-7dpN1RYB.js";
5
+ import { mkdir as i, writeFile as a } from "node:fs/promises";
6
+ import { dirname as o, relative as s } from "node:path";
7
+ //#region src/manifest/index.ts
8
+ async function c(i) {
9
+ let [a, o, s] = await Promise.all([
10
+ e(i.appDir),
11
+ n(i.appDir),
12
+ t(i.islandsDir)
13
+ ]);
14
+ d(a), h(s);
15
+ let c = {
16
+ version: 1,
17
+ root: i.root,
18
+ routes: a,
19
+ actions: o,
20
+ islands: s,
21
+ base: i.base,
22
+ output: i.output
23
+ };
24
+ return await r(i.integrations, "routes", [c, {
25
+ root: i.root,
26
+ command: "build"
27
+ }]), c;
28
+ }
29
+ async function l(e, t) {
30
+ await i(o(t), { recursive: !0 }), await a(t, JSON.stringify(g(e), null, 2), "utf8");
31
+ }
32
+ async function u(e, t) {
33
+ let n = e.routes.pages.map((e) => JSON.stringify(e.path)), r = Object.values(e.actions).flatMap((e) => Object.keys(e)).filter((e, t, n) => n.indexOf(e) === t).map((e) => JSON.stringify(e)), s = [
34
+ `export type ElurRoutePath = ${n.length ? n.join(" | ") : "never"};`,
35
+ `export type ElurActionName = ${r.length ? r.join(" | ") : "never"};`,
36
+ "export interface ElurRouteParams { [name: string]: string | string[] | undefined }",
37
+ ""
38
+ ].join("\n");
39
+ await i(o(t), { recursive: !0 }), await a(t, s, "utf8");
40
+ }
41
+ function d(e) {
42
+ let t = /* @__PURE__ */ new Map();
43
+ for (let n of e.pages) p(t, n.path, n.pagePath, "page"), m(n.path, n.pagePath);
44
+ for (let n of e.api) p(t, n.path, n.routePath, "API"), m(n.path, n.routePath);
45
+ }
46
+ function f(e, t) {
47
+ if (/\.server\.[cm]?[jt]sx?$/.test(e)) throw Error(`[elur-kit] Server-only module imported by client${t ? ` from ${t}` : ""}: ${e}`);
48
+ }
49
+ function p(e, t, n, r) {
50
+ let i = e.get(t);
51
+ if (i) throw Error(`[elur-kit] Duplicate ${r} route "${t}": ${i} and ${n}`);
52
+ e.set(t, n);
53
+ }
54
+ function m(e, t) {
55
+ if (e === "/__elur-js" || e.startsWith("/__elur-js/") || e === "/_elur" || e.startsWith("/_elur/")) throw Error(`[elur-kit] Reserved route "${e}" declared by ${t}`);
56
+ }
57
+ function h(e) {
58
+ let t = /* @__PURE__ */ new Set();
59
+ for (let n of e) {
60
+ if (t.has(n.name)) throw Error(`[elur-kit] Duplicate island name: ${n.name}`);
61
+ t.add(n.name);
62
+ }
63
+ }
64
+ function g(e) {
65
+ let t = (t) => t ? s(e.root, t).split("\\").join("/") : void 0, n = {
66
+ pages: e.routes.pages.map((e) => ({
67
+ ...e,
68
+ pagePath: t(e.pagePath),
69
+ dataPath: t(e.dataPath),
70
+ actionPath: t(e.actionPath),
71
+ loadingPath: t(e.loadingPath),
72
+ layouts: e.layouts.map((e) => t(e))
73
+ })),
74
+ api: e.routes.api.map((e) => ({
75
+ ...e,
76
+ routePath: t(e.routePath)
77
+ })),
78
+ error404: e.routes.error404 ? {
79
+ ...e.routes.error404,
80
+ pagePath: t(e.routes.error404.pagePath),
81
+ dataPath: t(e.routes.error404.dataPath),
82
+ actionPath: t(e.routes.error404.actionPath),
83
+ loadingPath: t(e.routes.error404.loadingPath),
84
+ layouts: e.routes.error404.layouts.map((e) => t(e))
85
+ } : void 0,
86
+ error500: e.routes.error500 ? {
87
+ ...e.routes.error500,
88
+ pagePath: t(e.routes.error500.pagePath),
89
+ dataPath: t(e.routes.error500.dataPath),
90
+ actionPath: t(e.routes.error500.actionPath),
91
+ loadingPath: t(e.routes.error500.loadingPath),
92
+ layouts: e.routes.error500.layouts.map((e) => t(e))
93
+ } : void 0
94
+ }, r = {};
95
+ for (let [n, i] of Object.entries(e.actions)) r[n] = Object.fromEntries(Object.entries(i).map(([e, n]) => [e, t(n)]));
96
+ return {
97
+ ...e,
98
+ root: ".",
99
+ routes: n,
100
+ actions: r,
101
+ islands: e.islands.map((e) => ({
102
+ ...e,
103
+ filePath: t(e.filePath)
104
+ }))
105
+ };
106
+ }
107
+ //#endregion
108
+ export { f as assertClientImportAllowed, c as createAppManifest, d as validateManifestRoutes, l as writeAppManifest, u as writeRouteTypes };
109
+
110
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/manifest/index.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, relative } from \"node:path\";\nimport { scanActions, type ActionRegistry } from \"../action/scan.js\";\nimport type { ResolvedElurConfig } from \"../config/index.js\";\nimport { runIntegrationHook } from \"../integrations/index.js\";\nimport { scanIslands, type IslandModule } from \"../island/scan.js\";\nimport { scanRoutes, type ScannedRoutes } from \"../router/route-scanner.js\";\n\nexport interface AppManifest {\n version: 1;\n root: string;\n routes: ScannedRoutes;\n actions: ActionRegistry;\n islands: IslandModule[];\n base: string;\n output: ResolvedElurConfig[\"output\"];\n}\n\nexport async function createAppManifest(config: ResolvedElurConfig): Promise<AppManifest> {\n const [routes, actions, islands] = await Promise.all([\n scanRoutes(config.appDir),\n scanActions(config.appDir),\n scanIslands(config.islandsDir),\n ]);\n validateManifestRoutes(routes);\n validateIslands(islands);\n const manifest: AppManifest = {\n version: 1,\n root: config.root,\n routes,\n actions,\n islands,\n base: config.base,\n output: config.output,\n };\n await runIntegrationHook(config.integrations, \"routes\", [\n manifest,\n { root: config.root, command: \"build\" },\n ]);\n return manifest;\n}\n\nexport async function writeAppManifest(manifest: AppManifest, path: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, JSON.stringify(toPortableManifest(manifest), null, 2), \"utf8\");\n}\n\nexport async function writeRouteTypes(manifest: AppManifest, path: string): Promise<void> {\n const routePaths = manifest.routes.pages.map((route) => JSON.stringify(route.path));\n const actionNames = Object.values(manifest.actions)\n .flatMap((actions) => Object.keys(actions))\n .filter((name, index, names) => names.indexOf(name) === index)\n .map((name) => JSON.stringify(name));\n const source = [\n `export type ElurRoutePath = ${routePaths.length ? routePaths.join(\" | \") : \"never\"};`,\n `export type ElurActionName = ${actionNames.length ? actionNames.join(\" | \") : \"never\"};`,\n \"export interface ElurRouteParams { [name: string]: string | string[] | undefined }\",\n \"\",\n ].join(\"\\n\");\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, source, \"utf8\");\n}\n\nexport function validateManifestRoutes(routes: ScannedRoutes): void {\n const seen = new Map<string, string>();\n for (const route of routes.pages) {\n registerRoute(seen, route.path, route.pagePath, \"page\");\n assertNotReserved(route.path, route.pagePath);\n }\n for (const route of routes.api) {\n registerRoute(seen, route.path, route.routePath, \"API\");\n assertNotReserved(route.path, route.routePath);\n }\n}\n\nexport function assertClientImportAllowed(id: string, importer?: string): void {\n if (/\\.server\\.[cm]?[jt]sx?$/.test(id)) {\n throw new Error(`[elur-kit] Server-only module imported by client${importer ? ` from ${importer}` : \"\"}: ${id}`);\n }\n}\n\nfunction registerRoute(seen: Map<string, string>, path: string, file: string, kind: string): void {\n const existing = seen.get(path);\n if (existing) {\n throw new Error(`[elur-kit] Duplicate ${kind} route \"${path}\": ${existing} and ${file}`);\n }\n seen.set(path, file);\n}\n\nfunction assertNotReserved(path: string, file: string): void {\n if (path === \"/__elur-js\" || path.startsWith(\"/__elur-js/\") || path === \"/_elur\" || path.startsWith(\"/_elur/\")) {\n throw new Error(`[elur-kit] Reserved route \"${path}\" declared by ${file}`);\n }\n}\n\nfunction validateIslands(islands: readonly IslandModule[]): void {\n const names = new Set<string>();\n for (const island of islands) {\n if (names.has(island.name)) throw new Error(`[elur-kit] Duplicate island name: ${island.name}`);\n names.add(island.name);\n }\n}\n\nfunction toPortableManifest(manifest: AppManifest): AppManifest {\n const relativePath = (path: string | undefined) => path ? relative(manifest.root, path).split(\"\\\\\").join(\"/\") : undefined;\n const routes: ScannedRoutes = {\n pages: manifest.routes.pages.map((route) => ({\n ...route,\n pagePath: relativePath(route.pagePath)!,\n dataPath: relativePath(route.dataPath),\n actionPath: relativePath(route.actionPath),\n loadingPath: relativePath(route.loadingPath),\n layouts: route.layouts.map((layout) => relativePath(layout)!),\n })),\n api: manifest.routes.api.map((route) => ({ ...route, routePath: relativePath(route.routePath)! })),\n error404: manifest.routes.error404 ? {\n ...manifest.routes.error404,\n pagePath: relativePath(manifest.routes.error404.pagePath)!,\n dataPath: relativePath(manifest.routes.error404.dataPath),\n actionPath: relativePath(manifest.routes.error404.actionPath),\n loadingPath: relativePath(manifest.routes.error404.loadingPath),\n layouts: manifest.routes.error404.layouts.map((layout) => relativePath(layout)!),\n } : undefined,\n error500: manifest.routes.error500 ? {\n ...manifest.routes.error500,\n pagePath: relativePath(manifest.routes.error500.pagePath)!,\n dataPath: relativePath(manifest.routes.error500.dataPath),\n actionPath: relativePath(manifest.routes.error500.actionPath),\n loadingPath: relativePath(manifest.routes.error500.loadingPath),\n layouts: manifest.routes.error500.layouts.map((layout) => relativePath(layout)!),\n } : undefined,\n };\n const actions: ActionRegistry = {};\n for (const [page, pageActions] of Object.entries(manifest.actions)) {\n actions[page] = Object.fromEntries(\n Object.entries(pageActions).map(([name, path]) => [name, relativePath(path)!]),\n );\n }\n return {\n ...manifest,\n root: \".\",\n routes,\n actions,\n islands: manifest.islands.map((island) => ({ ...island, filePath: relativePath(island.filePath)! })),\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /** The middleware function signature. */
2
+ export type Middleware = (request: Request, context: MiddlewareContext) => Response | void | Promise<Response | void>;
3
+ /** Context passed to the middleware function. */
4
+ export interface MiddlewareContext {
5
+ /** Helper to continue to the next handler. Can attach headers, params, and locals. */
6
+ next(options?: {
7
+ headers?: Record<string, string>;
8
+ params?: Record<string, string | string[]>;
9
+ locals?: Record<string, unknown>;
10
+ }): void;
11
+ /** Matched route params (only available if the path matches a page route). */
12
+ params?: Record<string, string | string[]>;
13
+ /** Per-request locals (populated by middleware, available to loaders/actions). */
14
+ locals?: Record<string, unknown>;
15
+ }
16
+ /** Configuration for the middleware module. */
17
+ export interface MiddlewareConfig {
18
+ /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */
19
+ matcher?: string[];
20
+ }
21
+ export interface LoadedMiddleware {
22
+ handler: Middleware;
23
+ config: MiddlewareConfig;
24
+ }
25
+ /** Result of running middleware: either a response to short-circuit with, or continue. */
26
+ export type MiddlewareResult = {
27
+ kind: "response";
28
+ response: Response;
29
+ } | {
30
+ kind: "continue";
31
+ headers?: Record<string, string>;
32
+ params?: Record<string, string | string[]>;
33
+ locals?: Record<string, unknown>;
34
+ };
35
+ /**
36
+ * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware
37
+ * file exists. Distinguishes "file not found" from "file has errors" (§6):
38
+ * an import error is not silently treated as "no middleware".
39
+ */
40
+ export declare function loadMiddleware(root: string): Promise<LoadedMiddleware | null>;
41
+ /**
42
+ * Checks if a pathname matches any of the middleware's matcher patterns.
43
+ * If no matcher is configured, the middleware runs for every request.
44
+ *
45
+ * Catch-all patterns (`:param*`) match both the base path and any sub-paths,
46
+ * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.
47
+ */
48
+ export declare function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean;
49
+ /**
50
+ * Runs the middleware for a request. Returns the result indicating whether to
51
+ * short-circuit with a response or continue with propagated headers/params/locals.
52
+ *
53
+ * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,
54
+ * headers/params/locals are propagated to downstream handlers.
55
+ */
56
+ export declare function runMiddleware(middleware: LoadedMiddleware, request: Request, params?: Record<string, string | string[]>): Promise<MiddlewareResult>;
@@ -0,0 +1,56 @@
1
+ /** The middleware function signature. */
2
+ export type Middleware = (request: Request, context: MiddlewareContext) => Response | void | Promise<Response | void>;
3
+ /** Context passed to the middleware function. */
4
+ export interface MiddlewareContext {
5
+ /** Helper to continue to the next handler. Can attach headers, params, and locals. */
6
+ next(options?: {
7
+ headers?: Record<string, string>;
8
+ params?: Record<string, string | string[]>;
9
+ locals?: Record<string, unknown>;
10
+ }): void;
11
+ /** Matched route params (only available if the path matches a page route). */
12
+ params?: Record<string, string | string[]>;
13
+ /** Per-request locals (populated by middleware, available to loaders/actions). */
14
+ locals?: Record<string, unknown>;
15
+ }
16
+ /** Configuration for the middleware module. */
17
+ export interface MiddlewareConfig {
18
+ /** Path patterns that trigger the middleware. Supports `:param` and `:param*`. */
19
+ matcher?: string[];
20
+ }
21
+ export interface LoadedMiddleware {
22
+ handler: Middleware;
23
+ config: MiddlewareConfig;
24
+ }
25
+ /** Result of running middleware: either a response to short-circuit with, or continue. */
26
+ export type MiddlewareResult = {
27
+ kind: "response";
28
+ response: Response;
29
+ } | {
30
+ kind: "continue";
31
+ headers?: Record<string, string>;
32
+ params?: Record<string, string | string[]>;
33
+ locals?: Record<string, unknown>;
34
+ };
35
+ /**
36
+ * Loads the user's `src/middleware.ts` module. Returns `null` if no middleware
37
+ * file exists. Distinguishes "file not found" from "file has errors" (§6):
38
+ * an import error is not silently treated as "no middleware".
39
+ */
40
+ export declare function loadMiddleware(root: string): Promise<LoadedMiddleware | null>;
41
+ /**
42
+ * Checks if a pathname matches any of the middleware's matcher patterns.
43
+ * If no matcher is configured, the middleware runs for every request.
44
+ *
45
+ * Catch-all patterns (`:param*`) match both the base path and any sub-paths,
46
+ * e.g. `/dashboard/:path*` matches `/dashboard` and `/dashboard/settings/users`.
47
+ */
48
+ export declare function matchesMiddleware(pathname: string, config: MiddlewareConfig): boolean;
49
+ /**
50
+ * Runs the middleware for a request. Returns the result indicating whether to
51
+ * short-circuit with a response or continue with propagated headers/params/locals.
52
+ *
53
+ * Per §6: cleanup runs in `finally`, response short-circuits the pipeline,
54
+ * headers/params/locals are propagated to downstream handlers.
55
+ */
56
+ export declare function runMiddleware(middleware: LoadedMiddleware, request: Request, params?: Record<string, string | string[]>): Promise<MiddlewareResult>;
@@ -0,0 +1,53 @@
1
+ import type { ElurTemplate } from "@elurjs/core";
2
+ export interface StreamBoundaryOptions<T> {
3
+ /** Fallback content shown while the promise resolves. */
4
+ fallback: ElurTemplate;
5
+ /** Promise that resolves to a ElurTemplate. */
6
+ promise: Promise<T>;
7
+ /** Renders the resolved value to a ElurTemplate. */
8
+ children: (value: T) => ElurTemplate;
9
+ }
10
+ /** Per-request boundary registry. */
11
+ interface BoundaryContext {
12
+ boundaries: Map<string, {
13
+ promise: Promise<unknown>;
14
+ children: (value: unknown) => ElurTemplate;
15
+ }>;
16
+ }
17
+ /**
18
+ * Gets the current per-request boundary context, if any.
19
+ * Used by the streaming response to collect boundaries for later resolution.
20
+ */
21
+ export declare function getCurrentBoundaryContext(): BoundaryContext | undefined;
22
+ /**
23
+ * Runs a function within a per-request boundary context.
24
+ * Used by the SSR streaming pipeline to collect boundaries.
25
+ */
26
+ export declare function withBoundaryContext<T>(fn: () => T): T;
27
+ /**
28
+ * Builds the fallback HTML wrapper for a boundary ID.
29
+ * The fallback content is wrapped in a `<div>` with the boundary ID so the
30
+ * browser can find it and replace it when the resolved content arrives.
31
+ *
32
+ * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)
33
+ */
34
+ export declare function buildFallbackHtml(boundaryId: string, fallbackHtml: string): string;
35
+ /**
36
+ * Builds the resolved content chunk for a boundary ID.
37
+ * Emits a `<template>` element with the resolved content, followed by a
38
+ * `<script>` that replaces the fallback div with the template content
39
+ * in-place. This is real Suspense streaming — the browser swaps the DOM
40
+ * node without a full re-render.
41
+ *
42
+ * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)
43
+ */
44
+ export declare function buildResolvedChunk(boundaryId: string, resolvedHtml: string): string;
45
+ /**
46
+ * Creates a stream boundary. During SSR, emits the fallback and registers the
47
+ * promise for later resolution by the streaming pipeline. During SSG, the
48
+ * build awaits all boundaries before writing HTML.
49
+ *
50
+ * The boundary ID is deterministic per-request via crypto.randomUUID().
51
+ */
52
+ export declare function streamBoundary<T>(options: StreamBoundaryOptions<T>): ElurTemplate;
53
+ export {};
@@ -0,0 +1,53 @@
1
+ import type { ElurTemplate } from "@elurjs/core";
2
+ export interface StreamBoundaryOptions<T> {
3
+ /** Fallback content shown while the promise resolves. */
4
+ fallback: ElurTemplate;
5
+ /** Promise that resolves to a ElurTemplate. */
6
+ promise: Promise<T>;
7
+ /** Renders the resolved value to a ElurTemplate. */
8
+ children: (value: T) => ElurTemplate;
9
+ }
10
+ /** Per-request boundary registry. */
11
+ interface BoundaryContext {
12
+ boundaries: Map<string, {
13
+ promise: Promise<unknown>;
14
+ children: (value: unknown) => ElurTemplate;
15
+ }>;
16
+ }
17
+ /**
18
+ * Gets the current per-request boundary context, if any.
19
+ * Used by the streaming response to collect boundaries for later resolution.
20
+ */
21
+ export declare function getCurrentBoundaryContext(): BoundaryContext | undefined;
22
+ /**
23
+ * Runs a function within a per-request boundary context.
24
+ * Used by the SSR streaming pipeline to collect boundaries.
25
+ */
26
+ export declare function withBoundaryContext<T>(fn: () => T): T;
27
+ /**
28
+ * Builds the fallback HTML wrapper for a boundary ID.
29
+ * The fallback content is wrapped in a `<div>` with the boundary ID so the
30
+ * browser can find it and replace it when the resolved content arrives.
31
+ *
32
+ * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)
33
+ */
34
+ export declare function buildFallbackHtml(boundaryId: string, fallbackHtml: string): string;
35
+ /**
36
+ * Builds the resolved content chunk for a boundary ID.
37
+ * Emits a `<template>` element with the resolved content, followed by a
38
+ * `<script>` that replaces the fallback div with the template content
39
+ * in-place. This is real Suspense streaming — the browser swaps the DOM
40
+ * node without a full re-render.
41
+ *
42
+ * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)
43
+ */
44
+ export declare function buildResolvedChunk(boundaryId: string, resolvedHtml: string): string;
45
+ /**
46
+ * Creates a stream boundary. During SSR, emits the fallback and registers the
47
+ * promise for later resolution by the streaming pipeline. During SSG, the
48
+ * build awaits all boundaries before writing HTML.
49
+ *
50
+ * The boundary ID is deterministic per-request via crypto.randomUUID().
51
+ */
52
+ export declare function streamBoundary<T>(options: StreamBoundaryOptions<T>): ElurTemplate;
53
+ export {};