@torpor/adapter-node 0.1.2 → 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.
- package/README.md +15 -1
- package/dist/index.d.mts +0 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +17 -21
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -10
package/README.md
CHANGED
|
@@ -6,4 +6,18 @@ A @torpor/build adapter for running on Node.
|
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install the adapter using `npm` (or your preferred package manager):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @torpor/adapter-node
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then use the adapter in your `site.config`:
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
import { node } from "@torpor/adapter-node";
|
|
19
|
+
import { Site } from "@torpor/build";
|
|
20
|
+
|
|
21
|
+
const site: Site = new Site();
|
|
22
|
+
site.adapter = node;
|
|
23
|
+
```
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/adapter.ts"],"mappings":";;cAIA,UAmBuB"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { createServer } from "node:http";
|
|
2
2
|
import { Readable } from "node:stream";
|
|
3
3
|
import { isArrayBufferView } from "node:util/types";
|
|
4
|
-
|
|
5
4
|
//#region src/constants.ts
|
|
6
5
|
const INTERNAL_BODY = Symbol("internal_body");
|
|
7
|
-
|
|
8
6
|
//#endregion
|
|
9
7
|
//#region src/flattenHeaders.ts
|
|
8
|
+
/**
|
|
9
|
+
* Flattens raw node request headers into a `HeadersInit`. Returning plain
|
|
10
|
+
* tuples (rather than a `Headers` instance) means the Request constructor
|
|
11
|
+
* walks them once instead of copying from a Headers into another Headers.
|
|
12
|
+
*/
|
|
10
13
|
function flattenHeaders(headers) {
|
|
11
|
-
const flatHeaders =
|
|
14
|
+
const flatHeaders = [];
|
|
12
15
|
for (const [key, value] of Object.entries(headers)) if (value === void 0 || value === null) continue;
|
|
13
16
|
else if (Array.isArray(value)) {
|
|
14
|
-
for (const v of value) if (v !== void 0 && v !== null)
|
|
15
|
-
} else flatHeaders.
|
|
17
|
+
for (const v of value) if (v !== void 0 && v !== null) flatHeaders.push([key, String(v)]);
|
|
18
|
+
} else flatHeaders.push([key, String(value)]);
|
|
16
19
|
return flatHeaders;
|
|
17
20
|
}
|
|
18
|
-
|
|
19
21
|
//#endregion
|
|
20
22
|
//#region src/nodeMessageToRequest.ts
|
|
21
23
|
async function nodeMessageToRequest(req) {
|
|
@@ -34,45 +36,41 @@ async function nodeMessageToRequest(req) {
|
|
|
34
36
|
duplex: "half"
|
|
35
37
|
});
|
|
36
38
|
}
|
|
37
|
-
|
|
38
39
|
//#endregion
|
|
39
40
|
//#region src/responseToNodeResponse.ts
|
|
40
41
|
async function responseToNodeResponse(res, outgoing) {
|
|
41
|
-
outgoing.statusCode = res.status;
|
|
42
|
-
outgoing.statusMessage = res.statusText;
|
|
43
42
|
res.headers.forEach((value, key) => {
|
|
44
43
|
outgoing.setHeader(key, value);
|
|
45
44
|
});
|
|
46
45
|
const body = res[INTERNAL_BODY];
|
|
47
46
|
if (body === null || body === void 0) {
|
|
48
|
-
outgoing.writeHead(res.status
|
|
47
|
+
outgoing.writeHead(res.status);
|
|
49
48
|
outgoing.end();
|
|
50
49
|
} else if (typeof body === "string" || body instanceof Uint8Array) {
|
|
51
|
-
outgoing.writeHead(res.status
|
|
50
|
+
outgoing.writeHead(res.status);
|
|
52
51
|
outgoing.end(body);
|
|
53
52
|
} else if (body instanceof Blob) {
|
|
54
|
-
outgoing.writeHead(res.status
|
|
53
|
+
outgoing.writeHead(res.status);
|
|
55
54
|
outgoing.end(new Uint8Array(await body.arrayBuffer()));
|
|
56
55
|
} else if (body instanceof ArrayBuffer) {
|
|
57
|
-
outgoing.writeHead(res.status
|
|
56
|
+
outgoing.writeHead(res.status);
|
|
58
57
|
outgoing.end(new Uint8Array(body));
|
|
59
58
|
} else if (isArrayBufferView(body)) {
|
|
60
|
-
outgoing.writeHead(res.status
|
|
59
|
+
outgoing.writeHead(res.status);
|
|
61
60
|
outgoing.end(new Uint8Array(body.buffer));
|
|
62
61
|
} else if (body instanceof FormData) {
|
|
63
62
|
outgoing.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
|
|
64
|
-
outgoing.writeHead(res.status
|
|
63
|
+
outgoing.writeHead(res.status);
|
|
65
64
|
outgoing.end();
|
|
66
65
|
} else {
|
|
67
|
-
outgoing.writeHead(res.status
|
|
66
|
+
outgoing.writeHead(res.status);
|
|
68
67
|
outgoing.end();
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
|
-
|
|
72
70
|
//#endregion
|
|
73
71
|
//#region src/createFetchHandler.ts
|
|
74
72
|
const GlobalResponse = Response;
|
|
75
|
-
globalThis.Response = class Response
|
|
73
|
+
globalThis.Response = class Response extends GlobalResponse {
|
|
76
74
|
[INTERNAL_BODY] = null;
|
|
77
75
|
constructor(body, init) {
|
|
78
76
|
super(body, init);
|
|
@@ -84,13 +82,11 @@ function createFetchHandler(handler) {
|
|
|
84
82
|
return await responseToNodeResponse(await handler(await nodeMessageToRequest(incoming)), outgoing);
|
|
85
83
|
};
|
|
86
84
|
}
|
|
87
|
-
|
|
88
85
|
//#endregion
|
|
89
86
|
//#region src/createNodeServer.ts
|
|
90
87
|
function createNodeServer(handler) {
|
|
91
88
|
return createServer(createFetchHandler(handler));
|
|
92
89
|
}
|
|
93
|
-
|
|
94
90
|
//#endregion
|
|
95
91
|
//#region src/adapter.ts
|
|
96
92
|
var adapter_default = { serve: (server) => {
|
|
@@ -104,7 +100,7 @@ var adapter_default = { serve: (server) => {
|
|
|
104
100
|
console.log(`Listening on ${url}\n`);
|
|
105
101
|
});
|
|
106
102
|
} };
|
|
107
|
-
|
|
108
103
|
//#endregion
|
|
109
104
|
export { adapter_default as node };
|
|
105
|
+
|
|
110
106
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/constants.ts","../src/flattenHeaders.ts","../src/nodeMessageToRequest.ts","../src/responseToNodeResponse.ts","../src/createFetchHandler.ts","../src/createNodeServer.ts","../src/adapter.ts"],"sourcesContent":["export const INTERNAL_BODY: unique symbol = Symbol(\"internal_body\");\n","import { IncomingHttpHeaders } from \"node:http\";\nimport { OutgoingHttpHeaders } from \"node:http2\";\n\n/**\n * Flattens raw node request headers into a `HeadersInit`. Returning plain\n * tuples (rather than a `Headers` instance) means the Request constructor\n * walks them once instead of copying from a Headers into another Headers.\n */\nexport default function flattenHeaders(\n\theaders: IncomingHttpHeaders | OutgoingHttpHeaders,\n): HeadersInit {\n\tconst flatHeaders: [string, string][] = [];\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (value === undefined || value === null) {\n\t\t\tcontinue;\n\t\t} else if (Array.isArray(value)) {\n\t\t\tfor (const v of value) {\n\t\t\t\tif (v !== undefined && v !== null) {\n\t\t\t\t\tflatHeaders.push([key, String(v)]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tflatHeaders.push([key, String(value)]);\n\t\t}\n\t}\n\treturn flatHeaders;\n}\n","import { type IncomingMessage } from \"node:http\";\nimport { Http2ServerRequest } from \"node:http2\";\nimport { Readable } from \"node:stream\";\nimport flattenHeaders from \"./flattenHeaders\";\n\n// From https://stackoverflow.com/a/78849544\n// and https://gist.github.com/marvinhagemeister/cc236ec97235ce0305ae9d48a24a607d\n\nexport default async function nodeMessageToRequest(\n\treq: IncomingMessage | Http2ServerRequest,\n): Promise<Request> {\n\t// Build the full url\n\tlet path = req.url;\n\tif (path === undefined) {\n\t\tthrow new Error(`Empty request URL`);\n\t}\n\tif (!path.startsWith(\"/\")) {\n\t\tpath = \"/\" + path;\n\t}\n\tconst { host } = req.headers;\n\tif (host == undefined) {\n\t\tthrow new Error(`Missing \"Host\" header`);\n\t}\n\tconst protocol = \"encrypted\" in req.socket && req.socket.encrypted ? \"https\" : \"http\";\n\tconst url = `${protocol}://${host}${path}`;\n\n\t// Flatten the headers\n\tconst headers = flattenHeaders(req.headers);\n\n\t// Convert body to Buffer if applicable\n\tconst body =\n\t\treq.method !== \"GET\" && req.method !== \"HEAD\"\n\t\t\t? // @ts-ignore\n\t\t\t\t(Readable.toWeb(req) as ReadableStream) // await readableToBuffer(req)\n\t\t\t: null;\n\n\treturn new Request(url, {\n\t\tmethod: req.method,\n\t\theaders: headers,\n\t\t// eslint-disable-next-line no-invalid-fetch-options\n\t\tbody: body,\n\t\t// @ts-ignore this is needed for using a Readable for some reason\n\t\tduplex: \"half\",\n\t});\n}\n","import { ServerResponse } from \"node:http\";\nimport { Http2ServerResponse } from \"node:http2\";\nimport { isArrayBufferView } from \"node:util/types\";\nimport { INTERNAL_BODY } from \"./constants\";\n\nexport default async function responseToNodeResponse(\n\tres: Response,\n\toutgoing: ServerResponse | Http2ServerResponse,\n): Promise<void> {\n\tres.headers.forEach((value, key) => {\n\t\toutgoing.setHeader(key, value);\n\t});\n\n\tconst body = (res as any)[INTERNAL_BODY];\n\n\t// `writeHead(status)` uses node's standard reason phrase for the code\n\t// (the same one undici's `statusText` carries)\n\tif (body === null || body === undefined) {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end();\n\t} else if (typeof body === \"string\" || body instanceof Uint8Array) {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end(body);\n\t} else if (body instanceof Blob) {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end(new Uint8Array(await body.arrayBuffer()));\n\t} else if (body instanceof ArrayBuffer) {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end(new Uint8Array(body));\n\t} else if (isArrayBufferView(body)) {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end(new Uint8Array(body.buffer));\n\t} else if (body instanceof FormData) {\n\t\toutgoing.setHeader(\"Content-Type\", \"application/x-www-form-urlencoded;charset=UTF-8\");\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end();\n\t} else {\n\t\t// @ts-ignore\n\t\toutgoing.writeHead(res.status);\n\t\toutgoing.end();\n\t}\n}\n","import { type IncomingMessage, ServerResponse } from \"node:http\";\nimport { Http2ServerRequest, Http2ServerResponse } from \"node:http2\";\nimport { INTERNAL_BODY } from \"./constants\";\nimport nodeMessageToRequest from \"./nodeMessageToRequest\";\nimport responseToNodeResponse from \"./responseToNodeResponse\";\n\n// From https://gist.github.com/marvinhagemeister/cc236ec97235ce0305ae9d48a24a607d\n// Referenced from https://marvinh.dev/blog/modern-way-to-write-javascript-servers/\n\nconst GlobalResponse = Response;\nglobalThis.Response = class Response extends GlobalResponse {\n\t[INTERNAL_BODY]: BodyInit | null | undefined = null;\n\n\tconstructor(body?: BodyInit | null, init?: ResponseInit) {\n\t\tsuper(body, init);\n\t\tthis[INTERNAL_BODY] = body;\n\t}\n};\n\nexport function createFetchHandler(handler: (req: Request) => Response | Promise<Response>) {\n\treturn async (\n\t\tincoming: IncomingMessage | Http2ServerRequest,\n\t\toutgoing: ServerResponse | Http2ServerResponse,\n\t): Promise<void> => {\n\t\tconst req = await nodeMessageToRequest(incoming);\n\t\tconst res = await handler(req);\n\t\treturn await responseToNodeResponse(res, outgoing);\n\t};\n}\n","import { IncomingMessage, Server, ServerResponse } from \"http\";\nimport { createServer } from \"node:http\";\nimport { createFetchHandler } from \"./createFetchHandler\";\n\nexport default function createNodeServer(\n\thandler: (req: Request) => Response | Promise<Response>,\n): Server<typeof IncomingMessage, typeof ServerResponse> {\n\t// Create a server, polyfill it so that it can handle standard fetch methods,\n\t// and pass our app fetch method to the handler\n\treturn createServer(createFetchHandler(handler));\n}\n","import { type Adapter } from \"@torpor/build\";\nimport { Server } from \"@torpor/build/server\";\nimport createNodeServer from \"./createNodeServer\";\n\nexport default {\n\tserve: (server: Server) => {\n\t\tprocess.env.PROTOCOL ??= \"http:\";\n\t\tprocess.env.HOST ??= \"localhost\";\n\t\tprocess.env.PORT ??= \"7059\";\n\n\t\t// Put adapter-specific functionality in the `adapter` property of\n\t\t// globalThis for now\n\t\t// @ts-ignore\n\t\tglobalThis.adapter = { env: process.env };\n\n\t\t// Create the Node server and start listening\n\t\tconst url = `${process.env.PROTOCOL}//${process.env.HOST}:${process.env.PORT}`;\n\t\tconsole.log(`\\nConnecting to ${url}`);\n\t\tconst devServer = createNodeServer(server.fetch);\n\t\tdevServer.listen(parseInt(process.env.PORT), process.env.HOST, () => {\n\t\t\tconsole.log(`Listening on ${url}\\n`);\n\t\t});\n\t},\n} satisfies Adapter as Adapter;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torpor/adapter-node",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "A @torpor/build adapter for running on Node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,19 +17,16 @@
|
|
|
17
17
|
"author": "",
|
|
18
18
|
"license": "ISC",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"@torpor/build": "^0.4.10"
|
|
20
|
+
"@torpor/build": "^1.0.1"
|
|
22
21
|
},
|
|
23
22
|
"devDependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"tsdown": "^0.16.4",
|
|
28
|
-
"typescript": "^5.9.3"
|
|
23
|
+
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
24
|
+
"typescript": "^7.0.2",
|
|
25
|
+
"vite-plus": "0.2.7"
|
|
29
26
|
},
|
|
30
27
|
"scripts": {
|
|
31
28
|
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
32
|
-
"build": "tsgo --noEmit &&
|
|
33
|
-
"build:watch": "
|
|
29
|
+
"build": "tsgo --noEmit && vp pack",
|
|
30
|
+
"build:watch": "vp pack --watch"
|
|
34
31
|
}
|
|
35
32
|
}
|