@torpor/build 1.2.0 → 1.3.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 (93) hide show
  1. package/dist/MiddlewareFunction-BZilnEei.d.mts +50 -0
  2. package/dist/MiddlewareFunction-BZilnEei.d.mts.map +1 -0
  3. package/dist/{Router-COdQuVUi.d.mts → Router-dAu_5PlG.d.mts} +2 -2
  4. package/dist/{Router-COdQuVUi.d.mts.map → Router-dAu_5PlG.d.mts.map} +1 -1
  5. package/dist/{Server-Dr6yqVa5.mjs → Server-CSti9cvM.mjs} +2 -2
  6. package/dist/Server-CSti9cvM.mjs.map +1 -0
  7. package/dist/{Server-Cler2w5O.d.mts → Server-qn5Ws_d0.d.mts} +3 -23
  8. package/dist/Server-qn5Ws_d0.d.mts.map +1 -0
  9. package/dist/ServerEvent-Bh9cEpro.mjs +335 -0
  10. package/dist/ServerEvent-Bh9cEpro.mjs.map +1 -0
  11. package/dist/ServerHook-BTSGfLiJ.d.mts +21 -0
  12. package/dist/ServerHook-BTSGfLiJ.d.mts.map +1 -0
  13. package/dist/{ServerHook-BlLArueW.d.mts → ServerLoadEvent-DzbxxkF8.d.mts} +13 -21
  14. package/dist/ServerLoadEvent-DzbxxkF8.d.mts.map +1 -0
  15. package/dist/SessionHelper-MaodMaLU.d.mts +148 -0
  16. package/dist/SessionHelper-MaodMaLU.d.mts.map +1 -0
  17. package/dist/{Site-CTADVh7d.d.mts → Site-CWyj0R-l.d.mts} +89 -75
  18. package/dist/Site-CWyj0R-l.d.mts.map +1 -0
  19. package/dist/{_page-4cQKs_Dn.mjs → _page-CW6SLuT4.mjs} +2 -1
  20. package/dist/{_page-4cQKs_Dn.mjs.map → _page-CW6SLuT4.mjs.map} +1 -1
  21. package/dist/basePath-DQnbHc7o.mjs +99 -0
  22. package/dist/basePath-DQnbHc7o.mjs.map +1 -0
  23. package/dist/bin/index.js +1 -1
  24. package/dist/clientEntry.mjs +16 -5
  25. package/dist/clientEntry.mjs.map +1 -1
  26. package/dist/dev.d.mts +2 -2
  27. package/dist/dev.mjs +2 -2
  28. package/dist/discovery.d.mts +43 -0
  29. package/dist/discovery.d.mts.map +1 -0
  30. package/dist/discovery.mjs +72 -0
  31. package/dist/discovery.mjs.map +1 -0
  32. package/dist/env-C2SUAwAq.mjs +49 -0
  33. package/dist/env-C2SUAwAq.mjs.map +1 -0
  34. package/dist/env.d.mts +84 -0
  35. package/dist/env.d.mts.map +1 -0
  36. package/dist/env.mjs +2 -0
  37. package/dist/form.d.mts +14 -5
  38. package/dist/form.d.mts.map +1 -1
  39. package/dist/form.mjs +14 -4
  40. package/dist/form.mjs.map +1 -1
  41. package/dist/index.d.mts +5 -3
  42. package/dist/index.d.mts.map +1 -1
  43. package/dist/index.mjs +54 -0
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/{load-C9RpS_4j.mjs → load-CydkUApM.mjs} +31 -20
  46. package/dist/load-CydkUApM.mjs.map +1 -0
  47. package/dist/nav.d.mts.map +1 -1
  48. package/dist/nav.mjs +4 -2
  49. package/dist/nav.mjs.map +1 -1
  50. package/dist/openapi.d.mts +1 -1
  51. package/dist/run.d.mts +1 -1
  52. package/dist/run.d.mts.map +1 -1
  53. package/dist/run.mjs +1 -1
  54. package/dist/{runOpenApi-yIzYasNN.mjs → runOpenApi-6iKszj_5.mjs} +230 -24
  55. package/dist/runOpenApi-6iKszj_5.mjs.map +1 -0
  56. package/dist/server/Server.d.mts +1 -1
  57. package/dist/server/Server.mjs +1 -1
  58. package/dist/server.d.mts +3 -2
  59. package/dist/server.d.mts.map +1 -1
  60. package/dist/server.mjs +2 -2
  61. package/dist/serverEntry-CnCVNNjp.mjs +13 -0
  62. package/dist/serverEntry-CnCVNNjp.mjs.map +1 -0
  63. package/dist/{serverEntry-DVv9YI3I.d.mts → serverEntry-fj0XfTs4.d.mts} +5 -4
  64. package/dist/serverEntry-fj0XfTs4.d.mts.map +1 -0
  65. package/dist/serverEntry.d.mts +2 -2
  66. package/dist/serverEntry.mjs +2 -2
  67. package/dist/{serverHandlers-fzIlmvX-.mjs → serverHandlers-B6EcNfBP.mjs} +95 -23
  68. package/dist/serverHandlers-B6EcNfBP.mjs.map +1 -0
  69. package/dist/sitemap-4imxJVrG.mjs +30 -0
  70. package/dist/sitemap-4imxJVrG.mjs.map +1 -0
  71. package/dist/sitemap-BjqN4d4W.d.mts +112 -0
  72. package/dist/sitemap-BjqN4d4W.d.mts.map +1 -0
  73. package/dist/state.d.mts +6 -0
  74. package/dist/state.d.mts.map +1 -1
  75. package/dist/state.mjs +1 -1
  76. package/dist/test.d.mts +2 -2
  77. package/dist/test.mjs +2 -2
  78. package/package.json +11 -3
  79. package/src/types/manifest.d.ts +3 -0
  80. package/dist/Server-Cler2w5O.d.mts.map +0 -1
  81. package/dist/Server-Dr6yqVa5.mjs.map +0 -1
  82. package/dist/ServerEvent-CTx3Q-hk.d.mts +0 -48
  83. package/dist/ServerEvent-CTx3Q-hk.d.mts.map +0 -1
  84. package/dist/ServerEvent-gmOvC_Yf.mjs +0 -100
  85. package/dist/ServerEvent-gmOvC_Yf.mjs.map +0 -1
  86. package/dist/ServerHook-BlLArueW.d.mts.map +0 -1
  87. package/dist/Site-CTADVh7d.d.mts.map +0 -1
  88. package/dist/load-C9RpS_4j.mjs.map +0 -1
  89. package/dist/runOpenApi-yIzYasNN.mjs.map +0 -1
  90. package/dist/serverEntry-CUXFQ07g.mjs +0 -11
  91. package/dist/serverEntry-CUXFQ07g.mjs.map +0 -1
  92. package/dist/serverEntry-DVv9YI3I.d.mts.map +0 -1
  93. package/dist/serverHandlers-fzIlmvX-.mjs.map +0 -1
@@ -0,0 +1,72 @@
1
+ import { t as sitemapResponse } from "./sitemap-4imxJVrG.mjs";
2
+ //#region src/discovery/seo.ts
3
+ /**
4
+ * Builds head element data for a page: a title, a description, and Open
5
+ * Graph / Twitter (property metas) when an image or url is included, e.g.
6
+ *
7
+ * ```ts
8
+ * import { seo } from "@torpor/build/discovery";
9
+ *
10
+ * export default {
11
+ * component,
12
+ * head: (event) => seo({
13
+ * title: post.title,
14
+ * description: post.excerpt,
15
+ * image: post.coverImage,
16
+ * url: event.url.href,
17
+ * }),
18
+ * } satisfies PageEndPoint;
19
+ * ```
20
+ *
21
+ * The values are rendered into the document head at render time (with
22
+ * `@head` markup from the component around them), overriding any static
23
+ * `<title>` in the site template. A twitter:card is included only when an
24
+ * image is set, with `og:title`/`og:description`/`og:url` set from the
25
+ * other values.
26
+ *
27
+ * @param options The values to include
28
+ * @returns The head element data
29
+ */
30
+ function seo(options) {
31
+ const head = [];
32
+ if (options.title !== void 0) {
33
+ head.push({ title: options.title });
34
+ head.push({
35
+ property: "og:title",
36
+ content: options.title
37
+ });
38
+ }
39
+ if (options.description !== void 0) {
40
+ head.push({
41
+ name: "description",
42
+ content: options.description
43
+ });
44
+ head.push({
45
+ property: "og:description",
46
+ content: options.description
47
+ });
48
+ }
49
+ if (options.image !== void 0) {
50
+ head.push({
51
+ property: "og:image",
52
+ content: options.image
53
+ });
54
+ head.push({
55
+ name: "twitter:card",
56
+ content: "summary_large_image"
57
+ });
58
+ head.push({
59
+ name: "twitter:image",
60
+ content: options.image
61
+ });
62
+ }
63
+ if (options.url !== void 0) head.push({
64
+ property: "og:url",
65
+ content: options.url
66
+ });
67
+ return head;
68
+ }
69
+ //#endregion
70
+ export { seo, sitemapResponse };
71
+
72
+ //# sourceMappingURL=discovery.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.mjs","names":[],"sources":["../src/discovery/seo.ts"],"sourcesContent":["import type { HeadElement } from \"../types/PageEndPoint\";\n\n/**\n * The elements to build head markup from: a page title, a description and\n * (for social media) an image and a canonical URL.\n */\nexport type SeoOptions = {\n\ttitle?: string;\n\tdescription?: string;\n\timage?: string;\n\turl?: string;\n};\n\n/**\n * Builds head element data for a page: a title, a description, and Open\n * Graph / Twitter (property metas) when an image or url is included, e.g.\n *\n * ```ts\n * import { seo } from \"@torpor/build/discovery\";\n *\n * export default {\n * \tcomponent,\n * \thead: (event) => seo({\n * \t\ttitle: post.title,\n * \t\tdescription: post.excerpt,\n * \t\timage: post.coverImage,\n * \t\turl: event.url.href,\n * \t}),\n * } satisfies PageEndPoint;\n * ```\n *\n * The values are rendered into the document head at render time (with\n * `@head` markup from the component around them), overriding any static\n * `<title>` in the site template. A twitter:card is included only when an\n * image is set, with `og:title`/`og:description`/`og:url` set from the\n * other values.\n *\n * @param options The values to include\n * @returns The head element data\n */\nexport default function seo(options: SeoOptions): HeadElement[] {\n\tconst head: HeadElement[] = [];\n\tif (options.title !== undefined) {\n\t\thead.push({ title: options.title });\n\t\thead.push({ property: \"og:title\", content: options.title });\n\t}\n\tif (options.description !== undefined) {\n\t\thead.push({ name: \"description\", content: options.description });\n\t\thead.push({ property: \"og:description\", content: options.description });\n\t}\n\tif (options.image !== undefined) {\n\t\thead.push({ property: \"og:image\", content: options.image });\n\t\thead.push({ name: \"twitter:card\", content: \"summary_large_image\" });\n\t\thead.push({ name: \"twitter:image\", content: options.image });\n\t}\n\tif (options.url !== undefined) {\n\t\thead.push({ property: \"og:url\", content: options.url });\n\t}\n\treturn head;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAwB,IAAI,SAAoC;CAC/D,MAAM,OAAsB,CAAC;CAC7B,IAAI,QAAQ,UAAU,KAAA,GAAW;EAChC,KAAK,KAAK,EAAE,OAAO,QAAQ,MAAM,CAAC;EAClC,KAAK,KAAK;GAAE,UAAU;GAAY,SAAS,QAAQ;EAAM,CAAC;CAC3D;CACA,IAAI,QAAQ,gBAAgB,KAAA,GAAW;EACtC,KAAK,KAAK;GAAE,MAAM;GAAe,SAAS,QAAQ;EAAY,CAAC;EAC/D,KAAK,KAAK;GAAE,UAAU;GAAkB,SAAS,QAAQ;EAAY,CAAC;CACvE;CACA,IAAI,QAAQ,UAAU,KAAA,GAAW;EAChC,KAAK,KAAK;GAAE,UAAU;GAAY,SAAS,QAAQ;EAAM,CAAC;EAC1D,KAAK,KAAK;GAAE,MAAM;GAAgB,SAAS;EAAsB,CAAC;EAClE,KAAK,KAAK;GAAE,MAAM;GAAiB,SAAS,QAAQ;EAAM,CAAC;CAC5D;CACA,IAAI,QAAQ,QAAQ,KAAA,GACnB,KAAK,KAAK;EAAE,UAAU;EAAU,SAAS,QAAQ;CAAI,CAAC;CAEvD,OAAO;AACR"}
@@ -0,0 +1,49 @@
1
+ //#region src/env.ts
2
+ let schema;
3
+ const cache = /* @__PURE__ */ new WeakMap();
4
+ /**
5
+ * Sets the schema used to validate (and parse) the environment. Called by
6
+ * the server runtime with `site.env`; not for use in app code.
7
+ */
8
+ function setEnvSchema(value) {
9
+ schema = value;
10
+ }
11
+ /**
12
+ * Returns the server environment: `process.env` on Node (with `.env` files
13
+ * loaded by the CLI in dev and preview), and the platform environment (with
14
+ * bindings, e.g. KV namespaces and D1 databases) on Cloudflare.
15
+ *
16
+ * ```ts
17
+ * import env from "@torpor/build/env";
18
+ *
19
+ * const { JWT_SECRET } = env();
20
+ * ```
21
+ *
22
+ * Server-only: call it from load functions, actions, +server endpoints and
23
+ * hooks. The client bundle never includes it, and on the server it throws
24
+ * when called outside a request.
25
+ *
26
+ * When a schema is set on `site.env`, the environment is validated on first
27
+ * use per request -- a missing or invalid key throws immediately with the
28
+ * schema's message, rather than passing `undefined` along. The schema must
29
+ * validate synchronously (zod, valibot and arktype all do).
30
+ */
31
+ function env() {
32
+ const source = globalThis.adapter?.env;
33
+ if (!source || typeof source !== "object") throw new Error("The environment is not available. env() can only be called in server code (load functions, actions, +server endpoints, hooks) while a request is being handled.");
34
+ if (!schema) return source;
35
+ const cached = cache.get(source);
36
+ if (cached) return cached;
37
+ const result = schema["~standard"].validate(source);
38
+ if (result instanceof Promise) throw new Error("The site.env schema must validate synchronously (zod, valibot and arktype all do).");
39
+ if (result.issues) {
40
+ const list = result.issues.map((issue) => issue.message).join("; ");
41
+ throw new Error(`The environment is invalid: ${list}`);
42
+ }
43
+ cache.set(source, result.value);
44
+ return result.value;
45
+ }
46
+ //#endregion
47
+ export { setEnvSchema as n, env as t };
48
+
49
+ //# sourceMappingURL=env-C2SUAwAq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-C2SUAwAq.mjs","names":[],"sources":["../src/env.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"./types/StandardSchema\";\n\ndeclare global {\n\t/**\n\t * The server environment, as provided by the runtime: `process.env` on\n\t * Node (with `.env` files loaded by the CLI in dev and preview), and the\n\t * platform environment (including bindings, e.g. KV namespaces and D1\n\t * databases) on Cloudflare.\n\t *\n\t * Declare your app's keys in an ambient `src/env.d.ts` file, e.g.\n\t *\n\t * ```ts\n\t * /// <reference types=\"@torpor/build/env\" />\n\t * declare global {\n\t * \tinterface TorporEnv {\n\t * \t\tJWT_SECRET: string;\n\t * \t\tDB: D1Database;\n\t * \t}\n\t * }\n\t * ```\n\t *\n\t * Adapter packages add their platform's keys the same way. For runtime\n\t * checking, set a Standard Schema (zod, valibot, arktype, etc) on\n\t * `site.env` in site.config.ts -- `env()` then validates on first use.\n\t */\n\tinterface TorporEnv {\n\t\t/**\n\t\t * The HMAC secret used to sign session cookies -- see\n\t\t * [Sessions](/build/sessions). Required when `event.session` is used.\n\t\t */\n\t\tTORPOR_SESSION_SECRET?: string;\n\t}\n}\n\n// The env schema rides on the Site, but route code doesn't have access to\n// the Site instance, so the server runtime hands it over at startup\nlet schema: StandardSchemaV1<unknown, TorporEnv> | undefined;\n\n// The parsed env, cached per source object: runtime environments may hand\n// each request a fresh env (e.g. Cloudflare), so a plain module-level\n// cache would go stale\nconst cache = new WeakMap<object, TorporEnv>();\n\n/**\n * Sets the schema used to validate (and parse) the environment. Called by\n * the server runtime with `site.env`; not for use in app code.\n */\nexport function setEnvSchema(value: StandardSchemaV1<unknown, TorporEnv> | undefined): void {\n\tschema = value;\n}\n\n/**\n * Returns the server environment: `process.env` on Node (with `.env` files\n * loaded by the CLI in dev and preview), and the platform environment (with\n * bindings, e.g. KV namespaces and D1 databases) on Cloudflare.\n *\n * ```ts\n * import env from \"@torpor/build/env\";\n *\n * const { JWT_SECRET } = env();\n * ```\n *\n * Server-only: call it from load functions, actions, +server endpoints and\n * hooks. The client bundle never includes it, and on the server it throws\n * when called outside a request.\n *\n * When a schema is set on `site.env`, the environment is validated on first\n * use per request -- a missing or invalid key throws immediately with the\n * schema's message, rather than passing `undefined` along. The schema must\n * validate synchronously (zod, valibot and arktype all do).\n */\nexport default function env(): TorporEnv {\n\tconst source = (globalThis as { adapter?: { env?: unknown } }).adapter?.env;\n\tif (!source || typeof source !== \"object\") {\n\t\tthrow new Error(\n\t\t\t\"The environment is not available. env() can only be called in server code \" +\n\t\t\t\t\"(load functions, actions, +server endpoints, hooks) while a request is being handled.\",\n\t\t);\n\t}\n\tif (!schema) return source as TorporEnv;\n\n\tconst cached = cache.get(source);\n\tif (cached) return cached;\n\n\tconst result = schema[\"~standard\"].validate(source);\n\tif (result instanceof Promise) {\n\t\tthrow new Error(\n\t\t\t\"The site.env schema must validate synchronously (zod, valibot and arktype all do).\",\n\t\t);\n\t}\n\tif (result.issues) {\n\t\tconst list = result.issues.map((issue) => issue.message).join(\"; \");\n\t\tthrow new Error(`The environment is invalid: ${list}`);\n\t}\n\tcache.set(source, result.value);\n\treturn result.value;\n}\n"],"mappings":";AAoCA,IAAI;AAKJ,MAAM,wBAAQ,IAAI,QAA2B;;;;;AAM7C,SAAgB,aAAa,OAA+D;CAC3F,SAAS;AACV;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAwB,MAAiB;CACxC,MAAM,SAAU,WAA+C,SAAS;CACxE,IAAI,CAAC,UAAU,OAAO,WAAW,UAChC,MAAM,IAAI,MACT,iKAED;CAED,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,SAAS,MAAM,IAAI,MAAM;CAC/B,IAAI,QAAQ,OAAO;CAEnB,MAAM,SAAS,OAAO,YAAY,CAAC,SAAS,MAAM;CAClD,IAAI,kBAAkB,SACrB,MAAM,IAAI,MACT,oFACD;CAED,IAAI,OAAO,QAAQ;EAClB,MAAM,OAAO,OAAO,OAAO,KAAK,UAAU,MAAM,OAAO,CAAC,CAAC,KAAK,IAAI;EAClE,MAAM,IAAI,MAAM,+BAA+B,MAAM;CACtD;CACA,MAAM,IAAI,QAAQ,OAAO,KAAK;CAC9B,OAAO,OAAO;AACf"}
package/dist/env.d.mts ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The server environment, as provided by the runtime: `process.env` on
3
+ * Node (with `.env` files loaded by the CLI in dev and preview), and the
4
+ * platform environment (including bindings, e.g. KV namespaces and D1
5
+ * databases) on Cloudflare.
6
+ *
7
+ * Declare your app's keys in an ambient `src/env.d.ts` file, e.g.
8
+ *
9
+ * ```ts
10
+ * /// <reference types="@torpor/build/env" />
11
+ * declare global {
12
+ * interface TorporEnv {
13
+ * JWT_SECRET: string;
14
+ * DB: D1Database;
15
+ * }
16
+ * }
17
+ * ```
18
+ *
19
+ * Adapter packages add their platform's keys the same way. For runtime
20
+ * checking, set a Standard Schema (zod, valibot, arktype, etc) on
21
+ * `site.env` in site.config.ts -- `env()` then validates on first use.
22
+ */
23
+ import { t as StandardSchemaV1 } from "./StandardSchema-D28L8_zZ.mjs";
24
+ //#region src/env.d.ts
25
+ declare global {
26
+ /**
27
+ * The server environment, as provided by the runtime: `process.env` on
28
+ * Node (with `.env` files loaded by the CLI in dev and preview), and the
29
+ * platform environment (including bindings, e.g. KV namespaces and D1
30
+ * databases) on Cloudflare.
31
+ *
32
+ * Declare your app's keys in an ambient `src/env.d.ts` file, e.g.
33
+ *
34
+ * ```ts
35
+ * /// <reference types="@torpor/build/env" />
36
+ * declare global {
37
+ * interface TorporEnv {
38
+ * JWT_SECRET: string;
39
+ * DB: D1Database;
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * Adapter packages add their platform's keys the same way. For runtime
45
+ * checking, set a Standard Schema (zod, valibot, arktype, etc) on
46
+ * `site.env` in site.config.ts -- `env()` then validates on first use.
47
+ */
48
+ interface TorporEnv {
49
+ /**
50
+ * The HMAC secret used to sign session cookies -- see
51
+ * [Sessions](/build/sessions). Required when `event.session` is used.
52
+ */
53
+ TORPOR_SESSION_SECRET?: string;
54
+ }
55
+ }
56
+ /**
57
+ * Sets the schema used to validate (and parse) the environment. Called by
58
+ * the server runtime with `site.env`; not for use in app code.
59
+ */
60
+ declare function setEnvSchema(value: StandardSchemaV1<unknown, TorporEnv> | undefined): void;
61
+ /**
62
+ * Returns the server environment: `process.env` on Node (with `.env` files
63
+ * loaded by the CLI in dev and preview), and the platform environment (with
64
+ * bindings, e.g. KV namespaces and D1 databases) on Cloudflare.
65
+ *
66
+ * ```ts
67
+ * import env from "@torpor/build/env";
68
+ *
69
+ * const { JWT_SECRET } = env();
70
+ * ```
71
+ *
72
+ * Server-only: call it from load functions, actions, +server endpoints and
73
+ * hooks. The client bundle never includes it, and on the server it throws
74
+ * when called outside a request.
75
+ *
76
+ * When a schema is set on `site.env`, the environment is validated on first
77
+ * use per request -- a missing or invalid key throws immediately with the
78
+ * schema's message, rather than passing `undefined` along. The schema must
79
+ * validate synchronously (zod, valibot and arktype all do).
80
+ */
81
+ declare function env(): TorporEnv;
82
+ //#endregion
83
+ export { env as default, setEnvSchema };
84
+ //# sourceMappingURL=env.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.mts","names":[],"sources":["../src/env.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;QAEe;;;;;;;;;;;;;;;;;;;;;;;YAuBJ;;;;;IAKT;;;;;;;iBAiBc,aAAa,OAAO,0BAA0B;;;;;;;;;;;;;;;;;;;;;iBAwBtC,OAAO"}
package/dist/env.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { n as setEnvSchema, t as env } from "./env-C2SUAwAq.mjs";
2
+ export { env as default, setEnvSchema };
package/dist/form.d.mts CHANGED
@@ -2,11 +2,13 @@
2
2
  /**
3
3
  * The values read from a form submission, coerced by the spec's default
4
4
  * value types: string defaults produce strings, number defaults produce
5
- * numbers, boolean defaults produce booleans, and array defaults produce
6
- * arrays of the element type (an empty array default produces strings).
5
+ * numbers, boolean defaults produce booleans, array defaults produce
6
+ * arrays of the element type (an empty array default produces strings),
7
+ * and the `File` class produces submitted files (a `[File]` array default
8
+ * produces an array of them).
7
9
  */
8
10
  type FormValues<Spec extends Record<string, unknown>> = { [K in keyof Spec]: FormValue<Spec[K]>; };
9
- type FormValue<T> = T extends boolean ? boolean : T extends number ? number : T extends readonly unknown[] ? [T[number]] extends [never] ? string[] : T[number][] : string;
11
+ type FormValue<T> = T extends boolean ? boolean : T extends number ? number : T extends readonly unknown[] ? [T[number]] extends [never] ? string[] : [T[number]] extends [typeof File] ? File[] : T[number][] : T extends typeof File ? File | undefined : string;
10
12
  /**
11
13
  * Reads and coerces the submitted fields of a form action, e.g.
12
14
  *
@@ -16,14 +18,21 @@ type FormValue<T> = T extends boolean ? boolean : T extends number ? number : T
16
18
  * done: false,
17
19
  * priority: 0,
18
20
  * tags: [],
21
+ * avatar: File,
22
+ * photos: [File],
19
23
  * });
20
- * // data: { title: string; done: boolean; priority: number; tags: string[] }
24
+ * // data: {
25
+ * title: string; done: boolean; priority: number; tags: string[];
26
+ * avatar: File | undefined; photos: File[]
27
+ * }
21
28
  * ```
22
29
  *
23
30
  * The spec object declares the expected fields; each key's default value
24
31
  * determines the type and the fallback when the field is missing or can't be
25
32
  * coerced. Booleans follow checkbox semantics (unchecked fields are absent,
26
- * so they come back as `false`).
33
+ * so they come back as `false`). File inputs are read as `File` values: use
34
+ * the `File` class as the default for a single file (missing when no file
35
+ * was selected), or `[File]` for every submitted file (empty when none).
27
36
  *
28
37
  * @param request The action event's request, or its FormData directly
29
38
  * @param spec An object mapping field names to default values
@@ -1 +1 @@
1
- {"version":3,"file":"form.d.mts","names":[],"sources":["../src/form/readForm.ts"],"mappings":";;;;;;;KAMY,WAAW,aAAa,8BAClC,WAAW,OAAO,UAAU,KAAK;KAG9B,UAAU,KAAK,8BAEjB,4BAEC,gCACE,wCAEA;;;;;;;;;;;;;;;;;;;;;;;iBAyBwB,SAAS,aAAa,yBACnD,SAAS,UAAU,UACnB,MAAM,OACJ,QAAQ,WAAW"}
1
+ {"version":3,"file":"form.d.mts","names":[],"sources":["../src/form/readForm.ts"],"mappings":";;;;;;;;;KAQY,WAAW,aAAa,8BAClC,WAAW,OAAO,UAAU,KAAK;KAG9B,UAAU,KAAK,8BAEjB,4BAEC,gCACE,yCAEC,2BAA2B,QAC3B,SACA,cACF,iBAAiB,OAChB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAgCwB,SAAS,aAAa,yBACnD,SAAS,UAAU,UACnB,MAAM,OACJ,QAAQ,WAAW"}
package/dist/form.mjs CHANGED
@@ -8,14 +8,21 @@
8
8
  * done: false,
9
9
  * priority: 0,
10
10
  * tags: [],
11
+ * avatar: File,
12
+ * photos: [File],
11
13
  * });
12
- * // data: { title: string; done: boolean; priority: number; tags: string[] }
14
+ * // data: {
15
+ * title: string; done: boolean; priority: number; tags: string[];
16
+ * avatar: File | undefined; photos: File[]
17
+ * }
13
18
  * ```
14
19
  *
15
20
  * The spec object declares the expected fields; each key's default value
16
21
  * determines the type and the fallback when the field is missing or can't be
17
22
  * coerced. Booleans follow checkbox semantics (unchecked fields are absent,
18
- * so they come back as `false`).
23
+ * so they come back as `false`). File inputs are read as `File` values: use
24
+ * the `File` class as the default for a single file (missing when no file
25
+ * was selected), or `[File]` for every submitted file (empty when none).
19
26
  *
20
27
  * @param request The action event's request, or its FormData directly
21
28
  * @param spec An object mapping field names to default values
@@ -26,11 +33,14 @@ async function readForm(request, spec) {
26
33
  const result = {};
27
34
  for (const key of Object.keys(spec)) {
28
35
  const defaultValue = spec[key];
29
- const values = formData.getAll(key).map((v) => v.toString());
30
- result[key] = Array.isArray(defaultValue) ? values.map((value) => coerceValue(value, defaultValue[0])) : coerceValue(values[0], defaultValue);
36
+ const values = formData.getAll(key);
37
+ result[key] = Array.isArray(defaultValue) ? defaultValue[0] === File ? values.filter((v) => v instanceof File) : values.map((value) => coerceValue(toStringValue(value), defaultValue[0])) : defaultValue === File ? values.find((v) => v instanceof File) : coerceValue(toStringValue(values[0]), defaultValue);
31
38
  }
32
39
  return result;
33
40
  }
41
+ function toStringValue(value) {
42
+ return value?.toString();
43
+ }
34
44
  function coerceValue(value, defaultValue) {
35
45
  if (typeof defaultValue === "number") {
36
46
  const n = value === void 0 || value === "" ? NaN : Number(value);
package/dist/form.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"form.mjs","names":[],"sources":["../src/form/readForm.ts"],"sourcesContent":["/**\n * The values read from a form submission, coerced by the spec's default\n * value types: string defaults produce strings, number defaults produce\n * numbers, boolean defaults produce booleans, and array defaults produce\n * arrays of the element type (an empty array default produces strings).\n */\nexport type FormValues<Spec extends Record<string, unknown>> = {\n\t[K in keyof Spec]: FormValue<Spec[K]>;\n};\n\ntype FormValue<T> = T extends boolean\n\t? boolean\n\t: T extends number\n\t\t? number\n\t\t: T extends readonly unknown[]\n\t\t\t? [T[number]] extends [never]\n\t\t\t\t? string[]\n\t\t\t\t: T[number][]\n\t\t\t: string;\n\n/**\n * Reads and coerces the submitted fields of a form action, e.g.\n *\n * ```ts\n * const data = await readForm(request, {\n * title: \"\",\n * done: false,\n * priority: 0,\n * tags: [],\n * });\n * // data: { title: string; done: boolean; priority: number; tags: string[] }\n * ```\n *\n * The spec object declares the expected fields; each key's default value\n * determines the type and the fallback when the field is missing or can't be\n * coerced. Booleans follow checkbox semantics (unchecked fields are absent,\n * so they come back as `false`).\n *\n * @param request The action event's request, or its FormData directly\n * @param spec An object mapping field names to default values\n * @returns The coerced field values\n */\nexport default async function readForm<Spec extends Record<string, unknown>>(\n\trequest: Request | FormData,\n\tspec: Spec,\n): Promise<FormValues<Spec>> {\n\tconst formData = request instanceof FormData ? request : await request.formData();\n\tconst result: Record<string, unknown> = {};\n\tfor (const key of Object.keys(spec)) {\n\t\tconst defaultValue = spec[key];\n\t\t// oxlint-disable-next-line typescript/no-base-to-string\n\t\tconst values = formData.getAll(key).map((v) => v.toString());\n\t\tresult[key] = Array.isArray(defaultValue)\n\t\t\t? values.map((value) => coerceValue(value, defaultValue[0]))\n\t\t\t: coerceValue(values[0], defaultValue);\n\t}\n\treturn result as FormValues<Spec>;\n}\n\nfunction coerceValue(value: string | undefined, defaultValue: unknown): unknown {\n\tif (typeof defaultValue === \"number\") {\n\t\tconst n = value === undefined || value === \"\" ? NaN : Number(value);\n\t\treturn Number.isNaN(n) ? defaultValue : n;\n\t}\n\tif (typeof defaultValue === \"boolean\") {\n\t\t// Unchecked checkboxes are not submitted at all\n\t\tif (value === undefined) return false;\n\t\treturn !(value === \"\" || value === \"false\" || value === \"off\");\n\t}\n\treturn value ?? defaultValue ?? \"\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA0CA,eAA8B,SAC7B,SACA,MAC4B;CAC5B,MAAM,WAAW,mBAAmB,WAAW,UAAU,MAAM,QAAQ,SAAS;CAChF,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;EACpC,MAAM,eAAe,KAAK;EAE1B,MAAM,SAAS,SAAS,OAAO,GAAG,CAAC,CAAC,KAAK,MAAM,EAAE,SAAS,CAAC;EAC3D,OAAO,OAAO,MAAM,QAAQ,YAAY,IACrC,OAAO,KAAK,UAAU,YAAY,OAAO,aAAa,EAAE,CAAC,IACzD,YAAY,OAAO,IAAI,YAAY;CACvC;CACA,OAAO;AACR;AAEA,SAAS,YAAY,OAA2B,cAAgC;CAC/E,IAAI,OAAO,iBAAiB,UAAU;EACrC,MAAM,IAAI,UAAU,KAAA,KAAa,UAAU,KAAK,MAAM,OAAO,KAAK;EAClE,OAAO,OAAO,MAAM,CAAC,IAAI,eAAe;CACzC;CACA,IAAI,OAAO,iBAAiB,WAAW;EAEtC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,OAAO,EAAE,UAAU,MAAM,UAAU,WAAW,UAAU;CACzD;CACA,OAAO,SAAS,gBAAgB;AACjC"}
1
+ {"version":3,"file":"form.mjs","names":[],"sources":["../src/form/readForm.ts"],"sourcesContent":["/**\n * The values read from a form submission, coerced by the spec's default\n * value types: string defaults produce strings, number defaults produce\n * numbers, boolean defaults produce booleans, array defaults produce\n * arrays of the element type (an empty array default produces strings),\n * and the `File` class produces submitted files (a `[File]` array default\n * produces an array of them).\n */\nexport type FormValues<Spec extends Record<string, unknown>> = {\n\t[K in keyof Spec]: FormValue<Spec[K]>;\n};\n\ntype FormValue<T> = T extends boolean\n\t? boolean\n\t: T extends number\n\t\t? number\n\t\t: T extends readonly unknown[]\n\t\t\t? [T[number]] extends [never]\n\t\t\t\t? string[]\n\t\t\t\t: [T[number]] extends [typeof File]\n\t\t\t\t\t? File[]\n\t\t\t\t\t: T[number][]\n\t\t\t: T extends typeof File\n\t\t\t\t? File | undefined\n\t\t\t\t: string;\n\n/**\n * Reads and coerces the submitted fields of a form action, e.g.\n *\n * ```ts\n * const data = await readForm(request, {\n * title: \"\",\n * done: false,\n * priority: 0,\n * tags: [],\n * avatar: File,\n * photos: [File],\n * });\n * // data: {\n * title: string; done: boolean; priority: number; tags: string[];\n * avatar: File | undefined; photos: File[]\n * }\n * ```\n *\n * The spec object declares the expected fields; each key's default value\n * determines the type and the fallback when the field is missing or can't be\n * coerced. Booleans follow checkbox semantics (unchecked fields are absent,\n * so they come back as `false`). File inputs are read as `File` values: use\n * the `File` class as the default for a single file (missing when no file\n * was selected), or `[File]` for every submitted file (empty when none).\n *\n * @param request The action event's request, or its FormData directly\n * @param spec An object mapping field names to default values\n * @returns The coerced field values\n */\nexport default async function readForm<Spec extends Record<string, unknown>>(\n\trequest: Request | FormData,\n\tspec: Spec,\n): Promise<FormValues<Spec>> {\n\tconst formData = request instanceof FormData ? request : await request.formData();\n\tconst result: Record<string, unknown> = {};\n\tfor (const key of Object.keys(spec)) {\n\t\tconst defaultValue = spec[key];\n\t\tconst values = formData.getAll(key);\n\t\tresult[key] = Array.isArray(defaultValue)\n\t\t\t? defaultValue[0] === File\n\t\t\t\t? values.filter((v) => v instanceof File)\n\t\t\t\t: values.map((value) => coerceValue(toStringValue(value), defaultValue[0]))\n\t\t\t: defaultValue === File\n\t\t\t\t? values.find((v) => v instanceof File)\n\t\t\t\t: coerceValue(toStringValue(values[0]), defaultValue);\n\t}\n\treturn result as FormValues<Spec>;\n}\n\nfunction toStringValue(value: FormDataEntryValue | undefined): string | undefined {\n\t// oxlint-disable-next-line typescript/no-base-to-string\n\treturn value?.toString();\n}\n\nfunction coerceValue(value: string | undefined, defaultValue: unknown): unknown {\n\tif (typeof defaultValue === \"number\") {\n\t\tconst n = value === undefined || value === \"\" ? NaN : Number(value);\n\t\treturn Number.isNaN(n) ? defaultValue : n;\n\t}\n\tif (typeof defaultValue === \"boolean\") {\n\t\t// Unchecked checkboxes are not submitted at all\n\t\tif (value === undefined) return false;\n\t\treturn !(value === \"\" || value === \"false\" || value === \"off\");\n\t}\n\treturn value ?? defaultValue ?? \"\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,eAA8B,SAC7B,SACA,MAC4B;CAC5B,MAAM,WAAW,mBAAmB,WAAW,UAAU,MAAM,QAAQ,SAAS;CAChF,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,OAAO,OAAO,KAAK,IAAI,GAAG;EACpC,MAAM,eAAe,KAAK;EAC1B,MAAM,SAAS,SAAS,OAAO,GAAG;EAClC,OAAO,OAAO,MAAM,QAAQ,YAAY,IACrC,aAAa,OAAO,OACnB,OAAO,QAAQ,MAAM,aAAa,IAAI,IACtC,OAAO,KAAK,UAAU,YAAY,cAAc,KAAK,GAAG,aAAa,EAAE,CAAC,IACzE,iBAAiB,OAChB,OAAO,MAAM,MAAM,aAAa,IAAI,IACpC,YAAY,cAAc,OAAO,EAAE,GAAG,YAAY;CACvD;CACA,OAAO;AACR;AAEA,SAAS,cAAc,OAA2D;CAEjF,OAAO,OAAO,SAAS;AACxB;AAEA,SAAS,YAAY,OAA2B,cAAgC;CAC/E,IAAI,OAAO,iBAAiB,UAAU;EACrC,MAAM,IAAI,UAAU,KAAA,KAAa,UAAU,KAAK,MAAM,OAAO,KAAK;EAClE,OAAO,OAAO,MAAM,CAAC,IAAI,eAAe;CACzC;CACA,IAAI,OAAO,iBAAiB,WAAW;EAEtC,IAAI,UAAU,KAAA,GAAW,OAAO;EAChC,OAAO,EAAE,UAAU,MAAM,UAAU,WAAW,UAAU;CACzD;CACA,OAAO,SAAS,gBAAgB;AACjC"}
package/dist/index.d.mts CHANGED
@@ -1,9 +1,11 @@
1
- import { t as Router } from "./Router-COdQuVUi.mjs";
1
+ import { t as Router } from "./Router-dAu_5PlG.mjs";
2
2
  import { a as RouteParamsOf, i as RouteArgsOf, n as ParseRouteParams, r as RouteArgs } from "./ParseRouteParams-DBmqlmLh.mjs";
3
- import { i as ServerLoadEvent, n as FormDataRecord, r as QueryRecord, t as ServerHook } from "./ServerHook-BlLArueW.mjs";
4
- import { _ as PageLoadEvent, a as ServerEndPoint, c as ActionBody, d as PageServerActionSchemas, f as PageServerEndPoint, g as PageLoadReturn, h as PageEndPoint, i as SchemaQuery, l as LoadParams, m as PageServerAction, n as ParamsOf, o as ServerEndPointSchemas, p as PageServerLoad, r as SchemaBody, s as ServerRequest, t as Site, u as LoadQuery, v as SitePlugin, y as Adapter } from "./Site-CTADVh7d.mjs";
3
+ import { n as QueryRecord, r as ServerLoadEvent, t as FormDataRecord } from "./ServerLoadEvent-DzbxxkF8.mjs";
4
+ import { t as ServerHook } from "./ServerHook-BTSGfLiJ.mjs";
5
+ import { a as PageLoadReturn, i as PageEndPoint, o as PageLoadEvent } from "./sitemap-BjqN4d4W.mjs";
5
6
  import { n as UntypedResponse, t as TypedResponse } from "./TypedResponse-6lznqN4U.mjs";
6
7
  import { t as StandardSchemaV1 } from "./StandardSchema-D28L8_zZ.mjs";
8
+ import { a as ServerEndPoint, c as ActionBody, d as PageServerActionSchemas, f as PageServerEndPoint, g as Adapter, h as SitePlugin, i as SchemaQuery, l as LoadParams, m as PageServerAction, n as ParamsOf, o as ServerEndPointSchemas, p as PageServerLoad, r as SchemaBody, s as ServerRequest, t as Site, u as LoadQuery } from "./Site-CWyj0R-l.mjs";
7
9
  import { t as Jsonify } from "./Jsonify-Dg6gChd3.mjs";
8
10
  //#region src/types/PageData.d.ts
9
11
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/PageData.ts","../src/types/PageForm.ts","../src/types/PageProps.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KAaY,SAAS,YAAY;EAAmB,QAAQ,qBAAqB;IAC9E,YAAY,QAAQ,iBAAiB,oBAAoB,QACxD,OACA,sBACD;;;;;;;;;;;;;;KAeS,cAAc,2BAA2B,yBAAyB,gBAAgB,QAC3F,SAAS,SAAS,cAAc;;;;KC9B9B,cAAc,cACjB,WAAW,UAAU,QAAQ,aAAY,qBAAqB,UAC5D,YAAY,QAAQ,0BAEhB;;;;;;;;;;;;;;KAeI,SAAS,YAAY;EAAmB,gBAAgB;KAChE,YAAY,4BACZ,sBACA,cAAc,YAAY,kBAAkB,oBAAoB,QAC/D,OACA,sBACF;;;;;;;UCxBsB,UAAU,OAAO,qBAAqB,OAAO;;;;EAIrE,MAAM;;;;EAIN,MAAM"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/PageData.ts","../src/types/PageForm.ts","../src/types/PageProps.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;KAaY,SAAS,YAAY;EAAmB,QAAQ,qBAAqB;IAC9E,YAAY,QAAQ,iBAAiB,oBAAoB,QACxD,OACA,sBACD;;;;;;;;;;;;;;KAeS,cAAc,2BAA2B,yBAAyB,gBAAgB,QAC3F,SAAS,SAAS,cAAc;;;;KC9B9B,cAAc,cACjB,WAAW,UAAU,QAAQ,aAAY,qBAAqB,UAC5D,YAAY,QAAQ,0BAEhB;;;;;;;;;;;;;;KAeI,SAAS,YAAY;EAAmB,gBAAgB;KAChE,YAAY,4BACZ,sBACA,cAAc,YAAY,kBAAkB,oBAAoB,QAC/D,OACA,sBACF;;;;;;;UCxBsB,UAAU,OAAO,qBAAqB,OAAO;;;;EAIrE,MAAM;;;;EAIN,MAAM"}
package/dist/index.mjs CHANGED
@@ -211,6 +211,60 @@ var Site = class {
211
211
  */
212
212
  pluginState = /* @__PURE__ */ new Map();
213
213
  /**
214
+ * The path the site is mounted under, e.g. "/app". Incoming server
215
+ * requests that don't carry it are not found; everything the framework
216
+ * generates (HTML attributes, redirect locations, paths from `route()`)
217
+ * gets it added. Server code and markup stay base-free.
218
+ */
219
+ basePath = "";
220
+ /**
221
+ * Prerendering, applied to every page route that doesn't have (or
222
+ * inherit) a `prerender` flag of its own. `true` prerenders everything;
223
+ * a `Record<string, boolean>` maps route paths -- exact (`/about`) or
224
+ * prefix (`/blog/**` for a path and everything below it) -- to flags,
225
+ * with the most specific matching key winning. Route endpoint and
226
+ * layout flags always take precedence over this.
227
+ */
228
+ prerender;
229
+ /**
230
+ * The site's absolute origin, e.g. "https://example.com". Required for
231
+ * a build-time sitemap (see `sitemap`), since there is no request to
232
+ * take the host from.
233
+ */
234
+ origin;
235
+ /**
236
+ * Global middleware, run for every request before routing -- including
237
+ * requests that won't match a route (maintenance mode, legacy url
238
+ * redirects, etc). Each middleware's `enter` runs in order and may
239
+ * return a Response to short-circuit the request; `exit` hooks run in
240
+ * reverse after the handler. Middleware see the raw request url.
241
+ *
242
+ * Route middleware (scoped to a single route) is set on the route's
243
+ * server endpoint instead, e.g. `middleware: [...]` in
244
+ * `+page.server.ts` or `+server.ts`.
245
+ */
246
+ middleware = [];
247
+ /**
248
+ * Writes a sitemap.xml listing the prerendered pages at build time
249
+ * (see `prerender`). Pass a custom path (e.g. "/blog/sitemap.xml") or
250
+ * `true` for "/sitemap.xml"; requires `origin` to be set.
251
+ *
252
+ * For a runtime sitemap -- one that reads the database and keeps up with
253
+ * content that isn't prerendered -- pass a config instead, e.g.
254
+ * `sitemap = { get: async () => ["/posts/a", "/posts/b"] }`, which serves
255
+ * the XML live at that path (customizable with `path`).
256
+ */
257
+ sitemap;
258
+ /**
259
+ * A Standard Schema (zod, valibot, arktype, etc) used to validate (and
260
+ * parse) the server environment. When set, `env()` from
261
+ * `@torpor/build/env` checks the environment on first use per request and
262
+ * throws with the schema's issues -- so a missing or invalid key fails
263
+ * immediately instead of passing `undefined` along. The schema must
264
+ * validate synchronously.
265
+ */
266
+ env;
267
+ /**
214
268
  * Inline endpoints keyed by `"path:type"`. Populated by `addRoute` when
215
269
  * the user passes an inline endpoint object instead of a file path.
216
270
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["fs","#routePath","#routeType","#sortRoutes","#routeTypeFromString","#pushRoute"],"sources":["../src/site/defaultAdapter.ts","../src/site/Site.ts"],"sourcesContent":["import type Server from \"../server/Server\";\nimport type Adapter from \"../types/Adapter\";\n\n/**\n * The known adapter packages, in preference order, with the platform env\n * markers that select them when the site is built in a deployment\n * environment. Only packages that are actually installed (somewhere in the\n * site's node_modules) can be resolved.\n */\nconst ADAPTER_PACKAGES = [\n\t{\n\t\t// The preferred fallback: a node server runs anywhere\n\t\tpkg: \"@torpor/adapter-node\",\n\t\tplatforms: [] as string[],\n\t},\n\t{\n\t\tpkg: \"@torpor/adapter-cloudflare\",\n\t\t// Set when building in a Cloudflare Pages environment. More markers\n\t\t// can be added as adapters are (e.g. `VERCEL` for an adapter-vercel)\n\t\tplatforms: [\"CF_PAGES\"],\n\t},\n];\n\n/**\n * Loads an adapter package, returning undefined when it isn't installed.\n * The import deliberately uses a variable specifier so that bundlers never\n * try to resolve adapter packages from @torpor/build.\n */\nasync function loadAdapterPackage(pkg: string): Promise<Adapter | undefined> {\n\ttry {\n\t\treturn (await import(/* @vite-ignore */ pkg)).default;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport type AdapterResolution = {\n\t/** The resolved adapter, or undefined when none could be found */\n\tadapter: Adapter | undefined;\n\t/** The package the adapter was resolved from */\n\tpkg: string | undefined;\n\t/** Why the adapter was chosen, for logging */\n\treason: string;\n};\n\nexport type ResolveAdapterOptions = {\n\t/** The environment to check for platform markers (defaults to process.env) */\n\tenv?: Record<string, string | undefined>;\n\t/** Loads an adapter package by name (defaults to a dynamic import) */\n\tload?: (pkg: string) => Promise<Adapter | undefined>;\n};\n\n/**\n * Finds an adapter to use when the site doesn't set one explicitly:\n *\n * 1. A platform build environment (e.g. `CF_PAGES` for Cloudflare Pages)\n * selects its adapter, when the adapter package is installed.\n * 2. Otherwise an installed adapter is used, preferring `adapter-node`\n * (a node server runs anywhere).\n * 3. Otherwise nothing is found, and the caller decides what to do (warn,\n * or serve on the current runtime).\n */\nexport async function resolveAdapter(\n\toptions: ResolveAdapterOptions = {},\n): Promise<AdapterResolution> {\n\tconst env = options.env ?? process.env;\n\tconst load = options.load ?? loadAdapterPackage;\n\n\t// Import each package at most once\n\tconst loaded = new Map<string, Adapter | undefined>();\n\tconst loadOnce = async (pkg: string) => {\n\t\tif (!loaded.has(pkg)) loaded.set(pkg, await load(pkg));\n\t\treturn loaded.get(pkg);\n\t};\n\n\t// 1. A deployment environment selects its adapter\n\tfor (const candidate of ADAPTER_PACKAGES) {\n\t\tfor (const marker of candidate.platforms) {\n\t\t\tif (env[marker] === undefined) continue;\n\t\t\tconst adapter = await loadOnce(candidate.pkg);\n\t\t\tif (adapter) {\n\t\t\t\treturn { adapter, pkg: candidate.pkg, reason: `${marker} detected` };\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. An installed adapter (the list is in preference order)\n\tfor (const candidate of ADAPTER_PACKAGES) {\n\t\tconst adapter = await loadOnce(candidate.pkg);\n\t\tif (adapter) {\n\t\t\treturn { adapter, pkg: candidate.pkg, reason: \"installed\" };\n\t\t}\n\t}\n\n\treturn { adapter: undefined, pkg: undefined, reason: \"none found\" };\n}\n\n/**\n * The current runtime, when preview can serve a fetch handler without an\n * adapter package. (Node's request conversion lives in adapter-node, so\n * node is deliberately not handled here.)\n */\nfunction currentRuntime(): \"bun\" | \"deno\" | undefined {\n\tconst global = globalThis as any;\n\tif (global.Bun) return \"bun\";\n\tif (global.Deno) return \"deno\";\n\treturn undefined;\n}\n\n/**\n * Serves the built output on the current runtime, for when no adapter\n * package could be found. Returns false when the runtime isn't supported.\n */\nfunction serveOnCurrentRuntime(server: Server): boolean {\n\tconst runtime = currentRuntime();\n\tif (!runtime) return false;\n\n\tconst global = globalThis as any;\n\tconst port = parseInt(process.env.PORT ?? \"7059\");\n\tconst hostname = process.env.HOST ?? \"localhost\";\n\tconsole.log(`[torpor] Serving on the ${runtime} runtime at ${hostname}:${port}`);\n\n\tif (runtime === \"bun\") {\n\t\tglobal.Bun.serve({ port, hostname, fetch: (req: Request) => server.fetch(req) });\n\t} else {\n\t\tglobal.Deno.serve({ port, hostname }, (req: Request) => server.fetch(req));\n\t}\n\treturn true;\n}\n\n// The resolution is cached per process, and the \"Using ...\" message is only\n// logged once\nlet cache: Promise<AdapterResolution> | undefined;\nlet loggedPkg: string | undefined;\n\nfunction getCachedResolution(): Promise<AdapterResolution> {\n\treturn (cache ??= resolveAdapter());\n}\n\nfunction logAdapter(pkg: string, reason: string): void {\n\tif (loggedPkg === pkg) return;\n\tloggedPkg = pkg;\n\tconsole.log(`[torpor] Using the ${pkg.replace(\"@torpor/adapter-\", \"\")} adapter (${reason})`);\n}\n\n/**\n * The adapter used when the site doesn't set one explicitly. Rather than\n * being a real adapter, it resolves one at the time it's needed (see\n * `resolveAdapter`), and as a last resort serves the built output on the\n * current runtime -- Bun and Deno can serve a fetch handler natively, while\n * node needs `@torpor/adapter-node`.\n *\n * Dev mode is not affected: with no `dev` hook, the framework's default\n * Node-runtime dev plugin is used.\n */\nconst defaultAdapter: Adapter = {\n\tasync serve(server, site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (adapter && pkg) {\n\t\t\tlogAdapter(pkg, reason);\n\t\t\tawait adapter.serve(server, site);\n\t\t\treturn;\n\t\t}\n\t\tif (serveOnCurrentRuntime(server)) return;\n\t\tconsole.log(\n\t\t\t\"[torpor] No adapter found -- install one (e.g. `pnpm add @torpor/adapter-node`) or set `site.adapter` in site.config.ts\",\n\t\t);\n\t},\n\tasync prebuild(site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (!adapter || !pkg) return;\n\t\tlogAdapter(pkg, reason);\n\t\tawait adapter.prebuild?.(site);\n\t},\n\tasync postbuild(site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (!adapter || !pkg) return;\n\t\tlogAdapter(pkg, reason);\n\t\tawait adapter.postbuild?.(site);\n\t},\n};\n\nexport default defaultAdapter;\n","import { promises as fs } from \"node:fs\";\nimport fpath from \"node:path\";\nimport { type Plugin, type UserConfig } from \"vite\";\nimport type Adapter from \"../types/Adapter\";\nimport type SitePlugin from \"../types/SitePlugin\";\nimport type { InlineEndPoint } from \"../types/Route\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport type Route from \"../types/Route\";\nimport type ServerEndPoint from \"../types/ServerEndPoint\";\nimport type ServerHook from \"../types/ServerHook\";\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\tRouteType,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport defaultAdapter from \"./defaultAdapter\";\n\n// 1. The user sets this up\n// 2. It is passed into serverEntry and clientEntry through a Vite plugin\n// (defined in manifest.ts)\n// 3. In those files, it is used to build a Router\n\n/**\n * The Site class contains the information necessary to setup a @torpor/build\n * site, including routes, plugins, and options.\n */\nexport default class Site {\n\troot: string;\n\troutes: Route[] = [];\n\t/**\n\t * Torpor site plugins, run when the site config is loaded (for dev and\n\t * build) and when the server starts up. Use these to register extra\n\t * routes (e.g. `openApi()` from `@torpor/build/openapi`) or add other\n\t * framework-level functionality\n\t */\n\tplugins: SitePlugin[] = [];\n\t/**\n\t * Vite plugins to add to the client and server builds\n\t */\n\tvitePlugins: Plugin[] = [];\n\t/**\n\t * The site's adapter, e.g. from `@torpor/adapter-node` or\n\t * `@torpor/adapter-cloudflare`. When not set, one is resolved when it's\n\t * needed: a deployment environment (e.g. `CF_PAGES`) picks the platform's\n\t * adapter when installed, an installed adapter is used otherwise, and\n\t * preview falls back to serving on the current runtime. See\n\t * defaultAdapter.ts\n\t */\n\tadapter: Adapter = defaultAdapter;\n\t/**\n\t * Extra inputs, as absolute file paths, that will be compiled with the\n\t * output. What you do with them after that is up to you. File paths ending\n\t * with server.ts/js will only be compiled with the server output\n\t */\n\tinputs: string[] = [];\n\t/**\n\t * Vite config options to merge with the standard Torpor build options.\n\t * `plugins` and `build.rollupOptions.input` will be overridden, so you\n\t * should set `Site.vitePlugins` and `Site.inputs` instead. Other options may be\n\t * overridden or incompatible, so experimentation may be required\n\t */\n\tviteConfig?: UserConfig;\n\t/**\n\t * The path to the site.config file. Set automatically by the CLI; used by\n\t * the manifest plugin to import inline endpoints for server builds.\n\t */\n\tconfigFile?: string;\n\t/**\n\t * A state map for plugins to store their configuration and state on the\n\t * site, so that framework internals (e.g. the manifest plugin) and other\n\t * plugins can read them back:\n\t *\n\t * ```ts\n\t * const myKey = Symbol.for(\"my-plugin\");\n\t * site.pluginState.set(myKey, options);\n\t * ```\n\t *\n\t * Prefer `Symbol.for(\"...\")` keys (or plain strings) over `Symbol()`,\n\t * because the user's config file and framework internals can be loaded as\n\t * separate module instances -- registry symbols are shared across\n\t * instances, `Symbol()` ones are not.\n\t */\n\tpluginState: Map<PropertyKey, unknown> = new Map();\n\t/**\n\t * Inline endpoints keyed by `\"path:type\"`. Populated by `addRoute` when\n\t * the user passes an inline endpoint object instead of a file path.\n\t */\n\tinlineEndPoints: Record<string, InlineEndPoint> = {};\n\n\tconstructor() {\n\t\tthis.root = process.cwd();\n\t}\n\n\t/**\n\t * Adds a folder of routes, based on the names of the files in the folder\n\t * @param folder The folder containing the routes\n\t * @param subFolder If a subFolder is set, all routes in the folder will be\n\t * placed under the subFolder, and only hooks, layouts etc will be used if\n\t * they have the same subFolder. This can be used to create e.g. an `api`\n\t * set of routes with their own hook for authorization from various clients\n\t */\n\tasync addRouteFolder(folder: string, subFolder?: string): Promise<void> {\n\t\tconst routeFolder = fpath.join(this.root, folder);\n\t\tconst routeFiles = await fs.readdir(routeFolder, { recursive: true });\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tfor (let file of routeFiles) {\n\t\t\t// The file must start with `+` or `_` and end with `.js` or `.ts`\n\t\t\tif (/^(\\+|_).+(\\.js|\\.ts)$/.test(fpath.basename(file))) {\n\t\t\t\tlet routePath = this.#routePath(file);\n\t\t\t\tif (subFolder !== undefined) {\n\t\t\t\t\troutePath = subFolder + routePath;\n\t\t\t\t}\n\t\t\t\tlet type = this.#routeType(file);\n\t\t\t\tfile = fpath.relative(this.root, fpath.resolve(routeFolder, file));\n\t\t\t\tthis.routes.push({\n\t\t\t\t\tpath: routePath,\n\t\t\t\t\tfile,\n\t\t\t\t\ttype,\n\t\t\t\t\tsubFolder: subFolder,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Adds a file route, which can have a standard name (e.g. +page.ts) or any\n\t * name if you pass the type\n\t * @param path The route path\n\t * @param file The file\n\t * @param type The file type (if the file name is non-standard)\n\t */\n\taddRouteFile(\n\t\tpath: string,\n\t\tfile: string,\n\t\ttype?:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));\n\t\tlet routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tthis.routes.push({\n\t\t\tpath: routePath,\n\t\t\tfile,\n\t\t\ttype: routeType,\n\t\t\tsubFolder: subFolder,\n\t\t});\n\t\tthis.#sortRoutes();\n\t}\n\n\t#routePath(file: string): string {\n\t\tlet routePath = file\n\t\t\t.replace(/^\\//, \"\")\n\t\t\t.replace(/(\\.ts|\\.js)$/, \"\")\n\t\t\t.replace(/_hook.server$/, \"_hook/~server\")\n\t\t\t.replace(/_layout.server$/, \"_layout/~server\")\n\t\t\t.replace(/\\+page.server$/, \"~server\")\n\t\t\t.replace(/\\+server$/, \"\")\n\t\t\t.replace(/\\+page$/, \"\")\n\t\t\t.replace(/\\/$/, \"\");\n\t\treturn routePath.length > 0 ? `/${routePath}` : \"/\";\n\t}\n\n\t#routeType(file: string): RouteType {\n\t\tlet routePath = file.replace(/^\\//, \"\").replace(/(\\.ts|\\.js)$/, \"\");\n\t\tif (routePath.endsWith(\"+page\")) {\n\t\t\treturn PAGE_ROUTE;\n\t\t} else if (routePath.endsWith(\"+page.server\")) {\n\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"+server\")) {\n\t\t\treturn SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout\")) {\n\t\t\treturn LAYOUT_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout.server\")) {\n\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook\")) {\n\t\t\treturn HOOK_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook.server\")) {\n\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_error\")) {\n\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t#routeTypeFromString(\n\t\ttype:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t) {\n\t\tswitch (type) {\n\t\t\tcase \"+page\":\n\t\t\t\treturn PAGE_ROUTE;\n\t\t\tcase \"+page.server\":\n\t\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t\tcase \"+server\":\n\t\t\t\treturn SERVER_ROUTE;\n\t\t\tcase \"_layout\":\n\t\t\t\treturn LAYOUT_ROUTE;\n\t\t\tcase \"_layout.server\":\n\t\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t\tcase \"_hook\":\n\t\t\t\treturn HOOK_ROUTE;\n\t\t\tcase \"_hook.server\":\n\t\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t\tcase \"_error\":\n\t\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t}\n\n\t/**\n\t * Adds a route in code, without needing the standard file naming\n\t * conventions (+page.ts, +page.server.ts, etc). This is useful for small\n\t * sites with one or two pages.\n\t *\n\t * Any combination of options may be provided; each maps to the\n\t * corresponding file-route type:\n\t *\n\t * - `page` — a `+page` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `pageServer` — a `+page.server` (`PageServerEndPoint`, file or inline)\n\t * - `server` — a `+server` HTTP endpoint (`ServerEndPoint`, file or inline)\n\t * - `layout` — a `_layout` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `layoutServer` — a `_layout.server` (`PageServerEndPoint`, file or inline)\n\t * - `hookServer` — a `_hook.server` (`ServerHook`, file or inline)\n\t * - `error` — an `_error` page (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t *\n\t * Options that need a component (`page`, `layout`, `error`) must be file\n\t * paths so the component is included in the client bundle. Server-only\n\t * options (`pageServer`, `server`, `layoutServer`, `hookServer`) accept\n\t * either a file path or an inline object; inline server code is kept out\n\t * of the client bundle automatically.\n\t *\n\t * @example\n\t * // A page with an inline server action\n\t * site.addRoute(\"/\", {\n\t * page: \"./src/Counter.torp\",\n\t * pageServer: { actions: { set: async ({ request }) => ok() } },\n\t * });\n\t * @example\n\t * // A JSON API endpoint\n\t * site.addRoute(\"/api/time\", {\n\t * server: { get: async () => ok({ time: Date.now() }) },\n\t * });\n\t * @example\n\t * // A root layout, server hook, and error page\n\t * site.addRoute(\"/\", {\n\t * layout: \"./src/Layout.torp\",\n\t * hookServer: { enter: async (event) => {} },\n\t * error: \"./src/ErrorPage.torp\",\n\t * });\n\t *\n\t * @param path The route path, e.g. `/` or `/about`\n\t * @param options File paths and/or inline endpoints for the route\n\t * @param subFolder An optional subFolder for grouping routes\n\t */\n\taddRoute(\n\t\tpath: string,\n\t\toptions: {\n\t\t\tpage?: string;\n\t\t\tpageServer?: string | PageServerEndPoint;\n\t\t\tserver?: string | ServerEndPoint;\n\t\t\tlayout?: string;\n\t\t\tlayoutServer?: string | PageServerEndPoint;\n\t\t\thookServer?: string | ServerHook;\n\t\t\terror?: string;\n\t\t},\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet normalizedSubFolder = subFolder;\n\t\tif (normalizedSubFolder !== undefined && !normalizedSubFolder.startsWith(\"/\")) {\n\t\t\tnormalizedSubFolder = \"/\" + normalizedSubFolder;\n\t\t}\n\n\t\t// The base path with any trailing slash stripped, so that we can\n\t\t// append \"/_layout\", \"/~server\" etc. For the root \"/\" this is \"\".\n\t\tlet base = path.replace(/\\/$/, \"\");\n\n\t\tif (options.page !== undefined) {\n\t\t\tthis.#pushRoute(path, PAGE_ROUTE, options.page, normalizedSubFolder);\n\t\t}\n\t\tif (options.pageServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/~server`,\n\t\t\t\tPAGE_SERVER_ROUTE,\n\t\t\t\toptions.pageServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.server !== undefined) {\n\t\t\tthis.#pushRoute(path, SERVER_ROUTE, options.server, normalizedSubFolder);\n\t\t}\n\t\tif (options.layout !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_layout`, LAYOUT_ROUTE, options.layout, normalizedSubFolder);\n\t\t}\n\t\tif (options.layoutServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_layout/~server`,\n\t\t\t\tLAYOUT_SERVER_ROUTE,\n\t\t\t\toptions.layoutServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.hookServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_hook/~server`,\n\t\t\t\tHOOK_SERVER_ROUTE,\n\t\t\t\toptions.hookServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.error !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_error`, ERROR_ROUTE, options.error, normalizedSubFolder);\n\t\t}\n\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Pushes a single route entry, handling both file-path and inline-endpoint\n\t * targets. File paths are resolved relative to the site root; inline\n\t * endpoints are also stored in `inlineEndPoints` keyed by `\"path:type\"`\n\t * so the manifest plugin can resolve them at build time.\n\t *\n\t * If a route with the same path and type was already added, it is\n\t * replaced, so that running a plugin twice (config load and server\n\t * startup) doesn't add duplicate routes.\n\t */\n\t#pushRoute(\n\t\troutePath: string,\n\t\ttype: RouteType,\n\t\ttarget: string | InlineEndPoint,\n\t\tsubFolder?: string,\n\t): void {\n\t\tconst index = this.routes.findIndex((r) => r.path === routePath && r.type === type);\n\t\tif (typeof target === \"string\") {\n\t\t\tlet file = fpath.relative(this.root, fpath.resolve(this.root, target));\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, file, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, file, type, subFolder });\n\t\t\t}\n\t\t} else {\n\t\t\tlet key = `${routePath}:${type}`;\n\t\t\tthis.inlineEndPoints[key] = target;\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, endPoint: target, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, endPoint: target, type, subFolder });\n\t\t\t}\n\t\t}\n\t}\n\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"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["fs","#routePath","#routeType","#sortRoutes","#routeTypeFromString","#pushRoute"],"sources":["../src/site/defaultAdapter.ts","../src/site/Site.ts"],"sourcesContent":["import type Server from \"../server/Server\";\nimport type Adapter from \"../types/Adapter\";\n\n/**\n * The known adapter packages, in preference order, with the platform env\n * markers that select them when the site is built in a deployment\n * environment. Only packages that are actually installed (somewhere in the\n * site's node_modules) can be resolved.\n */\nconst ADAPTER_PACKAGES = [\n\t{\n\t\t// The preferred fallback: a node server runs anywhere\n\t\tpkg: \"@torpor/adapter-node\",\n\t\tplatforms: [] as string[],\n\t},\n\t{\n\t\tpkg: \"@torpor/adapter-cloudflare\",\n\t\t// Set when building in a Cloudflare Pages environment. More markers\n\t\t// can be added as adapters are (e.g. `VERCEL` for an adapter-vercel)\n\t\tplatforms: [\"CF_PAGES\"],\n\t},\n];\n\n/**\n * Loads an adapter package, returning undefined when it isn't installed.\n * The import deliberately uses a variable specifier so that bundlers never\n * try to resolve adapter packages from @torpor/build.\n */\nasync function loadAdapterPackage(pkg: string): Promise<Adapter | undefined> {\n\ttry {\n\t\treturn (await import(/* @vite-ignore */ pkg)).default;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport type AdapterResolution = {\n\t/** The resolved adapter, or undefined when none could be found */\n\tadapter: Adapter | undefined;\n\t/** The package the adapter was resolved from */\n\tpkg: string | undefined;\n\t/** Why the adapter was chosen, for logging */\n\treason: string;\n};\n\nexport type ResolveAdapterOptions = {\n\t/** The environment to check for platform markers (defaults to process.env) */\n\tenv?: Record<string, string | undefined>;\n\t/** Loads an adapter package by name (defaults to a dynamic import) */\n\tload?: (pkg: string) => Promise<Adapter | undefined>;\n};\n\n/**\n * Finds an adapter to use when the site doesn't set one explicitly:\n *\n * 1. A platform build environment (e.g. `CF_PAGES` for Cloudflare Pages)\n * selects its adapter, when the adapter package is installed.\n * 2. Otherwise an installed adapter is used, preferring `adapter-node`\n * (a node server runs anywhere).\n * 3. Otherwise nothing is found, and the caller decides what to do (warn,\n * or serve on the current runtime).\n */\nexport async function resolveAdapter(\n\toptions: ResolveAdapterOptions = {},\n): Promise<AdapterResolution> {\n\tconst env = options.env ?? process.env;\n\tconst load = options.load ?? loadAdapterPackage;\n\n\t// Import each package at most once\n\tconst loaded = new Map<string, Adapter | undefined>();\n\tconst loadOnce = async (pkg: string) => {\n\t\tif (!loaded.has(pkg)) loaded.set(pkg, await load(pkg));\n\t\treturn loaded.get(pkg);\n\t};\n\n\t// 1. A deployment environment selects its adapter\n\tfor (const candidate of ADAPTER_PACKAGES) {\n\t\tfor (const marker of candidate.platforms) {\n\t\t\tif (env[marker] === undefined) continue;\n\t\t\tconst adapter = await loadOnce(candidate.pkg);\n\t\t\tif (adapter) {\n\t\t\t\treturn { adapter, pkg: candidate.pkg, reason: `${marker} detected` };\n\t\t\t}\n\t\t}\n\t}\n\n\t// 2. An installed adapter (the list is in preference order)\n\tfor (const candidate of ADAPTER_PACKAGES) {\n\t\tconst adapter = await loadOnce(candidate.pkg);\n\t\tif (adapter) {\n\t\t\treturn { adapter, pkg: candidate.pkg, reason: \"installed\" };\n\t\t}\n\t}\n\n\treturn { adapter: undefined, pkg: undefined, reason: \"none found\" };\n}\n\n/**\n * The current runtime, when preview can serve a fetch handler without an\n * adapter package. (Node's request conversion lives in adapter-node, so\n * node is deliberately not handled here.)\n */\nfunction currentRuntime(): \"bun\" | \"deno\" | undefined {\n\tconst global = globalThis as any;\n\tif (global.Bun) return \"bun\";\n\tif (global.Deno) return \"deno\";\n\treturn undefined;\n}\n\n/**\n * Serves the built output on the current runtime, for when no adapter\n * package could be found. Returns false when the runtime isn't supported.\n */\nfunction serveOnCurrentRuntime(server: Server): boolean {\n\tconst runtime = currentRuntime();\n\tif (!runtime) return false;\n\n\tconst global = globalThis as any;\n\tconst port = parseInt(process.env.PORT ?? \"7059\");\n\tconst hostname = process.env.HOST ?? \"localhost\";\n\tconsole.log(`[torpor] Serving on the ${runtime} runtime at ${hostname}:${port}`);\n\n\tif (runtime === \"bun\") {\n\t\tglobal.Bun.serve({ port, hostname, fetch: (req: Request) => server.fetch(req) });\n\t} else {\n\t\tglobal.Deno.serve({ port, hostname }, (req: Request) => server.fetch(req));\n\t}\n\treturn true;\n}\n\n// The resolution is cached per process, and the \"Using ...\" message is only\n// logged once\nlet cache: Promise<AdapterResolution> | undefined;\nlet loggedPkg: string | undefined;\n\nfunction getCachedResolution(): Promise<AdapterResolution> {\n\treturn (cache ??= resolveAdapter());\n}\n\nfunction logAdapter(pkg: string, reason: string): void {\n\tif (loggedPkg === pkg) return;\n\tloggedPkg = pkg;\n\tconsole.log(`[torpor] Using the ${pkg.replace(\"@torpor/adapter-\", \"\")} adapter (${reason})`);\n}\n\n/**\n * The adapter used when the site doesn't set one explicitly. Rather than\n * being a real adapter, it resolves one at the time it's needed (see\n * `resolveAdapter`), and as a last resort serves the built output on the\n * current runtime -- Bun and Deno can serve a fetch handler natively, while\n * node needs `@torpor/adapter-node`.\n *\n * Dev mode is not affected: with no `dev` hook, the framework's default\n * Node-runtime dev plugin is used.\n */\nconst defaultAdapter: Adapter = {\n\tasync serve(server, site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (adapter && pkg) {\n\t\t\tlogAdapter(pkg, reason);\n\t\t\tawait adapter.serve(server, site);\n\t\t\treturn;\n\t\t}\n\t\tif (serveOnCurrentRuntime(server)) return;\n\t\tconsole.log(\n\t\t\t\"[torpor] No adapter found -- install one (e.g. `pnpm add @torpor/adapter-node`) or set `site.adapter` in site.config.ts\",\n\t\t);\n\t},\n\tasync prebuild(site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (!adapter || !pkg) return;\n\t\tlogAdapter(pkg, reason);\n\t\tawait adapter.prebuild?.(site);\n\t},\n\tasync postbuild(site) {\n\t\tconst { adapter, pkg, reason } = await getCachedResolution();\n\t\tif (!adapter || !pkg) return;\n\t\tlogAdapter(pkg, reason);\n\t\tawait adapter.postbuild?.(site);\n\t},\n};\n\nexport default defaultAdapter;\n","import { promises as fs } from \"node:fs\";\nimport fpath from \"node:path\";\nimport { type Plugin, type UserConfig } from \"vite\";\nimport type Adapter from \"../types/Adapter\";\nimport type { SitemapOptions } from \"../discovery/sitemap\";\nimport type MiddlewareFunction from \"../server/types/MiddlewareFunction\";\nimport type { StandardSchemaV1 } from \"../types/StandardSchema\";\nimport type SitePlugin from \"../types/SitePlugin\";\nimport type { InlineEndPoint } from \"../types/Route\";\nimport type PageServerEndPoint from \"../types/PageServerEndPoint\";\nimport type Route from \"../types/Route\";\nimport type ServerEndPoint from \"../types/ServerEndPoint\";\nimport type ServerHook from \"../types/ServerHook\";\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\tRouteType,\n\tSERVER_ROUTE,\n} from \"../types/RouteType\";\nimport defaultAdapter from \"./defaultAdapter\";\n\n// 1. The user sets this up\n// 2. It is passed into serverEntry and clientEntry through a Vite plugin\n// (defined in manifest.ts)\n// 3. In those files, it is used to build a Router\n\n/**\n * The Site class contains the information necessary to setup a @torpor/build\n * site, including routes, plugins, and options.\n */\nexport default class Site {\n\troot: string;\n\troutes: Route[] = [];\n\t/**\n\t * Torpor site plugins, run when the site config is loaded (for dev and\n\t * build) and when the server starts up. Use these to register extra\n\t * routes (e.g. `openApi()` from `@torpor/build/openapi`) or add other\n\t * framework-level functionality\n\t */\n\tplugins: SitePlugin[] = [];\n\t/**\n\t * Vite plugins to add to the client and server builds\n\t */\n\tvitePlugins: Plugin[] = [];\n\t/**\n\t * The site's adapter, e.g. from `@torpor/adapter-node` or\n\t * `@torpor/adapter-cloudflare`. When not set, one is resolved when it's\n\t * needed: a deployment environment (e.g. `CF_PAGES`) picks the platform's\n\t * adapter when installed, an installed adapter is used otherwise, and\n\t * preview falls back to serving on the current runtime. See\n\t * defaultAdapter.ts\n\t */\n\tadapter: Adapter = defaultAdapter;\n\t/**\n\t * Extra inputs, as absolute file paths, that will be compiled with the\n\t * output. What you do with them after that is up to you. File paths ending\n\t * with server.ts/js will only be compiled with the server output\n\t */\n\tinputs: string[] = [];\n\t/**\n\t * Vite config options to merge with the standard Torpor build options.\n\t * `plugins` and `build.rollupOptions.input` will be overridden, so you\n\t * should set `Site.vitePlugins` and `Site.inputs` instead. Other options may be\n\t * overridden or incompatible, so experimentation may be required\n\t */\n\tviteConfig?: UserConfig;\n\t/**\n\t * The path to the site.config file. Set automatically by the CLI; used by\n\t * the manifest plugin to import inline endpoints for server builds.\n\t */\n\tconfigFile?: string;\n\t/**\n\t * A state map for plugins to store their configuration and state on the\n\t * site, so that framework internals (e.g. the manifest plugin) and other\n\t * plugins can read them back:\n\t *\n\t * ```ts\n\t * const myKey = Symbol.for(\"my-plugin\");\n\t * site.pluginState.set(myKey, options);\n\t * ```\n\t *\n\t * Prefer `Symbol.for(\"...\")` keys (or plain strings) over `Symbol()`,\n\t * because the user's config file and framework internals can be loaded as\n\t * separate module instances -- registry symbols are shared across\n\t * instances, `Symbol()` ones are not.\n\t */\n\tpluginState: Map<PropertyKey, unknown> = new Map();\n\t/**\n\t * The path the site is mounted under, e.g. \"/app\". Incoming server\n\t * requests that don't carry it are not found; everything the framework\n\t * generates (HTML attributes, redirect locations, paths from `route()`)\n\t * gets it added. Server code and markup stay base-free.\n\t */\n\tbasePath: string = \"\";\n\t/**\n\t * Prerendering, applied to every page route that doesn't have (or\n\t * inherit) a `prerender` flag of its own. `true` prerenders everything;\n\t * a `Record<string, boolean>` maps route paths -- exact (`/about`) or\n\t * prefix (`/blog/**` for a path and everything below it) -- to flags,\n\t * with the most specific matching key winning. Route endpoint and\n\t * layout flags always take precedence over this.\n\t */\n\tprerender?: boolean | Record<string, boolean>;\n\t/**\n\t * The site's absolute origin, e.g. \"https://example.com\". Required for\n\t * a build-time sitemap (see `sitemap`), since there is no request to\n\t * take the host from.\n\t */\n\torigin?: string;\n\t/**\n\t * Global middleware, run for every request before routing -- including\n\t * requests that won't match a route (maintenance mode, legacy url\n\t * redirects, etc). Each middleware's `enter` runs in order and may\n\t * return a Response to short-circuit the request; `exit` hooks run in\n\t * reverse after the handler. Middleware see the raw request url.\n\t *\n\t * Route middleware (scoped to a single route) is set on the route's\n\t * server endpoint instead, e.g. `middleware: [...]` in\n\t * `+page.server.ts` or `+server.ts`.\n\t */\n\tmiddleware: MiddlewareFunction[] = [];\n\t/**\n\t * Writes a sitemap.xml listing the prerendered pages at build time\n\t * (see `prerender`). Pass a custom path (e.g. \"/blog/sitemap.xml\") or\n\t * `true` for \"/sitemap.xml\"; requires `origin` to be set.\n\t *\n\t * For a runtime sitemap -- one that reads the database and keeps up with\n\t * content that isn't prerendered -- pass a config instead, e.g.\n\t * `sitemap = { get: async () => [\"/posts/a\", \"/posts/b\"] }`, which serves\n\t * the XML live at that path (customizable with `path`).\n\t */\n\tsitemap?: SitemapOptions | boolean | string;\n\t/**\n\t * A Standard Schema (zod, valibot, arktype, etc) used to validate (and\n\t * parse) the server environment. When set, `env()` from\n\t * `@torpor/build/env` checks the environment on first use per request and\n\t * throws with the schema's issues -- so a missing or invalid key fails\n\t * immediately instead of passing `undefined` along. The schema must\n\t * validate synchronously.\n\t */\n\tenv?: StandardSchemaV1<unknown, TorporEnv>;\n\t/**\n\t * Inline endpoints keyed by `\"path:type\"`. Populated by `addRoute` when\n\t * the user passes an inline endpoint object instead of a file path.\n\t */\n\tinlineEndPoints: Record<string, InlineEndPoint> = {};\n\n\tconstructor() {\n\t\tthis.root = process.cwd();\n\t}\n\n\t/**\n\t * Adds a folder of routes, based on the names of the files in the folder\n\t * @param folder The folder containing the routes\n\t * @param subFolder If a subFolder is set, all routes in the folder will be\n\t * placed under the subFolder, and only hooks, layouts etc will be used if\n\t * they have the same subFolder. This can be used to create e.g. an `api`\n\t * set of routes with their own hook for authorization from various clients\n\t */\n\tasync addRouteFolder(folder: string, subFolder?: string): Promise<void> {\n\t\tconst routeFolder = fpath.join(this.root, folder);\n\t\tconst routeFiles = await fs.readdir(routeFolder, { recursive: true });\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tfor (let file of routeFiles) {\n\t\t\t// The file must start with `+` or `_` and end with `.js` or `.ts`\n\t\t\tif (/^(\\+|_).+(\\.js|\\.ts)$/.test(fpath.basename(file))) {\n\t\t\t\tlet routePath = this.#routePath(file);\n\t\t\t\tif (subFolder !== undefined) {\n\t\t\t\t\troutePath = subFolder + routePath;\n\t\t\t\t}\n\t\t\t\tlet type = this.#routeType(file);\n\t\t\t\tfile = fpath.relative(this.root, fpath.resolve(routeFolder, file));\n\t\t\t\tthis.routes.push({\n\t\t\t\t\tpath: routePath,\n\t\t\t\t\tfile,\n\t\t\t\t\ttype,\n\t\t\t\t\tsubFolder: subFolder,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Adds a file route, which can have a standard name (e.g. +page.ts) or any\n\t * name if you pass the type\n\t * @param path The route path\n\t * @param file The file\n\t * @param type The file type (if the file name is non-standard)\n\t */\n\taddRouteFile(\n\t\tpath: string,\n\t\tfile: string,\n\t\ttype?:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet routePath = this.#routePath(fpath.join(path, type ?? fpath.basename(file)));\n\t\tlet routeType = type ? this.#routeTypeFromString(type) : this.#routeType(file);\n\t\tif (subFolder !== undefined && !subFolder.startsWith(\"/\")) {\n\t\t\tsubFolder = \"/\" + subFolder;\n\t\t}\n\t\tthis.routes.push({\n\t\t\tpath: routePath,\n\t\t\tfile,\n\t\t\ttype: routeType,\n\t\t\tsubFolder: subFolder,\n\t\t});\n\t\tthis.#sortRoutes();\n\t}\n\n\t#routePath(file: string): string {\n\t\tlet routePath = file\n\t\t\t.replace(/^\\//, \"\")\n\t\t\t.replace(/(\\.ts|\\.js)$/, \"\")\n\t\t\t.replace(/_hook.server$/, \"_hook/~server\")\n\t\t\t.replace(/_layout.server$/, \"_layout/~server\")\n\t\t\t.replace(/\\+page.server$/, \"~server\")\n\t\t\t.replace(/\\+server$/, \"\")\n\t\t\t.replace(/\\+page$/, \"\")\n\t\t\t.replace(/\\/$/, \"\");\n\t\treturn routePath.length > 0 ? `/${routePath}` : \"/\";\n\t}\n\n\t#routeType(file: string): RouteType {\n\t\tlet routePath = file.replace(/^\\//, \"\").replace(/(\\.ts|\\.js)$/, \"\");\n\t\tif (routePath.endsWith(\"+page\")) {\n\t\t\treturn PAGE_ROUTE;\n\t\t} else if (routePath.endsWith(\"+page.server\")) {\n\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"+server\")) {\n\t\t\treturn SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout\")) {\n\t\t\treturn LAYOUT_ROUTE;\n\t\t} else if (routePath.endsWith(\"_layout.server\")) {\n\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook\")) {\n\t\t\treturn HOOK_ROUTE;\n\t\t} else if (routePath.endsWith(\"_hook.server\")) {\n\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t} else if (routePath.endsWith(\"_error\")) {\n\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t\treturn -1;\n\t}\n\n\t#routeTypeFromString(\n\t\ttype:\n\t\t\t| \"+page\"\n\t\t\t| \"+page.server\"\n\t\t\t| \"+server\"\n\t\t\t| \"_layout\"\n\t\t\t| \"_layout.server\"\n\t\t\t| \"_hook\"\n\t\t\t| \"_hook.server\"\n\t\t\t| \"_error\",\n\t) {\n\t\tswitch (type) {\n\t\t\tcase \"+page\":\n\t\t\t\treturn PAGE_ROUTE;\n\t\t\tcase \"+page.server\":\n\t\t\t\treturn PAGE_SERVER_ROUTE;\n\t\t\tcase \"+server\":\n\t\t\t\treturn SERVER_ROUTE;\n\t\t\tcase \"_layout\":\n\t\t\t\treturn LAYOUT_ROUTE;\n\t\t\tcase \"_layout.server\":\n\t\t\t\treturn LAYOUT_SERVER_ROUTE;\n\t\t\tcase \"_hook\":\n\t\t\t\treturn HOOK_ROUTE;\n\t\t\tcase \"_hook.server\":\n\t\t\t\treturn HOOK_SERVER_ROUTE;\n\t\t\tcase \"_error\":\n\t\t\t\treturn ERROR_ROUTE;\n\t\t}\n\t}\n\n\t/**\n\t * Adds a route in code, without needing the standard file naming\n\t * conventions (+page.ts, +page.server.ts, etc). This is useful for small\n\t * sites with one or two pages.\n\t *\n\t * Any combination of options may be provided; each maps to the\n\t * corresponding file-route type:\n\t *\n\t * - `page` — a `+page` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `pageServer` — a `+page.server` (`PageServerEndPoint`, file or inline)\n\t * - `server` — a `+server` HTTP endpoint (`ServerEndPoint`, file or inline)\n\t * - `layout` — a `_layout` (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t * - `layoutServer` — a `_layout.server` (`PageServerEndPoint`, file or inline)\n\t * - `hookServer` — a `_hook.server` (`ServerHook`, file or inline)\n\t * - `error` — an `_error` page (`.torp` component or `.ts`/`.js` `PageEndPoint`)\n\t *\n\t * Options that need a component (`page`, `layout`, `error`) must be file\n\t * paths so the component is included in the client bundle. Server-only\n\t * options (`pageServer`, `server`, `layoutServer`, `hookServer`) accept\n\t * either a file path or an inline object; inline server code is kept out\n\t * of the client bundle automatically.\n\t *\n\t * @example\n\t * // A page with an inline server action\n\t * site.addRoute(\"/\", {\n\t * page: \"./src/Counter.torp\",\n\t * pageServer: { actions: { set: async ({ request }) => ok() } },\n\t * });\n\t * @example\n\t * // A JSON API endpoint\n\t * site.addRoute(\"/api/time\", {\n\t * server: { get: async () => ok({ time: Date.now() }) },\n\t * });\n\t * @example\n\t * // A root layout, server hook, and error page\n\t * site.addRoute(\"/\", {\n\t * layout: \"./src/Layout.torp\",\n\t * hookServer: { enter: async (event) => {} },\n\t * error: \"./src/ErrorPage.torp\",\n\t * });\n\t *\n\t * @param path The route path, e.g. `/` or `/about`\n\t * @param options File paths and/or inline endpoints for the route\n\t * @param subFolder An optional subFolder for grouping routes\n\t */\n\taddRoute(\n\t\tpath: string,\n\t\toptions: {\n\t\t\tpage?: string;\n\t\t\tpageServer?: string | PageServerEndPoint;\n\t\t\tserver?: string | ServerEndPoint;\n\t\t\tlayout?: string;\n\t\t\tlayoutServer?: string | PageServerEndPoint;\n\t\t\thookServer?: string | ServerHook;\n\t\t\terror?: string;\n\t\t},\n\t\tsubFolder?: string,\n\t): void {\n\t\tlet normalizedSubFolder = subFolder;\n\t\tif (normalizedSubFolder !== undefined && !normalizedSubFolder.startsWith(\"/\")) {\n\t\t\tnormalizedSubFolder = \"/\" + normalizedSubFolder;\n\t\t}\n\n\t\t// The base path with any trailing slash stripped, so that we can\n\t\t// append \"/_layout\", \"/~server\" etc. For the root \"/\" this is \"\".\n\t\tlet base = path.replace(/\\/$/, \"\");\n\n\t\tif (options.page !== undefined) {\n\t\t\tthis.#pushRoute(path, PAGE_ROUTE, options.page, normalizedSubFolder);\n\t\t}\n\t\tif (options.pageServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/~server`,\n\t\t\t\tPAGE_SERVER_ROUTE,\n\t\t\t\toptions.pageServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.server !== undefined) {\n\t\t\tthis.#pushRoute(path, SERVER_ROUTE, options.server, normalizedSubFolder);\n\t\t}\n\t\tif (options.layout !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_layout`, LAYOUT_ROUTE, options.layout, normalizedSubFolder);\n\t\t}\n\t\tif (options.layoutServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_layout/~server`,\n\t\t\t\tLAYOUT_SERVER_ROUTE,\n\t\t\t\toptions.layoutServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.hookServer !== undefined) {\n\t\t\tthis.#pushRoute(\n\t\t\t\t`${base}/_hook/~server`,\n\t\t\t\tHOOK_SERVER_ROUTE,\n\t\t\t\toptions.hookServer,\n\t\t\t\tnormalizedSubFolder,\n\t\t\t);\n\t\t}\n\t\tif (options.error !== undefined) {\n\t\t\tthis.#pushRoute(`${base}/_error`, ERROR_ROUTE, options.error, normalizedSubFolder);\n\t\t}\n\n\t\tthis.#sortRoutes();\n\t}\n\n\t/**\n\t * Pushes a single route entry, handling both file-path and inline-endpoint\n\t * targets. File paths are resolved relative to the site root; inline\n\t * endpoints are also stored in `inlineEndPoints` keyed by `\"path:type\"`\n\t * so the manifest plugin can resolve them at build time.\n\t *\n\t * If a route with the same path and type was already added, it is\n\t * replaced, so that running a plugin twice (config load and server\n\t * startup) doesn't add duplicate routes.\n\t */\n\t#pushRoute(\n\t\troutePath: string,\n\t\ttype: RouteType,\n\t\ttarget: string | InlineEndPoint,\n\t\tsubFolder?: string,\n\t): void {\n\t\tconst index = this.routes.findIndex((r) => r.path === routePath && r.type === type);\n\t\tif (typeof target === \"string\") {\n\t\t\tlet file = fpath.relative(this.root, fpath.resolve(this.root, target));\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, file, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, file, type, subFolder });\n\t\t\t}\n\t\t} else {\n\t\t\tlet key = `${routePath}:${type}`;\n\t\t\tthis.inlineEndPoints[key] = target;\n\t\t\tif (index >= 0) {\n\t\t\t\tthis.routes[index] = { path: routePath, endPoint: target, type, subFolder };\n\t\t\t} else {\n\t\t\t\tthis.routes.push({ path: routePath, endPoint: target, type, subFolder });\n\t\t\t}\n\t\t}\n\t}\n\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"],"mappings":"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"}