@torpor/build 1.0.0 → 1.0.1

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 (174) hide show
  1. package/dist/{flattenHeaders-C_YYLdOq.mjs → Server-BnjLJleZ.mjs} +2 -12
  2. package/dist/Server-BnjLJleZ.mjs.map +1 -0
  3. package/dist/{Server-C_IKav_e.d.mts → Server-Cler2w5O.d.mts} +3 -48
  4. package/dist/Server-Cler2w5O.d.mts.map +1 -0
  5. package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
  6. package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
  7. package/dist/{Site-CMa153FA.d.mts → Site-CE0_o5Ci.d.mts} +3 -2
  8. package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
  9. package/dist/bin/index.js +1 -1
  10. package/dist/clientEntry.d.mts +1 -0
  11. package/dist/clientEntry.mjs +59 -0
  12. package/dist/clientEntry.mjs.map +1 -0
  13. package/dist/clientEntryDev.d.mts +1 -0
  14. package/dist/clientEntryDev.mjs +7 -0
  15. package/dist/clientEntryDev.mjs.map +1 -0
  16. package/dist/dev.d.mts +3 -0
  17. package/dist/dev.mjs +3 -0
  18. package/dist/endpoint-EULRGCKA.mjs +108 -0
  19. package/dist/endpoint-EULRGCKA.mjs.map +1 -0
  20. package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
  21. package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
  22. package/dist/index.d.mts +1 -1
  23. package/dist/load-BntzvmwL.mjs +228 -0
  24. package/dist/load-BntzvmwL.mjs.map +1 -0
  25. package/dist/nav.mjs +1 -225
  26. package/dist/nav.mjs.map +1 -1
  27. package/dist/openapi.d.mts +1 -1
  28. package/dist/run.d.mts +1 -1
  29. package/dist/run.d.mts.map +1 -1
  30. package/dist/run.mjs +1 -1
  31. package/dist/{runOpenApi-BUi1RH1l.mjs → runOpenApi-DlOgF2Il.mjs} +54 -16
  32. package/dist/runOpenApi-DlOgF2Il.mjs.map +1 -0
  33. package/dist/server/Server.d.mts +2 -0
  34. package/dist/server/Server.mjs +2 -0
  35. package/dist/server.d.mts +2 -1
  36. package/dist/server.d.mts.map +1 -1
  37. package/dist/server.mjs +2 -1
  38. package/dist/server.mjs.map +1 -1
  39. package/dist/serverEntry-1qY3wZig.mjs +351 -0
  40. package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
  41. package/dist/serverEntry-BAO8m03V.d.mts +6 -0
  42. package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
  43. package/dist/serverEntry.d.mts +2 -0
  44. package/dist/serverEntry.mjs +2 -0
  45. package/dist/test.d.mts +2 -2
  46. package/dist/test.mjs +2 -104
  47. package/dist/test.mjs.map +1 -1
  48. package/package.json +9 -8
  49. package/dist/Server-C_IKav_e.d.mts.map +0 -1
  50. package/dist/Site-CMa153FA.d.mts.map +0 -1
  51. package/dist/flattenHeaders-C_YYLdOq.mjs.map +0 -1
  52. package/dist/runOpenApi-BUi1RH1l.mjs.map +0 -1
  53. package/src/bin/index.ts +0 -22
  54. package/src/dev.ts +0 -10
  55. package/src/form/formDataToRecord.ts +0 -23
  56. package/src/form/readForm.ts +0 -71
  57. package/src/form.ts +0 -4
  58. package/src/index.ts +0 -82
  59. package/src/nav/api.test-d.ts +0 -79
  60. package/src/nav/api.ts +0 -104
  61. package/src/nav/formSubmit.ts +0 -70
  62. package/src/nav/load.ts +0 -9
  63. package/src/nav/loadData.ts +0 -134
  64. package/src/nav/navigate.ts +0 -189
  65. package/src/nav/reload.ts +0 -9
  66. package/src/nav/route.ts +0 -38
  67. package/src/nav.ts +0 -6
  68. package/src/openapi/docsHtml.ts +0 -25
  69. package/src/openapi/document.ts +0 -154
  70. package/src/openapi/plugin.ts +0 -78
  71. package/src/openapi/types.ts +0 -82
  72. package/src/openapi.ts +0 -14
  73. package/src/response/TypedResponse.ts +0 -17
  74. package/src/response/badRequest.ts +0 -31
  75. package/src/response/created.ts +0 -31
  76. package/src/response/forbidden.ts +0 -21
  77. package/src/response/found.ts +0 -30
  78. package/src/response/methodNotAllowed.ts +0 -18
  79. package/src/response/movedPermanently.ts +0 -25
  80. package/src/response/notFound.ts +0 -24
  81. package/src/response/notModified.ts +0 -34
  82. package/src/response/ok.ts +0 -38
  83. package/src/response/permanentRedirect.ts +0 -30
  84. package/src/response/response.ts +0 -22
  85. package/src/response/seeOther.ts +0 -19
  86. package/src/response/serverError.ts +0 -24
  87. package/src/response/temporaryRedirect.ts +0 -31
  88. package/src/response/transfer.ts +0 -14
  89. package/src/response/unauthorized.ts +0 -21
  90. package/src/response/unprocessable.ts +0 -30
  91. package/src/response.ts +0 -37
  92. package/src/run/depCache.ts +0 -175
  93. package/src/run/devPlugin.ts +0 -128
  94. package/src/run/prepareTemplate.ts +0 -42
  95. package/src/run/run.ts +0 -86
  96. package/src/run/runBuild.ts +0 -128
  97. package/src/run/runDev.ts +0 -177
  98. package/src/run/runOpenApi.ts +0 -52
  99. package/src/run/runPreview.ts +0 -84
  100. package/src/run/staleTorpCopies.ts +0 -100
  101. package/src/run.ts +0 -7
  102. package/src/schema.ts +0 -7
  103. package/src/server/CookieHelper.ts +0 -46
  104. package/src/server/HeaderHelper.ts +0 -25
  105. package/src/server/Server.ts +0 -168
  106. package/src/server/ServerEvent.ts +0 -62
  107. package/src/server/connect/connectMiddleware.ts +0 -85
  108. package/src/server/connect/flattenHeaders.ts +0 -22
  109. package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
  110. package/src/server/connect/requestToNodeMessage.ts +0 -29
  111. package/src/server/contentType.ts +0 -85
  112. package/src/server/types/HttpMethod.ts +0 -12
  113. package/src/server/types/MiddlewareFunction.ts +0 -22
  114. package/src/server/types/ServerFunction.ts +0 -7
  115. package/src/server.ts +0 -7
  116. package/src/site/Router.ts +0 -175
  117. package/src/site/Site.ts +0 -388
  118. package/src/site/checkLayoutSlots.ts +0 -114
  119. package/src/site/checkRoutes.ts +0 -435
  120. package/src/site/clientEntry.ts +0 -121
  121. package/src/site/clientEntryDev.ts +0 -3
  122. package/src/site/defaultAdapter.ts +0 -11
  123. package/src/site/layoutSlots.ts +0 -53
  124. package/src/site/manifest.ts +0 -173
  125. package/src/site/serverEntry.ts +0 -648
  126. package/src/state/$page.ts +0 -25
  127. package/src/state/$serverPage.ts +0 -22
  128. package/src/state/client.ts +0 -15
  129. package/src/state.ts +0 -3
  130. package/src/test/runTest.ts +0 -538
  131. package/src/test.ts +0 -3
  132. package/src/types/Adapter.ts +0 -20
  133. package/src/types/ClientState.ts +0 -8
  134. package/src/types/InternalState.ts +0 -7
  135. package/src/types/Jsonify.ts +0 -20
  136. package/src/types/LayoutHandler.ts +0 -5
  137. package/src/types/LayoutPath.ts +0 -10
  138. package/src/types/ManifestRoute.ts +0 -6
  139. package/src/types/PageData.test-d.ts +0 -136
  140. package/src/types/PageData.ts +0 -35
  141. package/src/types/PageEndPoint.ts +0 -55
  142. package/src/types/PageForm.test-d.ts +0 -78
  143. package/src/types/PageForm.ts +0 -29
  144. package/src/types/PageLoadEvent.ts +0 -26
  145. package/src/types/PageLoadReturn.ts +0 -13
  146. package/src/types/PageProps.ts +0 -14
  147. package/src/types/PageServerAction.ts +0 -13
  148. package/src/types/PageServerEndPoint.test-d.ts +0 -105
  149. package/src/types/PageServerEndPoint.ts +0 -110
  150. package/src/types/PageServerLoad.ts +0 -15
  151. package/src/types/PageState.ts +0 -8
  152. package/src/types/ParseRouteParams.test-d.ts +0 -84
  153. package/src/types/ParseRouteParams.ts +0 -64
  154. package/src/types/Route.ts +0 -30
  155. package/src/types/RouteHandler.ts +0 -23
  156. package/src/types/RouteLayoutHandler.ts +0 -5
  157. package/src/types/RouteMatchResult.ts +0 -7
  158. package/src/types/RouteType.ts +0 -19
  159. package/src/types/ServerEndPoint.test-d.ts +0 -77
  160. package/src/types/ServerEndPoint.ts +0 -169
  161. package/src/types/ServerHook.ts +0 -18
  162. package/src/types/ServerLoadEvent.ts +0 -82
  163. package/src/types/ServerRequest.ts +0 -15
  164. package/src/types/SitePlugin.ts +0 -24
  165. package/src/types/StandardSchema.ts +0 -109
  166. package/src/utils/pathToRegex.ts +0 -17
  167. package/src/utils/pathTrie.ts +0 -182
  168. package/src/utils/searchParamsToRecord.ts +0 -18
  169. package/src/utils/torporPackages.ts +0 -150
  170. package/src/utils/tsconfigAliases.ts +0 -90
  171. package/src/validation/ValidationError.ts +0 -18
  172. package/src/validation/endpoint.ts +0 -83
  173. package/src/validation/validate.ts +0 -26
  174. package/src/vite-env.d.ts +0 -2
@@ -1,84 +0,0 @@
1
- import { configDotenv } from "dotenv";
2
- import { existsSync, promises as fs } from "node:fs";
3
- import path from "node:path";
4
- import { serverError } from "../response.ts";
5
- import Server from "../server/Server.ts";
6
- import contentType from "../server/contentType.ts";
7
- import Site from "../site/Site";
8
- import prepareTemplate from "./prepareTemplate.ts";
9
- import runBuild from "./runBuild";
10
-
11
- export default async function runPreview(site: Site): Promise<void> {
12
- // Build the site
13
- await runBuild(site);
14
-
15
- const distFolder = path.resolve(site.root, "dist");
16
- const clientFolder = path.join(distFolder, "client");
17
- const serverFolder = path.join(distFolder, "server");
18
-
19
- // Read site.html if present, and find the client script file in /assets.
20
- // An endpoints-only site has no site.html, and no template is needed
21
- let template: string | undefined;
22
- const siteHtml = path.join(clientFolder, "site.html");
23
- if (existsSync(siteHtml)) {
24
- let clientScript = (await fs.readdir(path.join(clientFolder, "assets"))).find((f) =>
25
- f.startsWith("clientEntry-"),
26
- );
27
- if (!clientScript) {
28
- throw new Error("clientEntry.js not found");
29
- }
30
- clientScript = `/assets/${clientScript}`;
31
-
32
- template = await fs.readFile(siteHtml, "utf-8");
33
-
34
- // Prepare site.html so that we can just splice components into it
35
- template = prepareTemplate(template, clientScript);
36
- }
37
-
38
- // Configure the server
39
- const server = new Server();
40
-
41
- // Serve dist/client/assets statically
42
- server.add("/assets/*", async (ev) => {
43
- try {
44
- const url = new URL(ev.request.url);
45
- const file = path.join(site.root, "dist", "client", url.pathname);
46
- if (existsSync(file)) {
47
- const body = await fs.readFile(file);
48
- return new Response(body, {
49
- status: 200,
50
- headers: {
51
- "Content-Type": contentType(path.extname(file)),
52
- },
53
- });
54
- }
55
- } catch (e: any) {
56
- return serverError(e);
57
- }
58
- });
59
-
60
- // Every request (GET, POST, etc) goes through loadEndPoint
61
- const serverScript = path.resolve(serverFolder, "serverEntry.js");
62
- server.add("*", async (ev) => {
63
- try {
64
- // Load the server entry
65
- const { load } = await import(serverScript);
66
-
67
- // Render the app HTML (or fetch server data etc) via serverEntry's
68
- // exported `load` function
69
- return await load(ev, template);
70
- } catch (e: any) {
71
- return serverError(e);
72
- }
73
- });
74
-
75
- // Load environment variables from a `.env` file, with defaults if not set
76
- configDotenv();
77
-
78
- process.env.PROTOCOL ??= "http:";
79
- process.env.HOST ??= "localhost";
80
- process.env.PORT ??= "7059";
81
-
82
- // Serve the site
83
- site.adapter.serve(server, site);
84
- }
@@ -1,100 +0,0 @@
1
- import { existsSync, readFileSync, readdirSync } from "node:fs";
2
- import path from "node:path";
3
- import type { PackageJson } from "../utils/torporPackages";
4
- import { findWorkspaceRoot, workspaceDependencyDirs } from "./depCache";
5
-
6
- /**
7
- * Workspace packages that ship copies of their .torp components (e.g.
8
- * @torpor/ui copies its src .torp files into dist/ during its build) can go
9
- * stale: a site resolves the package to its dist folder, so edits to src
10
- * silently keep serving the OLD components until the package is rebuilt.
11
- * The symptom is confusing -- e.g. a TypeError about a renamed slot prop
12
- * after a component's API changed.
13
- *
14
- * reportStaleTorpCopies runs at `tb --dev` startup: for every workspace
15
- * dependency with both src and dist folders, it compares the .torp files and
16
- * reports anything missing from dist, differing from src, or left over in
17
- * dist without a src file.
18
- */
19
-
20
- /**
21
- * Reports stale dist .torp copies in the site's workspace dependencies.
22
- * Never throws -- a broken check must not break dev startup.
23
- */
24
- export function reportStaleTorpCopies(siteRoot: string, log?: (message: string) => void): void {
25
- try {
26
- const workspaceRoot = findWorkspaceRoot(siteRoot);
27
- if (!workspaceRoot) return;
28
-
29
- for (const dir of workspaceDependencyDirs(siteRoot, workspaceRoot)) {
30
- const srcFolder = path.join(dir, "src");
31
- const distFolder = path.join(dir, "dist");
32
- // Only packages that ship dist copies of src .torp files are
33
- // checked; packages without the folders (or with neither .torp
34
- // set) are skipped
35
- if (!existsSync(srcFolder) || !existsSync(distFolder)) continue;
36
- const src = collectTorpFiles(srcFolder);
37
- const dist = collectTorpFiles(distFolder);
38
- if (src.size === 0 && dist.size === 0) continue;
39
-
40
- const problems: string[] = [];
41
- for (const [rel, content] of src) {
42
- const distContent = dist.get(rel);
43
- if (distContent === undefined) {
44
- problems.push(`${rel} (not copied to dist)`);
45
- } else if (distContent !== content) {
46
- problems.push(`${rel} (differs from src)`);
47
- }
48
- }
49
- for (const rel of dist.keys()) {
50
- if (!src.has(rel)) {
51
- problems.push(`${rel} (left over in dist, no src file)`);
52
- }
53
- }
54
- if (problems.length === 0) continue;
55
-
56
- const shown = problems
57
- .slice(0, 5)
58
- .map((p) => ` ${p}`)
59
- .join("\n");
60
- const more = problems.length > 5 ? `\n ... and ${problems.length - 5} more` : "";
61
- log?.(
62
- `${packageLabel(dir, workspaceRoot)}'s dist .torp copies are stale, so the site is ` +
63
- `serving OLD components:\n${shown}${more}\n` +
64
- `Rebuild the package (e.g. \`pnpm build\` in ` +
65
- `${path.relative(workspaceRoot, dir) || "."}) and restart \`tb --dev\`.`,
66
- );
67
- }
68
- } catch (e) {
69
- if (process.env.TORPOR_DEBUG_CACHE) throw e;
70
- }
71
- }
72
-
73
- function collectTorpFiles(folder: string): Map<string, string> {
74
- const files = new Map<string, string>();
75
- collect(folder, folder, files);
76
- return files;
77
- }
78
-
79
- function collect(folder: string, root: string, files: Map<string, string>): void {
80
- for (const entry of readdirSync(folder, { withFileTypes: true })) {
81
- if (entry.name === "node_modules" || entry.name === ".git") continue;
82
- const full = path.join(folder, entry.name);
83
- if (entry.isDirectory()) {
84
- collect(full, root, files);
85
- continue;
86
- }
87
- if (!entry.isFile() || !entry.name.endsWith(".torp")) continue;
88
- files.set(path.relative(root, full).replaceAll("\\", "/"), readFileSync(full, "utf8"));
89
- }
90
- }
91
-
92
- function packageLabel(dir: string, workspaceRoot: string): string {
93
- try {
94
- const pkg: PackageJson = JSON.parse(readFileSync(path.join(dir, "package.json"), "utf8"));
95
- if (pkg.name) return pkg.name;
96
- } catch {
97
- // Fall through to the relative path
98
- }
99
- return path.relative(workspaceRoot, dir) || dir;
100
- }
package/src/run.ts DELETED
@@ -1,7 +0,0 @@
1
- import run from "./run/run";
2
- import runBuild from "./run/runBuild";
3
- import runDev from "./run/runDev";
4
- import runOpenApi from "./run/runOpenApi";
5
- import runPreview from "./run/runPreview";
6
-
7
- export { run, runDev, runBuild, runPreview, runOpenApi };
package/src/schema.ts DELETED
@@ -1,7 +0,0 @@
1
- import validate from "./validation/validate";
2
-
3
- export { validate };
4
-
5
- export { default as ValidationError } from "./validation/ValidationError";
6
-
7
- export type { StandardSchemaV1 } from "./types/StandardSchema";
@@ -1,46 +0,0 @@
1
- import { type SerializeOptions, parseCookie, stringifySetCookie } from "cookie";
2
-
3
- export default class CookieHelper {
4
- request: Request;
5
- cookies: Map<string, string> = new Map();
6
-
7
- constructor(request: Request) {
8
- this.request = request;
9
- }
10
-
11
- get(name: string): string | undefined {
12
- // If it's been set, return the value from the map
13
- if (this.cookies.has(name)) {
14
- // The stored value is a serialized Set-Cookie header; pull the
15
- // "name=value" prefix off and re-parse to get just the value
16
- return parseCookie(this.cookies.get(name)!)[name];
17
- }
18
-
19
- // Otherwise, return the value from the `cookie` header
20
- const header = this.request.headers.get("Cookie");
21
- return parseCookie(header ?? "")[name];
22
- }
23
-
24
- set(name: string, value: string, options?: SerializeOptions): void {
25
- // Set a cookie header with some default values for security
26
- options ??= {};
27
- options.httpOnly ??= true;
28
- options.secure ??= true;
29
- options.sameSite ??= "lax";
30
-
31
- this.cookies.set(name, stringifySetCookie({ name, value, ...options }));
32
- }
33
-
34
- delete(name: string, options?: SerializeOptions): void {
35
- // Set a cookie header with an empty value and an immediate expiry date.
36
- // Apply the same secure defaults as `set` so a previously-set Secure
37
- // cookie is actually overridden by the deletion.
38
- options ??= {};
39
- options.httpOnly ??= true;
40
- options.secure ??= true;
41
- options.sameSite ??= "lax";
42
- options.expires = new Date(0);
43
-
44
- this.cookies.set(name, stringifySetCookie({ name, value: "", ...options }));
45
- }
46
- }
@@ -1,25 +0,0 @@
1
- export default class HeaderHelper {
2
- request: Request;
3
- headers: Map<string, string> = new Map();
4
-
5
- constructor(request: Request) {
6
- this.request = request;
7
- }
8
-
9
- get(name: string): string | undefined {
10
- // If it's been set, return the updated value
11
- if (this.headers.has(name)) {
12
- return this.headers.get(name);
13
- }
14
- // Otherwise, return the value from the header
15
- return this.request.headers.get(name) ?? undefined;
16
- }
17
-
18
- set(name: string, value: string): void {
19
- this.headers.set(name, value);
20
- }
21
-
22
- delete(name: string): void {
23
- this.headers.delete(name);
24
- }
25
- }
@@ -1,168 +0,0 @@
1
- import PathTrie from "../utils/pathTrie";
2
- import ServerEvent from "./ServerEvent";
3
- import type MiddlewareFunction from "./types/MiddlewareFunction";
4
- import type ServerFunction from "./types/ServerFunction";
5
-
6
- /**
7
- * A Fetch API Request/Response server.
8
- */
9
- export default class Server {
10
- // TODO: We don't use any of the routes, just add("*") in runDev/runBuild...
11
- routes: RouteHandler[] = [];
12
- // Match index for the routes above
13
- #trie = new PathTrie<RouteHandler>();
14
- // TODO: We only use the Vite middleware here
15
- middleware: MiddlewareFunction[] = [];
16
-
17
- constructor() {
18
- this.routes = [];
19
- this.middleware = [];
20
- }
21
-
22
- /**
23
- * The standard Fetch API method that should work in any runtime (including
24
- * Node with polyfills).
25
- * @returns
26
- */
27
- fetch = async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
28
- // Use the request as-is when we were given one, so that the adapter
29
- // doesn't pay for constructing it twice
30
- const request = input instanceof Request && !init ? input : new Request(input, init);
31
- const url = new URL(request.url);
32
- const match = this.match(url.pathname);
33
- let ev = new ServerEvent(request, match?.params, url);
34
-
35
- if (this.middleware.length) {
36
- // TODO: Get middleware that applies to this route only?
37
- let entered = 0;
38
- let response: Response | undefined = undefined;
39
- let error: unknown = undefined;
40
- try {
41
- for (; entered < this.middleware.length && !response; entered++) {
42
- // A middleware returns a Response to short-circuit the request
43
- const result = await this.middleware[entered].enter?.(ev);
44
- if (result) {
45
- response = result;
46
- }
47
- }
48
- if (!response && match) {
49
- const result = await match.fn(ev);
50
- if (result) {
51
- response = result;
52
- }
53
- }
54
- } catch (e) {
55
- // Exits can handle the error by returning a Response; if none
56
- // does, it is rethrown below
57
- error = e;
58
- ev.error = e;
59
- } finally {
60
- if (response) {
61
- ev.response = response;
62
- }
63
- // Run exit hooks in reverse order for the middleware that were
64
- // entered. An exit hook can return a Response to replace the
65
- // response, or to handle ev.error
66
- while (entered > 0) {
67
- entered--;
68
- const result = await this.middleware[entered].exit?.(ev);
69
- if (result) {
70
- response = result;
71
- ev.response = result;
72
- }
73
- }
74
- }
75
- if (error && !response) {
76
- throw error;
77
- }
78
- } else if (match) {
79
- ev.response = await match.fn(ev);
80
- } else {
81
- return new Response(null, { status: 404 });
82
- }
83
-
84
- // TODO: Should I be returning 200 if there's no response? Because it's not an error??
85
- ev.response ??= new Response(null, { status: 200 });
86
- ev.addHeaders();
87
-
88
- return ev.response;
89
- };
90
-
91
- /**
92
- * Adds a server function at a route path.
93
- * @param path The route path pattern.
94
- * @param fn The function to call when the pattern is matched.
95
- * @returns
96
- */
97
- add(path: string, fn: ServerFunction): Server {
98
- let handler = new RouteHandler(path, fn);
99
- this.routes.push(handler);
100
- this.#trie.insert(path, handler);
101
- this.#sortRoutes();
102
- return this;
103
- }
104
-
105
- match(path: string): Match | undefined {
106
- const found = this.#trie.match(path);
107
- if (found) {
108
- return {
109
- fn: found.value.fn,
110
- params: found.params,
111
- };
112
- }
113
- console.log(`Server path not found: ${path}`);
114
- }
115
-
116
- // TODO:
117
- //use(path: string, ...fn: MiddlewareFunction[])
118
- use(...fn: MiddlewareFunction[]): Server {
119
- this.middleware = this.middleware.concat(fn);
120
- return this;
121
- }
122
-
123
- #sortRoutes() {
124
- this.routes = this.routes.sort((a, b) => {
125
- // Sort [param]s after paths
126
- // There might be a quicker/easier way to do this
127
- for (let i = 0; i < Math.min(a.path.length, b.path.length); i++) {
128
- if (a.path[i] === "[" && b.path[i] !== "[") {
129
- return 1;
130
- } else if (b.path[i] === "[" && a.path[i] !== "[") {
131
- return -1;
132
- } else if (a.path[i] === b.path[i]) {
133
- // Keep going...
134
- } else {
135
- return a.path[i].localeCompare(b.path[i]);
136
- }
137
- }
138
- return a.path.length - b.path.length;
139
- });
140
- }
141
- }
142
-
143
- class RouteHandler {
144
- path: string;
145
- fn: ServerFunction;
146
-
147
- constructor(path: string, fn: ServerFunction) {
148
- this.path = path;
149
- this.fn = fn;
150
- }
151
- }
152
-
153
- //class MiddlewareHandler {
154
- // path: string;
155
- // regex: RegExp;
156
- // fn: MiddlewareFunction;
157
- //
158
- // constructor(path: string, fn: MiddlewareFunction) {
159
- // this.path = path;
160
- // this.regex = pathToRegex(path);
161
- // this.fn = fn;
162
- // }
163
- //}
164
-
165
- interface Match {
166
- fn: ServerFunction;
167
- params?: Record<string, string>;
168
- }
@@ -1,62 +0,0 @@
1
- import CookieHelper from "./CookieHelper";
2
- import HeaderHelper from "./HeaderHelper";
3
-
4
- export default class ServerEvent {
5
- request: Request;
6
- response: Response | undefined | void;
7
- /**
8
- * Set when the route handler (or a middleware enter hook) threw an
9
- * error. Exit hooks can return a Response to handle it; if none does,
10
- * the error is rethrown to the caller.
11
- */
12
- error: unknown;
13
- // TODO: Should we cast types??
14
- params?: Record<string, string>;
15
-
16
- adapter: any;
17
-
18
- cookies: CookieHelper;
19
- headers: HeaderHelper;
20
-
21
- // Parsed once per request; Server.fetch passes the URL it already needed
22
- // for matching so it isn't parsed twice
23
- #url: URL | undefined;
24
-
25
- /** The request URL, parsed lazily. */
26
- get url(): URL {
27
- return (this.#url ??= new URL(this.request.url));
28
- }
29
-
30
- constructor(request: Request, params?: Record<string, string>, url?: URL) {
31
- this.request = request;
32
- this.params = params;
33
- this.#url = url;
34
- this.cookies = new CookieHelper(request);
35
- this.headers = new HeaderHelper(request);
36
-
37
- // Adapters may have added the `adapter` property to globalThis
38
- // @ts-ignore
39
- this.adapter = globalThis.adapter;
40
- }
41
-
42
- addHeaders(): void {
43
- if (!this.response) {
44
- throw new Error("Response not created yet");
45
- }
46
-
47
- for (let cookie of this.cookies.cookies.values()) {
48
- this.response.headers.append("set-cookie", cookie);
49
- }
50
-
51
- for (let header of this.headers.headers.entries()) {
52
- this.response.headers.append(header[0], header[1]);
53
- }
54
- }
55
-
56
- /**
57
- * Reads the request body as JSON.
58
- */
59
- json(): Promise<unknown> {
60
- return this.request.json();
61
- }
62
- }
@@ -1,85 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from "node:http";
2
- import { Readable } from "node:stream";
3
- import type MiddlewareFunction from "../types/MiddlewareFunction";
4
- import flattenHeaders from "./flattenHeaders";
5
- import nodeMessageToNodeResponse from "./nodeMessageToNodeResponse";
6
- import requestToNodeMessage from "./requestToNodeMessage";
7
-
8
- // From https://github.com/vikejs/vike-node/blob/main/packages/vike-node/src/runtime/adapters/connectToWeb.ts
9
-
10
- /**
11
- * Converts a Connect-style middleware to a torpor middleware.
12
- *
13
- * If the Connect handler writes a response, it is returned from the `enter`
14
- * hook and the middleware chain is short-circuited. If the handler calls
15
- * `next()`, the rest of the chain runs after this middleware completes.
16
- *
17
- * @param handler - The Connect-style middleware function to be converted.
18
- * @returns A middleware whose `enter` hook handles the web request.
19
- */
20
- export default function connectMiddleware(
21
- handler: ConnectMiddleware | ConnectMiddlewareBoolean,
22
- ): MiddlewareFunction {
23
- return {
24
- enter: (ev) =>
25
- new Promise<Response | void>((resolve, reject) => {
26
- const req = requestToNodeMessage(ev.request);
27
- const { res, onReadable } = nodeMessageToNodeResponse(req);
28
-
29
- onReadable(({ readable, headers, status }) => {
30
- const responseBody: ReadableStream | null = statusCodesWithoutBody.includes(status)
31
- ? null
32
- : (Readable.toWeb(readable) as unknown as ReadableStream);
33
- resolve(
34
- new Response(responseBody, {
35
- status,
36
- headers: flattenHeaders(headers),
37
- }),
38
- );
39
- });
40
-
41
- const cnext = (error?: unknown) => {
42
- if (error) {
43
- // eslint-disable-next-line no-base-to-string
44
- reject(error instanceof Error ? error : new Error(String(error)));
45
- } else {
46
- // The Connect handler is passing the request on
47
- resolve();
48
- }
49
- };
50
-
51
- Promise.resolve(handler(req, res, cnext))
52
- .then((handled) => {
53
- if (handled === false) {
54
- res.destroy();
55
- // Returning false signals that the request was handled,
56
- // so return an empty response to stop the chain
57
- resolve(new Response(null, { status: 200 }));
58
- }
59
- })
60
- .catch((e: unknown) => {
61
- cnext(e);
62
- });
63
- }),
64
- };
65
- }
66
-
67
- const statusCodesWithoutBody = [
68
- 100, // Continue
69
- 101, // Switching Protocols
70
- 102, // Processing (WebDAV)
71
- 103, // Early Hints
72
- 204, // No Content
73
- 205, // Reset Content
74
- 304, // Not Modified
75
- ];
76
-
77
- type NextFunction = (err?: unknown) => void;
78
- type ConnectMiddleware<
79
- PlatformRequest extends IncomingMessage = IncomingMessage,
80
- PlatformResponse extends ServerResponse = ServerResponse,
81
- > = (req: PlatformRequest, res: PlatformResponse, next: NextFunction) => void | Promise<void>;
82
- type ConnectMiddlewareBoolean<
83
- PlatformRequest extends IncomingMessage = IncomingMessage,
84
- PlatformResponse extends ServerResponse = ServerResponse,
85
- > = (req: PlatformRequest, res: PlatformResponse, next: NextFunction) => boolean | Promise<boolean>;
@@ -1,22 +0,0 @@
1
- import { IncomingHttpHeaders } from "node:http";
2
- import { OutgoingHttpHeaders } from "node:http2";
3
-
4
- export default function flattenHeaders(
5
- headers: IncomingHttpHeaders | OutgoingHttpHeaders,
6
- ): HeadersInit {
7
- const flatHeaders = new Headers();
8
- for (const [key, value] of Object.entries(headers)) {
9
- if (value === undefined || value === null) {
10
- continue;
11
- } else if (Array.isArray(value)) {
12
- for (const v of value) {
13
- if (v !== undefined && v !== null) {
14
- flatHeaders.append(key, String(v));
15
- }
16
- }
17
- } else {
18
- flatHeaders.set(key, String(value));
19
- }
20
- }
21
- return flatHeaders;
22
- }