@torpor/build 1.0.2 → 1.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 (55) hide show
  1. package/dist/Router-COdQuVUi.d.mts +59 -0
  2. package/dist/Router-COdQuVUi.d.mts.map +1 -0
  3. package/dist/{Server-BnjLJleZ.mjs → Server-Dr6yqVa5.mjs} +2 -2
  4. package/dist/{Server-BnjLJleZ.mjs.map → Server-Dr6yqVa5.mjs.map} +1 -1
  5. package/dist/{ServerEvent-Dljn5i7A.mjs → ServerEvent-gmOvC_Yf.mjs} +3 -2
  6. package/dist/{ServerEvent-Dljn5i7A.mjs.map → ServerEvent-gmOvC_Yf.mjs.map} +1 -1
  7. package/dist/ServerHook-BlLArueW.d.mts +91 -0
  8. package/dist/ServerHook-BlLArueW.d.mts.map +1 -0
  9. package/dist/{Site-CE0_o5Ci.d.mts → Site-CTADVh7d.d.mts} +12 -91
  10. package/dist/Site-CTADVh7d.d.mts.map +1 -0
  11. package/dist/bin/index.js +1 -1
  12. package/dist/clientEntry.mjs +3 -2
  13. package/dist/clientEntry.mjs.map +1 -1
  14. package/dist/dev.d.mts +1 -1
  15. package/dist/dev.mjs +2 -2
  16. package/dist/index.d.mts +3 -57
  17. package/dist/index.d.mts.map +1 -1
  18. package/dist/index.mjs +153 -3
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/{load-BntzvmwL.mjs → load-C9RpS_4j.mjs} +32 -26
  21. package/dist/{load-BntzvmwL.mjs.map → load-C9RpS_4j.mjs.map} +1 -1
  22. package/dist/nav.mjs +7 -5
  23. package/dist/nav.mjs.map +1 -1
  24. package/dist/openapi.d.mts +1 -1
  25. package/dist/pathTrie-D4Ax2Mu9.mjs.map +1 -1
  26. package/dist/{response-Bg4w2S1q.mjs → response-CuNPcxsY.mjs} +2 -2
  27. package/dist/{response-Bg4w2S1q.mjs.map → response-CuNPcxsY.mjs.map} +1 -1
  28. package/dist/response.mjs +2 -2
  29. package/dist/run.d.mts +1 -1
  30. package/dist/run.mjs +1 -1
  31. package/dist/{runOpenApi-C8eI2Fh0.mjs → runOpenApi-yIzYasNN.mjs} +15 -15
  32. package/dist/{runOpenApi-C8eI2Fh0.mjs.map → runOpenApi-yIzYasNN.mjs.map} +1 -1
  33. package/dist/server/Server.mjs +1 -1
  34. package/dist/server.mjs +2 -2
  35. package/dist/serverEntry-DVv9YI3I.d.mts +13 -0
  36. package/dist/serverEntry-DVv9YI3I.d.mts.map +1 -0
  37. package/dist/serverEntry-LyB5pl0E.mjs +11 -0
  38. package/dist/serverEntry-LyB5pl0E.mjs.map +1 -0
  39. package/dist/serverEntry.d.mts +1 -1
  40. package/dist/serverEntry.mjs +1 -1
  41. package/dist/{serverEntry-1qY3wZig.mjs → serverHandlers-DeL1qZJa.mjs} +199 -71
  42. package/dist/serverHandlers-DeL1qZJa.mjs.map +1 -0
  43. package/dist/test.d.mts +1 -1
  44. package/dist/test.d.mts.map +1 -1
  45. package/dist/test.mjs +7 -242
  46. package/dist/test.mjs.map +1 -1
  47. package/dist/{unprocessable-DAZbrDeR.mjs → unprocessable-D6RcYLh6.mjs} +7 -5
  48. package/dist/{unprocessable-DAZbrDeR.mjs.map → unprocessable-D6RcYLh6.mjs.map} +1 -1
  49. package/package.json +5 -5
  50. package/dist/Site-CE0_o5Ci.d.mts.map +0 -1
  51. package/dist/endpoint-EULRGCKA.mjs +0 -108
  52. package/dist/endpoint-EULRGCKA.mjs.map +0 -1
  53. package/dist/serverEntry-1qY3wZig.mjs.map +0 -1
  54. package/dist/serverEntry-BAO8m03V.d.mts +0 -6
  55. package/dist/serverEntry-BAO8m03V.d.mts.map +0 -1
@@ -1,2 +1,2 @@
1
- import { t as load } from "./serverEntry-BAO8m03V.mjs";
1
+ import { t as load } from "./serverEntry-DVv9YI3I.mjs";
2
2
  export { load };
@@ -1,2 +1,2 @@
1
- import { t as load } from "./serverEntry-1qY3wZig.mjs";
1
+ import { t as load } from "./serverEntry-LyB5pl0E.mjs";
2
2
  export { load };
@@ -1,8 +1,91 @@
1
- import { t as Router } from "./Router-C4ADUfhv.mjs";
2
- import { i as notFound, n as seeOther, r as ok } from "./unprocessable-DAZbrDeR.mjs";
1
+ import { i as notFound, n as seeOther, r as ok, t as unprocessable } from "./unprocessable-D6RcYLh6.mjs";
3
2
  import { n as ValidationError, t as validate } from "./validate-Ok4krlkX.mjs";
4
- import { a as searchParamsToRecord, i as validationErrorResponse, n as validateEndpointParams, o as $page, s as formDataToRecord, t as endpointSchema } from "./endpoint-EULRGCKA.mjs";
5
- import manifest from "@torpor/build/manifest";
3
+ import { $watch } from "@torpor/view/ssr";
4
+ //#region src/form/formDataToRecord.ts
5
+ /**
6
+ * Converts the submitted fields of a form request into a plain record: one
7
+ * value per field, or an array when a field was submitted with multiple
8
+ * values (e.g. a multi select). File inputs pass through as `File` values;
9
+ * coercing them is left to the schema or the action.
10
+ *
11
+ * @param request The request (or FormData directly)
12
+ * @returns The field values
13
+ */
14
+ async function formDataToRecord(request) {
15
+ const formData = request instanceof FormData ? request : await request.formData();
16
+ const result = {};
17
+ for (const key of formData.keys()) {
18
+ if (key in result) continue;
19
+ const values = formData.getAll(key);
20
+ result[key] = values.length > 1 ? values : values[0];
21
+ }
22
+ return result;
23
+ }
24
+ //#endregion
25
+ //#region src/state/$serverPage.ts
26
+ const PAGE_SYMBOL = Symbol.for("torp.Page");
27
+ const $page = globalThis[PAGE_SYMBOL] ??= $watch({
28
+ status: 404,
29
+ url: new URL("http://localhost"),
30
+ form: void 0,
31
+ error: { message: "" }
32
+ });
33
+ //#endregion
34
+ //#region src/utils/searchParamsToRecord.ts
35
+ /**
36
+ * Converts a URL's query string into a plain record: one string per param,
37
+ * or an array when a param was repeated.
38
+ *
39
+ * @param searchParams The URL's search params
40
+ * @returns The param values
41
+ */
42
+ function searchParamsToRecord(searchParams) {
43
+ const result = {};
44
+ for (const key of searchParams.keys()) {
45
+ if (key in result) continue;
46
+ const values = searchParams.getAll(key);
47
+ result[key] = values.length > 1 ? values : values[0];
48
+ }
49
+ return result;
50
+ }
51
+ //#endregion
52
+ //#region src/validation/endpoint.ts
53
+ /**
54
+ * Gets the standard schema declared for a handler or action, if any. The
55
+ * schemas map is keyed by name; the runtime treats it as a loose record.
56
+ */
57
+ function endpointSchema(endPoint, name) {
58
+ return endPoint.schema?.[name];
59
+ }
60
+ /**
61
+ * Validates the route params against the endpoint's `params` schema, if it
62
+ * declares one, returning the coerced params. Throws a ValidationError when
63
+ * they don't match, which callers turn into a 404 (params come from the URL,
64
+ * so a failed validation means the resource doesn't exist).
65
+ *
66
+ * @param endPoint The endpoint declaring the (optional) `params` schema
67
+ * @param params The route params from the matched route
68
+ * @returns The validated params
69
+ */
70
+ async function validateEndpointParams(endPoint, params) {
71
+ const schema = endpointSchema(endPoint, "params");
72
+ return schema ? await validate(schema, params) : params;
73
+ }
74
+ /**
75
+ * Turns a ValidationError thrown during endpoint validation into a 422
76
+ * response with the schema's issues. Returns undefined for any other error,
77
+ * which callers should rethrow.
78
+ *
79
+ * @param error The error thrown by a handler or validation
80
+ * @returns A 422 response, or undefined when the error isn't a validation failure
81
+ */
82
+ function validationErrorResponse(error) {
83
+ if (error instanceof ValidationError) return unprocessable({
84
+ message: error.message,
85
+ issues: error.issues
86
+ });
87
+ }
88
+ //#endregion
6
89
  //#region src/site/layoutSlots.ts
7
90
  /**
8
91
  * Layouts are composed by the layout engine passing each layout (and finally
@@ -43,48 +126,80 @@ const warnedLayouts = /* @__PURE__ */ new Set();
43
126
  console.warn(`[torpor] The layout at "${layoutPath}" never rendered <slot />, so the page content was not included in the render. Every _layout component must render <slot /> (or pass the default slot down to a child component it renders).`);
44
127
  }
45
128
  //#endregion
46
- //#region src/site/serverEntry.ts
47
- const router = new Router();
48
- router.addPages(manifest.routes);
49
- async function load(ev, template) {
50
- const url = ev.url;
51
- const path = url.pathname;
52
- const query = url.searchParams;
53
- let route = router.match(path, query);
54
- if (!route) {
55
- if (ev.request.method === "POST" && !path.endsWith("/~server")) {
56
- const serverPath = (path.endsWith("/") ? path.substring(0, path.length - 1) : path) + "/~server";
57
- route = router.match(serverPath, query);
58
- }
129
+ //#region src/site/mergeHead.ts
130
+ const TITLE_REGEX = /<title(?:\s[^>]*)?>[\s\S]*?<\/title>/g;
131
+ const FIRST_TITLE_REGEX = /<title(?:\s[^>]*)?>[\s\S]*?<\/title>/;
132
+ /**
133
+ * Merges the head string rendered by a page (and its layouts) into the site
134
+ * template. The first <title> in the head string -- the page's, since pages
135
+ * render before the layouts around them -- wins, overriding both any later
136
+ * component title (e.g. from a child component) and a static <title> in the
137
+ * template itself. All other head content (styles, meta elements, etc) is
138
+ * appended where %COMPONENT_HEAD% sits in the template, before </head>.
139
+ */
140
+ function mergeHead(template, head) {
141
+ const titles = head.match(TITLE_REGEX);
142
+ let html = template;
143
+ let headHtml = head;
144
+ if (titles?.length) {
145
+ headHtml = head.replace(TITLE_REGEX, "");
146
+ html = html.replace(FIRST_TITLE_REGEX, "");
147
+ headHtml = titles[0] + headHtml;
59
148
  }
60
- if (!route) return handleResponse(notFound());
61
- const handler = route.handler;
62
- const params = route.params || {};
63
- $page.url = url;
64
- if (path.endsWith("/_error")) {
65
- $page.status = parseInt(query.get("status") ?? "404");
66
- $page.error = { message: query.get("message") ?? "" };
67
- } else $page.status = 200;
68
- switch (handler.type) {
69
- case 0:
70
- case 4:
71
- if (ev.request.method === "GET") return handleResponse(await loadView(ev, url, handler, params, template));
72
- else if (ev.request.method === "POST") return handleResponse(await runAction(ev, url, handler, handler.serverEndPoint && (await handler.serverEndPoint()).default, params, query, template), true);
73
- break;
74
- case 1:
75
- case 5:
76
- if (ev.request.method === "GET") return await loadData(ev, url, handler, "load", params);
77
- else if (ev.request.method === "POST") {
78
- const serverEndPoint = (await resolveModule(handler)).default;
79
- return handleResponse(await runAction(ev, url, handler, serverEndPoint, params, query, template), true);
149
+ return html.replace("%COMPONENT_HEAD%", headHtml);
150
+ }
151
+ //#endregion
152
+ //#region src/site/serverHandlers.ts
153
+ /**
154
+ * Creates the site's request handler for the given router. This is kept
155
+ * separate from serverEntry, which imports the virtual
156
+ * `@torpor/build/manifest` module (it only exists when a site runs or
157
+ * builds), so that tests can exercise the handlers with their own routes.
158
+ *
159
+ * @param router The router, built from the site's routes
160
+ * @returns The request handler, which takes the site template
161
+ */
162
+ function createServerLoad(router) {
163
+ return async function load(ev, template) {
164
+ const url = ev.url;
165
+ const path = url.pathname;
166
+ const query = url.searchParams;
167
+ let route = router.match(path, query);
168
+ if (!route) {
169
+ if (ev.request.method === "POST" && !path.endsWith("/~server")) {
170
+ const serverPath = (path.endsWith("/") ? path.substring(0, path.length - 1) : path) + "/~server";
171
+ route = router.match(serverPath, query);
80
172
  }
81
- break;
82
- case 3: return await loadData(ev, url, handler, ev.request.method.toLowerCase().replace("delete", "del"), params);
83
- case 6:
84
- case 7: break;
85
- case 8: return await loadView(ev, url, handler, params, template);
86
- }
87
- return notFound();
173
+ }
174
+ if (!route) return handleResponse(notFound());
175
+ const handler = route.handler;
176
+ const params = route.params || {};
177
+ $page.url = url;
178
+ if (path.endsWith("/_error")) {
179
+ $page.status = parseInt(query.get("status") ?? "404");
180
+ $page.error = { message: query.get("message") ?? "" };
181
+ } else $page.status = 200;
182
+ switch (handler.type) {
183
+ case 0:
184
+ case 4:
185
+ if (ev.request.method === "GET") return handleResponse(await loadView(ev, url, handler, params, template));
186
+ else if (ev.request.method === "POST") return handleResponse(await runAction(ev, url, handler, handler.serverEndPoint && (await handler.serverEndPoint()).default, params, query, template), true);
187
+ break;
188
+ case 1:
189
+ case 5:
190
+ if (ev.request.method === "GET") return await loadData(ev, url, handler, "load", params);
191
+ else if (ev.request.method === "POST") {
192
+ const serverEndPoint = (await resolveModule(handler)).default;
193
+ return handleResponse(await runAction(ev, url, handler, serverEndPoint, params, query, template), true);
194
+ }
195
+ break;
196
+ case 3: return await loadData(ev, url, handler, ev.request.method.toLowerCase().replace("delete", "del"), params);
197
+ case 6:
198
+ case 7: break;
199
+ case 8: return await loadView(ev, url, handler, params, template);
200
+ }
201
+ return notFound();
202
+ };
88
203
  }
89
204
  /**
90
205
  * Resolves the route's endpoint module, caching it on the handler so warm
@@ -135,7 +250,7 @@ async function loadData(ev, url, handler, functionName, params) {
135
250
  const hookResult = await hooks[entered].enter?.(serverParams);
136
251
  if (hookResult) enterResponse = hookResult;
137
252
  }
138
- return await handlerFn(serverParams) || ok();
253
+ return enterResponse || await handlerFn(serverParams) || ok();
139
254
  } finally {
140
255
  while (entered > 0) {
141
256
  entered--;
@@ -148,7 +263,7 @@ async function loadData(ev, url, handler, functionName, params) {
148
263
  throw error;
149
264
  }
150
265
  }
151
- async function loadView(ev, url, handler, params, template, formStatus, form, skipHook) {
266
+ async function loadView(ev, url, handler, params, template, formStatus, form, fromForm) {
152
267
  const clientEndPoint = (await resolveModule(handler)).default;
153
268
  if (!clientEndPoint?.component) return notFound();
154
269
  if (!template) throw new Error("Page routes require a src/site.html file, which was not found; add it, or remove page/layout routes to serve endpoints only");
@@ -162,16 +277,22 @@ async function loadView(ev, url, handler, params, template, formStatus, form, sk
162
277
  throw error;
163
278
  }
164
279
  const loadSchema = endpointSchema(serverEndPoint, "load");
165
- if (loadSchema) try {
166
- query = await validate(loadSchema, searchParamsToRecord(url.searchParams));
167
- } catch (error) {
168
- const response = validationErrorResponse(error);
169
- if (response) return response;
170
- throw error;
280
+ if (loadSchema) {
281
+ const queryRecord = searchParamsToRecord(url.searchParams);
282
+ try {
283
+ query = await validate(loadSchema, queryRecord);
284
+ } catch (error) {
285
+ if (fromForm) query = queryRecord;
286
+ else {
287
+ const response = validationErrorResponse(error);
288
+ if (response) return response;
289
+ throw error;
290
+ }
291
+ }
171
292
  }
172
293
  }
173
294
  const serverParams = buildServerParams(ev, url, params, query ? { query } : {});
174
- const serverHooks = skipHook ? [] : await loadServerHooks(handler);
295
+ const serverHooks = fromForm ? [] : await loadServerHooks(handler);
175
296
  let entered = 0;
176
297
  let enterResponse = void 0;
177
298
  try {
@@ -212,14 +333,16 @@ async function loadView(ev, url, handler, params, template, formStatus, form, sk
212
333
  });
213
334
  for (let i = handler.layouts.length - 1; i >= 0; i--) {
214
335
  const layoutEndPoint = (await handler.layouts[i].endPoint())?.default;
215
- if (layoutEndPoint?.component) if (i === 0) {
216
- component = layoutEndPoint.component;
217
- slots = { _: slotFunctions[i + 1] };
218
- } else slotFunctions[i] = trackSlot(i, (_, $context) => {
219
- let { body, head } = layoutEndPoint.component($props, $context, { _: slotFunctions[i + 1] });
220
- styles += head;
221
- return body;
222
- });
336
+ if (layoutEndPoint?.component) {
337
+ if (i === 0) {
338
+ component = layoutEndPoint.component;
339
+ slots = { _: slotFunctions[i + 1] };
340
+ } else slotFunctions[i] = trackSlot(i, (_, $context) => {
341
+ let { body, head } = layoutEndPoint.component($props, $context, { _: slotFunctions[i + 1] });
342
+ styles += head;
343
+ return body;
344
+ });
345
+ }
223
346
  }
224
347
  }
225
348
  let html;
@@ -227,7 +350,7 @@ async function loadView(ev, url, handler, params, template, formStatus, form, sk
227
350
  let { body, head } = component($props, void 0, slots);
228
351
  if (form) body += `\n<input type="hidden" id="t-form-data" value='${JSON.stringify(form).replaceAll("'", "\\'")}' />`;
229
352
  styles += head;
230
- html = template.replace("%COMPONENT_BODY%", body).replace("%COMPONENT_HEAD%", styles);
353
+ html = mergeHead(template, styles).replace("%COMPONENT_BODY%", body);
231
354
  } catch (error) {
232
355
  html = "<span style=\"color: red\">Script syntax error</span><p>" + error + "</p>";
233
356
  console.log(error);
@@ -248,7 +371,8 @@ async function loadView(ev, url, handler, params, template, formStatus, form, sk
248
371
  }
249
372
  }
250
373
  async function runAction(ev, url, handler, serverEndPoint, params, query, template) {
251
- const actionName = (Array.from(query.keys())[0] || "default").replace(/^\//, "");
374
+ const queryKeys = Array.from(query.keys());
375
+ const actionName = (queryKeys.find((key) => key.startsWith("/")) ?? queryKeys.find((key) => !!serverEndPoint?.actions?.[key.replace(/^\//, "")]) ?? "default").replace(/^\//, "");
252
376
  if (serverEndPoint?.actions) {
253
377
  const action = serverEndPoint.actions[actionName];
254
378
  if (action) {
@@ -313,15 +437,19 @@ async function loadClientAndServerData(url, params, serverParams, data, clientEn
313
437
  if (clientEndPoint?.load) {
314
438
  const clientParams = buildClientParams(url, params, data);
315
439
  const clientResponse = await clientEndPoint.load(clientParams);
316
- if (clientResponse) if (clientResponse.ok) {
317
- if (clientResponse.headers.get("Content-Type")?.includes("application/json")) Object.assign(data, await clientResponse.json());
318
- } else return clientResponse;
440
+ if (clientResponse) {
441
+ if (clientResponse.ok) {
442
+ if (clientResponse.headers.get("Content-Type")?.includes("application/json")) Object.assign(data, await clientResponse.json());
443
+ } else return clientResponse;
444
+ }
319
445
  }
320
446
  if (serverEndPoint?.load) {
321
447
  const serverResponse = await serverEndPoint.load(serverParams);
322
- if (serverResponse) if (serverResponse.ok) {
323
- if (serverResponse.headers.get("Content-Type")?.includes("application/json")) Object.assign(data, await serverResponse.json());
324
- } else return serverResponse;
448
+ if (serverResponse) {
449
+ if (serverResponse.ok) {
450
+ if (serverResponse.headers.get("Content-Type")?.includes("application/json")) Object.assign(data, await serverResponse.json());
451
+ } else return serverResponse;
452
+ }
325
453
  }
326
454
  }
327
455
  function buildClientParams(url, params, data) {
@@ -346,6 +474,6 @@ function buildServerParams(ev, url, params, values = {}) {
346
474
  };
347
475
  }
348
476
  //#endregion
349
- export { load as t };
477
+ export { createServerLoad as t };
350
478
 
351
- //# sourceMappingURL=serverEntry-1qY3wZig.mjs.map
479
+ //# sourceMappingURL=serverHandlers-DeL1qZJa.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverHandlers-DeL1qZJa.mjs","names":[],"sources":["../src/form/formDataToRecord.ts","../src/state/$serverPage.ts","../src/utils/searchParamsToRecord.ts","../src/validation/endpoint.ts","../src/site/layoutSlots.ts","../src/site/mergeHead.ts","../src/site/serverHandlers.ts"],"sourcesContent":["import type { FormDataRecord } from \"../types/ServerLoadEvent\";\n\n/**\n * Converts the submitted fields of a form request into a plain record: one\n * value per field, or an array when a field was submitted with multiple\n * values (e.g. a multi select). File inputs pass through as `File` values;\n * coercing them is left to the schema or the action.\n *\n * @param request The request (or FormData directly)\n * @returns The field values\n */\nexport default async function formDataToRecord(\n\trequest: Request | FormData,\n): Promise<FormDataRecord> {\n\tconst formData = request instanceof FormData ? request : await request.formData();\n\tconst result: FormDataRecord = {};\n\tfor (const key of formData.keys()) {\n\t\tif (key in result) continue;\n\t\tconst values = formData.getAll(key);\n\t\tresult[key] = values.length > 1 ? values : values[0];\n\t}\n\treturn result;\n}\n","import { $watch } from \"@torpor/view/ssr\";\nimport type PageState from \"../types/PageState\";\n\nconst PAGE_SYMBOL: unique symbol = Symbol.for(\"torp.Page\");\n\n// The server request path has no reactive subscribers, so the state is\n// stored plainly ($watch from `@torpor/view/ssr` is a no-op that returns\n// the object itself) instead of through a reactive proxy. Shares the same\n// `globalThis` slot as the client `$page`, so both variants always see the\n// same state object whichever loads first.\nconst $page: PageState =\n\t// @ts-ignore\n\t(globalThis[PAGE_SYMBOL] ??= $watch({\n\t\tstatus: 404,\n\t\turl: new URL(\"http://localhost\"),\n\t\tform: undefined,\n\t\terror: {\n\t\t\tmessage: \"\",\n\t\t},\n\t}));\n\nexport default $page;\n","import type { QueryRecord } from \"../types/ServerLoadEvent\";\n\n/**\n * Converts a URL's query string into a plain record: one string per param,\n * or an array when a param was repeated.\n *\n * @param searchParams The URL's search params\n * @returns The param values\n */\nexport default function searchParamsToRecord(searchParams: URLSearchParams): QueryRecord {\n\tconst result: QueryRecord = {};\n\tfor (const key of searchParams.keys()) {\n\t\tif (key in result) continue;\n\t\tconst values = searchParams.getAll(key);\n\t\tresult[key] = values.length > 1 ? values : values[0];\n\t}\n\treturn result;\n}\n","import type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport type ServerEndPoint from \"../types/ServerEndPoint\";\nimport type { StandardSchemaV1 } from \"../types/StandardSchema\";\nimport type ServerEvent from \"../server/ServerEvent\";\nimport searchParamsToRecord from \"../utils/searchParamsToRecord\";\nimport unprocessable from \"../response/unprocessable\";\nimport ValidationError from \"./ValidationError\";\nimport validate from \"./validate\";\n\n/**\n * Loose views of an endpoint's schema map, keyed by handler/action name (plus\n * the reserved `params` key). The type-level constraint differs per endpoint\n * type, but the runtime treats them the same.\n */\ntype Schemas = Record<string, StandardSchemaV1 | undefined>;\n\ntype EndPoint = ServerEndPoint | PageServerEndPoint;\n\n/**\n * Gets the standard schema declared for a handler or action, if any. The\n * schemas map is keyed by name; the runtime treats it as a loose record.\n */\nexport function endpointSchema(endPoint: EndPoint, name: string): StandardSchemaV1 | undefined {\n\treturn (endPoint.schema as Schemas | undefined)?.[name];\n}\n\n/**\n * Validates the route params against the endpoint's `params` schema, if it\n * declares one, returning the coerced params. Throws a ValidationError when\n * they don't match, which callers turn into a 404 (params come from the URL,\n * so a failed validation means the resource doesn't exist).\n *\n * @param endPoint The endpoint declaring the (optional) `params` schema\n * @param params The route params from the matched route\n * @returns The validated params\n */\nexport async function validateEndpointParams(\n\tendPoint: EndPoint,\n\tparams: Record<string, any>,\n): Promise<Record<string, any>> {\n\tconst schema = endpointSchema(endPoint, \"params\");\n\treturn schema ? ((await validate(schema, params)) as Record<string, any>) : params;\n}\n\n/**\n * Validates a handler's input against its schema: get/head handlers and load\n * functions validate the URL's query string, other handlers validate the JSON\n * request body. Returns the validated values (an empty object when no schema\n * is declared), which callers pass through to the handler's event.\n *\n * @param schema The handler's (optional) schema\n * @param functionName The handler name (`get`, `post`, `load`, etc)\n * @param url The request URL, for the query string\n * @param ev The server event, for the request body\n * @returns The validated values\n */\nexport async function validateHandlerInput(\n\tschema: StandardSchemaV1 | undefined,\n\tfunctionName: string,\n\turl: URL,\n\tev: ServerEvent,\n): Promise<{ json?: unknown; form?: unknown; query?: unknown }> {\n\tif (!schema) return {};\n\tif (functionName === \"get\" || functionName === \"head\" || functionName === \"load\") {\n\t\treturn { query: await validate(schema, searchParamsToRecord(url.searchParams)) };\n\t}\n\treturn { json: await validate(schema, await ev.json()) };\n}\n\n/**\n * Turns a ValidationError thrown during endpoint validation into a 422\n * response with the schema's issues. Returns undefined for any other error,\n * which callers should rethrow.\n *\n * @param error The error thrown by a handler or validation\n * @returns A 422 response, or undefined when the error isn't a validation failure\n */\nexport function validationErrorResponse(error: unknown): Response | undefined {\n\tif (error instanceof ValidationError) {\n\t\treturn unprocessable({ message: error.message, issues: error.issues });\n\t}\n\treturn undefined;\n}\n","/**\n * Layouts are composed by the layout engine passing each layout (and finally\n * the page) as the default slot of its parent — so every `_layout` component\n * must render `<slot />`, or pass the default slot down to a child component\n * it renders. A layout that doesn't drops its entire content: the server\n * renders the layout's own markup with an empty content area (no error), and\n * the first client-side navigation that reuses the layout crashes on a null\n * slot region.\n *\n * The static `checkLayoutSlots` check catches this at build/dev time; these\n * helpers report it at request time for the cases static analysis can't see\n * (e.g. a `<slot />` rendered conditionally inside `@if`). They are\n * server-side only — nothing here is imported into the client bundle.\n */\n\n/**\n * Returns the path of the outermost layout that never rendered its\n * `<slot />`, or undefined when every level rendered.\n *\n * Slot level j (1-based) is the slot render that layout j - 1 receives: the\n * root layout (layouts[0]) renders level 1, layout i renders level i + 1,\n * and level layouts.length renders the page. A broken level makes every\n * deeper level unreachable, so the first level that never ran identifies\n * the offending layout.\n */\nexport function findMissingSlotLayout(\n\tlayouts: readonly { path: string }[],\n\tinvokedLevels: ReadonlySet<number>,\n): string | undefined {\n\tfor (let level = 1; level <= layouts.length; level++) {\n\t\tif (!invokedLevels.has(level)) {\n\t\t\treturn layouts[level - 1].path;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nconst warnedLayouts = new Set<string>();\n\n/**\n * Reports a layout that dropped its slot content. The condition is a genuine\n * rendering bug (the page content is missing from the output), so it is not\n * dev-gated — but it warns only once per layout path to avoid per-request\n * noise.\n */ export function warnMissingSlotContent(layoutPath: string): void {\n\tif (warnedLayouts.has(layoutPath)) return;\n\twarnedLayouts.add(layoutPath);\n\tconsole.warn(\n\t\t`[torpor] The layout at \"${layoutPath}\" never rendered <slot />, so the page content ` +\n\t\t\t`was not included in the render. Every _layout component must render <slot /> (or ` +\n\t\t\t`pass the default slot down to a child component it renders).`,\n\t);\n}\n","const TITLE_REGEX = /<title(?:\\s[^>]*)?>[\\s\\S]*?<\\/title>/g;\nconst FIRST_TITLE_REGEX = /<title(?:\\s[^>]*)?>[\\s\\S]*?<\\/title>/;\n\n/**\n * Merges the head string rendered by a page (and its layouts) into the site\n * template. The first <title> in the head string -- the page's, since pages\n * render before the layouts around them -- wins, overriding both any later\n * component title (e.g. from a child component) and a static <title> in the\n * template itself. All other head content (styles, meta elements, etc) is\n * appended where %COMPONENT_HEAD% sits in the template, before </head>.\n */\nexport default function mergeHead(template: string, head: string): string {\n\tconst titles = head.match(TITLE_REGEX);\n\tlet html = template;\n\tlet headHtml = head;\n\tif (titles?.length) {\n\t\theadHtml = head.replace(TITLE_REGEX, \"\");\n\t\thtml = html.replace(FIRST_TITLE_REGEX, \"\");\n\t\theadHtml = titles[0] + headHtml;\n\t}\n\treturn html.replace(\"%COMPONENT_HEAD%\", headHtml);\n}\n","import { type ServerComponent, type ServerSlotRender } from \"@torpor/view/ssr\";\nimport formDataToRecord from \"../form/formDataToRecord.ts\";\nimport notFound from \"../response/notFound.ts\";\nimport ok from \"../response/ok.ts\";\nimport seeOther from \"../response/seeOther.ts\";\nimport ServerEvent from \"../server/ServerEvent.ts\";\nimport $page from \"../state/$serverPage.ts\";\nimport type PageEndPoint from \"../types/PageEndPoint.ts\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint.ts\";\nimport type RouteHandler from \"../types/RouteHandler.ts\";\nimport type ServerRequest from \"../types/ServerRequest.ts\";\nimport {\n\tERROR_ROUTE,\n\tHOOK_ROUTE,\n\tHOOK_SERVER_ROUTE,\n\tLAYOUT_ROUTE,\n\tLAYOUT_SERVER_ROUTE,\n\tPAGE_ROUTE,\n\tPAGE_SERVER_ROUTE,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport type ServerEndPoint from \"../types/ServerEndPoint.ts\";\nimport type ServerHook from \"../types/ServerHook.ts\";\nimport type ServerLoadEvent from \"../types/ServerLoadEvent.ts\";\nimport searchParamsToRecord from \"../utils/searchParamsToRecord.ts\";\nimport validate from \"../validation/validate.ts\";\nimport ValidationError from \"../validation/ValidationError.ts\";\nimport {\n\tendpointSchema,\n\tvalidateEndpointParams,\n\tvalidationErrorResponse,\n} from \"../validation/endpoint.ts\";\nimport { findMissingSlotLayout, warnMissingSlotContent } from \"./layoutSlots.ts\";\nimport mergeHead from \"./mergeHead.ts\";\nimport type Router from \"./Router.ts\";\n\n/**\n * The site's request handler, as created by `createServerLoad`.\n */\nexport type ServerLoad = (ev: ServerEvent, template?: string) => Promise<Response>;\n\n/**\n * Creates the site's request handler for the given router. This is kept\n * separate from serverEntry, which imports the virtual\n * `@torpor/build/manifest` module (it only exists when a site runs or\n * builds), so that tests can exercise the handlers with their own routes.\n *\n * @param router The router, built from the site's routes\n * @returns The request handler, which takes the site template\n */\nexport function createServerLoad(router: Router): ServerLoad {\n\treturn async function load(ev, template) {\n\t\tconst url = ev.url;\n\t\tconst path = url.pathname;\n\t\tconst query = url.searchParams;\n\n\t\t//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\t\tlet route = router.match(path, query);\n\t\tif (!route) {\n\t\t\t// If the route wasn't found, the user may be posting a form to a\n\t\t\t// +page.server.ts route that doesn't have a corresponding +page.ts\n\t\t\t// route, which is allowed, so check for that as well\n\t\t\tif (ev.request.method === \"POST\" && !path.endsWith(\"/~server\")) {\n\t\t\t\tconst serverPath =\n\t\t\t\t\t(path.endsWith(\"/\") ? path.substring(0, path.length - 1) : path) + \"/~server\";\n\t\t\t\troute = router.match(serverPath, query);\n\t\t\t}\n\t\t}\n\t\tif (!route) {\n\t\t\treturn handleResponse(notFound());\n\t\t}\n\t\tconst handler = route.handler;\n\t\tconst params = route.params || {};\n\n\t\t// Update $page before building the components\n\t\t$page.url = url;\n\t\tif (path.endsWith(\"/_error\")) {\n\t\t\t$page.status = parseInt(query.get(\"status\") ?? \"404\");\n\t\t\t$page.error = { message: query.get(\"message\") ?? \"\" };\n\t\t} else {\n\t\t\t$page.status = 200;\n\t\t}\n\n\t\t// TODO: Hit the server hook out here\n\t\t// We could maybe set up data loading as middleware on a route??????\n\n\t\tswitch (handler.type) {\n\t\t\tcase PAGE_ROUTE:\n\t\t\tcase LAYOUT_ROUTE: {\n\t\t\t\t// It's a /+page.ts or /_layout.ts endpoint\n\t\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\t\treturn handleResponse(await loadView(ev, url, handler, params, template));\n\t\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\t\t// The server end point comes from the sibling /+page.server.ts, if applicable\n\t\t\t\t\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\t\thandler.serverEndPoint && (await handler.serverEndPoint()).default;\n\t\t\t\t\treturn handleResponse(\n\t\t\t\t\t\tawait runAction(ev, url, handler, serverEndPoint, params, query, template),\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase PAGE_SERVER_ROUTE:\n\t\t\tcase LAYOUT_SERVER_ROUTE: {\n\t\t\t\t// It's a /+page.server.ts or /_layout.server.ts endpoint\n\t\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\t\treturn await loadData(ev, url, handler, \"load\", params);\n\t\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\t\tconst serverEndPoint: PageServerEndPoint | undefined = (await resolveModule(handler))\n\t\t\t\t\t\t.default;\n\t\t\t\t\treturn handleResponse(\n\t\t\t\t\t\tawait runAction(ev, url, handler, serverEndPoint, params, query, template),\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase SERVER_ROUTE: {\n\t\t\t\t// It's a /+server.ts endpoint\n\t\t\t\tconst functionName = ev.request.method.toLowerCase().replace(\"delete\", \"del\");\n\t\t\t\treturn await loadData(ev, url, handler, functionName, params);\n\t\t\t}\n\t\t\tcase HOOK_ROUTE:\n\t\t\tcase HOOK_SERVER_ROUTE: {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase ERROR_ROUTE: {\n\t\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t\t}\n\t\t}\n\n\t\treturn notFound();\n\t};\n}\n\n/**\n * Resolves the route's endpoint module, caching it on the handler so warm\n * requests don't pay for the loader promise.\n */\nasync function resolveModule(handler: RouteHandler): Promise<any> {\n\treturn (handler.resolvedModule ??= await (handler.modulePromise ??= handler.endPoint()));\n}\n\nasync function loadServerHooks(handler: RouteHandler): Promise<ServerHook[]> {\n\tif (!handler.serverHooks) return [];\n\t// Resolve each hook module once and cache the result on the handler\n\treturn (handler.resolvedHooks ??= Promise.all(\n\t\thandler.serverHooks.map(async (load) => (await load()).default),\n\t));\n}\n\nasync function handleResponse(response: Response, fromForm = false): Promise<Response> {\n\t$page.status = response.status;\n\n\t// Success codes and redirect codes are acceptable\n\tif (response.status >= 200 && response.status <= 399) {\n\t\treturn response;\n\t}\n\n\t// 4xx error codes are acceptable from form actions\n\tif (fromForm && response.status >= 400 && response.status <= 499) {\n\t\treturn response;\n\t}\n\n\t// It's an error, so redirect to the error page\n\t// We're just pushing the status and message in the URL, but maybe there's a\n\t// more sophisticated way to do this?\n\t// TODO: Should be returning loadView rather than redirecting\n\t// TODO: Should be returning the NEAREST error page to this path, including layouts\n\tlet params = new URLSearchParams();\n\tparams.append(\"status\", $page.status.toString());\n\tlet message = await response.text();\n\tif (response.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\tconst data = JSON.parse(message);\n\t\tmessage = data.message ?? message;\n\t}\n\tif (message) {\n\t\tif ($page.error) {\n\t\t\t$page.error.message = message;\n\t\t} else {\n\t\t\t$page.error = { message };\n\t\t}\n\t\tparams.append(\"message\", message);\n\t}\n\treturn seeOther(`/_error?${params.toString()}`);\n}\n\nasync function loadData(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tfunctionName: string,\n\tparams: Record<string, any>,\n) {\n\t// The endpoint module is cached on the handler, so once warm this is a\n\t// plain property read\n\tconst mod = handler.resolvedModule ?? (await resolveModule(handler));\n\tconst serverEndPoint: ServerEndPoint | undefined = mod?.default;\n\t// `functionName` is computed from the request method, so the handlers are\n\t// accessed through a loose record\n\tconst handlerFn = (\n\t\tserverEndPoint as unknown as Record<string, ServerRequest | undefined> | undefined\n\t)?.[functionName];\n\tif (!serverEndPoint || !handlerFn) {\n\t\treturn notFound();\n\t}\n\n\t// Route params come from the URL, so a failed validation means the\n\t// resource doesn't exist. Only endpoints declaring a params schema pay\n\t// for validation\n\tconst paramsSchema = endpointSchema(serverEndPoint, \"params\");\n\tif (paramsSchema) {\n\t\ttry {\n\t\t\tparams = (await validate(paramsSchema, params)) as Record<string, any>;\n\t\t} catch (error) {\n\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\ttry {\n\t\t// If the endpoint declares a schema for this handler, validate its\n\t\t// input up front and reject with 422 if it fails. get/head/load\n\t\t// schemas validate the query string; other schemas validate the json\n\t\t// body. Endpoints without a schema skip straight to the handler\n\t\tconst schema = endpointSchema(serverEndPoint, functionName);\n\t\tconst values = schema\n\t\t\t? functionName === \"get\" || functionName === \"head\" || functionName === \"load\"\n\t\t\t\t? { query: await validate(schema, searchParamsToRecord(url.searchParams)) }\n\t\t\t\t: { json: await validate(schema, await ev.json()) }\n\t\t\t: {};\n\n\t\tconst serverParams = buildServerParams(ev, url, params, values);\n\n\t\t// Hooks are resolved once per handler; routes without hooks don't\n\t\t// await anything here\n\t\tconst hooks = handler.serverHooks ? await loadServerHooks(handler) : [];\n\n\t\tlet entered = 0;\n\t\tlet enterResponse: Response | undefined = undefined;\n\t\ttry {\n\t\t\t// Hooks run from the root down; a hook's enter can return a\n\t\t\t// Response to short-circuit the request\n\t\t\tfor (; entered < hooks.length && !enterResponse; entered++) {\n\t\t\t\tconst hookResult = await hooks[entered].enter?.(serverParams);\n\t\t\t\tif (hookResult) {\n\t\t\t\t\tenterResponse = hookResult;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A hook's enter response short-circuits the request; otherwise, if\n\t\t\t// there was no response returned from load (such as errors or a\n\t\t\t// redirect), send an ok response\n\t\t\treturn enterResponse || (await handlerFn(serverParams)) || ok();\n\t\t} finally {\n\t\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\t\twhile (entered > 0) {\n\t\t\t\tentered--;\n\t\t\t\tawait hooks[entered].exit?.(serverParams);\n\t\t\t}\n\t\t}\n\t} catch (error) {\n\t\tconst response = validationErrorResponse(error);\n\t\tif (response) return response;\n\t\tthrow error;\n\t}\n}\n\nasync function loadView(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, any>,\n\ttemplate: string | undefined,\n\tformStatus?: number,\n\tform?: Record<string, string | number>,\n\tfromForm?: boolean,\n) {\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await resolveModule(handler)).default;\n\tif (!clientEndPoint?.component) {\n\t\treturn notFound();\n\t}\n\n\t// The template comes from site.html, which endpoints-only sites don't have\n\tif (!template) {\n\t\tthrow new Error(\n\t\t\t\"Page routes require a src/site.html file, which was not found; add it, or remove page/layout routes to serve endpoints only\",\n\t\t);\n\t}\n\n\t// There may be a server endpoint\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint())?.default;\n\n\t// Validate the route params and the load query against the server\n\t// endpoint's schemas, if it declares any. Params come from the URL, so a\n\t// failed validation means the resource doesn't exist; a failed query goes\n\t// to the error page like any other load failure\n\tlet query: unknown;\n\tif (serverEndPoint) {\n\t\ttry {\n\t\t\tparams = await validateEndpointParams(serverEndPoint, params);\n\t\t} catch (error) {\n\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\tthrow error;\n\t\t}\n\t\tconst loadSchema = endpointSchema(serverEndPoint, \"load\");\n\t\tif (loadSchema) {\n\t\t\tconst queryRecord = searchParamsToRecord(url.searchParams);\n\t\t\ttry {\n\t\t\t\tquery = await validate(loadSchema, queryRecord);\n\t\t\t} catch (error) {\n\t\t\t\t// A form re-render's url is the POST's, and a form action can\n\t\t\t\t// drop query params that the load schema requires, so failing\n\t\t\t\t// validation here would lose the form errors. Fall back to the\n\t\t\t\t// raw query values instead and let the load make of them what\n\t\t\t\t// it can\n\t\t\t\tif (fromForm) {\n\t\t\t\t\tquery = queryRecord;\n\t\t\t\t} else {\n\t\t\t\t\tconst response = validationErrorResponse(error);\n\t\t\t\t\tif (response) return response;\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Maybe hit the server hooks -- unless they already ran for this request,\n\t// which is the case when the view is re-rendered after a form action\n\tconst serverParams = buildServerParams(ev, url, params, query ? { query } : {});\n\tconst serverHooks = fromForm ? [] : await loadServerHooks(handler);\n\tlet entered = 0;\n\tlet enterResponse: Response | undefined = undefined;\n\ttry {\n\t\t// Hooks run from the root down; a hook's enter can return a Response\n\t\t// to short-circuit the request\n\t\tfor (; entered < serverHooks.length && !enterResponse; entered++) {\n\t\t\tconst hookResult = await serverHooks[entered].enter?.(serverParams);\n\t\t\tif (hookResult) {\n\t\t\t\tenterResponse = hookResult;\n\t\t\t}\n\t\t}\n\t\tif (enterResponse) {\n\t\t\treturn enterResponse;\n\t\t}\n\n\t\t// Pass the data into $props\n\t\t// TODO: Promise.all\n\t\t// NOTE: We're loading data from top to bottom, overriding as we go, and I'm\n\t\t// not sure if this is the best way to go\n\t\tlet data = {};\n\t\tif (handler.layouts) {\n\t\t\tfor (let layout of handler.layouts) {\n\t\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\t\turl,\n\t\t\t\t\tparams,\n\t\t\t\t\tserverParams,\n\t\t\t\t\tdata,\n\t\t\t\t\tlayoutEndPoint,\n\t\t\t\t\tlayoutServerEndPoint,\n\t\t\t\t);\n\t\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\t\treturn layoutResponse;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tlet endPointResponse = await loadClientAndServerData(\n\t\t\turl,\n\t\t\tparams,\n\t\t\tserverParams,\n\t\t\tdata,\n\t\t\tclientEndPoint,\n\t\t\tserverEndPoint,\n\t\t);\n\t\tif (endPointResponse?.ok === false) {\n\t\t\treturn endPointResponse;\n\t\t}\n\t\tlet $props: Record<string, any> = { data, form };\n\n\t\tlet styles = \"\";\n\n\t\t// If there are layouts, work our way upwards, pushing each component into\n\t\t// the default slot of its parent\n\t\t// TODO: Also handle layout server data\n\t\t// TODO: There's probably a nicer way to do this with reducers or something\n\t\tlet component = clientEndPoint.component as ServerComponent;\n\t\tlet slots: Record<string, ServerSlotRender> | undefined = undefined;\n\t\t// Which slot levels actually ran during the render below, so a layout\n\t\t// that never rendered its <slot /> (silently dropping the page\n\t\t// content) can be reported (see findMissingSlotLayout)\n\t\tconst invokedLevels = new Set<number>();\n\t\tconst trackSlot = (level: number, slot: ServerSlotRender): ServerSlotRender => {\n\t\t\treturn ($slot, $context) => {\n\t\t\t\tinvokedLevels.add(level);\n\t\t\t\treturn slot($slot, $context);\n\t\t\t};\n\t\t};\n\t\tif (handler.layouts) {\n\t\t\tlet slotFunctions: ServerSlotRender[] = [];\n\t\t\t// The last slot function will render the client component\n\t\t\tslotFunctions[handler.layouts.length] = trackSlot(handler.layouts.length, (_, $context) => {\n\t\t\t\tlet { body, head } = (clientEndPoint.component as ServerComponent)($props, $context);\n\t\t\t\tstyles += head;\n\t\t\t\treturn body;\n\t\t\t});\n\t\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t\t?.default;\n\t\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\t\tif (i === 0) {\n\t\t\t\t\t\tcomponent = layoutEndPoint.component as ServerComponent;\n\t\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t\t} else {\n\t\t\t\t\t\tslotFunctions[i] = trackSlot(i, (_, $context) => {\n\t\t\t\t\t\t\tlet { body, head } = (layoutEndPoint.component as ServerComponent)($props, $context, {\n\t\t\t\t\t\t\t\t_: slotFunctions[i + 1],\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tstyles += head;\n\t\t\t\t\t\t\treturn body;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet html;\n\t\ttry {\n\t\t\tlet { body, head } = component($props, undefined, slots);\n\n\t\t\t// Put the form info in a hidden input so that it can be accessed on the client\n\t\t\tif (form) {\n\t\t\t\tbody += `\\n<input type=\"hidden\" id=\"t-form-data\" value='${JSON.stringify(form).replaceAll(\"'\", \"\\\\'\")}' />`;\n\t\t\t}\n\n\t\t\tstyles += head;\n\t\t\thtml = mergeHead(template, styles).replace(\"%COMPONENT_BODY%\", body);\n\t\t} catch (error) {\n\t\t\t// TODO: Show a proper Error component\n\t\t\thtml = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\t\tconsole.log(error);\n\t\t}\n\n\t\t// A layout that never rendered <slot /> silently drops the whole page\n\t\t// content from the output; find and report the outermost one\n\t\tif (handler.layouts) {\n\t\t\tconst missingLayout = findMissingSlotLayout(handler.layouts, invokedLevels);\n\t\t\tif (missingLayout !== undefined) {\n\t\t\t\twarnMissingSlotContent(missingLayout);\n\t\t\t}\n\t\t}\n\n\t\treturn new Response(html, {\n\t\t\tstatus: formStatus ?? 200,\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"text/html\",\n\t\t\t},\n\t\t});\n\t} finally {\n\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\twhile (entered > 0) {\n\t\t\tentered--;\n\t\t\tawait serverHooks[entered].exit?.(serverParams);\n\t\t}\n\t}\n}\n\nasync function runAction(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tserverEndPoint: PageServerEndPoint | undefined,\n\tparams: Record<string, any>,\n\tquery: URLSearchParams,\n\ttemplate: string | undefined,\n) {\n\t// The action name rides in the query, so look for a `?/name` key rather\n\t// than taking the first key -- the url may carry ordinary load query\n\t// params as well (e.g. `?page=3&/like`, or a default action posted to a\n\t// url that already has a query). A key that names a declared action also\n\t// matches (e.g. `?set`), and a form without an action attribute calls the\n\t// `default` action\n\tconst queryKeys = Array.from(query.keys());\n\tconst actionKey =\n\t\tqueryKeys.find((key) => key.startsWith(\"/\")) ??\n\t\tqueryKeys.find((key) => !!serverEndPoint?.actions?.[key.replace(/^\\//, \"\")]);\n\tconst actionName = (actionKey ?? \"default\").replace(/^\\//, \"\");\n\tif (serverEndPoint?.actions) {\n\t\tconst action = serverEndPoint.actions[actionName];\n\t\tif (action) {\n\t\t\tif (actionName === \"load\" || actionName === \"params\") {\n\t\t\t\t// These names are reserved in the endpoint's schema map: `load`\n\t\t\t\t// validates the query string and `params` the route params, so\n\t\t\t\t// an action with one of these names would get the wrong schema\n\t\t\t\t// applied to its form data\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`The action name \"${actionName}\" is reserved. ` +\n\t\t\t\t\t\t`Rename the action (and its schema key, if any), e.g. to \"save\".`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Route params come from the URL, so a failed validation means the\n\t\t\t// resource doesn't exist\n\t\t\ttry {\n\t\t\t\tparams = await validateEndpointParams(serverEndPoint, params);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ValidationError) return notFound();\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tlet result: Response | undefined | void;\n\t\t\ttry {\n\t\t\t\t// If the endpoint declares a schema for this action, validate the\n\t\t\t\t// submitted form data up front and reject with 422 if it fails\n\t\t\t\tconst schema = endpointSchema(serverEndPoint, actionName);\n\t\t\t\tconst form = schema\n\t\t\t\t\t? await validate(schema, await formDataToRecord(ev.request))\n\t\t\t\t\t: undefined;\n\n\t\t\t\tconst serverParams = buildServerParams(ev, url, params, form ? { form } : {});\n\n\t\t\t\tconst serverHooks = await loadServerHooks(handler);\n\t\t\t\tlet entered = 0;\n\t\t\t\tlet enterResponse: Response | undefined = undefined;\n\t\t\t\ttry {\n\t\t\t\t\t// Hooks run from the root down; a hook's enter can return a\n\t\t\t\t\t// Response to short-circuit the action\n\t\t\t\t\tfor (; entered < serverHooks.length && !enterResponse; entered++) {\n\t\t\t\t\t\tconst hookResult = await serverHooks[entered].enter?.(serverParams);\n\t\t\t\t\t\tif (hookResult) {\n\t\t\t\t\t\t\tenterResponse = hookResult;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tresult = enterResponse || (await action(serverParams));\n\t\t\t\t} finally {\n\t\t\t\t\t// Run exit hooks in reverse order for hooks that were entered\n\t\t\t\t\twhile (entered > 0) {\n\t\t\t\t\t\tentered--;\n\t\t\t\t\t\tawait serverHooks[entered].exit?.(serverParams);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst response = validationErrorResponse(error);\n\t\t\t\tif (response) return response;\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tif (ev.request.headers.has(\"X-Torpor-Form-Submit\")) {\n\t\t\t\t// If the form was submitted from javascript, just return the\n\t\t\t\t// response and it will be handled on the client\n\t\t\t\t// TODO: Should we make the user return a response??\n\t\t\t\tresult ??= ok();\n\n\t\t\t\t// If it's a redirect, we need to stop the browser handling it\n\t\t\t\tif (result.status >= 300 && result.status <= 399) {\n\t\t\t\t\tconst location = result.headers.get(\"Location\") ?? \"\";\n\t\t\t\t\tresult = ok();\n\t\t\t\t\tresult.headers.set(\"X-Torpor-Form-Redirect\", \"\");\n\t\t\t\t\tresult.headers.set(\"Location\", location);\n\t\t\t\t}\n\n\t\t\t\treturn result;\n\t\t\t} else if (\n\t\t\t\t!result ||\n\t\t\t\t(result.status >= 200 && result.status <= 299) ||\n\t\t\t\t(result.status >= 400 && result.status <= 499)\n\t\t\t) {\n\t\t\t\t// If the form was submitted without javascript, and there was\n\t\t\t\t// no result, an ok result, or a 4xx error, re-render the view\n\t\t\t\t// with the form result. The server hook already ran around the\n\t\t\t\t// action, so it is not run again here\n\t\t\t\t// Keep the query string, so that loads validating the query see\n\t\t\t\t// the same values as the original GET. If the form action\n\t\t\t\t// dropped params that the load schema requires, loadView falls\n\t\t\t\t// back to the raw query instead of failing validation\n\t\t\t\tconst newUrl = new URL(url.pathname + url.search, url);\n\t\t\t\tconst formStatus = result?.status;\n\t\t\t\tlet formMessage = await result?.text();\n\t\t\t\tif (formMessage && result?.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\t$page.form = JSON.parse(formMessage);\n\t\t\t\t\tformMessage = undefined;\n\t\t\t\t} else {\n\t\t\t\t\t$page.form = {};\n\t\t\t\t}\n\t\t\t\t// Set status and message if they haven't already been set,\n\t\t\t\t// which means that you can just return e.g. forbidden() or\n\t\t\t\t// forbidden(\"no!\") and there will be something you can work\n\t\t\t\t// with in your component, or you can set a completely custom\n\t\t\t\t// object with whatever info you need\n\t\t\t\t$page.form!.status ??= formStatus ?? 0;\n\t\t\t\t$page.form!.message ??= formMessage ?? \"\";\n\t\t\t\treturn await loadView(ev, newUrl, handler, params, template, formStatus, $page.form, true);\n\t\t\t} else {\n\t\t\t\t// If the form was submitted without javascript and there was a\n\t\t\t\t// redirect or server error, just return the result to handle it\n\t\t\t\t// as normal\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadClientAndServerData(\n\turl: URL,\n\tparams: Record<string, any>,\n\tserverParams: ServerLoadEvent,\n\tdata: Record<string, any>,\n\tclientEndPoint?: PageEndPoint,\n\tserverEndPoint?: PageServerEndPoint,\n) {\n\tif (clientEndPoint?.load) {\n\t\tconst clientParams = buildClientParams(url, params, data);\n\t\tconst clientResponse = await clientEndPoint.load(clientParams);\n\t\tif (clientResponse) {\n\t\t\tif (clientResponse.ok) {\n\t\t\t\tif (clientResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await clientResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn clientResponse;\n\t\t\t}\n\t\t}\n\t}\n\tif (serverEndPoint?.load) {\n\t\tconst serverResponse = await serverEndPoint.load(serverParams);\n\t\tif (serverResponse) {\n\t\t\tif (serverResponse.ok) {\n\t\t\t\tif (serverResponse.headers.get(\"Content-Type\")?.includes(\"application/json\")) {\n\t\t\t\t\tObject.assign(data, await serverResponse.json());\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\treturn serverResponse;\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction buildClientParams(url: URL, params: Record<string, any>, data: Record<string, any>) {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tdata,\n\t};\n}\n\nfunction buildServerParams(\n\tev: ServerEvent,\n\turl: URL,\n\tparams: Record<string, any>,\n\tvalues: { json?: unknown; form?: unknown; query?: unknown } = {},\n): ServerLoadEvent {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tappData: {},\n\t\trequest: ev.request,\n\t\t// The inputs may have been validated already, in which case the request\n\t\t// body has been consumed and the parsed values are returned instead.\n\t\t// Their types come from the endpoint's schemas, so they are loosely\n\t\t// typed here\n\t\tjson: (): Promise<any> =>\n\t\t\tvalues.json !== undefined ? Promise.resolve(values.json) : ev.json(),\n\t\tform: (): Promise<any> =>\n\t\t\tvalues.form !== undefined ? Promise.resolve(values.form) : formDataToRecord(ev.request),\n\t\tquery: (): Promise<any> =>\n\t\t\tvalues.query !== undefined\n\t\t\t\t? Promise.resolve(values.query)\n\t\t\t\t: Promise.resolve(searchParamsToRecord(url.searchParams)),\n\t\tcookies: ev.cookies,\n\t\theaders: ev.headers,\n\t\tadapter: ev.adapter,\n\t};\n}\n"],"mappings":"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"}
package/dist/test.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as ServerEvent } from "./ServerEvent-CTx3Q-hk.mjs";
2
- import { t as Site } from "./Site-CE0_o5Ci.mjs";
2
+ import { t as Site } from "./Site-CTADVh7d.mjs";
3
3
  //#region src/test/runTest.d.ts
4
4
  /**
5
5
  * Runs a site route in test mode
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.mts","names":[],"sources":["../src/test/runTest.ts"],"mappings":";;;;;;;;;;iBA4C8B,QAC7B,MAAM,MACN,eACA,KAAK,cACH,QAAQ"}
1
+ {"version":3,"file":"test.d.mts","names":[],"sources":["../src/test/runTest.ts"],"mappings":";;;;;;;;;;iBAa8B,QAC7B,MAAM,MACN,eACA,KAAK,cACH,QAAQ"}