@meekojs/meeko 0.0.3 → 0.1.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/README.md +35 -16
- package/dist/autoimports-ts.d.ts +26 -0
- package/dist/autoimports-ts.js +183 -0
- package/dist/autoimports-ts.js.map +1 -0
- package/dist/autoimports.d.ts +21 -0
- package/dist/autoimports.js +301 -0
- package/dist/autoimports.js.map +1 -0
- package/dist/check.js +74 -33
- package/dist/check.js.map +1 -0
- package/dist/cli.js +67 -5
- package/dist/cli.js.map +1 -0
- package/dist/compiler/events.d.ts +2 -0
- package/dist/compiler/events.js +6 -0
- package/dist/compiler/events.js.map +1 -0
- package/dist/compiler/import-graph.d.ts +8 -0
- package/dist/compiler/import-graph.js +33 -0
- package/dist/compiler/import-graph.js.map +1 -0
- package/dist/compiler/mapping.js +1 -0
- package/dist/compiler/mapping.js.map +1 -0
- package/dist/compiler/project-root.d.ts +2 -0
- package/dist/compiler/project-root.js +18 -0
- package/dist/compiler/project-root.js.map +1 -0
- package/dist/compiler/props.d.ts +12 -0
- package/dist/compiler/props.js +32 -0
- package/dist/compiler/props.js.map +1 -0
- package/dist/compiler/sfc.js +1 -0
- package/dist/compiler/sfc.js.map +1 -0
- package/dist/compiler/sourcemap.d.ts +3 -0
- package/dist/compiler/sourcemap.js +75 -0
- package/dist/compiler/sourcemap.js.map +1 -0
- package/dist/compiler/template.js +1 -0
- package/dist/compiler/template.js.map +1 -0
- package/dist/compiler.js +66 -17
- package/dist/compiler.js.map +1 -0
- package/dist/component-errors.d.ts +18 -0
- package/dist/component-errors.js +65 -0
- package/dist/component-errors.js.map +1 -0
- package/dist/devtools-ui/index.js +1 -0
- package/dist/devtools-ui/index.js.map +1 -0
- package/dist/devtools.js +3 -4
- package/dist/devtools.js.map +1 -0
- package/dist/dom.d.ts +18 -5
- package/dist/dom.js +295 -90
- package/dist/dom.js.map +1 -0
- package/dist/error-observer.d.ts +6 -0
- package/dist/error-observer.js +41 -0
- package/dist/error-observer.js.map +1 -0
- package/dist/framework/check-app.d.ts +5 -0
- package/dist/framework/check-app.js +214 -0
- package/dist/framework/check-app.js.map +1 -0
- package/dist/framework/check-project.d.ts +4 -0
- package/dist/framework/check-project.js +42 -0
- package/dist/framework/check-project.js.map +1 -0
- package/dist/framework/client.d.ts +48 -0
- package/dist/framework/client.js +249 -0
- package/dist/framework/client.js.map +1 -0
- package/dist/framework/clients.d.ts +82 -0
- package/dist/framework/clients.js +190 -0
- package/dist/framework/clients.js.map +1 -0
- package/dist/framework/config-type.d.ts +10 -0
- package/dist/framework/config-type.js +4 -0
- package/dist/framework/config-type.js.map +1 -0
- package/dist/framework/config.d.ts +15 -0
- package/dist/framework/config.js +138 -0
- package/dist/framework/config.js.map +1 -0
- package/dist/framework/contracts-generate.d.ts +8 -0
- package/dist/framework/contracts-generate.js +290 -0
- package/dist/framework/contracts-generate.js.map +1 -0
- package/dist/framework/generate.d.ts +7 -0
- package/dist/framework/generate.js +67 -0
- package/dist/framework/generate.js.map +1 -0
- package/dist/framework/init.d.ts +2 -0
- package/dist/framework/init.js +183 -0
- package/dist/framework/init.js.map +1 -0
- package/dist/framework/middleware.d.ts +14 -0
- package/dist/framework/middleware.js +16 -0
- package/dist/framework/middleware.js.map +1 -0
- package/dist/framework/modules.d.ts +22 -0
- package/dist/framework/modules.js +29 -0
- package/dist/framework/modules.js.map +1 -0
- package/dist/framework/page-contracts-check.d.ts +8 -0
- package/dist/framework/page-contracts-check.js +110 -0
- package/dist/framework/page-contracts-check.js.map +1 -0
- package/dist/framework/page-contracts-generate.d.ts +2 -0
- package/dist/framework/page-contracts-generate.js +94 -0
- package/dist/framework/page-contracts-generate.js.map +1 -0
- package/dist/framework/page.d.ts +64 -0
- package/dist/framework/page.js +44 -0
- package/dist/framework/page.js.map +1 -0
- package/dist/framework/prerender.d.ts +14 -0
- package/dist/framework/prerender.js +110 -0
- package/dist/framework/prerender.js.map +1 -0
- package/dist/framework/project-templates.d.ts +3 -0
- package/dist/framework/project-templates.js +119 -0
- package/dist/framework/project-templates.js.map +1 -0
- package/dist/framework/project.d.ts +9 -0
- package/dist/framework/project.js +272 -0
- package/dist/framework/project.js.map +1 -0
- package/dist/framework/router.d.ts +41 -0
- package/dist/framework/router.js +259 -0
- package/dist/framework/router.js.map +1 -0
- package/dist/framework/server.d.ts +65 -0
- package/dist/framework/server.js +239 -0
- package/dist/framework/server.js.map +1 -0
- package/dist/framework/socket-contracts-generate.d.ts +5 -0
- package/dist/framework/socket-contracts-generate.js +162 -0
- package/dist/framework/socket-contracts-generate.js.map +1 -0
- package/dist/framework/socket.d.ts +72 -0
- package/dist/framework/socket.js +252 -0
- package/dist/framework/socket.js.map +1 -0
- package/dist/framework/standard-schema-types.d.ts +7 -0
- package/dist/framework/standard-schema-types.js +69 -0
- package/dist/framework/standard-schema-types.js.map +1 -0
- package/dist/framework/state.d.ts +22 -0
- package/dist/framework/state.js +136 -0
- package/dist/framework/state.js.map +1 -0
- package/dist/framework/styles.d.ts +2 -0
- package/dist/framework/styles.js +6 -0
- package/dist/framework/styles.js.map +1 -0
- package/dist/framework/type-declarations.d.ts +5 -0
- package/dist/framework/type-declarations.js +84 -0
- package/dist/framework/type-declarations.js.map +1 -0
- package/dist/framework-server/auto-clients.d.ts +3 -0
- package/dist/framework-server/auto-clients.js +27 -0
- package/dist/framework-server/auto-clients.js.map +1 -0
- package/dist/framework-server/context.d.ts +21 -0
- package/dist/framework-server/context.js +22 -0
- package/dist/framework-server/context.js.map +1 -0
- package/dist/framework-server/contracts.d.ts +73 -0
- package/dist/framework-server/contracts.js +184 -0
- package/dist/framework-server/contracts.js.map +1 -0
- package/dist/framework-server/discovery.d.ts +43 -0
- package/dist/framework-server/discovery.js +257 -0
- package/dist/framework-server/discovery.js.map +1 -0
- package/dist/framework-server/local.d.ts +21 -0
- package/dist/framework-server/local.js +100 -0
- package/dist/framework-server/local.js.map +1 -0
- package/dist/framework-server/middleware.d.ts +4 -0
- package/dist/framework-server/middleware.js +56 -0
- package/dist/framework-server/middleware.js.map +1 -0
- package/dist/framework-server/node-upgrade.d.ts +23 -0
- package/dist/framework-server/node-upgrade.js +96 -0
- package/dist/framework-server/node-upgrade.js.map +1 -0
- package/dist/framework-server/node-websocket.d.ts +16 -0
- package/dist/framework-server/node-websocket.js +127 -0
- package/dist/framework-server/node-websocket.js.map +1 -0
- package/dist/framework-server/openapi.d.ts +17 -0
- package/dist/framework-server/openapi.js +118 -0
- package/dist/framework-server/openapi.js.map +1 -0
- package/dist/framework-server/session-stores.d.ts +73 -0
- package/dist/framework-server/session-stores.js +122 -0
- package/dist/framework-server/session-stores.js.map +1 -0
- package/dist/framework-server/sessions.d.ts +70 -0
- package/dist/framework-server/sessions.js +167 -0
- package/dist/framework-server/sessions.js.map +1 -0
- package/dist/framework-server/socket.d.ts +83 -0
- package/dist/framework-server/socket.js +196 -0
- package/dist/framework-server/socket.js.map +1 -0
- package/dist/framework-server.d.ts +23 -0
- package/dist/framework-server.js +13 -0
- package/dist/framework-server.js.map +1 -0
- package/dist/framework.d.ts +31 -0
- package/dist/framework.js +14 -0
- package/dist/framework.js.map +1 -0
- package/dist/hmr.js +75 -24
- package/dist/hmr.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -0
- package/dist/init.js +31 -3
- package/dist/init.js.map +1 -0
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +3 -2
- package/dist/internal.js.map +1 -0
- package/dist/node.d.ts +16 -0
- package/dist/node.js +122 -0
- package/dist/node.js.map +1 -0
- package/dist/reactivity.d.ts +22 -1
- package/dist/reactivity.js +132 -18
- package/dist/reactivity.js.map +1 -0
- package/dist/server.js +1 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +26 -9
- package/dist/types.js.map +1 -0
- package/dist/vite.js +112 -7
- package/dist/vite.js.map +1 -0
- package/package.json +15 -4
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { mkdir, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { extname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { serializeJson } from "../server.js";
|
|
7
|
+
import { attachNodeWebSocketServer, toNodeListener } from "../node.js";
|
|
8
|
+
import { errorResponse, reportServerError } from "../framework-server/contracts.js";
|
|
9
|
+
import { composeServerMiddleware } from "../framework-server/middleware.js";
|
|
10
|
+
import { discoverConfiguredRoutes } from "./config.js";
|
|
11
|
+
import { prerenderProject } from "./prerender.js";
|
|
12
|
+
import { clientEntrySource, serverEntrySource } from "./project-templates.js";
|
|
13
|
+
import { stylesheetLinks } from "./styles.js";
|
|
14
|
+
export const frameworkClientEntry = ".meeko/generated/entry-client.ts";
|
|
15
|
+
export const frameworkServerEntry = ".meeko/generated/entry-server.ts";
|
|
16
|
+
const clientManifestFile = ".meeko/output/client/.vite/manifest.json";
|
|
17
|
+
const serverBundleFile = ".meeko/output/server/entry.js";
|
|
18
|
+
/** Writes framework-owned Vite entries; no application file is overwritten. */
|
|
19
|
+
export async function prepareProject(projectRoot) {
|
|
20
|
+
const root = resolve(projectRoot);
|
|
21
|
+
const generated = resolve(root, ".meeko/generated");
|
|
22
|
+
await mkdir(generated, { recursive: true });
|
|
23
|
+
await Promise.all([
|
|
24
|
+
writeFile(resolve(root, frameworkClientEntry), clientEntrySource, "utf8"),
|
|
25
|
+
writeFile(resolve(root, frameworkServerEntry), serverEntrySource, "utf8"),
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
/** Builds both Vite targets and prerenders routes with the same generated entries. */
|
|
29
|
+
export async function buildProject(projectRoot) {
|
|
30
|
+
const root = resolve(projectRoot);
|
|
31
|
+
await prepareProject(root);
|
|
32
|
+
const { build } = await import("vite");
|
|
33
|
+
const { meeko } = await import("../vite.js");
|
|
34
|
+
await build({
|
|
35
|
+
root, configFile: false, plugins: [meeko()],
|
|
36
|
+
build: {
|
|
37
|
+
outDir: ".meeko/output/client", manifest: true,
|
|
38
|
+
rollupOptions: { input: frameworkClientEntry },
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
await build({
|
|
42
|
+
root, configFile: false, plugins: [meeko()],
|
|
43
|
+
ssr: { external: ["@meekojs/meeko"] },
|
|
44
|
+
build: {
|
|
45
|
+
ssr: frameworkServerEntry,
|
|
46
|
+
outDir: ".meeko/output/server",
|
|
47
|
+
rollupOptions: { output: { entryFileNames: "entry.js" } },
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
await prerenderProject(root);
|
|
51
|
+
}
|
|
52
|
+
function portableRoutes(root, routes) {
|
|
53
|
+
const relativeFile = (file) => {
|
|
54
|
+
const path = relative(root, file);
|
|
55
|
+
if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path) || resolve(file) === root) {
|
|
56
|
+
throw new Error(`Route file escaped project: ${file}`);
|
|
57
|
+
}
|
|
58
|
+
return "/" + path.replaceAll("\\", "/");
|
|
59
|
+
};
|
|
60
|
+
return routes.map((route) => ({
|
|
61
|
+
id: route.id,
|
|
62
|
+
file: relativeFile(route.file),
|
|
63
|
+
layouts: route.layouts.map(relativeFile),
|
|
64
|
+
...(route.error ? { error: relativeFile(route.error) } : {}),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
async function readAsset(assetRoot, pathname) {
|
|
68
|
+
const file = resolve(assetRoot, "." + pathname);
|
|
69
|
+
if (!file.startsWith(assetRoot + sep))
|
|
70
|
+
return { status: 403 };
|
|
71
|
+
let actual;
|
|
72
|
+
try {
|
|
73
|
+
actual = await realpath(file);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code === "ENOENT")
|
|
77
|
+
return null;
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
const actualRoot = await realpath(assetRoot);
|
|
81
|
+
if (!actual.startsWith(actualRoot + sep))
|
|
82
|
+
return { status: 403 };
|
|
83
|
+
return { status: 200, body: await readFile(actual) };
|
|
84
|
+
}
|
|
85
|
+
/** Starts the package-owned Node 24 HTTP host for development or production. */
|
|
86
|
+
export async function startProject(projectRoot, production) {
|
|
87
|
+
const root = resolve(projectRoot);
|
|
88
|
+
const port = Number(process.env.PORT || 4173);
|
|
89
|
+
if (!production)
|
|
90
|
+
await prepareProject(root);
|
|
91
|
+
let vite;
|
|
92
|
+
let runtime;
|
|
93
|
+
let socketServer;
|
|
94
|
+
const reportError = (error, requestId) => {
|
|
95
|
+
reportServerError(error, requestId, (failure, id) => {
|
|
96
|
+
process.stderr.write(`[meeko] request error ${id} (${failure instanceof Error ? failure.name : "UnknownError"})\n`);
|
|
97
|
+
if (!production)
|
|
98
|
+
console.error(failure);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
try {
|
|
102
|
+
if (!production) {
|
|
103
|
+
const { createServer: createViteServer } = await import("vite");
|
|
104
|
+
const { meeko } = await import("../vite.js");
|
|
105
|
+
vite = await createViteServer({
|
|
106
|
+
root, configFile: false, plugins: [meeko()],
|
|
107
|
+
server: {
|
|
108
|
+
middlewareMode: true,
|
|
109
|
+
// Middleware mode opens a separate Vite WebSocket server. Derive its
|
|
110
|
+
// port from the app port so concurrent projects do not share 24678.
|
|
111
|
+
hmr: { host: "127.0.0.1", port: process.env.MEEKO_HMR_PORT
|
|
112
|
+
? Number(process.env.MEEKO_HMR_PORT)
|
|
113
|
+
: ((port + 32767) % 65535) + 1 },
|
|
114
|
+
},
|
|
115
|
+
appType: "custom",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const clientRoot = resolve(root, ".meeko/output/client");
|
|
119
|
+
const publicRoot = resolve(root, "public");
|
|
120
|
+
const routes = await discoverConfiguredRoutes(root);
|
|
121
|
+
let clientEntry = "/" + frameworkClientEntry;
|
|
122
|
+
let productionStyles = "";
|
|
123
|
+
if (production) {
|
|
124
|
+
const manifest = JSON.parse(await readFile(resolve(root, clientManifestFile), "utf8"));
|
|
125
|
+
const entry = manifest[frameworkClientEntry];
|
|
126
|
+
if (!entry?.file)
|
|
127
|
+
throw new Error(`Missing ${frameworkClientEntry} in Vite client manifest`);
|
|
128
|
+
clientEntry = "/" + entry.file;
|
|
129
|
+
productionStyles = stylesheetLinks((entry.css ?? []).map((file) => "/" + file));
|
|
130
|
+
}
|
|
131
|
+
const globalCss = resolve(root, "app/styles/main.css");
|
|
132
|
+
const serverEntry = production
|
|
133
|
+
? await import(pathToFileURL(resolve(root, serverBundleFile)).href)
|
|
134
|
+
: await vite.ssrLoadModule("/" + frameworkServerEntry);
|
|
135
|
+
const loadDevModule = vite
|
|
136
|
+
? (file) => vite.ssrLoadModule("/" + relative(root, file).replaceAll("\\", "/"))
|
|
137
|
+
: undefined;
|
|
138
|
+
const appRuntime = await serverEntry.createAppRuntime(root, routes, clientEntry, production, loadDevModule);
|
|
139
|
+
runtime = appRuntime;
|
|
140
|
+
const portable = portableRoutes(root, routes);
|
|
141
|
+
const decorated = composeServerMiddleware(appRuntime.serverMiddleware, async (request) => {
|
|
142
|
+
const pathname = new URL(request.url).pathname;
|
|
143
|
+
if (pathname.startsWith("/api/") || pathname.startsWith("/_meeko/rpc/"))
|
|
144
|
+
return appRuntime.endpointHandler(request);
|
|
145
|
+
const result = await appRuntime.pageHandler(request);
|
|
146
|
+
if (result.status !== 200 || !(result.headers.get("content-type") ?? "").includes("text/html"))
|
|
147
|
+
return result;
|
|
148
|
+
const developmentStyles = !production && (await stat(globalCss).catch((error) => {
|
|
149
|
+
if (error.code === "ENOENT")
|
|
150
|
+
return undefined;
|
|
151
|
+
throw error;
|
|
152
|
+
}))?.isFile() ? stylesheetLinks(["/app/styles/main.css?direct"]) : "";
|
|
153
|
+
let html = (await result.text())
|
|
154
|
+
.replace("</head>", `${production ? productionStyles : developmentStyles}</head>`)
|
|
155
|
+
.replace("</body>", `<script id="meeko-routes" type="application/json">${serializeJson(portable)}</script></body>`);
|
|
156
|
+
if (vite)
|
|
157
|
+
html = await vite.transformIndexHtml(pathname, html);
|
|
158
|
+
return new Response(request.method === "HEAD" ? null : html, { status: result.status, headers: result.headers });
|
|
159
|
+
});
|
|
160
|
+
const pageListener = toNodeListener(decorated, { onError: reportError });
|
|
161
|
+
const types = { ".js": "text/javascript", ".css": "text/css", ".svg": "image/svg+xml", ".png": "image/png" };
|
|
162
|
+
const server = createServer((request, response) => {
|
|
163
|
+
let pathname;
|
|
164
|
+
try {
|
|
165
|
+
pathname = new URL(request.url || "/", "http://localhost").pathname;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
pageListener(request, response);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (production && /\.[a-z0-9]+$/i.test(pathname)) {
|
|
172
|
+
void (async () => {
|
|
173
|
+
const asset = await readAsset(clientRoot, pathname) ?? await readAsset(publicRoot, pathname);
|
|
174
|
+
if (!asset || asset.status !== 200) {
|
|
175
|
+
response.writeHead(asset?.status ?? 404);
|
|
176
|
+
response.end();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
response.writeHead(200, { "content-type": types[extname(pathname)] || "application/octet-stream" });
|
|
180
|
+
response.end(asset.body);
|
|
181
|
+
})().catch(async (error) => {
|
|
182
|
+
const requestId = randomUUID();
|
|
183
|
+
reportError(error, requestId);
|
|
184
|
+
const destroySafely = (cause, previous) => {
|
|
185
|
+
try {
|
|
186
|
+
response.destroy(cause instanceof Error ? cause : undefined);
|
|
187
|
+
}
|
|
188
|
+
catch (destroyError) {
|
|
189
|
+
if (!Object.is(destroyError, error) && !Object.is(destroyError, cause) && !Object.is(destroyError, previous)) {
|
|
190
|
+
reportError(destroyError, requestId);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
try {
|
|
195
|
+
if (response.headersSent) {
|
|
196
|
+
destroySafely(error);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const failure = errorResponse(error, requestId);
|
|
200
|
+
const failureHeaders = {};
|
|
201
|
+
failure.headers.forEach((value, name) => { failureHeaders[name] = value; });
|
|
202
|
+
const cookies = failure.headers.getSetCookie();
|
|
203
|
+
if (cookies.length)
|
|
204
|
+
failureHeaders["set-cookie"] = cookies;
|
|
205
|
+
response.writeHead(failure.status, failureHeaders);
|
|
206
|
+
response.end(await failure.text());
|
|
207
|
+
}
|
|
208
|
+
catch (failure) {
|
|
209
|
+
if (!Object.is(failure, error))
|
|
210
|
+
reportError(failure, requestId);
|
|
211
|
+
destroySafely(failure, error);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (vite) {
|
|
217
|
+
vite.middlewares(request, response, () => pageListener(request, response));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
pageListener(request, response);
|
|
221
|
+
});
|
|
222
|
+
socketServer = appRuntime.manifest.socket?.length ? attachNodeWebSocketServer({
|
|
223
|
+
server, hub: appRuntime.socketHub,
|
|
224
|
+
publicOrigin: process.env.PUBLIC_ORIGIN || `http://127.0.0.1:${port}`,
|
|
225
|
+
onError: (error) => reportError(error, randomUUID()),
|
|
226
|
+
}) : undefined;
|
|
227
|
+
await new Promise((done, fail) => {
|
|
228
|
+
server.once("error", fail);
|
|
229
|
+
server.listen(port, "127.0.0.1", () => {
|
|
230
|
+
server.off("error", fail);
|
|
231
|
+
done();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
let stopping = false;
|
|
235
|
+
const shutdown = () => {
|
|
236
|
+
if (stopping)
|
|
237
|
+
return;
|
|
238
|
+
stopping = true;
|
|
239
|
+
process.off("SIGINT", shutdown);
|
|
240
|
+
process.off("SIGTERM", shutdown);
|
|
241
|
+
void (async () => {
|
|
242
|
+
const steps = [
|
|
243
|
+
() => socketServer?.close() ?? Promise.resolve(),
|
|
244
|
+
() => new Promise((done, fail) => server.close((error) => error ? fail(error) : done())),
|
|
245
|
+
() => appRuntime.sessions.close(),
|
|
246
|
+
() => vite?.close() ?? Promise.resolve(),
|
|
247
|
+
];
|
|
248
|
+
for (const close of steps) {
|
|
249
|
+
try {
|
|
250
|
+
await close();
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
reportError(error, randomUUID());
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
})();
|
|
257
|
+
};
|
|
258
|
+
process.once("SIGINT", shutdown);
|
|
259
|
+
process.once("SIGTERM", shutdown);
|
|
260
|
+
process.stdout.write(`http://127.0.0.1:${port}\n`);
|
|
261
|
+
return server;
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
const cleanup = await Promise.allSettled([socketServer?.close(), runtime?.sessions.close(), vite?.close()]);
|
|
265
|
+
for (const result of cleanup) {
|
|
266
|
+
if (result.status === "rejected")
|
|
267
|
+
reportError(result.reason, randomUUID());
|
|
268
|
+
}
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/framework/project.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createServer } from \"node:http\";\nimport { mkdir, readFile, realpath, stat, writeFile } from \"node:fs/promises\";\nimport { extname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { Server } from \"node:http\";\nimport type { ViteDevServer } from \"vite\";\nimport { serializeJson } from \"../server.js\";\nimport { attachNodeWebSocketServer, toNodeListener } from \"../node.js\";\nimport { errorResponse, reportServerError } from \"../framework-server/contracts.js\";\nimport { composeServerMiddleware } from \"../framework-server/middleware.js\";\nimport type { ServerMiddleware } from \"../framework-server/middleware.js\";\nimport { discoverConfiguredRoutes } from \"./config.js\";\nimport { prerenderProject } from \"./prerender.js\";\nimport { clientEntrySource, serverEntrySource } from \"./project-templates.js\";\nimport { stylesheetLinks } from \"./styles.js\";\nimport type { RouteRecord } from \"./router.js\";\n\nexport const frameworkClientEntry = \".meeko/generated/entry-client.ts\";\nexport const frameworkServerEntry = \".meeko/generated/entry-server.ts\";\nconst clientManifestFile = \".meeko/output/client/.vite/manifest.json\";\nconst serverBundleFile = \".meeko/output/server/entry.js\";\n\ninterface AppRuntime {\n readonly pageHandler: (request: Request) => Promise<Response>;\n readonly endpointHandler: (request: Request) => Promise<Response>;\n readonly socketHub: Parameters<typeof attachNodeWebSocketServer>[0][\"hub\"];\n readonly sessions: { close(): Promise<void> };\n readonly manifest: { readonly socket?: readonly unknown[] };\n readonly serverMiddleware: readonly ServerMiddleware[];\n}\n\ninterface AppEntry {\n createAppRuntime(\n root: string,\n routes: readonly RouteRecord[],\n clientEntry: string,\n production: boolean,\n loadDevModule?: (file: string) => Promise<Record<string, unknown>>,\n ): Promise<AppRuntime>;\n}\n\n/** Writes framework-owned Vite entries; no application file is overwritten. */\nexport async function prepareProject(projectRoot: string): Promise<void> {\n const root = resolve(projectRoot);\n const generated = resolve(root, \".meeko/generated\");\n await mkdir(generated, { recursive: true });\n await Promise.all([\n writeFile(resolve(root, frameworkClientEntry), clientEntrySource, \"utf8\"),\n writeFile(resolve(root, frameworkServerEntry), serverEntrySource, \"utf8\"),\n ]);\n}\n\n/** Builds both Vite targets and prerenders routes with the same generated entries. */\nexport async function buildProject(projectRoot: string): Promise<void> {\n const root = resolve(projectRoot);\n await prepareProject(root);\n const { build } = await import(\"vite\");\n const { meeko } = await import(\"../vite.js\");\n await build({\n root, configFile: false, plugins: [meeko()],\n build: {\n outDir: \".meeko/output/client\", manifest: true,\n rollupOptions: { input: frameworkClientEntry },\n },\n });\n await build({\n root, configFile: false, plugins: [meeko()],\n ssr: { external: [\"@meekojs/meeko\"] },\n build: {\n ssr: frameworkServerEntry,\n outDir: \".meeko/output/server\",\n rollupOptions: { output: { entryFileNames: \"entry.js\" } },\n },\n });\n await prerenderProject(root);\n}\n\nfunction portableRoutes(root: string, routes: readonly RouteRecord[]): unknown[] {\n const relativeFile = (file: string): string => {\n const path = relative(root, file);\n if (path === \"..\" || path.startsWith(`..${sep}`) || isAbsolute(path) || resolve(file) === root) {\n throw new Error(`Route file escaped project: ${file}`);\n }\n return \"/\" + path.replaceAll(\"\\\\\", \"/\");\n };\n return routes.map((route) => ({\n id: route.id,\n file: relativeFile(route.file),\n layouts: route.layouts.map(relativeFile),\n ...(route.error ? { error: relativeFile(route.error) } : {}),\n }));\n}\n\nasync function readAsset(assetRoot: string, pathname: string): Promise<{ status: number; body?: Buffer } | null> {\n const file = resolve(assetRoot, \".\" + pathname);\n if (!file.startsWith(assetRoot + sep)) return { status: 403 };\n let actual: string;\n try { actual = await realpath(file); }\n catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n const actualRoot = await realpath(assetRoot);\n if (!actual.startsWith(actualRoot + sep)) return { status: 403 };\n return { status: 200, body: await readFile(actual) };\n}\n\n/** Starts the package-owned Node 24 HTTP host for development or production. */\nexport async function startProject(projectRoot: string, production: boolean): Promise<Server> {\n const root = resolve(projectRoot);\n const port = Number(process.env.PORT || 4173);\n if (!production) await prepareProject(root);\n let vite: ViteDevServer | undefined;\n let runtime: AppRuntime | undefined;\n let socketServer: ReturnType<typeof attachNodeWebSocketServer> | undefined;\n const reportError = (error: unknown, requestId: string): void => {\n reportServerError(error, requestId, (failure, id) => {\n process.stderr.write(`[meeko] request error ${id} (${failure instanceof Error ? failure.name : \"UnknownError\"})\\n`);\n if (!production) console.error(failure);\n });\n };\n try {\n if (!production) {\n const { createServer: createViteServer } = await import(\"vite\");\n const { meeko } = await import(\"../vite.js\");\n vite = await createViteServer({\n root, configFile: false, plugins: [meeko()],\n server: {\n middlewareMode: true,\n // Middleware mode opens a separate Vite WebSocket server. Derive its\n // port from the app port so concurrent projects do not share 24678.\n hmr: { host: \"127.0.0.1\", port: process.env.MEEKO_HMR_PORT\n ? Number(process.env.MEEKO_HMR_PORT)\n : ((port + 32767) % 65535) + 1 },\n },\n appType: \"custom\",\n });\n }\n const clientRoot = resolve(root, \".meeko/output/client\");\n const publicRoot = resolve(root, \"public\");\n const routes = await discoverConfiguredRoutes(root);\n let clientEntry = \"/\" + frameworkClientEntry;\n let productionStyles = \"\";\n if (production) {\n const manifest = JSON.parse(await readFile(resolve(root, clientManifestFile), \"utf8\")) as Record<string, { file?: string; css?: string[] }>;\n const entry = manifest[frameworkClientEntry];\n if (!entry?.file) throw new Error(`Missing ${frameworkClientEntry} in Vite client manifest`);\n clientEntry = \"/\" + entry.file;\n productionStyles = stylesheetLinks((entry.css ?? []).map((file) => \"/\" + file));\n }\n const globalCss = resolve(root, \"app/styles/main.css\");\n const serverEntry = production\n ? await import(pathToFileURL(resolve(root, serverBundleFile)).href) as AppEntry\n : await vite!.ssrLoadModule(\"/\" + frameworkServerEntry) as AppEntry;\n const loadDevModule = vite\n ? (file: string) => vite!.ssrLoadModule(\"/\" + relative(root, file).replaceAll(\"\\\\\", \"/\")) as Promise<Record<string, unknown>>\n : undefined;\n const appRuntime = await serverEntry.createAppRuntime(root, routes, clientEntry, production, loadDevModule);\n runtime = appRuntime;\n const portable = portableRoutes(root, routes);\n const decorated = composeServerMiddleware(appRuntime.serverMiddleware, async (request: Request): Promise<Response> => {\n const pathname = new URL(request.url).pathname;\n if (pathname.startsWith(\"/api/\") || pathname.startsWith(\"/_meeko/rpc/\")) return appRuntime.endpointHandler(request);\n const result = await appRuntime.pageHandler(request);\n if (result.status !== 200 || !(result.headers.get(\"content-type\") ?? \"\").includes(\"text/html\")) return result;\n const developmentStyles = !production && (await stat(globalCss).catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return undefined;\n throw error;\n }))?.isFile() ? stylesheetLinks([\"/app/styles/main.css?direct\"]) : \"\";\n let html = (await result.text())\n .replace(\"</head>\", `${production ? productionStyles : developmentStyles}</head>`)\n .replace(\"</body>\", `<script id=\"meeko-routes\" type=\"application/json\">${serializeJson(portable)}</script></body>`);\n if (vite) html = await vite.transformIndexHtml(pathname, html);\n return new Response(request.method === \"HEAD\" ? null : html, { status: result.status, headers: result.headers });\n });\n const pageListener = toNodeListener(decorated, { onError: reportError });\n const types: Record<string, string> = { \".js\": \"text/javascript\", \".css\": \"text/css\", \".svg\": \"image/svg+xml\", \".png\": \"image/png\" };\n const server = createServer((request, response) => {\n let pathname: string;\n try { pathname = new URL(request.url || \"/\", \"http://localhost\").pathname; }\n catch { pageListener(request, response); return; }\n if (production && /\\.[a-z0-9]+$/i.test(pathname)) {\n void (async () => {\n const asset = await readAsset(clientRoot, pathname) ?? await readAsset(publicRoot, pathname);\n if (!asset || asset.status !== 200) { response.writeHead(asset?.status ?? 404); response.end(); return; }\n response.writeHead(200, { \"content-type\": types[extname(pathname)] || \"application/octet-stream\" });\n response.end(asset.body);\n })().catch(async (error: unknown) => {\n const requestId = randomUUID();\n reportError(error, requestId);\n const destroySafely = (cause: unknown, previous?: unknown): void => {\n try { response.destroy(cause instanceof Error ? cause : undefined); }\n catch (destroyError) {\n if (!Object.is(destroyError, error) && !Object.is(destroyError, cause) && !Object.is(destroyError, previous)) {\n reportError(destroyError, requestId);\n }\n }\n };\n try {\n if (response.headersSent) { destroySafely(error); return; }\n const failure = errorResponse(error, requestId);\n const failureHeaders: Record<string, string | string[]> = {};\n failure.headers.forEach((value, name) => { failureHeaders[name] = value; });\n const cookies = failure.headers.getSetCookie();\n if (cookies.length) failureHeaders[\"set-cookie\"] = cookies;\n response.writeHead(failure.status, failureHeaders);\n response.end(await failure.text());\n } catch (failure) {\n if (!Object.is(failure, error)) reportError(failure, requestId);\n destroySafely(failure, error);\n }\n });\n return;\n }\n if (vite) {\n vite.middlewares(request, response, () => pageListener(request, response));\n return;\n }\n pageListener(request, response);\n });\n socketServer = appRuntime.manifest.socket?.length ? attachNodeWebSocketServer({\n server, hub: appRuntime.socketHub,\n publicOrigin: process.env.PUBLIC_ORIGIN || `http://127.0.0.1:${port}`,\n onError: (error) => reportError(error, randomUUID()),\n }) : undefined;\n await new Promise<void>((done, fail) => {\n server.once(\"error\", fail);\n server.listen(port, \"127.0.0.1\", () => {\n server.off(\"error\", fail);\n done();\n });\n });\n let stopping = false;\n const shutdown = () => {\n if (stopping) return;\n stopping = true;\n process.off(\"SIGINT\", shutdown);\n process.off(\"SIGTERM\", shutdown);\n void (async () => {\n const steps: Array<() => Promise<unknown>> = [\n () => socketServer?.close() ?? Promise.resolve(),\n () => new Promise<void>((done, fail) => server.close((error) => error ? fail(error) : done())),\n () => appRuntime.sessions.close(),\n () => vite?.close() ?? Promise.resolve(),\n ];\n for (const close of steps) {\n try { await close(); }\n catch (error) { reportError(error, randomUUID()); }\n }\n })();\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n process.stdout.write(`http://127.0.0.1:${port}\\n`);\n return server;\n } catch (error) {\n const cleanup = await Promise.allSettled([socketServer?.close(), runtime?.sessions.close(), vite?.close()]);\n for (const result of cleanup) {\n if (result.status === \"rejected\") reportError(result.reason, randomUUID());\n }\n throw error;\n }\n}\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** A route manifest is shared by the server renderer and browser navigator. */
|
|
2
|
+
export interface RouteRecord {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly file: string;
|
|
5
|
+
readonly layouts: readonly string[];
|
|
6
|
+
readonly error?: string;
|
|
7
|
+
readonly sidecar?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface RouteMatch {
|
|
10
|
+
readonly route: RouteRecord;
|
|
11
|
+
readonly params: Readonly<Record<string, string>>;
|
|
12
|
+
}
|
|
13
|
+
/** Validates collisions before sorting static routes ahead of parameters. */
|
|
14
|
+
export declare function createRouteManifest(routes: readonly RouteRecord[]): readonly RouteRecord[];
|
|
15
|
+
/** Resolves a URL against the same manifest used for SSR and client navigation. */
|
|
16
|
+
export declare function matchRoute(routes: readonly RouteRecord[], pathname: string): RouteMatch | undefined;
|
|
17
|
+
/** Builds a URL from a manifest route and rejects absent or extra parameters. */
|
|
18
|
+
export declare function href(routes: readonly RouteRecord[], id: string, options?: {
|
|
19
|
+
params?: Readonly<Record<string, string>>;
|
|
20
|
+
search?: Readonly<Record<string, string | number | boolean | null | undefined>>;
|
|
21
|
+
}): string;
|
|
22
|
+
export interface NavigationContext {
|
|
23
|
+
readonly url: URL;
|
|
24
|
+
readonly match: RouteMatch;
|
|
25
|
+
readonly signal: AbortSignal;
|
|
26
|
+
/** Longest prefix shared with the previous route's layout chain. */
|
|
27
|
+
readonly retainedLayouts: number;
|
|
28
|
+
}
|
|
29
|
+
export interface Navigator {
|
|
30
|
+
navigate(to: string, options?: {
|
|
31
|
+
replace?: boolean;
|
|
32
|
+
}): Promise<boolean>;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
35
|
+
/** Browser events cannot return a navigation promise, so surface failures safely. */
|
|
36
|
+
export declare function reportNavigationError(browser: Window, error: unknown, observer?: (error: unknown) => void): void;
|
|
37
|
+
/**
|
|
38
|
+
* The host owns actual DOM outlet rendering. Its callback receives layout
|
|
39
|
+
* identity so it can retain matching layout instances and release exited ones.
|
|
40
|
+
*/
|
|
41
|
+
export declare function createNavigator(routes: readonly RouteRecord[], render: (context: NavigationContext) => Promise<void | boolean> | void | boolean, browser?: Window, onError?: (error: unknown) => void): Navigator;
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { observeCallback } from "../error-observer.js";
|
|
2
|
+
function segments(id) {
|
|
3
|
+
if (!id.startsWith("/"))
|
|
4
|
+
throw new TypeError(`Route must start with /: ${id}`);
|
|
5
|
+
return id.split("/").slice(1).filter(Boolean).map((part, index, all) => {
|
|
6
|
+
if (/^\[\.\.\.[A-Za-z_$][\w$]*\]$/.test(part)) {
|
|
7
|
+
if (index !== all.length - 1)
|
|
8
|
+
throw new TypeError(`Catch-all must be final: ${id}`);
|
|
9
|
+
return { kind: "rest", value: part.slice(4, -1) };
|
|
10
|
+
}
|
|
11
|
+
if (/^\[[A-Za-z_$][\w$]*\]$/.test(part))
|
|
12
|
+
return { kind: "param", value: part.slice(1, -1) };
|
|
13
|
+
if (part.includes("[") || part.includes("]"))
|
|
14
|
+
throw new TypeError(`Invalid route segment: ${part}`);
|
|
15
|
+
return { kind: "static", value: part };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function rank(id) {
|
|
19
|
+
return segments(id).map((part) => part.kind === "static" ? 3 : part.kind === "param" ? 2 : 1);
|
|
20
|
+
}
|
|
21
|
+
function compareRoute(a, b) {
|
|
22
|
+
const left = rank(a.id);
|
|
23
|
+
const right = rank(b.id);
|
|
24
|
+
for (let index = 0; index < Math.max(left.length, right.length); index++) {
|
|
25
|
+
const score = (right[index] ?? 0) - (left[index] ?? 0);
|
|
26
|
+
if (score)
|
|
27
|
+
return score;
|
|
28
|
+
}
|
|
29
|
+
return a.id.localeCompare(b.id);
|
|
30
|
+
}
|
|
31
|
+
function collisionKey(id) {
|
|
32
|
+
return segments(id).map((part) => part.kind === "static" ? part.value : part.kind === "param" ? "[]" : "[...]").join("/");
|
|
33
|
+
}
|
|
34
|
+
function decodePart(part) {
|
|
35
|
+
try {
|
|
36
|
+
const value = decodeURIComponent(part);
|
|
37
|
+
return value.includes("/") || value.includes("\\") || value === "." || value === ".." ? undefined : value;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Validates collisions before sorting static routes ahead of parameters. */
|
|
44
|
+
export function createRouteManifest(routes) {
|
|
45
|
+
const keys = new Map();
|
|
46
|
+
for (const route of routes) {
|
|
47
|
+
const key = collisionKey(route.id);
|
|
48
|
+
const previous = keys.get(key);
|
|
49
|
+
if (previous)
|
|
50
|
+
throw new Error(`Route collision: ${previous} and ${route.file} both resolve to ${key || "/"}`);
|
|
51
|
+
keys.set(key, route.file);
|
|
52
|
+
}
|
|
53
|
+
return Object.freeze([...routes].sort(compareRoute));
|
|
54
|
+
}
|
|
55
|
+
/** Resolves a URL against the same manifest used for SSR and client navigation. */
|
|
56
|
+
export function matchRoute(routes, pathname) {
|
|
57
|
+
if (!pathname.startsWith("/"))
|
|
58
|
+
return undefined;
|
|
59
|
+
const raw = pathname.split("/").slice(1).filter(Boolean);
|
|
60
|
+
const path = raw.map(decodePart);
|
|
61
|
+
if (path.some((part) => part === undefined))
|
|
62
|
+
return undefined;
|
|
63
|
+
for (const route of routes) {
|
|
64
|
+
const pattern = segments(route.id);
|
|
65
|
+
const params = {};
|
|
66
|
+
let index = 0;
|
|
67
|
+
let matched = true;
|
|
68
|
+
for (const part of pattern) {
|
|
69
|
+
if (part.kind === "rest") {
|
|
70
|
+
if (index === path.length) {
|
|
71
|
+
matched = false;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
params[part.value] = path.slice(index).join("/");
|
|
75
|
+
index = path.length;
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
const value = path[index++];
|
|
79
|
+
if (value === undefined || (part.kind === "static" && part.value !== value)) {
|
|
80
|
+
matched = false;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
if (part.kind === "param")
|
|
84
|
+
params[part.value] = value;
|
|
85
|
+
}
|
|
86
|
+
if (matched && index === path.length)
|
|
87
|
+
return { route, params };
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/** Builds a URL from a manifest route and rejects absent or extra parameters. */
|
|
92
|
+
export function href(routes, id, options = {}) {
|
|
93
|
+
if (!routes.some((route) => route.id === id))
|
|
94
|
+
throw new Error(`Unknown route: ${id}`);
|
|
95
|
+
const params = options.params ?? {};
|
|
96
|
+
const used = new Set();
|
|
97
|
+
const path = segments(id).map((part) => {
|
|
98
|
+
if (part.kind === "static")
|
|
99
|
+
return encodeURIComponent(part.value);
|
|
100
|
+
const value = params[part.value];
|
|
101
|
+
if (!value)
|
|
102
|
+
throw new Error(`Missing route parameter: ${part.value}`);
|
|
103
|
+
used.add(part.value);
|
|
104
|
+
if (part.kind === "rest") {
|
|
105
|
+
const pieces = value.split("/");
|
|
106
|
+
if (pieces.some((piece) => !piece || piece === "." || piece === ".."))
|
|
107
|
+
throw new Error(`Invalid catch-all parameter: ${part.value}`);
|
|
108
|
+
return pieces.map(encodeURIComponent).join("/");
|
|
109
|
+
}
|
|
110
|
+
if (value.includes("/") || value === "." || value === "..")
|
|
111
|
+
throw new Error(`Invalid route parameter: ${part.value}`);
|
|
112
|
+
return encodeURIComponent(value);
|
|
113
|
+
});
|
|
114
|
+
for (const key of Object.keys(params))
|
|
115
|
+
if (!used.has(key))
|
|
116
|
+
throw new Error(`Unknown route parameter: ${key}`);
|
|
117
|
+
const url = new URL(path.length ? `/${path.join("/")}` : "/", "http://meeko.local");
|
|
118
|
+
for (const [key, value] of Object.entries(options.search ?? {}))
|
|
119
|
+
if (value !== null && value !== undefined)
|
|
120
|
+
url.searchParams.set(key, String(value));
|
|
121
|
+
return url.pathname + url.search;
|
|
122
|
+
}
|
|
123
|
+
/** Browser events cannot return a navigation promise, so surface failures safely. */
|
|
124
|
+
export function reportNavigationError(browser, error, observer) {
|
|
125
|
+
const logger = browser.console ?? console;
|
|
126
|
+
const reportObserverFailure = (observerError) => {
|
|
127
|
+
try {
|
|
128
|
+
logger.error("Meeko navigation error observer failed", observerError, error);
|
|
129
|
+
}
|
|
130
|
+
catch { /* A broken console must not produce an unhandled rejection. */ }
|
|
131
|
+
};
|
|
132
|
+
if (observer)
|
|
133
|
+
observeCallback(() => observer(error), reportObserverFailure);
|
|
134
|
+
else
|
|
135
|
+
observeCallback(() => logger.error("Meeko navigation failed", error), () => { });
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The host owns actual DOM outlet rendering. Its callback receives layout
|
|
139
|
+
* identity so it can retain matching layout instances and release exited ones.
|
|
140
|
+
*/
|
|
141
|
+
export function createNavigator(routes, render, browser = window, onError) {
|
|
142
|
+
let current = matchRoute(routes, browser.location.pathname);
|
|
143
|
+
let currentUrl = browser.location.href;
|
|
144
|
+
let pending;
|
|
145
|
+
let disposed = false;
|
|
146
|
+
const navigate = async (to, options = {}) => {
|
|
147
|
+
if (disposed)
|
|
148
|
+
throw new Error("Navigator disposed");
|
|
149
|
+
const url = new URL(to, browser.location.href);
|
|
150
|
+
if (url.origin !== browser.location.origin)
|
|
151
|
+
throw new TypeError("External navigation is not a Meeko route");
|
|
152
|
+
const match = matchRoute(routes, url.pathname);
|
|
153
|
+
if (!match) {
|
|
154
|
+
if (options.replace && browser.location.href === url.href) {
|
|
155
|
+
try {
|
|
156
|
+
browser.history.replaceState(null, "", currentUrl);
|
|
157
|
+
}
|
|
158
|
+
catch (historyError) {
|
|
159
|
+
try {
|
|
160
|
+
browser.location.replace(currentUrl);
|
|
161
|
+
}
|
|
162
|
+
catch (recoveryError) {
|
|
163
|
+
throw new AggregateError([historyError, recoveryError], "Meeko history and URL recovery failed");
|
|
164
|
+
}
|
|
165
|
+
throw historyError;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
pending?.abort();
|
|
171
|
+
const controller = new AbortController();
|
|
172
|
+
pending = controller;
|
|
173
|
+
let retainedLayouts = 0;
|
|
174
|
+
if (current) {
|
|
175
|
+
while (current.route.layouts[retainedLayouts] === match.route.layouts[retainedLayouts]
|
|
176
|
+
&& retainedLayouts < current.route.layouts.length
|
|
177
|
+
&& retainedLayouts < match.route.layouts.length)
|
|
178
|
+
retainedLayouts++;
|
|
179
|
+
}
|
|
180
|
+
let result;
|
|
181
|
+
try {
|
|
182
|
+
result = await render({ url, match, signal: controller.signal, retainedLayouts });
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (pending === controller)
|
|
186
|
+
pending = undefined;
|
|
187
|
+
if (options.replace && !controller.signal.aborted && !disposed && browser.location.href === url.href) {
|
|
188
|
+
try {
|
|
189
|
+
browser.history.replaceState(null, "", currentUrl);
|
|
190
|
+
}
|
|
191
|
+
catch (historyError) {
|
|
192
|
+
try {
|
|
193
|
+
browser.location.replace(currentUrl);
|
|
194
|
+
}
|
|
195
|
+
catch (recoveryError) {
|
|
196
|
+
throw new AggregateError([error, historyError, recoveryError], "Meeko navigation and URL recovery failed");
|
|
197
|
+
}
|
|
198
|
+
throw new AggregateError([error, historyError], "Meeko navigation and history recovery failed");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
if (result === false) {
|
|
204
|
+
if (pending === controller)
|
|
205
|
+
pending = undefined;
|
|
206
|
+
if (options.replace && !controller.signal.aborted && browser.location.href === url.href) {
|
|
207
|
+
try {
|
|
208
|
+
browser.history.replaceState(null, "", currentUrl);
|
|
209
|
+
}
|
|
210
|
+
catch (historyError) {
|
|
211
|
+
try {
|
|
212
|
+
browser.location.replace(currentUrl);
|
|
213
|
+
}
|
|
214
|
+
catch (recoveryError) {
|
|
215
|
+
throw new AggregateError([historyError, recoveryError], "Meeko history and URL recovery failed");
|
|
216
|
+
}
|
|
217
|
+
throw historyError;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
if (controller.signal.aborted || disposed)
|
|
223
|
+
return false;
|
|
224
|
+
try {
|
|
225
|
+
if (options.replace)
|
|
226
|
+
browser.history.replaceState(null, "", url);
|
|
227
|
+
else
|
|
228
|
+
browser.history.pushState(null, "", url);
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
if (pending === controller)
|
|
232
|
+
pending = undefined;
|
|
233
|
+
try {
|
|
234
|
+
browser.location.replace(currentUrl);
|
|
235
|
+
}
|
|
236
|
+
catch (recoveryError) {
|
|
237
|
+
throw new AggregateError([error, recoveryError], "Meeko history update and navigation recovery failed");
|
|
238
|
+
}
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
current = match;
|
|
242
|
+
currentUrl = url.href;
|
|
243
|
+
pending = undefined;
|
|
244
|
+
return true;
|
|
245
|
+
};
|
|
246
|
+
const onPop = () => {
|
|
247
|
+
void navigate(browser.location.href, { replace: true }).catch((error) => reportNavigationError(browser, error, onError));
|
|
248
|
+
};
|
|
249
|
+
browser.addEventListener("popstate", onPop);
|
|
250
|
+
return {
|
|
251
|
+
navigate,
|
|
252
|
+
dispose() {
|
|
253
|
+
disposed = true;
|
|
254
|
+
pending?.abort();
|
|
255
|
+
browser.removeEventListener("popstate", onPop);
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=router.js.map
|