@useavalon/avalon 0.1.48 → 0.1.50
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.
- package/dist/mod.d.ts +48 -48
- package/dist/mod.js +1 -1
- package/dist/src/build/page-island-transform.d.ts +1 -1
- package/dist/src/build/page-island-transform.js +2 -3
- package/dist/src/client/components.d.ts +8 -8
- package/dist/src/client/components.js +1 -1
- package/dist/src/components/IslandErrorBoundary.d.ts +2 -2
- package/dist/src/components/IslandErrorBoundary.js +1 -1
- package/dist/src/components/LayoutErrorBoundary.d.ts +3 -3
- package/dist/src/components/LayoutErrorBoundary.js +1 -1
- package/dist/src/islands/integration-loader.d.ts +2 -2
- package/dist/src/islands/island.d.ts +7 -7
- package/dist/src/islands/island.js +1 -1
- package/dist/src/layout-system.d.ts +14 -17
- package/dist/src/layout-system.js +1 -1
- package/dist/src/nitro/config.d.ts +13 -0
- package/dist/src/nitro/renderer.d.ts +21 -11
- package/dist/src/nitro/renderer.js +20 -20
- package/dist/src/persistence/island-state-serializer.d.ts +9 -19
- package/dist/src/persistence/island-state-serializer.js +1 -1
- package/dist/src/persistence/use-persistent-state.d.ts +2 -2
- package/dist/src/persistence/use-persistent-state.js +1 -1
- package/dist/src/post-build/index.d.ts +57 -0
- package/dist/src/post-build/index.js +31 -0
- package/dist/src/schemas/core.d.ts +2 -2
- package/dist/src/schemas/layout.d.ts +4 -4
- package/dist/src/schemas/routing/index.d.ts +2 -2
- package/dist/src/schemas/routing.d.ts +4 -4
- package/dist/src/types/index.d.ts +8 -5
- package/dist/src/types/island-prop.d.ts +14 -8
- package/dist/src/types/layout.d.ts +11 -11
- package/dist/src/types/layout.js +1 -1
- package/dist/src/types/virtual-modules.d.ts +56 -0
- package/dist/src/vite-plugin/nitro-integration.d.ts +8 -0
- package/dist/src/vite-plugin/nitro-integration.js +15 -10
- package/package.json +6 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{getRequestURL as e}from"h3";import{
|
|
1
|
+
import{getRequestURL as e}from"h3";import{h as t}from"preact";import n from"preact-render-to-string";import{discoverScopedMiddleware as r,executeScopedMiddleware as i}from"../middleware/index.js";import{discoverErrorPages as a,handleRenderError as o}from"./error-handler.js";import{createNotFoundError as s,isHttpError as c}from"./types.js";export function createRenderContext(e,t={}){let n=getRequestURL(e);return{url:n,params:t,query:Object.fromEntries(n.searchParams),request:toRequest(e),event:e}}export function createRenderContextFromRequest(e,t={}){let n=new URL(e.url,`http://localhost`),r={method:e.method,path:n.pathname+n.search,context:{params:t}};return{url:n,params:t,query:Object.fromEntries(n.searchParams),request:e,event:r}}export function getRequestURL(t){return new URL(e(t).pathname,`http://localhost`)}export function toRequest(e){let t=getRequestURL(e);return new Request(t,{method:e.req.method,headers:getRequestHeaders(e)})}export function getRequestHeaders(e){return new Headers}export function setResponseHeader(e,t,n){e.context.responseHeaders||(e.context.responseHeaders={}),e.context.responseHeaders[t]=n}export function createErrorResponse(e,t){let n=c(e)?e.statusCode:500;if(t){let t=g(e,n);return new Response(t,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}let r=_(n);return new Response(r,{status:n,headers:{"Content-Type":`text/html; charset=utf-8`}})}function g(e,t){return`<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
@@ -56,15 +56,15 @@ import{getRequestURL as e}from"h3";import{createNotFoundError as t,isHttpError a
|
|
|
56
56
|
<body>
|
|
57
57
|
<div class="error-container">
|
|
58
58
|
<div class="status-code">${t}</div>
|
|
59
|
-
<h1>${
|
|
60
|
-
<p class="message">${
|
|
59
|
+
<h1>${v(t)}</h1>
|
|
60
|
+
<p class="message">${y(e.message)}</p>
|
|
61
61
|
${e.stack?`
|
|
62
62
|
<div class="stack-title">Stack Trace</div>
|
|
63
|
-
<pre>${
|
|
63
|
+
<pre>${y(e.stack)}</pre>
|
|
64
64
|
`:``}
|
|
65
65
|
</div>
|
|
66
66
|
</body>
|
|
67
|
-
</html>`}function
|
|
67
|
+
</html>`}function _(e){return`<!DOCTYPE html>
|
|
68
68
|
<html lang="en">
|
|
69
69
|
<head>
|
|
70
70
|
<meta charset="utf-8">
|
|
@@ -113,47 +113,47 @@ import{getRequestURL as e}from"h3";import{createNotFoundError as t,isHttpError a
|
|
|
113
113
|
<body>
|
|
114
114
|
<div class="error-container">
|
|
115
115
|
<div class="status-code">${e}</div>
|
|
116
|
-
<h1>${
|
|
116
|
+
<h1>${v(e)}</h1>
|
|
117
117
|
<p><a href="/">Return to home</a></p>
|
|
118
118
|
</div>
|
|
119
119
|
</body>
|
|
120
|
-
</html>`}function
|
|
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
|
|
120
|
+
</html>`}function v(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 y(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`).replaceAll(`'`,`'`)}export function extractIslandMarkers(e){let t=[],n=/<[^>]*data-framework="([^"]+)"[^>]*>/g,r=n.exec(e);for(;r!==null;){let i=r[0],a=r[1],o=/data-src="([^"]+)"/.exec(i),s=o?o[1]:``,c=/data-props="([^"]*)"/.exec(i),l=c?c[1]:void 0,u=/data-hydrate="([^"]+)"/.exec(i),d=u?u[1]:void 0;t.push({framework:a,src:s,props:l,hydrate:d}),r=n.exec(e)}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`,`entry-client`].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={},r){try{let i={};return e.getServerSideProps&&(i=await e.getServerSideProps(t)),{html:await b(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 b(e,r,i,a,o){let s=e.default,c=e.metadata||{},l=process.env.NODE_ENV!==`production`,u;try{let e=s(r);u=e instanceof Promise?await e:e}catch(e){console.error(`[renderer] Error calling page component:`,e),u=t(`div`,null,`Error rendering page`)}let d;try{d=n(u)}catch(e){console.error(`[renderer] Error in preactRenderToString:`,e),d=`<div>Error rendering page</div>`}if(o)return await o(d,e,i);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>${
|
|
128
|
-
${
|
|
127
|
+
<title>${y(String(c.title||`Avalon App`))}</title>
|
|
128
|
+
${c.description?`<meta name="description" content="${y(String(c.description))}">`:``}
|
|
129
129
|
</head>
|
|
130
130
|
<body>
|
|
131
131
|
<div id="app">
|
|
132
132
|
${d}
|
|
133
133
|
</div>${f}
|
|
134
134
|
</body>
|
|
135
|
-
</html>`}export async function renderPageStream(e,t,
|
|
135
|
+
</html>`}export async function renderPageStream(e,t,r={}){let i=new TextEncoder,a=null,o={shellSent:!1,contentSent:!1,closed:!1,error:null},s=r.shellReadyTimeout,c=r.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=w(e);r.enqueue(i.encode(t));let n=C();r.enqueue(i.encode(n))}else{let t=g(e,500);r.enqueue(i.encode(t))}n.closed=!0,r.close()}}async function p(s){a=s;let p={};e.getServerSideProps&&(p=await e.getServerSideProps(t));let m=x(e.metadata||{},t);if(o.closed||(s.enqueue(i.encode(m)),o.shellSent=!0,l&&=(clearTimeout(l),null),r.onShellReady&&r.onShellReady()),c&&c>0&&(u=setTimeout(()=>{!o.contentSent&&!o.closed&&f(Error(`All ready timeout after ${c}ms`),!1,o,a,i,d,r)},c)),!o.closed){let t=e.default;if(t&&typeof t==`function`){let r=t(p);if(r&&typeof r.then==`function`)try{let e=n(await r);s.enqueue(i.encode(` <div id="app">${e}</div>\n`))}catch(t){console.error(`[streaming] Async component error:`,t);let n=S(e,p);s.enqueue(i.encode(n))}else{let t=S(e,p);s.enqueue(i.encode(t))}}else{let t=S(e,p);s.enqueue(i.encode(t))}o.contentSent=!0}if(!o.closed){let e=C();s.enqueue(i.encode(e))}d(),r.onAllReady&&!o.closed&&r.onAllReady(),o.closed||(o.closed=!0,s.close())}return new ReadableStream({async start(e){a=e,s&&s>0&&(l=setTimeout(()=>{!o.shellSent&&!o.closed&&f(Error(`Shell ready timeout after ${s}ms`),!0,o,a,i,d,r)},s));try{await p(e)}catch(e){f(e instanceof Error?e:Error(String(e)),!o.shellSent,o,a,i,d,r)}},cancel(){if(d(),!o.closed&&a){o.closed=!0;try{a.close()}catch{}}}})}function x(e,t){return`<!DOCTYPE html>
|
|
136
136
|
<html lang="en">
|
|
137
137
|
<head>
|
|
138
138
|
<meta charset="utf-8">
|
|
139
139
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
140
|
-
<title>${
|
|
141
|
-
${e.description?`<meta name="description" content="${
|
|
140
|
+
<title>${y(String(e.title||`Avalon App`))}</title>
|
|
141
|
+
${e.description?`<meta name="description" content="${y(String(e.description))}">`:``}
|
|
142
142
|
</head>
|
|
143
143
|
<body>
|
|
144
|
-
`}function
|
|
144
|
+
`}function S(e,t){let r=e.default;if(r&&typeof r==`function`)try{let e=r(t);if(e&&typeof e.then==`function`){let e=r.name||`Page`;return` <div id="app" data-page="${y(String(e))}" data-props='${y(JSON.stringify(t))}'>
|
|
145
145
|
<!-- Async component — awaiting hydration -->
|
|
146
|
-
</div>\n`}return` <div id="app">${
|
|
146
|
+
</div>\n`}return` <div id="app">${n(e)}</div>\n`}catch(e){console.error(`[streaming] Error rendering page component:`,e)}let i=e.default?.name||`Page`;return` <div id="app" data-page="${y(String(i))}" data-props='${y(JSON.stringify(t))}'>
|
|
147
147
|
<!-- Component render fallback -->
|
|
148
|
-
</div>\n`}function
|
|
149
|
-
</html>`}function
|
|
148
|
+
</div>\n`}function C(){return` </body>
|
|
149
|
+
</html>`}function w(e){let t=process.env.NODE_ENV!==`production`,n=e.stack?`<pre style="
|
|
150
150
|
background: #f5f5f5;
|
|
151
151
|
padding: 10px;
|
|
152
152
|
border-radius: 4px;
|
|
153
153
|
overflow-x: auto;
|
|
154
154
|
font-size: 12px;
|
|
155
155
|
margin-top: 10px;
|
|
156
|
-
">${
|
|
156
|
+
">${y(e.stack)}</pre>`:``;return`
|
|
157
157
|
<div class="streaming-error-boundary" data-error-boundary="true" style="
|
|
158
158
|
background: #fff3cd;
|
|
159
159
|
border: 2px solid #ffc107;
|
|
@@ -175,10 +175,10 @@ import{getRequestURL as e}from"h3";import{createNotFoundError as t,isHttpError a
|
|
|
175
175
|
Error Details (Development Mode)
|
|
176
176
|
</summary>
|
|
177
177
|
<div style="margin-top: 10px;">
|
|
178
|
-
<p><strong>Error:</strong> ${
|
|
178
|
+
<p><strong>Error:</strong> ${y(e.message)}</p>
|
|
179
179
|
${n}
|
|
180
180
|
</div>
|
|
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
|
|
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 T(e,t,n){return async()=>(e.value??=await r({baseDir:t,devMode:n}),e.value)}function E(e,t,n){return async(r,i)=>e?o(r,i,t):createErrorResponse(r,n)}export function createNitroRenderer(e){let{avalonConfig:t,isDev:n=!1,enableCustomErrorPages:r=!0}=e,o={isDev:n,avalonConfig:t,loadPageModule:e.loadPageModule,pagesDir:t.pagesDir};r&&a(o).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let c=T({value:null},t.srcDir||`src`,n),f=E(r,o,n);async function p(r){let a=getRequestURL(r).pathname;try{let o=await i(r,await c(),{devMode:n});if(o)return n&&console.log(`[renderer] Middleware terminated request for ${a}`),o;let u=r.context.route,d=null;if(d=u||(e.resolvePageRoute?await e.resolvePageRoute(a,t.pagesDir):await D(a,t.pagesDir)),!d)return f(s(`Page not found: ${a}`),r);let p=e.loadPageModule?await e.loadPageModule(d.filePath):await O(d.filePath),h=createRenderContext(r,r.context.params||d.params);if(e.resolveLayouts&&(h.layoutContext={layouts:await e.resolveLayouts(a,t)}),t.streaming){let e=await renderPageStream(p,h,{onShellReady:()=>{setResponseHeader(r,`Content-Type`,`text/html; charset=utf-8`)}});return new Response(e,{headers:{"Content-Type":`text/html; charset=utf-8`}})}else{let t=await renderPage(p,h,{},e.wrapWithLayouts),r=injectHydrationScript(t.html,n);return new Response(r,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Renderer Error]`,e),f(e instanceof Error?e:Error(String(e)),r)}}return Object.assign(p,{async fetch(r){let i=new URL(r.url,`http://localhost`).pathname;try{let a=null;if(a=e.resolvePageRoute?await e.resolvePageRoute(i,t.pagesDir):await D(i,t.pagesDir),!a)return createErrorResponse(s(`Page not found: ${i}`),n);let o=await renderPage(e.loadPageModule?await e.loadPageModule(a.filePath):await O(a.filePath),createRenderContextFromRequest(r,a.params),{},e.wrapWithLayouts),c=injectHydrationScript(o.html,n);return new Response(c,{status:o.statusCode,headers:o.headers})}catch(e){return console.error(`[Nitro Renderer .fetch() Error]`,e),createErrorResponse(e instanceof Error?e:Error(String(e)),n)}}})}async function D(e,t){return e===`/`||e===``?{filePath:`src/pages/index.tsx`,pattern:`/`,params:{}}:{filePath:`src/pages/${e.replace(/^\//,``).replace(/\/$/,``)}.tsx`,pattern:e,params:{}}}async function O(e){return{default:()=>null,metadata:{title:`Avalon Page`}}}export function createNitroCatchAllRenderer(e){let{avalonConfig:t,isDev:n=!1,loadPageModule:r,resolveLayouts:o,enableCustomErrorPages:c=!0}=e,f={isDev:n,avalonConfig:t,loadPageModule:r,pagesDir:t.pagesDir};c&&a(f).catch(e=>{console.warn(`[renderer] Failed to discover error pages:`,e)});let p=T({value:null},t.srcDir||`src`,n),g=E(c,f,n);async function _(a){let c=getRequestURL(a).pathname;try{let u=await i(a,await p(),{devMode:n});if(u)return n&&console.log(`[renderer] Middleware terminated request for ${c}`),u;let d=a.context.params||{},f=d.slug||c.replace(/^\//,``)||`index`,h=`${t.pagesDir}/${f}.tsx`,_;try{_=await r(h)}catch(e){try{_=await r(`${t.pagesDir}/${f}/index.tsx`)}catch(r){return n&&(console.debug(`[renderer] Page not found: ${h}`,e),console.debug(`[renderer] Index fallback not found: ${t.pagesDir}/${f}/index.tsx`,r)),g(s(`Page not found: ${c}`),a)}}let v=createRenderContext(a,d);if(o&&(v.layoutContext={layouts:await o(c,t)}),t.streaming){let e=await renderPageStream(_,v,{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(_,v,{},e.wrapWithLayouts),r=injectHydrationScript(t.html,n);return new Response(r,{status:t.statusCode,headers:t.headers})}}catch(e){return console.error(`[Nitro Catch-All Renderer Error]`,e),g(e instanceof Error?e:Error(String(e)),a)}}return Object.assign(_,{async fetch(i){let a=new URL(i.url,`http://localhost`).pathname;try{let o=a.replace(/^\//,``)||`index`,c=`${t.pagesDir}/${o}.tsx`,l;try{l=await r(c)}catch{try{l=await r(`${t.pagesDir}/${o}/index.tsx`)}catch{return createErrorResponse(s(`Page not found: ${a}`),n)}}let d=createRenderContextFromRequest(i),f=await renderPage(l,d,{},e.wrapWithLayouts),p=injectHydrationScript(f.html,n);return new Response(p,{status:f.statusCode,headers:f.headers})}catch(e){return console.error(`[Nitro CatchAll .fetch() Error]`,e),createErrorResponse(e instanceof Error?e:Error(String(e)),n)}}})}export{clearMiddlewareCache as clearRendererMiddlewareCache}from"../middleware/index.js";
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import type { IslandState } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export declare
|
|
9
|
-
|
|
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
|
-
}
|
|
1
|
+
import type { IslandState } from "../schemas/layout.ts";
|
|
2
|
+
export declare function serialize(state: IslandState): string;
|
|
3
|
+
export declare function deserialize(serializedState: string): IslandState;
|
|
4
|
+
export declare function validate(state: IslandState): {
|
|
5
|
+
valid: boolean;
|
|
6
|
+
errors: string[];
|
|
7
|
+
};
|
|
8
|
+
export declare function clone(state: IslandState): IslandState;
|
|
9
|
+
export declare function equals(state1: IslandState, state2: IslandState): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function e(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])=>[e(t),e(n)])};if(t instanceof Set)return{__type:`Set`,__value:Array.from(t.values()).map(t=>e(t))};if(typeof t==`function`)return null;if(Array.isArray(t))return t.map(t=>e(t));if(typeof t==`object`){let n={},r=t;for(let t in r)Object.hasOwn(r,t)&&(n[t]=e(r[t]));return n}return t}function t(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}export function serialize(t){try{let n=e(t);return JSON.stringify(n)}catch(e){throw Error(`State serialization failed: ${e instanceof Error?e.message:String(e)}`)}}export function deserialize(e){try{return JSON.parse(e,t)}catch(e){throw Error(`State deserialization failed: ${e instanceof Error?e.message:String(e)}`)}}export function validate(e){let t=[];try{let i=serialize(e),a=new Blob([i]).size;a>5*1024*1024&&t.push(`Serialized state size (${Math.round(a/1024)}KB) exceeds 5MB limit`),deserialize(i)}catch(e){t.push(e instanceof Error?e.message:String(e))}return{valid:t.length===0,errors:t}}export function clone(e){try{return deserialize(serialize(e))}catch{return{...e}}}export function equals(e,t){try{return serialize(e)===serialize(t)}catch{return!1}}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `useState` that survives page navigations.
|
|
3
3
|
*
|
|
4
4
|
* State is serialized to sessionStorage (or localStorage) via
|
|
5
|
-
* {@link
|
|
5
|
+
* {@link serialize}/{@link deserialize}, so Date, Map, Set, and RegExp are preserved.
|
|
6
6
|
* On the server or when storage is unavailable, falls back to in-memory state.
|
|
7
7
|
*
|
|
8
8
|
* @param id - Unique key for this piece of state.
|
|
@@ -27,5 +27,5 @@
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
export declare function usePersistentState<T>(id: string, initialValue: T, options?: {
|
|
30
|
-
storage?:
|
|
30
|
+
storage?: "session" | "local";
|
|
31
31
|
}): [T, (value: T | ((prev: T) => T)) => void, () => void];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{useCallback as e,useEffect as t,useState as n}from"preact/hooks";import{deserialize as r,serialize as i}from"./island-state-serializer.js";export function usePersistentState(a,o,s){let c=s?.storage??`session`,l=`avalon-island:${a}`,[u,d]=n(()=>{if(globalThis.window===void 0)return o;try{let e=(c===`local`?localStorage:sessionStorage).getItem(l);return e===null?o:r(e).v??o}catch{return o}});return t(()=>{if(globalThis.window!==void 0)try{(c===`local`?localStorage:sessionStorage).setItem(l,i({v:u}))}catch{}},[u,l,c]),[u,e(e=>{d(t=>typeof e==`function`?e(t):e)},[]),e(()=>{if(d(o),globalThis.window!==void 0)try{(c===`local`?localStorage:sessionStorage).removeItem(l)}catch{}},[o,l,c])]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avalon Post-Build Script
|
|
3
|
+
*
|
|
4
|
+
* Handles all post-build tasks for production deployments:
|
|
5
|
+
*
|
|
6
|
+
* 1. Cleanup: Removes stale Vite template index.html files
|
|
7
|
+
* 2. CSS Patching: Copies SSR CSS to client assets, patches SSR bundle
|
|
8
|
+
* 3. Island Redirects: Generates _redirects for clean island paths (Netlify)
|
|
9
|
+
* 4. Adapter Copying: Copies framework adapters to dist/
|
|
10
|
+
* 5. Netlify Function Copying: Copies server function to all Netlify paths
|
|
11
|
+
* 6. Prerendering: Boots built server, fetches routes, writes static HTML
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { runPostBuild } from '@useavalon/avalon/post-build';
|
|
15
|
+
* await runPostBuild();
|
|
16
|
+
*
|
|
17
|
+
* With custom prerender config:
|
|
18
|
+
* await runPostBuild({
|
|
19
|
+
* prerender: { routes: ['/'], crawlLinks: true, ignore: ['/admin'] },
|
|
20
|
+
* });
|
|
21
|
+
*/
|
|
22
|
+
export interface PrerenderConfig {
|
|
23
|
+
/** Routes to prerender (default: ['/']) */
|
|
24
|
+
routes?: string[];
|
|
25
|
+
/** Crawl links found in prerendered pages (default: true) */
|
|
26
|
+
crawlLinks?: boolean;
|
|
27
|
+
/** Routes to ignore (default: []) */
|
|
28
|
+
ignore?: string[];
|
|
29
|
+
/** Fail build on prerender errors (default: false) */
|
|
30
|
+
failOnError?: boolean;
|
|
31
|
+
/** Max concurrent fetches (default: 4) */
|
|
32
|
+
concurrency?: number;
|
|
33
|
+
/** Create index.html in subdirectories (default: true) */
|
|
34
|
+
autoSubfolderIndex?: boolean;
|
|
35
|
+
/** Number of retry attempts (default: 3) */
|
|
36
|
+
retry?: number;
|
|
37
|
+
/** Delay between retries in ms (default: 500) */
|
|
38
|
+
retryDelay?: number;
|
|
39
|
+
}
|
|
40
|
+
export interface PostBuildOptions {
|
|
41
|
+
/** Project root directory (default: process.cwd()) */
|
|
42
|
+
cwd?: string;
|
|
43
|
+
/** Prerender configuration (default: { routes: ['/'], crawlLinks: true }) */
|
|
44
|
+
prerender?: PrerenderConfig | false;
|
|
45
|
+
/** Port for prerender server (default: 13172) */
|
|
46
|
+
prerenderPort?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Run all post-build tasks.
|
|
50
|
+
*
|
|
51
|
+
* Usage in consumer's post-build.mjs:
|
|
52
|
+
* ```js
|
|
53
|
+
* import { runPostBuild } from '@useavalon/avalon/post-build';
|
|
54
|
+
* await runPostBuild();
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function runPostBuild(options?: PostBuildOptions): Promise<void>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import{copyFileSync as e,cpSync as t,existsSync as n,mkdirSync as r,readdirSync as i,readFileSync as a,unlinkSync as o,writeFileSync as s}from"node:fs";import{dirname as c,join as l,relative as u}from"node:path";function d(e,t,r=[]){if(!n(e))return r;for(let n of i(e,{withFileTypes:!0})){let i=l(e,n.name);n.isDirectory()?d(i,t,r):t(n.name)&&r.push(i)}return r}function f(e){if(!n(e))return!1;let t=a(e,`utf-8`);return t.length<500&&!t.includes(`data-framework`)&&!t.includes(`avalon`)}function p(e){for(let t of[`dist/index.html`,`.netlify/functions-internal/server/public/index.html`]){let r=l(e,t);f(r)?(o(r),console.log(`[cleanup] Removed stale Vite template ${t}`)):n(r)&&console.log(`[cleanup] Preserved prerendered ${t}`)}}function m(e,t,r){if(!n(e))return;let i=[l(t,`assets`),l(r,`.netlify`,`functions-internal`,`server`,`public`,`assets`),l(r,`.output`,`public`,`assets`)].find(e=>n(e));if(!i)return;let o=d(i,e=>e.endsWith(`.css`)).map(e=>`/assets${e.substring(i.length).replaceAll(`\\`,`/`)}`);console.log(`[patch] Found ${o.length} CSS files in ${i}`);let c=a(e,`utf-8`);for(let{re:t,hrefRe:n,q:r}of[{re:/css:\[(\{href:`[^`]+`\}(?:,\{href:`[^`]+`\})*)\]/,hrefRe:/href:`([^`]+)`/g,q:"`"},{re:/css:\[(\{href:"[^"]+"\}(?:,\{href:"[^"]+"\})*)\]/,hrefRe:/href:"([^"]+)"/g,q:`"`}]){let i=t.exec(c);if(!i)continue;let a=new Set([...i[1].matchAll(n)].map(e=>e[1])),l=o.filter(e=>!a.has(e));if(l.length===0){console.log(`[patch] All CSS already included`);return}let u=l.map(e=>`{href:${r}${e}${r}}`).join(`,`);c=c.replace(i[0],`css:[${i[1]},${u}]`),s(e,c),console.log(`[patch] ✅ Added ${l.length} CSS files to SSR bundle`);return}console.warn(`[patch] Could not find CSS array in SSR bundle`)}function h(t,o){let c=[l(t,`node_modules`,`.nitro`,`vite`,`services`,`ssr`,`assets`)];for(let u of c){if(!n(u))continue;let c=i(u).filter(e=>e.endsWith(`.css`));if(c.length===0)continue;let d=[l(o,`assets`),l(t,`.output`,`public`,`assets`),l(t,`.netlify`,`functions-internal`,`server`,`public`,`assets`)];for(let t of d){r(t,{recursive:!0});for(let n of c)e(l(u,n),l(t,`ssr-${n}`))}let f=c[0],p=a(l(u,f)).length,m=`ssr-${f}`;console.log(`[ssr-css] Copied SSR CSS → /assets/${m} (${p} bytes)`);let h=[l(t,`.output`,`server`,`index.mjs`),l(t,`.netlify`,`functions-internal`,`server`,`main.mjs`)];for(let e of h){if(!n(e))continue;let t=a(e,`utf-8`),r=`/assets/${m}`;if(t.includes(r))continue;let i=/"\/assets\/[^"]+\.css":\{type:`text\/css[^}]+\}/.exec(t);if(i){let n=new Date().toISOString(),a=`,"${r}":{type:\`text/css; charset=utf-8\`,etag:\`${`"${p.toString(16)}-ssr"`}\`,mtime:\`${n}\`,size:${p},path:\`../public/assets/${m}\`}`;t=t.replace(i[0],i[0]+a),s(e,t),console.log(`[ssr-css] ✅ Patched asset manifest in ${e}`)}}return}console.log(`[ssr-css] No SSR CSS files found`)}function g(t){let i=[l(t,`assets`,`islands`),l(t,`islands`)].find(e=>n(e));if(!i)return;let o=d(i,e=>e.endsWith(`.js`)&&!e.endsWith(`.js.map`));if(o.length===0)return;let f=o.filter(e=>/-[A-Za-z0-9_-]{6,12}\.js$/.test(e));if(f.length===0){console.log(`[redirects] ${o.length} island(s) found with clean paths — no redirects needed`);return}let p=[];for(let n of f){let i=`/${u(t,n).replaceAll(`\\`,`/`)}`,a=i.replace(`/assets/`,`/`).replace(/-[A-Za-z0-9_-]{6,12}\.js$/,`.js`);p.push(`${a} ${i} 200`);let o=l(t,a.slice(1));r(c(o),{recursive:!0}),e(n,o)}let m=l(t,`_redirects`),h=n(m)?a(m,`utf-8`):``;h=h.replaceAll(/# Island JS path rewrites[^\n]*\n(?:\/islands\/[^\n]*\n)*/g,``).trim();let g=`# Island JS path rewrites (generated by Avalon post-build)
|
|
2
|
+
`;s(m,h?`${h}\n\n${g}${p.join(`
|
|
3
|
+
`)}\n`:`${g+p.join(`
|
|
4
|
+
`)}\n`),console.log(`[redirects] ✅ Wrote ${p.length} island redirects + local copies`)}function _(t,a){let o=[l(t,`.output`,`public`,`_adapters`),l(a,`_adapters`)];for(let t of o){if(!n(t))continue;let o=i(t).filter(e=>e.endsWith(`.js`));if(o.length===0)continue;let s=l(a,`_adapters`);r(s,{recursive:!0});for(let n of o){let r=l(t,n),i=l(s,n);r!==i&&e(r,i)}console.log(`[adapters] ✅ Copied ${o.length} framework adapters`);return}console.log(`[adapters] No _adapters/ directory found`)}function v(e){let r=l(e,`.netlify`,`functions-internal`,`server`);if(!n(r))return;let i=[l(e,`.netlify`,`v1`,`functions`,`server`),l(e,`netlify`,`functions`,`server`)];for(let n of i){t(r,n,{recursive:!0,force:!0});let i=n.substring(e.length).replaceAll(`\\`,`/`);console.log(`[netlify-fn] ✅ Copied server function to ${i}/`)}}function y(e){let t=a(e,`utf-8`);return t.includes(`path: "/*"`)||t.includes("path:`/*`")}function b(e,t,r){let i=l(c(e),`_prerender-server.mjs`),a=``,o=[l(r,`node_modules`,`urlpattern-polyfill`,`index.js`),l(r,`node_modules`,`urlpattern-polyfill`,`dist`,`urlpattern.js`)].find(e=>n(e));return o&&(a=`import '${o.replaceAll(`\\`,`/`)}';`),s(i,`
|
|
5
|
+
${a}
|
|
6
|
+
import { createServer } from 'node:http';
|
|
7
|
+
import handler from './main.mjs';
|
|
8
|
+
const PORT = ${t};
|
|
9
|
+
const server = createServer(async (req, res) => {
|
|
10
|
+
try {
|
|
11
|
+
const url = new URL(req.url, 'http://localhost:' + PORT);
|
|
12
|
+
const headers = new Headers();
|
|
13
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
14
|
+
if (value) headers.set(key, Array.isArray(value) ? value.join(', ') : value);
|
|
15
|
+
}
|
|
16
|
+
const request = new Request(url.toString(), { method: req.method, headers });
|
|
17
|
+
const response = await handler(request);
|
|
18
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
19
|
+
const body = await response.text();
|
|
20
|
+
res.end(body);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.error('[prerender-wrapper] Error:', err);
|
|
23
|
+
res.writeHead(500);
|
|
24
|
+
res.end('Internal Server Error');
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
server.listen(PORT, '127.0.0.1', () => {
|
|
28
|
+
console.log('[prerender-wrapper] Listening on http://127.0.0.1:' + PORT);
|
|
29
|
+
});
|
|
30
|
+
`),i}function x(e){let t=[],n=/<a\s[^>]*href=["']([^"'#?]+)/gi,r=null;for(r=n.exec(e);r!==null;r=n.exec(e)){let e=r[1];e.startsWith(`/`)&&!e.startsWith(`//`)&&!e.startsWith(`/assets/`)&&!e.startsWith(`/islands/`)&&!e.startsWith(`/chunks/`)&&!e.startsWith(`/_`)&&!e.match(/\.\w{2,5}$/)&&t.push(e)}return[...new Set(t)]}async function S(t,i,d,f){let p=[l(t,`.output`,`server`,`index.mjs`),l(t,`.netlify`,`functions-internal`,`server`,`server.mjs`),l(t,`.netlify`,`v1`,`functions`,`server`,`server.mjs`)].find(e=>n(e));if(!p){console.log(`[prerender] No server entry found, skipping prerender`);return}let m=[l(t,`.output`,`public`),i].find(e=>n(e));if(!m){console.log(`[prerender] No output directory found, skipping prerender`);return}let h={routes:d.routes??[`/`],crawlLinks:d.crawlLinks??!0,ignore:d.ignore??[],failOnError:d.failOnError??!1,concurrency:d.concurrency??4,autoSubfolderIndex:d.autoSubfolderIndex??!0,retry:d.retry??3,retryDelay:d.retryDelay??500},g=`http://localhost:${f}`,_=y(p),v=p;if(_){let e=l(c(p),`main.mjs`);if(!n(e)){console.error(`[prerender] Netlify handler detected but main.mjs not found`);return}v=b(e,f,t),console.log(`[prerender] Netlify handler detected — using wrapper`)}{let e=[p,l(c(p),`main.mjs`)].filter(e=>n(e));for(let n of e){let e=a(n,`utf-8`),r=/"\/[^"]*\.html":\{[^}]+\},?/g,i=e.length;e=e.replaceAll(r,``),i!==e.length&&(s(n,e),console.log(`[prerender] Patched ${u(t,n)}: removed HTML asset entries`))}}console.log(`[prerender] Starting prerender with server: ${u(t,v)}`);let{spawn:S}=await import(`node:child_process`),C;try{C=S(`node`,[v],{env:{...process.env,PORT:String(f),NITRO_PORT:String(f),HOST:`127.0.0.1`,NITRO_HOST:`127.0.0.1`,NODE_ENV:`production`},stdio:[`ignore`,`pipe`,`pipe`]}),C.stdout?.on(`data`,e=>{let t=e.toString().trim();t&&console.log(`[prerender:server] ${t}`)}),C.stderr?.on(`data`,e=>{let t=e.toString().trim();t&&console.error(`[prerender:server:err] ${t}`)});let e=Date.now(),t=!1;for(;Date.now()-e<15e3;){try{let e=await fetch(`${g}/`);if(e.ok||e.status<500){t=!0;break}}catch{}await new Promise(e=>setTimeout(e,200))}if(!t)throw Error(`Server did not become ready within 15s`);console.log(`[prerender] Server ready at ${g}`)}catch(e){C?.kill(`SIGKILL`),console.error(`[prerender] Failed to start server:`,e.message);return}let w=new Set,T=[...h.routes],E=[],D=[];function O(e){for(let t of h.ignore)if(typeof t==`string`&&e===t||typeof t==`string`&&t.endsWith(`/**`)&&e.startsWith(t.slice(0,-2)))return!0;return!1}try{for(;T.length>0;){let e=T.splice(0,h.concurrency);await Promise.all(e.map(async e=>{let t=e.endsWith(`/`)&&e!==`/`?e.slice(0,-1):e;if(w.has(t)||(w.add(t),O(t)))return;let n=null;for(let e=1;e<=h.retry;e++)try{let e=await fetch(`${g}${t}`);n={html:await e.text(),status:e.status};break}catch{e<h.retry&&await new Promise(e=>setTimeout(e,h.retryDelay))}if(!n){D.push({route:t,error:`Failed after ${h.retry} attempts`});return}if(n.status>=400){D.push({route:t,error:`Returned ${n.status}`});return}let i=h.autoSubfolderIndex?l(t,`index.html`):`${t}.html`,a=l(m,i);if(r(c(a),{recursive:!0}),s(a,n.html.replace(`<!DOCTYPE html>`,`<!DOCTYPE html>
|
|
31
|
+
<!-- SSG: prerendered at build time -->`)),E.push(t),console.log(`[prerender] ✅ ${t} → ${i}`),h.crawlLinks)for(let e of x(n.html)){let t=e.endsWith(`/`)&&e!==`/`?e.slice(0,-1):e;!w.has(t)&&!O(t)&&T.push(t)}}))}}finally{console.log(`[prerender] Shutting down server...`),C?.kill(`SIGKILL`)}if(console.log(`[prerender] Done: ${E.length} page(s) prerendered`+(D.length>0?`, ${D.length} error(s)`:``)),_){let e=l(c(p),`_prerender-server.mjs`);n(e)&&(o(e),console.log(`[prerender] Cleaned up wrapper script`))}if(E.length>0){let a=[i,l(t,`.netlify`,`functions-internal`,`server`,`public`),l(t,`.netlify`,`v1`,`functions`,`server`,`public`)].filter(e=>e!==m&&n(c(e)));for(let t of a)for(let i of E){let a=l(i,`index.html`),o=l(m,a),s=l(t,a);n(o)&&(r(c(s),{recursive:!0}),e(o,s))}a.length>0&&console.log(`[prerender] Copied prerendered HTML to ${a.length} additional output dir(s)`)}}export async function runPostBuild(e={}){let t=e.cwd??process.cwd(),r=l(t,`dist`),i=e.prerenderPort??13172;p(t),h(t,r);for(let e of[l(t,`.netlify`,`functions-internal`,`server`,`_ssr`,`ssr.mjs`),l(t,`.netlify`,`v1`,`functions`,`server`,`_ssr`,`ssr.mjs`),l(t,`.output`,`server`,`_ssr`,`ssr.mjs`)])n(e)&&(console.log(`[patch] Patching ${e}`),m(e,r,t));g(r),_(t,r),v(t),e.prerender!==!1&&await S(t,r,e.prerender??{},i),console.log(`[post-build] ✅ Complete`)}
|
|
@@ -23,9 +23,9 @@ export declare const ScriptConfigSchema: z.ZodUnion<readonly [z.ZodString, z.Zod
|
|
|
23
23
|
integrity: z.ZodOptional<z.ZodString>;
|
|
24
24
|
nomodule: z.ZodOptional<z.ZodBoolean>;
|
|
25
25
|
referrerpolicy: z.ZodOptional<z.ZodEnum<{
|
|
26
|
+
origin: "origin";
|
|
26
27
|
"no-referrer": "no-referrer";
|
|
27
28
|
"no-referrer-when-downgrade": "no-referrer-when-downgrade";
|
|
28
|
-
origin: "origin";
|
|
29
29
|
"origin-when-cross-origin": "origin-when-cross-origin";
|
|
30
30
|
"same-origin": "same-origin";
|
|
31
31
|
"strict-origin": "strict-origin";
|
|
@@ -53,9 +53,9 @@ export declare const RenderOptionsSchema: z.ZodObject<{
|
|
|
53
53
|
integrity: z.ZodOptional<z.ZodString>;
|
|
54
54
|
nomodule: z.ZodOptional<z.ZodBoolean>;
|
|
55
55
|
referrerpolicy: z.ZodOptional<z.ZodEnum<{
|
|
56
|
+
origin: "origin";
|
|
56
57
|
"no-referrer": "no-referrer";
|
|
57
58
|
"no-referrer-when-downgrade": "no-referrer-when-downgrade";
|
|
58
|
-
origin: "origin";
|
|
59
59
|
"origin-when-cross-origin": "origin-when-cross-origin";
|
|
60
60
|
"same-origin": "same-origin";
|
|
61
61
|
"strict-origin": "strict-origin";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
import { z } from "zod";
|
|
3
3
|
/**
|
|
4
4
|
* Layout Context Schema - Contains request information and state for layout processing
|
|
5
5
|
*/
|
|
@@ -132,10 +132,10 @@ export declare const LayoutErrorBoundaryPropsSchema: z.ZodObject<{
|
|
|
132
132
|
*/
|
|
133
133
|
export declare const ErrorRecoveryStrategySchema: z.ZodObject<{
|
|
134
134
|
type: z.ZodEnum<{
|
|
135
|
-
redirect: "redirect";
|
|
136
|
-
skip: "skip";
|
|
137
135
|
fallback: "fallback";
|
|
138
136
|
retry: "retry";
|
|
137
|
+
skip: "skip";
|
|
138
|
+
redirect: "redirect";
|
|
139
139
|
}>;
|
|
140
140
|
maxRetries: z.ZodOptional<z.ZodNumber>;
|
|
141
141
|
fallbackComponent: z.ZodOptional<z.ZodAny>;
|
|
@@ -3,7 +3,7 @@ export declare const routingValidators: {
|
|
|
3
3
|
readonly fileSystemRoute: (data: unknown) => {
|
|
4
4
|
pattern: any;
|
|
5
5
|
filePath: string;
|
|
6
|
-
routeType: "
|
|
6
|
+
routeType: "group" | "static" | "index" | "dynamic" | "catch-all";
|
|
7
7
|
dynamicSegments: string[];
|
|
8
8
|
priority: number;
|
|
9
9
|
isPrivate: boolean;
|
|
@@ -93,7 +93,7 @@ export declare const safeRoutingValidators: {
|
|
|
93
93
|
readonly fileSystemRoute: (data: unknown) => import("../index.ts").ValidationResult<{
|
|
94
94
|
pattern: any;
|
|
95
95
|
filePath: string;
|
|
96
|
-
routeType: "
|
|
96
|
+
routeType: "group" | "static" | "index" | "dynamic" | "catch-all";
|
|
97
97
|
dynamicSegments: string[];
|
|
98
98
|
priority: number;
|
|
99
99
|
isPrivate: boolean;
|
|
@@ -4,9 +4,9 @@ import type { ComponentType } from 'preact/compat';
|
|
|
4
4
|
* Route Type Schema - Defines the different types of routes supported
|
|
5
5
|
*/
|
|
6
6
|
export declare const RouteTypeSchema: z.ZodEnum<{
|
|
7
|
+
group: "group";
|
|
7
8
|
static: "static";
|
|
8
9
|
index: "index";
|
|
9
|
-
group: "group";
|
|
10
10
|
dynamic: "dynamic";
|
|
11
11
|
"catch-all": "catch-all";
|
|
12
12
|
}>;
|
|
@@ -17,9 +17,9 @@ export declare const FileSystemRouteSchema: z.ZodObject<{
|
|
|
17
17
|
pattern: z.ZodAny;
|
|
18
18
|
filePath: z.ZodString;
|
|
19
19
|
routeType: z.ZodEnum<{
|
|
20
|
+
group: "group";
|
|
20
21
|
static: "static";
|
|
21
22
|
index: "index";
|
|
22
|
-
group: "group";
|
|
23
23
|
dynamic: "dynamic";
|
|
24
24
|
"catch-all": "catch-all";
|
|
25
25
|
}>;
|
|
@@ -266,9 +266,9 @@ export declare const RouteHandlerSchema: z.ZodObject<{
|
|
|
266
266
|
metadata: z.ZodObject<{
|
|
267
267
|
filePath: z.ZodString;
|
|
268
268
|
routeType: z.ZodEnum<{
|
|
269
|
+
group: "group";
|
|
269
270
|
static: "static";
|
|
270
271
|
index: "index";
|
|
271
|
-
group: "group";
|
|
272
272
|
dynamic: "dynamic";
|
|
273
273
|
"catch-all": "catch-all";
|
|
274
274
|
}>;
|
|
@@ -284,9 +284,9 @@ export declare const RouteCacheEntrySchema: z.ZodObject<{
|
|
|
284
284
|
pattern: z.ZodAny;
|
|
285
285
|
filePath: z.ZodString;
|
|
286
286
|
routeType: z.ZodEnum<{
|
|
287
|
+
group: "group";
|
|
287
288
|
static: "static";
|
|
288
289
|
index: "index";
|
|
289
|
-
group: "group";
|
|
290
290
|
dynamic: "dynamic";
|
|
291
291
|
"catch-all": "catch-all";
|
|
292
292
|
}>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Avalon type definitions.
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
4
|
* Include this in your tsconfig.json `types` array to get island prop support:
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
6
|
* ```json
|
|
7
7
|
* {
|
|
8
8
|
* "compilerOptions": {
|
|
@@ -13,10 +13,13 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Re-export island prop types
|
|
16
|
-
export * from
|
|
16
|
+
export * from "./island-prop.d.ts";
|
|
17
17
|
|
|
18
18
|
// Import JSX augmentations (side-effect import for type augmentation)
|
|
19
|
-
import
|
|
19
|
+
import "./island-jsx.d.ts";
|
|
20
20
|
|
|
21
21
|
// Import image type declarations
|
|
22
|
-
import
|
|
22
|
+
import "./image.d.ts";
|
|
23
|
+
|
|
24
|
+
// Import virtual module declarations (virtual:avalon/*)
|
|
25
|
+
import "./virtual-modules.d.ts";
|
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export interface IslandDirective {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
/** Hydration condition (built-in or custom directive name) */
|
|
15
|
+
condition?:
|
|
16
|
+
| "on:visible"
|
|
17
|
+
| "on:interaction"
|
|
18
|
+
| "on:idle"
|
|
19
|
+
| "on:client"
|
|
20
|
+
| `media:${string}`
|
|
21
|
+
| `on:${string}`;
|
|
22
|
+
/** Optional argument passed to custom hydration directives */
|
|
23
|
+
conditionArg?: string;
|
|
24
|
+
/** Force SSR-only rendering without client hydration */
|
|
25
|
+
ssrOnly?: boolean;
|
|
26
|
+
/** Whether to render server-side (default: true) */
|
|
27
|
+
ssr?: boolean;
|
|
22
28
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
export type {
|
|
4
|
-
export type
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
1
|
+
import type { ComponentChildren, ComponentType } from "preact";
|
|
2
|
+
import type { ErrorRecoveryStrategy, IslandState, LayoutConfig, LayoutContext, LayoutDiscoveryOptions, LayoutErrorBoundaryProps, LayoutErrorInfo, LayoutHandler, LayoutLoader, LayoutProps, LayoutRoute, LayoutRule, PersistentIslandProps, ResolvedLayout, RouteInfo, StreamingComponent, StreamingLayoutProps } from "../schemas/layout.ts";
|
|
3
|
+
export type { ComponentChildren, ComponentType } from "preact";
|
|
4
|
+
export { createEnhancedLayoutResolver, EnhancedLayoutResolver, type EnhancedLayoutResolverOptions, EnhancedLayoutResolverUtils, } from "../core/layout/enhanced-layout-resolver.ts";
|
|
5
|
+
export { type CacheConfig, type CacheEntry, type CacheStats, LayoutCacheManager, } from "../core/layout/layout-cache-manager.ts";
|
|
6
|
+
export { LayoutComposer } from "../core/layout/layout-composer.ts";
|
|
7
|
+
export { LayoutDataLoader } from "../core/layout/layout-data-loader.ts";
|
|
8
|
+
export { LayoutDiscovery } from "../core/layout/layout-discovery.ts";
|
|
9
|
+
export { LayoutMatcher as LayoutMatcherClass } from "../core/layout/layout-matcher.ts";
|
|
10
|
+
export type { EnhancedLayoutContext, ErrorRecoveryStrategy, IslandState, IslandStateClearer, IslandStateLoader, IslandStateSaver, LayoutCache, LayoutConfig, LayoutContext, LayoutData, LayoutDiscoveryOptions, LayoutErrorBoundaryProps, LayoutErrorHandler, LayoutErrorInfo, LayoutFallbackRenderer, LayoutHandler, LayoutLoader, LayoutMatcherFunction, LayoutProps, LayoutRetryFunction, LayoutRoute, LayoutRule, PersistentIslandContext, PersistentIslandProps, ResolvedLayout, RouteInfo, StreamingComponent, StreamingLayoutProps, StreamingReadyCheck, } from "../schemas/layout.ts";
|
|
11
11
|
/**
|
|
12
12
|
* Layout Discovery Interface
|
|
13
13
|
*/
|
|
@@ -151,7 +151,7 @@ export interface LayoutDebugInfo {
|
|
|
151
151
|
size: number;
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
|
-
export type LayoutEventType =
|
|
154
|
+
export type LayoutEventType = "layout-discovered" | "layout-loaded" | "layout-rendered" | "layout-error" | "layout-cached" | "island-state-saved" | "island-state-loaded" | "streaming-started" | "streaming-completed";
|
|
155
155
|
export interface LayoutEventData {
|
|
156
156
|
type: LayoutEventType;
|
|
157
157
|
timestamp: number;
|
package/dist/src/types/layout.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{createEnhancedLayoutResolver,EnhancedLayoutResolver,EnhancedLayoutResolverUtils}from"../core/layout/enhanced-layout-resolver.js";export{LayoutCacheManager}from"../core/layout/layout-cache-manager.js";export{LayoutComposer}from"../core/layout/layout-composer.js";export{LayoutDataLoader}from"../core/layout/layout-data-loader.js";export{LayoutDiscovery}from"../core/layout/layout-discovery.js";export{LayoutMatcher as LayoutMatcherClass}from"../core/layout/layout-matcher.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for Avalon virtual modules.
|
|
3
|
+
*
|
|
4
|
+
* These modules are resolved at build time by Avalon's Vite plugin.
|
|
5
|
+
* Including `@useavalon/avalon/types` in your tsconfig `types` array
|
|
6
|
+
* makes these declarations available project-wide.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare module "virtual:avalon/page-loader" {
|
|
10
|
+
export function loadPage(
|
|
11
|
+
pathname: string,
|
|
12
|
+
): { default: unknown; metadata?: Record<string, unknown> } | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
declare module "virtual:avalon/config" {
|
|
16
|
+
const config: {
|
|
17
|
+
streaming: boolean;
|
|
18
|
+
pagesDir: string;
|
|
19
|
+
layoutsDir: string;
|
|
20
|
+
isDev: boolean;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
};
|
|
23
|
+
export default config;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
declare module "virtual:avalon/layouts" {
|
|
27
|
+
import type { PageModule, NitroRenderContext } from "@useavalon/avalon/nitro/types";
|
|
28
|
+
export function wrapWithLayouts(
|
|
29
|
+
pageHtml: string,
|
|
30
|
+
pageModule: PageModule,
|
|
31
|
+
context: NitroRenderContext,
|
|
32
|
+
injectAssets?: (html: string) => string,
|
|
33
|
+
): Promise<string>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module "virtual:avalon/assets" {
|
|
37
|
+
export function injectAssets(html: string): string;
|
|
38
|
+
export const clientAssets: {
|
|
39
|
+
css: Array<{ href: string; [key: string]: string }>;
|
|
40
|
+
js: Array<{ href: string; [key: string]: string }>;
|
|
41
|
+
entry: string;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
declare module "virtual:avalon/renderer" {
|
|
46
|
+
const handler: {
|
|
47
|
+
(event: unknown): Promise<Response>;
|
|
48
|
+
fetch(request: Request): Promise<Response>;
|
|
49
|
+
};
|
|
50
|
+
export default handler;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare module "virtual:avalon/client-entry" {
|
|
54
|
+
// Side-effect-only module: imports hydration runtime, global CSS, and layout CSS.
|
|
55
|
+
// No exports — just import it as your client entry point.
|
|
56
|
+
}
|
|
@@ -15,6 +15,10 @@ export declare const VIRTUAL_MODULE_IDS: {
|
|
|
15
15
|
readonly ISLAND_MANIFEST: "virtual:avalon/island-manifest";
|
|
16
16
|
readonly RUNTIME_CONFIG: "virtual:avalon/runtime-config";
|
|
17
17
|
readonly CONFIG: "virtual:avalon/config";
|
|
18
|
+
readonly LAYOUTS: "virtual:avalon/layouts";
|
|
19
|
+
readonly ASSETS: "virtual:avalon/assets";
|
|
20
|
+
readonly RENDERER: "virtual:avalon/renderer";
|
|
21
|
+
readonly CLIENT_ENTRY: "virtual:avalon/client-entry";
|
|
18
22
|
};
|
|
19
23
|
export declare const RESOLVED_VIRTUAL_IDS: {
|
|
20
24
|
readonly PAGE_ROUTES: string;
|
|
@@ -22,6 +26,10 @@ export declare const RESOLVED_VIRTUAL_IDS: {
|
|
|
22
26
|
readonly ISLAND_MANIFEST: string;
|
|
23
27
|
readonly RUNTIME_CONFIG: string;
|
|
24
28
|
readonly CONFIG: string;
|
|
29
|
+
readonly LAYOUTS: string;
|
|
30
|
+
readonly ASSETS: string;
|
|
31
|
+
readonly RENDERER: string;
|
|
32
|
+
readonly CLIENT_ENTRY: string;
|
|
25
33
|
};
|
|
26
34
|
export interface NitroIntegrationResult {
|
|
27
35
|
nitroOptions: NitroConfigOutput;
|