@useavalon/avalon 0.1.46 → 0.1.48

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 (73) hide show
  1. package/README.md +54 -54
  2. package/dist/mod.d.ts +8 -9
  3. package/dist/mod.js +1 -1
  4. package/dist/src/client/adapters/lit-adapter.js +1 -0
  5. package/dist/src/client/adapters/preact-adapter.js +1 -0
  6. package/dist/src/client/adapters/qwik-adapter.js +1 -0
  7. package/dist/src/client/adapters/react-adapter.js +1 -0
  8. package/dist/src/client/adapters/solid-adapter.js +1 -0
  9. package/dist/src/client/adapters/svelte-adapter.js +1 -0
  10. package/dist/src/client/adapters/vue-adapter.js +1 -0
  11. package/dist/src/client/components.d.ts +8 -16
  12. package/dist/src/client/components.js +1 -1
  13. package/dist/src/client/custom-directives.d.ts +25 -0
  14. package/dist/src/client/custom-directives.js +1 -0
  15. package/dist/src/client/main.js +3 -3
  16. package/dist/src/client/types/framework-runtime.d.ts +68 -68
  17. package/dist/src/client/types/vite-hmr.d.ts +46 -46
  18. package/dist/src/client/types/vite-virtual-modules.d.ts +70 -70
  19. package/dist/src/components/IslandErrorBoundary.d.ts +48 -9
  20. package/dist/src/components/IslandErrorBoundary.js +1 -1
  21. package/dist/src/components/LayoutErrorBoundary.d.ts +74 -9
  22. package/dist/src/components/LayoutErrorBoundary.js +1 -1
  23. package/dist/src/islands/builtin-directives.d.ts +15 -0
  24. package/dist/src/islands/builtin-directives.js +1 -0
  25. package/dist/src/islands/hydration-directives.d.ts +89 -0
  26. package/dist/src/islands/hydration-directives.js +1 -0
  27. package/dist/src/islands/island.d.ts +5 -3
  28. package/dist/src/islands/island.js +1 -1
  29. package/dist/src/islands/types.d.ts +4 -2
  30. package/dist/src/layout-system.d.ts +0 -13
  31. package/dist/src/layout-system.js +1 -1
  32. package/dist/src/nitro/config.d.ts +41 -2
  33. package/dist/src/nitro/config.js +1 -1
  34. package/dist/src/nitro/renderer.d.ts +14 -1
  35. package/dist/src/nitro/renderer.js +6 -6
  36. package/dist/src/persistence/island-state-serializer.d.ts +19 -0
  37. package/dist/src/persistence/island-state-serializer.js +1 -0
  38. package/dist/src/persistence/use-persistent-state.d.ts +31 -0
  39. package/dist/src/persistence/use-persistent-state.js +1 -0
  40. package/dist/src/prerender/index.d.ts +53 -0
  41. package/dist/src/prerender/index.js +1 -0
  42. package/dist/src/prerender/prerender.d.ts +20 -0
  43. package/dist/src/prerender/prerender.js +1 -0
  44. package/dist/src/schemas/layout.d.ts +1 -1
  45. package/dist/src/schemas/layout.js +1 -1
  46. package/dist/src/types/image.d.ts +106 -106
  47. package/dist/src/types/index.d.ts +22 -22
  48. package/dist/src/types/island-jsx.d.ts +33 -33
  49. package/dist/src/types/island-prop.d.ts +22 -20
  50. package/dist/src/types/layout.d.ts +0 -8
  51. package/dist/src/types/layout.js +1 -1
  52. package/dist/src/types/mdx.d.ts +6 -6
  53. package/dist/src/types/urlpattern.d.ts +49 -49
  54. package/dist/src/types/vite-env.d.ts +11 -11
  55. package/dist/src/vite-plugin/nitro-integration.d.ts +3 -3
  56. package/dist/src/vite-plugin/nitro-integration.js +14 -14
  57. package/package.json +7 -3
  58. package/dist/src/components/LayoutDataErrorBoundary.d.ts +0 -34
  59. package/dist/src/components/LayoutDataErrorBoundary.js +0 -1
  60. package/dist/src/components/PersistentIsland.d.ts +0 -36
  61. package/dist/src/components/PersistentIsland.js +0 -1
  62. package/dist/src/components/StreamingErrorBoundary.d.ts +0 -42
  63. package/dist/src/components/StreamingErrorBoundary.js +0 -1
  64. package/dist/src/components/StreamingLayout.d.ts +0 -83
  65. package/dist/src/components/StreamingLayout.js +0 -29
  66. package/dist/src/core/islands/island-persistence.d.ts +0 -74
  67. package/dist/src/core/islands/island-persistence.js +0 -1
  68. package/dist/src/core/islands/island-state-serializer.d.ts +0 -53
  69. package/dist/src/core/islands/island-state-serializer.js +0 -1
  70. package/dist/src/core/islands/persistent-island-context.d.ts +0 -36
  71. package/dist/src/core/islands/persistent-island-context.js +0 -1
  72. package/dist/src/core/islands/use-persistent-state.d.ts +0 -17
  73. package/dist/src/core/islands/use-persistent-state.js +0 -1
@@ -19,7 +19,7 @@
19
19
  * - `/*.html`, `/*.css`: Short cache with revalidation
20
20
  * - `/favicon.ico`: Medium cache (1 day)
21
21
  */
22
- import type { ResolvedAvalonConfig } from '../vite-plugin/types.ts';
22
+ import type { ResolvedAvalonConfig } from "../vite-plugin/types.ts";
23
23
  /**
24
24
  * Cache configuration options for route rules
25
25
  */
@@ -140,6 +140,43 @@ export interface AvalonNitroConfig {
140
140
  brotli?: boolean;
141
141
  zstd?: boolean;
142
142
  };
143
+ /**
144
+ * Prerender configuration for static site generation (SSG).
145
+ *
146
+ * Nitro will fetch each listed route at build time using the SSR handler
147
+ * and write the resulting HTML to the public output directory. These pages
148
+ * are then served as static files from the CDN — zero function invocations.
149
+ *
150
+ * Islands on prerendered pages still hydrate normally on the client.
151
+ *
152
+ * @example
153
+ * ```ts
154
+ * prerender: {
155
+ * routes: ['/', '/docs', '/blog'],
156
+ * crawlLinks: true,
157
+ * }
158
+ * ```
159
+ */
160
+ prerender?: {
161
+ /** Explicit routes to prerender at build time */
162
+ routes?: string[];
163
+ /** Crawl `<a>` links in prerendered pages to discover more routes */
164
+ crawlLinks?: boolean;
165
+ /** Number of concurrent prerender requests */
166
+ concurrency?: number;
167
+ /** Delay in ms between prerender requests */
168
+ interval?: number;
169
+ /** Fail the build if a prerender route errors */
170
+ failOnError?: boolean;
171
+ /** Routes to ignore (strings, RegExps, or filter functions) */
172
+ ignore?: Array<string | RegExp | ((path: string) => undefined | null | boolean)>;
173
+ /** Number of retry attempts per route @default 3 */
174
+ retry?: number;
175
+ /** Delay between retries in ms @default 500 */
176
+ retryDelay?: number;
177
+ /** Write `/about` as `/about/index.html` @default true */
178
+ autoSubfolderIndex?: boolean;
179
+ };
143
180
  }
144
181
  /**
145
182
  * Nitro configuration output structure
@@ -191,6 +228,8 @@ export interface NitroConfigOutput {
191
228
  }>;
192
229
  /** Static assets configuration */
193
230
  staticAssets?: StaticAssetsConfig;
231
+ /** Prerender configuration for SSG */
232
+ prerender?: AvalonNitroConfig["prerender"];
194
233
  }
195
234
  /**
196
235
  * Avalon-specific runtime configuration stored in Nitro's runtimeConfig
@@ -225,7 +264,7 @@ export declare function resolvePresetName(preset: string): string;
225
264
  /**
226
265
  * Default Nitro configuration values
227
266
  */
228
- export declare const DEFAULT_NITRO_CONFIG: Required<Pick<AvalonNitroConfig, 'preset' | 'serverDir' | 'streaming'>>;
267
+ export declare const DEFAULT_NITRO_CONFIG: Required<Pick<AvalonNitroConfig, "preset" | "serverDir" | "streaming">>;
229
268
  /**
230
269
  * Creates a Nitro configuration from Avalon plugin config and Nitro-specific options
231
270
  *
@@ -1 +1 @@
1
- export const DEFAULT_STATIC_ASSETS_CONFIG={publicDir:`public`,buildDir:`dist`,compression:!0,cacheControl:`public, max-age=31536000, immutable`,mutableCacheControl:`public, max-age=0, must-revalidate`,headers:{}};export const VALID_V3_PRESETS=[`node_server`,`node_middleware`,`vercel`,`cloudflare_module`,`cloudflare_pages`,`deno_deploy`,`deno_server`,`netlify`,`netlify_functions`,`netlify_edge`,`aws_lambda`,`azure_swa`,`firebase_functions`,`render_com`,`static`,`browser`];export function resolvePresetName(e){if(VALID_V3_PRESETS.includes(e))return e;throw Error(`Unknown Nitro preset: "${e}". Valid presets: ${VALID_V3_PRESETS.join(`, `)}`)}export const DEFAULT_NITRO_CONFIG={preset:`node_server`,serverDir:`server`,streaming:!0};export function createNitroConfig(t,i){let a=t.pagesDir??i.pagesDir,o=i.layoutsDir,s={streaming:t.streaming??DEFAULT_NITRO_CONFIG.streaming,pagesDir:a,layoutsDir:o};if(t.runtimeConfig&&`nitro`in t.runtimeConfig)throw Error(`The "nitro" key in runtimeConfig is reserved by Nitro v3 and cannot be used.`);let c={avalon:s,...t.runtimeConfig},l={...DEFAULT_STATIC_ASSETS_CONFIG,...t.staticAssets},u=mergeRouteRules(createDefaultStaticAssetRouteRules(l),t.routeRules??{}),d=[{dir:l.publicDir??DEFAULT_STATIC_ASSETS_CONFIG.publicDir,baseURL:`/`,maxAge:0}],f=t.renderer===!1?!1:t.renderer?{...t.renderer}:void 0;return{preset:resolvePresetName(process.env.NITRO_PRESET??t.preset??DEFAULT_NITRO_CONFIG.preset),serverDir:t.serverDir??DEFAULT_NITRO_CONFIG.serverDir,routeRules:u,runtimeConfig:c,publicRuntimeConfig:t.publicRuntimeConfig,renderer:f,compatibilityDate:t.compatibilityDate,traceDeps:t.traceDeps,rolldownConfig:t.rolldownConfig,serverEntry:t.serverEntry,compressPublicAssets:t.compressPublicAssets,publicAssets:d,staticAssets:l}}export function createDefaultStaticAssetRouteRules(t){let n=t.cacheControl??DEFAULT_STATIC_ASSETS_CONFIG.cacheControl,r=t.mutableCacheControl??DEFAULT_STATIC_ASSETS_CONFIG.mutableCacheControl;return{"/assets/**":{headers:{"Cache-Control":n,...t.headers}},"/islands/**":{headers:{"Cache-Control":n,...t.headers}},"/chunks/**":{headers:{"Cache-Control":n,...t.headers}},"/_nuxt/**":{headers:{"Cache-Control":n,...t.headers}},"/**/*.woff":{headers:{"Cache-Control":n,...t.headers}},"/**/*.woff2":{headers:{"Cache-Control":n,...t.headers}},"/**/*.html":{headers:{"Cache-Control":r,...t.headers}},"/favicon.ico":{headers:{"Cache-Control":`public, max-age=86400`,...t.headers}},"/**/*.css":{headers:{"Cache-Control":r,...t.headers}}}}export function isValidPreset(e){return VALID_V3_PRESETS.includes(e)}export function mergeRouteRules(e,t){let n={...e};for(let[e,r]of Object.entries(t))n[e]?n[e]={...n[e],...r,headers:{...n[e].headers,...r.headers}}:n[e]=r;return n}
1
+ export const DEFAULT_STATIC_ASSETS_CONFIG={publicDir:`public`,buildDir:`dist`,compression:!0,cacheControl:`public, max-age=31536000, immutable`,mutableCacheControl:`public, max-age=0, must-revalidate`,headers:{}};export const VALID_V3_PRESETS=[`node_server`,`node_middleware`,`vercel`,`cloudflare_module`,`cloudflare_pages`,`deno_deploy`,`deno_server`,`netlify`,`netlify_functions`,`netlify_edge`,`aws_lambda`,`azure_swa`,`firebase_functions`,`render_com`,`static`,`browser`];export function resolvePresetName(e){if(VALID_V3_PRESETS.includes(e))return e;throw Error(`Unknown Nitro preset: "${e}". Valid presets: ${VALID_V3_PRESETS.join(`, `)}`)}export const DEFAULT_NITRO_CONFIG={preset:`node_server`,serverDir:`server`,streaming:!0};export function createNitroConfig(t,i){let a=t.pagesDir??i.pagesDir,o=i.layoutsDir,s={streaming:t.streaming??DEFAULT_NITRO_CONFIG.streaming,pagesDir:a,layoutsDir:o};if(t.runtimeConfig&&`nitro`in t.runtimeConfig)throw Error(`The "nitro" key in runtimeConfig is reserved by Nitro v3 and cannot be used.`);let c={avalon:s,...t.runtimeConfig},l={...DEFAULT_STATIC_ASSETS_CONFIG,...t.staticAssets},u=mergeRouteRules(createDefaultStaticAssetRouteRules(l),t.routeRules??{}),d=[{dir:l.publicDir??DEFAULT_STATIC_ASSETS_CONFIG.publicDir,baseURL:`/`,maxAge:0}],f=t.renderer===!1?!1:t.renderer?{...t.renderer}:void 0;return{preset:resolvePresetName(process.env.NITRO_PRESET??t.preset??DEFAULT_NITRO_CONFIG.preset),serverDir:t.serverDir??DEFAULT_NITRO_CONFIG.serverDir,routeRules:u,runtimeConfig:c,publicRuntimeConfig:t.publicRuntimeConfig,renderer:f,compatibilityDate:t.compatibilityDate,traceDeps:t.traceDeps,rolldownConfig:t.rolldownConfig,serverEntry:t.serverEntry,compressPublicAssets:t.compressPublicAssets,publicAssets:d,staticAssets:l,prerender:t.prerender}}export function createDefaultStaticAssetRouteRules(t){let n=t.cacheControl??DEFAULT_STATIC_ASSETS_CONFIG.cacheControl,r=t.mutableCacheControl??DEFAULT_STATIC_ASSETS_CONFIG.mutableCacheControl;return{"/assets/**":{headers:{"Cache-Control":n,...t.headers}},"/islands/**":{headers:{"Cache-Control":n,...t.headers}},"/chunks/**":{headers:{"Cache-Control":n,...t.headers}},"/_nuxt/**":{headers:{"Cache-Control":n,...t.headers}},"/**/*.woff":{headers:{"Cache-Control":n,...t.headers}},"/**/*.woff2":{headers:{"Cache-Control":n,...t.headers}},"/**/*.html":{headers:{"Cache-Control":r,...t.headers}},"/favicon.ico":{headers:{"Cache-Control":`public, max-age=86400`,...t.headers}},"/**/*.css":{headers:{"Cache-Control":r,...t.headers}}}}export function isValidPreset(e){return VALID_V3_PRESETS.includes(e)}export function mergeRouteRules(e,t){let n={...e};for(let[e,r]of Object.entries(t))n[e]?n[e]={...n[e],...r,headers:{...n[e].headers,...r.headers}}:n[e]=r;return n}
@@ -68,6 +68,17 @@ export interface RenderHandlerOptions {
68
68
  loadPageModule?: (filePath: string) => Promise<PageModule>;
69
69
  /** Custom layout resolver */
70
70
  resolveLayouts?: (routePath: string, config: AvalonRuntimeConfig) => Promise<string[]>;
71
+ /**
72
+ * Wrap rendered page HTML with layout components.
73
+ *
74
+ * Called after the page component is rendered to HTML. The function
75
+ * receives the page HTML, page module, and render context, and should
76
+ * return the full HTML document string (including `<!DOCTYPE html>`).
77
+ *
78
+ * When provided, the renderer skips its default HTML shell generation
79
+ * and uses the returned string directly.
80
+ */
81
+ wrapWithLayouts?: (pageHtml: string, pageModule: PageModule, context: NitroRenderContext) => Promise<string> | string;
71
82
  /**
72
83
  * Enable custom error pages (404.tsx, 500.tsx, _error.tsx)
73
84
  * When enabled, the renderer will look for custom error pages in the pages directory
@@ -197,7 +208,7 @@ export declare function processHydrationRequirements(html: string, isDev: boolea
197
208
  * @param options - Render options
198
209
  * @returns SSR render result
199
210
  */
200
- export declare function renderPage(pageModule: PageModule, context: NitroRenderContext, options?: SSRRenderOptions): Promise<SSRRenderResult>;
211
+ export declare function renderPage(pageModule: PageModule, context: NitroRenderContext, options?: SSRRenderOptions, wrapWithLayouts?: RenderHandlerOptions['wrapWithLayouts']): Promise<SSRRenderResult>;
201
212
  /**
202
213
  * Extended streaming options with additional callbacks
203
214
  */
@@ -280,6 +291,8 @@ export interface NitroCatchAllOptions {
280
291
  loadPageModule: (filePath: string) => Promise<PageModule>;
281
292
  /** Optional layout resolver */
282
293
  resolveLayouts?: (routePath: string, config: AvalonRuntimeConfig) => Promise<string[]>;
294
+ /** Wrap rendered page HTML with layout components (same as RenderHandlerOptions) */
295
+ wrapWithLayouts?: RenderHandlerOptions['wrapWithLayouts'];
283
296
  /**
284
297
  * Enable custom error pages (404.tsx, 500.tsx, _error.tsx)
285
298
  * When enabled, the renderer will look for custom error pages in the pages directory
@@ -118,19 +118,19 @@ import{getRequestURL as e}from"h3";import{createNotFoundError as t,isHttpError a
118
118
  </div>
119
119
  </body>
120
120
  </html>`}function _(e){return{400:`Bad Request`,401:`Unauthorized`,403:`Forbidden`,404:`Page Not Found`,405:`Method Not Allowed`,500:`Internal Server Error`,502:`Bad Gateway`,503:`Service Unavailable`}[e]||`Error`}function v(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`"`,`&quot;`).replaceAll(`'`,`&#039;`)}export function extractIslandMarkers(e){let t=[],n=/<[^>]*data-framework="([^"]+)"[^>]*>/g,r;for(;(r=n.exec(e))!==null;){let e=r[0],n=r[1],i=/data-src="([^"]+)"/.exec(e),a=i?i[1]:``,o=/data-props="([^"]*)"/.exec(e),s=o?o[1]:void 0,c=/data-hydrate="([^"]+)"/.exec(e),l=c?c[1]:void 0;t.push({framework:n,src:a,props:s,hydrate:l})}return t}export function ensureHydrationMarkers(e,t){let n=e;return t.framework&&!n.includes(`data-framework=`)&&(n=n.replace(/>/,` data-framework="${t.framework}">`)),t.src&&!n.includes(`data-src=`)&&(n=n.replace(/>/,` data-src="${t.src}">`)),t.props!==void 0&&!n.includes(`data-props=`)&&(n=n.replace(/>/,` data-props="${t.props}">`)),n}export function injectHydrationScript(e,t,n={}){if(!(e.includes(`data-framework=`)||e.includes(`data-src=`))&&!n.forceInject||[`/src/client/main.js`,`/dist/client.js`,`client/main.js`].some(t=>e.includes(t)))return e;let r=n.scriptPath||(t?`/src/client/main.js`:`/dist/client.js`),i=[];i.push(`<script type="module" src="${r}"><\/script>`),n.additionalScripts&&i.push(...n.additionalScripts);let a=i.join(`
121
- `);return e.includes(`</body>`)?e.replace(`</body>`,`${a}\n</body>`):e+a}export function validateHydrationMarkers(e){let t=extractIslandMarkers(e),n=e.match(/data-framework="[^"]+"/g)||[],r=e.match(/data-src="[^"]+"/g)||[],i=e.match(/data-props="[^"]*"/g)||[],a=e.includes(`/src/client/main.js`)||e.includes(`/dist/client.js`)||e.includes(`client/main.js`),o=t.every(e=>e.framework&&e.src),s=t.length===0||o&&a;return{hasFrameworkAttr:n.length>0,hasSrcAttr:r.length>0,hasPropsAttr:i.length>0,islandCount:n.length,islands:t,hasClientScript:a,isValid:s}}export function processHydrationRequirements(e,t){let n=injectHydrationScript(e,t);return{html:n,validation:validateHydrationMarkers(n)}}export async function renderPage(e,t,n={}){try{let r={};return e.getServerSideProps&&(r=await e.getServerSideProps(t)),{html:await y(e,r,t,n),statusCode:200,headers:{"Content-Type":`text/html; charset=utf-8`}}}catch(e){throw console.error(`[SSR Error]`,e),n.onError&&e instanceof Error&&n.onError(e),e}}async function y(e,t,n,r){let i=e.default,a=e.metadata||{},o=process.env.NODE_ENV!==`production`,l;try{let e=i(t);l=e instanceof Promise?await e:e}catch(e){console.error(`[renderer] Error calling page component:`,e),l=s(`div`,null,`Error rendering page`)}let u;try{u=c(l)}catch(e){console.error(`[renderer] Error in preactRenderToString:`,e),u=`<div>Error rendering page</div>`}let d=o?`
121
+ `);return e.includes(`</body>`)?e.replace(`</body>`,`${a}\n</body>`):e+a}export function validateHydrationMarkers(e){let t=extractIslandMarkers(e),n=e.match(/data-framework="[^"]+"/g)||[],r=e.match(/data-src="[^"]+"/g)||[],i=e.match(/data-props="[^"]*"/g)||[],a=e.includes(`/src/client/main.js`)||e.includes(`/dist/client.js`)||e.includes(`client/main.js`),o=t.every(e=>e.framework&&e.src),s=t.length===0||o&&a;return{hasFrameworkAttr:n.length>0,hasSrcAttr:r.length>0,hasPropsAttr:i.length>0,islandCount:n.length,islands:t,hasClientScript:a,isValid:s}}export function processHydrationRequirements(e,t){let n=injectHydrationScript(e,t);return{html:n,validation:validateHydrationMarkers(n)}}export async function renderPage(e,t,n={},r){try{let i={};return e.getServerSideProps&&(i=await e.getServerSideProps(t)),{html:await y(e,i,t,n,r),statusCode:200,headers:{"Content-Type":`text/html; charset=utf-8`}}}catch(e){throw console.error(`[SSR Error]`,e),n.onError&&e instanceof Error&&n.onError(e),e}}async function y(e,t,n,r,i){let a=e.default,o=e.metadata||{},l=process.env.NODE_ENV!==`production`,u;try{let e=a(t);u=e instanceof Promise?await e:e}catch(e){console.error(`[renderer] Error calling page component:`,e),u=s(`div`,null,`Error rendering page`)}let d;try{d=c(u)}catch(e){console.error(`[renderer] Error in preactRenderToString:`,e),d=`<div>Error rendering page</div>`}if(i)return await i(d,e,n);let f=l?`
122
122
  <script type="module" src="/src/client/main.js"><\/script>`:``;return`<!DOCTYPE html>
123
123
  <html lang="en">
124
124
  <head>
125
125
  <meta charset="utf-8">
126
126
  <meta name="viewport" content="width=device-width, initial-scale=1">
127
- <title>${v(String(a.title||`Avalon App`))}</title>
128
- ${a.description?`<meta name="description" content="${v(String(a.description))}">`:``}
127
+ <title>${v(String(o.title||`Avalon App`))}</title>
128
+ ${o.description?`<meta name="description" content="${v(String(o.description))}">`:``}
129
129
  </head>
130
130
  <body>
131
131
  <div id="app">
132
- ${u}
133
- </div>${d}
132
+ ${d}
133
+ </div>${f}
134
134
  </body>
135
135
  </html>`}export async function renderPageStream(e,t,n={}){let r=new TextEncoder,i=null,a={shellSent:!1,contentSent:!1,closed:!1,error:null},o=n.shellReadyTimeout,s=n.allReadyTimeout,l=null,u=null,d=()=>{l&&=(clearTimeout(l),null),u&&=(clearTimeout(u),null)};function f(e,t,n,r,i,a,o){if(n.error=e,a(),console.error(`[Streaming Error]`,{message:e.message,stack:e.stack,shellSent:n.shellSent,isShellError:t,timestamp:new Date().toISOString()}),t&&o.onShellError&&o.onShellError(e),o.onError&&o.onError(e),!n.closed&&r){if(n.shellSent){let t=C(e);r.enqueue(i.encode(t));let n=S();r.enqueue(i.encode(n))}else{let t=h(e,500);r.enqueue(i.encode(t))}n.closed=!0,r.close()}}async function p(o){i=o;let p={};e.getServerSideProps&&(p=await e.getServerSideProps(t));let m=b(e.metadata||{},t);if(a.closed||(o.enqueue(r.encode(m)),a.shellSent=!0,l&&=(clearTimeout(l),null),n.onShellReady&&n.onShellReady()),s&&s>0&&(u=setTimeout(()=>{!a.contentSent&&!a.closed&&f(Error(`All ready timeout after ${s}ms`),!1,a,i,r,d,n)},s)),!a.closed){let t=e.default;if(t&&typeof t==`function`){let n=t(p);if(n&&typeof n.then==`function`)try{let e=c(await n);o.enqueue(r.encode(` <div id="app">${e}</div>\n`))}catch(t){console.error(`[streaming] Async component error:`,t);let n=x(e,p);o.enqueue(r.encode(n))}else{let t=x(e,p);o.enqueue(r.encode(t))}}else{let t=x(e,p);o.enqueue(r.encode(t))}a.contentSent=!0}if(!a.closed){let e=S();o.enqueue(r.encode(e))}d(),n.onAllReady&&!a.closed&&n.onAllReady(),a.closed||(a.closed=!0,o.close())}return new ReadableStream({async start(e){i=e,o&&o>0&&(l=setTimeout(()=>{!a.shellSent&&!a.closed&&f(Error(`Shell ready timeout after ${o}ms`),!0,a,i,r,d,n)},o));try{await p(e)}catch(e){f(e instanceof Error?e:Error(String(e)),!a.shellSent,a,i,r,d,n)}},cancel(){if(d(),!a.closed&&i){a.closed=!0;try{i.close()}catch{}}}})}function b(e,t){return`<!DOCTYPE html>
136
136
  <html lang="en">
@@ -181,4 +181,4 @@ import{getRequestURL as e}from"h3";import{createNotFoundError as t,isHttpError a
181
181
  </details>
182
182
  `:``}
183
183
  </div>
184
- `}export function createStreamingResponse(e,t={}){let n=new Headers({"Content-Type":`text/html; charset=utf-8`,"Transfer-Encoding":`chunked`,...t.headers});return new Response(e,{status:t.status||200,headers:n})}function w(e,t,n){return async()=>(e.value??=await r({baseDir:t,devMode:n}),e.value)}function T(e,t,n){return async(r,i)=>e?a(r,i,t):createErrorResponse(r,n)}export function createNitroRenderer(e){let{avalonConfig:n,isDev:r=!1,enableCustomErrorPages:a=!0}=e,s={isDev:r,avalonConfig:n,loadPageModule:e.loadPageModule,pagesDir:n.pagesDir};a&&o(s).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let c=w({value:null},n.srcDir||`src`,r),d=T(a,s,r);return async function(a){let o=getRequestURL(a).pathname;try{let s=await i(a,await c(),{devMode:r});if(s)return r&&console.log(`[renderer] Middleware terminated request for ${o}`),s;let u=a.context.route,f=null;if(f=u||(e.resolvePageRoute?await e.resolvePageRoute(o,n.pagesDir):await E(o,n.pagesDir)),!f)return d(t(`Page not found: ${o}`),a);let m=e.loadPageModule?await e.loadPageModule(f.filePath):await D(f.filePath),h=createRenderContext(a,a.context.params||f.params);if(e.resolveLayouts&&(h.layoutContext={layouts:await e.resolveLayouts(o,n)}),n.streaming){let e=await renderPageStream(m,h,{onShellReady:()=>{setResponseHeader(a,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let e=await renderPage(m,h),t=injectHydrationScript(e.html,r);return new Response(t,{status:e.statusCode,headers:e.headers})}}catch(e){return console.error(`[Nitro Renderer Error]`,e),d(e instanceof Error?e:Error(String(e)),a)}}}async function E(e,t){return e===`/`||e===``?{filePath:`src/pages/index.tsx`,pattern:`/`,params:{}}:{filePath:`src/pages/${e.replace(/^\//,``).replace(/\/$/,``)}.tsx`,pattern:e,params:{}}}async function D(e){return{default:()=>null,metadata:{title:`Avalon Page`}}}export function createNitroCatchAllRenderer(e){let{avalonConfig:n,isDev:r=!1,loadPageModule:a,resolveLayouts:s,enableCustomErrorPages:c=!0}=e,d={isDev:r,avalonConfig:n,loadPageModule:a,pagesDir:n.pagesDir};c&&o(d).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let f=w({value:null},n.srcDir||`src`,r),m=T(c,d,r);return async function(e){let o=getRequestURL(e).pathname;try{let c=await i(e,await f(),{devMode:r});if(c)return r&&console.log(`[renderer] Middleware terminated request for ${o}`),c;let u=e.context.params||{},d=u.slug||o.replace(/^\//,``)||`index`,h=`${n.pagesDir}/${d}.tsx`,g;try{g=await a(h)}catch(i){try{g=await a(`${n.pagesDir}/${d}/index.tsx`)}catch(a){return r&&(console.debug(`[renderer] Page not found: ${h}`,i),console.debug(`[renderer] Index fallback not found: ${n.pagesDir}/${d}/index.tsx`,a)),m(t(`Page not found: ${o}`),e)}}let _=createRenderContext(e,u);if(s&&(_.layoutContext={layouts:await s(o,n)}),n.streaming){let t=await renderPageStream(g,_,{onShellReady:()=>{setResponseHeader(e,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(t,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let e=await renderPage(g,_),t=injectHydrationScript(e.html,r);return new Response(t,{status:e.statusCode,headers:e.headers})}}catch(t){return console.error(`[Nitro Catch-All Renderer Error]`,t),m(t instanceof Error?t:Error(String(t)),e)}}}export{clearMiddlewareCache as clearRendererMiddlewareCache}from"../middleware/index.js";
184
+ `}export function createStreamingResponse(e,t={}){let n=new Headers({"Content-Type":`text/html; charset=utf-8`,"Transfer-Encoding":`chunked`,...t.headers});return new Response(e,{status:t.status||200,headers:n})}function w(e,t,n){return async()=>(e.value??=await r({baseDir:t,devMode:n}),e.value)}function T(e,t,n){return async(r,i)=>e?a(r,i,t):createErrorResponse(r,n)}export function createNitroRenderer(e){let{avalonConfig:n,isDev:r=!1,enableCustomErrorPages:a=!0}=e,s={isDev:r,avalonConfig:n,loadPageModule:e.loadPageModule,pagesDir:n.pagesDir};a&&o(s).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let c=w({value:null},n.srcDir||`src`,r),d=T(a,s,r);return async function(a){let o=getRequestURL(a).pathname;try{let s=await i(a,await c(),{devMode:r});if(s)return r&&console.log(`[renderer] Middleware terminated request for ${o}`),s;let u=a.context.route,f=null;if(f=u||(e.resolvePageRoute?await e.resolvePageRoute(o,n.pagesDir):await E(o,n.pagesDir)),!f)return d(t(`Page not found: ${o}`),a);let m=e.loadPageModule?await e.loadPageModule(f.filePath):await D(f.filePath),h=createRenderContext(a,a.context.params||f.params);if(e.resolveLayouts&&(h.layoutContext={layouts:await e.resolveLayouts(o,n)}),n.streaming){let e=await renderPageStream(m,h,{onShellReady:()=>{setResponseHeader(a,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let t=await renderPage(m,h,{},e.wrapWithLayouts),n=injectHydrationScript(t.html,r);return new Response(n,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Renderer Error]`,e),d(e instanceof Error?e:Error(String(e)),a)}}}async function E(e,t){return e===`/`||e===``?{filePath:`src/pages/index.tsx`,pattern:`/`,params:{}}:{filePath:`src/pages/${e.replace(/^\//,``).replace(/\/$/,``)}.tsx`,pattern:e,params:{}}}async function D(e){return{default:()=>null,metadata:{title:`Avalon Page`}}}export function createNitroCatchAllRenderer(e){let{avalonConfig:n,isDev:r=!1,loadPageModule:a,resolveLayouts:s,enableCustomErrorPages:c=!0}=e,d={isDev:r,avalonConfig:n,loadPageModule:a,pagesDir:n.pagesDir};c&&o(d).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let f=w({value:null},n.srcDir||`src`,r),m=T(c,d,r);return async function(o){let c=getRequestURL(o).pathname;try{let u=await i(o,await f(),{devMode:r});if(u)return r&&console.log(`[renderer] Middleware terminated request for ${c}`),u;let d=o.context.params||{},h=d.slug||c.replace(/^\//,``)||`index`,g=`${n.pagesDir}/${h}.tsx`,_;try{_=await a(g)}catch(e){try{_=await a(`${n.pagesDir}/${h}/index.tsx`)}catch(i){return r&&(console.debug(`[renderer] Page not found: ${g}`,e),console.debug(`[renderer] Index fallback not found: ${n.pagesDir}/${h}/index.tsx`,i)),m(t(`Page not found: ${c}`),o)}}let v=createRenderContext(o,d);if(s&&(v.layoutContext={layouts:await s(c,n)}),n.streaming){let e=await renderPageStream(_,v,{onShellReady:()=>{setResponseHeader(o,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let t=await renderPage(_,v,{},e.wrapWithLayouts),n=injectHydrationScript(t.html,r);return new Response(n,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Catch-All Renderer Error]`,e),m(e instanceof Error?e:Error(String(e)),o)}}}export{clearMiddlewareCache as clearRendererMiddlewareCache}from"../middleware/index.js";
@@ -0,0 +1,19 @@
1
+ import type { IslandState } from '../schemas/layout.ts';
2
+ /**
3
+ * Serializes and deserializes island state for browser storage.
4
+ *
5
+ * Handles complex types that `JSON.stringify` drops: Date, RegExp, Map, and Set.
6
+ * Functions are silently stripped.
7
+ */
8
+ export declare class IslandStateSerializer {
9
+ static serialize(state: IslandState): string;
10
+ static deserialize(serializedState: string): IslandState;
11
+ private static transformForSerialization;
12
+ private static reviver;
13
+ static validate(state: IslandState): {
14
+ valid: boolean;
15
+ errors: string[];
16
+ };
17
+ static clone(state: IslandState): IslandState;
18
+ static equals(state1: IslandState, state2: IslandState): boolean;
19
+ }
@@ -0,0 +1 @@
1
+ export class IslandStateSerializer{static serialize(t){try{let n=IslandStateSerializer.transformForSerialization(t);return JSON.stringify(n)}catch(e){throw Error(`State serialization failed: ${e instanceof Error?e.message:String(e)}`)}}static deserialize(t){try{return JSON.parse(t,IslandStateSerializer.reviver)}catch(e){throw Error(`State deserialization failed: ${e instanceof Error?e.message:String(e)}`)}}static transformForSerialization(t){if(t==null)return null;if(t instanceof Date)return{__type:`Date`,__value:t.toISOString()};if(t instanceof RegExp)return{__type:`RegExp`,__value:{source:t.source,flags:t.flags}};if(t instanceof Map)return{__type:`Map`,__value:Array.from(t.entries()).map(([t,n])=>[IslandStateSerializer.transformForSerialization(t),IslandStateSerializer.transformForSerialization(n)])};if(t instanceof Set)return{__type:`Set`,__value:Array.from(t.values()).map(t=>IslandStateSerializer.transformForSerialization(t))};if(typeof t==`function`)return null;if(Array.isArray(t))return t.map(t=>IslandStateSerializer.transformForSerialization(t));if(typeof t==`object`){let n={},r=t;for(let t in r)Object.hasOwn(r,t)&&(n[t]=IslandStateSerializer.transformForSerialization(r[t]));return n}return t}static reviver(e,t){if(t&&typeof t==`object`){let e=t;if(e.__type&&e.__value!==void 0)switch(e.__type){case`Date`:return new Date(e.__value);case`RegExp`:{let t=e.__value;return new RegExp(t.source,t.flags)}case`Map`:return new Map(e.__value);case`Set`:return new Set(e.__value);default:return e.__value}}return t}static validate(e){let t=[];try{let n=this.serialize(e),r=new Blob([n]).size;r>5*1024*1024&&t.push(`Serialized state size (${Math.round(r/1024)}KB) exceeds 5MB limit`),this.deserialize(n)}catch(e){t.push(e instanceof Error?e.message:String(e))}return{valid:t.length===0,errors:t}}static clone(e){try{return this.deserialize(this.serialize(e))}catch{return{...e}}}static equals(e,t){try{return this.serialize(e)===this.serialize(t)}catch{return!1}}}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `useState` that survives page navigations.
3
+ *
4
+ * State is serialized to sessionStorage (or localStorage) via
5
+ * {@link IslandStateSerializer}, so Date, Map, Set, and RegExp are preserved.
6
+ * On the server or when storage is unavailable, falls back to in-memory state.
7
+ *
8
+ * @param id - Unique key for this piece of state.
9
+ * @param initialValue - Default value when nothing is stored yet.
10
+ * @param options - Optional `{ storage: 'session' | 'local' }`. Defaults to `'session'`.
11
+ * @returns `[value, setValue, clearValue]` — same shape as `useState` plus a clear function.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { usePersistentState } from '@useavalon/avalon';
16
+ *
17
+ * function Counter() {
18
+ * const [count, setCount, clearCount] = usePersistentState('my-counter', 0);
19
+ * return (
20
+ * <div>
21
+ * <p>{count}</p>
22
+ * <button onClick={() => setCount(c => c + 1)}>+1</button>
23
+ * <button onClick={clearCount}>Reset</button>
24
+ * </div>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ export declare function usePersistentState<T>(id: string, initialValue: T, options?: {
30
+ storage?: 'session' | 'local';
31
+ }): [T, (value: T | ((prev: T) => T)) => void, () => void];
@@ -0,0 +1 @@
1
+ import{useState as e,useEffect as t,useCallback as n}from"preact/hooks";import{IslandStateSerializer as r}from"./island-state-serializer.js";export function usePersistentState(i,a,o){let s=o?.storage??`session`,c=`avalon-island:${i}`,[l,u]=e(()=>{if(globalThis.window===void 0)return a;try{let e=(s===`local`?localStorage:sessionStorage).getItem(c);return e===null?a:r.deserialize(e).v??a}catch{return a}});return t(()=>{if(globalThis.window!==void 0)try{(s===`local`?localStorage:sessionStorage).setItem(c,r.serialize({v:l}))}catch{}},[l,c,s]),[l,n(e=>{u(t=>typeof e==`function`?e(t):e)},[]),n(()=>{if(u(a),globalThis.window!==void 0)try{(s===`local`?localStorage:sessionStorage).removeItem(c)}catch{}},[a,c,s])]}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Avalon Prerender Module
3
+ *
4
+ * Post-build prerendering for static site generation (SSG).
5
+ *
6
+ * Nitro v3's Vite builder does not yet support the built-in prerender
7
+ * pipeline. This module provides an equivalent: after the Vite/Nitro build
8
+ * completes, it boots the built server locally, fetches each configured
9
+ * route, and writes the resulting HTML to the public output directory.
10
+ *
11
+ * Prerendered pages are served as static files from the CDN — zero
12
+ * function invocations. Islands on prerendered pages still hydrate
13
+ * normally on the client.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // In post-build.mjs or build.mjs:
18
+ * import { prerenderRoutes } from '@useavalon/avalon/prerender';
19
+ *
20
+ * await prerenderRoutes({
21
+ * serverEntryPath: '.output/server/index.mjs',
22
+ * outputDir: '.output/public',
23
+ * routes: ['/'],
24
+ * crawlLinks: true,
25
+ * ignore: ['/demo/data-fetching'],
26
+ * });
27
+ * ```
28
+ */
29
+ export interface PrerenderConfig {
30
+ /** Path to the built Nitro server entry (e.g. '.output/server/index.mjs') */
31
+ serverEntryPath: string;
32
+ /** Directory to write prerendered HTML files */
33
+ outputDir: string;
34
+ /** Explicit routes to prerender */
35
+ routes?: string[];
36
+ /** Crawl <a> links in prerendered pages to discover more routes */
37
+ crawlLinks?: boolean;
38
+ /** Routes to ignore (strings, RegExps, or filter functions) */
39
+ ignore?: Array<string | RegExp | ((path: string) => undefined | null | boolean)>;
40
+ /** Number of concurrent prerender requests @default 4 */
41
+ concurrency?: number;
42
+ /** Fail the build if a prerender route errors @default false */
43
+ failOnError?: boolean;
44
+ /** Write /about as /about/index.html @default true */
45
+ autoSubfolderIndex?: boolean;
46
+ /** Number of retry attempts per route @default 3 */
47
+ retry?: number;
48
+ /** Delay between retries in ms @default 500 */
49
+ retryDelay?: number;
50
+ /** Port to run the prerender server on @default 13172 */
51
+ port?: number;
52
+ }
53
+ export { prerenderRoutes } from "./prerender.ts";
@@ -0,0 +1 @@
1
+ export{prerenderRoutes}from"./prerender.js";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Prerender implementation.
3
+ *
4
+ * Spawns the built Nitro server as a child process, fetches routes via HTTP,
5
+ * extracts links, and writes static HTML files to the output directory.
6
+ *
7
+ * This approach avoids dependency issues (e.g. undici, platform-specific
8
+ * modules) that occur when importing the server bundle directly.
9
+ */
10
+ import type { PrerenderConfig } from "./index.ts";
11
+ /**
12
+ * Prerender routes by spawning the built server and fetching each route via HTTP.
13
+ */
14
+ export declare function prerenderRoutes(config: PrerenderConfig): Promise<{
15
+ prerenderedRoutes: string[];
16
+ errors: Array<{
17
+ route: string;
18
+ error: string;
19
+ }>;
20
+ }>;
@@ -0,0 +1 @@
1
+ import{spawn as e}from"node:child_process";import{existsSync as t,mkdirSync as n,writeFileSync as r}from"node:fs";import{dirname as i,join as a,resolve as o}from"node:path";function s(e){let t=[],n=/<a\s[^>]*href=["']([^"'#?]+)/gi,r=n.exec(e);for(;r!==null;){let i=r[1];if(i.startsWith(`/`)&&!i.startsWith(`//`)){if(i.startsWith(`/assets/`)||i.startsWith(`/islands/`)||i.startsWith(`/chunks/`)||i.startsWith(`/_`)||i.match(/\.\w{2,5}$/))continue;t.push(i)}r=n.exec(e)}return[...new Set(t)]}function c(e,t){for(let n of t)if(typeof n==`string`){if(e===n||n.endsWith(`/**`)&&e.startsWith(n.slice(0,-2)))return!0}else if(n instanceof RegExp){if(n.test(e))return!0}else if(typeof n==`function`&&n(e))return!0;return!1}async function l(e,t=15e3){let n=Date.now();for(;Date.now()-n<t;){try{let t=await fetch(`${e}/`);if(t.ok||t.status<500)return}catch{}await new Promise(e=>setTimeout(e,200))}throw Error(`[prerender] Server did not become ready within ${t}ms`)}export async function prerenderRoutes(u){let{serverEntryPath:d,outputDir:f,routes:p=[`/`],crawlLinks:m=!1,ignore:h=[],concurrency:g=4,failOnError:_=!1,autoSubfolderIndex:v=!0,retry:y=3,retryDelay:b=500,port:x=13172}=u,S=o(d);if(!t(S))throw Error(`[prerender] Server entry not found: ${S}`);let C=o(f),w=`http://localhost:${x}`;console.log(`[prerender] Spawning server from ${d} on port ${x}...`);let T=null;try{T=e(`node`,[S],{env:{...process.env,PORT:String(x),NITRO_PORT:String(x),HOST:`127.0.0.1`,NITRO_HOST:`127.0.0.1`,NODE_ENV:`production`},stdio:[`ignore`,`pipe`,`pipe`]}),T.stdout?.on(`data`,e=>{let t=e.toString().trim();t&&console.log(`[prerender:server] ${t}`)}),T.stderr?.on(`data`,e=>{let t=e.toString().trim();t&&console.error(`[prerender:server:err] ${t}`)}),await l(w),console.log(`[prerender] Server ready at ${w}`)}catch(e){T?.kill(`SIGKILL`);let t=e instanceof Error?e.message:String(e);throw Error(`[prerender] Failed to start server: ${t}`)}let E=[],D=[],O=new Set,k=[...p];console.log(`[prerender] Starting with ${k.length} route(s), crawlLinks=${m}`);async function A(e){for(let t=1;t<=y;t++)try{let t=await fetch(`${w}${e}`);return{html:await t.text(),status:t.status}}catch(n){let r=n instanceof Error?n.message:String(n);if(t<y)console.warn(`[prerender] Retry ${t}/${y} for ${e}: ${r}`),await new Promise(e=>setTimeout(e,b));else return console.error(`[prerender] All ${y} attempts failed for ${e}: ${r}`),null}return null}try{for(;k.length>0;){let e=k.splice(0,g);await Promise.all(e.map(async e=>{let t=e.endsWith(`/`)&&e!==`/`?e.slice(0,-1):e;if(O.has(t))return;if(O.add(t),c(t,h)){console.log(`[prerender] ⏭ ${t} (ignored)`);return}let o=await A(t);if(!o){let e=`Failed to fetch ${t} after ${y} attempts`;if(console.error(`[prerender] ❌ ${e}`),D.push({route:t,error:e}),_)throw Error(e);return}if(o.status>=400){let e=`${t} returned ${o.status}`;if(console.error(`[prerender] ❌ ${e}`),D.push({route:t,error:e}),_)throw Error(e);return}let l=v?a(t,`index.html`):`${t}.html`,u=a(C,l);if(n(i(u),{recursive:!0}),r(u,o.html),E.push(t),console.log(`[prerender] ✅ ${t} → ${l}`),m){let e=s(o.html);for(let t of e){let e=t.endsWith(`/`)&&t!==`/`?t.slice(0,-1):t;!O.has(e)&&!c(e,h)&&k.push(e)}}}))}}finally{T&&(console.log(`[prerender] Shutting down server...`),T.kill(`SIGKILL`))}return console.log(`[prerender] Done: ${E.length} page(s) prerendered`+(D.length>0?`, ${D.length} error(s)`:``)),{prerenderedRoutes:E,errors:D}}
@@ -108,7 +108,7 @@ export declare const PersistentIslandContextSchema: z.ZodObject<{
108
108
  * Layout Error Info Schema
109
109
  */
110
110
  export declare const LayoutErrorInfoSchema: z.ZodObject<{
111
- layoutPath: z.ZodString;
111
+ layoutPath: z.ZodOptional<z.ZodString>;
112
112
  errorType: z.ZodEnum<{
113
113
  island: "island";
114
114
  component: "component";
@@ -1 +1 @@
1
- import{z as e}from"zod";export const LayoutContextSchema=e.object({request:e.instanceof(Request),params:e.record(e.string(),e.string()),query:e.instanceof(URLSearchParams),state:e.instanceof(Map),middlewareContext:e.any().optional()});export const LayoutDataSchema=e.record(e.string(),e.unknown());export const LayoutRouteSchema=e.object({pattern:e.instanceof(URLPattern),layoutPath:e.string().min(1),priority:e.number().int().min(0),type:e.enum([`root`,`nested`]),depth:e.number().int().min(0)});export const LayoutHandlerSchema=e.object({component:e.any(),loader:e.any().optional(),path:e.string().min(1),priority:e.number().int().min(0)});export const LayoutPropsSchema=e.object({children:e.any(),data:LayoutDataSchema,frontmatter:e.record(e.string(),e.any()).optional(),route:e.object({path:e.string(),params:e.record(e.string(),e.string()),query:e.instanceof(URLSearchParams)})});export const LayoutDiscoveryOptionsSchema=e.object({baseDirectory:e.string().min(1),filePattern:e.string().min(1).optional().default(`_layout.tsx`),excludeDirectories:e.array(e.string()).optional().default([]),enableWatching:e.boolean().optional().default(!1),developmentMode:e.boolean().optional().default(!1)});export const RouteInfoSchema=e.object({path:e.string(),params:e.record(e.string(),e.string()),method:e.string(),headers:e.instanceof(Headers)});export const LayoutRuleSchema=e.object({matches:e.any(),apply:e.boolean(),priority:e.number().int()});export const LayoutConfigSchema=e.object({skipLayouts:e.array(e.string()).optional(),replaceLayout:e.boolean().optional(),onlyLayouts:e.array(e.string()).optional(),customLayout:e.string().optional()});export const IslandStateSchema=e.record(e.string(),e.unknown());export const PersistentIslandPropsSchema=e.object({persistentId:e.string().min(1),children:e.any()});export const PersistentIslandContextSchema=e.object({saveState:e.any(),loadState:e.any(),clearState:e.any()});export const LayoutErrorInfoSchema=e.object({layoutPath:e.string(),errorType:e.enum([`component`,`loader`,`rendering`,`island`]),timestamp:e.number().int().positive(),componentStack:e.string().optional(),errorBoundary:e.string().optional()});export const LayoutErrorBoundaryPropsSchema=e.object({children:e.any(),fallback:e.any(),onError:e.any().optional()});export const ErrorRecoveryStrategySchema=e.object({type:e.enum([`retry`,`fallback`,`skip`,`redirect`]),maxRetries:e.number().int().positive().optional(),fallbackComponent:e.any().optional(),redirectUrl:e.url().optional()});export const StreamingLayoutPropsSchema=e.object({children:e.any(),fallback:e.any().optional(),priority:e.enum([`high`,`medium`,`low`]).default(`medium`)});export const StreamingComponentSchema=e.object({component:e.any(),fallback:e.any(),priority:e.number().int().min(0),isReady:e.any()});export const ResolvedLayoutSchema=e.object({handlers:e.array(LayoutHandlerSchema),dataLoaders:e.array(e.any()),errorBoundaries:e.array(e.any()),streamingComponents:e.array(StreamingComponentSchema),metadata:e.object({totalLayouts:e.number().int().min(0),resolutionTime:e.number().positive(),cacheHit:e.boolean()})});export const LayoutCacheSchema=e.object({resolved:e.instanceof(Map),handlers:e.instanceof(Map),data:e.instanceof(Map),ttl:e.instanceof(Map)});export const EnhancedLayoutContextSchema=LayoutContextSchema.extend({layouts:e.array(LayoutHandlerSchema),parentData:e.array(LayoutDataSchema),islandStates:e.instanceof(Map),streamingEnabled:e.boolean(),errorBoundaries:e.array(e.any())});
1
+ import{z as e}from"zod";export const LayoutContextSchema=e.object({request:e.instanceof(Request),params:e.record(e.string(),e.string()),query:e.instanceof(URLSearchParams),state:e.instanceof(Map),middlewareContext:e.any().optional()});export const LayoutDataSchema=e.record(e.string(),e.unknown());export const LayoutRouteSchema=e.object({pattern:e.instanceof(URLPattern),layoutPath:e.string().min(1),priority:e.number().int().min(0),type:e.enum([`root`,`nested`]),depth:e.number().int().min(0)});export const LayoutHandlerSchema=e.object({component:e.any(),loader:e.any().optional(),path:e.string().min(1),priority:e.number().int().min(0)});export const LayoutPropsSchema=e.object({children:e.any(),data:LayoutDataSchema,frontmatter:e.record(e.string(),e.any()).optional(),route:e.object({path:e.string(),params:e.record(e.string(),e.string()),query:e.instanceof(URLSearchParams)})});export const LayoutDiscoveryOptionsSchema=e.object({baseDirectory:e.string().min(1),filePattern:e.string().min(1).optional().default(`_layout.tsx`),excludeDirectories:e.array(e.string()).optional().default([]),enableWatching:e.boolean().optional().default(!1),developmentMode:e.boolean().optional().default(!1)});export const RouteInfoSchema=e.object({path:e.string(),params:e.record(e.string(),e.string()),method:e.string(),headers:e.instanceof(Headers)});export const LayoutRuleSchema=e.object({matches:e.any(),apply:e.boolean(),priority:e.number().int()});export const LayoutConfigSchema=e.object({skipLayouts:e.array(e.string()).optional(),replaceLayout:e.boolean().optional(),onlyLayouts:e.array(e.string()).optional(),customLayout:e.string().optional()});export const IslandStateSchema=e.record(e.string(),e.unknown());export const PersistentIslandPropsSchema=e.object({persistentId:e.string().min(1),children:e.any()});export const PersistentIslandContextSchema=e.object({saveState:e.any(),loadState:e.any(),clearState:e.any()});export const LayoutErrorInfoSchema=e.object({layoutPath:e.string().optional(),errorType:e.enum([`component`,`loader`,`rendering`,`island`]),timestamp:e.number().int().positive(),componentStack:e.string().optional(),errorBoundary:e.string().optional()});export const LayoutErrorBoundaryPropsSchema=e.object({children:e.any(),fallback:e.any(),onError:e.any().optional()});export const ErrorRecoveryStrategySchema=e.object({type:e.enum([`retry`,`fallback`,`skip`,`redirect`]),maxRetries:e.number().int().positive().optional(),fallbackComponent:e.any().optional(),redirectUrl:e.url().optional()});export const StreamingLayoutPropsSchema=e.object({children:e.any(),fallback:e.any().optional(),priority:e.enum([`high`,`medium`,`low`]).default(`medium`)});export const StreamingComponentSchema=e.object({component:e.any(),fallback:e.any(),priority:e.number().int().min(0),isReady:e.any()});export const ResolvedLayoutSchema=e.object({handlers:e.array(LayoutHandlerSchema),dataLoaders:e.array(e.any()),errorBoundaries:e.array(e.any()),streamingComponents:e.array(StreamingComponentSchema),metadata:e.object({totalLayouts:e.number().int().min(0),resolutionTime:e.number().positive(),cacheHit:e.boolean()})});export const LayoutCacheSchema=e.object({resolved:e.instanceof(Map),handlers:e.instanceof(Map),data:e.instanceof(Map),ttl:e.instanceof(Map)});export const EnhancedLayoutContextSchema=LayoutContextSchema.extend({layouts:e.array(LayoutHandlerSchema),parentData:e.array(LayoutDataSchema),islandStates:e.instanceof(Map),streamingEnabled:e.boolean(),errorBoundaries:e.array(e.any())});
@@ -1,106 +1,106 @@
1
- /**
2
- * Type declarations for image imports with vite-imagetools
3
- *
4
- * These types enable TypeScript support for optimized image imports.
5
- * Include this in your tsconfig.json `types` array:
6
- *
7
- * ```json
8
- * {
9
- * "compilerOptions": {
10
- * "types": ["@useavalon/avalon/types"]
11
- * }
12
- * }
13
- * ```
14
- */
15
-
16
- declare module "*.jpg" {
17
- const src: string;
18
- export default src;
19
- }
20
-
21
- declare module "*.jpeg" {
22
- const src: string;
23
- export default src;
24
- }
25
-
26
- declare module "*.png" {
27
- const src: string;
28
- export default src;
29
- }
30
-
31
- declare module "*.webp" {
32
- const src: string;
33
- export default src;
34
- }
35
-
36
- declare module "*.avif" {
37
- const src: string;
38
- export default src;
39
- }
40
-
41
- declare module "*.gif" {
42
- const src: string;
43
- export default src;
44
- }
45
-
46
- declare module "*.tiff" {
47
- const src: string;
48
- export default src;
49
- }
50
-
51
- declare module "*.svg" {
52
- const src: string;
53
- export default src;
54
- }
55
-
56
- // vite-imagetools srcset output
57
- interface ImageToolsSrcset {
58
- src: string;
59
- srcset: string;
60
- width: number;
61
- height: number;
62
- }
63
-
64
- declare module "*&as=srcset" {
65
- const srcset: ImageToolsSrcset;
66
- export default srcset;
67
- }
68
-
69
- declare module "*?as=srcset" {
70
- const srcset: ImageToolsSrcset;
71
- export default srcset;
72
- }
73
-
74
- // vite-imagetools picture output (multiple formats)
75
- interface ImageToolsPicture {
76
- sources: Record<string, ImageToolsSrcset>;
77
- img: ImageToolsSrcset;
78
- }
79
-
80
- declare module "*&as=picture" {
81
- const picture: ImageToolsPicture;
82
- export default picture;
83
- }
84
-
85
- declare module "*?as=picture" {
86
- const picture: ImageToolsPicture;
87
- export default picture;
88
- }
89
-
90
- // vite-imagetools metadata output
91
- interface ImageToolsMetadata {
92
- src: string;
93
- width: number;
94
- height: number;
95
- format: string;
96
- }
97
-
98
- declare module "*&as=metadata" {
99
- const metadata: ImageToolsMetadata;
100
- export default metadata;
101
- }
102
-
103
- declare module "*?as=metadata" {
104
- const metadata: ImageToolsMetadata;
105
- export default metadata;
106
- }
1
+ /**
2
+ * Type declarations for image imports with vite-imagetools
3
+ *
4
+ * These types enable TypeScript support for optimized image imports.
5
+ * Include this in your tsconfig.json `types` array:
6
+ *
7
+ * ```json
8
+ * {
9
+ * "compilerOptions": {
10
+ * "types": ["@useavalon/avalon/types"]
11
+ * }
12
+ * }
13
+ * ```
14
+ */
15
+
16
+ declare module "*.jpg" {
17
+ const src: string;
18
+ export default src;
19
+ }
20
+
21
+ declare module "*.jpeg" {
22
+ const src: string;
23
+ export default src;
24
+ }
25
+
26
+ declare module "*.png" {
27
+ const src: string;
28
+ export default src;
29
+ }
30
+
31
+ declare module "*.webp" {
32
+ const src: string;
33
+ export default src;
34
+ }
35
+
36
+ declare module "*.avif" {
37
+ const src: string;
38
+ export default src;
39
+ }
40
+
41
+ declare module "*.gif" {
42
+ const src: string;
43
+ export default src;
44
+ }
45
+
46
+ declare module "*.tiff" {
47
+ const src: string;
48
+ export default src;
49
+ }
50
+
51
+ declare module "*.svg" {
52
+ const src: string;
53
+ export default src;
54
+ }
55
+
56
+ // vite-imagetools srcset output
57
+ interface ImageToolsSrcset {
58
+ src: string;
59
+ srcset: string;
60
+ width: number;
61
+ height: number;
62
+ }
63
+
64
+ declare module "*&as=srcset" {
65
+ const srcset: ImageToolsSrcset;
66
+ export default srcset;
67
+ }
68
+
69
+ declare module "*?as=srcset" {
70
+ const srcset: ImageToolsSrcset;
71
+ export default srcset;
72
+ }
73
+
74
+ // vite-imagetools picture output (multiple formats)
75
+ interface ImageToolsPicture {
76
+ sources: Record<string, ImageToolsSrcset>;
77
+ img: ImageToolsSrcset;
78
+ }
79
+
80
+ declare module "*&as=picture" {
81
+ const picture: ImageToolsPicture;
82
+ export default picture;
83
+ }
84
+
85
+ declare module "*?as=picture" {
86
+ const picture: ImageToolsPicture;
87
+ export default picture;
88
+ }
89
+
90
+ // vite-imagetools metadata output
91
+ interface ImageToolsMetadata {
92
+ src: string;
93
+ width: number;
94
+ height: number;
95
+ format: string;
96
+ }
97
+
98
+ declare module "*&as=metadata" {
99
+ const metadata: ImageToolsMetadata;
100
+ export default metadata;
101
+ }
102
+
103
+ declare module "*?as=metadata" {
104
+ const metadata: ImageToolsMetadata;
105
+ export default metadata;
106
+ }
@@ -1,22 +1,22 @@
1
- /**
2
- * Avalon type definitions.
3
- *
4
- * Include this in your tsconfig.json `types` array to get island prop support:
5
- *
6
- * ```json
7
- * {
8
- * "compilerOptions": {
9
- * "types": ["@useavalon/avalon/types"]
10
- * }
11
- * }
12
- * ```
13
- */
14
-
15
- // Re-export island prop types
16
- export * from './island-prop.d.ts';
17
-
18
- // Import JSX augmentations (side-effect import for type augmentation)
19
- import './island-jsx.d.ts';
20
-
21
- // Import image type declarations
22
- import './image.d.ts';
1
+ /**
2
+ * Avalon type definitions.
3
+ *
4
+ * Include this in your tsconfig.json `types` array to get island prop support:
5
+ *
6
+ * ```json
7
+ * {
8
+ * "compilerOptions": {
9
+ * "types": ["@useavalon/avalon/types"]
10
+ * }
11
+ * }
12
+ * ```
13
+ */
14
+
15
+ // Re-export island prop types
16
+ export * from './island-prop.d.ts';
17
+
18
+ // Import JSX augmentations (side-effect import for type augmentation)
19
+ import './island-jsx.d.ts';
20
+
21
+ // Import image type declarations
22
+ import './image.d.ts';