srvx 0.11.6 → 0.11.8

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.
@@ -25,6 +25,20 @@ function lazyInherit(target, source, sourceKey) {
25
25
  if (modified) Object.defineProperty(target, key, desc);
26
26
  }
27
27
  }
28
+ /**
29
+ * URL wrapper with fast paths to access to the following props:
30
+ *
31
+ * - `url.pathname`
32
+ * - `url.search`
33
+ * - `url.searchParams`
34
+ * - `url.protocol`
35
+ *
36
+ * **NOTES:**
37
+ *
38
+ * - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
39
+ * - Triggering the setters or getters on other props will deoptimize to full URL parsing.
40
+ * - Changes to `searchParams` will be discarded as we don't track them.
41
+ */
28
42
  const FastURL = /* @__PURE__ */ (() => {
29
43
  const NativeURL = globalThis.URL;
30
44
  const FastURL = class URL {
@@ -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,3 +1,4 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
2
3
  function awsRequest(event, context) {
3
4
  const req = new Request(awsEventURL(event), {
@@ -1,3 +1,4 @@
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";
@@ -24,7 +25,7 @@ var BunServer = class {
24
25
  const loader = globalThis.__srvxLoader__;
25
26
  if (loader) {
26
27
  this.fetch = fetchHandler;
27
- loader(fetchHandler);
28
+ loader({ server: this });
28
29
  return;
29
30
  }
30
31
  this.#wait = createWaitUntil();
@@ -1,3 +1,4 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
2
3
  const FastURL = URL;
3
4
  const FastResponse = Response;
@@ -1,3 +1,4 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
2
3
  const FastURL = URL;
3
4
  const FastResponse = Response;
@@ -1,3 +1,4 @@
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";
@@ -26,7 +27,7 @@ var DenoServer = class {
26
27
  const loader = globalThis.__srvxLoader__;
27
28
  if (loader) {
28
29
  this.fetch = fetchHandler;
29
- loader(fetchHandler);
30
+ loader({ server: this });
30
31
  return;
31
32
  }
32
33
  this.#wait = createWaitUntil();
@@ -1,3 +1,4 @@
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";
3
4
  const FastURL = URL;
@@ -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";
@@ -62,6 +63,11 @@ function streamBody(stream, nodeRes) {
62
63
  nodeRes.off("error", streamCancel);
63
64
  });
64
65
  }
66
+ /**
67
+ * Validates an HTTP Host header value (domain, IPv4, or bracketed IPv6) with optional port.
68
+ * Intended for preliminary filtering invalid values like "localhost:3000/foobar?"
69
+ */
70
+ 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
71
  var NodeRequestURL = class extends FastURL {
66
72
  #req;
67
73
  constructor({ req }) {
@@ -70,7 +76,11 @@ var NodeRequestURL = class extends FastURL {
70
76
  const qIndex = path.indexOf("?");
71
77
  const pathname = qIndex === -1 ? path : path?.slice(0, qIndex) || "/";
72
78
  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"}`;
79
+ let host = req.headers.host || req.headers[":authority"];
80
+ if (host) {
81
+ if (!HOST_RE.test(host)) throw new TypeError(`Invalid host header: ${host}`);
82
+ } else if (req.socket) host = `${req.socket.localFamily === "IPv6" ? "[" + req.socket.localAddress + "]" : req.socket.localAddress}:${req.socket?.localPort || "80"}`;
83
+ else host = "localhost";
74
84
  const protocol = req.socket?.encrypted || req.headers["x-forwarded-proto"] === "https" || req.headers[":scheme"] === "https" ? "https:" : "http:";
75
85
  super({
76
86
  protocol,
@@ -248,6 +258,13 @@ const NodeRequest = /* @__PURE__ */ (() => {
248
258
  Object.setPrototypeOf(Request.prototype, NativeRequest.prototype);
249
259
  return Request;
250
260
  })();
261
+ /**
262
+ * Undici uses an incompatible Request constructor depending on private property accessors.
263
+ *
264
+ * This utility, patches global Request to support `new Request(req)` in Node.js.
265
+ *
266
+ * Alternatively you can use `new Request(req._request || req)` instead of patching global Request.
267
+ */
251
268
  function patchGlobalRequest() {
252
269
  const NativeRequest = globalThis[Symbol.for("srvx.nativeRequest")] ??= globalThis.Request;
253
270
  const PatchedRequest = class Request extends NativeRequest {
@@ -282,6 +299,11 @@ function readBody(req) {
282
299
  req.on("data", onData).once("end", onEnd).once("error", onError);
283
300
  });
284
301
  }
302
+ /**
303
+ * Fast Response for Node.js runtime
304
+ *
305
+ * It is faster because in most cases it doesn't create a full Response instance.
306
+ */
285
307
  const NodeResponse = /* @__PURE__ */ (() => {
286
308
  const NativeResponse = globalThis.Response;
287
309
  const STATUS_CODES = globalThis.process?.getBuiltinModule?.("node:http")?.STATUS_CODES || {};
@@ -390,6 +412,11 @@ const NodeResponse = /* @__PURE__ */ (() => {
390
412
  Object.setPrototypeOf(NodeResponse.prototype, NativeResponse.prototype);
391
413
  return NodeResponse;
392
414
  })();
415
+ /**
416
+ * Events:
417
+ * - Readable (req from client): readable => data => end (push(null)) => error => close
418
+ * - Writable (res to client): pipe => unpipe => drain => finish (end called) => error => close
419
+ */
393
420
  var WebRequestSocket = class extends Duplex {
394
421
  _httpMessage;
395
422
  autoSelectFamilyAttemptedAddresses = [];
@@ -604,6 +631,17 @@ var WebServerResponse = class extends ServerResponse {
604
631
  });
605
632
  }
606
633
  };
634
+ /**
635
+ * Calls a Node.js HTTP Request handler with a Fetch API Request object and returns a Response object.
636
+ *
637
+ * 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.
638
+ *
639
+ * 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.
640
+ *
641
+ * The handler is invoked with these objects, and the response is constructed from the ServerResponse once it is finished.
642
+ *
643
+ * @experimental Behavior might be unstable.
644
+ */
607
645
  async function fetchNodeHandler(handler, req) {
608
646
  const nodeRuntime = req.runtime?.node;
609
647
  if (nodeRuntime && nodeRuntime.req && nodeRuntime.res) return await callNodeHandler(handler, req);
@@ -624,6 +662,9 @@ async function fetchNodeHandler(handler, req) {
624
662
  });
625
663
  }
626
664
  }
665
+ /**
666
+ * Converts a Fetch API handler to a Node.js HTTP handler.
667
+ */
627
668
  function toNodeHandler(handler) {
628
669
  if (handler.__nodeHandler) return handler.__nodeHandler;
629
670
  function convertedNodeHandler(nodeReq, nodeRes) {
@@ -637,6 +678,11 @@ function toNodeHandler(handler) {
637
678
  assignFnName(convertedNodeHandler, handler, " (converted to Node handler)");
638
679
  return convertedNodeHandler;
639
680
  }
681
+ /**
682
+ * Converts a Node.js HTTP handler into a Fetch API handler.
683
+ *
684
+ * @experimental Behavior might be unstable and won't work in Bun and Deno currently (tracker: https://github.com/h3js/srvx/issues/132)
685
+ */
640
686
  function toFetchHandler(handler) {
641
687
  if (handler.__fetchHandler) return handler.__fetchHandler;
642
688
  function convertedNodeHandler(req) {
@@ -672,14 +718,6 @@ var NodeServer = class {
672
718
  for (const plugin of options.plugins || []) plugin(this);
673
719
  errorPlugin(this);
674
720
  const fetchHandler = this.fetch = wrapFetch(this);
675
- const loader = globalThis.__srvxLoader__;
676
- if (loader) {
677
- loader(fetchHandler);
678
- return;
679
- }
680
- gracefulShutdownPlugin(this);
681
- this.#wait = createWaitUntil();
682
- this.waitUntil = this.#wait.waitUntil;
683
721
  const handler = (nodeReq, nodeRes) => {
684
722
  const request = new NodeRequest({
685
723
  req: nodeReq,
@@ -689,6 +727,18 @@ var NodeServer = class {
689
727
  const res = fetchHandler(request);
690
728
  return res instanceof Promise ? res.then((resolvedRes) => sendNodeResponse(nodeRes, resolvedRes)) : sendNodeResponse(nodeRes, res);
691
729
  };
730
+ this.node = {
731
+ handler,
732
+ server: void 0
733
+ };
734
+ const loader = globalThis.__srvxLoader__;
735
+ if (loader) {
736
+ loader({ server: this });
737
+ return;
738
+ }
739
+ gracefulShutdownPlugin(this);
740
+ this.#wait = createWaitUntil();
741
+ this.waitUntil = this.#wait.waitUntil;
692
742
  const tls = resolveTLSOptions(this.options);
693
743
  const { port, hostname: host } = resolvePortAndHost(this.options);
694
744
  this.serveOptions = {
@@ -711,10 +761,7 @@ var NodeServer = class {
711
761
  else throw new Error("node.http2 option requires tls certificate!");
712
762
  else if (this.#isSecure) server = nodeHTTPS.createServer(this.serveOptions, handler);
713
763
  else server = nodeHTTP.createServer(this.serveOptions, handler);
714
- this.node = {
715
- server,
716
- handler
717
- };
764
+ this.node.server = server;
718
765
  if (!options.manual) this.serve();
719
766
  }
720
767
  serve() {
@@ -1,3 +1,4 @@
1
+ import "../_chunks/_utils.mjs";
1
2
  import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
2
3
  const FastURL = URL;
3
4
  const FastResponse = Response;
package/dist/cli.d.mts CHANGED
@@ -1,10 +1,6 @@
1
- import { LoadOptions } from "./loader.mjs";
2
- import { Server } from "srvx";
1
+ import { t as LoadOptions } from "./_chunks/loader.mjs";
3
2
 
4
3
  //#region src/cli/types.d.ts
5
- declare global {
6
- var __srvx__: Server | undefined;
7
- }
8
4
  type MainOptions = CLIOptions & {
9
5
  args?: string[];
10
6
  meta?: {
package/dist/cli.mjs CHANGED
@@ -10,9 +10,13 @@ const NO_ENTRY_ERROR = "No server entry or public directory found";
10
10
  async function cliServe(cliOpts) {
11
11
  try {
12
12
  if (!process.env.NODE_ENV) process.env.NODE_ENV = cliOpts.prod ? "production" : "development";
13
+ let server;
13
14
  const loaded = await loadServerEntry({
14
15
  entry: cliOpts.entry,
15
- dir: cliOpts.dir
16
+ dir: cliOpts.dir,
17
+ get srvxServer() {
18
+ return server;
19
+ }
16
20
  });
17
21
  const { serve: srvxServe } = loaded.nodeCompat ? await import("srvx/node") : await import("srvx");
18
22
  const { serveStatic } = await import("srvx/static");
@@ -29,7 +33,7 @@ async function cliServe(cliOpts) {
29
33
  ...loaded.module
30
34
  };
31
35
  printInfo(cliOpts, loaded);
32
- await (globalThis.__srvx__ = srvxServe({
36
+ server = srvxServe({
33
37
  ...serverOptions,
34
38
  gracefulShutdown: !!cliOpts.prod,
35
39
  port: cliOpts.port ?? serverOptions.port,
@@ -48,7 +52,8 @@ async function cliServe(cliOpts) {
48
52
  cliOpts.static ? serveStatic({ dir: cliOpts.static }) : void 0,
49
53
  ...serverOptions.middleware || []
50
54
  ].filter(Boolean)
51
- })).ready();
55
+ });
56
+ await server.ready();
52
57
  } catch (error) {
53
58
  console.error(error);
54
59
  process.exit(1);
@@ -174,7 +179,7 @@ function getResponseFormat(res) {
174
179
  }
175
180
  const srvxMeta = {
176
181
  name: "srvx",
177
- version: "0.11.6",
182
+ version: "0.11.8",
178
183
  description: "Universal Server."
179
184
  };
180
185
  function usage(mainOpts) {
package/dist/loader.d.mts CHANGED
@@ -1,74 +1,2 @@
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
1
+ import { a as loadServerEntry, i as defaultExts, n as LoadedServerEntry, r as defaultEntries, t as LoadOptions } from "./_chunks/loader.mjs";
74
2
  export { LoadOptions, LoadedServerEntry, defaultEntries, defaultExts, loadServerEntry };
package/dist/loader.mjs CHANGED
@@ -2,6 +2,7 @@ import { pathToFileURL } from "node:url";
2
2
  import { existsSync } from "node:fs";
3
3
  import { resolve } from "node:path";
4
4
  import * as nodeHTTP$1 from "node:http";
5
+ import { EventEmitter } from "node:events";
5
6
  const defaultExts = [
6
7
  ".mjs",
7
8
  ".js",
@@ -35,13 +36,13 @@ async function loadServerEntry(opts) {
35
36
  const url = entry.startsWith("file://") ? entry : pathToFileURL(resolve(entry)).href;
36
37
  let mod;
37
38
  let interceptedNodeHandler;
38
- let interceptedFetchHandler;
39
+ let interceptedServer;
39
40
  try {
40
41
  if (opts.interceptHttpListen !== false) {
41
- const loaded = await interceptListen(() => import(url));
42
+ const loaded = await interceptListen(() => import(url), opts);
42
43
  mod = loaded.res;
43
44
  interceptedNodeHandler = loaded.listenHandler;
44
- interceptedFetchHandler = loaded.fetchHandler;
45
+ interceptedServer = loaded.server;
45
46
  } else mod = await import(url);
46
47
  } catch (error) {
47
48
  if (error?.code === "ERR_UNKNOWN_FILE_EXTENSION") {
@@ -52,7 +53,7 @@ async function loadServerEntry(opts) {
52
53
  throw error;
53
54
  }
54
55
  mod = await opts?.onLoad?.(mod) || mod;
55
- let fetchHandler = mod?.fetch || mod?.default?.fetch || mod?.default?.default?.fetch || interceptedFetchHandler;
56
+ let fetchHandler = mod?.fetch || mod?.default?.fetch || mod?.default?.default?.fetch || interceptedServer?.fetch;
56
57
  if (!fetchHandler && typeof mod?.default === "function" && mod.default.length < 2) fetchHandler = mod.default;
57
58
  let nodeCompat = false;
58
59
  if (!fetchHandler && opts.nodeCompat !== false) {
@@ -67,18 +68,21 @@ async function loadServerEntry(opts) {
67
68
  module: mod,
68
69
  nodeCompat,
69
70
  url,
70
- fetch: fetchHandler
71
+ fetch: fetchHandler,
72
+ srvxServer: interceptedServer
71
73
  };
72
74
  }
73
75
  let _interceptQueue = Promise.resolve();
74
- async function interceptListen(cb) {
76
+ async function interceptListen(cb, opts = {}) {
75
77
  const result = _interceptQueue.then(async () => {
76
78
  const originalListen = nodeHTTP$1.Server.prototype.listen;
77
79
  let res;
78
80
  let listenHandler;
79
- let fetchHandler;
80
- globalThis.__srvxLoader__ = (handler) => {
81
- fetchHandler = handler;
81
+ let server;
82
+ const nodeServerStub = opts.nodeServer || opts.srvxServer?.node?.server || new StubNodeServer(() => opts.srvxServer?.node?.server);
83
+ globalThis.__srvxLoader__ = (obj) => {
84
+ server = obj.server;
85
+ if (server && server.node) server.node.server ||= nodeServerStub;
82
86
  };
83
87
  try {
84
88
  nodeHTTP$1.Server.prototype.listen = function(arg1, arg2) {
@@ -89,15 +93,7 @@ async function interceptListen(cb) {
89
93
  setImmediate(() => {
90
94
  listenCallback?.();
91
95
  });
92
- return new Proxy({}, { get(_, prop) {
93
- const server = globalThis.__srvx__;
94
- if (!server && prop === "address") return () => ({
95
- address: "",
96
- family: "",
97
- port: 0
98
- });
99
- return server?.node?.server?.[prop];
100
- } });
96
+ return nodeServerStub;
101
97
  };
102
98
  res = await cb();
103
99
  } finally {
@@ -107,10 +103,36 @@ async function interceptListen(cb) {
107
103
  return {
108
104
  res,
109
105
  listenHandler,
110
- fetchHandler
106
+ server
111
107
  };
112
108
  });
113
109
  _interceptQueue = result.catch(() => {});
114
110
  return result;
115
111
  }
112
+ var StubNodeServer = class extends EventEmitter {
113
+ constructor(getServer) {
114
+ super();
115
+ return new Proxy(this, { get(target, prop, receiver) {
116
+ const server = getServer();
117
+ if (!server) return Reflect.get(target, prop, receiver);
118
+ const value = server[prop];
119
+ if (typeof value === "function") return value.bind(server);
120
+ return value;
121
+ } });
122
+ }
123
+ address() {
124
+ return {
125
+ address: "",
126
+ family: "",
127
+ port: 0
128
+ };
129
+ }
130
+ listen() {
131
+ return this;
132
+ }
133
+ close(callback) {
134
+ callback?.();
135
+ return this;
136
+ }
137
+ };
116
138
  export { defaultEntries, defaultExts, loadServerEntry };
package/dist/tracing.mjs CHANGED
@@ -1,3 +1,24 @@
1
+ /**
2
+ *
3
+ * @experimental Channel names, event types and config options may change in future releases.
4
+ *
5
+ * Tracing plugin that adds diagnostics channel tracing to middleware and fetch handlers.
6
+ *
7
+ * This plugin wraps all middleware and the fetch handler with tracing instrumentation,
8
+ * allowing you to subscribe to `srvx.request` and `srvx.middleware` tracing channels.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { serve } from "srvx";
13
+ * import { tracingPlugin } from "srvx/tracing";
14
+ *
15
+ * const server = serve({
16
+ * fetch: (req) => new Response("OK"),
17
+ * middleware: [myMiddleware],
18
+ * plugins: [tracingPlugin()],
19
+ * });
20
+ * ```
21
+ */
1
22
  function tracingPlugin(opts = {}) {
2
23
  return (server) => {
3
24
  const { tracingChannel } = globalThis.process?.getBuiltinModule?.("node:diagnostics_channel") || {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "srvx",
3
- "version": "0.11.6",
3
+ "version": "0.11.8",
4
4
  "description": "Universal Server.",
5
5
  "homepage": "https://srvx.h3.dev",
6
6
  "license": "MIT",
@@ -59,7 +59,7 @@
59
59
  "vitest": "vitest"
60
60
  },
61
61
  "devDependencies": {
62
- "@cloudflare/workers-types": "^4.20260217.0",
62
+ "@cloudflare/workers-types": "^4.20260304.0",
63
63
  "@hono/node-server": "^1.19.9",
64
64
  "@mitata/counters": "^0.0.8",
65
65
  "@mjackson/node-fetch-server": "^0.7.0",
@@ -67,10 +67,10 @@
67
67
  "@types/bun": "^1.3.9",
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.0",
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.192",
73
+ "@typescript/native-preview": "^7.0.0-dev.20260225.1",
74
74
  "@vitest/coverage-v8": "^4.0.18",
75
75
  "@whatwg-node/server": "^0.10.18",
76
76
  "automd": "^0.4.3",
@@ -83,10 +83,10 @@
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.31",
87
+ "oxfmt": "^0.35.0",
88
+ "oxlint": "^1.50.0",
89
+ "srvx-release": "npm:srvx@^0.11.7",
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
  }