@marko/run 0.0.1-beta2 → 0.0.1-beta4
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 +102 -74
- package/dist/adapter/default-entry.mjs +9 -3
- package/dist/adapter/dev-server.d.ts +1 -1
- package/dist/adapter/index.cjs +189 -8
- package/dist/adapter/index.d.ts +1 -0
- package/dist/adapter/index.js +188 -7
- package/dist/adapter/middleware.cjs +199 -0
- package/dist/adapter/middleware.d.ts +55 -0
- package/dist/adapter/middleware.js +168 -0
- package/dist/cli/default.config.mjs +1 -1
- package/dist/cli/index.mjs +88 -59
- package/dist/runtime/index.cjs +0 -16
- package/dist/runtime/index.d.ts +10 -2
- package/dist/runtime/index.js +0 -7
- package/dist/runtime/internal.cjs +148 -0
- package/dist/runtime/internal.d.ts +10 -0
- package/dist/runtime/internal.js +115 -0
- package/dist/runtime/router.cjs +9 -7
- package/dist/runtime/router.d.ts +4 -4
- package/dist/runtime/router.js +7 -5
- package/dist/runtime/types.d.ts +29 -16
- package/dist/vite/codegen/index.d.ts +4 -3
- package/dist/vite/codegen/writer.d.ts +1 -1
- package/dist/vite/constants.d.ts +3 -2
- package/dist/vite/index.cjs +557 -282
- package/dist/vite/index.d.ts +4 -3
- package/dist/vite/index.js +554 -282
- package/dist/vite/types.d.ts +14 -7
- package/dist/vite/utils/config.d.ts +5 -3
- package/dist/vite/utils/server.d.ts +3 -1
- package/package.json +23 -11
package/dist/adapter/index.js
CHANGED
|
@@ -4,7 +4,173 @@ import { fileURLToPath } from "url";
|
|
|
4
4
|
|
|
5
5
|
// src/adapter/dev-server.ts
|
|
6
6
|
import { createServer } from "vite";
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
// src/adapter/middleware.ts
|
|
9
|
+
import * as webStream from "stream/web";
|
|
10
|
+
import installCrypto from "@hattip/polyfills/crypto";
|
|
11
|
+
installCrypto();
|
|
12
|
+
for (const key of Object.keys(webStream)) {
|
|
13
|
+
if (!(key in global)) {
|
|
14
|
+
global[key] = webStream[key];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function getForwardedHeader(req, name) {
|
|
18
|
+
const value = req.headers["x-forwarded-" + name];
|
|
19
|
+
if (value) {
|
|
20
|
+
if (typeof value === "string") {
|
|
21
|
+
const index = value.indexOf(",");
|
|
22
|
+
return index < 0 ? value : value.slice(0, index);
|
|
23
|
+
}
|
|
24
|
+
return value[0];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function getOrigin(req, protocol, host, trustProxy) {
|
|
28
|
+
var _a;
|
|
29
|
+
protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
|
|
30
|
+
host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
|
|
31
|
+
if (!host) {
|
|
32
|
+
if (process.env.NODE_ENV !== "production") {
|
|
33
|
+
host = "localhost";
|
|
34
|
+
console.warn(
|
|
35
|
+
`Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
36
|
+
);
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return `${protocol}://${host}`;
|
|
44
|
+
}
|
|
45
|
+
function createMiddleware(router, options = {}) {
|
|
46
|
+
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
47
|
+
let { origin = process.env.ORIGIN } = options;
|
|
48
|
+
let protocol;
|
|
49
|
+
let host;
|
|
50
|
+
if (origin) {
|
|
51
|
+
({ protocol, host } = new URL(origin));
|
|
52
|
+
protocol = protocol.slice(0, -1);
|
|
53
|
+
}
|
|
54
|
+
return async (req, res, next) => {
|
|
55
|
+
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
56
|
+
const url = new URL(req.url, origin);
|
|
57
|
+
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
58
|
+
const requestContext = {
|
|
59
|
+
method: req.method,
|
|
60
|
+
url,
|
|
61
|
+
platform: {
|
|
62
|
+
ip,
|
|
63
|
+
request: req,
|
|
64
|
+
response: res,
|
|
65
|
+
setCookie(cookie) {
|
|
66
|
+
res.appendHeader("set-cookie", cookie);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
Object.defineProperty(requestContext, "request", {
|
|
71
|
+
get() {
|
|
72
|
+
const headers = req.headers;
|
|
73
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
74
|
+
start(controller) {
|
|
75
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
76
|
+
req.on("end", () => controller.close());
|
|
77
|
+
req.on("error", (err) => controller.error(err));
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
const request = new Request(url, {
|
|
81
|
+
method: req.method,
|
|
82
|
+
headers,
|
|
83
|
+
body,
|
|
84
|
+
duplex: "half"
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(this, "request", {
|
|
87
|
+
value: request,
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: true
|
|
90
|
+
});
|
|
91
|
+
return request;
|
|
92
|
+
},
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true
|
|
95
|
+
});
|
|
96
|
+
const response = await router(requestContext);
|
|
97
|
+
if (!response) {
|
|
98
|
+
if (next) {
|
|
99
|
+
next();
|
|
100
|
+
} else {
|
|
101
|
+
res.statusCode = 404;
|
|
102
|
+
res.setHeader("content-length", "0");
|
|
103
|
+
res.end();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
res.statusCode = response.status;
|
|
109
|
+
for (const [key, value] of response.headers) {
|
|
110
|
+
if (key === "set-cookie") {
|
|
111
|
+
let sepIndex = value.indexOf(",") + 1;
|
|
112
|
+
if (!sepIndex) {
|
|
113
|
+
res.setHeader(key, value);
|
|
114
|
+
} else {
|
|
115
|
+
let index = 0;
|
|
116
|
+
do {
|
|
117
|
+
res.appendHeader(key, value.slice(index, sepIndex - 1));
|
|
118
|
+
index = sepIndex;
|
|
119
|
+
sepIndex = value.indexOf(",", sepIndex) + 1;
|
|
120
|
+
} while (sepIndex);
|
|
121
|
+
res.appendHeader(key, value.slice(index));
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
res.setHeader(key, value);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (!response.body) {
|
|
128
|
+
if (!response.headers.has("content-length")) {
|
|
129
|
+
res.setHeader("content-length", "0");
|
|
130
|
+
}
|
|
131
|
+
res.end();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const reader = response.body.getReader();
|
|
135
|
+
if (res.destroyed) {
|
|
136
|
+
reader.cancel();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
res.on("close", cancel);
|
|
140
|
+
res.on("error", cancel);
|
|
141
|
+
write();
|
|
142
|
+
function cancel(error) {
|
|
143
|
+
res.off("close", cancel);
|
|
144
|
+
res.off("error", cancel);
|
|
145
|
+
reader.cancel(error).catch(() => {
|
|
146
|
+
});
|
|
147
|
+
error && res.destroy(error);
|
|
148
|
+
}
|
|
149
|
+
async function write() {
|
|
150
|
+
try {
|
|
151
|
+
while (true) {
|
|
152
|
+
const { done, value } = await reader.read();
|
|
153
|
+
if (done) {
|
|
154
|
+
res.end();
|
|
155
|
+
return;
|
|
156
|
+
} else if (!res.write(value)) {
|
|
157
|
+
res.once("drain", write);
|
|
158
|
+
return;
|
|
159
|
+
} else if (res.flush) {
|
|
160
|
+
res.flush();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const error = err instanceof Error ? err : new Error("Error while writing to node response", {
|
|
165
|
+
cause: err
|
|
166
|
+
});
|
|
167
|
+
cancel(error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/adapter/dev-server.ts
|
|
8
174
|
function createViteDevMiddleware(devServer, load, factory) {
|
|
9
175
|
let value;
|
|
10
176
|
let middleware;
|
|
@@ -32,7 +198,7 @@ async function createDevServer(configFile) {
|
|
|
32
198
|
});
|
|
33
199
|
const middleware = createViteDevMiddleware(
|
|
34
200
|
devServer,
|
|
35
|
-
async () => (await devServer.ssrLoadModule("@marko/run/router")).
|
|
201
|
+
async () => (await devServer.ssrLoadModule("@marko/run/router")).router,
|
|
36
202
|
createMiddleware
|
|
37
203
|
);
|
|
38
204
|
return devServer.middlewares.use(middleware);
|
|
@@ -41,16 +207,30 @@ async function createDevServer(configFile) {
|
|
|
41
207
|
// src/vite/utils/server.ts
|
|
42
208
|
import net from "net";
|
|
43
209
|
import cp from "child_process";
|
|
44
|
-
|
|
210
|
+
import { parse, config } from "dotenv";
|
|
211
|
+
import fs from "fs";
|
|
212
|
+
async function parseEnv(envFile) {
|
|
213
|
+
if (fs.existsSync(envFile)) {
|
|
214
|
+
const content = await fs.promises.readFile(envFile, "utf8");
|
|
215
|
+
return parse(content);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function loadEnv(envFile) {
|
|
219
|
+
config({ path: envFile });
|
|
220
|
+
}
|
|
221
|
+
async function spawnServer(cmd, port = 0, env, cwd = process.cwd(), wait = 3e4) {
|
|
45
222
|
if (port <= 0) {
|
|
46
223
|
port = await getAvailablePort();
|
|
47
224
|
}
|
|
225
|
+
if (typeof env === "string") {
|
|
226
|
+
env = await parseEnv(env);
|
|
227
|
+
}
|
|
48
228
|
const proc = cp.spawn(cmd, {
|
|
49
229
|
cwd,
|
|
50
230
|
shell: true,
|
|
51
231
|
stdio: "inherit",
|
|
52
232
|
windowsHide: true,
|
|
53
|
-
env: { NODE_ENV: "development", ...process.env, PORT: `${port}` }
|
|
233
|
+
env: { ...env, NODE_ENV: "development", ...process.env, PORT: `${port}` }
|
|
54
234
|
});
|
|
55
235
|
const close = () => {
|
|
56
236
|
proc.unref();
|
|
@@ -103,7 +283,8 @@ function adapter() {
|
|
|
103
283
|
const entry = path.join(__dirname, "default-entry");
|
|
104
284
|
return entry;
|
|
105
285
|
},
|
|
106
|
-
async startDev(configFile, port) {
|
|
286
|
+
async startDev(configFile, port, envFile) {
|
|
287
|
+
envFile && await loadEnv(envFile);
|
|
107
288
|
const server = await createDevServer(configFile);
|
|
108
289
|
return new Promise((resolve) => {
|
|
109
290
|
const listener = server.listen(port, () => {
|
|
@@ -113,8 +294,8 @@ function adapter() {
|
|
|
113
294
|
});
|
|
114
295
|
});
|
|
115
296
|
},
|
|
116
|
-
async startPreview(dir, entry, port) {
|
|
117
|
-
const server = await spawnServer(`node ${entry}`, port, dir);
|
|
297
|
+
async startPreview(dir, entry, port, envFile) {
|
|
298
|
+
const server = await spawnServer(`node ${entry}`, port, envFile, dir);
|
|
118
299
|
console.log(`Preview server started: http://localhost:${server.port}`);
|
|
119
300
|
}
|
|
120
301
|
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// src/adapter/middleware.ts
|
|
27
|
+
var middleware_exports = {};
|
|
28
|
+
__export(middleware_exports, {
|
|
29
|
+
default: () => createMiddleware,
|
|
30
|
+
getOrigin: () => getOrigin
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(middleware_exports);
|
|
33
|
+
var webStream = __toESM(require("stream/web"), 1);
|
|
34
|
+
var import_crypto = __toESM(require("@hattip/polyfills/crypto"), 1);
|
|
35
|
+
(0, import_crypto.default)();
|
|
36
|
+
for (const key of Object.keys(webStream)) {
|
|
37
|
+
if (!(key in global)) {
|
|
38
|
+
global[key] = webStream[key];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function getForwardedHeader(req, name) {
|
|
42
|
+
const value = req.headers["x-forwarded-" + name];
|
|
43
|
+
if (value) {
|
|
44
|
+
if (typeof value === "string") {
|
|
45
|
+
const index = value.indexOf(",");
|
|
46
|
+
return index < 0 ? value : value.slice(0, index);
|
|
47
|
+
}
|
|
48
|
+
return value[0];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function getOrigin(req, protocol, host, trustProxy) {
|
|
52
|
+
var _a;
|
|
53
|
+
protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
|
|
54
|
+
host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
|
|
55
|
+
if (!host) {
|
|
56
|
+
if (process.env.NODE_ENV !== "production") {
|
|
57
|
+
host = "localhost";
|
|
58
|
+
console.warn(
|
|
59
|
+
`Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
60
|
+
);
|
|
61
|
+
} else {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return `${protocol}://${host}`;
|
|
68
|
+
}
|
|
69
|
+
function createMiddleware(router, options = {}) {
|
|
70
|
+
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
71
|
+
let { origin = process.env.ORIGIN } = options;
|
|
72
|
+
let protocol;
|
|
73
|
+
let host;
|
|
74
|
+
if (origin) {
|
|
75
|
+
({ protocol, host } = new URL(origin));
|
|
76
|
+
protocol = protocol.slice(0, -1);
|
|
77
|
+
}
|
|
78
|
+
return async (req, res, next) => {
|
|
79
|
+
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
80
|
+
const url = new URL(req.url, origin);
|
|
81
|
+
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
82
|
+
const requestContext = {
|
|
83
|
+
method: req.method,
|
|
84
|
+
url,
|
|
85
|
+
platform: {
|
|
86
|
+
ip,
|
|
87
|
+
request: req,
|
|
88
|
+
response: res,
|
|
89
|
+
setCookie(cookie) {
|
|
90
|
+
res.appendHeader("set-cookie", cookie);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
Object.defineProperty(requestContext, "request", {
|
|
95
|
+
get() {
|
|
96
|
+
const headers = req.headers;
|
|
97
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
98
|
+
start(controller) {
|
|
99
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
100
|
+
req.on("end", () => controller.close());
|
|
101
|
+
req.on("error", (err) => controller.error(err));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
const request = new Request(url, {
|
|
105
|
+
method: req.method,
|
|
106
|
+
headers,
|
|
107
|
+
body,
|
|
108
|
+
duplex: "half"
|
|
109
|
+
});
|
|
110
|
+
Object.defineProperty(this, "request", {
|
|
111
|
+
value: request,
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: true
|
|
114
|
+
});
|
|
115
|
+
return request;
|
|
116
|
+
},
|
|
117
|
+
enumerable: true,
|
|
118
|
+
configurable: true
|
|
119
|
+
});
|
|
120
|
+
const response = await router(requestContext);
|
|
121
|
+
if (!response) {
|
|
122
|
+
if (next) {
|
|
123
|
+
next();
|
|
124
|
+
} else {
|
|
125
|
+
res.statusCode = 404;
|
|
126
|
+
res.setHeader("content-length", "0");
|
|
127
|
+
res.end();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
res.statusCode = response.status;
|
|
133
|
+
for (const [key, value] of response.headers) {
|
|
134
|
+
if (key === "set-cookie") {
|
|
135
|
+
let sepIndex = value.indexOf(",") + 1;
|
|
136
|
+
if (!sepIndex) {
|
|
137
|
+
res.setHeader(key, value);
|
|
138
|
+
} else {
|
|
139
|
+
let index = 0;
|
|
140
|
+
do {
|
|
141
|
+
res.appendHeader(key, value.slice(index, sepIndex - 1));
|
|
142
|
+
index = sepIndex;
|
|
143
|
+
sepIndex = value.indexOf(",", sepIndex) + 1;
|
|
144
|
+
} while (sepIndex);
|
|
145
|
+
res.appendHeader(key, value.slice(index));
|
|
146
|
+
}
|
|
147
|
+
} else {
|
|
148
|
+
res.setHeader(key, value);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (!response.body) {
|
|
152
|
+
if (!response.headers.has("content-length")) {
|
|
153
|
+
res.setHeader("content-length", "0");
|
|
154
|
+
}
|
|
155
|
+
res.end();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const reader = response.body.getReader();
|
|
159
|
+
if (res.destroyed) {
|
|
160
|
+
reader.cancel();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
res.on("close", cancel);
|
|
164
|
+
res.on("error", cancel);
|
|
165
|
+
write();
|
|
166
|
+
function cancel(error) {
|
|
167
|
+
res.off("close", cancel);
|
|
168
|
+
res.off("error", cancel);
|
|
169
|
+
reader.cancel(error).catch(() => {
|
|
170
|
+
});
|
|
171
|
+
error && res.destroy(error);
|
|
172
|
+
}
|
|
173
|
+
async function write() {
|
|
174
|
+
try {
|
|
175
|
+
while (true) {
|
|
176
|
+
const { done, value } = await reader.read();
|
|
177
|
+
if (done) {
|
|
178
|
+
res.end();
|
|
179
|
+
return;
|
|
180
|
+
} else if (!res.write(value)) {
|
|
181
|
+
res.once("drain", write);
|
|
182
|
+
return;
|
|
183
|
+
} else if (res.flush) {
|
|
184
|
+
res.flush();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
const error = err instanceof Error ? err : new Error("Error while writing to node response", {
|
|
189
|
+
cause: err
|
|
190
|
+
});
|
|
191
|
+
cancel(error);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
197
|
+
0 && (module.exports = {
|
|
198
|
+
getOrigin
|
|
199
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Router } from "../runtime";
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from "http";
|
|
3
|
+
declare module "net" {
|
|
4
|
+
interface Socket {
|
|
5
|
+
encrypted?: boolean;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
declare module "http" {
|
|
9
|
+
interface IncomingMessage {
|
|
10
|
+
ip?: string;
|
|
11
|
+
protocol?: string;
|
|
12
|
+
}
|
|
13
|
+
interface ServerResponse {
|
|
14
|
+
appendHeader(key: string, value: string | string[]): this;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export interface NodePlatformInfo {
|
|
18
|
+
ip: string;
|
|
19
|
+
request: IncomingMessage;
|
|
20
|
+
response: ServerResponse;
|
|
21
|
+
setCookie(cookie: string): void;
|
|
22
|
+
}
|
|
23
|
+
/** Connect/Express style request listener/middleware */
|
|
24
|
+
export declare type NodeMiddleware = (req: IncomingMessage, res: ServerResponse & {
|
|
25
|
+
flush?: () => void;
|
|
26
|
+
}, next?: () => void) => void;
|
|
27
|
+
/** Adapter options */
|
|
28
|
+
export interface NodeAdapterOptions {
|
|
29
|
+
/**
|
|
30
|
+
* Set the origin part of the URL to a constant value.
|
|
31
|
+
* It defaults to `process.env.ORIGIN`. If neither is set,
|
|
32
|
+
* the origin is computed from the protocol and hostname.
|
|
33
|
+
* To determine the protocol, `req.protocol` is tried first.
|
|
34
|
+
* If `trustProxy` is set, `X-Forwarded-Proto` header is used.
|
|
35
|
+
* Otherwise, `req.socket.encrypted` is used.
|
|
36
|
+
* To determine the hostname, `X-Forwarded-Host`
|
|
37
|
+
* (if `trustProxy` is set) or `Host` header is used.
|
|
38
|
+
*/
|
|
39
|
+
origin?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Whether to trust `X-Forwarded-*` headers. `X-Forwarded-Proto`
|
|
42
|
+
* and `X-Forwarded-Host` are used to determine the origin when
|
|
43
|
+
* `origin` and `process.env.ORIGIN` are not set. `X-Forwarded-For`
|
|
44
|
+
* is used to determine the IP address. The leftmost values are used
|
|
45
|
+
* if multiple values are set. Defaults to true if `process.env.TRUST_PROXY`
|
|
46
|
+
* is set to `1`, otherwise false.
|
|
47
|
+
*/
|
|
48
|
+
trustProxy?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export declare function getOrigin(req: IncomingMessage, protocol?: string, host?: string, trustProxy?: boolean): string;
|
|
51
|
+
/**
|
|
52
|
+
* Creates a request handler to be passed to http.createServer() or used as a
|
|
53
|
+
* middleware in Connect-style frameworks like Express.
|
|
54
|
+
*/
|
|
55
|
+
export default function createMiddleware(router: Router<NodePlatformInfo>, options?: NodeAdapterOptions): NodeMiddleware;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// src/adapter/middleware.ts
|
|
2
|
+
import * as webStream from "stream/web";
|
|
3
|
+
import installCrypto from "@hattip/polyfills/crypto";
|
|
4
|
+
installCrypto();
|
|
5
|
+
for (const key of Object.keys(webStream)) {
|
|
6
|
+
if (!(key in global)) {
|
|
7
|
+
global[key] = webStream[key];
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function getForwardedHeader(req, name) {
|
|
11
|
+
const value = req.headers["x-forwarded-" + name];
|
|
12
|
+
if (value) {
|
|
13
|
+
if (typeof value === "string") {
|
|
14
|
+
const index = value.indexOf(",");
|
|
15
|
+
return index < 0 ? value : value.slice(0, index);
|
|
16
|
+
}
|
|
17
|
+
return value[0];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getOrigin(req, protocol, host, trustProxy) {
|
|
21
|
+
var _a;
|
|
22
|
+
protocol ?? (protocol = req.protocol || trustProxy && getForwardedHeader(req, "proto") || ((_a = req.socket) == null ? void 0 : _a.encrypted) && "https" || "http");
|
|
23
|
+
host ?? (host = trustProxy && getForwardedHeader(req, "host") || req.headers.host);
|
|
24
|
+
if (!host) {
|
|
25
|
+
if (process.env.NODE_ENV !== "production") {
|
|
26
|
+
host = "localhost";
|
|
27
|
+
console.warn(
|
|
28
|
+
`Could not automatically determine the origin host, using 'localhost'. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
29
|
+
);
|
|
30
|
+
} else {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Could not automatically determine the origin host. Use the 'origin' option or the 'ORIGIN' environment variable to set the origin explicitly.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return `${protocol}://${host}`;
|
|
37
|
+
}
|
|
38
|
+
function createMiddleware(router, options = {}) {
|
|
39
|
+
const { trustProxy = process.env.TRUST_PROXY === "1" } = options;
|
|
40
|
+
let { origin = process.env.ORIGIN } = options;
|
|
41
|
+
let protocol;
|
|
42
|
+
let host;
|
|
43
|
+
if (origin) {
|
|
44
|
+
({ protocol, host } = new URL(origin));
|
|
45
|
+
protocol = protocol.slice(0, -1);
|
|
46
|
+
}
|
|
47
|
+
return async (req, res, next) => {
|
|
48
|
+
origin ?? (origin = getOrigin(req, protocol, host, trustProxy));
|
|
49
|
+
const url = new URL(req.url, origin);
|
|
50
|
+
const ip = req.ip || trustProxy && getForwardedHeader(req, "for") || req.socket.remoteAddress || "";
|
|
51
|
+
const requestContext = {
|
|
52
|
+
method: req.method,
|
|
53
|
+
url,
|
|
54
|
+
platform: {
|
|
55
|
+
ip,
|
|
56
|
+
request: req,
|
|
57
|
+
response: res,
|
|
58
|
+
setCookie(cookie) {
|
|
59
|
+
res.appendHeader("set-cookie", cookie);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
Object.defineProperty(requestContext, "request", {
|
|
64
|
+
get() {
|
|
65
|
+
const headers = req.headers;
|
|
66
|
+
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : req.socket ? req : new ReadableStream({
|
|
67
|
+
start(controller) {
|
|
68
|
+
req.on("data", (chunk) => controller.enqueue(chunk));
|
|
69
|
+
req.on("end", () => controller.close());
|
|
70
|
+
req.on("error", (err) => controller.error(err));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const request = new Request(url, {
|
|
74
|
+
method: req.method,
|
|
75
|
+
headers,
|
|
76
|
+
body,
|
|
77
|
+
duplex: "half"
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(this, "request", {
|
|
80
|
+
value: request,
|
|
81
|
+
enumerable: true,
|
|
82
|
+
configurable: true
|
|
83
|
+
});
|
|
84
|
+
return request;
|
|
85
|
+
},
|
|
86
|
+
enumerable: true,
|
|
87
|
+
configurable: true
|
|
88
|
+
});
|
|
89
|
+
const response = await router(requestContext);
|
|
90
|
+
if (!response) {
|
|
91
|
+
if (next) {
|
|
92
|
+
next();
|
|
93
|
+
} else {
|
|
94
|
+
res.statusCode = 404;
|
|
95
|
+
res.setHeader("content-length", "0");
|
|
96
|
+
res.end();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
res.statusCode = response.status;
|
|
102
|
+
for (const [key, value] of response.headers) {
|
|
103
|
+
if (key === "set-cookie") {
|
|
104
|
+
let sepIndex = value.indexOf(",") + 1;
|
|
105
|
+
if (!sepIndex) {
|
|
106
|
+
res.setHeader(key, value);
|
|
107
|
+
} else {
|
|
108
|
+
let index = 0;
|
|
109
|
+
do {
|
|
110
|
+
res.appendHeader(key, value.slice(index, sepIndex - 1));
|
|
111
|
+
index = sepIndex;
|
|
112
|
+
sepIndex = value.indexOf(",", sepIndex) + 1;
|
|
113
|
+
} while (sepIndex);
|
|
114
|
+
res.appendHeader(key, value.slice(index));
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
res.setHeader(key, value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!response.body) {
|
|
121
|
+
if (!response.headers.has("content-length")) {
|
|
122
|
+
res.setHeader("content-length", "0");
|
|
123
|
+
}
|
|
124
|
+
res.end();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const reader = response.body.getReader();
|
|
128
|
+
if (res.destroyed) {
|
|
129
|
+
reader.cancel();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
res.on("close", cancel);
|
|
133
|
+
res.on("error", cancel);
|
|
134
|
+
write();
|
|
135
|
+
function cancel(error) {
|
|
136
|
+
res.off("close", cancel);
|
|
137
|
+
res.off("error", cancel);
|
|
138
|
+
reader.cancel(error).catch(() => {
|
|
139
|
+
});
|
|
140
|
+
error && res.destroy(error);
|
|
141
|
+
}
|
|
142
|
+
async function write() {
|
|
143
|
+
try {
|
|
144
|
+
while (true) {
|
|
145
|
+
const { done, value } = await reader.read();
|
|
146
|
+
if (done) {
|
|
147
|
+
res.end();
|
|
148
|
+
return;
|
|
149
|
+
} else if (!res.write(value)) {
|
|
150
|
+
res.once("drain", write);
|
|
151
|
+
return;
|
|
152
|
+
} else if (res.flush) {
|
|
153
|
+
res.flush();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
const error = err instanceof Error ? err : new Error("Error while writing to node response", {
|
|
158
|
+
cause: err
|
|
159
|
+
});
|
|
160
|
+
cancel(error);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
createMiddleware as default,
|
|
167
|
+
getOrigin
|
|
168
|
+
};
|