@useavalon/avalon 0.1.93 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +1 -1
- package/dist/src/actions/client.d.ts +21 -0
- package/dist/src/actions/client.js +1 -0
- package/dist/src/actions/define.d.ts +61 -0
- package/dist/src/actions/define.js +1 -0
- package/dist/src/actions/endpoint.d.ts +25 -0
- package/dist/src/actions/endpoint.js +1 -0
- package/dist/src/actions/registry.d.ts +21 -0
- package/dist/src/actions/registry.js +1 -0
- package/dist/src/actions/route.d.ts +12 -0
- package/dist/src/actions/route.js +1 -0
- package/dist/src/actions/serialization.d.ts +39 -0
- package/dist/src/actions/serialization.js +1 -0
- package/dist/src/actions/types.d.ts +100 -0
- package/dist/src/actions/types.js +1 -0
- package/dist/src/build/actions-types-generator.d.ts +39 -0
- package/dist/src/build/actions-types-generator.js +7 -0
- package/dist/src/schemas/api.d.ts +1 -1
- package/dist/src/schemas/routing/index.d.ts +4 -4
- package/dist/src/schemas/routing.d.ts +6 -6
- package/dist/src/types/virtual-modules.d.ts +14 -0
- package/dist/src/vite-plugin/nitro-integration.d.ts +2 -0
- package/dist/src/vite-plugin/nitro-integration.js +19 -17
- package/package.json +6 -2
package/dist/mod.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type { Integration, IntegrationConfig, RenderParams as IntegrationRenderParams, RenderResult, } from "@useavalon/core";
|
|
2
|
+
export { ACTION_ERROR_STATUS, ActionError, type ActionErrorOptions, createActionClient, defineAction, isAction, isActionError, } from "./src/actions/define.ts";
|
|
3
|
+
export type { AcceptMode, Action, ActionClient, ActionClientOptions, ActionConfig, ActionContext, ActionErrorCode, ActionNamespace, ActionResult, SerializedActionError, } from "./src/actions/types.ts";
|
|
4
|
+
export type { ActionsTypeGenerationResult } from "./src/build/actions-types-generator.ts";
|
|
5
|
+
export { findActionsEntryInfo, generateActionTypes, getActionsWatchDir, } from "./src/build/actions-types-generator.ts";
|
|
2
6
|
export type { ExtendedIslandEntry, ExtendedIslandManifest, IslandEntry, IslandManifest, } from "./src/build/island-manifest.ts";
|
|
3
7
|
export { generateIslandManifest, getIslandBundlePath, loadIslandManifest, } from "./src/build/island-manifest.ts";
|
|
4
8
|
export type { IslandTypeGeneratorOptions, TypeGenerationResult, } from "./src/build/island-types-generator.ts";
|
package/dist/mod.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{generateIslandManifest,getIslandBundlePath,loadIslandManifest}from"./src/build/island-manifest.js";export{generateIslandTypes,watchAndGenerateTypes}from"./src/build/island-types-generator.js";export{mdxIslandTransform}from"./src/build/mdx-island-transform.js";export{pageIslandTransform}from"./src/build/page-island-transform.js";export{registry as integrationRegistry}from"./src/core/integrations/registry.js";export{registerBuiltinDirectives}from"./src/islands/builtin-directives.js";export{analyzeComponentFile,renderComponentSSROnly}from"./src/islands/component-analysis.js";export{addSvelteSSRCSS,clearSvelteComponentCSS,generateComponentScopeId,getSvelteComponentCSS,getSvelteSSRCSS,getSvelteSSRCSSForHead,getSvelteSSRCSSStats}from"./src/islands/css-utils.js";export{createIslandRegistry,createIslandResolver,createIslandValidator,createIslandWatcher,DEFAULT_DISCOVERY_CONFIG,discoverAllIslands,discoverIslandDirectories,discoverIslandsInDirectory,formatCircularDependency,formatValidationError,formatValidationResult,formatValidationWarning,getDefaultIslandsPath,getQualifiedIslandName,hasDefaultIslandsDirectory,ISLAND_FILE_EXTENSIONS,IslandRegistry,IslandResolver,IslandValidator,IslandWatcher,isIslandsDirectory,isSupportedIslandExtension,parseQualifiedIslandName,validateAllIslands}from"./src/islands/discovery/index.js";export{detectFramework,detectFrameworkFromSrc,resolveIslandPath}from"./src/islands/framework-detection.js";export{getDirective,getRegisteredDirectives,isCustomDirective,registerHydrationDirective,unregisterHydrationDirective}from"./src/islands/hydration-directives.js";export{DEFAULT_PRELOAD_FRAMEWORKS,detectAndLoadIntegration,detectFrameworksFromPageContent,loadIntegration,preloadIntegrations}from"./src/islands/integration-loader.js";export{default as Island,renderIsland}from"./src/islands/island.js";export{clearCache,clearIslandCache,configureCache,getCacheConfig,getCacheStats,invalidateCacheForFile,invalidateCacheForPath,logCacheStats}from"./src/islands/render-cache.js";export{renderToHtml}from"./src/render/ssr.js";export{createNitroCoordinationPlugin,createNitroIntegration,createVirtualModulesPlugin,getAvalonConfig,getViteDevServer,isDevelopmentMode,RESOLVED_VIRTUAL_IDS,VIRTUAL_MODULE_IDS}from"./src/vite-plugin/nitro-integration.js";export{avalon,getLayoutsDir,getNitroConfig,getPagesDir,getResolvedConfig,isNitroEnabled}from"./src/vite-plugin/plugin.js";export async function build(e){throw Error("avalon build() is not available in the published package. Use `vite build` or the Avalon CLI instead.")}export{IslandErrorBoundary,withIslandErrorBoundary}from"./src/components/IslandErrorBoundary.js";export{LayoutErrorBoundary}from"./src/components/LayoutErrorBoundary.js";export*from"./src/layout-system.js";export{clearDiscoveryCache,clearMiddlewareCache,discoverScopedMiddleware,executeScopedMiddleware,getContextValue,getMatchingMiddleware,getMiddlewareCacheSize,hasContextValue,invalidateMiddleware,setContextValue}from"./src/middleware/index.js";export{usePersistentState}from"./src/persistence/use-persistent-state.js";
|
|
1
|
+
export{ACTION_ERROR_STATUS,ActionError,createActionClient,defineAction,isAction,isActionError}from"./src/actions/define.js";export{findActionsEntryInfo,generateActionTypes,getActionsWatchDir}from"./src/build/actions-types-generator.js";export{generateIslandManifest,getIslandBundlePath,loadIslandManifest}from"./src/build/island-manifest.js";export{generateIslandTypes,watchAndGenerateTypes}from"./src/build/island-types-generator.js";export{mdxIslandTransform}from"./src/build/mdx-island-transform.js";export{pageIslandTransform}from"./src/build/page-island-transform.js";export{registry as integrationRegistry}from"./src/core/integrations/registry.js";export{registerBuiltinDirectives}from"./src/islands/builtin-directives.js";export{analyzeComponentFile,renderComponentSSROnly}from"./src/islands/component-analysis.js";export{addSvelteSSRCSS,clearSvelteComponentCSS,generateComponentScopeId,getSvelteComponentCSS,getSvelteSSRCSS,getSvelteSSRCSSForHead,getSvelteSSRCSSStats}from"./src/islands/css-utils.js";export{createIslandRegistry,createIslandResolver,createIslandValidator,createIslandWatcher,DEFAULT_DISCOVERY_CONFIG,discoverAllIslands,discoverIslandDirectories,discoverIslandsInDirectory,formatCircularDependency,formatValidationError,formatValidationResult,formatValidationWarning,getDefaultIslandsPath,getQualifiedIslandName,hasDefaultIslandsDirectory,ISLAND_FILE_EXTENSIONS,IslandRegistry,IslandResolver,IslandValidator,IslandWatcher,isIslandsDirectory,isSupportedIslandExtension,parseQualifiedIslandName,validateAllIslands}from"./src/islands/discovery/index.js";export{detectFramework,detectFrameworkFromSrc,resolveIslandPath}from"./src/islands/framework-detection.js";export{getDirective,getRegisteredDirectives,isCustomDirective,registerHydrationDirective,unregisterHydrationDirective}from"./src/islands/hydration-directives.js";export{DEFAULT_PRELOAD_FRAMEWORKS,detectAndLoadIntegration,detectFrameworksFromPageContent,loadIntegration,preloadIntegrations}from"./src/islands/integration-loader.js";export{default as Island,renderIsland}from"./src/islands/island.js";export{clearCache,clearIslandCache,configureCache,getCacheConfig,getCacheStats,invalidateCacheForFile,invalidateCacheForPath,logCacheStats}from"./src/islands/render-cache.js";export{renderToHtml}from"./src/render/ssr.js";export{createNitroCoordinationPlugin,createNitroIntegration,createVirtualModulesPlugin,getAvalonConfig,getViteDevServer,isDevelopmentMode,RESOLVED_VIRTUAL_IDS,VIRTUAL_MODULE_IDS}from"./src/vite-plugin/nitro-integration.js";export{avalon,getLayoutsDir,getNitroConfig,getPagesDir,getResolvedConfig,isNitroEnabled}from"./src/vite-plugin/plugin.js";export async function build(e){throw Error("avalon build() is not available in the published package. Use `vite build` or the Avalon CLI instead.")}export{IslandErrorBoundary,withIslandErrorBoundary}from"./src/components/IslandErrorBoundary.js";export{LayoutErrorBoundary}from"./src/components/LayoutErrorBoundary.js";export*from"./src/layout-system.js";export{clearDiscoveryCache,clearMiddlewareCache,discoverScopedMiddleware,executeScopedMiddleware,getContextValue,getMatchingMiddleware,getMiddlewareCacheSize,hasContextValue,invalidateMiddleware,setContextValue}from"./src/middleware/index.js";export{usePersistentState}from"./src/persistence/use-persistent-state.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — typed client proxy.
|
|
3
|
+
*
|
|
4
|
+
* `createActionClient<typeof server>()` returns a Proxy whose property accesses
|
|
5
|
+
* mirror the `server` export. Calling an action POSTs to `/_actions/<name>` and
|
|
6
|
+
* resolves to an {@link ActionResult}. The proxy never throws for action-level
|
|
7
|
+
* failures — network/parse errors also resolve to an error result.
|
|
8
|
+
*
|
|
9
|
+
* This module contains ONLY the fetch proxy (no handler source), so it is safe
|
|
10
|
+
* to include in the browser bundle.
|
|
11
|
+
*
|
|
12
|
+
* @module actions/client
|
|
13
|
+
*/
|
|
14
|
+
import type { ActionClient, ActionClientOptions } from "./types.ts";
|
|
15
|
+
/**
|
|
16
|
+
* Creates a typed client proxy for calling server actions.
|
|
17
|
+
*
|
|
18
|
+
* @typeParam T - The `server` export type (for inference).
|
|
19
|
+
* @param options - Optional base URL / custom fetch.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createActionClient<T = Record<string, never>>(options?: ActionClientOptions): ActionClient<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ActionError as e}from"./define.js";import{parseClientResult as t}from"./serialization.js";async function n(n,r,i,a){let o=`${n}/${i}`;try{let e,n={};return a instanceof FormData?e=a:a!==void 0&&(e=JSON.stringify(a),n[`Content-Type`]=`application/json`),await t(await r(o,{method:`POST`,body:e,headers:n}))}catch(t){return{data:void 0,error:new e({code:`INTERNAL_SERVER_ERROR`,message:t instanceof Error?t.message:`Request failed`,cause:t})}}}export function createActionClient(e={}){let t=(e.baseUrl??`/_actions`).replace(/\/+$/,``),r=e.fetch??globalThis.fetch,i=e=>new Proxy((i=>n(t,r,e,i)),{get(t,n){return typeof n==`string`?i(e?`${e}.${n}`:n):void 0},apply(i,a,o){return n(t,r,e,o[0])}});return i(``)}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — public API surface.
|
|
3
|
+
*
|
|
4
|
+
* Exports `defineAction` for declaring type-safe server functions, the
|
|
5
|
+
* `ActionError` class and `isActionError` guard for structured failures, and
|
|
6
|
+
* `createActionClient` for an explicitly-typed client proxy.
|
|
7
|
+
*
|
|
8
|
+
* Importable as `@useavalon/avalon/actions`.
|
|
9
|
+
*
|
|
10
|
+
* @module actions/define
|
|
11
|
+
*/
|
|
12
|
+
import { type Action, type ActionConfig, type ActionErrorCode } from "./types.ts";
|
|
13
|
+
/** Maps each {@link ActionErrorCode} to its HTTP status code. */
|
|
14
|
+
export declare const ACTION_ERROR_STATUS: Record<ActionErrorCode, number>;
|
|
15
|
+
/** Options for constructing an {@link ActionError}. */
|
|
16
|
+
export interface ActionErrorOptions {
|
|
17
|
+
code: ActionErrorCode;
|
|
18
|
+
message?: string;
|
|
19
|
+
/** Per-field validation issues (for `BAD_REQUEST`). */
|
|
20
|
+
fields?: Record<string, string[]>;
|
|
21
|
+
/** Underlying cause. */
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A structured error thrown by (or returned from) an action. Carries a stable
|
|
26
|
+
* `code`, the derived HTTP `status`, and optional per-field validation issues.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ActionError extends Error {
|
|
29
|
+
readonly code: ActionErrorCode;
|
|
30
|
+
readonly status: number;
|
|
31
|
+
readonly fields?: Record<string, string[]>;
|
|
32
|
+
constructor(options: ActionErrorOptions);
|
|
33
|
+
/** Rebuilds an {@link ActionError} from a serialized wire format + status. */
|
|
34
|
+
static fromStatus(status: number, body: {
|
|
35
|
+
code?: string;
|
|
36
|
+
message?: string;
|
|
37
|
+
fields?: Record<string, string[]>;
|
|
38
|
+
} | undefined): ActionError;
|
|
39
|
+
}
|
|
40
|
+
/** Type guard for {@link ActionError}. */
|
|
41
|
+
export declare function isActionError(value: unknown): value is ActionError;
|
|
42
|
+
/**
|
|
43
|
+
* Defines a type-safe server action.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* export const server = {
|
|
48
|
+
* greet: defineAction({
|
|
49
|
+
* input: z.object({ name: z.string() }),
|
|
50
|
+
* handler: async ({ name }) => `Hello, ${name}!`,
|
|
51
|
+
* }),
|
|
52
|
+
* };
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @param config - The action's input schema, accept mode, and handler.
|
|
56
|
+
* @returns A branded {@link Action} for use under the `server` export.
|
|
57
|
+
*/
|
|
58
|
+
export declare function defineAction<TInput, TOutput>(config: ActionConfig<TInput, TOutput>): Action<TInput, Awaited<TOutput>>;
|
|
59
|
+
export { createActionClient } from "./client.ts";
|
|
60
|
+
export { isAction } from "./registry.ts";
|
|
61
|
+
export type { AcceptMode, Action, ActionClient, ActionClientOptions, ActionConfig, ActionContext, ActionErrorCode, ActionNamespace, ActionResult, SerializedActionError, } from "./types.ts";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ACTION_MARKER as e}from"./types.js";export const ACTION_ERROR_STATUS={BAD_REQUEST:400,UNAUTHORIZED:401,FORBIDDEN:403,NOT_FOUND:404,METHOD_NOT_ALLOWED:405,CONFLICT:409,UNSUPPORTED_MEDIA_TYPE:415,INTERNAL_SERVER_ERROR:500};const n=Object.fromEntries(Object.entries(ACTION_ERROR_STATUS).map(([e,t])=>[t,e]));export class ActionError extends Error{code;status;fields;constructor(e){super(e.message??i(e.code),{cause:e.cause}),this.name=`ActionError`,this.code=e.code,this.status=ACTION_ERROR_STATUS[e.code]??500,e.fields&&(this.fields=e.fields)}static fromStatus(e,t){return new ActionError({code:t?.code??n[e]??`INTERNAL_SERVER_ERROR`,message:t?.message,fields:t?.fields})}}function i(e){switch(e){case`BAD_REQUEST`:return`Bad request`;case`UNAUTHORIZED`:return`Unauthorized`;case`FORBIDDEN`:return`Forbidden`;case`NOT_FOUND`:return`Not found`;case`METHOD_NOT_ALLOWED`:return`Method not allowed`;case`CONFLICT`:return`Conflict`;case`UNSUPPORTED_MEDIA_TYPE`:return`Unsupported media type`;default:return`Internal server error`}}export function isActionError(e){return e instanceof ActionError}export function defineAction(t){let n=t.accept??`json`,r=async(e,n)=>await t.handler(e,n);return{[e]:!0,input:t.input,accept:n,handler:r}}export{createActionClient}from"./client.js";export{isAction}from"./registry.js";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — endpoint handler.
|
|
3
|
+
*
|
|
4
|
+
* Factory producing an H3/Nitro handler for `/_actions/:name`. Performs method
|
|
5
|
+
* guarding, action lookup, body parsing, Zod validation, handler execution, and
|
|
6
|
+
* error serialization — returning a JSON `{ data }` or `{ error }` response.
|
|
7
|
+
*
|
|
8
|
+
* @module actions/endpoint
|
|
9
|
+
*/
|
|
10
|
+
import type { H3Event } from "h3";
|
|
11
|
+
import type { Action } from "./types.ts";
|
|
12
|
+
/** Options for {@link defineActionHandler}. */
|
|
13
|
+
export interface ActionHandlerOptions {
|
|
14
|
+
/** The flattened action registry (name → Action). */
|
|
15
|
+
registry: Map<string, Action>;
|
|
16
|
+
/** Whether running in development (controls error message exposure). */
|
|
17
|
+
isDev?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates the action endpoint handler.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Registry and dev flag.
|
|
23
|
+
* @returns An async H3 handler returning a web `Response`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function defineActionHandler(options: ActionHandlerOptions): (event: H3Event) => Promise<Response>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ActionError as e}from"./define.js";import{getWebRequest as t,parseRequestInput as n,serializeError as r,zodIssuesToFields as i}from"./serialization.js";const a={"Content-Type":`application/json`};function o(e,t){return new Response(JSON.stringify(e),{status:t,headers:a})}export function defineActionHandler(t){let{registry:a,isDev:s=process.env.NODE_ENV!==`production`}=t;return async t=>{if(u(t).toUpperCase()!==`POST`)return o({error:{code:`METHOD_NOT_ALLOWED`,message:`Method not allowed`}},405);let l=d(t);if(!l)return o({error:{code:`BAD_REQUEST`,message:`Missing action name`}},400);let f=a.get(l);if(!f)return o({error:{code:`NOT_FOUND`,message:`Unknown action: ${l}`}},404);let p;try{p=await n(t,f.accept)}catch(e){let{status:t,body:n}=r(e,s);return o({error:n},t)}let m=p;if(f.input){let e=f.input.safeParse(p);if(!e.success)return o({error:{code:`BAD_REQUEST`,message:`Validation failed`,fields:i(e.error)}},400);m=e.data}let h=c(t);try{return o({data:await f.handler(m,h)},200)}catch(t){t instanceof e||console.error(`[actions] Handler error for`,l,t);let{status:n,body:i}=r(t,s);return o({error:i},n)}}}function c(e){let n=t(e),r=n?.headers??new Headers(e.node?.req?.headers??{}),i=r.get(`cookie`)??``;return{event:e,request:n,headers:r,cookies:{get:e=>l(i,e)}}}function l(e,t){if(e)for(let n of e.split(`;`)){let e=n.indexOf(`=`);if(e!==-1&&n.slice(0,e).trim()===t)return decodeURIComponent(n.slice(e+1).trim())}}function u(e){let n=t(e);return n?n.method:e.node?.req?.method??`GET`}function d(e){let t=e.context?.params;if(t?.name)return decodeURIComponent(String(t.name));let n=f(e),r=/\/_actions\/([^/?]+)/.exec(n);return r?decodeURIComponent(r[1]):void 0}function f(e){if(e.url)return e.url.pathname;let t=e.node?.req?.url;return t?new URL(t,`http://localhost`).pathname:`/`}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — registry.
|
|
3
|
+
*
|
|
4
|
+
* Flattens a `server` export (a tree of actions and namespace objects) into a
|
|
5
|
+
* flat lookup map keyed by dotted name (e.g. `user.like`).
|
|
6
|
+
*
|
|
7
|
+
* @module actions/registry
|
|
8
|
+
*/
|
|
9
|
+
import { type Action, type ActionNamespace } from "./types.ts";
|
|
10
|
+
/** Narrows an unknown value to an {@link Action} via the brand marker. */
|
|
11
|
+
export declare function isAction(value: unknown): value is Action;
|
|
12
|
+
/**
|
|
13
|
+
* Recursively flattens a `server` export into `Map<dottedName, Action>`.
|
|
14
|
+
*
|
|
15
|
+
* Nested plain objects become namespaces: `{ user: { like } }` yields the key
|
|
16
|
+
* `"user.like"`. Non-action, non-object values are ignored.
|
|
17
|
+
*
|
|
18
|
+
* @param server - The `server` export (or any sub-namespace).
|
|
19
|
+
* @param prefix - Internal accumulator for the dotted key path.
|
|
20
|
+
*/
|
|
21
|
+
export declare function flattenActions(server: ActionNamespace | undefined | null, prefix?: string): Map<string, Action>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ACTION_MARKER as e}from"./types.js";export function isAction(t){return typeof t==`object`&&!!t&&t[e]===!0}export function flattenActions(e,n=``){let r=new Map;if(!e||typeof e!=`object`)return r;for(let[i,a]of Object.entries(e)){let e=n?`${n}.${i}`:i;if(isAction(a))r.set(e,a);else if(a&&typeof a==`object`)for(let[t,n]of flattenActions(a,e))r.set(t,n)}return r}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — Nitro route entry.
|
|
3
|
+
*
|
|
4
|
+
* Registered as the handler for `/_actions/**` by the Avalon Nitro integration.
|
|
5
|
+
* It statically imports the actions manifest virtual module so the user's action
|
|
6
|
+
* handlers are bundled into the Nitro server output (mirroring the server-islands
|
|
7
|
+
* route — NO `@vite-ignore` dynamic imports).
|
|
8
|
+
*
|
|
9
|
+
* @module actions/route
|
|
10
|
+
*/
|
|
11
|
+
declare const handler: (event: import("h3").H3Event) => Promise<Response>;
|
|
12
|
+
export default handler;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{server as e}from"virtual:avalon-actions-manifest";import{defineActionHandler as t}from"./endpoint.js";import{flattenActions as n}from"./registry.js";const r=t({registry:n(e)});export default r;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — request/response serialization.
|
|
3
|
+
*
|
|
4
|
+
* Parses incoming request bodies (JSON or form) into action input, serializes
|
|
5
|
+
* errors into the wire format, and parses client-side responses into an
|
|
6
|
+
* {@link ActionResult}.
|
|
7
|
+
*
|
|
8
|
+
* @module actions/serialization
|
|
9
|
+
*/
|
|
10
|
+
import type { H3Event } from "h3";
|
|
11
|
+
import type { z } from "zod";
|
|
12
|
+
import type { AcceptMode, ActionResult, SerializedActionError } from "./types.ts";
|
|
13
|
+
/** Resolves the web-standard Request from an H3 event, when available. */
|
|
14
|
+
export declare function getWebRequest(event: H3Event): Request | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Parses the request body into raw action input.
|
|
17
|
+
*
|
|
18
|
+
* - `accept: "form"` (or a form content-type) → parses FormData / urlencoded.
|
|
19
|
+
* - otherwise → parses JSON (empty body → `undefined`).
|
|
20
|
+
*
|
|
21
|
+
* @throws {ActionError} `UNSUPPORTED_MEDIA_TYPE` / `BAD_REQUEST` on malformed bodies.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseRequestInput(event: H3Event, accept: AcceptMode): Promise<unknown>;
|
|
24
|
+
/** Builds the `fields` map from a Zod error's issues (Zod v4 uses `.issues`). */
|
|
25
|
+
export declare function zodIssuesToFields(error: z.ZodError): Record<string, string[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Serializes an error into the wire format and its HTTP status. Unexpected
|
|
28
|
+
* (non-ActionError) errors become `INTERNAL_SERVER_ERROR`; their message is
|
|
29
|
+
* included only in development.
|
|
30
|
+
*/
|
|
31
|
+
export declare function serializeError(err: unknown, isDev: boolean): {
|
|
32
|
+
status: number;
|
|
33
|
+
body: SerializedActionError;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Parses a fetch Response into an {@link ActionResult}. Used by the client
|
|
37
|
+
* proxy. Never throws — network/parse issues become an error result.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseClientResult<T>(response: Response): Promise<ActionResult<T>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ActionError as e}from"./define.js";export function getWebRequest(e){let t=e;return t.req&&typeof t.req.text==`function`?t.req:t.web?.request??t.request??void 0}function n(e){return new Promise((t,n)=>{let r=[];e.on(`data`,e=>r.push(e)),e.on(`end`,()=>t(Buffer.concat(r).toString(`utf8`))),e.on(`error`,n)})}function r(e){let t={};for(let n of new Set(e.keys())){let r=e.getAll(n);t[n]=r.length>1?r:r[0]}return t}function i(e){let t=new URLSearchParams(e),n={};for(let e of new Set(t.keys())){let r=t.getAll(e);n[e]=r.length>1?r:r[0]}return n}export async function parseRequestInput(n,a){let s=getWebRequest(n),c=(s?.headers.get(`content-type`)??n.node?.req?.headers?.[`content-type`]??``).toLowerCase();if(a===`form`||c.includes(`multipart/form-data`)||c.includes(`application/x-www-form-urlencoded`)){if(s&&c.includes(`multipart/form-data`))try{return r(await s.formData())}catch(t){throw new e({code:`BAD_REQUEST`,message:`Invalid form data`,cause:t})}return i(await o(n,s))}let l=await o(n,s);if(l)try{return JSON.parse(l)}catch(t){throw new e({code:`BAD_REQUEST`,message:`Invalid JSON body`,cause:t})}}async function o(e,t){if(t)return await t.text();let r=e.node?.req;if(r?.on)return await n(r);let i=e._body;return i===void 0?``:typeof i==`string`?i:JSON.stringify(i)}export function zodIssuesToFields(e){let t={};for(let n of e.issues){let e=n.path.length>0?n.path.join(`.`):`_`,r=t[e]??[];r.push(n.message),t[e]=r}return t}export function serializeError(t,n){return t instanceof e?{status:t.status,body:{code:t.code,message:t.message,...t.fields?{fields:t.fields}:{}}}:{status:500,body:{code:`INTERNAL_SERVER_ERROR`,message:n&&t instanceof Error?t.message:`Internal server error`}}}export async function parseClientResult(t){let n;try{n=await t.json()}catch{n=void 0}if(t.ok)return{data:n?.data,error:void 0};let r=n?.error;return{data:void 0,error:e.fromStatus(t.status,r)}}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — shared types.
|
|
3
|
+
*
|
|
4
|
+
* @module actions/types
|
|
5
|
+
*/
|
|
6
|
+
import type { H3Event } from "h3";
|
|
7
|
+
import type { z } from "zod";
|
|
8
|
+
/** Unique marker distinguishing an Action from a plain namespace object. */
|
|
9
|
+
export declare const ACTION_MARKER: unique symbol;
|
|
10
|
+
/**
|
|
11
|
+
* Stable error codes for action failures. Each maps to an HTTP status
|
|
12
|
+
* (see `ACTION_ERROR_STATUS` in `define.ts`).
|
|
13
|
+
*/
|
|
14
|
+
export type ActionErrorCode = "BAD_REQUEST" | "UNAUTHORIZED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_ALLOWED" | "CONFLICT" | "UNSUPPORTED_MEDIA_TYPE" | "INTERNAL_SERVER_ERROR";
|
|
15
|
+
/** How the endpoint parses the request body for an action. */
|
|
16
|
+
export type AcceptMode = "json" | "form";
|
|
17
|
+
/**
|
|
18
|
+
* Context passed to every action handler. Provides access to the underlying
|
|
19
|
+
* request so handlers can read cookies, headers, and perform auth checks.
|
|
20
|
+
*/
|
|
21
|
+
export interface ActionContext {
|
|
22
|
+
/** The raw Nitro/H3 event. */
|
|
23
|
+
event: H3Event;
|
|
24
|
+
/** The web-standard Request, when available on the event. */
|
|
25
|
+
request: Request | undefined;
|
|
26
|
+
/** Minimal cookie accessor (reads from the request `Cookie` header). */
|
|
27
|
+
cookies: {
|
|
28
|
+
get(name: string): string | undefined;
|
|
29
|
+
};
|
|
30
|
+
/** Request headers. */
|
|
31
|
+
headers: Headers;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Configuration accepted by {@link defineAction}.
|
|
35
|
+
*
|
|
36
|
+
* @typeParam TInput - The validated input type (inferred from `input`).
|
|
37
|
+
* @typeParam TOutput - The handler's return type.
|
|
38
|
+
*/
|
|
39
|
+
export interface ActionConfig<TInput, TOutput> {
|
|
40
|
+
/** Optional Zod schema validating the input before the handler runs. */
|
|
41
|
+
input?: z.ZodType<TInput>;
|
|
42
|
+
/** Body parsing mode. Defaults to `"json"`. */
|
|
43
|
+
accept?: AcceptMode;
|
|
44
|
+
/** The server-side handler. Receives validated input and a context. */
|
|
45
|
+
handler: (input: TInput, context: ActionContext) => TOutput | Promise<TOutput>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A defined server action. Branded with {@link ACTION_MARKER} so the registry
|
|
49
|
+
* builder can distinguish actions from namespace objects.
|
|
50
|
+
*/
|
|
51
|
+
export interface Action<TInput = unknown, TOutput = unknown> {
|
|
52
|
+
readonly [ACTION_MARKER]: true;
|
|
53
|
+
/** Optional Zod input schema. */
|
|
54
|
+
readonly input?: z.ZodType<TInput>;
|
|
55
|
+
/** Resolved accept mode. */
|
|
56
|
+
readonly accept: AcceptMode;
|
|
57
|
+
/** The handler (always returns a promise once normalized). */
|
|
58
|
+
readonly handler: (input: TInput, context: ActionContext) => Promise<TOutput>;
|
|
59
|
+
}
|
|
60
|
+
/** Wire format for a serialized error. */
|
|
61
|
+
export interface SerializedActionError {
|
|
62
|
+
code: ActionErrorCode;
|
|
63
|
+
message: string;
|
|
64
|
+
/** Per-field validation issues (present for `BAD_REQUEST` validation errors). */
|
|
65
|
+
fields?: Record<string, string[]>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Discriminated result returned by the client proxy. Exactly one of `data` /
|
|
69
|
+
* `error` is populated. The client never throws for action-level failures.
|
|
70
|
+
*/
|
|
71
|
+
export type ActionResult<TOutput, TError = SerializedActionError> = {
|
|
72
|
+
data: TOutput;
|
|
73
|
+
error: undefined;
|
|
74
|
+
} | {
|
|
75
|
+
data: undefined;
|
|
76
|
+
error: TError;
|
|
77
|
+
};
|
|
78
|
+
/** A tree of actions and namespaces, as exported under `server`. */
|
|
79
|
+
export interface ActionNamespace {
|
|
80
|
+
[key: string]: Action<any, any> | ActionNamespace;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Maps a `server` export to its client surface: each {@link Action} becomes an
|
|
84
|
+
* async callable returning an {@link ActionResult}; each namespace maps to a
|
|
85
|
+
* nested client.
|
|
86
|
+
*/
|
|
87
|
+
export type ActionClient<T> = {
|
|
88
|
+
[K in keyof T]: T[K] extends Action<infer I, infer O> ? undefined extends I ? (input?: I) => Promise<ActionResult<O>> : (input: I) => Promise<ActionResult<O>> : T[K] extends ActionNamespace ? ActionClient<T[K]> : never;
|
|
89
|
+
};
|
|
90
|
+
/** Options for {@link createActionClient}. */
|
|
91
|
+
export interface ActionClientOptions {
|
|
92
|
+
/**
|
|
93
|
+
* Base URL/path for the actions endpoint. Defaults to `/_actions`.
|
|
94
|
+
* Provide an absolute origin (e.g. `https://api.example.com/_actions`) for
|
|
95
|
+
* cross-origin usage.
|
|
96
|
+
*/
|
|
97
|
+
baseUrl?: string;
|
|
98
|
+
/** Optional custom fetch implementation (testing / non-browser runtimes). */
|
|
99
|
+
fetch?: typeof fetch;
|
|
100
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const ACTION_MARKER=Symbol.for(`avalon.action`);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Actions — type generation.
|
|
3
|
+
*
|
|
4
|
+
* Generates an ambient declaration that types the `virtual:avalon/actions`
|
|
5
|
+
* module against the project's `server` export, giving full input/output
|
|
6
|
+
* inference on the `actions` client proxy without manual wiring.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the island types generator: a pure utility that writes a `.d.ts`.
|
|
9
|
+
*
|
|
10
|
+
* @module build/actions-types-generator
|
|
11
|
+
*/
|
|
12
|
+
/** Result of an actions type-generation pass. */
|
|
13
|
+
export interface ActionsTypeGenerationResult {
|
|
14
|
+
/** Whether an actions entry was found and a declaration written. */
|
|
15
|
+
generated: boolean;
|
|
16
|
+
/** The absolute path of the written declaration, when generated. */
|
|
17
|
+
outputPath?: string;
|
|
18
|
+
/** The actions entry directory base (`app` or `src`), when found. */
|
|
19
|
+
base?: "app" | "src";
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Locates the actions entry under the project root. Returns the base directory
|
|
23
|
+
* (`app`/`src`) and the entry filename (e.g. `index.ts`), or `undefined`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findActionsEntryInfo(projectRoot: string): {
|
|
26
|
+
base: "app" | "src";
|
|
27
|
+
file: string;
|
|
28
|
+
} | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Generates `<base>/avalon-actions.d.ts` typing `virtual:avalon/actions` against
|
|
31
|
+
* the project's `server` export. No-op (returns `{ generated: false }`) when no
|
|
32
|
+
* actions entry exists — the loose ambient declaration in `virtual-modules.d.ts`
|
|
33
|
+
* covers that case without producing spurious type errors.
|
|
34
|
+
*
|
|
35
|
+
* @param projectRoot - Absolute path to the project root.
|
|
36
|
+
*/
|
|
37
|
+
export declare function generateActionTypes(projectRoot: string): ActionsTypeGenerationResult;
|
|
38
|
+
/** Returns the directory that should be watched for actions entry changes. */
|
|
39
|
+
export declare function getActionsWatchDir(projectRoot: string): string | undefined;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import{dirname as r,join as i}from"node:path";const a=[`ts`,`tsx`,`js`,`jsx`,`mts`,`mjs`];export function findActionsEntryInfo(t){for(let n of[`app`,`src`])for(let r of a){let a=`index.${r}`;if(e(i(t,n,`actions`,a)))return{base:n,file:a}}}export function generateActionTypes(r){let a=findActionsEntryInfo(r);if(!a)return{generated:!1};let o=i(r,a.base,`avalon-actions.d.ts`),s=`// AUTO-GENERATED by Avalon — do not edit. Types \`virtual:avalon/actions\`.
|
|
2
|
+
declare module "virtual:avalon/actions" {
|
|
3
|
+
type Server = (typeof import("./actions/index"))["server"];
|
|
4
|
+
export const actions: import("@useavalon/avalon/actions").ActionClient<Server>;
|
|
5
|
+
export default actions;
|
|
6
|
+
}
|
|
7
|
+
`;try{if(e(o)&&t(o,`utf8`)===s)return{generated:!0,outputPath:o,base:a.base}}catch{}try{n(o,s,`utf8`)}catch{return{generated:!1}}return{generated:!0,outputPath:o,base:a.base}}export function getActionsWatchDir(e){let t=findActionsEntryInfo(e);if(t)return r(i(e,t.base,`actions`,t.file))}
|
|
@@ -53,7 +53,7 @@ export declare const routingValidators: {
|
|
|
53
53
|
siteName?: string;
|
|
54
54
|
};
|
|
55
55
|
twitter?: {
|
|
56
|
-
card?: "
|
|
56
|
+
card?: "app" | "summary" | "summary_large_image" | "player";
|
|
57
57
|
title?: string;
|
|
58
58
|
description?: string;
|
|
59
59
|
image?: string;
|
|
@@ -76,7 +76,7 @@ export declare const routingValidators: {
|
|
|
76
76
|
siteName?: string;
|
|
77
77
|
};
|
|
78
78
|
twitter?: {
|
|
79
|
-
card?: "
|
|
79
|
+
card?: "app" | "summary" | "summary_large_image" | "player";
|
|
80
80
|
title?: string;
|
|
81
81
|
description?: string;
|
|
82
82
|
image?: string;
|
|
@@ -143,7 +143,7 @@ export declare const safeRoutingValidators: {
|
|
|
143
143
|
siteName?: string;
|
|
144
144
|
};
|
|
145
145
|
twitter?: {
|
|
146
|
-
card?: "
|
|
146
|
+
card?: "app" | "summary" | "summary_large_image" | "player";
|
|
147
147
|
title?: string;
|
|
148
148
|
description?: string;
|
|
149
149
|
image?: string;
|
|
@@ -166,7 +166,7 @@ export declare const safeRoutingValidators: {
|
|
|
166
166
|
siteName?: string;
|
|
167
167
|
};
|
|
168
168
|
twitter?: {
|
|
169
|
-
card?: "
|
|
169
|
+
card?: "app" | "summary" | "summary_large_image" | "player";
|
|
170
170
|
title?: string;
|
|
171
171
|
description?: string;
|
|
172
172
|
image?: string;
|
|
@@ -77,8 +77,8 @@ export declare const MetadataSchema: z.ZodObject<{
|
|
|
77
77
|
}, z.core.$strip>>;
|
|
78
78
|
twitter: z.ZodOptional<z.ZodObject<{
|
|
79
79
|
card: z.ZodOptional<z.ZodEnum<{
|
|
80
|
-
summary: "summary";
|
|
81
80
|
app: "app";
|
|
81
|
+
summary: "summary";
|
|
82
82
|
summary_large_image: "summary_large_image";
|
|
83
83
|
player: "player";
|
|
84
84
|
}>>;
|
|
@@ -108,8 +108,8 @@ export declare const ResolvedMetadataSchema: z.ZodObject<{
|
|
|
108
108
|
}, z.core.$strip>>;
|
|
109
109
|
twitter: z.ZodOptional<z.ZodObject<{
|
|
110
110
|
card: z.ZodOptional<z.ZodEnum<{
|
|
111
|
-
summary: "summary";
|
|
112
111
|
app: "app";
|
|
112
|
+
summary: "summary";
|
|
113
113
|
summary_large_image: "summary_large_image";
|
|
114
114
|
player: "player";
|
|
115
115
|
}>>;
|
|
@@ -142,8 +142,8 @@ export declare const MetadataChainSchema: z.ZodObject<{
|
|
|
142
142
|
}, z.core.$strip>>;
|
|
143
143
|
twitter: z.ZodOptional<z.ZodObject<{
|
|
144
144
|
card: z.ZodOptional<z.ZodEnum<{
|
|
145
|
-
summary: "summary";
|
|
146
145
|
app: "app";
|
|
146
|
+
summary: "summary";
|
|
147
147
|
summary_large_image: "summary_large_image";
|
|
148
148
|
player: "player";
|
|
149
149
|
}>>;
|
|
@@ -172,8 +172,8 @@ export declare const MetadataChainSchema: z.ZodObject<{
|
|
|
172
172
|
}, z.core.$strip>>;
|
|
173
173
|
twitter: z.ZodOptional<z.ZodObject<{
|
|
174
174
|
card: z.ZodOptional<z.ZodEnum<{
|
|
175
|
-
summary: "summary";
|
|
176
175
|
app: "app";
|
|
176
|
+
summary: "summary";
|
|
177
177
|
summary_large_image: "summary_large_image";
|
|
178
178
|
player: "player";
|
|
179
179
|
}>>;
|
|
@@ -201,8 +201,8 @@ export declare const MetadataChainSchema: z.ZodObject<{
|
|
|
201
201
|
}, z.core.$strip>>;
|
|
202
202
|
twitter: z.ZodOptional<z.ZodObject<{
|
|
203
203
|
card: z.ZodOptional<z.ZodEnum<{
|
|
204
|
-
summary: "summary";
|
|
205
204
|
app: "app";
|
|
205
|
+
summary: "summary";
|
|
206
206
|
summary_large_image: "summary_large_image";
|
|
207
207
|
player: "player";
|
|
208
208
|
}>>;
|
|
@@ -305,8 +305,8 @@ export declare const FileSystemApiRouteSchema: z.ZodObject<{
|
|
|
305
305
|
pattern: z.ZodAny;
|
|
306
306
|
filePath: z.ZodString;
|
|
307
307
|
methods: z.ZodArray<z.ZodEnum<{
|
|
308
|
-
GET: "GET";
|
|
309
308
|
POST: "POST";
|
|
309
|
+
GET: "GET";
|
|
310
310
|
PUT: "PUT";
|
|
311
311
|
DELETE: "DELETE";
|
|
312
312
|
PATCH: "PATCH";
|
|
@@ -79,3 +79,17 @@ declare module "virtual:server-island-integrations" {
|
|
|
79
79
|
// Side-effect module that registers framework integrations in the global registry.
|
|
80
80
|
// No exports — import for side effects only.
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
declare module "virtual:avalon-actions-manifest" {
|
|
84
|
+
/** The project's `server` export (tree of actions/namespaces), bundled into the Nitro server. */
|
|
85
|
+
export const server: Record<string, unknown>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
declare module "virtual:avalon/actions" {
|
|
89
|
+
/**
|
|
90
|
+
* The typed action client proxy. This declaration is overridden by the
|
|
91
|
+
* generated `avalon-actions.d.ts` when an actions entry exists, which types
|
|
92
|
+
* `actions` against the project's `server` export.
|
|
93
|
+
*/
|
|
94
|
+
export const actions: Record<string, (input?: unknown) => Promise<unknown>>;
|
|
95
|
+
}
|
|
@@ -20,6 +20,7 @@ export declare const VIRTUAL_MODULE_IDS: {
|
|
|
20
20
|
readonly RENDERER: "virtual:avalon/renderer";
|
|
21
21
|
readonly CLIENT_ENTRY: "virtual:avalon/client-entry";
|
|
22
22
|
readonly INTEGRATION_LOADER: "virtual:avalon/integration-loader";
|
|
23
|
+
readonly ACTIONS: "virtual:avalon/actions";
|
|
23
24
|
};
|
|
24
25
|
export declare const RESOLVED_VIRTUAL_IDS: {
|
|
25
26
|
readonly PAGE_ROUTES: "\0virtual:avalon/page-routes";
|
|
@@ -32,6 +33,7 @@ export declare const RESOLVED_VIRTUAL_IDS: {
|
|
|
32
33
|
readonly RENDERER: "\0virtual:avalon/renderer";
|
|
33
34
|
readonly CLIENT_ENTRY: "\0virtual:avalon/client-entry";
|
|
34
35
|
readonly INTEGRATION_LOADER: "\0virtual:avalon/integration-loader";
|
|
36
|
+
readonly ACTIONS: "\0virtual:avalon/actions";
|
|
35
37
|
};
|
|
36
38
|
export interface NitroIntegrationResult {
|
|
37
39
|
nitroOptions: NitroConfigOutput;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import{existsSync as e,readdirSync as t,readFileSync as n,rmSync as r}from"node:fs";import{stat as i}from"node:fs/promises";import{createRequire as a}from"node:module";import{dirname as o,join as s,relative as c,resolve as l}from"node:path";import{nitro as u}from"nitro/vite";import{isRunnableDevEnvironment as d}from"vite";import{
|
|
1
|
+
import{existsSync as e,readdirSync as t,readFileSync as n,rmSync as r}from"node:fs";import{stat as i}from"node:fs/promises";import{createRequire as a}from"node:module";import{dirname as o,join as s,relative as c,resolve as l}from"node:path";import{nitro as u}from"nitro/vite";import{isRunnableDevEnvironment as d}from"vite";import{generateActionTypes as f}from"../build/actions-types-generator.js";import{getUniversalCSSForHead as p}from"../islands/universal-css-collector.js";import{getUniversalHeadForInjection as m,injectSolidHydrationScriptIfNeeded as h}from"../islands/universal-head-collector.js";import{clearMiddlewareCache as g,discoverScopedMiddleware as _,executeScopedMiddleware as v}from"../middleware/index.js";import{createNitroConfig as y}from"../nitro/config.js";import{createIslandManifestPlugin as b,createNitroBuildPlugin as x,createSourceMapConfig as S,createSourceMapPlugin as C}from"../nitro/index.js";import{collectCssFromModuleGraph as w,injectSsrCss as T}from"../render/collect-css.js";import{generateErrorPage as E,generateFallback404 as D}from"../render/error-pages.js";import{generateComponentId as O}from"../server-islands/manifest.js";import{resolveToRelativePath as k}from"./server-islands-plugin.js";function A(e){let t=ee(e),r=Object.entries(t),i=`export const serverIslandManifest = ${JSON.stringify(t)};\n\n`;i+=`export const serverIslandLoaders = {
|
|
2
2
|
`;for(let[e,t]of r)i+=` ${JSON.stringify(e)}: () => import(${JSON.stringify(t)}),\n`;i+=`};
|
|
3
3
|
|
|
4
|
-
`;let a={};for(let[t,i]of r)if(i.endsWith(`.svelte`))try{let r=n(s(e,i.startsWith(`/`)?i.slice(1):i),`utf8`).match(/<style[^>]*>([\s\S]*?)<\/style>/);r&&(a[t]=r[1].trim())}catch{}return i+=`export const serverIslandCSS = ${JSON.stringify(a)};\n`,i}const
|
|
5
|
-
import {
|
|
4
|
+
`;let a={};for(let[t,i]of r)if(i.endsWith(`.svelte`))try{let r=n(s(e,i.startsWith(`/`)?i.slice(1):i),`utf8`).match(/<style[^>]*>([\s\S]*?)<\/style>/);r&&(a[t]=r[1].trim())}catch{}return i+=`export const serverIslandCSS = ${JSON.stringify(a)};\n`,i}const j=/^[ \t]*import\s+([A-Z]\w*)\s+from\s+(['"][^'"]+['"])/gm,M=new Set([`node_modules`,`dist`,`.output`,`.netlify`,`.vercel`,`.cloudflare`,`.wrangler`,`.firebase`,`.amplify-hosting`,`.git`]);function N(e,t){return new RegExp(String.raw`<${t}\s[\s\S]*?\bserver\s*[={/>]`).test(e)}function P(e,n){let r;try{r=t(e,{withFileTypes:!0})}catch{return}for(let t of r){let r=s(e,t.name);if(t.isDirectory()){if(M.has(t.name))continue;P(r,n)}else if(t.isFile()){if(!/\.(tsx|jsx)$/.test(t.name)||/\.(test|spec)\.[jt]sx$/.test(t.name)||r.includes(`__tests__`))continue;n.push(r)}}}function ee(t){let r={},i=[];for(let n of[`app`,`src`]){let r=s(t,n);e(r)&&P(r,i)}for(let e of i){let i;try{i=n(e,`utf8`)}catch{continue}if(!/\bserver\s*[={/>]/.test(i))continue;j.lastIndex=0;let a=j.exec(i);for(;a!==null;a=j.exec(i)){let n=a[1],o=a[2].slice(1,-1);if(!N(i,n))continue;let s=k(o,e,t),c=O(s);c in r||(r[c]=s)}}return r}function F(t){let n=[`ts`,`tsx`,`js`,`jsx`,`mts`,`mjs`];for(let r of[`app`,`src`])for(let i of n){let n=`/${r}/actions/index.${i}`;if(e(s(t,n.slice(1))))return n}}function I(e){let t=F(e);return t?`export { server } from ${JSON.stringify(t)};\n`:`export const server = {};
|
|
5
|
+
`}function te(){return[`import { createActionClient } from "@useavalon/avalon/actions";`,`export const actions = createActionClient();`,`export default actions;`,``].join(`
|
|
6
|
+
`)}function L(t){let n=s(o(a(import.meta.url).resolve(`@useavalon/avalon`)),t);if(t.endsWith(`.ts`)&&!e(n)){let t=n.replace(/\.ts$/,`.js`);if(e(t))return t}return n}function ne(t,n){let r=s(o(a(s(process.cwd(),`package.json`)).resolve(`@useavalon/${t}`)),n);if(n.endsWith(`.ts`)&&!e(r)){let t=r.replace(/\.ts$/,`.js`);if(e(t))return t}return r}export const VIRTUAL_MODULE_IDS={PAGE_ROUTES:`virtual:avalon/page-routes`,PAGE_LOADER:`virtual:avalon/page-loader`,ISLAND_MANIFEST:`virtual:avalon/island-manifest`,RUNTIME_CONFIG:`virtual:avalon/runtime-config`,CONFIG:`virtual:avalon/config`,LAYOUTS:`virtual:avalon/layouts`,ASSETS:`virtual:avalon/assets`,RENDERER:`virtual:avalon/renderer`,CLIENT_ENTRY:`virtual:avalon/client-entry`,INTEGRATION_LOADER:`virtual:avalon/integration-loader`,ACTIONS:`virtual:avalon/actions`};export const RESOLVED_VIRTUAL_IDS={PAGE_ROUTES:`\0${VIRTUAL_MODULE_IDS.PAGE_ROUTES}`,PAGE_LOADER:`\0${VIRTUAL_MODULE_IDS.PAGE_LOADER}`,ISLAND_MANIFEST:`\0${VIRTUAL_MODULE_IDS.ISLAND_MANIFEST}`,RUNTIME_CONFIG:`\0${VIRTUAL_MODULE_IDS.RUNTIME_CONFIG}`,CONFIG:`\0${VIRTUAL_MODULE_IDS.CONFIG}`,LAYOUTS:`\0${VIRTUAL_MODULE_IDS.LAYOUTS}`,ASSETS:`\0${VIRTUAL_MODULE_IDS.ASSETS}`,RENDERER:`\0${VIRTUAL_MODULE_IDS.RENDERER}`,CLIENT_ENTRY:`\0${VIRTUAL_MODULE_IDS.CLIENT_ENTRY}`,INTEGRATION_LOADER:`\0${VIRTUAL_MODULE_IDS.INTEGRATION_LOADER}`,ACTIONS:`\0${VIRTUAL_MODULE_IDS.ACTIONS}`};export function createNitroIntegration(e,t={}){let n=y(t,e),r=process.cwd(),i=L(`src/server-islands/route.ts`),a=L(`src/actions/route.ts`),o={preset:n.preset,serverDir:t.serverDir??n.serverDir??`./server`,routeRules:n.routeRules,runtimeConfig:n.runtimeConfig,compatibilityDate:n.compatibilityDate,scanDirs:[`.`],noExternals:[`estree-walker`,/^@useavalon\//,/^estree-util/,/^preact/,/^react/,/^react-dom/,/^vue/,/^@vue\//],handlers:[{route:`/_server-islands/**`,handler:i},{route:`/_actions/**`,handler:a}],virtual:{"virtual:server-island-manifest":()=>A(r),"virtual:avalon-actions-manifest":()=>I(r),"virtual:server-island-key":()=>{let e=process.env.AVALON_KEY??``;return`export const serverIslandKey = ${JSON.stringify(e)};\n`},"virtual:server-island-integrations":()=>`
|
|
7
|
+
import { registry } from "${L(`src/core/integrations/registry.ts`)}";
|
|
6
8
|
import { solidIntegration } from "@useavalon/solid";
|
|
7
9
|
import { vueIntegration } from "@useavalon/vue";
|
|
8
10
|
import { svelteIntegration } from "@useavalon/svelte";
|
|
@@ -11,28 +13,28 @@ if (solidIntegration) registry.register(solidIntegration);
|
|
|
11
13
|
if (vueIntegration) registry.register(vueIntegration);
|
|
12
14
|
if (svelteIntegration) registry.register(svelteIntegration);
|
|
13
15
|
if (litIntegration) registry.register(litIntegration);
|
|
14
|
-
`}};t.renderer===!1?
|
|
15
|
-
import{hydrateServerIsland}from"/@fs${
|
|
16
|
+
`}};t.renderer===!1?o.renderer=!1:n.renderer&&(o.renderer=n.renderer),n.publicRuntimeConfig&&(o.publicRuntimeConfig=n.publicRuntimeConfig),n.publicAssets&&(o.publicAssets=n.publicAssets),n.compressPublicAssets&&(o.compressPublicAssets=n.compressPublicAssets),n.serverEntry&&(o.serverEntry=n.serverEntry);let s=n.traceDeps??[];o.traceDeps=[...new Set([`undici`,...s])],n.prerender&&(o.prerender={routes:[],crawlLinks:!1});let c=u(o),l=createNitroCoordinationPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),d=createVirtualModulesPlugin({avalonConfig:e,nitroConfig:t,verbose:e.verbose}),f=x(e,t),p=b(e,{verbose:e.verbose,generatePreloadHints:!0}),m=C(S(t.preset??`node_server`,e.isDev));return{nitroOptions:n,plugins:[...Array.isArray(c)?c:[c],l,d,f,p,m]}}export function createNitroCoordinationPlugin(e){let{avalonConfig:t,verbose:n}=e;return{name:`avalon:nitro-coordination`,enforce:`pre`,config(e,{command:t}){if(t===`serve`)return{server:{watch:{ignored:[`**/.output/**`,`**/dist/**`,`**/.netlify/**`,`**/.vercel/**`,`**/.cloudflare/**`,`**/.wrangler/**`,`**/.firebase/**`,`**/.amplify-hosting/**`]},fs:{deny:[`.output`,`dist`,`.netlify`,`.vercel`,`.cloudflare`,`.wrangler`,`.firebase`,`.amplify-hosting`]}}}},configResolved(e){globalThis.__avalonHydrationMode=e.command===`serve`?`entry-client`:`per-island`;try{f(e.root||process.cwd())}catch{}},configureServer(e){globalThis.__viteDevServer=e;let i=e.config.root||process.cwd(),a=e=>{if(e.includes(`/actions/`))try{f(i)}catch{}};e.watcher.on(`add`,a),e.watcher.on(`unlink`,a);let o=e.config.root||process.cwd(),c=e.config.build?.outDir?s(o,e.config.build.outDir):s(o,`dist`);try{r(c,{recursive:!0,force:!0})}catch{}try{r(s(o,`.output`),{recursive:!0,force:!0})}catch{}let l=!1,u=new Promise(t=>{let n=()=>{(e.environments?.nitro)?.devServer?.entry||l?(l=!0,t()):setTimeout(n,50)};setTimeout(n,100),setTimeout(()=>{l=!0,t()},15e3)});e.middlewares.use(async(e,t,n)=>{if(l)return n();let r=e.url||`/`;if(r.startsWith(`/@`)||r.startsWith(`/__`)||/\/[^/]+\.[a-z0-9]+(\?|$)/i.test(r))return n();await u,n()});let p=null;async function m(){return p||=await _({baseDir:`${e.config.root||process.cwd()}/src`,devMode:!1}),p}function h(){p=null}oe(e,t,n,h),m().catch(e=>{console.warn(`[middleware] Failed to discover middleware:`,e)});let g=e.environments?.ssr,v=!!g&&d(g);v&&ae(e,t.integrations,n).catch(e=>{console.error(`[prewarm] Core modules pre-warm failed:`,e)}),e.middlewares.use(async(t,n,r)=>{let i=t.url||`/`;if(!i.startsWith(`/_server-islands/`)||!v)return r();try{let{decrypt:r}=await e.ssrLoadModule(L(`src/server-islands/encryption.ts`)),{h:a}=await e.ssrLoadModule(`preact`),o=(await e.ssrLoadModule(`preact-render-to-string`)).default,s=new URL(i,`http://${t.headers.host||`localhost`}`).searchParams.get(`p`)||``;if(!s&&t.method===`POST`&&(s=await new Promise((e,n)=>{let r=[],i=e=>r.push(e),a=()=>{c(),e(Buffer.concat(r).toString(`utf8`))},o=e=>{c(),n(e)},s=setTimeout(()=>{c(),n(Error(`Timed out reading server island request body`))},15e3);function c(){clearTimeout(s),t.off(`data`,i),t.off(`end`,a),t.off(`error`,o)}t.on(`data`,i),t.on(`end`,a),t.on(`error`,o)})),!s){n.statusCode=400,n.setHeader(`Content-Type`,`text/plain`),n.end(`Missing encrypted props`);return}let c,l,u;try{let e;if(s.startsWith(`dev.`)){let t=s.slice(4);e=Buffer.from(t,`base64url`).toString(`utf8`)}else e=r(s);let t=JSON.parse(e);t.__island&&(u=t.__island,delete t.__island),t.__src&&(l=t.__src,delete t.__src),c=t}catch{n.statusCode=400,n.setHeader(`Content-Type`,`text/plain`),n.end(`Bad Request: decryption failed`);return}if(!l){n.statusCode=404,n.setHeader(`Content-Type`,`text/plain`),n.end(`Component not found (no __src in payload)`);return}let d=(await e.ssrLoadModule(l)).default;if(typeof d!=`function`){n.statusCode=500,n.setHeader(`Content-Type`,`text/plain`),n.end(`Module "${l}" does not export a default component function`);return}let f=o(a(d,c));if(u){let e=i.split(`/`)[2]?.split(`?`)[0]||``,t=u.elementId??`si-${e}`,n=u.componentSrc??l,r=JSON.stringify(c),a=u.condition??`on:client`,o=u.framework??`preact`,s=`<script type="module">
|
|
17
|
+
import{hydrateServerIsland}from"/@fs${L(`src/client/server-island-hydrate.ts`)}";
|
|
16
18
|
hydrateServerIsland(${JSON.stringify(t)},${JSON.stringify(n)},${r},${JSON.stringify(a)},${JSON.stringify(o)});
|
|
17
|
-
<\/script>`;f+=s}n.statusCode=200,n.setHeader(`Content-Type`,`text/html`),n.setHeader(`Cache-Control`,`private, no-store`),n.end(f)}catch(e){console.error(`[server-islands] Dev handler error:`,e),n.statusCode=500,n.setHeader(`Content-Type`,`text/plain`),n.end(`Server island render failed: ${e instanceof Error?e.message:String(e)}`)}}),e.middlewares.use(async(r,i,a)=>{if(!
|
|
19
|
+
<\/script>`;f+=s}n.statusCode=200,n.setHeader(`Content-Type`,`text/html`),n.setHeader(`Cache-Control`,`private, no-store`),n.end(f)}catch(e){console.error(`[server-islands] Dev handler error:`,e),n.statusCode=500,n.setHeader(`Content-Type`,`text/plain`),n.end(`Server island render failed: ${e instanceof Error?e.message:String(e)}`)}}),e.middlewares.use(async(t,n,r)=>{let i=t.url||`/`;if(!i.startsWith(`/_actions/`)||!v)return r();try{let r=F(e.config.root||process.cwd()),[a,o,s]=await Promise.all([e.ssrLoadModule(L(`src/actions/endpoint.ts`)),e.ssrLoadModule(L(`src/actions/registry.ts`)),r?e.ssrLoadModule(r):Promise.resolve({server:{}})]),c=o.flattenActions(s.server),l=a.defineActionHandler({registry:c,isDev:!0}),u=new URL(i,`http://${t.headers.host||`localhost`}`),d=(t.method||`GET`).toUpperCase(),f;d===`POST`&&(f=await new Promise((e,n)=>{let r=[],i=e=>r.push(e),a=()=>{c(),e(Buffer.concat(r).toString(`utf8`))},o=e=>{c(),n(e)},s=setTimeout(()=>{c(),n(Error(`Timed out reading action request body`))},15e3);function c(){clearTimeout(s),t.off(`data`,i),t.off(`end`,a),t.off(`error`,o)}t.on(`data`,i),t.on(`end`,a),t.on(`error`,o)}));let p=await l({url:u,web:{request:new Request(u,{method:d,headers:t.headers,body:f})},context:{}});n.statusCode=p.status,p.headers.forEach((e,t)=>{n.setHeader(t,e)}),n.end(await p.text())}catch(e){console.error(`[actions] Dev handler error:`,e),n.statusCode=500,n.setHeader(`Content-Type`,`application/json`),n.end(JSON.stringify({error:{code:`INTERNAL_SERVER_ERROR`,message:e instanceof Error?e.message:String(e)}}))}}),e.middlewares.use(async(r,i,a)=>{if(!v)return a();let o=r.url||`/`;if(o.endsWith(`.html`)&&(o=o.slice(0,-5)||`/`),o===`/index`&&(o=`/`),o.startsWith(`/@`)||o.startsWith(`/__`)||o.startsWith(`/node_modules/`)||o.startsWith(`/src/client/`)||o.startsWith(`/packages/`)||o.includes(`.`)&&!o.endsWith(`/`)||o.startsWith(`/api/`))return a();try{if(await re(e,o,r,i,m,n)||await se(e,o,t,i))return;let a=await Y(e,o,t);if(a){i.statusCode=200,i.setHeader(`Content-Type`,`text/html`),i.end(a);return}await ie(e,o,i,t)}catch(e){console.error(`[SSR Error]`,e),i.statusCode=500,i.setHeader(`Content-Type`,`text/html`),i.end(E(e))}})},buildStart(){}}}async function re(e,t,n,r,i,a){let o=performance.now(),s=await i();if(s.length===0)return!1;let c={};for(let[e,t]of Object.entries(n.headers))typeof t==`string`?c[e]=t:Array.isArray(t)&&(c[e]=t.join(`, `));let l=`http://${n.headers.host||`localhost`}${t}`,u=await v({url:l,method:n.method||`GET`,path:t,node:{req:n,res:r},req:new Request(l,{method:n.method||`GET`,headers:c}),context:{}},s,{devMode:!1}),d=performance.now()-o;return d>100&&console.warn(`⚠️ Slow middleware: ${d.toFixed(0)}ms for ${t}`),u?(r.statusCode=u.status,u.headers.forEach((e,t)=>{r.setHeader(t,e)}),r.end(await u.text()),!0):!1}async function ie(e,t,n,r){try{let{discoverErrorPages:i,getErrorPageModule:a,generateDefaultErrorPage:o}=await import(`../nitro/error-handler.js`),s=a(404,await i({isDev:r.isDev,pagesDir:r.pagesDir,loadPageModule:async t=>await e.ssrLoadModule(t)}));if(s?.default&&typeof s.default==`function`){let{renderToHtml:e}=await import(`../render/ssr.js`),r=s.default,i=await e({component:()=>r({statusCode:404,message:`Page not found: ${t}`,url:t})},{});n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(i);return}let c=o(404,`Page not found: ${t}`,r.isDev);n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(c)}catch{n.statusCode=404,n.setHeader(`Content-Type`,`text/html`),n.end(D(t))}}async function ae(e,t,n){let r=performance.now(),i=[{path:L(`src/render/ssr.ts`),assignTo:`ssr`},{path:L(`src/core/layout/enhanced-layout-resolver.ts`),assignTo:`layout`},{path:L(`src/middleware/index.ts`),assignTo:null},...t.map(e=>({path:ne(e,`server/renderer.ts`),assignTo:null}))],a=(await Promise.allSettled(i.map(async({path:t,assignTo:n})=>{let r=await e.ssrLoadModule(t);n===`ssr`&&(q=r),n===`layout`&&(J=r)}))).filter(e=>e.status===`fulfilled`).length,o=performance.now()-r;n&&a>0&&console.log(`🔥 SSR ready in ${o.toFixed(0)}ms (${a}/${i.length} core modules)`)}export function createVirtualModulesPlugin(e){let{avalonConfig:n,nitroConfig:r,verbose:i}=e,a=null,o=s,u=c,d=l,f=process.cwd(),p=[];function m(e){try{let n=t(e,{withFileTypes:!0});for(let t of n){let n=o(e,t.name);if(t.isDirectory()&&t.name!==`node_modules`&&!t.name.startsWith(`.`))m(n);else if(t.isFile()&&t.name.endsWith(`.css`)){let e=u(f,n).replaceAll(`\\`,`/`);p.push(e.startsWith(`/`)?e:`/${e}`)}}}catch{}}function h(){p.length=0;for(let e of r.globalCSS??[])p.push(e.startsWith(`/`)?e:`/${e}`);n.modules&&m(d(f,n.modules.dir)),m(d(f,n.layoutsDir));let e=d(f,n.layoutsDir,`..`);e!==f&&e!==d(f,n.layoutsDir)&&e.startsWith(f)&&m(e)}return n.isDev&&h(),{name:`avalon:nitro-virtual-modules`,enforce:`pre`,resolveId(e){return e===VIRTUAL_MODULE_IDS.PAGE_ROUTES?RESOLVED_VIRTUAL_IDS.PAGE_ROUTES:e===VIRTUAL_MODULE_IDS.PAGE_LOADER?RESOLVED_VIRTUAL_IDS.PAGE_LOADER:e===VIRTUAL_MODULE_IDS.ISLAND_MANIFEST?RESOLVED_VIRTUAL_IDS.ISLAND_MANIFEST:e===VIRTUAL_MODULE_IDS.RUNTIME_CONFIG?RESOLVED_VIRTUAL_IDS.RUNTIME_CONFIG:e===VIRTUAL_MODULE_IDS.CONFIG?RESOLVED_VIRTUAL_IDS.CONFIG:e===VIRTUAL_MODULE_IDS.LAYOUTS?RESOLVED_VIRTUAL_IDS.LAYOUTS:e===VIRTUAL_MODULE_IDS.ASSETS?RESOLVED_VIRTUAL_IDS.ASSETS:e===VIRTUAL_MODULE_IDS.RENDERER?RESOLVED_VIRTUAL_IDS.RENDERER:e===VIRTUAL_MODULE_IDS.CLIENT_ENTRY?RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY:e===VIRTUAL_MODULE_IDS.INTEGRATION_LOADER?RESOLVED_VIRTUAL_IDS.INTEGRATION_LOADER:e===VIRTUAL_MODULE_IDS.ACTIONS?RESOLVED_VIRTUAL_IDS.ACTIONS:null},async load(e){return e===RESOLVED_VIRTUAL_IDS.PAGE_ROUTES?await R(n,i):e===RESOLVED_VIRTUAL_IDS.PAGE_LOADER?await z(n,i):e===RESOLVED_VIRTUAL_IDS.ISLAND_MANIFEST?B():e===RESOLVED_VIRTUAL_IDS.RUNTIME_CONFIG?V(n,r):e===RESOLVED_VIRTUAL_IDS.CONFIG?generateConfigModule(n,r):e===RESOLVED_VIRTUAL_IDS.LAYOUTS?(a||=await H(n,r,p),a):e===RESOLVED_VIRTUAL_IDS.ASSETS?U(r):e===RESOLVED_VIRTUAL_IDS.RENDERER?W(n):e===RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY?await G(n,r):e===RESOLVED_VIRTUAL_IDS.INTEGRATION_LOADER?generateIntegrationLoaderModule(n):e===RESOLVED_VIRTUAL_IDS.ACTIONS?te():null},handleHotUpdate({file:e,server:t}){let n=e.includes(`/pages/`)&&!e.endsWith(`.css`),r=e.includes(`/components/`)&&/\.[tj]sx?$/.test(e),i=(e.includes(`/layouts/`)||e.includes(`_layout`))&&/\.[tj]sx?$/.test(e),o=e.endsWith(`.css`);if(n||r||i||o){if(n){let e=t.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.PAGE_ROUTES);e&&t.moduleGraph.invalidateModule(e);let n=t.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.PAGE_LOADER);n&&t.moduleGraph.invalidateModule(n)}if(i){a=null,h();let e=t.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.LAYOUTS);e&&t.moduleGraph.invalidateModule(e);let n=t.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.CLIENT_ENTRY);n&&t.moduleGraph.invalidateModule(n)}setTimeout(()=>{t.ws.send({type:`full-reload`,path:`*`})},500)}if(e.includes(`vite.config`)||e.includes(`avalon.config`)||e.includes(`nitro.config`)){let e=t.moduleGraph.getModuleById(RESOLVED_VIRTUAL_IDS.CONFIG);e&&t.moduleGraph.invalidateModule(e)}}}}function oe(e,t,n,r){e.watcher.on(`change`,e=>{e.includes(`_middleware`)&&(g(),r?.()),(e.includes(`/render/`)||e.includes(`/layout/`)||e.includes(`/islands/`))&&(q=null,J=null),(e.includes(`/layouts/`)||e.includes(`_layout`))&&globalThis.__avalonLayoutResolver?.clearCache?.()}),e.watcher.on(`add`,e=>{e.includes(`_middleware`)&&(g(),r?.())}),e.watcher.on(`unlink`,e=>{e.includes(`_middleware`)&&(g(),r?.())})}async function R(e,t){try{let{getAllPageDirs:t}=await import(`./module-discovery.js`),{discoverPageRoutesFromMultipleDirs:n}=await import(`../nitro/route-discovery.js`),r=await n(await t(e.pagesDir,e.modules,process.cwd()),{developmentMode:e.isDev});return`export const pageRoutes = ${JSON.stringify(r,null,2)};\nexport default pageRoutes;\n`}catch{return`export const pageRoutes = [];
|
|
18
20
|
export default pageRoutes;
|
|
19
|
-
`}}async function
|
|
21
|
+
`}}async function z(e,t){try{let{getAllPageDirs:t}=await import(`./module-discovery.js`),{discoverPageRoutesFromMultipleDirs:n}=await import(`../nitro/route-discovery.js`),{relative:r}=await import(`node:path`),i=process.cwd(),a=await n(await t(e.pagesDir,e.modules,i),{developmentMode:e.isDev}),o=[],s=[];for(let e=0;e<a.length;e++){let t=a[e],n=`page_${e}`,c=r(i,t.filePath).replaceAll(`\\`,`/`),l=c.startsWith(`/`)?c:`/${c}`;o.push(`import * as ${n} from '${l}';`),s.push(` { pattern: ${JSON.stringify(t.pattern)}, params: ${JSON.stringify(t.params)}, module: ${n} }`)}return[...o,``,`const routes = [`,s.join(`,
|
|
20
22
|
`),`];`,``,`/**`,` * Match a pathname against discovered routes and return the page module.`,` * Uses the same pattern matching as Avalon's route discovery.`,` */`,`export function loadPage(pathname) {`,` const cleanPath = pathname.split('?')[0];`,` for (const route of routes) {`,` if (matchRoute(cleanPath, route.pattern, route.params)) {`,` return route.module;`,` }`,` }`,` return null;`,`}`,``,`function matchRoute(pathname, pattern, paramNames) {`,` // Exact match`,` if (pattern === pathname) return true;`,` // Normalize trailing slashes`,` const normPath = pathname === '/' ? '/' : pathname.replace(/\\/$/, '');`,` const normPattern = pattern === '/' ? '/' : pattern.replace(/\\/$/, '');`,` if (normPath === normPattern) return true;`,` // Dynamic segments: /users/:id matches /users/123`,` if (paramNames.length > 0) {`,` const patternParts = normPattern.split('/');`,` const pathParts = normPath.split('/');`,` if (patternParts.length !== pathParts.length) return false;`,` return patternParts.every((part, i) => part.startsWith(':') || part === pathParts[i]);`,` }`,` return false;`,`}`,``,`export default { loadPage, routes };`,``].join(`
|
|
21
23
|
`)}catch(e){return console.error(`[page-loader] Failed to generate page loader:`,e),`export function loadPage() { return null; }
|
|
22
24
|
export default { loadPage, routes: [] };
|
|
23
|
-
`}}function
|
|
25
|
+
`}}function B(){return`export const islandManifest = { islands: {}, clientEntry: "", css: [] };
|
|
24
26
|
export default islandManifest;
|
|
25
|
-
`}function
|
|
27
|
+
`}function V(e,t){let n={avalon:{streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:e.isDev},...t.runtimeConfig};return`export const runtimeConfig = ${JSON.stringify(n,null,2)};\nexport function useRuntimeConfig() { return runtimeConfig; }\nexport default runtimeConfig;\n`}export function generateConfigModule(e,t){let n=globalThis.__avalonConfig?.isDev??e.isDev,r={streaming:t.streaming??!0,pagesDir:e.pagesDir,layoutsDir:e.layoutsDir,isDev:n,...t.runtimeConfig};return`globalThis.__avalonHydrationMode = "${n?`entry-client`:`per-island`}";\nconst config = ${JSON.stringify(r,null,2)};\nexport function useAvalonConfig() { return config; }\nexport default config;\n`}async function H(e,t,n){let{getAllLayoutDirs:r}=await import(`./module-discovery.js`),{relative:i,resolve:a}=await import(`node:path`),{stat:o,readFile:c}=await import(`node:fs/promises`),l=process.cwd(),u=await r(e.layoutsDir,e.modules,l),d=[],f=0,p=a(l,e.layoutsDir);for(let{dir:t,prefix:n}of u){let r=s(t,`_layout.tsx`);try{if(!(await o(r)).isFile())continue}catch{continue}let a=i(l,r).replaceAll(`\\`,`/`),c=a.startsWith(`/`)?a:`/${a}`,u=t.startsWith(p),m=u&&!e.modules?`RootLayout`:`Layout_${f}`;d.push({prefix:n,importPath:c,varName:m,isShared:u,filePath:r}),f++}let m=d.filter(e=>e.isShared),h=d.filter(e=>!e.isShared),g=new Map;for(let e of h)try{let t=(await c(e.filePath,`utf8`)).match(/layoutConfig\s*=\s*{[\s\S]*?skipLayouts\s*:\s*\[([^\]]*)\]/)?.[1]??``,n=/['"`]_layout['"`]/.test(t);g.set(e.importPath,n)}catch{g.set(e.importPath,!1)}let _=d.map(e=>`import ${e.varName} from '${e.importPath}';`),v=h.toSorted((e,t)=>t.prefix.length-e.prefix.length).map(e=>{let t=e.prefix===`/`,n=g.get(e.importPath)??!1,r=t||n;return` { prefix: ${JSON.stringify(e.prefix)}, Layout: ${e.varName}, skipRoot: ${r} }`}),y=m.length>0?m[0].varName:`null`,b=JSON.stringify(n);return[`// Auto-generated by Avalon — do not edit`,`import { h } from 'preact';`,`import preactRenderToString from 'preact-render-to-string';`,`import { getUniversalCSSForHead } from '@useavalon/avalon/islands/universal-css-collector';`,`import { getUniversalHeadForInjection, injectSolidHydrationScriptIfNeeded } from '@useavalon/avalon/islands/universal-head-collector';`,..._,``,`const RootLayoutComponent = ${y};`,`const _cssLinks = ${b};`,``,`const moduleLayouts = [`,v.join(`,
|
|
26
28
|
`),`];`,``,`function getLayoutsForPath(pathname) {`,` for (const entry of moduleLayouts) {`,` if (entry.prefix === '/' ? pathname === '/' : pathname.startsWith(entry.prefix)) {`,` return entry;`,` }`,` }`,` return null;`,`}`,``,`function injectUniversalAssets(html) {`,` // In dev, inject <link> tags for all discovered CSS files.`,` // The ?direct suffix makes Vite return raw CSS (text/css) instead`,` // of a JS module wrapper, so <link rel="stylesheet"> works.`,` if (process.env.NODE_ENV !== 'production' && _cssLinks.length > 0) {`,` var links = _cssLinks.map(function(href) {`,` return '<link rel="stylesheet" href="' + href + '?direct">';`,` }).join('\\n');`,` if (html.includes('</head>')) {`,` html = html.replace('</head>', links + '\\n</head>');`,` }`,` }`,` const universalCSS = getUniversalCSSForHead(true);`,` if (universalCSS && html.includes('</head>')) {`,` html = html.replace('</head>', universalCSS + '\\n</head>');`,` }`,` const universalHead = getUniversalHeadForInjection(true);`,` if (universalHead && html.includes('</head>')) {`,` html = html.replace('</head>', universalHead + '\\n</head>');`,` }`,` html = injectSolidHydrationScriptIfNeeded(html);`,` return html;`,`}`,``,`export async function wrapWithLayouts(pageHtml, pageModule, context, injectAssets) {`,` const pathname = context.url.pathname;`,` const frontmatter = {`,` ...(pageModule.frontmatter || {}),`,` ...(pageModule.metadata || {}),`,` currentPath: pathname,`,` };`,` const pageLayoutConfig = pageModule.layoutConfig;`,` const skipAll = pageLayoutConfig?.skipLayouts?.includes('_layout');`,``,` const layoutEntry = getLayoutsForPath(pathname);`,` const routeInfo = { path: pathname, params: context.params, query: context.url.searchParams };`,` let html;`,``,` if (!layoutEntry || skipAll) {`,` if (RootLayoutComponent && !skipAll) {`,` const rootProps = {`,` children: h('avalon-page', { id: 'app', dangerouslySetInnerHTML: { __html: pageHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const rootResult = RootLayoutComponent(rootProps);`,` const resolvedRoot = rootResult instanceof Promise ? await rootResult : rootResult;`,` html = '<!DOCTYPE html>\\n' + preactRenderToString(resolvedRoot);`,` } else {`,` // skipAll is true — page provides its own HTML shell or needs a minimal one`,` if (pageHtml.trimStart().startsWith('<html')) {`,` html = '<!DOCTYPE html>\\n' + pageHtml;`,` } else {`,` const title = String(frontmatter.title || 'Avalon');`,` html = [`,` '<!DOCTYPE html>',`,` '<html lang="en">',`,` '<head>',`,` '<meta charset="utf-8">',`,` '<meta name="viewport" content="width=device-width, initial-scale=1">',`,` '<title>' + title + '</title>',`,` '</head>',`,` '<body>',`,` '<div id="app">' + pageHtml + '</div>',`,` '</body>',`,` '</html>',`,` ].join('\\n');`,` }`,` }`,` } else {`,` const layoutProps = {`,` children: h('avalon-page', { dangerouslySetInnerHTML: { __html: pageHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const layoutResult = layoutEntry.Layout(layoutProps);`,` const resolvedLayout = layoutResult instanceof Promise ? await layoutResult : layoutResult;`,` let wrappedHtml = preactRenderToString(resolvedLayout);`,``,` // If the layout provides its own HTML shell (starts with <html),`,` // skip root wrapping regardless of skipRoot flag — the layout IS the document.`,` const layoutProvidesShell = wrappedHtml.trimStart().startsWith('<html');`,``,` if (!layoutProvidesShell && !layoutEntry.skipRoot && RootLayoutComponent) {`,` const rootProps = {`,` children: h('avalon-page', { dangerouslySetInnerHTML: { __html: wrappedHtml } }),`,` frontmatter,`,` data: {},`,` route: routeInfo,`,` };`,` const rootResult = RootLayoutComponent(rootProps);`,` const resolvedRoot = rootResult instanceof Promise ? await rootResult : rootResult;`,` wrappedHtml = preactRenderToString(resolvedRoot);`,` }`,``,` html = '<!DOCTYPE html>\\n' + wrappedHtml;`,` }`,``,` if (injectAssets) {`,` html = injectAssets(html);`,` }`,` return injectUniversalAssets(html);`,`}`,``,`export default { wrapWithLayouts };`,``].join(`
|
|
27
|
-
`)}function
|
|
28
|
-
`)}function
|
|
29
|
-
`)}async function
|
|
29
|
+
`)}function U(e){let t=e.clientEntry??`app/entry-client`;return[`// Auto-generated by Avalon — do not edit`,`// @ts-ignore — virtual import resolved by Nitro's Vite assets plugin at build time`,`import clientAssets from '${t.startsWith(`/`)?t:`/${t}`}?assets=client';`,``,`function buildAssetTags() {`,` const cssLinks = (clientAssets?.css ?? [])`,` .filter(attr => {`,` const href = attr.href || '';`,` // The SSR build produces ssr-index-*.css containing all global, reset,`,` // token, and layout CSS module styles. The client build may also emit`,` // entry-client-*.css (a subset) and index-*.css. To avoid duplicates,`,` // prefer ssr-index when it exists; otherwise fall back to the others.`,` var hasSsrIndex = (clientAssets?.css ?? []).some(function(a) { return (a.href || '').includes('ssr-index'); });`,` if (hasSsrIndex) return href.includes('ssr-index');`,` if (href.includes('entry-client') && href.endsWith('.css')) return true;`,` if (/\\/index-[^/]+\\.css$/.test(href)) return true;`,` return false;`,` })`,` .map(attr => '<link rel="stylesheet" href="' + attr.href + '">')`,` .join('\\n');`,` const jsPreloads = (clientAssets?.js ?? [])`,` .map(attr => '<link rel="modulepreload" href="' + attr.href + '">')`,` .join('\\n');`,` const entryScript = clientAssets?.entry`,` ? '<script type="module" src="' + clientAssets.entry + '"><\/script>'`,` : '';`,` return { cssLinks, jsPreloads, entryScript };`,`}`,``,`export function injectAssets(html) {`,` const { cssLinks, jsPreloads, entryScript } = buildAssetTags();`,` if (html.includes('</head>')) {`,` html = html.replace('</head>', cssLinks + '\\n' + jsPreloads + '\\n</head>');`,` }`,` if (html.includes('</body>')) {`,` html = html.replace('</body>', entryScript + '\\n</body>');`,` }`,` return html;`,`}`,``,`export { clientAssets };`,`export default { injectAssets, clientAssets };`,``].join(`
|
|
30
|
+
`)}function W(e){let t=Array.isArray(e.integrations)?e.integrations:[],n=[],r=[];for(let e of t){let t=`${e}Integration`;n.push(`import { ${t} } from '@useavalon/${e}';`),r.push(`registry.register(${t});`)}return[`// Auto-generated by Avalon — do not edit`,`import { createNitroRenderer } from '@useavalon/avalon/nitro/renderer';`,`import { registerBuiltinDirectives } from '@useavalon/avalon';`,`import { registry } from '@useavalon/avalon/islands/integration-registry';`,`import avalonConfig from 'virtual:avalon/config';`,`import { loadPage } from 'virtual:avalon/page-loader';`,`import { wrapWithLayouts } from 'virtual:avalon/layouts';`,`import { injectAssets } from 'virtual:avalon/assets';`,...n,``,`// Pre-register framework integrations for SSR`,...r,``,`// Register built-in custom hydration directives (on:delay, on:scroll, etc.)`,`registerBuiltinDirectives();`,``,`// Set hydration mode flag — intentionally duplicated from virtual:avalon/config`,`// for module-load-order resilience (config import may be tree-shaken or deferred).`,`globalThis.__avalonHydrationMode = avalonConfig.isDev ? "entry-client" : "per-island";`,``,`export default createNitroRenderer({`,` avalonConfig,`,` isDev: avalonConfig.isDev,`,` resolvePageRoute: async (pathname) => {`,` const mod = loadPage(pathname);`,` if (!mod || !('default' in mod)) return null;`,` return { filePath: '[virtual:' + pathname + ']', pattern: pathname, params: {} };`,` },`,` loadPageModule: async (filePath) => {`,` const match = filePath.match(/^\\[virtual:(.+)\\]$/);`,` const pathname = match ? match[1] : filePath;`,` const mod = loadPage(pathname);`,` if (mod) return mod;`,` return { default: () => null, metadata: { title: 'Avalon' } };`,` },`,` wrapWithLayouts: (pageHtml, pageModule, context) =>`,` wrapWithLayouts(pageHtml, pageModule, context, injectAssets),`,`});`,``].join(`
|
|
31
|
+
`)}async function G(e,t){let{getAllLayoutDirs:n}=await import(`./module-discovery.js`),{readdir:r}=await import(`node:fs/promises`),{relative:i,join:a}=await import(`node:path`),o=process.cwd(),s=await n(e.layoutsDir,e.modules,o),c=[];for(let{dir:e}of s)try{let t=await r(e,{withFileTypes:!0});for(let n of t){if(!n.isFile()||!n.name.endsWith(`.css`))continue;let t=i(o,a(e,n.name)).replaceAll(`\\`,`/`),r=t.startsWith(`/`)?t:`/${t}`;c.push(r)}}catch{}let l=globalThis.__avalonConfig?.isDev??e.isDev?`@useavalon/avalon/client/main`:`@useavalon/avalon/client/main-slim`,u=[`// Auto-generated by Avalon — do not edit`,`// Island hydration runtime`];(globalThis.__avalonHydrationMode??`entry-client`)===`entry-client`?u.push(`import '${l}';`):u.push(`// Per-island hydration mode — no shared runtime needed`),u.push(``);let d=t.globalCSS??[];for(let e of d){let t=e.startsWith(`/`)?e:`/${e}`;u.push(`// Global CSS`),u.push(`import '${t}';`)}if(d.length>0&&u.push(``),c.length>0){u.push(`// Layout CSS (auto-discovered)`);let e=0;for(let t of c)t.includes(`.module.`)?(u.push(`import _lcss${e} from '${t}';`),e++):u.push(`import '${t}';`)}return u.push(``),u.join(`
|
|
30
32
|
`)}export function generateIntegrationLoaderModule(e){let t=(e.integrations??[]).map(e=>typeof e==`string`?e:e.name),n={preact:`@useavalon/preact/client`,react:`@useavalon/preact/client`,vue:`@useavalon/vue/client`,svelte:`@useavalon/svelte/client`,solid:`@useavalon/solid/client`,lit:`@useavalon/lit/client`,qwik:`@useavalon/qwik/client`},r={preact:`@useavalon/preact/client/hmr`,react:`@useavalon/react/client/hmr`,vue:`@useavalon/vue/client/hmr`,svelte:`@useavalon/svelte/client/hmr`,solid:`@useavalon/solid/client/hmr`,lit:`@useavalon/lit/client/hmr`,qwik:`@useavalon/qwik/client/hmr`},i=t.includes(`lit`),a=t.includes(`solid`),o=(globalThis.__avalonConfig?.isDev??e.isDev)===!0,s=[`// Auto-generated by Avalon — only includes configured integrations`,``];a&&!o&&(s.push(`// --- Inlined Solid adapter (production) ---`),s.push(`// Eliminates a separate chunk + network request for the Solid client adapter.`),s.push(`// Only imports hydrate/createComponent — no render() fallback (saves ~1-2 KiB).`),s.push(`function _ensureHydrationContext() {`),s.push(` if (!globalThis._$HY) {`),s.push(` globalThis._$HY = { events: [], completed: new WeakSet(), r: {}, fe() {} };`),s.push(` }`),s.push(`}`),s.push(``),s.push(`async function _solidHydrate(container, Component, props) {`),s.push(` if (!container) throw new Error("Container element is required for hydration");`),s.push(` if (!Component || typeof Component !== "function") {`),s.push(` throw new Error("Invalid Solid component: expected function, got " + typeof Component);`),s.push(` }`),s.push(` var el = container;`),s.push(` var renderId = el.dataset.solidRenderId || el.dataset.renderId;`),s.push(` var { hydrate: solidHydrate, createComponent } = await import("solid-js/web");`),s.push(` _ensureHydrationContext();`),s.push(` solidHydrate(function() { return createComponent(Component, props || {}); }, el, { renderId: renderId || "" });`),s.push(`}`),s.push(``),s.push(`var _solidModule = { hydrate: _solidHydrate };`),s.push(``)),s.push(`// --- loadIntegrationModule ---`),s.push(`export async function loadIntegrationModule(framework) {`),s.push(` switch (framework) {`);for(let e of t){let t=n[e];t&&(e===`solid`&&!o?(s.push(` case "solid":`),s.push(` return _solidModule;`)):e===`react`?s.push(` case "react":`):e===`preact`?(s.push(` case "preact":`),s.push(` return import("${t}");`)):(s.push(` case "${e}":`),s.push(` return import("${t}");`)))}t.includes(`react`)&&!t.includes(`preact`)&&s.push(` return import("${n.react}");`),s.push(` default:`),s.push(" throw new Error(`Unknown or unconfigured framework: ${framework}`);"),s.push(` }`),s.push(`}`),s.push(``),s.push(`// --- Lit hydration pre-load (only if Lit is configured) ---`),i?(s.push(`export async function preLitHydration() {`),s.push(` await import("@useavalon/lit/client");`),s.push(`}`)):s.push(`export async function preLitHydration() {}`),s.push(``),s.push(`// --- HMR adapter loader ---`),s.push(`export async function loadHMRAdapter(framework) {`),s.push(` switch (framework) {`);for(let e of t){let t=r[e];t&&(s.push(` case "${e}":`),s.push(` return import("${t}").then(m => m.${e}Adapter);`))}return s.push(` default: return null;`),s.push(` }`),s.push(`}`),s.join(`
|
|
31
|
-
`)}export function getViteDevServer(){return globalThis.__viteDevServer}export function getAvalonConfig(){return globalThis.__avalonConfig}export function isDevelopmentMode(){return globalThis.__avalonConfig?.isDev??!0}const
|
|
32
|
-
`)}</style>`;k=D.includes(`</head>`)?D.replace(`</head>`,`${e}\n</head>`):D+e}k.trim().toLowerCase().startsWith(`<!doctype`)||(k=`<!DOCTYPE html>\n${k}`);let A=
|
|
33
|
+
`)}export function getViteDevServer(){return globalThis.__viteDevServer}export function getAvalonConfig(){return globalThis.__avalonConfig}export function isDevelopmentMode(){return globalThis.__avalonConfig?.isDev??!0}const K=`<!--AVALON_STREAM_BOUNDARY-->`;let q=null,J=null;async function se(e,t,n,r){if(!n.modules)return!1;let i=t.split(`?`)[0],a=await Q(i,n,e);if(!a)return!1;try{let t=await e.ssrLoadModule(a),n=t.default;if(!n)return!1;let o=t.layoutConfig,s=await w(e,a),c=await Z(i,e),l=[];for(let t of c){let n=await e.ssrLoadModule(t);l.push({file:t,module:n})}for(let t of c){let n=await w(e,t);s.push(...n)}if(l.length===0)return!1;let{render:u}=await e.ssrLoadModule(`preact-render-to-string`),{h:d}=await e.ssrLoadModule(`preact`),f=o?.skipLayouts||[],g=l.filter(({file:e})=>{let t=e.split(`/`).pop()?.replace(/\.[^.]+$/,``)||``;return!f.includes(t)}),_=t.frontmatter,v=t.metadata,y={children:null,frontmatter:{..._,...v,currentPath:i},params:{},url:i},b=[],x=[];for(let e of g){let t=e.module.default;if(!(!t||typeof t!=`function`))try{let n=t({...y,children:d(`div`,null,`test`)}),r=u(n instanceof Promise?await n:n);r.trim().startsWith(`<html`)||r.includes(`<!DOCTYPE`)?b.push(e):x.push(e)}catch{x.push(e)}}if(b.length===0)return!1;let{module:S}=b[b.length-1],C=S.default;if(!C||typeof C!=`function`)return!1;let T;try{let e=C({...y,children:d(`div`,{dangerouslySetInnerHTML:{__html:K}})});T=u(e instanceof Promise?await e:e)}catch{return!1}let E=T.indexOf(K);if(E===-1)return!1;let D=T.slice(0,E),O=T.slice(E+29),k=D;if(s.length>0){let e=`<style data-avalon-ssr-css>${s.join(`
|
|
34
|
+
`)}</style>`;k=D.includes(`</head>`)?D.replace(`</head>`,`${e}\n</head>`):D+e}k.trim().toLowerCase().startsWith(`<!doctype`)||(k=`<!DOCTYPE html>\n${k}`);let A=p(!0);A&&k.includes(`</head>`)&&(k=k.replace(`</head>`,`${A}\n</head>`));let j=m(!0);j&&k.includes(`</head>`)&&(k=k.replace(`</head>`,`${j}\n</head>`)),k=h(k),r.statusCode=200,r.setHeader(`Content-Type`,`text/html; charset=utf-8`),r.setHeader(`Transfer-Encoding`,`chunked`),r.setHeader(`X-Avalon-Streaming`,`1`),r.flushHeaders(),r.write(k);let M;try{let e=typeof n==`function`?n():n;M=u(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR Streaming] Error rendering page component:`,e),M=`<div>Error rendering page</div>`}if(M.trim().startsWith(`<!DOCTYPE html>`)||M.trim().startsWith(`<html`))return r.end(M),!0;let N=M;for(let{module:e}of x){let t=e.default;if(!(!t||typeof t!=`function`))try{let e=t({...y,children:d(`div`,{dangerouslySetInnerHTML:{__html:N}})});N=u(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR Streaming] Error rendering wrapper layout:`,e)}}let P=N+O;if(!P.includes(`/src/client/main.js`)&&!P.includes(`/@vite/client`)){let e=P.lastIndexOf(`</body>`);e!==-1&&(P=P.slice(0,e)+`
|
|
33
35
|
<script type="module" src="/@vite/client"><\/script>
|
|
34
36
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
35
|
-
`+P.slice(e))}return r.end(P),!0}catch(e){return r.headersSent?(r.end(`<div>Streaming SSR error: ${e.message}</div></body></html>`),!0):!1}}async function
|
|
37
|
+
`+P.slice(e))}return r.end(P),!0}catch(e){return r.headersSent?(r.end(`<div>Streaming SSR error: ${e.message}</div></body></html>`),!0):!1}}async function Y(e,t,n){let r=t.split(`?`)[0],i=await Q(r,n,e);if(!i)return null;try{let t=await e.ssrLoadModule(i),a=t.default;if(!a)return console.warn(`[SSR] Page ${i} has no default export`),null;let o=await w(e,i),s=await Z(r,e),c=[];for(let t of s){let n=await e.ssrLoadModule(t);c.push({file:t,module:n})}for(let t of s){let n=await w(e,t);o.push(...n)}let l;return l=n.modules&&c.length>0?await ce(a,t,c,r,n,e):await le(a,t,r,n,e),o.length>0&&(l=T(l,o)),l}catch(e){throw console.error(`[SSR] Error rendering ${i}:`,e),e}}async function ce(e,t,n,r,i,a){let{render:o}=await a.ssrLoadModule(`preact-render-to-string`),{h:s}=await a.ssrLoadModule(`preact`),c=t.layoutConfig?.skipLayouts||[],l=n.filter(({file:e})=>{let t=e.split(`/`).pop()?.replace(/\.[^.]+$/,``)||``;return!c.includes(t)}),u;try{let t=typeof e==`function`?e():e;u=o(t instanceof Promise?await t:t)}catch(e){console.error(`[SSR] Error rendering page component:`,e),u=`<div>Error rendering page</div>`}if(u.trim().startsWith(`<!DOCTYPE html>`)||u.trim().startsWith(`<html`))return X(u);let d=t.frontmatter,f=t.metadata,p={children:null,frontmatter:{...d,...f,currentPath:r},params:{},url:r},m=[],h=[];for(let e of l){let t=e.module.default;if(!(!t||typeof t!=`function`))try{let n=t({...p,children:s(`div`,null,`test`)}),r=o(n instanceof Promise?await n:n);r.trim().startsWith(`<html`)||r.includes(`<!DOCTYPE`)?m.push(e):h.push(e)}catch{h.push(e)}}let g=u;for(let{module:e}of h){let t=e.default;if(!(!t||typeof t!=`function`))try{let e=t({...p,children:s(`div`,{dangerouslySetInnerHTML:{__html:g}})});g=o(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR] Error rendering wrapper layout:`,e)}}if(m.length>0){let{module:e}=m[m.length-1],t=e.default;if(t&&typeof t==`function`)try{let e=t({...p,children:s(`div`,{dangerouslySetInnerHTML:{__html:g}})});g=o(e instanceof Promise?await e:e)}catch(e){console.error(`[SSR] Error rendering shell layout:`,e)}}if(g.trim().startsWith(`<!DOCTYPE html>`)||g.trim().startsWith(`<html`))return X(g);let _=t.metadata||{},v=_.title||`Avalon App`,y=_.description||``;return`<!DOCTYPE html>
|
|
36
38
|
<html lang="en">
|
|
37
39
|
<head>
|
|
38
40
|
<meta charset="utf-8">
|
|
@@ -45,12 +47,12 @@ export default islandManifest;
|
|
|
45
47
|
${g}
|
|
46
48
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
47
49
|
</body>
|
|
48
|
-
</html>`}function X(e){let t=e;if(t.trim().toLowerCase().startsWith(`<!doctype`)||(t=`<!DOCTYPE html>\n${t}`),!t.includes(`data-universal-ssr="true"`)){let e=
|
|
50
|
+
</html>`}function X(e){let t=e;if(t.trim().toLowerCase().startsWith(`<!doctype`)||(t=`<!DOCTYPE html>\n${t}`),!t.includes(`data-universal-ssr="true"`)){let e=p(!0);e&&t.includes(`</head>`)&&(t=t.replace(`</head>`,`${e}\n</head>`))}let n=m(!0);if(n&&t.includes(`</head>`)&&(t=t.replace(`</head>`,`${n}\n</head>`)),t=h(t),t.includes(`/src/client/main.js`)||t.includes(`/@vite/client`))return t;let r=t.lastIndexOf(`</body>`);return r===-1?t+`
|
|
49
51
|
<script type="module" src="/@vite/client"><\/script>
|
|
50
52
|
<script type="module" src="/src/client/main.js"><\/script>`:t.slice(0,r)+`
|
|
51
53
|
<script type="module" src="/@vite/client"><\/script>
|
|
52
54
|
<script type="module" src="/src/client/main.js"><\/script>
|
|
53
|
-
`+t.slice(r)}async function Z(e,t){let n=t.config.root||process.cwd(),r=globalThis.__avalonConfig,a=`_layout.tsx`,o=[],s=e.split(`/`).filter(Boolean),c=[``];for(let e=0;e<s.length;e++)c.push(`/${s.slice(0,e+1).join(`/`)}`);async function l(e){try{if((await i(e)).isFile()){let t=e.slice(n.length);o.includes(t)||o.push(t)}}catch{}}if(r?.layoutsDir&&await l(`${`${n}/${r.layoutsDir}`}/${a}`),r?.modules){let e=`${n}/${r.modules.dir}`,t=r.modules.layoutsDirName,i=s[0]||``,o=[`home`,`root`,`main`,`index`];if(!i||o.includes(i.toLowerCase()))for(let n of o)await l(`${e}/${n}/${t}/${a}`);else await l(`${e}/${i}/${t}/${a}`)}let u=`${n}/src/layouts`;for(let e of c)await l(e===``?`${u}/${a}`:`${u}${e}/${a}`);return o}async function Q(e,t,n){let r=e;r.endsWith(`/`)&&r!==`/`&&(r=r.slice(0,-1)),r===`/`&&(r=`/index`);let a=[`.tsx`,`.ts`,`.jsx`,`.js`,`.mdx`,`.md`],o=n.config.root||process.cwd();async function s(e){try{if((await i(`${o}/${e}`)).isFile())return`/${e}`}catch{}return null}if(t.modules){let n=t.modules.dir,i=t.modules.pagesDirName,o=e.split(`/`).filter(Boolean),c=o[0]||``,l=[`home`,`root`,`main`,`index`],u,d;if(!c||l.includes(c.toLowerCase()))u=`home`,d=r;else{u=c;let e=o.slice(1);d=e.length>0?`/${e.join(`/`)}`:`/index`}for(let e of a){let t=await s(`${n}/${u}/${i}${d}${e}`);if(t)return t}if(!d.endsWith(`/index`))for(let e of a){let t=await s(`${n}/${u}/${i}${d}/index${e}`);if(t)return t}}let c=t.pagesDir;for(let e of a){let t=await s(`${c}${r}${e}`);if(t)return t}if(!r.endsWith(`/index`))for(let e of a){let t=await s(`${c}${r}/index${e}`);if(t)return t}return null}async function
|
|
55
|
+
`+t.slice(r)}async function Z(e,t){let n=t.config.root||process.cwd(),r=globalThis.__avalonConfig,a=`_layout.tsx`,o=[],s=e.split(`/`).filter(Boolean),c=[``];for(let e=0;e<s.length;e++)c.push(`/${s.slice(0,e+1).join(`/`)}`);async function l(e){try{if((await i(e)).isFile()){let t=e.slice(n.length);o.includes(t)||o.push(t)}}catch{}}if(r?.layoutsDir&&await l(`${`${n}/${r.layoutsDir}`}/${a}`),r?.modules){let e=`${n}/${r.modules.dir}`,t=r.modules.layoutsDirName,i=s[0]||``,o=[`home`,`root`,`main`,`index`];if(!i||o.includes(i.toLowerCase()))for(let n of o)await l(`${e}/${n}/${t}/${a}`);else await l(`${e}/${i}/${t}/${a}`)}let u=`${n}/src/layouts`;for(let e of c)await l(e===``?`${u}/${a}`:`${u}${e}/${a}`);return o}async function Q(e,t,n){let r=e;r.endsWith(`/`)&&r!==`/`&&(r=r.slice(0,-1)),r===`/`&&(r=`/index`);let a=[`.tsx`,`.ts`,`.jsx`,`.js`,`.mdx`,`.md`],o=n.config.root||process.cwd();async function s(e){try{if((await i(`${o}/${e}`)).isFile())return`/${e}`}catch{}return null}if(t.modules){let n=t.modules.dir,i=t.modules.pagesDirName,o=e.split(`/`).filter(Boolean),c=o[0]||``,l=[`home`,`root`,`main`,`index`],u,d;if(!c||l.includes(c.toLowerCase()))u=`home`,d=r;else{u=c;let e=o.slice(1);d=e.length>0?`/${e.join(`/`)}`:`/index`}for(let e of a){let t=await s(`${n}/${u}/${i}${d}${e}`);if(t)return t}if(!d.endsWith(`/index`))for(let e of a){let t=await s(`${n}/${u}/${i}${d}/index${e}`);if(t)return t}}let c=t.pagesDir;for(let e of a){let t=await s(`${c}${r}${e}`);if(t)return t}if(!r.endsWith(`/index`))for(let e of a){let t=await s(`${c}${r}/index${e}`);if(t)return t}return null}async function le(e,t,n,r,i){let a=t.metadata||{};try{q||=await i.ssrLoadModule(L(`src/render/ssr.ts`));let o=q;J||=await i.ssrLoadModule(L(`src/core/layout/enhanced-layout-resolver.ts`));let s=J,c={component:()=>typeof e==`function`?e():e,options:{title:a.title||`Avalon App`},frontmatter:t.frontmatter};if(o.renderToHtmlWithLayouts&&s.EnhancedLayoutResolver&&s.EnhancedLayoutResolverUtils)try{let e=i.config.root||process.cwd();if(!globalThis.__avalonLayoutResolver){let t=s.EnhancedLayoutResolver,n=r.layoutsDir||`src/layouts`;globalThis.__avalonLayoutResolver=new t({baseDirectory:`${e}/${n}`,filePattern:`_layout.tsx`,excludeDirectories:[`node_modules`,`.git`,`dist`,`build`],enableWatching:!0,developmentMode:!1,enableCaching:!0,cacheTTL:60*1e3,maxCacheSize:100,enableStreaming:!0,enableErrorBoundaries:!0,enableMetrics:!1,enableDebugInfo:!1,modulesDir:r.modules?`${e}/${r.modules.dir}`:void 0,modulesLayoutsDirName:r.modules?.layoutsDirName})}let t=`http://localhost${n}`,l={params:{},query:{},url:t,request:{method:`GET`,url:t,headers:new Headers}};return await o.renderToHtmlWithLayouts(c,globalThis.__avalonLayoutResolver,l,n,{title:a.title||`Avalon App`},void 0,{suppressWarnings:!0})}catch{}if(o.renderToHtml)return await o.renderToHtml(c,{title:a.title||`Avalon App`},void 0,{suppressWarnings:!0})}catch{}let o=a.title||`Avalon App`,s=a.description||``,c=``;try{let t=await i.ssrLoadModule(`preact-render-to-string`);t.render&&typeof e==`function`&&(c=t.render(e()))}catch{c=`<p>Loading page: ${$(n)}</p>`}return`<!DOCTYPE html>
|
|
54
56
|
<html lang="en">
|
|
55
57
|
<head>
|
|
56
58
|
<meta charset="utf-8">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@useavalon/avalon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Multi-framework islands architecture for the modern web",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
"types": "./dist/mod.d.ts",
|
|
44
44
|
"default": "./dist/mod.js"
|
|
45
45
|
},
|
|
46
|
+
"./actions": {
|
|
47
|
+
"types": "./dist/src/actions/define.d.ts",
|
|
48
|
+
"default": "./dist/src/actions/define.js"
|
|
49
|
+
},
|
|
46
50
|
"./client": {
|
|
47
51
|
"types": "./dist/src/client/components.d.ts",
|
|
48
52
|
"default": "./dist/src/client/components.js"
|
|
@@ -147,7 +151,7 @@
|
|
|
147
151
|
}
|
|
148
152
|
},
|
|
149
153
|
"dependencies": {
|
|
150
|
-
"@useavalon/core": "^0.
|
|
154
|
+
"@useavalon/core": "^0.2.0",
|
|
151
155
|
"@mdx-js/rollup": "^3.0.0",
|
|
152
156
|
"h3": "^2.0.1-rc.16",
|
|
153
157
|
"marked": "^17.0.4",
|