srvx 0.10.0 → 0.11.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.
- package/README.md +6 -20
- package/bin/srvx.mjs +5 -3
- package/dist/_chunks/_plugins.mjs +14 -11
- package/dist/_chunks/_url.mjs +33 -25
- package/dist/_chunks/_utils.mjs +14 -71
- package/dist/_chunks/_utils2.mjs +70 -0
- package/dist/_chunks/loader.d.mts +74 -0
- package/dist/_chunks/loader.mjs +108 -0
- package/dist/adapters/aws-lambda.d.mts +19 -0
- package/dist/adapters/aws-lambda.mjs +292 -0
- package/dist/adapters/bun.d.mts +1 -1
- package/dist/adapters/bun.mjs +2 -6
- package/dist/adapters/cloudflare.d.mts +1 -1
- package/dist/adapters/cloudflare.mjs +1 -5
- package/dist/adapters/deno.d.mts +1 -1
- package/dist/adapters/deno.mjs +2 -6
- package/dist/adapters/generic.d.mts +1 -1
- package/dist/adapters/generic.mjs +2 -6
- package/dist/adapters/node.d.mts +1 -1
- package/dist/adapters/node.mjs +165 -86
- package/dist/adapters/service-worker.d.mts +1 -1
- package/dist/adapters/service-worker.mjs +2 -6
- package/dist/cli.d.mts +46 -11
- package/dist/cli.mjs +323 -272
- package/dist/loader.d.mts +2 -0
- package/dist/loader.mjs +2 -0
- package/dist/log.d.mts +1 -1
- package/dist/log.mjs +2 -6
- package/dist/static.d.mts +1 -1
- package/dist/static.mjs +4 -8
- package/dist/tracing.d.mts +1 -2
- package/dist/tracing.mjs +1 -25
- package/dist/types.d.mts +302 -1
- package/package.json +41 -37
- package/dist/_chunks/_color.mjs +0 -18
- package/dist/_chunks/_inherit.mjs +0 -31
- package/dist/_chunks/call.mjs +0 -157
- package/dist/_chunks/call2.mjs +0 -3
- package/dist/_chunks/types.d.mts +0 -283
package/README.md
CHANGED
|
@@ -41,29 +41,15 @@ $ deno -A npm:srvx
|
|
|
41
41
|
$ bunx --bun srvx
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Starter Examples
|
|
44
|
+
You can also use `srvx fetch` to directly call your server handler without starting a server:
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
```bash
|
|
47
|
+
$ npx srvx fetch /api/users
|
|
48
|
+
```
|
|
51
49
|
|
|
52
|
-
|
|
53
|
-
| ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
|
|
54
|
-
| `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` |
|
|
55
|
-
| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` |
|
|
56
|
-
| `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` |
|
|
57
|
-
| `h3` | [examples/h3](https://github.com/h3js/srvx/tree/main/examples/h3/) | `npx giget gh:h3js/srvx/examples/h3 srvx-h3` |
|
|
58
|
-
| `hello-world` | [examples/hello-world](https://github.com/h3js/srvx/tree/main/examples/hello-world/) | `npx giget gh:h3js/srvx/examples/hello-world srvx-hello-world` |
|
|
59
|
-
| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` |
|
|
60
|
-
| `jsx` | [examples/jsx](https://github.com/h3js/srvx/tree/main/examples/jsx/) | `npx giget gh:h3js/srvx/examples/jsx srvx-jsx` |
|
|
61
|
-
| `node-handler` | [examples/node-handler](https://github.com/h3js/srvx/tree/main/examples/node-handler/) | `npx giget gh:h3js/srvx/examples/node-handler srvx-node-handler` |
|
|
62
|
-
| `service-worker` | [examples/service-worker](https://github.com/h3js/srvx/tree/main/examples/service-worker/) | `npx giget gh:h3js/srvx/examples/service-worker srvx-service-worker` |
|
|
63
|
-
| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` |
|
|
64
|
-
| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` |
|
|
50
|
+
See [CLI documentation](https://srvx.h3.dev/guide/cli) for more options.
|
|
65
51
|
|
|
66
|
-
|
|
52
|
+
👉 **Visit the 📖 [Documentation](https://srvx.h3.dev/) to learn more.**
|
|
67
53
|
|
|
68
54
|
## Contribution
|
|
69
55
|
|
package/bin/srvx.mjs
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { main } from "../dist/cli.mjs";
|
|
3
3
|
|
|
4
4
|
await main({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
usage: {
|
|
6
|
+
command: "srvx",
|
|
7
|
+
docs: "https://srvx.h3.dev",
|
|
8
|
+
issues: "https://github.com/h3js/srvx/issues",
|
|
9
|
+
},
|
|
8
10
|
});
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { i as gray,
|
|
2
|
-
|
|
3
|
-
//#region src/_middleware.ts
|
|
1
|
+
import { i as gray, o as red } from "./_utils.mjs";
|
|
4
2
|
function wrapFetch(server) {
|
|
5
3
|
const fetchHandler = server.options.fetch;
|
|
6
4
|
const middleware = server.options.middleware || [];
|
|
@@ -10,9 +8,6 @@ function callMiddleware(request, fetchHandler, middleware, index) {
|
|
|
10
8
|
if (index === middleware.length) return fetchHandler(request);
|
|
11
9
|
return middleware[index](request, () => callMiddleware(request, fetchHandler, middleware, index + 1));
|
|
12
10
|
}
|
|
13
|
-
|
|
14
|
-
//#endregion
|
|
15
|
-
//#region src/_plugins.ts
|
|
16
11
|
const errorPlugin = (server) => {
|
|
17
12
|
const errorHandler = server.options.error;
|
|
18
13
|
if (!errorHandler) return;
|
|
@@ -31,16 +26,26 @@ const gracefulShutdownPlugin = (server) => {
|
|
|
31
26
|
const gracefulShutdown = config === true || !config?.gracefulTimeout ? Number.parseInt(process.env.SERVER_SHUTDOWN_TIMEOUT || "") || 3 : config.gracefulTimeout;
|
|
32
27
|
const forceShutdown = config === true || !config?.forceTimeout ? Number.parseInt(process.env.SERVER_FORCE_SHUTDOWN_TIMEOUT || "") || 5 : config.forceTimeout;
|
|
33
28
|
let isShuttingDown = false;
|
|
29
|
+
let forceClose;
|
|
34
30
|
const shutdown = async () => {
|
|
35
|
-
if (isShuttingDown)
|
|
31
|
+
if (isShuttingDown) {
|
|
32
|
+
forceClose?.();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
36
35
|
isShuttingDown = true;
|
|
37
36
|
const w = process.stderr.write.bind(process.stderr);
|
|
38
|
-
w(gray(`\nShutting down server in ${gracefulShutdown}s
|
|
37
|
+
w(gray(`\nShutting down server in ${gracefulShutdown}s... (press Ctrl+C again to force close)`));
|
|
39
38
|
let timeout;
|
|
40
39
|
await Promise.race([server.close().finally(() => {
|
|
41
40
|
clearTimeout(timeout);
|
|
42
41
|
w(gray(" Server closed.\n"));
|
|
43
42
|
}), new Promise((resolve) => {
|
|
43
|
+
forceClose = () => {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
w(gray("\nForce closing...\n"));
|
|
46
|
+
server.close(true);
|
|
47
|
+
resolve();
|
|
48
|
+
};
|
|
44
49
|
timeout = setTimeout(() => {
|
|
45
50
|
w(gray(`\nForce closing connections in ${forceShutdown}s...`));
|
|
46
51
|
timeout = setTimeout(() => {
|
|
@@ -54,6 +59,4 @@ const gracefulShutdownPlugin = (server) => {
|
|
|
54
59
|
};
|
|
55
60
|
for (const sig of ["SIGINT", "SIGTERM"]) globalThis.process.on(sig, shutdown);
|
|
56
61
|
};
|
|
57
|
-
|
|
58
|
-
//#endregion
|
|
59
|
-
export { gracefulShutdownPlugin as n, wrapFetch as r, errorPlugin as t };
|
|
62
|
+
export { gracefulShutdownPlugin as n, wrapFetch as r, errorPlugin as t };
|
package/dist/_chunks/_url.mjs
CHANGED
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
1
|
+
function lazyInherit(target, source, sourceKey) {
|
|
2
|
+
for (const key of [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]) {
|
|
3
|
+
if (key === "constructor") continue;
|
|
4
|
+
const targetDesc = Object.getOwnPropertyDescriptor(target, key);
|
|
5
|
+
const desc = Object.getOwnPropertyDescriptor(source, key);
|
|
6
|
+
let modified = false;
|
|
7
|
+
if (desc.get) {
|
|
8
|
+
modified = true;
|
|
9
|
+
desc.get = targetDesc?.get || function() {
|
|
10
|
+
return this[sourceKey][key];
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (desc.set) {
|
|
14
|
+
modified = true;
|
|
15
|
+
desc.set = targetDesc?.set || function(value) {
|
|
16
|
+
this[sourceKey][key] = value;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (!targetDesc?.value && typeof desc.value === "function") {
|
|
20
|
+
modified = true;
|
|
21
|
+
desc.value = function(...args) {
|
|
22
|
+
return this[sourceKey][key](...args);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (modified) Object.defineProperty(target, key, desc);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
18
28
|
const FastURL = /* @__PURE__ */ (() => {
|
|
19
29
|
const NativeURL = globalThis.URL;
|
|
20
|
-
const FastURL
|
|
30
|
+
const FastURL = class URL {
|
|
21
31
|
#url;
|
|
22
32
|
#href;
|
|
23
33
|
#protocol;
|
|
@@ -108,11 +118,9 @@ const FastURL = /* @__PURE__ */ (() => {
|
|
|
108
118
|
return this.href;
|
|
109
119
|
}
|
|
110
120
|
};
|
|
111
|
-
lazyInherit(FastURL
|
|
112
|
-
Object.setPrototypeOf(FastURL
|
|
113
|
-
Object.setPrototypeOf(FastURL
|
|
114
|
-
return FastURL
|
|
121
|
+
lazyInherit(FastURL.prototype, NativeURL.prototype, "_url");
|
|
122
|
+
Object.setPrototypeOf(FastURL.prototype, NativeURL.prototype);
|
|
123
|
+
Object.setPrototypeOf(FastURL, NativeURL);
|
|
124
|
+
return FastURL;
|
|
115
125
|
})();
|
|
116
|
-
|
|
117
|
-
//#endregion
|
|
118
|
-
export { FastURL as t };
|
|
126
|
+
export { lazyInherit as n, FastURL as t };
|
package/dist/_chunks/_utils.mjs
CHANGED
|
@@ -1,71 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
function printListening(opts, url) {
|
|
17
|
-
if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) return;
|
|
18
|
-
const _url = new URL(url);
|
|
19
|
-
const allInterfaces = _url.hostname === "[::]" || _url.hostname === "0.0.0.0";
|
|
20
|
-
if (allInterfaces) {
|
|
21
|
-
_url.hostname = "localhost";
|
|
22
|
-
url = _url.href;
|
|
23
|
-
}
|
|
24
|
-
let listeningOn = `➜ Listening on:`;
|
|
25
|
-
let additionalInfo = allInterfaces ? " (all interfaces)" : "";
|
|
26
|
-
if (globalThis.process.stdout?.isTTY) {
|
|
27
|
-
listeningOn = `\u001B[32m${listeningOn}\u001B[0m`;
|
|
28
|
-
url = `\u001B[36m${url}\u001B[0m`;
|
|
29
|
-
additionalInfo = `\u001B[2m${additionalInfo}\u001B[0m`;
|
|
30
|
-
}
|
|
31
|
-
console.log(`${listeningOn} ${url}${additionalInfo}`);
|
|
32
|
-
}
|
|
33
|
-
function resolveTLSOptions(opts) {
|
|
34
|
-
if (!opts.tls || opts.protocol === "http") return;
|
|
35
|
-
const cert = resolveCertOrKey(opts.tls.cert);
|
|
36
|
-
const key = resolveCertOrKey(opts.tls.key);
|
|
37
|
-
if (!cert && !key) {
|
|
38
|
-
if (opts.protocol === "https") throw new TypeError("TLS `cert` and `key` must be provided for `https` protocol.");
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (!cert || !key) throw new TypeError("TLS `cert` and `key` must be provided together.");
|
|
42
|
-
return {
|
|
43
|
-
cert,
|
|
44
|
-
key,
|
|
45
|
-
passphrase: opts.tls.passphrase
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function resolveCertOrKey(value) {
|
|
49
|
-
if (!value) return;
|
|
50
|
-
if (typeof value !== "string") throw new TypeError("TLS certificate and key must be strings in PEM format or file paths.");
|
|
51
|
-
if (value.startsWith("-----BEGIN ")) return value;
|
|
52
|
-
const { readFileSync } = process.getBuiltinModule("node:fs");
|
|
53
|
-
return readFileSync(value, "utf8");
|
|
54
|
-
}
|
|
55
|
-
function createWaitUntil() {
|
|
56
|
-
const promises = /* @__PURE__ */ new Set();
|
|
57
|
-
return {
|
|
58
|
-
waitUntil: (promise) => {
|
|
59
|
-
if (typeof promise?.then !== "function") return;
|
|
60
|
-
promises.add(Promise.resolve(promise).catch(console.error).finally(() => {
|
|
61
|
-
promises.delete(promise);
|
|
62
|
-
}));
|
|
63
|
-
},
|
|
64
|
-
wait: () => {
|
|
65
|
-
return Promise.all(promises);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
//#endregion
|
|
71
|
-
export { resolveTLSOptions as a, resolvePortAndHost as i, fmtURL as n, printListening as r, createWaitUntil as t };
|
|
1
|
+
const noColor = /* @__PURE__ */ (() => {
|
|
2
|
+
const env = globalThis.process?.env ?? {};
|
|
3
|
+
return env.NO_COLOR === "1" || env.TERM === "dumb";
|
|
4
|
+
})();
|
|
5
|
+
const _c = (c, r = 39) => (t) => noColor ? t : `\u001B[${c}m${t}\u001B[${r}m`;
|
|
6
|
+
const bold = /* @__PURE__ */ _c(1, 22);
|
|
7
|
+
const red = /* @__PURE__ */ _c(31);
|
|
8
|
+
const green = /* @__PURE__ */ _c(32);
|
|
9
|
+
const yellow = /* @__PURE__ */ _c(33);
|
|
10
|
+
const blue = /* @__PURE__ */ _c(34);
|
|
11
|
+
const cyan = /* @__PURE__ */ _c(36);
|
|
12
|
+
const gray = /* @__PURE__ */ _c(90);
|
|
13
|
+
const url = (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`;
|
|
14
|
+
export { green as a, yellow as c, gray as i, bold as n, red as o, cyan as r, url as s, blue as t };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function resolvePortAndHost(opts) {
|
|
2
|
+
const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
|
|
3
|
+
const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
|
|
4
|
+
if (port < 0 || port > 65535) throw new RangeError(`Port must be between 0 and 65535 (got "${port}").`);
|
|
5
|
+
return {
|
|
6
|
+
port,
|
|
7
|
+
hostname: opts.hostname ?? globalThis.process?.env.HOST
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function fmtURL(host, port, secure) {
|
|
11
|
+
if (!host || !port) return;
|
|
12
|
+
if (host.includes(":")) host = `[${host}]`;
|
|
13
|
+
return `http${secure ? "s" : ""}://${host}:${port}/`;
|
|
14
|
+
}
|
|
15
|
+
function printListening(opts, url) {
|
|
16
|
+
if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) return;
|
|
17
|
+
let additionalInfo = "";
|
|
18
|
+
try {
|
|
19
|
+
const _url = new URL(url);
|
|
20
|
+
if (_url.hostname === "[::]" || _url.hostname === "0.0.0.0") {
|
|
21
|
+
_url.hostname = "localhost";
|
|
22
|
+
url = _url.href;
|
|
23
|
+
additionalInfo = " (all interfaces)";
|
|
24
|
+
}
|
|
25
|
+
} catch {}
|
|
26
|
+
let listeningOn = `➜ Listening on:`;
|
|
27
|
+
if (globalThis.process.stdout?.isTTY) {
|
|
28
|
+
listeningOn = `\u001B[32m${listeningOn}\u001B[0m`;
|
|
29
|
+
url = `\u001B[36m${url}\u001B[0m`;
|
|
30
|
+
additionalInfo = `\u001B[2m${additionalInfo}\u001B[0m`;
|
|
31
|
+
}
|
|
32
|
+
console.log(`${listeningOn} ${url}${additionalInfo}`);
|
|
33
|
+
}
|
|
34
|
+
function resolveTLSOptions(opts) {
|
|
35
|
+
if (!opts.tls || opts.protocol === "http") return;
|
|
36
|
+
const cert = resolveCertOrKey(opts.tls.cert);
|
|
37
|
+
const key = resolveCertOrKey(opts.tls.key);
|
|
38
|
+
if (!cert && !key) {
|
|
39
|
+
if (opts.protocol === "https") throw new TypeError("TLS `cert` and `key` must be provided for `https` protocol.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (!cert || !key) throw new TypeError("TLS `cert` and `key` must be provided together.");
|
|
43
|
+
return {
|
|
44
|
+
cert,
|
|
45
|
+
key,
|
|
46
|
+
passphrase: opts.tls.passphrase
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function resolveCertOrKey(value) {
|
|
50
|
+
if (!value) return;
|
|
51
|
+
if (typeof value !== "string") throw new TypeError("TLS certificate and key must be strings in PEM format or file paths.");
|
|
52
|
+
if (value.startsWith("-----BEGIN ")) return value;
|
|
53
|
+
const { readFileSync } = process.getBuiltinModule("node:fs");
|
|
54
|
+
return readFileSync(value, "utf8");
|
|
55
|
+
}
|
|
56
|
+
function createWaitUntil() {
|
|
57
|
+
const promises = /* @__PURE__ */ new Set();
|
|
58
|
+
return {
|
|
59
|
+
waitUntil: (promise) => {
|
|
60
|
+
if (typeof promise?.then !== "function") return;
|
|
61
|
+
promises.add(Promise.resolve(promise).catch(console.error).finally(() => {
|
|
62
|
+
promises.delete(promise);
|
|
63
|
+
}));
|
|
64
|
+
},
|
|
65
|
+
wait: () => {
|
|
66
|
+
return Promise.all(promises);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export { resolveTLSOptions as a, resolvePortAndHost as i, fmtURL as n, printListening as r, createWaitUntil as t };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { ServerHandler } from "srvx";
|
|
2
|
+
|
|
3
|
+
//#region src/loader.d.ts
|
|
4
|
+
declare const defaultExts: string[];
|
|
5
|
+
declare const defaultEntries: string[];
|
|
6
|
+
/**
|
|
7
|
+
* Options for loading a server entry module.
|
|
8
|
+
*/
|
|
9
|
+
type LoadOptions = {
|
|
10
|
+
/**
|
|
11
|
+
* Path or URL to the server entry file.
|
|
12
|
+
*
|
|
13
|
+
* If not provided, common entry points will be searched automatically.
|
|
14
|
+
*/
|
|
15
|
+
entry?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base directory for resolving relative paths.
|
|
18
|
+
*
|
|
19
|
+
* @default "."
|
|
20
|
+
*/
|
|
21
|
+
dir?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Set to `false` to disable interception of `http.Server.listen` to detect legacy handlers.
|
|
24
|
+
*
|
|
25
|
+
* @default true
|
|
26
|
+
*/
|
|
27
|
+
interceptHttpListen?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Set to `false` to disable Node.js handler (req, res) compatibility.
|
|
30
|
+
*/
|
|
31
|
+
nodeCompat?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Hook called after the module is loaded to allow for custom processing.
|
|
34
|
+
*
|
|
35
|
+
* You can return a modified version of the module if needed.
|
|
36
|
+
*/
|
|
37
|
+
onLoad?: (module: unknown) => any;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Result of loading a server entry module.
|
|
41
|
+
*/
|
|
42
|
+
type LoadedServerEntry = {
|
|
43
|
+
/**
|
|
44
|
+
* The web fetch handler extracted from the loaded module.
|
|
45
|
+
*
|
|
46
|
+
* This is resolved from `module.fetch`, `module.default.fetch`,
|
|
47
|
+
* or upgraded from a legacy Node.js handler.
|
|
48
|
+
*/
|
|
49
|
+
fetch?: ServerHandler;
|
|
50
|
+
/**
|
|
51
|
+
* The raw loaded module.
|
|
52
|
+
*/
|
|
53
|
+
module?: any;
|
|
54
|
+
/**
|
|
55
|
+
* Whether the handler was upgraded from a legacy Node.js HTTP handler.
|
|
56
|
+
*
|
|
57
|
+
* When `true`, the original module exported a Node.js-style `(req, res)` handler
|
|
58
|
+
* that has been wrapped for web fetch compatibility.
|
|
59
|
+
*/
|
|
60
|
+
nodeCompat?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* The resolved `file://` URL of the loaded entry module.
|
|
63
|
+
*/
|
|
64
|
+
url?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Whether the specified entry file was not found.
|
|
67
|
+
*
|
|
68
|
+
* When `true`, no valid entry point could be located.
|
|
69
|
+
*/
|
|
70
|
+
notFound?: boolean;
|
|
71
|
+
};
|
|
72
|
+
declare function loadServerEntry(opts: LoadOptions): Promise<LoadedServerEntry>;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { loadServerEntry as a, defaultExts as i, LoadedServerEntry as n, defaultEntries as r, LoadOptions as t };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import * as nodeHTTP$1 from "node:http";
|
|
5
|
+
const defaultExts = [
|
|
6
|
+
".mjs",
|
|
7
|
+
".js",
|
|
8
|
+
".mts",
|
|
9
|
+
".ts"
|
|
10
|
+
];
|
|
11
|
+
const defaultEntries = [
|
|
12
|
+
"server",
|
|
13
|
+
"server/index",
|
|
14
|
+
"src/server",
|
|
15
|
+
"server/server"
|
|
16
|
+
];
|
|
17
|
+
async function loadServerEntry(opts) {
|
|
18
|
+
let entry = opts.entry;
|
|
19
|
+
if (entry) {
|
|
20
|
+
entry = resolve(opts.dir || ".", entry);
|
|
21
|
+
if (!existsSync(entry)) return { notFound: true };
|
|
22
|
+
} else {
|
|
23
|
+
for (const defEntry of defaultEntries) {
|
|
24
|
+
for (const defExt of defaultExts) {
|
|
25
|
+
const entryPath = resolve(opts.dir || ".", `${defEntry}${defExt}`);
|
|
26
|
+
if (existsSync(entryPath)) {
|
|
27
|
+
entry = entryPath;
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (entry) break;
|
|
32
|
+
}
|
|
33
|
+
if (!entry) return { notFound: true };
|
|
34
|
+
}
|
|
35
|
+
const url = entry.startsWith("file://") ? entry : pathToFileURL(resolve(entry)).href;
|
|
36
|
+
let mod;
|
|
37
|
+
let listenHandler;
|
|
38
|
+
try {
|
|
39
|
+
if (opts.interceptHttpListen !== false) {
|
|
40
|
+
const loaded = await interceptListen(() => import(url));
|
|
41
|
+
mod = loaded.res;
|
|
42
|
+
listenHandler = loaded.listenHandler;
|
|
43
|
+
} else mod = await import(url);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
if (error?.code === "ERR_UNKNOWN_FILE_EXTENSION") {
|
|
46
|
+
const message = String(error);
|
|
47
|
+
if (/"\.(m|c)?ts"/g.test(message)) throw new Error(`Make sure you're using Node.js v22.18+ or v24+ for TypeScript support (current version: ${process.versions.node})`, { cause: error });
|
|
48
|
+
else if (/"\.(m|c)?tsx"/g.test(message)) throw new Error(`You need a compatible loader for JSX support (Deno, Bun or srvx --register jiti/register)`, { cause: error });
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
mod = await opts?.onLoad?.(mod) || mod;
|
|
53
|
+
let fetchHandler = mod?.fetch || mod?.default?.fetch || mod?.default?.default?.fetch;
|
|
54
|
+
if (!fetchHandler && typeof mod?.default === "function" && mod.default.length < 2) fetchHandler = mod.default;
|
|
55
|
+
let nodeCompat = false;
|
|
56
|
+
if (!fetchHandler && opts.nodeCompat !== false) {
|
|
57
|
+
const nodeHandler = listenHandler || (typeof mod?.default === "function" ? mod.default : void 0);
|
|
58
|
+
if (nodeHandler) {
|
|
59
|
+
nodeCompat = true;
|
|
60
|
+
const { fetchNodeHandler } = await import("srvx/node");
|
|
61
|
+
fetchHandler = (webReq) => fetchNodeHandler(nodeHandler, webReq);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
module: mod,
|
|
66
|
+
nodeCompat,
|
|
67
|
+
url,
|
|
68
|
+
fetch: fetchHandler
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
let _interceptQueue = Promise.resolve();
|
|
72
|
+
async function interceptListen(cb) {
|
|
73
|
+
const result = _interceptQueue.then(async () => {
|
|
74
|
+
const originalListen = nodeHTTP$1.Server.prototype.listen;
|
|
75
|
+
let res;
|
|
76
|
+
let listenHandler;
|
|
77
|
+
try {
|
|
78
|
+
nodeHTTP$1.Server.prototype.listen = function(arg1, arg2) {
|
|
79
|
+
listenHandler = this._events.request;
|
|
80
|
+
if (Array.isArray(listenHandler)) listenHandler = listenHandler[0];
|
|
81
|
+
nodeHTTP$1.Server.prototype.listen = originalListen;
|
|
82
|
+
const listenCallback = [arg1, arg2].find((arg) => typeof arg === "function");
|
|
83
|
+
setImmediate(() => {
|
|
84
|
+
listenCallback?.();
|
|
85
|
+
});
|
|
86
|
+
return new Proxy({}, { get(_, prop) {
|
|
87
|
+
const server = globalThis.__srvx__;
|
|
88
|
+
if (!server && prop === "address") return () => ({
|
|
89
|
+
address: "",
|
|
90
|
+
family: "",
|
|
91
|
+
port: 0
|
|
92
|
+
});
|
|
93
|
+
return server?.node?.server?.[prop];
|
|
94
|
+
} });
|
|
95
|
+
};
|
|
96
|
+
res = await cb();
|
|
97
|
+
} finally {
|
|
98
|
+
nodeHTTP$1.Server.prototype.listen = originalListen;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
res,
|
|
102
|
+
listenHandler
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
_interceptQueue = result.catch(() => {});
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
export { defaultExts as n, loadServerEntry as r, defaultEntries as t };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FetchHandler, ServerOptions } from "../types.mjs";
|
|
2
|
+
import * as AWS from "aws-lambda";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/_aws/utils.d.ts
|
|
5
|
+
type AWSLambdaResponseStream = NodeJS.WritableStream & {
|
|
6
|
+
setContentType(contentType: string): void;
|
|
7
|
+
};
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/adapters/aws-lambda.d.ts
|
|
10
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
11
|
+
type AwsLambdaEvent = AWS.APIGatewayProxyEvent | AWS.APIGatewayProxyEventV2;
|
|
12
|
+
type AWSLambdaHandler = (event: AwsLambdaEvent, context: AWS.Context) => MaybePromise<AWS.APIGatewayProxyResult | AWS.APIGatewayProxyResultV2>;
|
|
13
|
+
type AWSLambdaStreamingHandler = (event: AwsLambdaEvent, responseStream: AWSLambdaResponseStream, context: AWS.Context) => MaybePromise<void>;
|
|
14
|
+
declare function toLambdaHandler(options: ServerOptions): AWSLambdaHandler;
|
|
15
|
+
declare function handleLambdaEvent(fetchHandler: FetchHandler, event: AwsLambdaEvent, context: AWS.Context): Promise<AWS.APIGatewayProxyResult | AWS.APIGatewayProxyResultV2>;
|
|
16
|
+
declare function handleLambdaEventWithStream(fetchHandler: FetchHandler, event: AwsLambdaEvent, responseStream: AWSLambdaResponseStream, context: AWS.Context): Promise<void>;
|
|
17
|
+
declare function invokeLambdaHandler(handler: AWSLambdaHandler, request: Request): Promise<Response>;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { AWSLambdaHandler, type AWSLambdaResponseStream, AWSLambdaStreamingHandler, AwsLambdaEvent, handleLambdaEvent, handleLambdaEventWithStream, invokeLambdaHandler, toLambdaHandler };
|