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 CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { main } from "../dist/cli.mjs";
3
3
 
4
+ globalThis.__SRVX_BIN__ = import.meta.url;
5
+
4
6
  await main({
5
7
  usage: {
6
8
  command: "srvx",
@@ -1,4 +1,4 @@
1
- import { i as gray, o as red } from "./_utils.mjs";
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 gracefulShutdown = config === true || !config?.gracefulTimeout ? Number.parseInt(process.env.SERVER_SHUTDOWN_TIMEOUT || "") || 3 : config.gracefulTimeout;
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
- let forceClose;
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 w = process.stderr.write.bind(process.stderr);
37
- w(gray(`\nShutting down server in ${gracefulShutdown}s... (press Ctrl+C again to force close)`));
38
- let timeout;
39
- await Promise.race([server.close().finally(() => {
40
- clearTimeout(timeout);
41
- w(gray(" Server closed.\n"));
42
- }), new Promise((resolve) => {
43
- forceClose = () => {
44
- clearTimeout(timeout);
45
- w(gray("\nForce closing...\n"));
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
  };
@@ -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, yellow as c, gray as i, bold as n, red as o, cyan as r, url as s, blue as t };
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
@@ -7,6 +7,11 @@ declare global {
7
7
  }
8
8
  type MainOptions = CLIOptions & {
9
9
  args?: string[];
10
+ meta?: {
11
+ name?: string;
12
+ version?: string;
13
+ description?: string;
14
+ };
10
15
  usage?: {
11
16
  command?: string;
12
17
  docs?: string;
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as green, c as yellow, i as gray, n as bold, o as red, r as cyan, s as url } from "./_chunks/_utils.mjs";
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(command)} - Universal Server CLI
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
- console.log(`srvx ${version}\n${runtime()}`);
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 (isNode || isDeno) runtimeArgs.push(...[".env", cliOpts.prod ? ".env.production" : ".env.local"].filter((f) => existsSync(f)).map((f) => `--env-file=${f}`));
268
- if (isNode) {
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
@@ -1,4 +1,4 @@
1
- import { a as green, c as yellow, i as gray, n as bold, o as red, t as blue } from "./_chunks/_utils.mjs";
1
+ import { a as green, i as gray, l as yellow, n as bold, s as red, t as blue } from "./_chunks/_utils.mjs";
2
2
  const statusColors = {
3
3
  1: blue,
4
4
  2: green,
@@ -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.fetch` and `srvx.middleware` tracing channels.
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.fetch");
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.0",
4
- "description": "Universal Server API based on web platform standards. Works seamlessly with Deno, Bun and Node.js.",
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",