@torpor/build 0.1.24 → 0.1.26

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 (58) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{Site-DgXPSQoO.d.ts → Site-4tGiw2-I.d.ts} +23 -2
  3. package/dist/{Site-BeCLcGj_.d.cts → Site-BXX-S541.d.cts} +23 -2
  4. package/dist/bin/index.cjs +1 -1
  5. package/dist/bin/index.cjs.map +1 -1
  6. package/dist/bin/index.js +3 -3
  7. package/dist/{chunk-WMHM5RGZ.js → chunk-565UOT3F.js} +5 -2
  8. package/dist/chunk-565UOT3F.js.map +1 -0
  9. package/dist/{chunk-D6LHMH2C.js → chunk-B2UNTYN5.js} +8 -8
  10. package/dist/chunk-B2UNTYN5.js.map +1 -0
  11. package/dist/{chunk-65K3R3CP.js → chunk-PTVHEJZ5.js} +4 -2
  12. package/dist/{chunk-65K3R3CP.js.map → chunk-PTVHEJZ5.js.map} +1 -1
  13. package/dist/{chunk-LHRCEWBG.js → chunk-UCA7KIGG.js} +2 -2
  14. package/dist/{chunk-2QQN3D3K.js → chunk-WVXAN4IV.js} +2 -2
  15. package/dist/{chunk-2QQN3D3K.js.map → chunk-WVXAN4IV.js.map} +1 -1
  16. package/dist/index.cjs +64 -26
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +1 -1
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.js +48 -10
  21. package/dist/index.js.map +1 -1
  22. package/dist/metafile-cjs.json +1 -1
  23. package/dist/metafile-esm.json +1 -1
  24. package/dist/response.cjs +7 -7
  25. package/dist/response.cjs.map +1 -1
  26. package/dist/response.js +2 -2
  27. package/dist/run.cjs +1 -1
  28. package/dist/run.cjs.map +1 -1
  29. package/dist/run.d.cts +1 -1
  30. package/dist/run.d.ts +1 -1
  31. package/dist/run.js +3 -3
  32. package/dist/state.cjs +4 -1
  33. package/dist/state.cjs.map +1 -1
  34. package/dist/state.d.cts +4 -0
  35. package/dist/state.d.ts +4 -0
  36. package/dist/state.js +1 -1
  37. package/dist/test.cjs +5 -2
  38. package/dist/test.cjs.map +1 -1
  39. package/dist/test.d.cts +1 -1
  40. package/dist/test.d.ts +1 -1
  41. package/dist/test.js +3 -3
  42. package/package.json +4 -4
  43. package/src/response/badRequest.ts +1 -1
  44. package/src/response/forbidden.ts +1 -1
  45. package/src/response/methodNotAllowed.ts +1 -1
  46. package/src/response/notFound.ts +1 -1
  47. package/src/response/serverError.ts +1 -1
  48. package/src/response/unauthorized.ts +1 -1
  49. package/src/response/unprocessable.ts +1 -1
  50. package/src/site/Site.ts +82 -20
  51. package/src/site/clientEntry.ts +11 -4
  52. package/src/site/serverEntry.ts +42 -5
  53. package/src/state/$page.ts +4 -5
  54. package/src/state/Page.ts +8 -0
  55. package/src/types/RouteType.ts +12 -0
  56. package/dist/chunk-D6LHMH2C.js.map +0 -1
  57. package/dist/chunk-WMHM5RGZ.js.map +0 -1
  58. /package/dist/{chunk-LHRCEWBG.js.map → chunk-UCA7KIGG.js.map} +0 -0
package/dist/run.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  runBuild,
4
4
  runDev,
5
5
  runPreview
6
- } from "./chunk-LHRCEWBG.js";
7
- import "./chunk-D6LHMH2C.js";
6
+ } from "./chunk-UCA7KIGG.js";
7
+ import "./chunk-B2UNTYN5.js";
8
8
  import "./chunk-LO7P2LJE.js";
9
- import "./chunk-2QQN3D3K.js";
9
+ import "./chunk-WVXAN4IV.js";
10
10
  import "./chunk-KKFND4HK.js";
11
11
  import "./chunk-SJVHHGVW.js";
12
12
  export {
package/dist/state.cjs CHANGED
@@ -29,7 +29,10 @@ var import_view = require("@torpor/view");
29
29
  var $page = (0, import_view.$watch)(
30
30
  {
31
31
  status: 404,
32
- url: new URL("http://localhost")
32
+ url: new URL("http://localhost"),
33
+ error: {
34
+ message: ""
35
+ }
33
36
  },
34
37
  { shallow: true }
35
38
  );
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/state.ts","../src/state/$page.ts"],"sourcesContent":["import $page from \"./state/$page\";\n\nexport { $page };\n","import { $watch } from \"@torpor/view\";\n\ntype Page = {\n\tstatus: number;\n\turl: URL;\n};\n\nconst $page: Page = $watch(\n\t{\n\t\tstatus: 404,\n\t\turl: new URL(\"http://localhost\"),\n\t},\n\t{ shallow: true },\n);\n\nexport default $page;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAuB;AAOvB,IAAM,YAAc;AAAA,EACnB;AAAA,IACC,QAAQ;AAAA,IACR,KAAK,IAAI,IAAI,kBAAkB;AAAA,EAChC;AAAA,EACA,EAAE,SAAS,KAAK;AACjB;AAEA,IAAO,eAAQ;","names":[]}
1
+ {"version":3,"sources":["../src/state.ts","../src/state/$page.ts"],"sourcesContent":["import $page from \"./state/$page\";\n\nexport { $page };\n","import { $watch } from \"@torpor/view\";\nimport Page from \"./Page\";\n\nconst $page: Page = $watch(\n\t{\n\t\tstatus: 404,\n\t\turl: new URL(\"http://localhost\"),\n\t\terror: {\n\t\t\tmessage: \"\",\n\t\t},\n\t},\n\t{ shallow: true },\n);\n\nexport default $page;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAAuB;AAGvB,IAAM,YAAc;AAAA,EACnB;AAAA,IACC,QAAQ;AAAA,IACR,KAAK,IAAI,IAAI,kBAAkB;AAAA,IAC/B,OAAO;AAAA,MACN,SAAS;AAAA,IACV;AAAA,EACD;AAAA,EACA,EAAE,SAAS,KAAK;AACjB;AAEA,IAAO,eAAQ;","names":[]}
package/dist/state.d.cts CHANGED
@@ -1,7 +1,11 @@
1
1
  type Page = {
2
2
  status: number;
3
3
  url: URL;
4
+ error: {
5
+ message: string;
6
+ };
4
7
  };
8
+
5
9
  declare const $page: Page;
6
10
 
7
11
  export { $page };
package/dist/state.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  type Page = {
2
2
  status: number;
3
3
  url: URL;
4
+ error: {
5
+ message: string;
6
+ };
4
7
  };
8
+
5
9
  declare const $page: Page;
6
10
 
7
11
  export { $page };
package/dist/state.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  page_default
3
- } from "./chunk-WMHM5RGZ.js";
3
+ } from "./chunk-565UOT3F.js";
4
4
  export {
5
5
  page_default as $page
6
6
  };
package/dist/test.cjs CHANGED
@@ -56,7 +56,7 @@ function response(status, body) {
56
56
 
57
57
  // src/response/notFound.ts
58
58
  function notFound(body) {
59
- return response(404, body);
59
+ return response(404, body ?? "Not found");
60
60
  }
61
61
 
62
62
  // src/response/ok.ts
@@ -298,7 +298,10 @@ var import_view = require("@torpor/view");
298
298
  var $page = (0, import_view.$watch)(
299
299
  {
300
300
  status: 404,
301
- url: new URL("http://localhost")
301
+ url: new URL("http://localhost"),
302
+ error: {
303
+ message: ""
304
+ }
302
305
  },
303
306
  { shallow: true }
304
307
  );
package/dist/test.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/test.ts","../src/test/runTest.ts","../src/response/response.ts","../src/response/notFound.ts","../src/response/ok.ts","../src/response/transfer.ts","../src/response/seeOther.ts","../src/server/CookieHelper.ts","../src/server/HeaderHelper.ts","../src/server/ServerEvent.ts","../src/site/pathToRegex.ts","../src/site/Router.ts","../src/state/$page.ts","../src/types/RouteType.ts"],"sourcesContent":["import runTest from \"./test/runTest\";\n\nexport { runTest };\n","import { type ServerComponent, type ServerSlotRender } from \"@torpor/view\";\nimport path from \"node:path\";\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 Router from \"../site/Router.ts\";\nimport Site from \"../site/Site\";\nimport $page from \"../state/$page.ts\";\nimport type PageEndPoint from \"../types/PageEndPoint.ts\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint.ts\";\nimport RouteHandler from \"../types/RouteHandler.ts\";\nimport {\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\";\n\n/**\n * Runs a site route in test mode\n * @param site The site configuration, with routing configured\n * @param route The route path e.g. `/home`\n * @param ev An optional ServerEvent, which you can use to set test cookies and headers\n * @returns An HTTP response containing the requested data, HTML, or error status\n */\nexport default async function runTest(\n\tsite: Site,\n\troute: string,\n\tev?: ServerEvent,\n): Promise<Response> {\n\tif (!route.startsWith(\"/\")) route += \"/\";\n\tif (!ev) {\n\t\tconst req = new Request(`http://localhost${route}`);\n\t\tev = new ServerEvent(req);\n\t}\n\n\t// Build the router from the Site object created by the user\n\tconst router = new Router();\n\trouter.addPages(\n\t\tsite.routes.map((r) => ({\n\t\t\tpath: r.path,\n\t\t\ttype: r.type,\n\t\t\tendPoint: () => import(/* @vite-ignore */ path.join(site.root, r.file)),\n\t\t})),\n\t);\n\n\treturn await load(router, ev, \"%COMPONENT_HTML%\");\n}\n\n// HACK: This is a copy of serverEntry\n// TODO: Make it shared instead!\nasync function load(router: Router, ev: ServerEvent, template: string): Promise<Response> {\n\t//const url = new URL(`http://${process.env.HOST ?? \"localhost\"}${ev.request.url}`);\n\tconst url = new URL(ev.request.url);\n\tconst path = url.pathname;\n\tconst query = url.searchParams;\n\n\t//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\tconst route = router.match(path, query);\n\tif (!route) {\n\t\treturn notFound();\n\t}\n\n\t// Update $page before building the components\n\t// TODO: Find somewhere better to put this\n\t$page.url = url;\n\n\tconst handler = route.handler;\n\tconst params = route.params || {};\n\n\t// TODO: Hit the server hook out here\n\t// We could maybe set data loading up as middleware on a route??????\n\n\tswitch (handler.type) {\n\t\tcase PAGE_ROUTE:\n\t\tcase LAYOUT_ROUTE: {\n\t\t\t// It's a /+page.ts or /_layout.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\treturn await runAction(ev, url, handler, params, query);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase PAGE_SERVER_ROUTE:\n\t\tcase LAYOUT_SERVER_ROUTE: {\n\t\t\t// It's a /+page.server.ts or /_layout.server.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadData(ev, url, handler, \"load\", params);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase SERVER_ROUTE: {\n\t\t\t// It's a /+server.ts endpoint\n\t\t\tconst functionName = ev.request.method.toLowerCase().replace(\"delete\", \"del\");\n\t\t\treturn await loadData(ev, url, handler, functionName, params);\n\t\t}\n\t\tcase HOOK_ROUTE:\n\t\tcase HOOK_SERVER_ROUTE: {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadData(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tfunctionName: string,\n\tparams: Record<string, string>,\n) {\n\tconst serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint()).default;\n\tif (serverEndPoint && serverEndPoint[functionName]) {\n\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\tif (handler.serverHook) {\n\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\tif (serverHook?.handle) {\n\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t}\n\t\t}\n\n\t\tconst result = await serverEndPoint[functionName](serverParams);\n\n\t\t// If there was no response returned from load (such as errors or a\n\t\t// redirect), send an ok response\n\t\treturn result || ok();\n\t}\n\n\treturn notFound();\n}\n\nasync function loadView(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\ttemplate: string,\n) {\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;\n\tif (!clientEndPoint?.component) {\n\t\treturn notFound();\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// Maybe hit the server hook\n\tconst serverParams = buildServerParams(ev, url, params);\n\tif (handler.serverHook) {\n\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\tif (serverHook?.handle) {\n\t\t\tawait serverHook.handle(serverParams);\n\t\t}\n\t}\n\n\t// Pass the data into $props\n\t// TODO: Promise.all\n\t// NOTE: We're loading data from top to bottom, overriding as we go, and I'm not sure if this is the best way to go\n\tlet data = {};\n\tif (handler.layouts) {\n\t\tfor (let layout of handler.layouts) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tserverParams,\n\t\t\t\tdata,\n\t\t\t\tlayoutEndPoint,\n\t\t\t\tlayoutServerEndPoint,\n\t\t\t);\n\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\treturn layoutResponse;\n\t\t\t}\n\t\t}\n\t}\n\tlet endPointResponse = await loadClientAndServerData(\n\t\turl,\n\t\tparams,\n\t\tserverParams,\n\t\tdata,\n\t\tclientEndPoint,\n\t\tserverEndPoint,\n\t);\n\tif (endPointResponse?.ok === false) {\n\t\treturn endPointResponse;\n\t}\n\tlet $props: Record<string, any> = { data };\n\n\t// If there are layouts, work our way upwards, pushing each component into\n\t// the default slot of its parent\n\t// TODO: Also handle layout server data\n\t// TODO: There's probably a nicer way to do this with reducers or something\n\t// TODO: Re-use parent layouts\n\tlet component = clientEndPoint.component as ServerComponent;\n\tlet slots: Record<string, ServerSlotRender> | undefined = undefined;\n\tif (handler.layouts) {\n\t\tlet slotFunctions: ServerSlotRender[] = [];\n\t\tslotFunctions[handler.layouts.length] = (_, context) =>\n\t\t\t(clientEndPoint.component as ServerComponent)($props, context);\n\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t?.default;\n\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\tif (i === 0) {\n\t\t\t\t\tcomponent = layoutEndPoint.component as ServerComponent;\n\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t} else {\n\t\t\t\t\tslotFunctions[i] = (_, context) =>\n\t\t\t\t\t\t(layoutEndPoint.component as ServerComponent)($props, context, {\n\t\t\t\t\t\t\t_: slotFunctions[i + 1],\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlet html;\n\ttry {\n\t\thtml = component($props, undefined, slots);\n\t\thtml = template.replace(\"%COMPONENT_HTML%\", html);\n\t} catch (error) {\n\t\t// TODO: Show a proper Error component\n\t\thtml = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\tconsole.log(error);\n\t}\n\n\treturn new Response(html, {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"text/html\",\n\t\t},\n\t});\n}\n\nasync function runAction(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\tquery: URLSearchParams,\n) {\n\tconst actionName = (Array.from(query.keys())[0] || \"default\").replace(/^\\//, \"\");\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint()).default;\n\tif (serverEndPoint?.actions) {\n\t\tconst action = serverEndPoint.actions[actionName];\n\t\tif (action) {\n\t\t\t// TODO: form.errors etc\n\t\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\t\tif (handler.serverHook) {\n\t\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\t\tif (serverHook?.handle) {\n\t\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await action(serverParams); // <--\n\n\t\t\t// If there was no response returned from the action (such as errors\n\t\t\t// or a redirect), reload the page by redirecting\n\t\t\treturn result || seeOther(url.pathname);\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadClientAndServerData(\n\turl: URL,\n\tparams: Record<string, string>,\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, data, params);\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, string>, 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, string>,\n): ServerLoadEvent {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tappData: {},\n\t\trequest: ev.request,\n\t\tcookies: ev.cookies,\n\t\theaders: ev.headers,\n\t\tadapter: ev.adapter,\n\t};\n}\n","/**\n * Creates a response with the supplied status code and optional body.\n *\n * If the body is an object, it will be converted to JSON and the content-type\n * header set to \"application/json\". Otherwise, if the body is a string, the\n * content-type header will be set to \"text/plain\".\n */\nexport default function response(status: number, body?: object | string): Response {\n\tlet headers: Record<string, string> | undefined = undefined;\n\tif (body) {\n\t\tif (typeof body === \"object\") {\n\t\t\theaders = { \"content-type\": \"application/json\" };\n\t\t\tbody = JSON.stringify(body);\n\t\t} else {\n\t\t\theaders = { \"content-type\": \"text/plain\" };\n\t\t}\n\t}\n\treturn new Response(body, {\n\t\tstatus,\n\t\theaders,\n\t});\n}\n","import response from \"./response\";\n\n/**\n * 404 Not Found\n *\n * The server cannot find the requested resource. In the browser, this means the\n * URL is not recognized. In an API, this can also mean that the endpoint is\n * valid but the resource itself does not exist. Servers may also send this\n * response instead of 403 Forbidden to hide the existence of a resource from an\n * unauthorized client. This response code is probably the most well known due\n * to its frequent occurrence on the web.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404\n */\nexport default function notFound(body?: object | string): Response {\n\treturn response(404, body);\n}\n","import response from \"./response\";\n\n/**\n * 200 OK\n *\n * The request succeeded. The result meaning of \"success\" depends on the HTTP\n * method:\n * - GET: The resource has been fetched and transmitted in the message body.\n * - HEAD: The representation headers are included in the response without any\n * message body.\n * - PUT or POST: The resource describing the result of the action is\n * transmitted in the message body.\n * - TRACE: The message body contains the request message as received by the\n * server.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200\n */\nexport default function ok(body?: object | string): Response {\n\treturn response(200, body);\n}\n","/**\n * Creates a redirect response with the supplied status code.\n */\nexport default function transfer(\n\tstatus: 300 | 301 | 302 | 303 | 304 | 307 | 308,\n\tlocation: string,\n): Response {\n\treturn new Response(null, {\n\t\tstatus,\n\t\theaders: {\n\t\t\tlocation,\n\t\t},\n\t});\n}\n","import transfer from \"./transfer\";\n\n/**\n * 303 See Other\n *\n * The server sent this response to direct the client to get the requested\n * resource at another URI with a GET request.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303\n */\nexport default function seeOther(location: string): Response {\n\treturn transfer(303, location);\n}\n","import { SerializeOptions, parse, serialize } from \"cookie\";\n\nexport default class CookieHelper {\n\trequest: Request;\n\tcookies: Map<string, string> = new Map();\n\n\tconstructor(request: Request) {\n\t\tthis.request = request;\n\t}\n\n\tget(name: string): string | undefined {\n\t\t// If it's been set, return the value from the map\n\t\tif (this.cookies.has(name)) {\n\t\t\treturn parse(this.cookies.get(name)!)[name];\n\t\t}\n\t\t// Otherwise, return the value from the `cookie` header\n\t\tconst header = this.request.headers.get(\"cookie\");\n\t\treturn parse(header ?? \"\")[name];\n\t}\n\n\tset(name: string, value: string, options?: SerializeOptions): void {\n\t\t// Set a cookie header with some default values for security\n\t\toptions ??= {};\n\t\toptions.httpOnly ??= true;\n\t\toptions.secure ??= true;\n\t\toptions.sameSite ??= \"lax\";\n\t\tthis.cookies.set(name, serialize(name, value, options));\n\t}\n\n\tdelete(name: string, options?: SerializeOptions): void {\n\t\t// Set a cookie header with an immediate expiry date\n\t\toptions ??= {};\n\t\toptions.expires = new Date(0);\n\t\tthis.cookies.set(name, serialize(name, \"\", options));\n\t}\n}\n","export default class HeaderHelper {\n\trequest: Request;\n\theaders: Map<string, string> = new Map();\n\n\tconstructor(request: Request) {\n\t\tthis.request = request;\n\t}\n\n\tget(name: string): string | undefined {\n\t\t// If it's been set, return the updated value\n\t\tif (this.headers.has(name)) {\n\t\t\treturn this.headers.get(name);\n\t\t}\n\t\t// Otherwise, return the value from the header\n\t\treturn this.request.headers.get(name) ?? undefined;\n\t}\n\n\tset(name: string, value: string): void {\n\t\tthis.headers.set(name, value);\n\t}\n\n\tdelete(name: string): void {\n\t\tthis.headers.delete(name);\n\t}\n}\n","import CookieHelper from \"./CookieHelper\";\nimport HeaderHelper from \"./HeaderHelper\";\n\nexport default class ServerEvent {\n\trequest: Request;\n\tresponse?: Response;\n\t// TODO: Should we cast types??\n\tparams?: Record<string, string>;\n\n\tadapter: any;\n\n\tcookies: CookieHelper;\n\theaders: HeaderHelper;\n\n\tconstructor(request: Request, params?: Record<string, string>) {\n\t\tthis.request = request;\n\t\tthis.params = params;\n\t\tthis.cookies = new CookieHelper(request);\n\t\tthis.headers = new HeaderHelper(request);\n\n\t\t// Adapters may have added the `adapter` property to globalThis\n\t\t// @ts-ignore\n\t\tthis.adapter = globalThis.adapter;\n\t}\n\n\taddHeaders(): void {\n\t\tif (!this.response) {\n\t\t\tthrow new Error(\"Response not created yet\");\n\t\t}\n\n\t\tfor (let cookie of this.cookies.cookies.values()) {\n\t\t\tthis.response.headers.append(\"set-cookie\", cookie);\n\t\t}\n\n\t\tfor (let header of this.headers.headers.entries()) {\n\t\t\tthis.response.headers.append(header[0], header[1]);\n\t\t}\n\t}\n}\n","export default function pathToRegex(path: string): RegExp {\n\tconst pattern =\n\t\tpath\n\t\t\t.split(\"/\")\n\t\t\t// Replace `[slug]` with a grouped match so we can pull out params\n\t\t\t.map((p) => {\n\t\t\t\treturn p.replace(/\\[([^\\/]+?)\\]/, \"(?<$1>[^\\\\/]+?)\");\n\t\t\t})\n\t\t\t.join(\"\\\\/\")\n\t\t\t// Replace globs with regex syntax\n\t\t\t.replaceAll(\"*\", \".*\") +\n\t\t// Ignore trailing slashes -- `/path` should be treated the same as `/path/`\n\t\t\"/*\";\n\treturn new RegExp(`^${pattern}/*$`);\n}\n","import type LayoutHandler from \"../types/LayoutHandler\";\nimport type ManifestRoute from \"../types/ManifestRoute\";\nimport type RouteHandler from \"../types/RouteHandler\";\nimport pathToRegex from \"./pathToRegex\";\n\n/**\n * A router that handles file routes with layouts, hooks, etc.\n */\nexport default class Router {\n\troutes: Route[] = [];\n\n\tconstructor() {}\n\n\t//add(path: string, file: string) {\n\t//\t// TODO: Call the specific addX method based on the file path\n\t//\tthis.addPage(route, file);\n\t//}\n\n\taddPages(routes: ManifestRoute[]): this {\n\t\tfor (let r of routes) {\n\t\t\tthis.routes.push(\n\t\t\t\tnew Route(r.path, {\n\t\t\t\t\tpath: r.path,\n\t\t\t\t\ttype: r.type,\n\t\t\t\t\tendPoint: r.endPoint,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tthis.#sortRoutes();\n\t\treturn this;\n\t}\n\n\t// TODO: Allow calling with the endpoint itself, so that you can setup an app with no scaffold\n\taddPage(path: string, type: number, endPoint: () => Promise<any>): this {\n\t\tthis.routes.push(\n\t\t\tnew Route(path, {\n\t\t\t\tpath,\n\t\t\t\ttype,\n\t\t\t\tendPoint,\n\t\t\t}),\n\t\t);\n\t\tthis.#sortRoutes();\n\t\treturn this;\n\t}\n\n\t/*\n\tloadPage(ev: ServerEvent, file: string): Response {\n\t\tlet page = this.pages.get(file);\n\t\tif (!page) {\n\t\t\tpage = {};\n\t\t\tthis.pages.set(file, page);\n\t\t}\n\t}\n\t*/\n\n\tmatch(path: string, query: URLSearchParams): Match | undefined {\n\t\tfor (let route of this.routes) {\n\t\t\tlet match = path.match(route.regex);\n\t\t\tif (match) {\n\t\t\t\t// Lazy load server endpoints and layouts\n\t\t\t\tif (!route.handler.loaded) {\n\t\t\t\t\tthis.#loadHandler(route.handler, route.path);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\thandler: route.handler,\n\t\t\t\t\tparams: match.groups,\n\t\t\t\t\tquery,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconsole.log(`not found: '${path}'`);\n\t}\n\n\t#loadHandler(handler: RouteHandler, path: string) {\n\t\thandler.layouts = this.#findLayouts(path);\n\t\thandler.serverEndPoint = this.#findServer(path);\n\t\thandler.serverHook = this.#findServerHook(path);\n\t}\n\n\t#findLayouts(path: string): LayoutHandler[] | undefined {\n\t\tlet layouts: LayoutHandler[] = [];\n\t\tlet parts = path\n\t\t\t// Strip the trailing`~/server` (so we don't look for `~/server/_layout`\n\t\t\t// which will never exist)\n\t\t\t.replace(/\\/~server$/, \"\")\n\t\t\t// The path will always start with / so splitting e.g. `/first/second`\n\t\t\t// will result in ['', 'first', 'second'] and checks for `/_layout`,\n\t\t\t// `/first/_layout` and `/second/_layout`\n\t\t\t.split(\"/\");\n\t\tlet basePath = \"\";\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tbasePath += \"/\" + parts[i];\n\t\t\t}\n\t\t\tconst layoutPath = basePath + \"/_layout\";\n\t\t\tconst layoutRoute = this.routes.find((r) => r.path === layoutPath);\n\t\t\tif (layoutRoute) {\n\t\t\t\tlayouts.push({\n\t\t\t\t\tpath: layoutPath,\n\t\t\t\t\tendPoint: layoutRoute.handler.endPoint,\n\t\t\t\t\tserverEndPoint: this.#findServer(layoutPath),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn layouts.length ? layouts : undefined;\n\t}\n\n\t#findServer(path: string): (() => Promise<any>) | undefined {\n\t\tconst serverPath = path.replace(/\\/$/, \"\") + \"/~server\";\n\t\tconst serverRoute = this.routes.find((r) => r.path === serverPath);\n\t\treturn serverRoute && serverRoute.handler.endPoint;\n\t}\n\n\t#findServerHook(path: string): (() => Promise<any>) | undefined {\n\t\t// TODO: Should this be a collection, like layouts?\n\t\tlet serverHookPath = \"/_hook/~server\";\n\t\t// HACK: Generalise this\n\t\tif (path.startsWith(\"/api/\")) {\n\t\t\tserverHookPath = \"/api\" + serverHookPath;\n\t\t}\n\t\tconst serverHookRoute = this.routes.find((r) => r.path === serverHookPath);\n\t\treturn serverHookRoute && serverHookRoute.handler.endPoint;\n\t}\n\n\t// HACK: We're doing this here as well as in Site, so that you can use the\n\t// Router externally without Site\n\t#sortRoutes() {\n\t\tthis.routes = this.routes.sort((a, b) => {\n\t\t\t// Sort [param]s after paths\n\t\t\t// There might be a quicker/easier way to do this\n\t\t\tfor (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {\n\t\t\t\tif (a.path[i] === \"[\" && b.path[i] !== \"[\") {\n\t\t\t\t\treturn 1;\n\t\t\t\t} else if (b.path[i] === \"[\" && a.path[i] !== \"[\") {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (a.path[i] === b.path[i]) {\n\t\t\t\t\t// Keep going...\n\t\t\t\t} else {\n\t\t\t\t\treturn a.path[i].localeCompare(b.path[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a.path.length - b.path.length;\n\t\t});\n\t}\n}\n\nclass Route {\n\tpath: string;\n\tregex: RegExp;\n\thandler: RouteHandler;\n\n\tconstructor(path: string, handler: RouteHandler) {\n\t\tthis.path = path;\n\t\tthis.regex = pathToRegex(path);\n\t\tthis.handler = handler;\n\t}\n}\n\ntype Match = {\n\thandler: RouteHandler;\n\tparams?: Record<string, string>;\n\tquery: URLSearchParams;\n};\n","import { $watch } from \"@torpor/view\";\n\ntype Page = {\n\tstatus: number;\n\turl: URL;\n};\n\nconst $page: Page = $watch(\n\t{\n\t\tstatus: 404,\n\t\turl: new URL(\"http://localhost\"),\n\t},\n\t{ shallow: true },\n);\n\nexport default $page;\n","export const PAGE_ROUTE = 0;\nexport const PAGE_SERVER_ROUTE = 1;\nexport const SERVER_ROUTE = 3;\nexport const LAYOUT_ROUTE = 4;\nexport const LAYOUT_SERVER_ROUTE = 5;\nexport const HOOK_ROUTE = 6;\nexport const HOOK_SERVER_ROUTE = 7;\n"],"mappings":"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","names":["path","path","path"]}
1
+ {"version":3,"sources":["../src/test.ts","../src/test/runTest.ts","../src/response/response.ts","../src/response/notFound.ts","../src/response/ok.ts","../src/response/transfer.ts","../src/response/seeOther.ts","../src/server/CookieHelper.ts","../src/server/HeaderHelper.ts","../src/server/ServerEvent.ts","../src/site/pathToRegex.ts","../src/site/Router.ts","../src/state/$page.ts","../src/types/RouteType.ts"],"sourcesContent":["import runTest from \"./test/runTest\";\n\nexport { runTest };\n","import { type ServerComponent, type ServerSlotRender } from \"@torpor/view\";\nimport path from \"node:path\";\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 Router from \"../site/Router.ts\";\nimport Site from \"../site/Site\";\nimport $page from \"../state/$page.ts\";\nimport type PageEndPoint from \"../types/PageEndPoint.ts\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint.ts\";\nimport RouteHandler from \"../types/RouteHandler.ts\";\nimport {\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\";\n\n/**\n * Runs a site route in test mode\n * @param site The site configuration, with routing configured\n * @param route The route path e.g. `/home`\n * @param ev An optional ServerEvent, which you can use to set test cookies and headers\n * @returns An HTTP response containing the requested data, HTML, or error status\n */\nexport default async function runTest(\n\tsite: Site,\n\troute: string,\n\tev?: ServerEvent,\n): Promise<Response> {\n\tif (!route.startsWith(\"/\")) route += \"/\";\n\tif (!ev) {\n\t\tconst req = new Request(`http://localhost${route}`);\n\t\tev = new ServerEvent(req);\n\t}\n\n\t// Build the router from the Site object created by the user\n\tconst router = new Router();\n\trouter.addPages(\n\t\tsite.routes.map((r) => ({\n\t\t\tpath: r.path,\n\t\t\ttype: r.type,\n\t\t\tendPoint: () => import(/* @vite-ignore */ path.join(site.root, r.file)),\n\t\t})),\n\t);\n\n\treturn await load(router, ev, \"%COMPONENT_HTML%\");\n}\n\n// HACK: This is a copy of serverEntry\n// TODO: Make it shared instead!\nasync function load(router: Router, ev: ServerEvent, template: string): Promise<Response> {\n\t//const url = new URL(`http://${process.env.HOST ?? \"localhost\"}${ev.request.url}`);\n\tconst url = new URL(ev.request.url);\n\tconst path = url.pathname;\n\tconst query = url.searchParams;\n\n\t//console.log(`handling ${ev.request.method} for '${path}'${query.size ? ` with ${query}` : \"\"}`);\n\n\tconst route = router.match(path, query);\n\tif (!route) {\n\t\treturn notFound();\n\t}\n\n\t// Update $page before building the components\n\t// TODO: Find somewhere better to put this\n\t$page.url = url;\n\n\tconst handler = route.handler;\n\tconst params = route.params || {};\n\n\t// TODO: Hit the server hook out here\n\t// We could maybe set data loading up as middleware on a route??????\n\n\tswitch (handler.type) {\n\t\tcase PAGE_ROUTE:\n\t\tcase LAYOUT_ROUTE: {\n\t\t\t// It's a /+page.ts or /_layout.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadView(ev, url, handler, params, template);\n\t\t\t} else if (ev.request.method === \"POST\") {\n\t\t\t\treturn await runAction(ev, url, handler, params, query);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase PAGE_SERVER_ROUTE:\n\t\tcase LAYOUT_SERVER_ROUTE: {\n\t\t\t// It's a /+page.server.ts or /_layout.server.ts endpoint\n\t\t\tif (ev.request.method === \"GET\") {\n\t\t\t\treturn await loadData(ev, url, handler, \"load\", params);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase SERVER_ROUTE: {\n\t\t\t// It's a /+server.ts endpoint\n\t\t\tconst functionName = ev.request.method.toLowerCase().replace(\"delete\", \"del\");\n\t\t\treturn await loadData(ev, url, handler, functionName, params);\n\t\t}\n\t\tcase HOOK_ROUTE:\n\t\tcase HOOK_SERVER_ROUTE: {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadData(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tfunctionName: string,\n\tparams: Record<string, string>,\n) {\n\tconst serverEndPoint: ServerEndPoint | undefined = (await handler.endPoint()).default;\n\tif (serverEndPoint && serverEndPoint[functionName]) {\n\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\tif (handler.serverHook) {\n\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\tif (serverHook?.handle) {\n\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t}\n\t\t}\n\n\t\tconst result = await serverEndPoint[functionName](serverParams);\n\n\t\t// If there was no response returned from load (such as errors or a\n\t\t// redirect), send an ok response\n\t\treturn result || ok();\n\t}\n\n\treturn notFound();\n}\n\nasync function loadView(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\ttemplate: string,\n) {\n\t// There must be a client endpoint with a component\n\tconst clientEndPoint: PageEndPoint | undefined = (await handler.endPoint()).default;\n\tif (!clientEndPoint?.component) {\n\t\treturn notFound();\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// Maybe hit the server hook\n\tconst serverParams = buildServerParams(ev, url, params);\n\tif (handler.serverHook) {\n\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\tif (serverHook?.handle) {\n\t\t\tawait serverHook.handle(serverParams);\n\t\t}\n\t}\n\n\t// Pass the data into $props\n\t// TODO: Promise.all\n\t// NOTE: We're loading data from top to bottom, overriding as we go, and I'm not sure if this is the best way to go\n\tlet data = {};\n\tif (handler.layouts) {\n\t\tfor (let layout of handler.layouts) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;\n\t\t\tconst layoutServerEndPoint: PageServerEndPoint | undefined =\n\t\t\t\tlayout.serverEndPoint && (await layout.serverEndPoint())?.default;\n\t\t\tconst layoutResponse = await loadClientAndServerData(\n\t\t\t\turl,\n\t\t\t\tparams,\n\t\t\t\tserverParams,\n\t\t\t\tdata,\n\t\t\t\tlayoutEndPoint,\n\t\t\t\tlayoutServerEndPoint,\n\t\t\t);\n\t\t\tif (layoutResponse?.ok === false) {\n\t\t\t\treturn layoutResponse;\n\t\t\t}\n\t\t}\n\t}\n\tlet endPointResponse = await loadClientAndServerData(\n\t\turl,\n\t\tparams,\n\t\tserverParams,\n\t\tdata,\n\t\tclientEndPoint,\n\t\tserverEndPoint,\n\t);\n\tif (endPointResponse?.ok === false) {\n\t\treturn endPointResponse;\n\t}\n\tlet $props: Record<string, any> = { data };\n\n\t// If there are layouts, work our way upwards, pushing each component into\n\t// the default slot of its parent\n\t// TODO: Also handle layout server data\n\t// TODO: There's probably a nicer way to do this with reducers or something\n\t// TODO: Re-use parent layouts\n\tlet component = clientEndPoint.component as ServerComponent;\n\tlet slots: Record<string, ServerSlotRender> | undefined = undefined;\n\tif (handler.layouts) {\n\t\tlet slotFunctions: ServerSlotRender[] = [];\n\t\tslotFunctions[handler.layouts.length] = (_, context) =>\n\t\t\t(clientEndPoint.component as ServerComponent)($props, context);\n\t\tfor (let i = handler.layouts.length - 1; i >= 0; i--) {\n\t\t\tconst layoutEndPoint: PageEndPoint | undefined = (await handler.layouts[i].endPoint())\n\t\t\t\t?.default;\n\t\t\tif (layoutEndPoint?.component) {\n\t\t\t\tif (i === 0) {\n\t\t\t\t\tcomponent = layoutEndPoint.component as ServerComponent;\n\t\t\t\t\tslots = { _: slotFunctions[i + 1] };\n\t\t\t\t} else {\n\t\t\t\t\tslotFunctions[i] = (_, context) =>\n\t\t\t\t\t\t(layoutEndPoint.component as ServerComponent)($props, context, {\n\t\t\t\t\t\t\t_: slotFunctions[i + 1],\n\t\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlet html;\n\ttry {\n\t\thtml = component($props, undefined, slots);\n\t\thtml = template.replace(\"%COMPONENT_HTML%\", html);\n\t} catch (error) {\n\t\t// TODO: Show a proper Error component\n\t\thtml = '<span style=\"color: red\">Script syntax error</span><p>' + error + \"</p>\";\n\t\tconsole.log(error);\n\t}\n\n\treturn new Response(html, {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"Content-Type\": \"text/html\",\n\t\t},\n\t});\n}\n\nasync function runAction(\n\tev: ServerEvent,\n\turl: URL,\n\thandler: RouteHandler,\n\tparams: Record<string, string>,\n\tquery: URLSearchParams,\n) {\n\tconst actionName = (Array.from(query.keys())[0] || \"default\").replace(/^\\//, \"\");\n\tconst serverEndPoint: PageServerEndPoint | undefined =\n\t\thandler.serverEndPoint && (await handler.serverEndPoint()).default;\n\tif (serverEndPoint?.actions) {\n\t\tconst action = serverEndPoint.actions[actionName];\n\t\tif (action) {\n\t\t\t// TODO: form.errors etc\n\t\t\tconst serverParams = buildServerParams(ev, url, params);\n\n\t\t\tif (handler.serverHook) {\n\t\t\t\tconst serverHook: ServerHook | undefined = (await handler.serverHook()).default;\n\t\t\t\tif (serverHook?.handle) {\n\t\t\t\t\tawait serverHook.handle(serverParams);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = await action(serverParams); // <--\n\n\t\t\t// If there was no response returned from the action (such as errors\n\t\t\t// or a redirect), reload the page by redirecting\n\t\t\treturn result || seeOther(url.pathname);\n\t\t}\n\t}\n\n\treturn notFound();\n}\n\nasync function loadClientAndServerData(\n\turl: URL,\n\tparams: Record<string, string>,\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, data, params);\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, string>, 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, string>,\n): ServerLoadEvent {\n\treturn {\n\t\turl,\n\t\tparams,\n\t\tappData: {},\n\t\trequest: ev.request,\n\t\tcookies: ev.cookies,\n\t\theaders: ev.headers,\n\t\tadapter: ev.adapter,\n\t};\n}\n","/**\n * Creates a response with the supplied status code and optional body.\n *\n * If the body is an object, it will be converted to JSON and the content-type\n * header set to \"application/json\". Otherwise, if the body is a string, the\n * content-type header will be set to \"text/plain\".\n */\nexport default function response(status: number, body?: object | string): Response {\n\tlet headers: Record<string, string> | undefined = undefined;\n\tif (body) {\n\t\tif (typeof body === \"object\") {\n\t\t\theaders = { \"content-type\": \"application/json\" };\n\t\t\tbody = JSON.stringify(body);\n\t\t} else {\n\t\t\theaders = { \"content-type\": \"text/plain\" };\n\t\t}\n\t}\n\treturn new Response(body, {\n\t\tstatus,\n\t\theaders,\n\t});\n}\n","import response from \"./response\";\n\n/**\n * 404 Not Found\n *\n * The server cannot find the requested resource. In the browser, this means the\n * URL is not recognized. In an API, this can also mean that the endpoint is\n * valid but the resource itself does not exist. Servers may also send this\n * response instead of 403 Forbidden to hide the existence of a resource from an\n * unauthorized client. This response code is probably the most well known due\n * to its frequent occurrence on the web.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404\n */\nexport default function notFound(body?: object | string): Response {\n\treturn response(404, body ?? \"Not found\");\n}\n","import response from \"./response\";\n\n/**\n * 200 OK\n *\n * The request succeeded. The result meaning of \"success\" depends on the HTTP\n * method:\n * - GET: The resource has been fetched and transmitted in the message body.\n * - HEAD: The representation headers are included in the response without any\n * message body.\n * - PUT or POST: The resource describing the result of the action is\n * transmitted in the message body.\n * - TRACE: The message body contains the request message as received by the\n * server.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200\n */\nexport default function ok(body?: object | string): Response {\n\treturn response(200, body);\n}\n","/**\n * Creates a redirect response with the supplied status code.\n */\nexport default function transfer(\n\tstatus: 300 | 301 | 302 | 303 | 304 | 307 | 308,\n\tlocation: string,\n): Response {\n\treturn new Response(null, {\n\t\tstatus,\n\t\theaders: {\n\t\t\tlocation,\n\t\t},\n\t});\n}\n","import transfer from \"./transfer\";\n\n/**\n * 303 See Other\n *\n * The server sent this response to direct the client to get the requested\n * resource at another URI with a GET request.\n *\n * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303\n */\nexport default function seeOther(location: string): Response {\n\treturn transfer(303, location);\n}\n","import { SerializeOptions, parse, serialize } from \"cookie\";\n\nexport default class CookieHelper {\n\trequest: Request;\n\tcookies: Map<string, string> = new Map();\n\n\tconstructor(request: Request) {\n\t\tthis.request = request;\n\t}\n\n\tget(name: string): string | undefined {\n\t\t// If it's been set, return the value from the map\n\t\tif (this.cookies.has(name)) {\n\t\t\treturn parse(this.cookies.get(name)!)[name];\n\t\t}\n\t\t// Otherwise, return the value from the `cookie` header\n\t\tconst header = this.request.headers.get(\"cookie\");\n\t\treturn parse(header ?? \"\")[name];\n\t}\n\n\tset(name: string, value: string, options?: SerializeOptions): void {\n\t\t// Set a cookie header with some default values for security\n\t\toptions ??= {};\n\t\toptions.httpOnly ??= true;\n\t\toptions.secure ??= true;\n\t\toptions.sameSite ??= \"lax\";\n\t\tthis.cookies.set(name, serialize(name, value, options));\n\t}\n\n\tdelete(name: string, options?: SerializeOptions): void {\n\t\t// Set a cookie header with an immediate expiry date\n\t\toptions ??= {};\n\t\toptions.expires = new Date(0);\n\t\tthis.cookies.set(name, serialize(name, \"\", options));\n\t}\n}\n","export default class HeaderHelper {\n\trequest: Request;\n\theaders: Map<string, string> = new Map();\n\n\tconstructor(request: Request) {\n\t\tthis.request = request;\n\t}\n\n\tget(name: string): string | undefined {\n\t\t// If it's been set, return the updated value\n\t\tif (this.headers.has(name)) {\n\t\t\treturn this.headers.get(name);\n\t\t}\n\t\t// Otherwise, return the value from the header\n\t\treturn this.request.headers.get(name) ?? undefined;\n\t}\n\n\tset(name: string, value: string): void {\n\t\tthis.headers.set(name, value);\n\t}\n\n\tdelete(name: string): void {\n\t\tthis.headers.delete(name);\n\t}\n}\n","import CookieHelper from \"./CookieHelper\";\nimport HeaderHelper from \"./HeaderHelper\";\n\nexport default class ServerEvent {\n\trequest: Request;\n\tresponse?: Response;\n\t// TODO: Should we cast types??\n\tparams?: Record<string, string>;\n\n\tadapter: any;\n\n\tcookies: CookieHelper;\n\theaders: HeaderHelper;\n\n\tconstructor(request: Request, params?: Record<string, string>) {\n\t\tthis.request = request;\n\t\tthis.params = params;\n\t\tthis.cookies = new CookieHelper(request);\n\t\tthis.headers = new HeaderHelper(request);\n\n\t\t// Adapters may have added the `adapter` property to globalThis\n\t\t// @ts-ignore\n\t\tthis.adapter = globalThis.adapter;\n\t}\n\n\taddHeaders(): void {\n\t\tif (!this.response) {\n\t\t\tthrow new Error(\"Response not created yet\");\n\t\t}\n\n\t\tfor (let cookie of this.cookies.cookies.values()) {\n\t\t\tthis.response.headers.append(\"set-cookie\", cookie);\n\t\t}\n\n\t\tfor (let header of this.headers.headers.entries()) {\n\t\t\tthis.response.headers.append(header[0], header[1]);\n\t\t}\n\t}\n}\n","export default function pathToRegex(path: string): RegExp {\n\tconst pattern =\n\t\tpath\n\t\t\t.split(\"/\")\n\t\t\t// Replace `[slug]` with a grouped match so we can pull out params\n\t\t\t.map((p) => {\n\t\t\t\treturn p.replace(/\\[([^\\/]+?)\\]/, \"(?<$1>[^\\\\/]+?)\");\n\t\t\t})\n\t\t\t.join(\"\\\\/\")\n\t\t\t// Replace globs with regex syntax\n\t\t\t.replaceAll(\"*\", \".*\") +\n\t\t// Ignore trailing slashes -- `/path` should be treated the same as `/path/`\n\t\t\"/*\";\n\treturn new RegExp(`^${pattern}/*$`);\n}\n","import type LayoutHandler from \"../types/LayoutHandler\";\nimport type ManifestRoute from \"../types/ManifestRoute\";\nimport type RouteHandler from \"../types/RouteHandler\";\nimport pathToRegex from \"./pathToRegex\";\n\n/**\n * A router that handles file routes with layouts, hooks, etc.\n */\nexport default class Router {\n\troutes: Route[] = [];\n\n\tconstructor() {}\n\n\t//add(path: string, file: string) {\n\t//\t// TODO: Call the specific addX method based on the file path\n\t//\tthis.addPage(route, file);\n\t//}\n\n\taddPages(routes: ManifestRoute[]): this {\n\t\tfor (let r of routes) {\n\t\t\tthis.routes.push(\n\t\t\t\tnew Route(r.path, {\n\t\t\t\t\tpath: r.path,\n\t\t\t\t\ttype: r.type,\n\t\t\t\t\tendPoint: r.endPoint,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tthis.#sortRoutes();\n\t\treturn this;\n\t}\n\n\t// TODO: Allow calling with the endpoint itself, so that you can setup an app with no scaffold\n\taddPage(path: string, type: number, endPoint: () => Promise<any>): this {\n\t\tthis.routes.push(\n\t\t\tnew Route(path, {\n\t\t\t\tpath,\n\t\t\t\ttype,\n\t\t\t\tendPoint,\n\t\t\t}),\n\t\t);\n\t\tthis.#sortRoutes();\n\t\treturn this;\n\t}\n\n\t/*\n\tloadPage(ev: ServerEvent, file: string): Response {\n\t\tlet page = this.pages.get(file);\n\t\tif (!page) {\n\t\t\tpage = {};\n\t\t\tthis.pages.set(file, page);\n\t\t}\n\t}\n\t*/\n\n\tmatch(path: string, query: URLSearchParams): Match | undefined {\n\t\tfor (let route of this.routes) {\n\t\t\tlet match = path.match(route.regex);\n\t\t\tif (match) {\n\t\t\t\t// Lazy load server endpoints and layouts\n\t\t\t\tif (!route.handler.loaded) {\n\t\t\t\t\tthis.#loadHandler(route.handler, route.path);\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\thandler: route.handler,\n\t\t\t\t\tparams: match.groups,\n\t\t\t\t\tquery,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tconsole.log(`not found: '${path}'`);\n\t}\n\n\t#loadHandler(handler: RouteHandler, path: string) {\n\t\thandler.layouts = this.#findLayouts(path);\n\t\thandler.serverEndPoint = this.#findServer(path);\n\t\thandler.serverHook = this.#findServerHook(path);\n\t}\n\n\t#findLayouts(path: string): LayoutHandler[] | undefined {\n\t\tlet layouts: LayoutHandler[] = [];\n\t\tlet parts = path\n\t\t\t// Strip the trailing`~/server` (so we don't look for `~/server/_layout`\n\t\t\t// which will never exist)\n\t\t\t.replace(/\\/~server$/, \"\")\n\t\t\t// The path will always start with / so splitting e.g. `/first/second`\n\t\t\t// will result in ['', 'first', 'second'] and checks for `/_layout`,\n\t\t\t// `/first/_layout` and `/second/_layout`\n\t\t\t.split(\"/\");\n\t\tlet basePath = \"\";\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tif (i > 0) {\n\t\t\t\tbasePath += \"/\" + parts[i];\n\t\t\t}\n\t\t\tconst layoutPath = basePath + \"/_layout\";\n\t\t\tconst layoutRoute = this.routes.find((r) => r.path === layoutPath);\n\t\t\tif (layoutRoute) {\n\t\t\t\tlayouts.push({\n\t\t\t\t\tpath: layoutPath,\n\t\t\t\t\tendPoint: layoutRoute.handler.endPoint,\n\t\t\t\t\tserverEndPoint: this.#findServer(layoutPath),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn layouts.length ? layouts : undefined;\n\t}\n\n\t#findServer(path: string): (() => Promise<any>) | undefined {\n\t\tconst serverPath = path.replace(/\\/$/, \"\") + \"/~server\";\n\t\tconst serverRoute = this.routes.find((r) => r.path === serverPath);\n\t\treturn serverRoute && serverRoute.handler.endPoint;\n\t}\n\n\t#findServerHook(path: string): (() => Promise<any>) | undefined {\n\t\t// TODO: Should this be a collection, like layouts?\n\t\tlet serverHookPath = \"/_hook/~server\";\n\t\t// HACK: Generalise this\n\t\tif (path.startsWith(\"/api/\")) {\n\t\t\tserverHookPath = \"/api\" + serverHookPath;\n\t\t}\n\t\tconst serverHookRoute = this.routes.find((r) => r.path === serverHookPath);\n\t\treturn serverHookRoute && serverHookRoute.handler.endPoint;\n\t}\n\n\t// HACK: We're doing this here as well as in Site, so that you can use the\n\t// Router externally without Site\n\t#sortRoutes() {\n\t\tthis.routes = this.routes.sort((a, b) => {\n\t\t\t// Sort [param]s after paths\n\t\t\t// There might be a quicker/easier way to do this\n\t\t\tfor (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {\n\t\t\t\tif (a.path[i] === \"[\" && b.path[i] !== \"[\") {\n\t\t\t\t\treturn 1;\n\t\t\t\t} else if (b.path[i] === \"[\" && a.path[i] !== \"[\") {\n\t\t\t\t\treturn -1;\n\t\t\t\t} else if (a.path[i] === b.path[i]) {\n\t\t\t\t\t// Keep going...\n\t\t\t\t} else {\n\t\t\t\t\treturn a.path[i].localeCompare(b.path[i]);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn a.path.length - b.path.length;\n\t\t});\n\t}\n}\n\nclass Route {\n\tpath: string;\n\tregex: RegExp;\n\thandler: RouteHandler;\n\n\tconstructor(path: string, handler: RouteHandler) {\n\t\tthis.path = path;\n\t\tthis.regex = pathToRegex(path);\n\t\tthis.handler = handler;\n\t}\n}\n\ntype Match = {\n\thandler: RouteHandler;\n\tparams?: Record<string, string>;\n\tquery: URLSearchParams;\n};\n","import { $watch } from \"@torpor/view\";\nimport Page from \"./Page\";\n\nconst $page: Page = $watch(\n\t{\n\t\tstatus: 404,\n\t\turl: new URL(\"http://localhost\"),\n\t\terror: {\n\t\t\tmessage: \"\",\n\t\t},\n\t},\n\t{ shallow: true },\n);\n\nexport default $page;\n","export const PAGE_ROUTE = 0;\nexport const PAGE_SERVER_ROUTE = 1;\nexport const SERVER_ROUTE = 3;\nexport const LAYOUT_ROUTE = 4;\nexport const LAYOUT_SERVER_ROUTE = 5;\nexport const HOOK_ROUTE = 6;\nexport const HOOK_SERVER_ROUTE = 7;\nexport const ERROR_ROUTE = 8;\n\nexport type RouteType =\n\t| typeof PAGE_ROUTE\n\t| typeof PAGE_SERVER_ROUTE\n\t| typeof SERVER_ROUTE\n\t| typeof LAYOUT_ROUTE\n\t| typeof LAYOUT_SERVER_ROUTE\n\t| typeof HOOK_ROUTE\n\t| typeof HOOK_SERVER_ROUTE\n\t| typeof ERROR_ROUTE\n\t| -1;\n"],"mappings":"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","names":["path","path","path"]}
package/dist/test.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as ServerEvent } from './Server-BC_dixZQ.cjs';
2
- import { S as Site } from './Site-BeCLcGj_.cjs';
2
+ import { S as Site } from './Site-BXX-S541.cjs';
3
3
  import 'cookie';
4
4
  import 'vite';
5
5
 
package/dist/test.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as ServerEvent } from './Server-BC_dixZQ.js';
2
- import { S as Site } from './Site-DgXPSQoO.js';
2
+ import { S as Site } from './Site-4tGiw2-I.js';
3
3
  import 'cookie';
4
4
  import 'vite';
5
5
 
package/dist/test.js CHANGED
@@ -7,15 +7,15 @@ import {
7
7
  PAGE_SERVER_ROUTE,
8
8
  Router,
9
9
  SERVER_ROUTE
10
- } from "./chunk-65K3R3CP.js";
10
+ } from "./chunk-PTVHEJZ5.js";
11
11
  import {
12
12
  page_default
13
- } from "./chunk-WMHM5RGZ.js";
13
+ } from "./chunk-565UOT3F.js";
14
14
  import {
15
15
  notFound,
16
16
  ok,
17
17
  seeOther
18
- } from "./chunk-2QQN3D3K.js";
18
+ } from "./chunk-WVXAN4IV.js";
19
19
  import {
20
20
  ServerEvent
21
21
  } from "./chunk-KKFND4HK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@torpor/build",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "A framework for building sites and apps with Torpor",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -55,15 +55,15 @@
55
55
  "dotenv": "^16.5.0",
56
56
  "vite": "^6.3.5",
57
57
  "vite-tsconfig-paths": "^5.1.4",
58
- "@torpor/view": "^0.1.0",
59
- "@torpor/unplugin": "^0.1.11"
58
+ "@torpor/unplugin": "^0.1.13",
59
+ "@torpor/view": "^0.1.2"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@trivago/prettier-plugin-sort-imports": "^5.2.2",
63
63
  "tsup": "^8.5.0",
64
64
  "tsx": "^4.19.4",
65
65
  "typescript": "^5.8.3",
66
- "vitest": "^3.2.0"
66
+ "vitest": "^3.2.1"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsc --noEmit && tsup",
@@ -10,5 +10,5 @@ import response from "./response";
10
10
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400
11
11
  */
12
12
  export default function badRequest(body?: object | string): Response {
13
- return response(400, body);
13
+ return response(400, body ?? "Bad request");
14
14
  }
@@ -10,5 +10,5 @@ import response from "./response";
10
10
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403
11
11
  */
12
12
  export default function forbidden(body?: object | string): Response {
13
- return response(403, body);
13
+ return response(403, body ?? "Forbidden");
14
14
  }
@@ -10,5 +10,5 @@ import response from "./response";
10
10
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405
11
11
  */
12
12
  export default function methodNotAllowed(body?: object | string): Response {
13
- return response(405, body);
13
+ return response(405, body ?? "Method not allowed");
14
14
  }
@@ -13,5 +13,5 @@ import response from "./response";
13
13
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404
14
14
  */
15
15
  export default function notFound(body?: object | string): Response {
16
- return response(404, body);
16
+ return response(404, body ?? "Not found");
17
17
  }
@@ -8,5 +8,5 @@ import response from "./response";
8
8
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500
9
9
  */
10
10
  export default function serverError(body?: object | string): Response {
11
- return response(500, body);
11
+ return response(500, body ?? "Server error");
12
12
  }
@@ -10,5 +10,5 @@ import response from "./response";
10
10
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
11
11
  */
12
12
  export default function unauthorized(body?: object | string): Response {
13
- return response(401, body);
13
+ return response(401, body ?? "Unauthorized");
14
14
  }
@@ -8,5 +8,5 @@ import response from "./response";
8
8
  * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422
9
9
  */
10
10
  export default function unprocessable(body?: object | string): Response {
11
- return response(422, body);
11
+ return response(422, body ?? "Unprocessable");
12
12
  }
package/src/site/Site.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import { promises as fs } from "node:fs";
2
- import path from "node:path";
2
+ import fpath from "node:path";
3
3
  import { type Plugin, type UserConfig } from "vite";
4
4
  import tsconfigPaths from "vite-tsconfig-paths";
5
5
  import type Adapter from "../types/Adapter";
6
6
  import {
7
+ ERROR_ROUTE,
7
8
  HOOK_ROUTE,
8
9
  HOOK_SERVER_ROUTE,
9
10
  LAYOUT_ROUTE,
10
11
  LAYOUT_SERVER_ROUTE,
11
12
  PAGE_ROUTE,
12
13
  PAGE_SERVER_ROUTE,
14
+ RouteType,
13
15
  SERVER_ROUTE,
14
16
  } from "../types/RouteType";
15
17
  import defaultAdapter from "./defaultAdapter";
@@ -25,7 +27,7 @@ import defaultAdapter from "./defaultAdapter";
25
27
  */
26
28
  export default class Site {
27
29
  root: string;
28
- routes: { path: string; file: string; type: number }[] = [];
30
+ routes: { path: string; file: string; type: RouteType }[] = [];
29
31
  // Is default plugins a bad idea?
30
32
  // HACK: Set loose because otherwise it uses a list of allowed extensions
31
33
  // TODO: Should probably just do this ourselves and let the user pass in
@@ -51,38 +53,65 @@ export default class Site {
51
53
  this.root = process.cwd();
52
54
  }
53
55
 
56
+ /**
57
+ * Adds a folder of routes, based on the names of the files in the folder
58
+ * @param folder The folder containing the routes
59
+ */
54
60
  async addRouteFolder(folder: string): Promise<void> {
55
- const routeFolder = path.join(this.root, folder);
61
+ const routeFolder = fpath.join(this.root, folder);
56
62
  const routeFiles = await fs.readdir(routeFolder, { recursive: true });
57
-
58
63
  for (let file of routeFiles) {
59
64
  // The file must start with `+` or `_` and end with `.js` or `.ts`
60
- if (/^(\+|_).+(\.js|\.ts)$/.test(path.basename(file))) {
61
- let routePath = file
62
- .replace(/^\//, "")
63
- .replace(/(\.ts|\.js)$/, "")
64
- .replace(/_hook.server$/, "_hook/~server")
65
- .replace(/_layout.server$/, "_layout/~server")
66
- .replace(/\+page.server$/, "~server")
67
- .replace(/\+server$/, "")
68
- .replace(/\+page$/, "")
69
- .replace(/\/$/, "");
70
- routePath = routePath.length > 0 ? `/${routePath}` : "/";
65
+ if (/^(\+|_).+(\.js|\.ts)$/.test(fpath.basename(file))) {
66
+ let routePath = this.#routePath(file);
71
67
  let type = this.#routeType(file);
72
- file = path.relative(this.root, path.resolve(routeFolder, file));
68
+ file = fpath.relative(this.root, fpath.resolve(routeFolder, file));
73
69
  this.routes.push({ path: routePath, file, type });
74
70
  }
75
71
  }
76
72
  this.#sortRoutes();
77
73
  }
78
74
 
79
- addRouteFile(path: string, file: string): void {
80
- let type = this.#routeType(file);
81
- this.routes.push({ path, file, type });
75
+ /**
76
+ * Adds a file route, which can have a standard name (e.g. +page.ts) or any
77
+ * name if you pass the type
78
+ * @param path The route path
79
+ * @param file The file
80
+ * @param type The file type (if the file name is non-standard)
81
+ */
82
+ addRouteFile(
83
+ path: string,
84
+ file: string,
85
+ type?:
86
+ | "+page"
87
+ | "+page.server"
88
+ | "+server"
89
+ | "_layout"
90
+ | "_layout.server"
91
+ | "_hook"
92
+ | "_hook.server"
93
+ | "_error",
94
+ ): void {
95
+ let routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));
96
+ let routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);
97
+ this.routes.push({ path: routePath, file, type: routeType });
82
98
  this.#sortRoutes();
83
99
  }
84
100
 
85
- #routeType(file: string): number {
101
+ #routePath(file: string): string {
102
+ let routePath = file
103
+ .replace(/^\//, "")
104
+ .replace(/(\.ts|\.js)$/, "")
105
+ .replace(/_hook.server$/, "_hook/~server")
106
+ .replace(/_layout.server$/, "_layout/~server")
107
+ .replace(/\+page.server$/, "~server")
108
+ .replace(/\+server$/, "")
109
+ .replace(/\+page$/, "")
110
+ .replace(/\/$/, "");
111
+ return routePath.length > 0 ? `/${routePath}` : "/";
112
+ }
113
+
114
+ #routeType(file: string): RouteType {
86
115
  let routePath = file.replace(/^\//, "").replace(/(\.ts|\.js)$/, "");
87
116
  if (routePath.endsWith("+page")) {
88
117
  return PAGE_ROUTE;
@@ -98,10 +127,43 @@ export default class Site {
98
127
  return HOOK_ROUTE;
99
128
  } else if (routePath.endsWith("_hook.server")) {
100
129
  return HOOK_SERVER_ROUTE;
130
+ } else if (routePath.endsWith("_error")) {
131
+ return ERROR_ROUTE;
101
132
  }
102
133
  return -1;
103
134
  }
104
135
 
136
+ #routeTypeFromString(
137
+ type:
138
+ | "+page"
139
+ | "+page.server"
140
+ | "+server"
141
+ | "_layout"
142
+ | "_layout.server"
143
+ | "_hook"
144
+ | "_hook.server"
145
+ | "_error",
146
+ ) {
147
+ switch (type) {
148
+ case "+page":
149
+ return PAGE_ROUTE;
150
+ case "+page.server":
151
+ return PAGE_SERVER_ROUTE;
152
+ case "+server":
153
+ return SERVER_ROUTE;
154
+ case "_layout":
155
+ return LAYOUT_ROUTE;
156
+ case "_layout.server":
157
+ return LAYOUT_SERVER_ROUTE;
158
+ case "_hook":
159
+ return HOOK_ROUTE;
160
+ case "_hook.server":
161
+ return HOOK_SERVER_ROUTE;
162
+ case "_error":
163
+ return ERROR_ROUTE;
164
+ }
165
+ }
166
+
105
167
  // TODO: You could make your site in a single file, like e.g. Hono...
106
168
  //addPage(path: string, endPoint: PageEndPoint, serverEndPoint?: PageServerEndPoint) {}
107
169
  //addEndPoint(path: string, endPoint: ServerEndPoint) {}
@@ -60,8 +60,15 @@ async function navigate(url: URL, firstTime = false): Promise<boolean> {
60
60
  }
61
61
 
62
62
  // Update $page before building the components
63
- // TODO: Find somewhere better to put this
64
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
+ // Make it look a bit classier by removing the query
68
+ window.history.replaceState({}, "", url.toString().split("?")[0]);
69
+ } else {
70
+ $page.status = 200;
71
+ }
65
72
 
66
73
  const handler = route.handler;
67
74
  const params = route.params || {};
@@ -98,7 +105,7 @@ async function navigate(url: URL, firstTime = false): Promise<boolean> {
98
105
  layoutServerEndPoint,
99
106
  );
100
107
  if (layoutResponse?.ok === false) {
101
- return layoutResponse;
108
+ return false;
102
109
  }
103
110
  }
104
111
  }
@@ -110,7 +117,7 @@ async function navigate(url: URL, firstTime = false): Promise<boolean> {
110
117
  serverEndPoint,
111
118
  );
112
119
  if (endPointResponse?.ok === false) {
113
- return endPointResponse;
120
+ return false;
114
121
  }
115
122
  let $props: Record<string, any> = { data };
116
123
 
@@ -167,7 +174,7 @@ async function loadClientAndServerData(
167
174
  params: Record<string, string>,
168
175
  clientEndPoint?: PageEndPoint,
169
176
  serverEndPoint?: PageServerEndPoint,
170
- ) {
177
+ ): Promise<Response | void> {
171
178
  if (clientEndPoint?.load) {
172
179
  const clientUrl = new URL(document.location.href);
173
180
  const clientParams = buildClientParams(clientUrl, params, data);