srvx-nightly 0.11.7 → 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";
@@ -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";
@@ -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) {
@@ -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,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
@@ -179,7 +179,7 @@ function getResponseFormat(res) {
179
179
  }
180
180
  const srvxMeta = {
181
181
  name: "srvx-nightly",
182
- version: "0.11.7",
182
+ version: "0.11.8",
183
183
  description: "Universal Server."
184
184
  };
185
185
  function usage(mainOpts) {
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/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-nightly",
3
- "version": "0.11.7",
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
  }