srvx 0.11.7 → 0.11.9

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.
@@ -1,4 +1,5 @@
1
1
  import { a as green, i as gray, n as bold, s as red } from "./_utils.mjs";
2
+ //#region src/_middleware.ts
2
3
  function wrapFetch(server) {
3
4
  const fetchHandler = server.options.fetch;
4
5
  const middleware = server.options.middleware || [];
@@ -8,6 +9,8 @@ function callMiddleware(request, fetchHandler, middleware, index) {
8
9
  if (index === middleware.length) return fetchHandler(request);
9
10
  return middleware[index](request, () => callMiddleware(request, fetchHandler, middleware, index + 1));
10
11
  }
12
+ //#endregion
13
+ //#region src/_plugins.ts
11
14
  const errorPlugin = (server) => {
12
15
  const errorHandler = server.options.error;
13
16
  if (!errorHandler) return;
@@ -53,4 +56,5 @@ const gracefulShutdownPlugin = (server) => {
53
56
  };
54
57
  for (const sig of ["SIGINT", "SIGTERM"]) globalThis.process.on(sig, shutdown);
55
58
  };
59
+ //#endregion
56
60
  export { gracefulShutdownPlugin as n, wrapFetch as r, errorPlugin as t };
@@ -1,3 +1,4 @@
1
+ //#region src/_inherit.ts
1
2
  function lazyInherit(target, source, sourceKey) {
2
3
  for (const key of [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]) {
3
4
  if (key === "constructor") continue;
@@ -25,6 +26,23 @@ function lazyInherit(target, source, sourceKey) {
25
26
  if (modified) Object.defineProperty(target, key, desc);
26
27
  }
27
28
  }
29
+ //#endregion
30
+ //#region src/_url.ts
31
+ const _needsNormRE = /(?:(?:^|\/)(?:\.|\.\.|%2e|%2e\.|\.%2e|%2e%2e)(?:\/|$))|[\\^\x80-\uffff]/i;
32
+ /**
33
+ * URL wrapper with fast paths to access to the following props:
34
+ *
35
+ * - `url.pathname`
36
+ * - `url.search`
37
+ * - `url.searchParams`
38
+ * - `url.protocol`
39
+ *
40
+ * **NOTES:**
41
+ *
42
+ * - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
43
+ * - Triggering the setters or getters on other props will deoptimize to full URL parsing.
44
+ * - Changes to `searchParams` will be discarded as we don't track them.
45
+ */
28
46
  const FastURL = /* @__PURE__ */ (() => {
29
47
  const NativeURL = globalThis.URL;
30
48
  const FastURL = class URL {
@@ -38,6 +56,7 @@ const FastURL = /* @__PURE__ */ (() => {
38
56
  #pos;
39
57
  constructor(url) {
40
58
  if (typeof url === "string") this.#href = url;
59
+ else if (_needsNormRE.test(url.pathname)) this.#url = new NativeURL(`${url.protocol || "http:"}//${url.host || "localhost"}${url.pathname}${url.search || ""}`);
41
60
  else {
42
61
  this.#protocol = url.protocol;
43
62
  this.#host = url.host;
@@ -123,4 +142,5 @@ const FastURL = /* @__PURE__ */ (() => {
123
142
  Object.setPrototypeOf(FastURL, NativeURL);
124
143
  return FastURL;
125
144
  })();
145
+ //#endregion
126
146
  export { lazyInherit as n, FastURL as t };
@@ -1,3 +1,4 @@
1
+ //#region src/cli/_utils.ts
1
2
  const noColor = /* @__PURE__ */ (() => {
2
3
  const env = globalThis.process?.env ?? {};
3
4
  return env.NO_COLOR === "1" || env.TERM === "dumb";
@@ -12,4 +13,5 @@ const magenta = /* @__PURE__ */ _c(35);
12
13
  const cyan = /* @__PURE__ */ _c(36);
13
14
  const gray = /* @__PURE__ */ _c(90);
14
15
  const url = (title, url) => noColor ? `[${title}](${url})` : `\u001B]8;;${url}\u001B\\${title}\u001B]8;;\u001B\\`;
16
+ //#endregion
15
17
  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 };
@@ -1,3 +1,4 @@
1
+ //#region src/_utils.ts
1
2
  function resolvePortAndHost(opts) {
2
3
  const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
3
4
  const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
@@ -67,4 +68,5 @@ function createWaitUntil() {
67
68
  }
68
69
  };
69
70
  }
71
+ //#endregion
70
72
  export { resolveTLSOptions as a, resolvePortAndHost as i, fmtURL as n, printListening as r, createWaitUntil as t };
@@ -0,0 +1,87 @@
1
+ import { Server, 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
+ * Node.js server instance to return when intercepting `http.Server.listen`.
34
+ */
35
+ nodeServer?: NodeServer;
36
+ /**
37
+ * srvx server instance to return when intercepting `http.Server.listen`.
38
+ */
39
+ srvxServer?: Server;
40
+ /**
41
+ * Hook called after the module is loaded to allow for custom processing.
42
+ *
43
+ * You can return a modified version of the module if needed.
44
+ */
45
+ onLoad?: (module: unknown) => any;
46
+ };
47
+ /**
48
+ * Result of loading a server entry module.
49
+ */
50
+ type LoadedServerEntry = {
51
+ /**
52
+ * The web fetch handler extracted from the loaded module.
53
+ *
54
+ * This is resolved from `module.fetch`, `module.default.fetch`,
55
+ * or upgraded from a legacy Node.js handler.
56
+ */
57
+ fetch?: ServerHandler;
58
+ /**
59
+ * The raw loaded module.
60
+ */
61
+ module?: any;
62
+ /**
63
+ * Whether the handler was upgraded from a legacy Node.js HTTP handler.
64
+ *
65
+ * When `true`, the original module exported a Node.js-style `(req, res)` handler
66
+ * that has been wrapped for web fetch compatibility.
67
+ */
68
+ nodeCompat?: boolean;
69
+ /**
70
+ * The resolved `file://` URL of the loaded entry module.
71
+ */
72
+ url?: string;
73
+ /**
74
+ * Whether the specified entry file was not found.
75
+ *
76
+ * When `true`, no valid entry point could be located.
77
+ */
78
+ notFound?: boolean;
79
+ /**
80
+ * The original srvx server instance if the module used the loader API to export a server directly.
81
+ */
82
+ srvxServer?: Server;
83
+ };
84
+ declare function loadServerEntry(opts: LoadOptions): Promise<LoadedServerEntry>;
85
+ type NodeServer = NonNullable<Server["node"]>["server"];
86
+ //#endregion
87
+ export { loadServerEntry as a, defaultExts as i, LoadedServerEntry as n, defaultEntries as r, LoadOptions as t };
@@ -1,4 +1,6 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
3
+ //#region src/adapters/_aws/utils.ts
2
4
  function awsRequest(event, context) {
3
5
  const req = new Request(awsEventURL(event), {
4
6
  method: awsEventMethod(event),
@@ -249,6 +251,8 @@ function createMockContext() {
249
251
  succeed: () => {}
250
252
  };
251
253
  }
254
+ //#endregion
255
+ //#region src/adapters/aws-lambda.ts
252
256
  function toLambdaHandler(options) {
253
257
  const server = new AWSLambdaServer(options);
254
258
  return (event, context) => server.fetch(event, context);
@@ -289,4 +293,5 @@ var AWSLambdaServer = class {
289
293
  return Promise.resolve();
290
294
  }
291
295
  };
296
+ //#endregion
292
297
  export { handleLambdaEvent, handleLambdaEventWithStream, invokeLambdaHandler, toLambdaHandler };
@@ -1,6 +1,8 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { t as FastURL } from "../_chunks/_url.mjs";
2
3
  import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils2.mjs";
3
4
  import { n as gracefulShutdownPlugin, r as wrapFetch } from "../_chunks/_plugins.mjs";
5
+ //#region src/adapters/bun.ts
4
6
  const FastResponse = Response;
5
7
  function serve(options) {
6
8
  return new BunServer(options);
@@ -83,4 +85,5 @@ var BunServer = class {
83
85
  await Promise.all([this.#wait?.wait(), Promise.resolve(this.bun?.server?.stop(closeAll))]);
84
86
  }
85
87
  };
88
+ //#endregion
86
89
  export { FastResponse, FastURL, serve };
@@ -1,4 +1,6 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
3
+ //#region src/adapters/bunny.ts
2
4
  const FastURL = URL;
3
5
  const FastResponse = Response;
4
6
  function serve(options) {
@@ -51,4 +53,5 @@ var BunnyServer = class {
51
53
  return Promise.resolve();
52
54
  }
53
55
  };
56
+ //#endregion
54
57
  export { FastResponse, FastURL, serve };
@@ -1,4 +1,6 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
3
+ //#region src/adapters/cloudflare.ts
2
4
  const FastURL = URL;
3
5
  const FastResponse = Response;
4
6
  function serve(options) {
@@ -54,4 +56,5 @@ var CloudflareServer = class {
54
56
  return Promise.resolve();
55
57
  }
56
58
  };
59
+ //#endregion
57
60
  export { FastResponse, FastURL, serve };
@@ -1,6 +1,8 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { t as FastURL } from "../_chunks/_url.mjs";
2
3
  import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils2.mjs";
3
4
  import { n as gracefulShutdownPlugin, r as wrapFetch } from "../_chunks/_plugins.mjs";
5
+ //#region src/adapters/deno.ts
4
6
  const FastResponse = Response;
5
7
  function serve(options) {
6
8
  return new DenoServer(options);
@@ -92,4 +94,5 @@ var DenoServer = class {
92
94
  await Promise.all([this.#wait?.wait(), Promise.resolve(this.deno?.server?.shutdown())]);
93
95
  }
94
96
  };
97
+ //#endregion
95
98
  export { FastResponse, FastURL, serve };
@@ -1,5 +1,7 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { t as createWaitUntil } from "../_chunks/_utils2.mjs";
2
3
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
4
+ //#region src/adapters/generic.ts
3
5
  const FastURL = URL;
4
6
  const FastResponse = Response;
5
7
  function serve(options) {
@@ -34,4 +36,5 @@ var GenericServer = class {
34
36
  await this.#wait.wait();
35
37
  }
36
38
  };
39
+ //#endregion
37
40
  export { FastResponse, FastURL, serve };
@@ -1,3 +1,4 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { n as lazyInherit, t as FastURL } from "../_chunks/_url.mjs";
2
3
  import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils2.mjs";
3
4
  import { n as gracefulShutdownPlugin, r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
@@ -5,6 +6,7 @@ import nodeHTTP, { IncomingMessage, ServerResponse } from "node:http";
5
6
  import { Duplex, PassThrough, Readable } from "node:stream";
6
7
  import nodeHTTPS from "node:https";
7
8
  import nodeHTTP2 from "node:http2";
9
+ //#region src/adapters/_node/send.ts
8
10
  async function sendNodeResponse(nodeRes, webRes) {
9
11
  if (!webRes) {
10
12
  nodeRes.statusCode = 500;
@@ -62,6 +64,13 @@ function streamBody(stream, nodeRes) {
62
64
  nodeRes.off("error", streamCancel);
63
65
  });
64
66
  }
67
+ //#endregion
68
+ //#region src/adapters/_node/url.ts
69
+ /**
70
+ * Validates an HTTP Host header value (domain, IPv4, or bracketed IPv6) with optional port.
71
+ * Intended for preliminary filtering invalid values like "localhost:3000/foobar?"
72
+ */
73
+ const HOST_RE = /^(\[(?:[A-Fa-f0-9:.]+)\]|(?:[A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+|(?:\d{1,3}\.){3}\d{1,3})(:\d{1,5})?$/;
65
74
  var NodeRequestURL = class extends FastURL {
66
75
  #req;
67
76
  constructor({ req }) {
@@ -70,7 +79,11 @@ var NodeRequestURL = class extends FastURL {
70
79
  const qIndex = path.indexOf("?");
71
80
  const pathname = qIndex === -1 ? path : path?.slice(0, qIndex) || "/";
72
81
  const search = qIndex === -1 ? "" : path?.slice(qIndex) || "";
73
- const host = req.headers.host || req.headers[":authority"] || `${req.socket.localFamily === "IPv6" ? "[" + req.socket.localAddress + "]" : req.socket.localAddress}:${req.socket?.localPort || "80"}`;
82
+ let host = req.headers.host || req.headers[":authority"];
83
+ if (host) {
84
+ if (!HOST_RE.test(host)) throw new TypeError(`Invalid host header: ${host}`);
85
+ } else if (req.socket) host = `${req.socket.localFamily === "IPv6" ? "[" + req.socket.localAddress + "]" : req.socket.localAddress}:${req.socket?.localPort || "80"}`;
86
+ else host = "localhost";
74
87
  const protocol = req.socket?.encrypted || req.headers["x-forwarded-proto"] === "https" || req.headers[":scheme"] === "https" ? "https:" : "http:";
75
88
  super({
76
89
  protocol,
@@ -89,6 +102,8 @@ var NodeRequestURL = class extends FastURL {
89
102
  this.#req.url = this._url.pathname + this._url.search;
90
103
  }
91
104
  };
105
+ //#endregion
106
+ //#region src/adapters/_node/headers.ts
92
107
  const NodeRequestHeaders = /* @__PURE__ */ (() => {
93
108
  const NativeHeaders = globalThis.Headers;
94
109
  class Headers {
@@ -150,6 +165,8 @@ const NodeRequestHeaders = /* @__PURE__ */ (() => {
150
165
  Object.setPrototypeOf(Headers.prototype, NativeHeaders.prototype);
151
166
  return Headers;
152
167
  })();
168
+ //#endregion
169
+ //#region src/adapters/_node/request.ts
153
170
  const NodeRequest = /* @__PURE__ */ (() => {
154
171
  const NativeRequest = globalThis.Request;
155
172
  class Request {
@@ -248,6 +265,13 @@ const NodeRequest = /* @__PURE__ */ (() => {
248
265
  Object.setPrototypeOf(Request.prototype, NativeRequest.prototype);
249
266
  return Request;
250
267
  })();
268
+ /**
269
+ * Undici uses an incompatible Request constructor depending on private property accessors.
270
+ *
271
+ * This utility, patches global Request to support `new Request(req)` in Node.js.
272
+ *
273
+ * Alternatively you can use `new Request(req._request || req)` instead of patching global Request.
274
+ */
251
275
  function patchGlobalRequest() {
252
276
  const NativeRequest = globalThis[Symbol.for("srvx.nativeRequest")] ??= globalThis.Request;
253
277
  const PatchedRequest = class Request extends NativeRequest {
@@ -282,6 +306,13 @@ function readBody(req) {
282
306
  req.on("data", onData).once("end", onEnd).once("error", onError);
283
307
  });
284
308
  }
309
+ //#endregion
310
+ //#region src/adapters/_node/response.ts
311
+ /**
312
+ * Fast Response for Node.js runtime
313
+ *
314
+ * It is faster because in most cases it doesn't create a full Response instance.
315
+ */
285
316
  const NodeResponse = /* @__PURE__ */ (() => {
286
317
  const NativeResponse = globalThis.Response;
287
318
  const STATUS_CODES = globalThis.process?.getBuiltinModule?.("node:http")?.STATUS_CODES || {};
@@ -390,6 +421,13 @@ const NodeResponse = /* @__PURE__ */ (() => {
390
421
  Object.setPrototypeOf(NodeResponse.prototype, NativeResponse.prototype);
391
422
  return NodeResponse;
392
423
  })();
424
+ //#endregion
425
+ //#region src/adapters/_node/web/socket.ts
426
+ /**
427
+ * Events:
428
+ * - Readable (req from client): readable => data => end (push(null)) => error => close
429
+ * - Writable (res to client): pipe => unpipe => drain => finish (end called) => error => close
430
+ */
393
431
  var WebRequestSocket = class extends Duplex {
394
432
  _httpMessage;
395
433
  autoSelectFamilyAttemptedAddresses = [];
@@ -505,6 +543,8 @@ var WebRequestSocket = class extends Duplex {
505
543
  cb(err ?? void 0);
506
544
  }
507
545
  };
546
+ //#endregion
547
+ //#region src/adapters/_node/web/incoming.ts
508
548
  var WebIncomingMessage = class extends IncomingMessage {
509
549
  constructor(req, socket) {
510
550
  super(socket);
@@ -523,6 +563,8 @@ var WebIncomingMessage = class extends IncomingMessage {
523
563
  });
524
564
  }
525
565
  };
566
+ //#endregion
567
+ //#region src/adapters/_node/call.ts
526
568
  function callNodeHandler(handler, req) {
527
569
  const isMiddleware = handler.length > 2;
528
570
  const nodeCtx = req.runtime?.node;
@@ -566,6 +608,8 @@ function callNodeHandler(handler, req) {
566
608
  }
567
609
  });
568
610
  }
611
+ //#endregion
612
+ //#region src/adapters/_node/web/response.ts
569
613
  var WebServerResponse = class extends ServerResponse {
570
614
  #socket;
571
615
  constructor(req, socket) {
@@ -604,6 +648,19 @@ var WebServerResponse = class extends ServerResponse {
604
648
  });
605
649
  }
606
650
  };
651
+ //#endregion
652
+ //#region src/adapters/_node/web/fetch.ts
653
+ /**
654
+ * Calls a Node.js HTTP Request handler with a Fetch API Request object and returns a Response object.
655
+ *
656
+ * If the web Request contains an existing Node.js req/res pair (indicating it originated from a Node.js server from srvx/node), it will be called directly.
657
+ *
658
+ * Otherwise, new Node.js IncomingMessage and ServerResponse objects are created and linked to a custom Duplex stream that bridges the Fetch API streams with Node.js streams.
659
+ *
660
+ * The handler is invoked with these objects, and the response is constructed from the ServerResponse once it is finished.
661
+ *
662
+ * @experimental Behavior might be unstable.
663
+ */
607
664
  async function fetchNodeHandler(handler, req) {
608
665
  const nodeRuntime = req.runtime?.node;
609
666
  if (nodeRuntime && nodeRuntime.req && nodeRuntime.res) return await callNodeHandler(handler, req);
@@ -624,6 +681,11 @@ async function fetchNodeHandler(handler, req) {
624
681
  });
625
682
  }
626
683
  }
684
+ //#endregion
685
+ //#region src/adapters/_node/adapter.ts
686
+ /**
687
+ * Converts a Fetch API handler to a Node.js HTTP handler.
688
+ */
627
689
  function toNodeHandler(handler) {
628
690
  if (handler.__nodeHandler) return handler.__nodeHandler;
629
691
  function convertedNodeHandler(nodeReq, nodeRes) {
@@ -637,6 +699,11 @@ function toNodeHandler(handler) {
637
699
  assignFnName(convertedNodeHandler, handler, " (converted to Node handler)");
638
700
  return convertedNodeHandler;
639
701
  }
702
+ /**
703
+ * Converts a Node.js HTTP handler into a Fetch API handler.
704
+ *
705
+ * @experimental Behavior might be unstable and won't work in Bun and Deno currently (tracker: https://github.com/h3js/srvx/issues/132)
706
+ */
640
707
  function toFetchHandler(handler) {
641
708
  if (handler.__fetchHandler) return handler.__fetchHandler;
642
709
  function convertedNodeHandler(req) {
@@ -651,6 +718,8 @@ function assignFnName(target, source, suffix) {
651
718
  Object.defineProperty(target, "name", { value: `${source.name}${suffix}` });
652
719
  } catch {}
653
720
  }
721
+ //#endregion
722
+ //#region src/adapters/node.ts
654
723
  function serve(options) {
655
724
  return new NodeServer(options);
656
725
  }
@@ -744,4 +813,5 @@ var NodeServer = class {
744
813
  })]);
745
814
  }
746
815
  };
816
+ //#endregion
747
817
  export { NodeResponse as FastResponse, NodeResponse, FastURL, NodeRequest, fetchNodeHandler, patchGlobalRequest, sendNodeResponse, serve, toFetchHandler, toNodeHandler };
@@ -1,4 +1,6 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
3
+ //#region src/adapters/service-worker.ts
2
4
  const FastURL = URL;
3
5
  const FastResponse = Response;
4
6
  const isBrowserWindow = typeof window !== "undefined" && typeof navigator !== "undefined";
@@ -73,4 +75,5 @@ var ServiceWorkerServer = class {
73
75
  } else if (isServiceWorker) await self.registration.unregister();
74
76
  }
75
77
  };
78
+ //#endregion
76
79
  export { FastResponse, FastURL, serve };
package/dist/cli.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { LoadOptions } from "./loader.mjs";
1
+ import { t as LoadOptions } from "./_chunks/loader.mjs";
2
2
 
3
3
  //#region src/cli/types.d.ts
4
4
  type MainOptions = CLIOptions & {
package/dist/cli.mjs CHANGED
@@ -6,6 +6,7 @@ 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
+ //#region src/cli/serve.ts
9
10
  const NO_ENTRY_ERROR = "No server entry or public directory found";
10
11
  async function cliServe(cliOpts) {
11
12
  try {
@@ -88,6 +89,8 @@ function printInfo(cliOpts, loaded) {
88
89
  console.log(gray(`${bold(gray("◇"))} Static files: ${staticInfo}`));
89
90
  console.log("");
90
91
  }
92
+ //#endregion
93
+ //#region src/cli/fetch.ts
91
94
  async function cliFetch(cliOpts) {
92
95
  const stdin = cliOpts.stdin || process.stdin;
93
96
  const stdout = cliOpts.stdout || process.stdout;
@@ -177,11 +180,15 @@ function getResponseFormat(res) {
177
180
  encoding: contentType.includes("charset=") ? contentType.split("charset=")[1].split(";")[0].trim() : "utf8"
178
181
  };
179
182
  }
183
+ //#endregion
184
+ //#region src/cli/_meta.ts
180
185
  const srvxMeta = {
181
186
  name: "srvx",
182
- version: "0.11.7",
187
+ version: "0.11.9",
183
188
  description: "Universal Server."
184
189
  };
190
+ //#endregion
191
+ //#region src/cli/usage.ts
185
192
  function usage(mainOpts) {
186
193
  const command = mainOpts.usage?.command || "srvx";
187
194
  const name = mainOpts.meta?.name || srvxMeta.name;
@@ -247,6 +254,8 @@ ${mainOpts.usage?.docs ? `➤ ${url("Documentation", mainOpts.usage.docs)}` : ""
247
254
  ${mainOpts.usage?.issues ? `➤ ${url("Report issues", mainOpts.usage.issues)}` : ""}
248
255
  `.trim();
249
256
  }
257
+ //#endregion
258
+ //#region src/cli/main.ts
250
259
  async function main(mainOpts) {
251
260
  const args = process.argv.slice(2);
252
261
  const cliOpts = parseArgs$1(args);
@@ -423,4 +432,5 @@ function runtime() {
423
432
  else if (process.versions.deno) return `deno ${process.versions.deno}`;
424
433
  else return `node ${process.versions.node}`;
425
434
  }
435
+ //#endregion
426
436
  export { cliFetch, main };
package/dist/loader.d.mts CHANGED
@@ -1,87 +1,2 @@
1
- import { Server, 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
- * Node.js server instance to return when intercepting `http.Server.listen`.
34
- */
35
- nodeServer?: NodeServer;
36
- /**
37
- * srvx server instance to return when intercepting `http.Server.listen`.
38
- */
39
- srvxServer?: Server;
40
- /**
41
- * Hook called after the module is loaded to allow for custom processing.
42
- *
43
- * You can return a modified version of the module if needed.
44
- */
45
- onLoad?: (module: unknown) => any;
46
- };
47
- /**
48
- * Result of loading a server entry module.
49
- */
50
- type LoadedServerEntry = {
51
- /**
52
- * The web fetch handler extracted from the loaded module.
53
- *
54
- * This is resolved from `module.fetch`, `module.default.fetch`,
55
- * or upgraded from a legacy Node.js handler.
56
- */
57
- fetch?: ServerHandler;
58
- /**
59
- * The raw loaded module.
60
- */
61
- module?: any;
62
- /**
63
- * Whether the handler was upgraded from a legacy Node.js HTTP handler.
64
- *
65
- * When `true`, the original module exported a Node.js-style `(req, res)` handler
66
- * that has been wrapped for web fetch compatibility.
67
- */
68
- nodeCompat?: boolean;
69
- /**
70
- * The resolved `file://` URL of the loaded entry module.
71
- */
72
- url?: string;
73
- /**
74
- * Whether the specified entry file was not found.
75
- *
76
- * When `true`, no valid entry point could be located.
77
- */
78
- notFound?: boolean;
79
- /**
80
- * The original srvx server instance if the module used the loader API to export a server directly.
81
- */
82
- srvxServer?: Server;
83
- };
84
- declare function loadServerEntry(opts: LoadOptions): Promise<LoadedServerEntry>;
85
- type NodeServer = NonNullable<Server["node"]>["server"];
86
- //#endregion
1
+ import { a as loadServerEntry, i as defaultExts, n as LoadedServerEntry, r as defaultEntries, t as LoadOptions } from "./_chunks/loader.mjs";
87
2
  export { LoadOptions, LoadedServerEntry, defaultEntries, defaultExts, loadServerEntry };
package/dist/loader.mjs CHANGED
@@ -3,6 +3,7 @@ import { existsSync } from "node:fs";
3
3
  import { resolve } from "node:path";
4
4
  import * as nodeHTTP$1 from "node:http";
5
5
  import { EventEmitter } from "node:events";
6
+ //#region src/loader.ts
6
7
  const defaultExts = [
7
8
  ".mjs",
8
9
  ".js",
@@ -135,4 +136,5 @@ var StubNodeServer = class extends EventEmitter {
135
136
  return this;
136
137
  }
137
138
  };
139
+ //#endregion
138
140
  export { defaultEntries, defaultExts, loadServerEntry };
package/dist/log.mjs CHANGED
@@ -1,4 +1,5 @@
1
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
+ //#region src/log.ts
2
3
  const statusColors = {
3
4
  1: blue,
4
5
  2: green,
@@ -14,4 +15,5 @@ const log = (_options = {}) => {
14
15
  return res;
15
16
  };
16
17
  };
18
+ //#endregion
17
19
  export { log };
package/dist/static.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import { t as FastURL } from "./_chunks/_url.mjs";
2
2
  import { createReadStream } from "node:fs";
3
- import { extname, join, resolve } from "node:path";
3
+ import { extname, join, resolve, sep } from "node:path";
4
4
  import { readFile, stat } from "node:fs/promises";
5
5
  import { FastResponse } from "srvx";
6
6
  import { createBrotliCompress, createGzip } from "node:zlib";
7
+ //#region src/static.ts
7
8
  const COMMON_MIME_TYPES = {
8
9
  ".html": "text/html",
9
10
  ".htm": "text/html",
@@ -28,7 +29,7 @@ const COMMON_MIME_TYPES = {
28
29
  ".pdf": "application/pdf"
29
30
  };
30
31
  const serveStatic = (options) => {
31
- const dir = resolve(options.dir) + "/";
32
+ const dir = resolve(options.dir) + sep;
32
33
  const methods = new Set((options.methods || ["GET", "HEAD"]).map((m) => m.toUpperCase()));
33
34
  return async (req, next) => {
34
35
  if (!methods.has(req.method)) return next();
@@ -71,4 +72,5 @@ const serveStatic = (options) => {
71
72
  return next();
72
73
  };
73
74
  };
75
+ //#endregion
74
76
  export { serveStatic };
package/dist/tracing.mjs CHANGED
@@ -1,3 +1,25 @@
1
+ //#region src/tracing.ts
2
+ /**
3
+ *
4
+ * @experimental Channel names, event types and config options may change in future releases.
5
+ *
6
+ * Tracing plugin that adds diagnostics channel tracing to middleware and fetch handlers.
7
+ *
8
+ * This plugin wraps all middleware and the fetch handler with tracing instrumentation,
9
+ * allowing you to subscribe to `srvx.request` and `srvx.middleware` tracing channels.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { serve } from "srvx";
14
+ * import { tracingPlugin } from "srvx/tracing";
15
+ *
16
+ * const server = serve({
17
+ * fetch: (req) => new Response("OK"),
18
+ * middleware: [myMiddleware],
19
+ * plugins: [tracingPlugin()],
20
+ * });
21
+ * ```
22
+ */
1
23
  function tracingPlugin(opts = {}) {
2
24
  return (server) => {
3
25
  const { tracingChannel } = globalThis.process?.getBuiltinModule?.("node:diagnostics_channel") || {};
@@ -31,4 +53,5 @@ function tracingPlugin(opts = {}) {
31
53
  }
32
54
  };
33
55
  }
56
+ //#endregion
34
57
  export { tracingPlugin };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "srvx",
3
- "version": "0.11.7",
3
+ "version": "0.11.9",
4
4
  "description": "Universal Server.",
5
5
  "homepage": "https://srvx.h3.dev",
6
6
  "license": "MIT",
@@ -59,18 +59,18 @@
59
59
  "vitest": "vitest"
60
60
  },
61
61
  "devDependencies": {
62
- "@cloudflare/workers-types": "^4.20260217.0",
63
- "@hono/node-server": "^1.19.9",
62
+ "@cloudflare/workers-types": "^4.20260307.1",
63
+ "@hono/node-server": "^1.19.11",
64
64
  "@mitata/counters": "^0.0.8",
65
65
  "@mjackson/node-fetch-server": "^0.7.0",
66
- "@types/aws-lambda": "^8.10.160",
67
- "@types/bun": "^1.3.9",
66
+ "@types/aws-lambda": "^8.10.161",
67
+ "@types/bun": "^1.3.10",
68
68
  "@types/deno": "^2.5.0",
69
69
  "@types/express": "^5.0.6",
70
- "@types/node": "^25.2.3",
70
+ "@types/node": "^25.3.5",
71
71
  "@types/node-forge": "^1.3.14",
72
- "@types/serviceworker": "^0.0.191",
73
- "@typescript/native-preview": "^7.0.0-dev.20260217.1",
72
+ "@types/serviceworker": "^0.0.193",
73
+ "@typescript/native-preview": "7.0.0-dev.20260309.1",
74
74
  "@vitest/coverage-v8": "^4.0.18",
75
75
  "@whatwg-node/server": "^0.10.18",
76
76
  "automd": "^0.4.3",
@@ -78,15 +78,15 @@
78
78
  "eslint-config-unjs": "^0.6.2",
79
79
  "execa": "^9.6.1",
80
80
  "express": "^5.2.1",
81
- "fastify": "^5.7.4",
81
+ "fastify": "^5.8.2",
82
82
  "get-port-please": "^3.2.0",
83
83
  "mdbox": "^0.1.1",
84
84
  "mitata": "^1.0.34",
85
85
  "node-forge": "^1.3.3",
86
- "obuild": "^0.4.27",
87
- "oxfmt": "^0.33.0",
88
- "oxlint": "^1.48.0",
89
- "srvx-release": "npm:srvx@^0.11.4",
86
+ "obuild": "^0.4.32",
87
+ "oxfmt": "^0.36.0",
88
+ "oxlint": "^1.51.0",
89
+ "srvx-release": "npm:srvx@^0.11.8",
90
90
  "tslib": "^2.8.1",
91
91
  "typescript": "^5.9.3",
92
92
  "undici": "^7.22.0",
@@ -98,5 +98,5 @@
98
98
  "engines": {
99
99
  "node": ">=20.16.0"
100
100
  },
101
- "packageManager": "pnpm@10.29.3"
101
+ "packageManager": "pnpm@10.30.2"
102
102
  }