srvx 0.11.0 → 0.11.2
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/bin/srvx.mjs +2 -0
- package/dist/_chunks/_plugins.mjs +20 -31
- package/dist/_chunks/_utils.mjs +2 -1
- package/dist/cli.d.mts +5 -0
- package/dist/cli.mjs +68 -48
- package/dist/log.mjs +1 -1
- package/dist/tracing.d.mts +1 -1
- package/dist/tracing.mjs +1 -1
- package/package.json +2 -2
package/bin/srvx.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as gray,
|
|
1
|
+
import { a as green, i as gray, l as yellow, n as bold, s as red } from "./_utils.mjs";
|
|
2
2
|
function wrapFetch(server) {
|
|
3
3
|
const fetchHandler = server.options.fetch;
|
|
4
4
|
const middleware = server.options.middleware || [];
|
|
@@ -23,39 +23,28 @@ const errorPlugin = (server) => {
|
|
|
23
23
|
const gracefulShutdownPlugin = (server) => {
|
|
24
24
|
const config = server.options?.gracefulShutdown;
|
|
25
25
|
if (!globalThis.process?.on || config === false || config === void 0 && (process.env.CI || process.env.TEST)) return;
|
|
26
|
-
const
|
|
27
|
-
const forceShutdown = config === true || !config?.forceTimeout ? Number.parseInt(process.env.SERVER_FORCE_SHUTDOWN_TIMEOUT || "") || 5 : config.forceTimeout;
|
|
26
|
+
const gracefulTimeout = config === true || !config?.gracefulTimeout ? Number.parseInt(process.env.SERVER_SHUTDOWN_TIMEOUT || "") || 5 : config.gracefulTimeout;
|
|
28
27
|
let isShuttingDown = false;
|
|
29
|
-
|
|
28
|
+
const w = server.options.silent ? () => {} : process.stderr.write.bind(process.stderr);
|
|
29
|
+
const forceClose = async () => {
|
|
30
|
+
w(red("\x1B[2K\rForcibly closing connections...\n"));
|
|
31
|
+
await server.close(true);
|
|
32
|
+
w(yellow("Server closed.\n"));
|
|
33
|
+
globalThis.process.exit(0);
|
|
34
|
+
};
|
|
30
35
|
const shutdown = async () => {
|
|
31
|
-
if (isShuttingDown)
|
|
32
|
-
forceClose?.();
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
36
|
+
if (isShuttingDown) return forceClose();
|
|
35
37
|
isShuttingDown = true;
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
server.close(true);
|
|
47
|
-
resolve();
|
|
48
|
-
};
|
|
49
|
-
timeout = setTimeout(() => {
|
|
50
|
-
w(gray(`\nForce closing connections in ${forceShutdown}s...`));
|
|
51
|
-
timeout = setTimeout(() => {
|
|
52
|
-
w(red("\nCould not close connections in time, force exiting."));
|
|
53
|
-
resolve();
|
|
54
|
-
}, forceShutdown * 1e3);
|
|
55
|
-
return server.close(true);
|
|
56
|
-
}, gracefulShutdown * 1e3);
|
|
57
|
-
})]);
|
|
58
|
-
globalThis.process.exit(0);
|
|
38
|
+
const closePromise = server.close();
|
|
39
|
+
for (let remaining = gracefulTimeout; remaining > 0; remaining--) {
|
|
40
|
+
w(gray(`\rStopping server gracefully (${remaining}s)... Press ${bold("Ctrl+C")} again to force close.`));
|
|
41
|
+
if (await Promise.race([closePromise.then(() => true), new Promise((r) => setTimeout(() => r(false), 1e3))])) {
|
|
42
|
+
w("\x1B[2K\r" + green("Server closed successfully.\n"));
|
|
43
|
+
globalThis.process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
w("\x1B[2K\rGraceful shutdown timed out.\n");
|
|
47
|
+
await forceClose();
|
|
59
48
|
};
|
|
60
49
|
for (const sig of ["SIGINT", "SIGTERM"]) globalThis.process.on(sig, shutdown);
|
|
61
50
|
};
|
package/dist/_chunks/_utils.mjs
CHANGED
|
@@ -8,7 +8,8 @@ const red = /* @__PURE__ */ _c(31);
|
|
|
8
8
|
const green = /* @__PURE__ */ _c(32);
|
|
9
9
|
const yellow = /* @__PURE__ */ _c(33);
|
|
10
10
|
const blue = /* @__PURE__ */ _c(34);
|
|
11
|
+
const magenta = /* @__PURE__ */ _c(35);
|
|
11
12
|
const cyan = /* @__PURE__ */ _c(36);
|
|
12
13
|
const gray = /* @__PURE__ */ _c(90);
|
|
13
14
|
const url = (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`;
|
|
14
|
-
export { green as a,
|
|
15
|
+
export { green as a, url as c, gray as i, yellow as l, bold as n, magenta as o, cyan as r, red as s, blue as t };
|
package/dist/cli.d.mts
CHANGED
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as green, c as
|
|
1
|
+
import { a as green, c as url, i as gray, l as yellow, n as bold, o as magenta, r as cyan, s as red } from "./_chunks/_utils.mjs";
|
|
2
2
|
import { r as loadServerEntry } from "./_chunks/loader.mjs";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -6,7 +6,6 @@ import { fork } from "node:child_process";
|
|
|
6
6
|
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
7
7
|
import { dirname, relative, resolve } from "node:path";
|
|
8
8
|
import { Readable } from "node:stream";
|
|
9
|
-
var version = "0.11.0";
|
|
10
9
|
const NO_ENTRY_ERROR = "No server entry or public directory found";
|
|
11
10
|
async function cliServe(cliOpts) {
|
|
12
11
|
try {
|
|
@@ -32,7 +31,7 @@ async function cliServe(cliOpts) {
|
|
|
32
31
|
printInfo(cliOpts, loaded);
|
|
33
32
|
await (globalThis.__srvx__ = srvxServe({
|
|
34
33
|
...serverOptions,
|
|
35
|
-
gracefulShutdown: cliOpts.prod,
|
|
34
|
+
gracefulShutdown: !!cliOpts.prod,
|
|
36
35
|
port: cliOpts.port ?? serverOptions.port,
|
|
37
36
|
hostname: cliOpts.hostname ?? cliOpts.host ?? serverOptions.hostname,
|
|
38
37
|
tls: cliOpts.tls ? {
|
|
@@ -173,10 +172,18 @@ function getResponseFormat(res) {
|
|
|
173
172
|
encoding: contentType.includes("charset=") ? contentType.split("charset=")[1].split(";")[0].trim() : "utf8"
|
|
174
173
|
};
|
|
175
174
|
}
|
|
175
|
+
const srvxMeta = {
|
|
176
|
+
name: "srvx",
|
|
177
|
+
version: "0.11.2",
|
|
178
|
+
description: "Universal Server."
|
|
179
|
+
};
|
|
176
180
|
function usage(mainOpts) {
|
|
177
181
|
const command = mainOpts.usage?.command || "srvx";
|
|
182
|
+
const name = mainOpts.meta?.name || srvxMeta.name;
|
|
183
|
+
const ver = mainOpts.meta?.version || srvxMeta.version;
|
|
184
|
+
const desc = mainOpts.meta?.description || srvxMeta.description;
|
|
178
185
|
return `
|
|
179
|
-
${cyan(
|
|
186
|
+
${cyan(name)}${gray(`${ver ? ` ${ver}` : ""} ${desc ? `- ${desc}` : ""}`)}
|
|
180
187
|
|
|
181
188
|
${bold("SERVE MODE")}
|
|
182
189
|
|
|
@@ -239,19 +246,13 @@ async function main(mainOpts) {
|
|
|
239
246
|
const args = process.argv.slice(2);
|
|
240
247
|
const cliOpts = parseArgs$1(args);
|
|
241
248
|
if (cliOpts.version) {
|
|
242
|
-
|
|
249
|
+
process.stdout.write(versions(mainOpts).join("\n") + "\n");
|
|
243
250
|
process.exit(0);
|
|
244
251
|
}
|
|
245
252
|
if (cliOpts.help) {
|
|
246
253
|
console.log(usage(mainOpts));
|
|
247
254
|
process.exit(cliOpts.help ? 0 : 1);
|
|
248
255
|
}
|
|
249
|
-
if (process.send) {
|
|
250
|
-
console.log(gray(`srvx ${version} - ${runtime()}`));
|
|
251
|
-
setupProcessErrorHandlers();
|
|
252
|
-
await cliServe(cliOpts);
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
256
|
if (cliOpts.mode === "fetch") try {
|
|
256
257
|
const res = await cliFetch(cliOpts);
|
|
257
258
|
process.exit(res.ok ? 0 : 22);
|
|
@@ -259,48 +260,23 @@ async function main(mainOpts) {
|
|
|
259
260
|
console.error(error);
|
|
260
261
|
process.exit(1);
|
|
261
262
|
}
|
|
263
|
+
if (process.send) return startServer(cliOpts);
|
|
264
|
+
console.log(gray([...versions(mainOpts), cliOpts.prod ? "prod" : "dev"].join(" · ")));
|
|
265
|
+
const envFiles = [".env", cliOpts.prod ? ".env.production" : ".env.local"].filter((f) => existsSync(f));
|
|
266
|
+
if (envFiles.length > 0) console.log(`${gray(`Loading environment variables from ${magenta(envFiles.join(", "))}`)}`);
|
|
267
|
+
if (cliOpts.prod && !cliOpts.import) {
|
|
268
|
+
for (const envFile of [...envFiles].reverse()) process.loadEnvFile?.(envFile);
|
|
269
|
+
await startServer(cliOpts);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
262
272
|
const isBun = !!process.versions.bun;
|
|
263
273
|
const isDeno = !!process.versions.deno;
|
|
264
274
|
const isNode = !isBun && !isDeno;
|
|
265
275
|
const runtimeArgs = [];
|
|
276
|
+
runtimeArgs.push(...envFiles.map((f) => `--env-file=${f}`));
|
|
266
277
|
if (!cliOpts.prod) runtimeArgs.push("--watch");
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
const [major, minor] = process.versions.node.split(".");
|
|
270
|
-
if (major === "22" && +minor >= 6) runtimeArgs.push("--experimental-strip-types");
|
|
271
|
-
if (cliOpts.import) runtimeArgs.push(`--import=${cliOpts.import}`);
|
|
272
|
-
}
|
|
273
|
-
const child = fork(fileURLToPath(new URL("../bin/srvx.mjs", import.meta.url)), args, { execArgv: [...process.execArgv, ...runtimeArgs].filter(Boolean) });
|
|
274
|
-
child.on("error", (error) => {
|
|
275
|
-
console.error("Error in child process:", error);
|
|
276
|
-
process.exit(1);
|
|
277
|
-
});
|
|
278
|
-
child.on("exit", (code) => {
|
|
279
|
-
if (code !== 0) {
|
|
280
|
-
console.error(`Child process exited with code ${code}`);
|
|
281
|
-
process.exit(code);
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
child.on("message", (msg) => {
|
|
285
|
-
if (msg && msg.error === "no-entry") {
|
|
286
|
-
console.error("\n" + red(NO_ENTRY_ERROR) + "\n");
|
|
287
|
-
process.exit(3);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
let cleanupCalled = false;
|
|
291
|
-
const cleanup = (signal, exitCode) => {
|
|
292
|
-
if (cleanupCalled) return;
|
|
293
|
-
cleanupCalled = true;
|
|
294
|
-
try {
|
|
295
|
-
child.kill(signal || "SIGTERM");
|
|
296
|
-
} catch (error) {
|
|
297
|
-
console.error("Error killing child process:", error);
|
|
298
|
-
}
|
|
299
|
-
if (exitCode !== void 0) process.exit(exitCode);
|
|
300
|
-
};
|
|
301
|
-
process.on("exit", () => cleanup("SIGTERM"));
|
|
302
|
-
process.on("SIGINT", () => cleanup("SIGINT", 130));
|
|
303
|
-
process.on("SIGTERM", () => cleanup("SIGTERM", 143));
|
|
278
|
+
if (cliOpts.import && (isNode || isBun)) runtimeArgs.push(`--import=${cliOpts.import}`);
|
|
279
|
+
await forkCLI(args, runtimeArgs);
|
|
304
280
|
}
|
|
305
281
|
function parseArgs$1(args) {
|
|
306
282
|
const pArg0 = args.find((a) => !a.startsWith("-"));
|
|
@@ -383,6 +359,43 @@ function parseArgs$1(args) {
|
|
|
383
359
|
method
|
|
384
360
|
};
|
|
385
361
|
}
|
|
362
|
+
async function startServer(cliOpts) {
|
|
363
|
+
setupProcessErrorHandlers();
|
|
364
|
+
await cliServe(cliOpts);
|
|
365
|
+
}
|
|
366
|
+
async function forkCLI(args, runtimeArgs) {
|
|
367
|
+
const child = fork(fileURLToPath(globalThis.__SRVX_BIN__ || new URL("../bin/srvx.mjs", import.meta.url)), [...args], { execArgv: [...process.execArgv, ...runtimeArgs].filter(Boolean) });
|
|
368
|
+
child.on("error", (error) => {
|
|
369
|
+
console.error("Error in child process:", error);
|
|
370
|
+
process.exit(1);
|
|
371
|
+
});
|
|
372
|
+
child.on("exit", (code) => {
|
|
373
|
+
if (code !== 0) {
|
|
374
|
+
console.error(`Child process exited with code ${code}`);
|
|
375
|
+
process.exit(code);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
child.on("message", (msg) => {
|
|
379
|
+
if (msg && msg.error === "no-entry") {
|
|
380
|
+
console.error("\n" + red(NO_ENTRY_ERROR) + "\n");
|
|
381
|
+
process.exit(3);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
let cleanupCalled = false;
|
|
385
|
+
const cleanup = (signal, exitCode) => {
|
|
386
|
+
if (cleanupCalled) return;
|
|
387
|
+
cleanupCalled = true;
|
|
388
|
+
try {
|
|
389
|
+
child.kill(signal || "SIGTERM");
|
|
390
|
+
} catch (error) {
|
|
391
|
+
console.error("Error killing child process:", error);
|
|
392
|
+
}
|
|
393
|
+
if (exitCode !== void 0) process.exit(exitCode);
|
|
394
|
+
};
|
|
395
|
+
process.on("exit", () => cleanup("SIGTERM"));
|
|
396
|
+
process.on("SIGTERM", () => cleanup("SIGTERM", 143));
|
|
397
|
+
if (args.includes("--watch")) process.on("SIGINT", () => cleanup("SIGINT", 130));
|
|
398
|
+
}
|
|
386
399
|
function setupProcessErrorHandlers() {
|
|
387
400
|
process.on("uncaughtException", (error) => {
|
|
388
401
|
console.error("Uncaught exception:", error);
|
|
@@ -393,6 +406,13 @@ function setupProcessErrorHandlers() {
|
|
|
393
406
|
process.exit(1);
|
|
394
407
|
});
|
|
395
408
|
}
|
|
409
|
+
function versions(mainOpts) {
|
|
410
|
+
const versions = [];
|
|
411
|
+
if (mainOpts.meta?.name) versions.push(`${mainOpts.meta.name} ${mainOpts.meta.version || ""}`.trim());
|
|
412
|
+
versions.push(`${srvxMeta.name} ${srvxMeta.version}`);
|
|
413
|
+
versions.push(runtime());
|
|
414
|
+
return versions;
|
|
415
|
+
}
|
|
396
416
|
function runtime() {
|
|
397
417
|
if (process.versions.bun) return `bun ${process.versions.bun}`;
|
|
398
418
|
else if (process.versions.deno) return `deno ${process.versions.deno}`;
|
package/dist/log.mjs
CHANGED
package/dist/tracing.d.mts
CHANGED
|
@@ -19,7 +19,7 @@ type RequestEvent = {
|
|
|
19
19
|
* Tracing plugin that adds diagnostics channel tracing to middleware and fetch handlers.
|
|
20
20
|
*
|
|
21
21
|
* This plugin wraps all middleware and the fetch handler with tracing instrumentation,
|
|
22
|
-
* allowing you to subscribe to `srvx.
|
|
22
|
+
* allowing you to subscribe to `srvx.request` and `srvx.middleware` tracing channels.
|
|
23
23
|
*
|
|
24
24
|
* @example
|
|
25
25
|
* ```ts
|
package/dist/tracing.mjs
CHANGED
|
@@ -3,7 +3,7 @@ function tracingPlugin(opts = {}) {
|
|
|
3
3
|
const { tracingChannel } = globalThis.process?.getBuiltinModule?.("node:diagnostics_channel") || {};
|
|
4
4
|
if (!tracingChannel) return;
|
|
5
5
|
if (opts.fetch !== false) {
|
|
6
|
-
const fetchChannel = tracingChannel("srvx.
|
|
6
|
+
const fetchChannel = tracingChannel("srvx.request");
|
|
7
7
|
const originalFetch = server.options.fetch;
|
|
8
8
|
server.options.fetch = (request) => {
|
|
9
9
|
return fetchChannel.tracePromise(async () => await originalFetch(request), {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srvx",
|
|
3
|
-
"version": "0.11.
|
|
4
|
-
"description": "Universal Server
|
|
3
|
+
"version": "0.11.2",
|
|
4
|
+
"description": "Universal Server.",
|
|
5
5
|
"homepage": "https://srvx.h3.dev",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "h3js/srvx",
|