@meekojs/meeko 0.0.4 → 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.
Files changed (139) hide show
  1. package/README.md +21 -19
  2. package/dist/autoimports-ts.d.ts +4 -4
  3. package/dist/autoimports-ts.js +86 -30
  4. package/dist/autoimports-ts.js.map +1 -1
  5. package/dist/autoimports.d.ts +2 -2
  6. package/dist/autoimports.js +23 -7
  7. package/dist/autoimports.js.map +1 -1
  8. package/dist/check.js +39 -10
  9. package/dist/check.js.map +1 -1
  10. package/dist/cli.js +37 -4
  11. package/dist/cli.js.map +1 -1
  12. package/dist/compiler/events.d.ts +2 -0
  13. package/dist/compiler/events.js +6 -0
  14. package/dist/compiler/events.js.map +1 -0
  15. package/dist/compiler/props.d.ts +12 -0
  16. package/dist/compiler/props.js +32 -0
  17. package/dist/compiler/props.js.map +1 -0
  18. package/dist/compiler.js +55 -12
  19. package/dist/compiler.js.map +1 -1
  20. package/dist/component-errors.d.ts +18 -0
  21. package/dist/component-errors.js +65 -0
  22. package/dist/component-errors.js.map +1 -0
  23. package/dist/devtools.js +2 -4
  24. package/dist/devtools.js.map +1 -1
  25. package/dist/dom.d.ts +11 -5
  26. package/dist/dom.js +288 -88
  27. package/dist/dom.js.map +1 -1
  28. package/dist/error-observer.d.ts +6 -0
  29. package/dist/error-observer.js +41 -0
  30. package/dist/error-observer.js.map +1 -0
  31. package/dist/framework/check-app.js +160 -5
  32. package/dist/framework/check-app.js.map +1 -1
  33. package/dist/framework/check-project.d.ts +4 -0
  34. package/dist/framework/check-project.js +42 -0
  35. package/dist/framework/check-project.js.map +1 -0
  36. package/dist/framework/client.d.ts +3 -0
  37. package/dist/framework/client.js +136 -33
  38. package/dist/framework/client.js.map +1 -1
  39. package/dist/framework/clients.js +12 -4
  40. package/dist/framework/clients.js.map +1 -1
  41. package/dist/framework/contracts-generate.js +66 -3
  42. package/dist/framework/contracts-generate.js.map +1 -1
  43. package/dist/framework/init.js +21 -200
  44. package/dist/framework/init.js.map +1 -1
  45. package/dist/framework/middleware.d.ts +14 -0
  46. package/dist/framework/middleware.js +16 -0
  47. package/dist/framework/middleware.js.map +1 -0
  48. package/dist/framework/page-contracts-check.d.ts +8 -0
  49. package/dist/framework/page-contracts-check.js +110 -0
  50. package/dist/framework/page-contracts-check.js.map +1 -0
  51. package/dist/framework/page-contracts-generate.d.ts +2 -0
  52. package/dist/framework/page-contracts-generate.js +94 -0
  53. package/dist/framework/page-contracts-generate.js.map +1 -0
  54. package/dist/framework/page.d.ts +7 -3
  55. package/dist/framework/page.js +3 -1
  56. package/dist/framework/page.js.map +1 -1
  57. package/dist/framework/prerender.js +11 -5
  58. package/dist/framework/prerender.js.map +1 -1
  59. package/dist/framework/project-templates.d.ts +3 -0
  60. package/dist/framework/project-templates.js +119 -0
  61. package/dist/framework/project-templates.js.map +1 -0
  62. package/dist/framework/project.d.ts +9 -0
  63. package/dist/framework/project.js +272 -0
  64. package/dist/framework/project.js.map +1 -0
  65. package/dist/framework/router.d.ts +3 -1
  66. package/dist/framework/router.js +94 -8
  67. package/dist/framework/router.js.map +1 -1
  68. package/dist/framework/server.d.ts +17 -3
  69. package/dist/framework/server.js +71 -24
  70. package/dist/framework/server.js.map +1 -1
  71. package/dist/framework/socket-contracts-generate.js +8 -5
  72. package/dist/framework/socket-contracts-generate.js.map +1 -1
  73. package/dist/framework/socket.d.ts +2 -0
  74. package/dist/framework/socket.js +119 -17
  75. package/dist/framework/socket.js.map +1 -1
  76. package/dist/framework/standard-schema-types.d.ts +7 -0
  77. package/dist/framework/standard-schema-types.js +69 -0
  78. package/dist/framework/standard-schema-types.js.map +1 -0
  79. package/dist/framework/state.d.ts +7 -0
  80. package/dist/framework/state.js +78 -14
  81. package/dist/framework/state.js.map +1 -1
  82. package/dist/framework/styles.d.ts +2 -0
  83. package/dist/framework/styles.js +6 -0
  84. package/dist/framework/styles.js.map +1 -0
  85. package/dist/framework/type-declarations.d.ts +5 -0
  86. package/dist/framework/type-declarations.js +84 -0
  87. package/dist/framework/type-declarations.js.map +1 -0
  88. package/dist/framework-server/contracts.d.ts +11 -2
  89. package/dist/framework-server/contracts.js +62 -7
  90. package/dist/framework-server/contracts.js.map +1 -1
  91. package/dist/framework-server/discovery.d.ts +4 -1
  92. package/dist/framework-server/discovery.js +21 -16
  93. package/dist/framework-server/discovery.js.map +1 -1
  94. package/dist/framework-server/local.d.ts +2 -0
  95. package/dist/framework-server/local.js +5 -3
  96. package/dist/framework-server/local.js.map +1 -1
  97. package/dist/framework-server/middleware.d.ts +4 -0
  98. package/dist/framework-server/middleware.js +56 -0
  99. package/dist/framework-server/middleware.js.map +1 -0
  100. package/dist/framework-server/node-upgrade.js +27 -9
  101. package/dist/framework-server/node-upgrade.js.map +1 -1
  102. package/dist/framework-server/node-websocket.js +4 -2
  103. package/dist/framework-server/node-websocket.js.map +1 -1
  104. package/dist/framework-server/session-stores.d.ts +9 -2
  105. package/dist/framework-server/session-stores.js +37 -11
  106. package/dist/framework-server/session-stores.js.map +1 -1
  107. package/dist/framework-server/sessions.d.ts +6 -0
  108. package/dist/framework-server/sessions.js +41 -10
  109. package/dist/framework-server/sessions.js.map +1 -1
  110. package/dist/framework-server/socket.js +62 -27
  111. package/dist/framework-server/socket.js.map +1 -1
  112. package/dist/framework-server.d.ts +2 -0
  113. package/dist/framework-server.js +1 -0
  114. package/dist/framework-server.js.map +1 -1
  115. package/dist/framework.d.ts +3 -1
  116. package/dist/framework.js +1 -0
  117. package/dist/framework.js.map +1 -1
  118. package/dist/hmr.js +74 -24
  119. package/dist/hmr.js.map +1 -1
  120. package/dist/index.d.ts +2 -0
  121. package/dist/index.js +1 -0
  122. package/dist/index.js.map +1 -1
  123. package/dist/init.js +27 -1
  124. package/dist/init.js.map +1 -1
  125. package/dist/internal.d.ts +2 -2
  126. package/dist/internal.js +2 -2
  127. package/dist/internal.js.map +1 -1
  128. package/dist/node.d.ts +5 -1
  129. package/dist/node.js +80 -10
  130. package/dist/node.js.map +1 -1
  131. package/dist/reactivity.d.ts +12 -3
  132. package/dist/reactivity.js +94 -17
  133. package/dist/reactivity.js.map +1 -1
  134. package/dist/types.d.ts +2 -0
  135. package/dist/types.js +3 -1
  136. package/dist/types.js.map +1 -1
  137. package/dist/vite.js +29 -9
  138. package/dist/vite.js.map +1 -1
  139. package/package.json +2 -2
@@ -0,0 +1,9 @@
1
+ import type { Server } from "node:http";
2
+ export declare const frameworkClientEntry = ".meeko/generated/entry-client.ts";
3
+ export declare const frameworkServerEntry = ".meeko/generated/entry-server.ts";
4
+ /** Writes framework-owned Vite entries; no application file is overwritten. */
5
+ export declare function prepareProject(projectRoot: string): Promise<void>;
6
+ /** Builds both Vite targets and prerenders routes with the same generated entries. */
7
+ export declare function buildProject(projectRoot: string): Promise<void>;
8
+ /** Starts the package-owned Node 24 HTTP host for development or production. */
9
+ export declare function startProject(projectRoot: string, production: boolean): Promise<Server>;
@@ -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"]}
@@ -32,8 +32,10 @@ export interface Navigator {
32
32
  }): Promise<boolean>;
33
33
  dispose(): void;
34
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;
35
37
  /**
36
38
  * The host owns actual DOM outlet rendering. Its callback receives layout
37
39
  * identity so it can retain matching layout instances and release exited ones.
38
40
  */
39
- export declare function createNavigator(routes: readonly RouteRecord[], render: (context: NavigationContext) => Promise<void> | void, browser?: Window): Navigator;
41
+ export declare function createNavigator(routes: readonly RouteRecord[], render: (context: NavigationContext) => Promise<void | boolean> | void | boolean, browser?: Window, onError?: (error: unknown) => void): Navigator;
@@ -1,3 +1,4 @@
1
+ import { observeCallback } from "../error-observer.js";
1
2
  function segments(id) {
2
3
  if (!id.startsWith("/"))
3
4
  throw new TypeError(`Route must start with /: ${id}`);
@@ -119,12 +120,27 @@ export function href(routes, id, options = {}) {
119
120
  url.searchParams.set(key, String(value));
120
121
  return url.pathname + url.search;
121
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
+ }
122
137
  /**
123
138
  * The host owns actual DOM outlet rendering. Its callback receives layout
124
139
  * identity so it can retain matching layout instances and release exited ones.
125
140
  */
126
- export function createNavigator(routes, render, browser = window) {
141
+ export function createNavigator(routes, render, browser = window, onError) {
127
142
  let current = matchRoute(routes, browser.location.pathname);
143
+ let currentUrl = browser.location.href;
128
144
  let pending;
129
145
  let disposed = false;
130
146
  const navigate = async (to, options = {}) => {
@@ -134,8 +150,23 @@ export function createNavigator(routes, render, browser = window) {
134
150
  if (url.origin !== browser.location.origin)
135
151
  throw new TypeError("External navigation is not a Meeko route");
136
152
  const match = matchRoute(routes, url.pathname);
137
- if (!match)
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
+ }
138
168
  return false;
169
+ }
139
170
  pending?.abort();
140
171
  const controller = new AbortController();
141
172
  pending = controller;
@@ -146,19 +177,74 @@ export function createNavigator(routes, render, browser = window) {
146
177
  && retainedLayouts < match.route.layouts.length)
147
178
  retainedLayouts++;
148
179
  }
149
- await render({ url, match, signal: controller.signal, retainedLayouts });
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
+ }
150
222
  if (controller.signal.aborted || disposed)
151
223
  return false;
152
- if (options.replace)
153
- browser.history.replaceState(null, "", url);
154
- else
155
- browser.history.pushState(null, "", url);
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
+ }
156
241
  current = match;
242
+ currentUrl = url.href;
157
243
  pending = undefined;
158
244
  return true;
159
245
  };
160
246
  const onPop = () => {
161
- void navigate(browser.location.href, { replace: true });
247
+ void navigate(browser.location.href, { replace: true }).catch((error) => reportNavigationError(browser, error, onError));
162
248
  };
163
249
  browser.addEventListener("popstate", onPop);
164
250
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/framework/router.ts"],"names":[],"mappings":"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","sourcesContent":["/** A route manifest is shared by the server renderer and browser navigator. */\nexport interface RouteRecord {\n readonly id: string;\n readonly file: string;\n readonly layouts: readonly string[];\n readonly error?: string;\n readonly sidecar?: string;\n}\n\nexport interface RouteMatch {\n readonly route: RouteRecord;\n readonly params: Readonly<Record<string, string>>;\n}\n\ntype Segment = { kind: \"static\" | \"param\" | \"rest\"; value: string };\n\nfunction segments(id: string): Segment[] {\n if (!id.startsWith(\"/\")) throw new TypeError(`Route must start with /: ${id}`);\n return id.split(\"/\").slice(1).filter(Boolean).map((part, index, all) => {\n if (/^\\[\\.\\.\\.[A-Za-z_$][\\w$]*\\]$/.test(part)) {\n if (index !== all.length - 1) throw new TypeError(`Catch-all must be final: ${id}`);\n return { kind: \"rest\", value: part.slice(4, -1) };\n }\n if (/^\\[[A-Za-z_$][\\w$]*\\]$/.test(part)) return { kind: \"param\", value: part.slice(1, -1) };\n if (part.includes(\"[\") || part.includes(\"]\")) throw new TypeError(`Invalid route segment: ${part}`);\n return { kind: \"static\", value: part };\n });\n}\n\nfunction rank(id: string): number[] {\n return segments(id).map((part) => part.kind === \"static\" ? 3 : part.kind === \"param\" ? 2 : 1);\n}\n\nfunction compareRoute(a: RouteRecord, b: RouteRecord): number {\n const left = rank(a.id);\n const right = rank(b.id);\n for (let index = 0; index < Math.max(left.length, right.length); index++) {\n const score = (right[index] ?? 0) - (left[index] ?? 0);\n if (score) return score;\n }\n return a.id.localeCompare(b.id);\n}\n\nfunction collisionKey(id: string): string {\n return segments(id).map((part) => part.kind === \"static\" ? part.value : part.kind === \"param\" ? \"[]\" : \"[...]\").join(\"/\");\n}\n\nfunction decodePart(part: string): string | undefined {\n try {\n const value = decodeURIComponent(part);\n return value.includes(\"/\") || value.includes(\"\\\\\") || value === \".\" || value === \"..\" ? undefined : value;\n } catch {\n return undefined;\n }\n}\n\n/** Validates collisions before sorting static routes ahead of parameters. */\nexport function createRouteManifest(routes: readonly RouteRecord[]): readonly RouteRecord[] {\n const keys = new Map<string, string>();\n for (const route of routes) {\n const key = collisionKey(route.id);\n const previous = keys.get(key);\n if (previous) throw new Error(`Route collision: ${previous} and ${route.file} both resolve to ${key || \"/\"}`);\n keys.set(key, route.file);\n }\n return Object.freeze([...routes].sort(compareRoute));\n}\n\n/** Resolves a URL against the same manifest used for SSR and client navigation. */\nexport function matchRoute(routes: readonly RouteRecord[], pathname: string): RouteMatch | undefined {\n if (!pathname.startsWith(\"/\")) return undefined;\n const raw = pathname.split(\"/\").slice(1).filter(Boolean);\n const path = raw.map(decodePart);\n if (path.some((part) => part === undefined)) return undefined;\n for (const route of routes) {\n const pattern = segments(route.id);\n const params: Record<string, string> = {};\n let index = 0;\n let matched = true;\n for (const part of pattern) {\n if (part.kind === \"rest\") {\n if (index === path.length) { matched = false; break; }\n params[part.value] = path.slice(index).join(\"/\");\n index = path.length;\n break;\n }\n const value = path[index++];\n if (value === undefined || (part.kind === \"static\" && part.value !== value)) { matched = false; break; }\n if (part.kind === \"param\") params[part.value] = value;\n }\n if (matched && index === path.length) return { route, params };\n }\n return undefined;\n}\n\n/** Builds a URL from a manifest route and rejects absent or extra parameters. */\nexport function href(routes: readonly RouteRecord[], id: string, options: {\n params?: Readonly<Record<string, string>>;\n search?: Readonly<Record<string, string | number | boolean | null | undefined>>;\n} = {}): string {\n if (!routes.some((route) => route.id === id)) throw new Error(`Unknown route: ${id}`);\n const params = options.params ?? {};\n const used = new Set<string>();\n const path = segments(id).map((part) => {\n if (part.kind === \"static\") return encodeURIComponent(part.value);\n const value = params[part.value];\n if (!value) throw new Error(`Missing route parameter: ${part.value}`);\n used.add(part.value);\n if (part.kind === \"rest\") {\n const pieces = value.split(\"/\");\n if (pieces.some((piece) => !piece || piece === \".\" || piece === \"..\")) throw new Error(`Invalid catch-all parameter: ${part.value}`);\n return pieces.map(encodeURIComponent).join(\"/\");\n }\n if (value.includes(\"/\") || value === \".\" || value === \"..\") throw new Error(`Invalid route parameter: ${part.value}`);\n return encodeURIComponent(value);\n });\n for (const key of Object.keys(params)) if (!used.has(key)) throw new Error(`Unknown route parameter: ${key}`);\n const url = new URL(path.length ? `/${path.join(\"/\")}` : \"/\", \"http://meeko.local\");\n for (const [key, value] of Object.entries(options.search ?? {})) if (value !== null && value !== undefined) url.searchParams.set(key, String(value));\n return url.pathname + url.search;\n}\n\nexport interface NavigationContext {\n readonly url: URL;\n readonly match: RouteMatch;\n readonly signal: AbortSignal;\n /** Longest prefix shared with the previous route's layout chain. */\n readonly retainedLayouts: number;\n}\n\nexport interface Navigator {\n navigate(to: string, options?: { replace?: boolean }): Promise<boolean>;\n dispose(): void;\n}\n\n/**\n * The host owns actual DOM outlet rendering. Its callback receives layout\n * identity so it can retain matching layout instances and release exited ones.\n */\nexport function createNavigator(routes: readonly RouteRecord[], render: (context: NavigationContext) => Promise<void> | void, browser: Window = window): Navigator {\n let current = matchRoute(routes, browser.location.pathname);\n let pending: AbortController | undefined;\n let disposed = false;\n const navigate = async (to: string, options: { replace?: boolean } = {}): Promise<boolean> => {\n if (disposed) throw new Error(\"Navigator disposed\");\n const url = new URL(to, browser.location.href);\n if (url.origin !== browser.location.origin) throw new TypeError(\"External navigation is not a Meeko route\");\n const match = matchRoute(routes, url.pathname);\n if (!match) return false;\n pending?.abort();\n const controller = new AbortController();\n pending = controller;\n let retainedLayouts = 0;\n if (current) {\n while (current.route.layouts[retainedLayouts] === match.route.layouts[retainedLayouts]\n && retainedLayouts < current.route.layouts.length\n && retainedLayouts < match.route.layouts.length) retainedLayouts++;\n }\n await render({ url, match, signal: controller.signal, retainedLayouts });\n if (controller.signal.aborted || disposed) return false;\n if (options.replace) browser.history.replaceState(null, \"\", url);\n else browser.history.pushState(null, \"\", url);\n current = match;\n pending = undefined;\n return true;\n };\n const onPop = () => {\n void navigate(browser.location.href, { replace: true });\n };\n browser.addEventListener(\"popstate\", onPop);\n return {\n navigate,\n dispose() {\n disposed = true;\n pending?.abort();\n browser.removeEventListener(\"popstate\", onPop);\n },\n };\n}\n"]}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/framework/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAkBvD,SAAS,QAAQ,CAAC,EAAU;IAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;IAC/E,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrE,IAAI,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,IAAI,KAAK,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;YACpF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,CAAC;QACD,IAAI,wBAAwB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5F,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;QACpG,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,IAAI,CAAC,EAAU;IACtB,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChG,CAAC;AAED,SAAS,YAAY,CAAC,CAAc,EAAE,CAAc;IAClD,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxB,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACzE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;QACvD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5H,CAAC;AAED,SAAS,UAAU,CAAC,IAAY;IAC9B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;IAC5G,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,MAA8B;IAChE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,QAAQ,KAAK,CAAC,IAAI,oBAAoB,GAAG,IAAI,GAAG,EAAE,CAAC,CAAC;QAC9G,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;AACvD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,UAAU,CAAC,MAA8B,EAAE,QAAgB;IACzE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnC,MAAM,MAAM,GAA2B,EAAE,CAAC;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,OAAO,GAAG,IAAI,CAAC;QACnB,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzB,IAAI,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;oBAAC,OAAO,GAAG,KAAK,CAAC;oBAAC,MAAM;gBAAC,CAAC;gBACtD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjD,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;gBACpB,MAAM;YACR,CAAC;YACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC;gBAAC,OAAO,GAAG,KAAK,CAAC;gBAAC,MAAM;YAAC,CAAC;YACxG,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;QACxD,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACjE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,IAAI,CAAC,MAA8B,EAAE,EAAU,EAAE,OAAO,GAGpE,EAAE;IACJ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACrC,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YACrI,OAAO,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtH,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IACH,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,EAAE,CAAC,CAAC;IAC9G,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC;IACpF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;QAAE,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;YAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACrJ,OAAO,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC;AACnC,CAAC;AAeD,qFAAqF;AACrF,MAAM,UAAU,qBAAqB,CAAC,OAAe,EAAE,KAAc,EAAE,QAAmC;IACxG,MAAM,MAAM,GAAI,OAAsE,CAAC,OAAO,IAAI,OAAO,CAAC;IAC1G,MAAM,qBAAqB,GAAG,CAAC,aAAsB,EAAE,EAAE;QACvD,IAAI,CAAC;YAAC,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;QAAC,CAAC;QACrF,MAAM,CAAC,CAAC,+DAA+D,CAAC,CAAC;IAC3E,CAAC,CAAC;IACF,IAAI,QAAQ;QAAE,eAAe,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC,CAAC;;QACvE,eAAe,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAA8B,EAAE,MAAgF,EAAE,OAAO,GAAW,MAAM,EAAE,OAAkC;IAC5M,IAAI,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IACvC,IAAI,OAAoC,CAAC;IACzC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,QAAQ,GAAG,KAAK,EAAE,EAAU,EAAE,OAAO,GAA0B,EAAE,EAAoB,EAAE;QAC3F,IAAI,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,GAAG,CAAC,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,MAAM;YAAE,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;QAC5G,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC1D,IAAI,CAAC;oBAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;gBAAC,CAAC;gBAC3D,OAAO,YAAY,EAAE,CAAC;oBACpB,IAAI,CAAC;wBAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBAAC,CAAC;oBAC7C,OAAO,aAAa,EAAE,CAAC;wBACrB,MAAM,IAAI,cAAc,CAAC,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,uCAAuC,CAAC,CAAC;oBACnG,CAAC;oBACD,MAAM,YAAY,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,OAAO,GAAG,UAAU,CAAC;QACrB,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC;mBACjF,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM;mBAC9C,eAAe,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM;gBAAE,eAAe,EAAE,CAAC;QACvE,CAAC;QACD,IAAI,MAAsB,CAAC;QAC3B,IAAI,CAAC;YAAC,MAAM,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;QAAC,CAAC;QAC1F,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO,GAAG,SAAS,CAAC;YAChD,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;gBACrG,IAAI,CAAC;oBAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;gBAAC,CAAC;gBAC3D,OAAO,YAAY,EAAE,CAAC;oBACpB,IAAI,CAAC;wBAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBAAC,CAAC;oBAC7C,OAAO,aAAa,EAAE,CAAC;wBACrB,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,aAAa,CAAC,EAAE,0CAA0C,CAAC,CAAC;oBAC7G,CAAC;oBACD,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,8CAA8C,CAAC,CAAC;gBAClG,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO,GAAG,SAAS,CAAC;YAChD,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,EAAE,CAAC;gBACxF,IAAI,CAAC;oBAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC;gBAAC,CAAC;gBAC3D,OAAO,YAAY,EAAE,CAAC;oBACpB,IAAI,CAAC;wBAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;oBAAC,CAAC;oBAC7C,OAAO,aAAa,EAAE,CAAC;wBACrB,MAAM,IAAI,cAAc,CAAC,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,uCAAuC,CAAC,CAAC;oBACnG,CAAC;oBACD,MAAM,YAAY,CAAC;gBACrB,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,IAAI,QAAQ;YAAE,OAAO,KAAK,CAAC;QACxD,IAAI,CAAC;YACH,IAAI,OAAO,CAAC,OAAO;gBAAE,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;;gBAC5D,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,KAAK,UAAU;gBAAE,OAAO,GAAG,SAAS,CAAC;YAChD,IAAI,CAAC;gBAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAAC,CAAC;YAC7C,OAAO,aAAa,EAAE,CAAC;gBACrB,MAAM,IAAI,cAAc,CAAC,CAAC,KAAK,EAAE,aAAa,CAAC,EAAE,qDAAqD,CAAC,CAAC;YAC1G,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,OAAO,GAAG,KAAK,CAAC;QAChB,UAAU,GAAG,GAAG,CAAC,IAAI,CAAC;QACtB,OAAO,GAAG,SAAS,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,KAAK,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACpI,CAAC,CAAC;IACF,OAAO,CAAC,gBAAgB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC5C,OAAO;QACL,QAAQ;QACR,OAAO;YACL,QAAQ,GAAG,IAAI,CAAC;YAChB,OAAO,EAAE,KAAK,EAAE,CAAC;YACjB,OAAO,CAAC,mBAAmB,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { observeCallback } from \"../error-observer.js\";\n\n/** A route manifest is shared by the server renderer and browser navigator. */\nexport interface RouteRecord {\n readonly id: string;\n readonly file: string;\n readonly layouts: readonly string[];\n readonly error?: string;\n readonly sidecar?: string;\n}\n\nexport interface RouteMatch {\n readonly route: RouteRecord;\n readonly params: Readonly<Record<string, string>>;\n}\n\ntype Segment = { kind: \"static\" | \"param\" | \"rest\"; value: string };\n\nfunction segments(id: string): Segment[] {\n if (!id.startsWith(\"/\")) throw new TypeError(`Route must start with /: ${id}`);\n return id.split(\"/\").slice(1).filter(Boolean).map((part, index, all) => {\n if (/^\\[\\.\\.\\.[A-Za-z_$][\\w$]*\\]$/.test(part)) {\n if (index !== all.length - 1) throw new TypeError(`Catch-all must be final: ${id}`);\n return { kind: \"rest\", value: part.slice(4, -1) };\n }\n if (/^\\[[A-Za-z_$][\\w$]*\\]$/.test(part)) return { kind: \"param\", value: part.slice(1, -1) };\n if (part.includes(\"[\") || part.includes(\"]\")) throw new TypeError(`Invalid route segment: ${part}`);\n return { kind: \"static\", value: part };\n });\n}\n\nfunction rank(id: string): number[] {\n return segments(id).map((part) => part.kind === \"static\" ? 3 : part.kind === \"param\" ? 2 : 1);\n}\n\nfunction compareRoute(a: RouteRecord, b: RouteRecord): number {\n const left = rank(a.id);\n const right = rank(b.id);\n for (let index = 0; index < Math.max(left.length, right.length); index++) {\n const score = (right[index] ?? 0) - (left[index] ?? 0);\n if (score) return score;\n }\n return a.id.localeCompare(b.id);\n}\n\nfunction collisionKey(id: string): string {\n return segments(id).map((part) => part.kind === \"static\" ? part.value : part.kind === \"param\" ? \"[]\" : \"[...]\").join(\"/\");\n}\n\nfunction decodePart(part: string): string | undefined {\n try {\n const value = decodeURIComponent(part);\n return value.includes(\"/\") || value.includes(\"\\\\\") || value === \".\" || value === \"..\" ? undefined : value;\n } catch {\n return undefined;\n }\n}\n\n/** Validates collisions before sorting static routes ahead of parameters. */\nexport function createRouteManifest(routes: readonly RouteRecord[]): readonly RouteRecord[] {\n const keys = new Map<string, string>();\n for (const route of routes) {\n const key = collisionKey(route.id);\n const previous = keys.get(key);\n if (previous) throw new Error(`Route collision: ${previous} and ${route.file} both resolve to ${key || \"/\"}`);\n keys.set(key, route.file);\n }\n return Object.freeze([...routes].sort(compareRoute));\n}\n\n/** Resolves a URL against the same manifest used for SSR and client navigation. */\nexport function matchRoute(routes: readonly RouteRecord[], pathname: string): RouteMatch | undefined {\n if (!pathname.startsWith(\"/\")) return undefined;\n const raw = pathname.split(\"/\").slice(1).filter(Boolean);\n const path = raw.map(decodePart);\n if (path.some((part) => part === undefined)) return undefined;\n for (const route of routes) {\n const pattern = segments(route.id);\n const params: Record<string, string> = {};\n let index = 0;\n let matched = true;\n for (const part of pattern) {\n if (part.kind === \"rest\") {\n if (index === path.length) { matched = false; break; }\n params[part.value] = path.slice(index).join(\"/\");\n index = path.length;\n break;\n }\n const value = path[index++];\n if (value === undefined || (part.kind === \"static\" && part.value !== value)) { matched = false; break; }\n if (part.kind === \"param\") params[part.value] = value;\n }\n if (matched && index === path.length) return { route, params };\n }\n return undefined;\n}\n\n/** Builds a URL from a manifest route and rejects absent or extra parameters. */\nexport function href(routes: readonly RouteRecord[], id: string, options: {\n params?: Readonly<Record<string, string>>;\n search?: Readonly<Record<string, string | number | boolean | null | undefined>>;\n} = {}): string {\n if (!routes.some((route) => route.id === id)) throw new Error(`Unknown route: ${id}`);\n const params = options.params ?? {};\n const used = new Set<string>();\n const path = segments(id).map((part) => {\n if (part.kind === \"static\") return encodeURIComponent(part.value);\n const value = params[part.value];\n if (!value) throw new Error(`Missing route parameter: ${part.value}`);\n used.add(part.value);\n if (part.kind === \"rest\") {\n const pieces = value.split(\"/\");\n if (pieces.some((piece) => !piece || piece === \".\" || piece === \"..\")) throw new Error(`Invalid catch-all parameter: ${part.value}`);\n return pieces.map(encodeURIComponent).join(\"/\");\n }\n if (value.includes(\"/\") || value === \".\" || value === \"..\") throw new Error(`Invalid route parameter: ${part.value}`);\n return encodeURIComponent(value);\n });\n for (const key of Object.keys(params)) if (!used.has(key)) throw new Error(`Unknown route parameter: ${key}`);\n const url = new URL(path.length ? `/${path.join(\"/\")}` : \"/\", \"http://meeko.local\");\n for (const [key, value] of Object.entries(options.search ?? {})) if (value !== null && value !== undefined) url.searchParams.set(key, String(value));\n return url.pathname + url.search;\n}\n\nexport interface NavigationContext {\n readonly url: URL;\n readonly match: RouteMatch;\n readonly signal: AbortSignal;\n /** Longest prefix shared with the previous route's layout chain. */\n readonly retainedLayouts: number;\n}\n\nexport interface Navigator {\n navigate(to: string, options?: { replace?: boolean }): Promise<boolean>;\n dispose(): void;\n}\n\n/** Browser events cannot return a navigation promise, so surface failures safely. */\nexport function reportNavigationError(browser: Window, error: unknown, observer?: (error: unknown) => void): void {\n const logger = (browser as Window & { console?: { error(...args: unknown[]): void } }).console ?? console;\n const reportObserverFailure = (observerError: unknown) => {\n try { logger.error(\"Meeko navigation error observer failed\", observerError, error); }\n catch { /* A broken console must not produce an unhandled rejection. */ }\n };\n if (observer) observeCallback(() => observer(error), reportObserverFailure);\n else observeCallback(() => logger.error(\"Meeko navigation failed\", error), () => {});\n}\n\n/**\n * The host owns actual DOM outlet rendering. Its callback receives layout\n * identity so it can retain matching layout instances and release exited ones.\n */\nexport function createNavigator(routes: readonly RouteRecord[], render: (context: NavigationContext) => Promise<void | boolean> | void | boolean, browser: Window = window, onError?: (error: unknown) => void): Navigator {\n let current = matchRoute(routes, browser.location.pathname);\n let currentUrl = browser.location.href;\n let pending: AbortController | undefined;\n let disposed = false;\n const navigate = async (to: string, options: { replace?: boolean } = {}): Promise<boolean> => {\n if (disposed) throw new Error(\"Navigator disposed\");\n const url = new URL(to, browser.location.href);\n if (url.origin !== browser.location.origin) throw new TypeError(\"External navigation is not a Meeko route\");\n const match = matchRoute(routes, url.pathname);\n if (!match) {\n if (options.replace && browser.location.href === url.href) {\n try { browser.history.replaceState(null, \"\", currentUrl); }\n catch (historyError) {\n try { browser.location.replace(currentUrl); }\n catch (recoveryError) {\n throw new AggregateError([historyError, recoveryError], \"Meeko history and URL recovery failed\");\n }\n throw historyError;\n }\n }\n return false;\n }\n pending?.abort();\n const controller = new AbortController();\n pending = controller;\n let retainedLayouts = 0;\n if (current) {\n while (current.route.layouts[retainedLayouts] === match.route.layouts[retainedLayouts]\n && retainedLayouts < current.route.layouts.length\n && retainedLayouts < match.route.layouts.length) retainedLayouts++;\n }\n let result: void | boolean;\n try { result = await render({ url, match, signal: controller.signal, retainedLayouts }); }\n catch (error) {\n if (pending === controller) pending = undefined;\n if (options.replace && !controller.signal.aborted && !disposed && browser.location.href === url.href) {\n try { browser.history.replaceState(null, \"\", currentUrl); }\n catch (historyError) {\n try { browser.location.replace(currentUrl); }\n catch (recoveryError) {\n throw new AggregateError([error, historyError, recoveryError], \"Meeko navigation and URL recovery failed\");\n }\n throw new AggregateError([error, historyError], \"Meeko navigation and history recovery failed\");\n }\n }\n throw error;\n }\n if (result === false) {\n if (pending === controller) pending = undefined;\n if (options.replace && !controller.signal.aborted && browser.location.href === url.href) {\n try { browser.history.replaceState(null, \"\", currentUrl); }\n catch (historyError) {\n try { browser.location.replace(currentUrl); }\n catch (recoveryError) {\n throw new AggregateError([historyError, recoveryError], \"Meeko history and URL recovery failed\");\n }\n throw historyError;\n }\n }\n return false;\n }\n if (controller.signal.aborted || disposed) return false;\n try {\n if (options.replace) browser.history.replaceState(null, \"\", url);\n else browser.history.pushState(null, \"\", url);\n } catch (error) {\n if (pending === controller) pending = undefined;\n try { browser.location.replace(currentUrl); }\n catch (recoveryError) {\n throw new AggregateError([error, recoveryError], \"Meeko history update and navigation recovery failed\");\n }\n throw error;\n }\n current = match;\n currentUrl = url.href;\n pending = undefined;\n return true;\n };\n const onPop = () => {\n void navigate(browser.location.href, { replace: true }).catch((error: unknown) => reportNavigationError(browser, error, onError));\n };\n browser.addEventListener(\"popstate\", onPop);\n return {\n navigate,\n dispose() {\n disposed = true;\n pending?.abort();\n browser.removeEventListener(\"popstate\", onPop);\n },\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import type { RequestContext } from "../framework-server/context.js";
2
2
  import type { ServerFetch } from "../framework-server/context.js";
3
+ import type { RouteMiddleware } from "./middleware.js";
3
4
  import type { PageSchema } from "./page.js";
4
5
  import { StateStore } from "./state.js";
5
6
  import type { RouteRecord } from "./router.js";
@@ -22,7 +23,7 @@ export interface PageRoute {
22
23
  }) => {
23
24
  title?: string;
24
25
  };
25
- render?: "ssr" | "prerender" | "client";
26
+ render?: "ssr" | "prerender";
26
27
  prerenderEntries?: readonly Readonly<Record<string, string>>[] | (() => Promise<readonly Readonly<Record<string, string>>[]>);
27
28
  }
28
29
  export interface PageRequestContext {
@@ -36,16 +37,29 @@ export interface PageRequestContext {
36
37
  /** Present when the host supplies a per-request context. */
37
38
  readonly context?: RequestContext;
38
39
  }
39
- export interface FrameworkHandlerOptions {
40
+ export interface FrameworkHandlerOptions<Route = PageRoute> {
40
41
  readonly routes: readonly RouteRecord[];
41
42
  readonly loadPage: (file: string) => Promise<PageModule>;
42
- readonly loadRoute?: (file: string) => Promise<PageRoute>;
43
+ readonly loadRoute?: (file: string) => Promise<Route>;
44
+ /** Isomorphic route middleware, evaluated before the route sidecar loader. */
45
+ readonly loadMiddleware?: () => Promise<readonly RouteMiddleware[]>;
43
46
  readonly createContext?: (request: Request) => RequestContext | Promise<RequestContext>;
44
47
  /** Optional in-process dispatcher for automatic API/RPC calls during SSR. */
45
48
  readonly serverFetch?: ServerFetch;
49
+ /** Receives internal page failures with the request ID; never sent to the browser. */
50
+ readonly onError?: (error: unknown, requestId: string) => void;
46
51
  /** Optional host layout renderer; default composition uses <component is="props.outlet"/>. */
47
52
  readonly renderLayout?: (file: string, children: string, context: PageRequestContext) => Promise<string> | string;
48
53
  readonly clientEntry?: string;
49
54
  }
55
+ /** A typed sidecar may narrow callback arguments after its schemas validate them. */
56
+ type TypedPageRoute = Omit<PageRoute, "load" | "head"> & {
57
+ load?: (context: never) => Promise<unknown> | unknown;
58
+ head?: (context: never) => {
59
+ title?: string;
60
+ };
61
+ };
50
62
  /** Portable Fetch handler. Node adapters can pass their Request directly. */
51
63
  export declare function createFrameworkHandler(options: FrameworkHandlerOptions): (request: Request) => Promise<Response>;
64
+ export declare function createFrameworkHandler<Route extends TypedPageRoute>(options: FrameworkHandlerOptions<Route>): (request: Request) => Promise<Response>;
65
+ export {};