srvx 0.5.1 → 0.5.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.
@@ -1,5 +1,6 @@
1
1
  import { ServerOptions, Server, BunFetchHandler } from '../types.mjs';
2
2
  import * as Bun from 'bun';
3
+ export { F as URL } from '../shared/srvx.DEE2RO4O.mjs';
3
4
  import 'node:http';
4
5
  import 'node:https';
5
6
  import 'node:net';
@@ -1,4 +1,5 @@
1
- import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.FQfHxe2J.mjs';
1
+ import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.Ctaz0clH.mjs';
2
+ export { F as URL } from '../shared/srvx.Ctaz0clH.mjs';
2
3
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
3
4
  import 'node:fs';
4
5
 
@@ -5,7 +5,8 @@ import 'node:https';
5
5
  import 'node:net';
6
6
  import 'bun';
7
7
 
8
+ declare const URL: typeof globalThis.URL;
8
9
  declare const Response: typeof globalThis.Response;
9
10
  declare function serve(options: ServerOptions): Server<CF.ExportedHandlerFetchHandler>;
10
11
 
11
- export { Response, serve };
12
+ export { Response, URL, serve };
@@ -1,6 +1,7 @@
1
1
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
2
2
  import { w as wrapFetchOnError } from '../shared/srvx.BD9sRkkl.mjs';
3
3
 
4
+ const URL = globalThis.URL;
4
5
  const Response = globalThis.Response;
5
6
  function serve(options) {
6
7
  return new CloudflareServer(options);
@@ -49,4 +50,4 @@ class CloudflareServer {
49
50
  }
50
51
  }
51
52
 
52
- export { Response, serve };
53
+ export { Response, URL, serve };
@@ -1,4 +1,5 @@
1
1
  import { ServerOptions, Server, DenoFetchHandler } from '../types.mjs';
2
+ export { F as URL } from '../shared/srvx.DEE2RO4O.mjs';
2
3
  import 'node:http';
3
4
  import 'node:https';
4
5
  import 'node:net';
@@ -1,4 +1,5 @@
1
- import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.FQfHxe2J.mjs';
1
+ import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.Ctaz0clH.mjs';
2
+ export { F as URL } from '../shared/srvx.Ctaz0clH.mjs';
2
3
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
3
4
  import 'node:fs';
4
5
 
@@ -5,7 +5,8 @@ import 'node:net';
5
5
  import 'bun';
6
6
  import '@cloudflare/workers-types';
7
7
 
8
+ declare const URL: typeof globalThis.URL;
8
9
  declare const Response: typeof globalThis.Response;
9
10
  declare function serve(options: ServerOptions): Server;
10
11
 
11
- export { Response, serve };
12
+ export { Response, URL, serve };
@@ -1,6 +1,7 @@
1
1
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
2
2
  import { w as wrapFetchOnError } from '../shared/srvx.BD9sRkkl.mjs';
3
3
 
4
+ const URL = globalThis.URL;
4
5
  const Response = globalThis.Response;
5
6
  function serve(options) {
6
7
  return new GenericServer(options);
@@ -27,4 +28,4 @@ class GenericServer {
27
28
  }
28
29
  }
29
30
 
30
- export { Response, serve };
31
+ export { Response, URL, serve };
@@ -1,4 +1,5 @@
1
1
  import { ServerRequest, ServerOptions, Server, FetchHandler, NodeHttpHandler } from '../types.mjs';
2
+ export { F as URL } from '../shared/srvx.DEE2RO4O.mjs';
2
3
  import NodeHttp__default from 'node:http';
3
4
  import { Readable } from 'node:stream';
4
5
  import 'node:https';
@@ -1,7 +1,8 @@
1
1
  import NodeHttp from 'node:http';
2
2
  import NodeHttps from 'node:https';
3
3
  import { splitSetCookieString } from 'cookie-es';
4
- import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.FQfHxe2J.mjs';
4
+ import { r as resolveTLSOptions, a as resolvePortAndHost, p as printListening, f as fmtURL } from '../shared/srvx.Ctaz0clH.mjs';
5
+ export { F as URL } from '../shared/srvx.Ctaz0clH.mjs';
5
6
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
6
7
  import { w as wrapFetchOnError } from '../shared/srvx.BD9sRkkl.mjs';
7
8
  import 'node:fs';
@@ -5,9 +5,10 @@ import 'node:net';
5
5
  import 'bun';
6
6
  import '@cloudflare/workers-types';
7
7
 
8
+ declare const URL: typeof globalThis.URL;
8
9
  declare const Response: typeof globalThis.Response;
9
10
  type ServiceWorkerHandler = (request: ServerRequest, event: FetchEvent) => Response | Promise<Response>;
10
11
  declare function serve(options: ServerOptions): Server<ServiceWorkerHandler>;
11
12
 
12
- export { Response, serve };
13
+ export { Response, URL, serve };
13
14
  export type { ServiceWorkerHandler };
@@ -1,6 +1,7 @@
1
1
  import { w as wrapFetch } from '../shared/srvx.DhN4g5wJ.mjs';
2
2
  import { w as wrapFetchOnError } from '../shared/srvx.BD9sRkkl.mjs';
3
3
 
4
+ const URL = globalThis.URL;
4
5
  const Response = globalThis.Response;
5
6
  const isBrowserWindow = typeof window !== "undefined" && typeof navigator !== "undefined";
6
7
  const isServiceWorker = typeof self !== "undefined" && "skipWaiting" in self;
@@ -94,4 +95,4 @@ class ServiceWorkerServer {
94
95
  }
95
96
  }
96
97
 
97
- export { Response, serve };
98
+ export { Response, URL, serve };
@@ -0,0 +1,172 @@
1
+ import { readFileSync } from 'node:fs';
2
+
3
+ function resolvePortAndHost(opts) {
4
+ const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
5
+ const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
6
+ const hostname = opts.hostname ?? globalThis.process?.env.HOST;
7
+ return { port, hostname };
8
+ }
9
+ function fmtURL(host, port, secure) {
10
+ if (!host || !port) {
11
+ return void 0;
12
+ }
13
+ if (host.includes(":")) {
14
+ host = `[${host}]`;
15
+ }
16
+ return `http${secure ? "s" : ""}://${host}:${port}/`;
17
+ }
18
+ function printListening(opts, url) {
19
+ if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) {
20
+ return;
21
+ }
22
+ const _url = new URL(url);
23
+ const allInterfaces = _url.hostname === "[::]" || _url.hostname === "0.0.0.0";
24
+ if (allInterfaces) {
25
+ _url.hostname = "localhost";
26
+ url = _url.href;
27
+ }
28
+ let listeningOn = `\u279C Listening on:`;
29
+ let additionalInfo = allInterfaces ? " (all interfaces)" : "";
30
+ if (globalThis.process.stdout?.isTTY) {
31
+ listeningOn = `\x1B[32m${listeningOn}\x1B[0m`;
32
+ url = `\x1B[36m${url}\x1B[0m`;
33
+ additionalInfo = `\x1B[2m${additionalInfo}\x1B[0m`;
34
+ }
35
+ console.log(` ${listeningOn} ${url}${additionalInfo}`);
36
+ }
37
+ function resolveTLSOptions(opts) {
38
+ if (!opts.tls || opts.protocol === "http") {
39
+ return;
40
+ }
41
+ const cert = resolveCertOrKey(opts.tls.cert);
42
+ const key = resolveCertOrKey(opts.tls.key);
43
+ if (!cert && !key) {
44
+ if (opts.protocol === "https") {
45
+ throw new TypeError(
46
+ "TLS `cert` and `key` must be provided for `https` protocol."
47
+ );
48
+ }
49
+ return;
50
+ }
51
+ if (!cert || !key) {
52
+ throw new TypeError("TLS `cert` and `key` must be provided together.");
53
+ }
54
+ return {
55
+ cert,
56
+ key,
57
+ passphrase: opts.tls.passphrase
58
+ };
59
+ }
60
+ function resolveCertOrKey(value) {
61
+ if (!value) {
62
+ return;
63
+ }
64
+ if (typeof value !== "string") {
65
+ throw new TypeError(
66
+ "TLS certificate and key must be strings in PEM format or file paths."
67
+ );
68
+ }
69
+ if (value.startsWith("-----BEGIN ")) {
70
+ return value;
71
+ }
72
+ return readFileSync(value, "utf8");
73
+ }
74
+
75
+ const FastURL = /* @__PURE__ */ (() => {
76
+ const FastURL2 = class URL {
77
+ #originalURL;
78
+ #parsedURL;
79
+ constructor(url) {
80
+ this.#originalURL = url;
81
+ }
82
+ get _url() {
83
+ if (!this.#parsedURL) {
84
+ this.#parsedURL = new globalThis.URL(this.#originalURL);
85
+ }
86
+ return this.#parsedURL;
87
+ }
88
+ toString() {
89
+ return this._url.toString();
90
+ }
91
+ toJSON() {
92
+ return this.toString();
93
+ }
94
+ get pathname() {
95
+ if (this.#parsedURL) {
96
+ return this.#parsedURL.pathname;
97
+ }
98
+ if (!this._pathname) {
99
+ const url = this.#originalURL;
100
+ const protoIndex = url.indexOf("://");
101
+ if (protoIndex === -1) {
102
+ return this._url.pathname;
103
+ }
104
+ const pIndex = url.indexOf(
105
+ "/",
106
+ protoIndex + 4
107
+ /* :// */
108
+ );
109
+ if (pIndex === -1) {
110
+ return this._url.pathname;
111
+ }
112
+ const qIndex = this._urlqindex = url.indexOf("?", pIndex);
113
+ this._pathname = url.slice(pIndex, qIndex === -1 ? void 0 : qIndex);
114
+ }
115
+ return this._pathname;
116
+ }
117
+ set pathname(value) {
118
+ this._url.pathname = value;
119
+ }
120
+ get searchParams() {
121
+ if (this.#parsedURL) {
122
+ return this.#parsedURL.searchParams;
123
+ }
124
+ if (!this._query) {
125
+ this._query = new URLSearchParams(this.search);
126
+ }
127
+ return this._query;
128
+ }
129
+ get search() {
130
+ if (this.#parsedURL) {
131
+ return this.#parsedURL.search;
132
+ }
133
+ if (!this._search) {
134
+ const qIndex = this._urlqindex;
135
+ if (qIndex === -1) {
136
+ this._search = "";
137
+ } else {
138
+ this._search = qIndex === void 0 ? this._url.search : this.#originalURL.slice(this._urlqindex);
139
+ }
140
+ }
141
+ return this._search;
142
+ }
143
+ set search(value) {
144
+ this._url.search = value;
145
+ }
146
+ };
147
+ const slowProps = [
148
+ "hash",
149
+ "host",
150
+ "hostname",
151
+ "href",
152
+ "origin",
153
+ "password",
154
+ "port",
155
+ "protocol",
156
+ "username"
157
+ ];
158
+ for (const prop of slowProps) {
159
+ Object.defineProperty(FastURL2.prototype, prop, {
160
+ get() {
161
+ return this._url[prop];
162
+ },
163
+ set(value) {
164
+ this._url[prop] = value;
165
+ }
166
+ });
167
+ }
168
+ Object.setPrototypeOf(FastURL2, globalThis.URL);
169
+ return FastURL2;
170
+ })();
171
+
172
+ export { FastURL as F, resolvePortAndHost as a, fmtURL as f, printListening as p, resolveTLSOptions as r };
@@ -0,0 +1,5 @@
1
+ declare const FastURL: {
2
+ new (url: string): URL;
3
+ };
4
+
5
+ export { FastURL as F };
package/dist/types.d.mts CHANGED
@@ -6,6 +6,7 @@ import * as CF from '@cloudflare/workers-types';
6
6
 
7
7
  type MaybePromise<T> = T | Promise<T>;
8
8
  declare const Response: typeof globalThis.Response;
9
+ declare const URL: typeof globalThis.URL;
9
10
  /**
10
11
  * Create a new server instance.
11
12
  */
@@ -231,5 +232,5 @@ type DenoFetchHandler = (request: Request, info?: Deno.ServeHandlerInfo<Deno.Net
231
232
  type NodeHttpHandler = (nodeReq: NodeHttp.IncomingMessage, nodeRes: NodeHttp.ServerResponse) => void | Promise<void>;
232
233
  type CloudflareFetchHandler = CF.ExportedHandlerFetchHandler;
233
234
 
234
- export { Response, serve };
235
+ export { Response, URL, serve };
235
236
  export type { BunFetchHandler, CloudflareFetchHandler, DenoFetchHandler, ErrorHandler, FetchHandler, NodeHttpHandler, Server, ServerHandler, ServerOptions, ServerPlugin, ServerPluginInstance, ServerRequest, ServerRuntimeContext };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "srvx",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Universal Server API based on web platform standards. Works seamlessly with Deno, Bun and Node.js.",
5
- "repository": "h3js/srvx",
6
5
  "homepage": "https://srvx.h3.dev",
6
+ "repository": "h3js/srvx",
7
7
  "license": "MIT",
8
8
  "sideEffects": false,
9
9
  "type": "module",
@@ -31,10 +31,14 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "bench:node": "node test/bench-node/_run.mjs",
34
+ "bench:url:bun": "bun run ./test/url.bench.ts",
35
+ "bench:url:deno": "deno run -A ./test/url.bench.ts",
36
+ "bench:url:node": "pnpm node-ts --expose-gc --allow-natives-syntax ./test/url.bench.ts",
34
37
  "build": "unbuild",
35
38
  "dev": "vitest dev",
36
39
  "lint": "eslint . && prettier -c .",
37
40
  "lint:fix": "automd && eslint . --fix && prettier -w .",
41
+ "node-ts": "node --disable-warning=ExperimentalWarning --experimental-strip-types",
38
42
  "prepack": "pnpm build",
39
43
  "play:bun": "bun playground/app.mjs",
40
44
  "play:cf": "pnpx wrangler dev playground/app.mjs",
@@ -55,6 +59,7 @@
55
59
  "devDependencies": {
56
60
  "@cloudflare/workers-types": "^4.20250423.0",
57
61
  "@hono/node-server": "^1.14.1",
62
+ "@mitata/counters": "^0.0.8",
58
63
  "@mjackson/node-fetch-server": "^0.6.1",
59
64
  "@types/bun": "^1.2.10",
60
65
  "@types/deno": "^2.2.0",
@@ -67,7 +72,7 @@
67
72
  "eslint-config-unjs": "^0.4.2",
68
73
  "execa": "^9.5.2",
69
74
  "get-port-please": "^3.1.2",
70
- "jiti": "^2.4.2",
75
+ "mitata": "^1.0.34",
71
76
  "prettier": "^3.5.3",
72
77
  "typescript": "^5.8.3",
73
78
  "unbuild": "^3.5.0",
@@ -1,75 +0,0 @@
1
- import { readFileSync } from 'node:fs';
2
-
3
- function resolvePortAndHost(opts) {
4
- const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
5
- const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
6
- const hostname = opts.hostname ?? globalThis.process?.env.HOST;
7
- return { port, hostname };
8
- }
9
- function fmtURL(host, port, secure) {
10
- if (!host || !port) {
11
- return void 0;
12
- }
13
- if (host.includes(":")) {
14
- host = `[${host}]`;
15
- }
16
- return `http${secure ? "s" : ""}://${host}:${port}/`;
17
- }
18
- function printListening(opts, url) {
19
- if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) {
20
- return;
21
- }
22
- const _url = new URL(url);
23
- const allInterfaces = _url.hostname === "[::]" || _url.hostname === "0.0.0.0";
24
- if (allInterfaces) {
25
- _url.hostname = "localhost";
26
- url = _url.href;
27
- }
28
- let listeningOn = `\u279C Listening on:`;
29
- let additionalInfo = allInterfaces ? " (all interfaces)" : "";
30
- if (globalThis.process.stdout?.isTTY) {
31
- listeningOn = `\x1B[32m${listeningOn}\x1B[0m`;
32
- url = `\x1B[36m${url}\x1B[0m`;
33
- additionalInfo = `\x1B[2m${additionalInfo}\x1B[0m`;
34
- }
35
- console.log(` ${listeningOn} ${url}${additionalInfo}`);
36
- }
37
- function resolveTLSOptions(opts) {
38
- if (!opts.tls || opts.protocol === "http") {
39
- return;
40
- }
41
- const cert = resolveCertOrKey(opts.tls.cert);
42
- const key = resolveCertOrKey(opts.tls.key);
43
- if (!cert && !key) {
44
- if (opts.protocol === "https") {
45
- throw new TypeError(
46
- "TLS `cert` and `key` must be provided for `https` protocol."
47
- );
48
- }
49
- return;
50
- }
51
- if (!cert || !key) {
52
- throw new TypeError("TLS `cert` and `key` must be provided together.");
53
- }
54
- return {
55
- cert,
56
- key,
57
- passphrase: opts.tls.passphrase
58
- };
59
- }
60
- function resolveCertOrKey(value) {
61
- if (!value) {
62
- return;
63
- }
64
- if (typeof value !== "string") {
65
- throw new TypeError(
66
- "TLS certificate and key must be strings in PEM format or file paths."
67
- );
68
- }
69
- if (value.startsWith("-----BEGIN ")) {
70
- return value;
71
- }
72
- return readFileSync(value, "utf8");
73
- }
74
-
75
- export { resolvePortAndHost as a, fmtURL as f, printListening as p, resolveTLSOptions as r };