@ilha/router 0.8.13 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/codegen.d.ts +3 -0
- package/dist/index.d.ts +28 -3
- package/dist/index.js +2247 -2
- package/dist/{plugin-DdquB3Nf.js → plugin-DogkskcY.js} +510 -15
- package/dist/plugin.d.ts +9 -1
- package/dist/request-scope-C4reU4v0.js +34 -0
- package/dist/request-scope.d.ts +22 -0
- package/dist/rolldown.d.ts +2 -1
- package/dist/rolldown.js +2 -2
- package/dist/rspack.d.ts +1 -0
- package/dist/rspack.js +2 -2
- package/dist/server-island-registry.d.ts +77 -0
- package/dist/server-island-registry.js +134 -0
- package/dist/server-island.d.ts +44 -0
- package/dist/server-island.js +229 -0
- package/dist/server-islands.d.ts +72 -0
- package/dist/ssr.d.ts +17 -88
- package/dist/ssr.js +138 -130
- package/dist/vite.d.ts +1 -0
- package/dist/vite.js +2 -2
- package/package.json +12 -5
- package/dist/src-BKWkRtMx.js +0 -2119
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time support for server-defined islands. The plugin scans
|
|
3
|
+
* `*.server.ts(x)` modules for island exports (`export const X = ilha…`),
|
|
4
|
+
* generates a client virtual module per file that re-creates those exports as
|
|
5
|
+
* proxies wired to tacho stubs, and rewrites client-graph import sites so
|
|
6
|
+
* island bindings resolve to the proxy while everything else keeps flowing
|
|
7
|
+
* through oxidejs's tacho stub replacement.
|
|
8
|
+
*/
|
|
9
|
+
export interface ScannedServerIsland {
|
|
10
|
+
/** Export binding name, or `"default"` for `export default ilha…`. */
|
|
11
|
+
name: string;
|
|
12
|
+
/** Slot tag from `.as()` — must match what SSR emits. */
|
|
13
|
+
as: string;
|
|
14
|
+
/** Stream key → referenced module export used as its transport. */
|
|
15
|
+
streams: Record<string, string>;
|
|
16
|
+
/** Action key → referenced module export used as its transport. */
|
|
17
|
+
actions: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
export interface ClientIslandRef {
|
|
20
|
+
id: string;
|
|
21
|
+
local: string;
|
|
22
|
+
imported: string;
|
|
23
|
+
spec: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ServerModuleScan {
|
|
26
|
+
islands: ScannedServerIsland[];
|
|
27
|
+
/** All value-export names of the module (transport candidates). */
|
|
28
|
+
exports: string[];
|
|
29
|
+
/** Imported JSX components that must hydrate inside the server island. */
|
|
30
|
+
clientRefs: ClientIslandRef[];
|
|
31
|
+
/** True when the module declares `export const load = loader.client(…)` —
|
|
32
|
+
* the proxy wires it as an RPC call invoked when the view hydrates. */
|
|
33
|
+
clientLoader?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function clientRefPublicId(spec: string, imported: string): string;
|
|
36
|
+
/** Scan a `*.server.ts(x)` module source for island exports and their
|
|
37
|
+
* declarative wiring. Convention: islands start with `ilha` — both builder
|
|
38
|
+
* chains (`ilha.state()…render()`) and direct factories (`ilha(() => …)`). */
|
|
39
|
+
export declare function scanServerIslands(source: string): ServerModuleScan;
|
|
40
|
+
export declare function loadServerModuleScan(path: string): ServerModuleScan;
|
|
41
|
+
/** Virtual-module id prefix for generated client proxies of server islands.
|
|
42
|
+
* The file path rides base64url-encoded: a raw suffix like
|
|
43
|
+
* `\0…:…/tasks.server.tsx` would end in `.server.*` and oxidejs's client-stub
|
|
44
|
+
* loader would claim the virtual module before us. */
|
|
45
|
+
export declare const SERVER_ISLAND_PREFIX = "\0ilha:server-island:";
|
|
46
|
+
/** Virtual-module specifier serving the client proxy for one server island file. */
|
|
47
|
+
export declare function serverIslandVirtualSpec(file: string): string;
|
|
48
|
+
/** Emit the client virtual module for one scanned server file. Plain JS —
|
|
49
|
+
* `\0` virtual modules bypass Vite's built-in TS transform, so type-only
|
|
50
|
+
* constructs here would reach the browser unparsed. Editor types are
|
|
51
|
+
* unaffected: TS resolves the ORIGINAL specifier (the real server module);
|
|
52
|
+
* this module exists only inside the client bundle. Frames are fetched from
|
|
53
|
+
* the plugin's `/__ilha/frame` dev middleware. */
|
|
54
|
+
export declare function serverIslandPublicId(spec: string, name: string): string;
|
|
55
|
+
export declare function generateServerIslandModule(spec: string, scan: ServerModuleScan): string;
|
|
56
|
+
export interface SplitContext {
|
|
57
|
+
/** Resolved absolute path of the imported specifier, when it's a scanned
|
|
58
|
+
* server module carrying islands; null otherwise. */
|
|
59
|
+
islandNamesFor(spec: string): {
|
|
60
|
+
islands: Set<string>;
|
|
61
|
+
hasDefault: boolean;
|
|
62
|
+
} | null;
|
|
63
|
+
/** Virtual module specifier that provides the island bindings. */
|
|
64
|
+
virtualSpecFor(spec: string): string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Rewrite import sites whose specifier targets a server module containing
|
|
68
|
+
* island exports. Island bindings move to the virtual proxy module; all other
|
|
69
|
+
* bindings stay on the original specifier (oxidejs replaces them with tacho
|
|
70
|
+
* stubs). Returns null when no statement needed rewriting.
|
|
71
|
+
*/
|
|
72
|
+
export declare function splitServerImports(code: string, ctx: SplitContext): string | null;
|
package/dist/ssr.d.ts
CHANGED
|
@@ -1,94 +1,23 @@
|
|
|
1
|
-
import "ilha:loaders";
|
|
2
|
-
import type { HeadInput, HydratableRenderOptions, SerializedHead } from "./index";
|
|
3
|
-
export interface AssetAttrs {
|
|
4
|
-
href: string;
|
|
5
|
-
"data-vite-dev-id"?: string;
|
|
6
|
-
}
|
|
7
|
-
/** Manifest slice without `merge` — matches Nitro/Vite `?assets` raw shape. */
|
|
8
|
-
export interface AssetsRaw {
|
|
9
|
-
entry?: string;
|
|
10
|
-
js: AssetAttrs[];
|
|
11
|
-
css: AssetAttrs[];
|
|
12
|
-
}
|
|
13
1
|
/**
|
|
14
|
-
*
|
|
15
|
-
* imports. `merge` combines several descriptors, de-duping by href.
|
|
2
|
+
* Production SSR endpoints for server-owned islands and regular-page loads.
|
|
16
3
|
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
merge(...others: AssetsRaw[]): Assets;
|
|
21
|
-
};
|
|
22
|
-
/** Client + SSR manifests from `?assets=client` / `?assets=ssr` imports. */
|
|
23
|
-
export interface IlhaAssetsPair {
|
|
24
|
-
client: Assets;
|
|
25
|
-
server: Assets;
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Merge client and SSR asset manifests (same as `client.merge(server)` from Nitro).
|
|
29
|
-
*/
|
|
30
|
-
export declare function mergeAssets({ client, server }: IlhaAssetsPair): Assets;
|
|
31
|
-
/**
|
|
32
|
-
* App-wide `<head>` defaults for `IlhaHandler`. Same shape as route `head()`;
|
|
33
|
-
* use so entry files read clearly: `head: appHead({ title, script: [...] })`.
|
|
34
|
-
*/
|
|
35
|
-
export declare function appHead(head: HeadInput): HeadInput;
|
|
36
|
-
export interface IlhaHandlerOptions {
|
|
37
|
-
/**
|
|
38
|
-
* Merged asset manifest — use `mergeAssets({ client, server })` with imports
|
|
39
|
-
* from `?assets=client` and `?assets=ssr`.
|
|
40
|
-
*/
|
|
41
|
-
assets: Assets;
|
|
42
|
-
/** `<html lang>` value. Default: `"en"`. */
|
|
43
|
-
lang?: string;
|
|
44
|
-
/** Id of the hydration mount container. Default: `"app"`. */
|
|
45
|
-
appId?: string;
|
|
46
|
-
/** Fallback client entry used when the merged assets have no `entry`. */
|
|
47
|
-
clientEntry?: string;
|
|
48
|
-
/**
|
|
49
|
-
* App-wide `<head>` defaults — same shape as the `head()` API. Route-level
|
|
50
|
-
* head (from loaders/pages) is merged on top, so anything here acts as a
|
|
51
|
-
* base. Use it for the default `title`, app meta, and inline scripts.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* head: {
|
|
55
|
-
* title: "Ilha + Oxide",
|
|
56
|
-
* script: [{ children: themeScript }],
|
|
57
|
-
* }
|
|
58
|
-
*/
|
|
59
|
-
head?: HeadInput;
|
|
60
|
-
/** Options forwarded to `pageRouter.renderHydratable` / `renderResponse`. */
|
|
61
|
-
renderOptions?: HydratableRenderOptions;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* SSR host helper for Ilha apps. Wires the generated `pageRouter` /
|
|
65
|
-
* `registry` and the asset manifest into a single `fetch`-style handler so a
|
|
66
|
-
* host entry collapses to:
|
|
4
|
+
* Default export is an oxidejs-style fetch middleware:
|
|
5
|
+
* `(request) => Response | undefined`. Returns `undefined` for any request it
|
|
6
|
+
* does not own, so hosts can chain it ahead of their own handler:
|
|
67
7
|
*
|
|
68
8
|
* ```ts
|
|
69
|
-
*
|
|
70
|
-
* import client from "./entry-client.ts?assets=client";
|
|
71
|
-
* import server from "./entry-server.ts?assets=ssr";
|
|
72
|
-
*
|
|
73
|
-
* const handler = new IlhaHandler({
|
|
74
|
-
* assets: mergeAssets({ client, server }),
|
|
75
|
-
* head: appHead({ title: "My app", script: [{ children: "..." }] }),
|
|
76
|
-
* });
|
|
77
|
-
*
|
|
78
|
-
* export default { fetch: (request: Request) => handler.handle(request) };
|
|
9
|
+
* oxide({ middleware: ["@ilha/router/ssr"] });
|
|
79
10
|
* ```
|
|
11
|
+
*
|
|
12
|
+
* Serves:
|
|
13
|
+
* - `POST /__ilha/frame` — re-renders a server island (JSON `{ id, path }` in,
|
|
14
|
+
* `{ html }` out). Renderers come from the process-global registry
|
|
15
|
+
* populated by self-registration code appended to `.server` modules.
|
|
16
|
+
* - `GET /__ilha/loader?path=…` — regular-page server loads via the loader
|
|
17
|
+
* runner (`setFrameLoaderRunner`, wired by the generated server module).
|
|
80
18
|
*/
|
|
81
|
-
export declare
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
private readonly head;
|
|
87
|
-
private readonly renderOptions;
|
|
88
|
-
constructor(options: IlhaHandlerOptions);
|
|
89
|
-
/** Render the document shell around an already-rendered island body. */
|
|
90
|
-
document(body: string, head?: SerializedHead): string;
|
|
91
|
-
/** Handle an incoming request and return a full HTML / redirect Response. */
|
|
92
|
-
handle(request: Request): Promise<Response>;
|
|
93
|
-
private handleInner;
|
|
94
|
-
}
|
|
19
|
+
export declare const FRAME_ENDPOINT = "/__ilha/frame";
|
|
20
|
+
/** Regular-page server loads: served through the loader-runner slot. */
|
|
21
|
+
export declare const LOADER_ENDPOINT = "/__ilha/loader";
|
|
22
|
+
declare function ssr(request: Request): Promise<Response | undefined>;
|
|
23
|
+
export default ssr;
|
package/dist/ssr.js
CHANGED
|
@@ -1,152 +1,160 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import "ilha:loaders";
|
|
1
|
+
import { t as runWithIslandRequest } from "./request-scope-C4reU4v0.js";
|
|
2
|
+
import { FrameError, getFrameGuard, getFrameLoaderRunner, renderServerIsland } from "./server-island-registry.js";
|
|
4
3
|
|
|
5
4
|
//#region src/ssr.ts
|
|
6
5
|
/**
|
|
7
|
-
*
|
|
8
|
-
*/
|
|
9
|
-
function mergeAssets({ client, server }) {
|
|
10
|
-
return client.merge(server);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* App-wide `<head>` defaults for `IlhaHandler`. Same shape as route `head()`;
|
|
14
|
-
* use so entry files read clearly: `head: appHead({ title, script: [...] })`.
|
|
15
|
-
*/
|
|
16
|
-
function appHead(head) {
|
|
17
|
-
return head;
|
|
18
|
-
}
|
|
19
|
-
const ATTR_ESC = {
|
|
20
|
-
"&": "&",
|
|
21
|
-
"<": "<",
|
|
22
|
-
">": ">",
|
|
23
|
-
"\"": """,
|
|
24
|
-
"'": "'"
|
|
25
|
-
};
|
|
26
|
-
function escapeAttr(value) {
|
|
27
|
-
return value.replace(/[&<>"']/g, (c) => ATTR_ESC[c]);
|
|
28
|
-
}
|
|
29
|
-
function stylesheetTag(attrs) {
|
|
30
|
-
const devId = attrs["data-vite-dev-id"] ? ` data-vite-dev-id="${escapeAttr(attrs["data-vite-dev-id"])}"` : "";
|
|
31
|
-
return `<link rel="stylesheet" href="${escapeAttr(attrs.href)}"${devId} />`;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* SSR host helper for Ilha apps. Wires the generated `pageRouter` /
|
|
35
|
-
* `registry` and the asset manifest into a single `fetch`-style handler so a
|
|
36
|
-
* host entry collapses to:
|
|
6
|
+
* Production SSR endpoints for server-owned islands and regular-page loads.
|
|
37
7
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* import server from "./entry-server.ts?assets=ssr";
|
|
8
|
+
* Default export is an oxidejs-style fetch middleware:
|
|
9
|
+
* `(request) => Response | undefined`. Returns `undefined` for any request it
|
|
10
|
+
* does not own, so hosts can chain it ahead of their own handler:
|
|
42
11
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* head: appHead({ title: "My app", script: [{ children: "..." }] }),
|
|
46
|
-
* });
|
|
47
|
-
*
|
|
48
|
-
* export default { fetch: (request: Request) => handler.handle(request) };
|
|
12
|
+
* ```ts
|
|
13
|
+
* oxide({ middleware: ["@ilha/router/ssr"] });
|
|
49
14
|
* ```
|
|
15
|
+
*
|
|
16
|
+
* Serves:
|
|
17
|
+
* - `POST /__ilha/frame` — re-renders a server island (JSON `{ id, path }` in,
|
|
18
|
+
* `{ html }` out). Renderers come from the process-global registry
|
|
19
|
+
* populated by self-registration code appended to `.server` modules.
|
|
20
|
+
* - `GET /__ilha/loader?path=…` — regular-page server loads via the loader
|
|
21
|
+
* runner (`setFrameLoaderRunner`, wired by the generated server module).
|
|
50
22
|
*/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const routeHead = head?.headTags ? `\n ${head.headTags}` : "";
|
|
72
|
-
const htmlAttrsStr = head?.htmlAttrs ?? "";
|
|
73
|
-
const langFromHead = htmlAttrsStr.match(/\blang="([^"]*)"/)?.[1];
|
|
74
|
-
return `<!doctype html>
|
|
75
|
-
<html lang="${langFromHead == null ? this.lang : langFromHead}"${htmlAttrsStr.replace(/\s*lang="[^"]*"/, "")}>
|
|
76
|
-
<head>
|
|
77
|
-
<meta charset="UTF-8" />
|
|
78
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
79
|
-
${titleTag}<link rel="icon" href="/favicon.svg" />
|
|
80
|
-
${styles}${routeHead}
|
|
81
|
-
</head>
|
|
82
|
-
<body${head?.bodyAttrs ?? ""}>
|
|
83
|
-
<div id="${escapeAttr(this.appId)}">${body}</div>
|
|
84
|
-
<script type="module" src="${escapeAttr(clientEntry)}"><\/script>
|
|
85
|
-
</body>
|
|
86
|
-
</html>`;
|
|
23
|
+
const FRAME_ENDPOINT = "/__ilha/frame";
|
|
24
|
+
/** Regular-page server loads: served through the loader-runner slot. */
|
|
25
|
+
const LOADER_ENDPOINT = "/__ilha/loader";
|
|
26
|
+
/** Max request body size — matches the dev middleware cap. */
|
|
27
|
+
const MAX_BODY = 16384;
|
|
28
|
+
function json(status, body) {
|
|
29
|
+
return new Response(JSON.stringify(body), {
|
|
30
|
+
status,
|
|
31
|
+
headers: {
|
|
32
|
+
"cache-control": "no-store",
|
|
33
|
+
"content-type": "application/json;charset=utf-8"
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async function ssr(request) {
|
|
38
|
+
let pathname;
|
|
39
|
+
try {
|
|
40
|
+
pathname = new URL(request.url).pathname;
|
|
41
|
+
} catch {
|
|
42
|
+
return json(400, { error: "frame failed" });
|
|
87
43
|
}
|
|
88
|
-
|
|
89
|
-
|
|
44
|
+
if (pathname !== "/__ilha/frame" && pathname !== "/__ilha/loader") return;
|
|
45
|
+
const origin = request.headers.get("origin");
|
|
46
|
+
const host = request.headers.get("host");
|
|
47
|
+
if (origin && host && origin !== `http://${host}` && origin !== `https://${host}`) return json(403, { error: "frame failed" });
|
|
48
|
+
if (pathname === "/__ilha/loader") {
|
|
49
|
+
if (request.method !== "GET") return json(405, { error: "method not allowed" });
|
|
90
50
|
try {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return
|
|
95
|
-
status: 500,
|
|
96
|
-
headers: { "content-type": "text/plain;charset=utf-8" }
|
|
97
|
-
});
|
|
51
|
+
const denied = await getFrameGuard()?.(request);
|
|
52
|
+
if (denied) return denied;
|
|
53
|
+
} catch {
|
|
54
|
+
return json(403, { error: "loader failed" });
|
|
98
55
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
56
|
+
const runner = getFrameLoaderRunner();
|
|
57
|
+
if (!runner) return json(404, {
|
|
58
|
+
kind: "error",
|
|
59
|
+
status: 404,
|
|
60
|
+
message: "not found"
|
|
61
|
+
});
|
|
62
|
+
const cl = request.headers.get("content-length");
|
|
63
|
+
if (cl && Number(cl) > MAX_BODY) return json(413, { error: "frame failed" });
|
|
64
|
+
let target = "/";
|
|
102
65
|
try {
|
|
103
|
-
|
|
66
|
+
target = new URL(request.url).searchParams.get("path") ?? "/";
|
|
104
67
|
} catch {
|
|
105
|
-
return
|
|
68
|
+
return json(400, {
|
|
69
|
+
kind: "error",
|
|
106
70
|
status: 400,
|
|
107
|
-
|
|
71
|
+
message: "bad request"
|
|
108
72
|
});
|
|
109
73
|
}
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
74
|
+
if (!target.startsWith("/") || target.includes("//") || target.length > 2048) return json(400, {
|
|
75
|
+
kind: "error",
|
|
76
|
+
status: 400,
|
|
77
|
+
message: "bad request"
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
const result = await runner(target);
|
|
81
|
+
if (result.kind === "redirect") return json(result.status || 302, {
|
|
82
|
+
kind: "redirect",
|
|
83
|
+
to: result.to,
|
|
84
|
+
status: result.status
|
|
121
85
|
});
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
86
|
+
if (result.kind !== "data") {
|
|
87
|
+
const status = result.status || 500;
|
|
88
|
+
return json(status, {
|
|
89
|
+
kind: result.kind,
|
|
90
|
+
status,
|
|
91
|
+
message: result.message
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return json(200, result);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("[ilha-router] loader endpoint failed:", error);
|
|
97
|
+
return json(500, {
|
|
98
|
+
kind: "error",
|
|
99
|
+
status: 500,
|
|
100
|
+
message: "loader failed"
|
|
131
101
|
});
|
|
132
102
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
103
|
+
}
|
|
104
|
+
if (request.method !== "POST") return json(405, { error: "frame failed" });
|
|
105
|
+
if (!(request.headers.get("content-type") ?? "").startsWith("application/json")) return json(415, { error: "frame failed" });
|
|
106
|
+
try {
|
|
107
|
+
const denied = await getFrameGuard()?.(request);
|
|
108
|
+
if (denied) return denied;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error("[ilha-router] frame guard failed:", error);
|
|
111
|
+
return json(403, { error: "frame failed" });
|
|
112
|
+
}
|
|
113
|
+
let id;
|
|
114
|
+
let path = "/";
|
|
115
|
+
try {
|
|
116
|
+
const text = await request.text();
|
|
117
|
+
if (text.length > MAX_BODY) return json(413, { error: "frame failed" });
|
|
118
|
+
const body = JSON.parse(text);
|
|
119
|
+
id = String(body.id ?? "");
|
|
120
|
+
if (typeof body.path === "string" && body.path.startsWith("/") && !body.path.includes("//") && body.path.length <= 2048) path = body.path;
|
|
121
|
+
} catch {
|
|
122
|
+
return json(400, { error: "frame failed" });
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
const headers = new Headers();
|
|
126
|
+
for (const name of [
|
|
127
|
+
"cookie",
|
|
128
|
+
"authorization",
|
|
129
|
+
"user-agent",
|
|
130
|
+
"x-forwarded-for"
|
|
131
|
+
]) {
|
|
132
|
+
const value = request.headers.get(name);
|
|
133
|
+
if (value !== null) headers.set(name, value);
|
|
134
|
+
}
|
|
135
|
+
const scoped = new Request(new URL(path, `http://${host ?? "localhost"}`), {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers
|
|
147
138
|
});
|
|
139
|
+
for (const sym of Object.getOwnPropertySymbols(request)) {
|
|
140
|
+
if (Symbol.keyFor(sym) === void 0) continue;
|
|
141
|
+
try {
|
|
142
|
+
scoped[sym] = request[sym];
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
return json(200, { html: await renderServerIsland(id, scoped, (scopedRequest, fn) => Promise.resolve(runWithIslandRequest(scopedRequest, fn))) });
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof FrameError) {
|
|
148
|
+
if (error.redirect) return json(error.status, { redirect: error.redirect });
|
|
149
|
+
if (error.status >= 500) console.error("[ilha-router] frame render failed:", error);
|
|
150
|
+
return json(error.status, { error: "frame failed" });
|
|
151
|
+
}
|
|
152
|
+
console.error("[ilha-router] frame render failed:", error);
|
|
153
|
+
return json(400, { error: "frame failed" });
|
|
148
154
|
}
|
|
149
|
-
}
|
|
155
|
+
}
|
|
156
|
+
/** Side-effect imports required alongside this handler. */
|
|
157
|
+
ssr.imports = ["ilha:pages/server", "ilha:loaders"];
|
|
150
158
|
|
|
151
159
|
//#endregion
|
|
152
|
-
export {
|
|
160
|
+
export { FRAME_ENDPOINT, LOADER_ENDPOINT, ssr as default };
|
package/dist/vite.d.ts
CHANGED
package/dist/vite.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ilhaPages } from "./plugin-
|
|
1
|
+
import { t as ilhaPages } from "./plugin-DogkskcY.js";
|
|
2
2
|
|
|
3
3
|
//#region src/vite.ts
|
|
4
4
|
/** Vite plugin — use via `@ilha/router/vite`. */
|
|
@@ -7,4 +7,4 @@ function pages(options = {}) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
//#endregion
|
|
10
|
-
export {
|
|
10
|
+
export { pages as default, pages, ilhaPages };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ilha/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "A tiny SPA router for Ilha",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontend",
|
|
@@ -39,10 +39,18 @@
|
|
|
39
39
|
"types": "./dist/index.d.ts",
|
|
40
40
|
"import": "./dist/index.js"
|
|
41
41
|
},
|
|
42
|
+
"./server-island": {
|
|
43
|
+
"types": "./dist/server-island.d.ts",
|
|
44
|
+
"import": "./dist/server-island.js"
|
|
45
|
+
},
|
|
42
46
|
"./ssr": {
|
|
43
47
|
"types": "./dist/ssr.d.ts",
|
|
44
48
|
"import": "./dist/ssr.js"
|
|
45
49
|
},
|
|
50
|
+
"./server-island-registry": {
|
|
51
|
+
"types": "./dist/server-island-registry.d.ts",
|
|
52
|
+
"import": "./dist/server-island-registry.js"
|
|
53
|
+
},
|
|
46
54
|
"./vite": {
|
|
47
55
|
"types": "./dist/vite.d.ts",
|
|
48
56
|
"import": "./dist/vite.js"
|
|
@@ -65,14 +73,13 @@
|
|
|
65
73
|
"test": "bun test"
|
|
66
74
|
},
|
|
67
75
|
"dependencies": {
|
|
68
|
-
"rou3": "0.9.1",
|
|
69
76
|
"unplugin": "3.3.0"
|
|
70
77
|
},
|
|
71
78
|
"devDependencies": {
|
|
72
|
-
"ilha": "0.
|
|
73
|
-
"vite": "^8.2.
|
|
79
|
+
"ilha": "0.11.0",
|
|
80
|
+
"vite": "^8.2.2"
|
|
74
81
|
},
|
|
75
82
|
"peerDependencies": {
|
|
76
|
-
"ilha": ">=0.
|
|
83
|
+
"ilha": ">=0.11.0"
|
|
77
84
|
}
|
|
78
85
|
}
|