Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

21
Frontend-Learner/node_modules/srvx/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) Pooya Parsa <pooya@pi0.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

96
Frontend-Learner/node_modules/srvx/README.md generated vendored Normal file
View file

@ -0,0 +1,96 @@
# λ srvx
<!-- automd:badges color=yellow packagephobia -->
[![npm version](https://img.shields.io/npm/v/srvx?color=yellow)](https://npmjs.com/package/srvx)
[![npm downloads](https://img.shields.io/npm/dm/srvx?color=yellow)](https://npm.chart.dev/srvx)
[![install size](https://badgen.net/packagephobia/install/srvx?color=yellow)](https://packagephobia.com/result?p=srvx)
<!-- /automd -->
Universal Server based on web standards. Works with [Deno](https://deno.com/), [Bun](https://bun.sh/) and [Node.js](https://nodejs.org/en).
- ✅ Zero dependency
- ✅ Full featured CLI with watcher, error handler, serve static and logger
- ✅ Seamless runtime integration with same API ([handler](https://srvx.h3.dev/guide/handler) and [instance](https://srvx.h3.dev/guide/server)).
- ✅ [Node.js compatibility](https://srvx.h3.dev/guide/node) with a [**close to native performance**](https://github.com/h3js/srvx/tree/main/test/bench-node).
- ✅ Zero overhead [Deno](https://deno.com/) and [Bun](https://bun.sh/) support.
## Quick start
```js
export default {
fetch(req: Request) {
return Response.json({ hello: "world!" });
},
};
```
Then, run the server using your favorite runtime:
```bash
# Node.js
$ npx srvx # npm
$ pnpx srvx # pnpm
$ yarn dlx srvx # yarn
# Deno
$ deno -A npm:srvx
# Bun
$ bunx --bun srvx
```
👉 **Visit the 📖 [Documentation](https://srvx.h3.dev/) to learn more.**
## Starter Examples
[➤ Online Playground](https://stackblitz.com/fork/github/h3js/srvx/tree/main/examples/stackblitz?startScript=dev&file=server.mjs)
<!-- automd:examples -->
| Example | Source | Try |
| ---------------- | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| `elysia` | [examples/elysia](https://github.com/h3js/srvx/tree/main/examples/elysia/) | `npx giget gh:h3js/srvx/examples/elysia srvx-elysia` |
| `express` | [examples/express](https://github.com/h3js/srvx/tree/main/examples/express/) | `npx giget gh:h3js/srvx/examples/express srvx-express` |
| `fastify` | [examples/fastify](https://github.com/h3js/srvx/tree/main/examples/fastify/) | `npx giget gh:h3js/srvx/examples/fastify srvx-fastify` |
| `h3` | [examples/h3](https://github.com/h3js/srvx/tree/main/examples/h3/) | `npx giget gh:h3js/srvx/examples/h3 srvx-h3` |
| `hello-world` | [examples/hello-world](https://github.com/h3js/srvx/tree/main/examples/hello-world/) | `npx giget gh:h3js/srvx/examples/hello-world srvx-hello-world` |
| `hono` | [examples/hono](https://github.com/h3js/srvx/tree/main/examples/hono/) | `npx giget gh:h3js/srvx/examples/hono srvx-hono` |
| `jsx` | [examples/jsx](https://github.com/h3js/srvx/tree/main/examples/jsx/) | `npx giget gh:h3js/srvx/examples/jsx srvx-jsx` |
| `node-handler` | [examples/node-handler](https://github.com/h3js/srvx/tree/main/examples/node-handler/) | `npx giget gh:h3js/srvx/examples/node-handler srvx-node-handler` |
| `service-worker` | [examples/service-worker](https://github.com/h3js/srvx/tree/main/examples/service-worker/) | `npx giget gh:h3js/srvx/examples/service-worker srvx-service-worker` |
| `tracing` | [examples/tracing](https://github.com/h3js/srvx/tree/main/examples/tracing/) | `npx giget gh:h3js/srvx/examples/tracing srvx-tracing` |
| `websocket` | [examples/websocket](https://github.com/h3js/srvx/tree/main/examples/websocket/) | `npx giget gh:h3js/srvx/examples/websocket srvx-websocket` |
<!-- /automd -->
## Contribution
- Clone this repository
- Install the latest LTS version of [Node.js](https://nodejs.org/en/)
- Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable`
- Install dependencies using `pnpm install`
- **Prepare stub mode using `pnpm build --stub`**
- Run interactive tests using `pnpm dev`
## License
<!-- automd:contributors author=pi0 license=MIT -->
Published under the [MIT](https://github.com/h3js/srvx/blob/main/LICENSE) license.
Made by [@pi0](https://github.com/pi0) and [community](https://github.com/h3js/srvx/graphs/contributors) 💛
<br><br>
<a href="https://github.com/h3js/srvx/graphs/contributors">
<img src="https://contrib.rocks/image?repo=h3js/srvx" />
</a>
<!-- /automd -->
<!-- automd:with-automd -->
---
_🤖 auto updated with [automd](https://automd.unjs.io)_
<!-- /automd -->

8
Frontend-Learner/node_modules/srvx/bin/srvx.mjs generated vendored Normal file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env node
import { main } from "../dist/cli.mjs";
await main({
command: "srvx",
docs: "https://srvx.h3.dev",
issues: "https://github.com/h3js/srvx/issues",
});

View file

@ -0,0 +1,18 @@
//#region src/_color.ts
const noColor = /* @__PURE__ */ (() => {
const env = globalThis.process?.env ?? {};
return env.NO_COLOR === "1" || env.TERM === "dumb";
})();
const _c = (c, r = 39) => (t) => noColor ? t : `\u001B[${c}m${t}\u001B[${r}m`;
const bold = /* @__PURE__ */ _c(1, 22);
const red = /* @__PURE__ */ _c(31);
const green = /* @__PURE__ */ _c(32);
const yellow = /* @__PURE__ */ _c(33);
const blue = /* @__PURE__ */ _c(34);
const magenta = /* @__PURE__ */ _c(35);
const cyan = /* @__PURE__ */ _c(36);
const gray = /* @__PURE__ */ _c(90);
const url = (title, url$1) => noColor ? `[${title}](${url$1})` : `\u001B]8;;${url$1}\u001B\\${title}\u001B]8;;\u001B\\`;
//#endregion
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 };

View file

@ -0,0 +1,31 @@
//#region src/_inherit.ts
function lazyInherit(target, source, sourceKey) {
for (const key of [...Object.getOwnPropertyNames(source), ...Object.getOwnPropertySymbols(source)]) {
if (key === "constructor") continue;
const targetDesc = Object.getOwnPropertyDescriptor(target, key);
const desc = Object.getOwnPropertyDescriptor(source, key);
let modified = false;
if (desc.get) {
modified = true;
desc.get = targetDesc?.get || function() {
return this[sourceKey][key];
};
}
if (desc.set) {
modified = true;
desc.set = targetDesc?.set || function(value) {
this[sourceKey][key] = value;
};
}
if (!targetDesc?.value && typeof desc.value === "function") {
modified = true;
desc.value = function(...args) {
return this[sourceKey][key](...args);
};
}
if (modified) Object.defineProperty(target, key, desc);
}
}
//#endregion
export { lazyInherit as t };

View file

@ -0,0 +1,59 @@
import { i as gray, s as red } from "./_color.mjs";
//#region src/_middleware.ts
function wrapFetch(server) {
const fetchHandler = server.options.fetch;
const middleware = server.options.middleware || [];
return middleware.length === 0 ? fetchHandler : (request) => callMiddleware(request, fetchHandler, middleware, 0);
}
function callMiddleware(request, fetchHandler, middleware, index) {
if (index === middleware.length) return fetchHandler(request);
return middleware[index](request, () => callMiddleware(request, fetchHandler, middleware, index + 1));
}
//#endregion
//#region src/_plugins.ts
const errorPlugin = (server) => {
const errorHandler = server.options.error;
if (!errorHandler) return;
server.options.middleware.unshift((_req, next) => {
try {
const res = next();
return res instanceof Promise ? res.catch((error) => errorHandler(error)) : res;
} catch (error) {
return errorHandler(error);
}
});
};
const gracefulShutdownPlugin = (server) => {
const config = server.options?.gracefulShutdown;
if (!globalThis.process?.on || config === false || config === void 0 && (process.env.CI || process.env.TEST)) return;
const gracefulShutdown = config === true || !config?.gracefulTimeout ? Number.parseInt(process.env.SERVER_SHUTDOWN_TIMEOUT || "") || 3 : config.gracefulTimeout;
const forceShutdown = config === true || !config?.forceTimeout ? Number.parseInt(process.env.SERVER_FORCE_SHUTDOWN_TIMEOUT || "") || 5 : config.forceTimeout;
let isShuttingDown = false;
const shutdown = async () => {
if (isShuttingDown) return;
isShuttingDown = true;
const w = process.stderr.write.bind(process.stderr);
w(gray(`\nShutting down server in ${gracefulShutdown}s...`));
let timeout;
await Promise.race([server.close().finally(() => {
clearTimeout(timeout);
w(gray(" Server closed.\n"));
}), new Promise((resolve) => {
timeout = setTimeout(() => {
w(gray(`\nForce closing connections in ${forceShutdown}s...`));
timeout = setTimeout(() => {
w(red("\nCould not close connections in time, force exiting."));
resolve();
}, forceShutdown * 1e3);
return server.close(true);
}, gracefulShutdown * 1e3);
})]);
globalThis.process.exit(0);
};
for (const sig of ["SIGINT", "SIGTERM"]) globalThis.process.on(sig, shutdown);
};
//#endregion
export { gracefulShutdownPlugin as n, wrapFetch as r, errorPlugin as t };

View file

@ -0,0 +1,28 @@
//#region src/_url.d.ts
type URLInit = {
protocol: string;
host: string;
pathname: string;
search: string;
};
/**
* URL wrapper with fast paths to access to the following props:
*
* - `url.pathname`
* - `url.search`
* - `url.searchParams`
* - `url.protocol`
*
* **NOTES:**
*
* - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
* - Triggering the setters or getters on other props will deoptimize to full URL parsing.
* - Changes to `searchParams` will be discarded as we don't track them.
*/
declare const FastURL: {
new (url: string | URLInit): URL & {
_url: URL;
};
};
//#endregion
export { FastURL as t };

View file

@ -0,0 +1,118 @@
import { t as lazyInherit } from "./_inherit.mjs";
//#region src/_url.ts
/**
* URL wrapper with fast paths to access to the following props:
*
* - `url.pathname`
* - `url.search`
* - `url.searchParams`
* - `url.protocol`
*
* **NOTES:**
*
* - It is assumed that the input URL is **already encoded** and formatted from an HTTP request and contains no hash.
* - Triggering the setters or getters on other props will deoptimize to full URL parsing.
* - Changes to `searchParams` will be discarded as we don't track them.
*/
const FastURL = /* @__PURE__ */ (() => {
const NativeURL = globalThis.URL;
const FastURL$1 = class URL {
#url;
#href;
#protocol;
#host;
#pathname;
#search;
#searchParams;
#pos;
constructor(url) {
if (typeof url === "string") this.#href = url;
else {
this.#protocol = url.protocol;
this.#host = url.host;
this.#pathname = url.pathname;
this.#search = url.search;
}
}
static [Symbol.hasInstance](val) {
return val instanceof NativeURL;
}
get _url() {
if (this.#url) return this.#url;
this.#url = new NativeURL(this.href);
this.#href = void 0;
this.#protocol = void 0;
this.#host = void 0;
this.#pathname = void 0;
this.#search = void 0;
this.#searchParams = void 0;
this.#pos = void 0;
return this.#url;
}
get href() {
if (this.#url) return this.#url.href;
if (!this.#href) this.#href = `${this.#protocol || "http:"}//${this.#host || "localhost"}${this.#pathname || "/"}${this.#search || ""}`;
return this.#href;
}
#getPos() {
if (!this.#pos) {
const url = this.href;
const protoIndex = url.indexOf("://");
const pathnameIndex = protoIndex === -1 ? -1 : url.indexOf("/", protoIndex + 4);
this.#pos = [
protoIndex,
pathnameIndex,
pathnameIndex === -1 ? -1 : url.indexOf("?", pathnameIndex)
];
}
return this.#pos;
}
get pathname() {
if (this.#url) return this.#url.pathname;
if (this.#pathname === void 0) {
const [, pathnameIndex, queryIndex] = this.#getPos();
if (pathnameIndex === -1) return this._url.pathname;
this.#pathname = this.href.slice(pathnameIndex, queryIndex === -1 ? void 0 : queryIndex);
}
return this.#pathname;
}
get search() {
if (this.#url) return this.#url.search;
if (this.#search === void 0) {
const [, pathnameIndex, queryIndex] = this.#getPos();
if (pathnameIndex === -1) return this._url.search;
const url = this.href;
this.#search = queryIndex === -1 || queryIndex === url.length - 1 ? "" : url.slice(queryIndex);
}
return this.#search;
}
get searchParams() {
if (this.#url) return this.#url.searchParams;
if (!this.#searchParams) this.#searchParams = new URLSearchParams(this.search);
return this.#searchParams;
}
get protocol() {
if (this.#url) return this.#url.protocol;
if (this.#protocol === void 0) {
const [protocolIndex] = this.#getPos();
if (protocolIndex === -1) return this._url.protocol;
this.#protocol = this.href.slice(0, protocolIndex + 1);
}
return this.#protocol;
}
toString() {
return this.href;
}
toJSON() {
return this.href;
}
};
lazyInherit(FastURL$1.prototype, NativeURL.prototype, "_url");
Object.setPrototypeOf(FastURL$1.prototype, NativeURL.prototype);
Object.setPrototypeOf(FastURL$1, NativeURL);
return FastURL$1;
})();
//#endregion
export { FastURL as t };

View file

@ -0,0 +1,71 @@
//#region src/_utils.ts
function resolvePortAndHost(opts) {
const _port = opts.port ?? globalThis.process?.env.PORT ?? 3e3;
const port = typeof _port === "number" ? _port : Number.parseInt(_port, 10);
if (port < 0 || port > 65535) throw new RangeError(`Port must be between 0 and 65535 (got "${port}").`);
return {
port,
hostname: opts.hostname ?? globalThis.process?.env.HOST
};
}
function fmtURL(host, port, secure) {
if (!host || !port) return;
if (host.includes(":")) host = `[${host}]`;
return `http${secure ? "s" : ""}://${host}:${port}/`;
}
function printListening(opts, url) {
if (!url || (opts.silent ?? globalThis.process?.env?.TEST)) return;
const _url = new URL(url);
const allInterfaces = _url.hostname === "[::]" || _url.hostname === "0.0.0.0";
if (allInterfaces) {
_url.hostname = "localhost";
url = _url.href;
}
let listeningOn = `➜ Listening on:`;
let additionalInfo = allInterfaces ? " (all interfaces)" : "";
if (globalThis.process.stdout?.isTTY) {
listeningOn = `\u001B[32m${listeningOn}\u001B[0m`;
url = `\u001B[36m${url}\u001B[0m`;
additionalInfo = `\u001B[2m${additionalInfo}\u001B[0m`;
}
console.log(`${listeningOn} ${url}${additionalInfo}`);
}
function resolveTLSOptions(opts) {
if (!opts.tls || opts.protocol === "http") return;
const cert = resolveCertOrKey(opts.tls.cert);
const key = resolveCertOrKey(opts.tls.key);
if (!cert && !key) {
if (opts.protocol === "https") throw new TypeError("TLS `cert` and `key` must be provided for `https` protocol.");
return;
}
if (!cert || !key) throw new TypeError("TLS `cert` and `key` must be provided together.");
return {
cert,
key,
passphrase: opts.tls.passphrase
};
}
function resolveCertOrKey(value) {
if (!value) return;
if (typeof value !== "string") throw new TypeError("TLS certificate and key must be strings in PEM format or file paths.");
if (value.startsWith("-----BEGIN ")) return value;
const { readFileSync } = process.getBuiltinModule("node:fs");
return readFileSync(value, "utf8");
}
function createWaitUntil() {
const promises = /* @__PURE__ */ new Set();
return {
waitUntil: (promise) => {
if (typeof promise?.then !== "function") return;
promises.add(Promise.resolve(promise).catch(console.error).finally(() => {
promises.delete(promise);
}));
},
wait: () => {
return Promise.all(promises);
}
};
}
//#endregion
export { resolveTLSOptions as a, resolvePortAndHost as i, fmtURL as n, printListening as r, createWaitUntil as t };

View file

@ -0,0 +1,157 @@
import { t as lazyInherit } from "./_inherit.mjs";
//#region src/adapters/_node/response.ts
/**
* Fast Response for Node.js runtime
*
* It is faster because in most cases it doesn't create a full Response instance.
*/
const NodeResponse = /* @__PURE__ */ (() => {
const NativeResponse = globalThis.Response;
const STATUS_CODES = globalThis.process?.getBuiltinModule?.("node:http")?.STATUS_CODES || {};
class NodeResponse$1 {
#body;
#init;
#headers;
#response;
constructor(body, init) {
this.#body = body;
this.#init = init;
}
static [Symbol.hasInstance](val) {
return val instanceof NativeResponse;
}
get status() {
return this.#response?.status || this.#init?.status || 200;
}
get statusText() {
return this.#response?.statusText || this.#init?.statusText || STATUS_CODES[this.status] || "";
}
get headers() {
if (this.#response) return this.#response.headers;
if (this.#headers) return this.#headers;
const initHeaders = this.#init?.headers;
return this.#headers = initHeaders instanceof Headers ? initHeaders : new Headers(initHeaders);
}
get ok() {
if (this.#response) return this.#response.ok;
const status = this.status;
return status >= 200 && status < 300;
}
get _response() {
if (this.#response) return this.#response;
this.#response = new NativeResponse(this.#body, this.#headers ? {
...this.#init,
headers: this.#headers
} : this.#init);
this.#init = void 0;
this.#headers = void 0;
this.#body = void 0;
return this.#response;
}
_toNodeResponse() {
const status = this.status;
const statusText = this.statusText;
let body;
let contentType;
let contentLength;
if (this.#response) body = this.#response.body;
else if (this.#body) if (this.#body instanceof ReadableStream) body = this.#body;
else if (typeof this.#body === "string") {
body = this.#body;
contentType = "text/plain; charset=UTF-8";
contentLength = Buffer.byteLength(this.#body);
} else if (this.#body instanceof ArrayBuffer) {
body = Buffer.from(this.#body);
contentLength = this.#body.byteLength;
} else if (this.#body instanceof Uint8Array) {
body = this.#body;
contentLength = this.#body.byteLength;
} else if (this.#body instanceof DataView) {
body = Buffer.from(this.#body.buffer);
contentLength = this.#body.byteLength;
} else if (this.#body instanceof Blob) {
body = this.#body.stream();
contentType = this.#body.type;
contentLength = this.#body.size;
} else if (typeof this.#body.pipe === "function") body = this.#body;
else body = this._response.body;
const headers = [];
const initHeaders = this.#init?.headers;
const headerEntries = this.#response?.headers || this.#headers || (initHeaders ? Array.isArray(initHeaders) ? initHeaders : initHeaders?.entries ? initHeaders.entries() : Object.entries(initHeaders).map(([k, v]) => [k.toLowerCase(), v]) : void 0);
let hasContentTypeHeader;
let hasContentLength;
if (headerEntries) for (const [key, value] of headerEntries) {
if (Array.isArray(value)) for (const v of value) headers.push([key, v]);
else headers.push([key, value]);
if (key === "content-type") hasContentTypeHeader = true;
else if (key === "content-length") hasContentLength = true;
}
if (contentType && !hasContentTypeHeader) headers.push(["content-type", contentType]);
if (contentLength && !hasContentLength) headers.push(["content-length", String(contentLength)]);
this.#init = void 0;
this.#headers = void 0;
this.#response = void 0;
this.#body = void 0;
return {
status,
statusText,
headers,
body
};
}
}
lazyInherit(NodeResponse$1.prototype, NativeResponse.prototype, "_response");
Object.setPrototypeOf(NodeResponse$1, NativeResponse);
Object.setPrototypeOf(NodeResponse$1.prototype, NativeResponse.prototype);
return NodeResponse$1;
})();
//#endregion
//#region src/adapters/_node/call.ts
function callNodeHandler(handler, req) {
const isMiddleware = handler.length > 2;
const nodeCtx = req.runtime?.node;
if (!nodeCtx || !nodeCtx.req || !nodeCtx.res) throw new Error("Node.js runtime context is not available.");
const { req: nodeReq, res: nodeRes } = nodeCtx;
let _headers;
const webRes = new NodeResponse(void 0, {
get status() {
return nodeRes.statusCode;
},
get statusText() {
return nodeRes.statusMessage;
},
get headers() {
if (!_headers) {
const headerEntries = [];
const rawHeaders = nodeRes.getHeaders();
for (const [name, value] of Object.entries(rawHeaders)) if (Array.isArray(value)) for (const v of value) headerEntries.push([name, v]);
else if (value) headerEntries.push([name, String(value)]);
_headers = new Headers(headerEntries);
}
return _headers;
}
});
return new Promise((resolve, reject) => {
nodeRes.once("close", () => resolve(webRes));
nodeRes.once("finish", () => resolve(webRes));
nodeRes.once("error", (error) => reject(error));
let streamPromise;
nodeRes.once("pipe", (stream) => {
streamPromise = new Promise((resolve$1) => {
stream.once("end", () => resolve$1(webRes));
stream.once("error", (error) => reject(error));
});
});
try {
if (isMiddleware) Promise.resolve(handler(nodeReq, nodeRes, (error) => error ? reject(error) : streamPromise || resolve(webRes))).catch((error) => reject(error));
else Promise.resolve(handler(nodeReq, nodeRes)).then(() => streamPromise || webRes);
} catch (error) {
reject(error);
}
});
}
//#endregion
export { NodeResponse as n, callNodeHandler as t };

View file

@ -0,0 +1,3 @@
import { t as callNodeHandler } from "./call.mjs";
export { callNodeHandler };

View file

@ -0,0 +1,273 @@
import * as NodeHttp from "node:http";
import * as NodeHttps from "node:https";
import * as NodeHttp2 from "node:http2";
import * as cloudflare_workers0 from "cloudflare:workers";
import * as NodeNet from "node:net";
import * as Bun from "bun";
import * as CF from "@cloudflare/workers-types";
//#region src/types.d.ts
type MaybePromise<T> = T | Promise<T>;
type IsAny<T> = Equal<T, any> extends true ? true : false;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
/**
* Faster URL constructor with lazy access to pathname and search params (For Node, Deno, and Bun).
*/
declare const FastURL: typeof globalThis.URL;
/**
* Faster Response constructor optimized for Node.js (same as Response for other runtimes).
*/
declare const FastResponse: typeof globalThis.Response;
/**
* Create a new server instance.
*/
declare function serve(options: ServerOptions): Server;
/**
* Web fetch compatible request handler
*/
type ServerHandler = (request: ServerRequest) => MaybePromise<Response>;
type ServerMiddleware = (request: ServerRequest, next: () => Response | Promise<Response>) => Response | Promise<Response>;
type ServerPlugin = (server: Server) => void;
/**
* Server options
*/
interface ServerOptions {
/**
* The fetch handler handles incoming requests.
*/
fetch: ServerHandler;
/**
* Handle lifecycle errors.
*
* @note This handler will set built-in Bun and Deno error handler.
*/
error?: ErrorHandler;
/**
* Server middleware handlers to run before the main fetch handler.
*/
middleware?: ServerMiddleware[];
/**
* Server plugins.
*/
plugins?: ServerPlugin[];
/**
* If set to `true`, server will not start listening automatically.
*/
manual?: boolean;
/**
* The port server should be listening to.
*
* Default is read from `PORT` environment variable or will be `3000`.
*
* **Tip:** You can set the port to `0` to use a random port.
*/
port?: string | number;
/**
* The hostname (IP or resolvable host) server listener should bound to.
*
* When not provided, server with listen to all network interfaces by default.
*
* **Important:** If you are running a server that is not expected to be exposed to the network, use `hostname: "localhost"`.
*/
hostname?: string;
/**
* Enabling this option allows multiple processes to bind to the same port, which is useful for load balancing.
*
* **Note:** Despite Node.js built-in behavior that has `exclusive` flag (opposite of `reusePort`) enabled by default, srvx uses non-exclusive mode for consistency.
*/
reusePort?: boolean;
/**
* The protocol to use for the server.
*
* Possible values are `http` and `https`.
*
* If `protocol` is not set, Server will use `http` as the default protocol or `https` if both `tls.cert` and `tls.key` options are provided.
*/
protocol?: "http" | "https";
/**
* If set to `true`, server will not print the listening address.
*/
silent?: boolean;
/**
* Graceful shutdown on SIGINT and SIGTERM signals.
*
* Supported for Node.js, Deno and Bun runtimes.
*
* @default true (disabled in test and ci environments)
*/
gracefulShutdown?: boolean | {
gracefulTimeout?: number;
forceTimeout?: number;
};
/**
* TLS server options.
*/
tls?: {
/**
* File path or inlined TLS certificate in PEM format (required).
*/
cert?: string;
/**
* File path or inlined TLS private key in PEM format (required).
*/
key?: string;
/**
* Passphrase for the private key (optional).
*/
passphrase?: string;
};
/**
* Node.js server options.
*/
node?: (NodeHttp.ServerOptions | NodeHttps.ServerOptions | NodeHttp2.ServerOptions) & NodeNet.ListenOptions & {
http2?: boolean;
};
/**
* Bun server options
*
* @docs https://bun.sh/docs/api/http
*/
bun?: Omit<Bun.Serve.Options<any>, "fetch">;
/**
* Deno server options
*
* @docs https://docs.deno.com/api/deno/~/Deno.serve
*/
deno?: Deno.ServeOptions;
/**
* Service worker options
*/
serviceWorker?: {
/**
* The path to the service worker file to be registered.
*/
url?: string;
/**
* The scope of the service worker.
*
*/
scope?: string;
};
}
interface Server<Handler = ServerHandler> {
/**
* Current runtime name
*/
readonly runtime: "node" | "deno" | "bun" | "cloudflare" | "service-worker" | "generic";
/**
* Server options
*/
readonly options: ServerOptions & {
middleware: ServerMiddleware[];
};
/**
* Server URL address.
*/
readonly url?: string;
/**
* Node.js context.
*/
readonly node?: {
server?: NodeHttp.Server | NodeHttp2.Http2Server;
handler: (req: NodeServerRequest, res: NodeServerResponse) => void | Promise<void>;
};
/**
* Bun context.
*/
readonly bun?: {
server?: Bun.Server<any>;
};
/**
* Deno context.
*/
readonly deno?: {
server?: Deno.HttpServer;
};
/**
* Server fetch handler
*/
readonly fetch: Handler;
/**
* Start listening for incoming requests.
* When `manual` option is enabled, this method needs to be called explicitly to begin accepting connections.
*/
serve(): void | Promise<Server<Handler>>;
/**
* Returns a promise that resolves when the server is ready.
*/
ready(): Promise<Server<Handler>>;
/**
* Stop listening to prevent new connections from being accepted.
*
* By default, it does not cancel in-flight requests or websockets. That means it may take some time before all network activity stops.
*
* @param closeActiveConnections Immediately terminate in-flight requests, websockets, and stop accepting new connections.
* @default false
*/
close(closeActiveConnections?: boolean): Promise<void>;
}
interface ServerRuntimeContext {
name: "node" | "deno" | "bun" | "cloudflare" | (string & {});
/**
* Underlying Node.js server request info.
*/
node?: {
req: NodeServerRequest;
res?: NodeServerResponse;
};
/**
* Underlying Deno server request info.
*/
deno?: {
info: Deno.ServeHandlerInfo<Deno.NetAddr>;
};
/**
* Underlying Bun server request context.
*/
bun?: {
server: Bun.Server<any>;
};
/**
* Underlying Cloudflare request context.
*/
cloudflare?: {
context: CF.ExecutionContext;
env: IsAny<typeof cloudflare_workers0> extends true ? Record<string, unknown> : typeof cloudflare_workers0.env;
};
}
interface ServerRequestContext {
[key: string]: unknown;
}
interface ServerRequest extends Request {
/**
* Access to the parsed URL
*/
_url?: URL;
/**
* Runtime specific request context.
*/
runtime?: ServerRuntimeContext;
/**
* IP address of the client.
*/
ip?: string | undefined;
/**
* Arbitrary context related to the request.
*/
context?: ServerRequestContext;
/**
* Tell the runtime about an ongoing operation that shouldn't close until the promise resolves.
*/
waitUntil?: (promise: Promise<unknown>) => void | Promise<void>;
}
type FetchHandler = (request: Request) => Response | Promise<Response>;
type ErrorHandler = (error: unknown) => Response | Promise<Response>;
type BunFetchHandler = (request: Request, server?: Bun.Server<any>) => Response | Promise<Response>;
type DenoFetchHandler = (request: Request, info?: Deno.ServeHandlerInfo<Deno.NetAddr>) => Response | Promise<Response>;
type NodeServerRequest = NodeHttp.IncomingMessage | NodeHttp2.Http2ServerRequest;
type NodeServerResponse = NodeHttp.ServerResponse | NodeHttp2.Http2ServerResponse;
type NodeHttpHandler = (req: NodeServerRequest, res: NodeServerResponse) => void | Promise<void>;
type NodeHTTPMiddleware = (req: NodeServerRequest, res: NodeServerResponse, next: (error?: Error) => void) => unknown | Promise<unknown>;
type CloudflareFetchHandler = CF.ExportedHandlerFetchHandler;
//#endregion
export { ServerRequest as _, FastResponse as a, serve as b, NodeHTTPMiddleware as c, NodeServerResponse as d, Server as f, ServerPlugin as g, ServerOptions as h, ErrorHandler as i, NodeHttpHandler as l, ServerMiddleware as m, CloudflareFetchHandler as n, FastURL as o, ServerHandler as p, DenoFetchHandler as r, FetchHandler as s, BunFetchHandler as t, NodeServerRequest as u, ServerRequestContext as v, ServerRuntimeContext as y };

View file

@ -0,0 +1,22 @@
import { f as Server, h as ServerOptions, t as BunFetchHandler } from "../_chunks/types.mjs";
import { t as FastURL } from "../_chunks/_url.mjs";
import * as bun from "bun";
//#region src/adapters/bun.d.ts
declare const FastResponse: typeof globalThis.Response;
declare function serve(options: ServerOptions): BunServer;
declare class BunServer implements Server<BunFetchHandler> {
#private;
readonly runtime = "bun";
readonly options: Server["options"];
readonly bun: Server["bun"];
readonly serveOptions: bun.Serve.Options<any>;
readonly fetch: BunFetchHandler;
constructor(options: ServerOptions);
serve(): Promise<this>;
get url(): string | undefined;
ready(): Promise<this>;
close(closeAll?: boolean): Promise<void>;
}
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,82 @@
import { t as FastURL } from "../_chunks/_url.mjs";
import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils.mjs";
import { n as gracefulShutdownPlugin, r as wrapFetch } from "../_chunks/_plugins.mjs";
//#region src/adapters/bun.ts
const FastResponse = Response;
function serve(options) {
return new BunServer(options);
}
var BunServer = class {
runtime = "bun";
options;
bun = {};
serveOptions;
fetch;
#wait;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
gracefulShutdownPlugin(this);
const fetchHandler = wrapFetch(this);
this.#wait = createWaitUntil();
this.fetch = (request, server) => {
Object.defineProperties(request, {
waitUntil: { value: this.#wait.waitUntil },
runtime: {
enumerable: true,
value: {
name: "bun",
bun: { server }
}
},
ip: {
enumerable: true,
get() {
return server?.requestIP(request)?.address;
}
}
});
return fetchHandler(request);
};
const tls = resolveTLSOptions(this.options);
this.serveOptions = {
...resolvePortAndHost(this.options),
reusePort: this.options.reusePort,
error: this.options.error,
...this.options.bun,
tls: {
cert: tls?.cert,
key: tls?.key,
passphrase: tls?.passphrase,
...this.options.bun?.tls
},
fetch: this.fetch
};
if (!options.manual) this.serve();
}
serve() {
if (!this.bun.server) this.bun.server = Bun.serve(this.serveOptions);
printListening(this.options, this.url);
return Promise.resolve(this);
}
get url() {
const server = this.bun?.server;
if (!server) return;
const address = server.address;
if (address) return fmtURL(address.address, address.port, server.protocol === "https");
return server.url.href;
}
ready() {
return Promise.resolve(this);
}
async close(closeAll) {
await Promise.all([this.#wait.wait(), Promise.resolve(this.bun?.server?.stop(closeAll))]);
}
};
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,9 @@
import { f as Server, h as ServerOptions } from "../_chunks/types.mjs";
import * as CF from "@cloudflare/workers-types";
//#region src/adapters/cloudflare.d.ts
declare const FastURL: typeof globalThis.URL;
declare const FastResponse: typeof globalThis.Response;
declare function serve(options: ServerOptions): Server<CF.ExportedHandlerFetchHandler>;
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,61 @@
import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
//#region src/adapters/cloudflare.ts
const FastURL = URL;
const FastResponse = Response;
function serve(options) {
return new CloudflareServer(options);
}
var CloudflareServer = class {
runtime = "cloudflare";
options;
serveOptions;
fetch;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
errorPlugin(this);
const fetchHandler = wrapFetch(this);
this.fetch = (request, env, context) => {
Object.defineProperties(request, {
waitUntil: { value: context.waitUntil.bind(context) },
runtime: {
enumerable: true,
value: {
name: "cloudflare",
cloudflare: {
env,
context
}
}
},
ip: {
enumerable: true,
get() {
return request.headers.get("cf-connecting-ip");
}
}
});
return fetchHandler(request);
};
this.serveOptions = { fetch: this.fetch };
if (!options.manual) this.serve();
}
serve() {
addEventListener("fetch", (event) => {
event.respondWith(this.fetch(event.request, {}, event));
});
}
ready() {
return Promise.resolve().then(() => this);
}
close() {
return Promise.resolve();
}
};
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,21 @@
import { f as Server, h as ServerOptions, r as DenoFetchHandler } from "../_chunks/types.mjs";
import { t as FastURL } from "../_chunks/_url.mjs";
//#region src/adapters/deno.d.ts
declare const FastResponse: typeof globalThis.Response;
declare function serve(options: ServerOptions): DenoServer;
declare class DenoServer implements Server<DenoFetchHandler> {
#private;
readonly runtime = "deno";
readonly options: Server["options"];
readonly deno: Server["deno"];
readonly serveOptions: Deno.ServeTcpOptions | (Deno.ServeTcpOptions & Deno.TlsCertifiedKeyPem);
readonly fetch: DenoFetchHandler;
constructor(options: ServerOptions);
serve(): Promise<this>;
get url(): string | undefined;
ready(): Promise<Server>;
close(): Promise<void>;
}
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,91 @@
import { t as FastURL } from "../_chunks/_url.mjs";
import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils.mjs";
import { n as gracefulShutdownPlugin, r as wrapFetch } from "../_chunks/_plugins.mjs";
//#region src/adapters/deno.ts
const FastResponse = Response;
function serve(options) {
return new DenoServer(options);
}
var DenoServer = class {
runtime = "deno";
options;
deno = {};
serveOptions;
fetch;
#listeningPromise;
#listeningInfo;
#wait;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
gracefulShutdownPlugin(this);
const fetchHandler = wrapFetch(this);
this.#wait = createWaitUntil();
this.fetch = (request, info) => {
Object.defineProperties(request, {
waitUntil: { value: this.#wait.waitUntil },
runtime: {
enumerable: true,
value: {
name: "deno",
deno: {
info,
server: this.deno?.server
}
}
},
ip: {
enumerable: true,
get() {
return (info?.remoteAddr)?.hostname;
}
}
});
return fetchHandler(request);
};
const tls = resolveTLSOptions(this.options);
this.serveOptions = {
...resolvePortAndHost(this.options),
reusePort: this.options.reusePort,
onError: this.options.error,
...tls ? {
key: tls.key,
cert: tls.cert,
passphrase: tls.passphrase
} : {},
...this.options.deno
};
if (!options.manual) this.serve();
}
serve() {
if (this.deno?.server) return Promise.resolve(this.#listeningPromise).then(() => this);
const onListenPromise = Promise.withResolvers();
this.#listeningPromise = onListenPromise.promise;
this.deno.server = Deno.serve({
...this.serveOptions,
onListen: (info) => {
this.#listeningInfo = info;
if (this.options.deno?.onListen) this.options.deno.onListen(info);
printListening(this.options, this.url);
onListenPromise.resolve();
}
}, this.fetch);
return Promise.resolve(this.#listeningPromise).then(() => this);
}
get url() {
return this.#listeningInfo ? fmtURL(this.#listeningInfo.hostname, this.#listeningInfo.port, !!this.serveOptions.cert) : void 0;
}
ready() {
return Promise.resolve(this.#listeningPromise).then(() => this);
}
async close() {
await Promise.all([this.#wait.wait(), Promise.resolve(this.deno?.server?.shutdown())]);
}
};
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,8 @@
import { f as Server, h as ServerOptions } from "../_chunks/types.mjs";
//#region src/adapters/generic.d.ts
declare const FastURL: typeof globalThis.URL;
declare const FastResponse: typeof globalThis.Response;
declare function serve(options: ServerOptions): Server;
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,39 @@
import { t as createWaitUntil } from "../_chunks/_utils.mjs";
import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
//#region src/adapters/generic.ts
const FastURL = URL;
const FastResponse = Response;
function serve(options) {
return new GenericServer(options);
}
var GenericServer = class {
runtime = "generic";
options;
fetch;
#wait;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
errorPlugin(this);
this.#wait = createWaitUntil();
const fetchHandler = wrapFetch(this);
this.fetch = (request) => {
Object.defineProperties(request, { waitUntil: { value: this.#wait.waitUntil } });
return Promise.resolve(fetchHandler(request));
};
}
serve() {}
ready() {
return Promise.resolve(this);
}
async close() {
await this.#wait.wait();
}
};
//#endregion
export { FastResponse, FastURL, serve };

View file

@ -0,0 +1,70 @@
import { _ as ServerRequest, d as NodeServerResponse, f as Server, h as ServerOptions, l as NodeHttpHandler, s as FetchHandler, u as NodeServerRequest } from "../_chunks/types.mjs";
import { t as FastURL } from "../_chunks/_url.mjs";
import { Readable } from "node:stream";
//#region src/adapters/_node/request.d.ts
type NodeRequestContext = {
req: NodeServerRequest;
res?: NodeServerResponse;
};
declare const NodeRequest: {
new (nodeCtx: NodeRequestContext): ServerRequest;
};
//#endregion
//#region src/adapters/_node/response.d.ts
type PreparedNodeResponseBody = string | Buffer | Uint8Array | DataView | ReadableStream | Readable | undefined | null;
interface PreparedNodeResponse {
status: number;
statusText: string;
headers: [string, string][];
body: PreparedNodeResponseBody;
}
/**
* Fast Response for Node.js runtime
*
* It is faster because in most cases it doesn't create a full Response instance.
*/
declare const NodeResponse: {
new (body?: BodyInit | null, init?: ResponseInit): globalThis.Response & {
_toNodeResponse: () => PreparedNodeResponse;
};
};
type NodeResponse = InstanceType<typeof NodeResponse>;
//#endregion
//#region src/adapters/_node/send.d.ts
declare function sendNodeResponse(nodeRes: NodeServerResponse, webRes: Response | NodeResponse): Promise<void>;
//#endregion
//#region src/adapters/_node/web/fetch.d.ts
/**
* Calls a Node.js HTTP Request handler with a Fetch API Request object and returns a Response object.
*
* 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.
*
* 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.
*
* The handler is invoked with these objects, and the response is constructed from the ServerResponse once it is finished.
*
* @experimental Behavior might be unstable.
*/
declare function fetchNodeHandler(handler: NodeHttpHandler, req: ServerRequest): Promise<Response>;
//#endregion
//#region src/adapters/_node/adapter.d.ts
type AdapterMeta = {
__nodeHandler?: NodeHttpHandler;
__fetchHandler?: FetchHandler;
};
/**
* Converts a Fetch API handler to a Node.js HTTP handler.
*/
declare function toNodeHandler(handler: FetchHandler & AdapterMeta): NodeHttpHandler & AdapterMeta;
/**
* Converts a Node.js HTTP handler into a Fetch API handler.
*
* @experimental Behavior might be unstable and won't work in Bun and Deno currently (tracker: https://github.com/h3js/srvx/issues/132)
*/
declare function toFetchHandler(handler: NodeHttpHandler & AdapterMeta): FetchHandler & AdapterMeta;
//#endregion
//#region src/adapters/node.d.ts
declare function serve(options: ServerOptions): Server;
//#endregion
export { type AdapterMeta, NodeResponse as FastResponse, NodeResponse, FastURL, NodeRequest, fetchNodeHandler, sendNodeResponse, serve, toFetchHandler, toNodeHandler };

View file

@ -0,0 +1,640 @@
import { t as lazyInherit } from "../_chunks/_inherit.mjs";
import { t as FastURL } from "../_chunks/_url.mjs";
import { a as resolveTLSOptions, i as resolvePortAndHost, n as fmtURL, r as printListening, t as createWaitUntil } from "../_chunks/_utils.mjs";
import { n as gracefulShutdownPlugin, r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
import { n as NodeResponse, t as callNodeHandler } from "../_chunks/call.mjs";
import nodeHTTP, { IncomingMessage, ServerResponse } from "node:http";
import { Duplex, Readable } from "node:stream";
import nodeHTTPS from "node:https";
import nodeHTTP2 from "node:http2";
//#region src/adapters/_node/send.ts
async function sendNodeResponse(nodeRes, webRes) {
if (!webRes) {
nodeRes.statusCode = 500;
return endNodeResponse(nodeRes);
}
if (webRes._toNodeResponse) {
const res = webRes._toNodeResponse();
writeHead(nodeRes, res.status, res.statusText, res.headers);
if (res.body) {
if (res.body instanceof ReadableStream) return streamBody(res.body, nodeRes);
else if (typeof res.body?.pipe === "function") {
res.body.pipe(nodeRes);
return new Promise((resolve) => nodeRes.on("close", resolve));
}
nodeRes.write(res.body);
}
return endNodeResponse(nodeRes);
}
const rawHeaders = [...webRes.headers];
writeHead(nodeRes, webRes.status, webRes.statusText, rawHeaders);
return webRes.body ? streamBody(webRes.body, nodeRes) : endNodeResponse(nodeRes);
}
function writeHead(nodeRes, status, statusText, rawHeaders) {
const writeHeaders = globalThis.Deno ? rawHeaders : rawHeaders.flat();
if (!nodeRes.headersSent) if (nodeRes.req?.httpVersion === "2.0") nodeRes.writeHead(status, writeHeaders);
else nodeRes.writeHead(status, statusText, writeHeaders);
}
function endNodeResponse(nodeRes) {
return new Promise((resolve) => nodeRes.end(resolve));
}
function streamBody(stream, nodeRes) {
if (nodeRes.destroyed) {
stream.cancel();
return;
}
const reader = stream.getReader();
function streamCancel(error) {
reader.cancel(error).catch(() => {});
if (error) nodeRes.destroy(error);
}
function streamHandle({ done, value }) {
try {
if (done) nodeRes.end();
else if (nodeRes.write(value)) reader.read().then(streamHandle, streamCancel);
else nodeRes.once("drain", () => reader.read().then(streamHandle, streamCancel));
} catch (error) {
streamCancel(error instanceof Error ? error : void 0);
}
}
nodeRes.on("close", streamCancel);
nodeRes.on("error", streamCancel);
reader.read().then(streamHandle, streamCancel);
return reader.closed.catch(streamCancel).finally(() => {
nodeRes.off("close", streamCancel);
nodeRes.off("error", streamCancel);
});
}
//#endregion
//#region src/adapters/_node/url.ts
var NodeRequestURL = class extends FastURL {
#req;
constructor({ req }) {
const path = req.url || "/";
if (path[0] === "/") {
const qIndex = path.indexOf("?");
const pathname = qIndex === -1 ? path : path?.slice(0, qIndex) || "/";
const search = qIndex === -1 ? "" : path?.slice(qIndex) || "";
const host = req.headers.host || req.headers[":authority"] || `${req.socket.localFamily === "IPv6" ? "[" + req.socket.localAddress + "]" : req.socket.localAddress}:${req.socket?.localPort || "80"}`;
const protocol = req.socket?.encrypted || req.headers["x-forwarded-proto"] === "https" || req.headers[":scheme"] === "https" ? "https:" : "http:";
super({
protocol,
host,
pathname,
search
});
} else super(path);
this.#req = req;
}
get pathname() {
return super.pathname;
}
set pathname(value) {
this._url.pathname = value;
this.#req.url = this._url.pathname + this._url.search;
}
};
//#endregion
//#region src/adapters/_node/headers.ts
const NodeRequestHeaders = /* @__PURE__ */ (() => {
const NativeHeaders = globalThis.Headers;
class Headers {
#req;
#headers;
constructor(req) {
this.#req = req;
}
static [Symbol.hasInstance](val) {
return val instanceof NativeHeaders;
}
get _headers() {
if (!this.#headers) {
const headers = new NativeHeaders();
const rawHeaders = this.#req.rawHeaders;
const len = rawHeaders.length;
for (let i = 0; i < len; i += 2) {
const key = rawHeaders[i];
if (key.charCodeAt(0) === 58) continue;
const value = rawHeaders[i + 1];
headers.append(key, value);
}
this.#headers = headers;
}
return this.#headers;
}
get(name) {
if (this.#headers) return this.#headers.get(name);
const value = this.#req.headers[name.toLowerCase()];
return Array.isArray(value) ? value.join(", ") : value || null;
}
has(name) {
if (this.#headers) return this.#headers.has(name);
return name.toLowerCase() in this.#req.headers;
}
getSetCookie() {
if (this.#headers) return this.#headers.getSetCookie();
const value = this.#req.headers["set-cookie"];
return Array.isArray(value) ? value : value ? [value] : [];
}
*_entries() {
const rawHeaders = this.#req.rawHeaders;
const len = rawHeaders.length;
for (let i = 0; i < len; i += 2) {
const key = rawHeaders[i];
if (key.charCodeAt(0) === 58) continue;
yield [key.toLowerCase(), rawHeaders[i + 1]];
}
}
entries() {
return this.#headers ? this.#headers.entries() : this._entries();
}
[Symbol.iterator]() {
return this.entries();
}
}
lazyInherit(Headers.prototype, NativeHeaders.prototype, "_headers");
Object.setPrototypeOf(Headers, NativeHeaders);
Object.setPrototypeOf(Headers.prototype, NativeHeaders.prototype);
return Headers;
})();
//#endregion
//#region src/adapters/_node/request.ts
const NodeRequest = /* @__PURE__ */ (() => {
const NativeRequest = globalThis[Symbol.for("srvx.nativeRequest")] ??= globalThis.Request;
const PatchedRequest = class Request$1 extends NativeRequest {
static _srvx = true;
static [Symbol.hasInstance](instance) {
if (this === PatchedRequest) return instance instanceof NativeRequest;
else return Object.prototype.isPrototypeOf.call(this.prototype, instance);
}
constructor(input, options) {
if (typeof input === "object" && "_request" in input) input = input._request;
if ((options?.body)?.getReader !== void 0) options.duplex ??= "half";
super(input, options);
}
};
if (!globalThis.Request._srvx) globalThis.Request = PatchedRequest;
class Request {
runtime;
#req;
#url;
#bodyStream;
#request;
#headers;
#abortController;
constructor(ctx) {
this.#req = ctx.req;
this.runtime = {
name: "node",
node: ctx
};
}
static [Symbol.hasInstance](val) {
return val instanceof NativeRequest;
}
get ip() {
return this.#req.socket?.remoteAddress;
}
get method() {
if (this.#request) return this.#request.method;
return this.#req.method || "GET";
}
get _url() {
return this.#url ||= new NodeRequestURL({ req: this.#req });
}
set _url(url) {
this.#url = url;
}
get url() {
if (this.#request) return this.#request.url;
return this._url.href;
}
get headers() {
if (this.#request) return this.#request.headers;
return this.#headers ||= new NodeRequestHeaders(this.#req);
}
get _abortController() {
if (!this.#abortController) {
this.#abortController = new AbortController();
const { req, res } = this.runtime.node;
const abortController = this.#abortController;
const abort = (err) => abortController.abort?.(err);
req.once("error", abort);
if (res) res.once("close", () => {
const reqError = req.errored;
if (reqError) abort(reqError);
else if (!res.writableEnded) abort();
});
else req.once("close", () => {
if (!req.complete) abort();
});
}
return this.#abortController;
}
get signal() {
return this.#request ? this.#request.signal : this._abortController.signal;
}
get body() {
if (this.#request) return this.#request.body;
if (this.#bodyStream === void 0) {
const method = this.method;
this.#bodyStream = !(method === "GET" || method === "HEAD") ? Readable.toWeb(this.#req) : null;
}
return this.#bodyStream;
}
text() {
if (this.#request) return this.#request.text();
if (this.#bodyStream !== void 0) return this.#bodyStream ? new Response(this.#bodyStream).text() : Promise.resolve("");
return readBody(this.#req).then((buf) => buf.toString());
}
json() {
if (this.#request) return this.#request.json();
return this.text().then((text) => JSON.parse(text));
}
get _request() {
if (!this.#request) {
this.#request = new PatchedRequest(this.url, {
method: this.method,
headers: this.headers,
body: this.body,
signal: this._abortController.signal
});
this.#headers = void 0;
this.#bodyStream = void 0;
}
return this.#request;
}
}
lazyInherit(Request.prototype, NativeRequest.prototype, "_request");
Object.setPrototypeOf(Request.prototype, NativeRequest.prototype);
return Request;
})();
function readBody(req) {
return new Promise((resolve, reject) => {
const chunks = [];
const onData = (chunk) => {
chunks.push(chunk);
};
const onError = (err) => {
reject(err);
};
const onEnd = () => {
req.off("error", onError);
req.off("data", onData);
resolve(Buffer.concat(chunks));
};
req.on("data", onData).once("end", onEnd).once("error", onError);
});
}
//#endregion
//#region src/adapters/_node/web/incoming.ts
var WebIncomingMessage = class extends IncomingMessage {
constructor(req, socket) {
super(socket);
this.method = req.method;
const url = req._url ??= new FastURL(req.url);
this.url = url.pathname + url.search;
for (const [key, value] of req.headers.entries()) this.headers[key.toLowerCase()] = value;
if (req.method !== "GET" && req.method !== "HEAD" && !this.headers["content-length"] && !this.headers["transfer-encoding"]) this.headers["transfer-encoding"] = "chunked";
const onData = (chunk) => {
this.push(chunk);
};
socket.on("data", onData);
socket.once("end", () => {
this.emit("end");
this.off("data", onData);
});
}
};
//#endregion
//#region src/adapters/_node/web/socket.ts
/**
* Events:
* - Readable (req from client): readable => data => end (push(null)) => error => close
* - Writable (res to client): pipe => unpipe => drain => finish (end called) => error => close
*/
var WebRequestSocket = class extends Duplex {
_httpMessage;
autoSelectFamilyAttemptedAddresses = [];
bufferSize = 0;
bytesRead = 0;
bytesWritten = 0;
connecting = false;
pending = false;
readyState = "open";
remoteAddress = "";
remoteFamily = "";
remotePort = 0;
#request;
#timeoutTimer;
#reqReader;
#headersWritten;
#_writeBody;
_webResBody;
constructor(request) {
super({
signal: request.signal,
allowHalfOpen: true
});
this.#request = request;
this._webResBody = new ReadableStream({ start: (controller) => {
this.#_writeBody = controller.enqueue.bind(controller);
this.once("finish", () => {
this.readyState = "closed";
controller.close();
});
} });
}
setTimeout(ms, cb) {
if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) return this;
if (cb) this.on("timeout", cb);
if (this.#timeoutTimer) clearTimeout(this.#timeoutTimer);
if (ms > 0) this.#timeoutTimer = setTimeout(() => this.emit("timeout"), ms);
return this;
}
setNoDelay() {
return this;
}
setKeepAlive() {
return this;
}
ref() {
return this;
}
unref() {
return this;
}
destroySoon() {
this.destroy();
}
connect() {
return this;
}
resetAndDestroy() {
this.destroy();
return this;
}
address() {
return {
address: "",
family: "",
port: 0
};
}
_read(_size) {
const reader = this.#reqReader ??= this.#request.body?.getReader();
if (!reader) {
this.push(null);
return;
}
reader.read().then((res) => this._onRead(res)).catch((error) => {
this.emit("error", error);
});
}
_onRead(res) {
if (res.done) {
this.push(null);
return;
}
if (res.value) {
this.bytesRead += res.value.byteLength;
this.push(res.value);
}
}
_write(chunk, encoding, callback) {
if (this.#headersWritten) this.#_writeBody(typeof chunk === "string" ? Buffer.from(chunk, encoding) : chunk);
else if (chunk?.length > 0) {
this.#headersWritten = true;
const headerEnd = chunk.lastIndexOf("\r\n\r\n");
if (headerEnd === -1) throw new Error("Invalid HTTP headers chunk!");
if (headerEnd < chunk.length - 4) {
this._write(chunk.slice(headerEnd + 4), encoding, () => {
callback(null);
});
return;
}
}
callback(null);
}
_final(callback) {
callback(null);
}
_destroy(err, cb) {
if (this.#timeoutTimer) clearTimeout(this.#timeoutTimer);
if (this.#reqReader) this.#reqReader.cancel().catch((error) => {
console.error(error);
});
this.readyState = "closed";
cb(err ?? void 0);
}
};
//#endregion
//#region src/adapters/_node/web/response.ts
var WebServerResponse = class extends ServerResponse {
#socket;
constructor(req, socket) {
super(req);
this.assignSocket(socket);
this.once("finish", () => {
socket.end();
});
this.#socket = socket;
this.waitToFinish = this.waitToFinish.bind(this);
this.toWebResponse = this.toWebResponse.bind(this);
}
waitToFinish() {
if (this.writableEnded) return Promise.resolve();
return new Promise((resolve, reject) => {
this.on("finish", () => resolve());
this.on("error", (err) => reject(err));
});
}
async toWebResponse() {
await this.waitToFinish();
const headers = [];
const httpHeader = this._header?.split("\r\n");
for (let i = 1; httpHeader && i < httpHeader.length; i++) {
const sepIndex = httpHeader[i].indexOf(": ");
if (sepIndex === -1) continue;
const key = httpHeader[i].slice(0, Math.max(0, sepIndex));
const value = httpHeader[i].slice(Math.max(0, sepIndex + 2));
if (!key) continue;
headers.push([key, value]);
}
return new Response(this.#socket._webResBody, {
status: this.statusCode,
statusText: this.statusMessage,
headers
});
}
};
//#endregion
//#region src/adapters/_node/web/fetch.ts
/**
* Calls a Node.js HTTP Request handler with a Fetch API Request object and returns a Response object.
*
* 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.
*
* 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.
*
* The handler is invoked with these objects, and the response is constructed from the ServerResponse once it is finished.
*
* @experimental Behavior might be unstable.
*/
async function fetchNodeHandler(handler, req) {
const nodeRuntime = req.runtime?.node;
if (nodeRuntime && nodeRuntime.req && nodeRuntime.res) return await callNodeHandler(handler, req);
const socket = new WebRequestSocket(req);
const nodeReq = new WebIncomingMessage(req, socket);
const nodeRes = new WebServerResponse(nodeReq, socket);
try {
await handler(nodeReq, nodeRes);
return await nodeRes.toWebResponse();
} catch (error) {
console.error(error, { cause: {
req,
handler
} });
return new Response(null, {
status: 500,
statusText: "Internal Server Error"
});
}
}
//#endregion
//#region src/adapters/_node/adapter.ts
/**
* Converts a Fetch API handler to a Node.js HTTP handler.
*/
function toNodeHandler(handler) {
if (handler.__nodeHandler) return handler.__nodeHandler;
function convertedNodeHandler(nodeReq, nodeRes) {
const res = handler(new NodeRequest({
req: nodeReq,
res: nodeRes
}));
return res instanceof Promise ? res.then((resolvedRes) => sendNodeResponse(nodeRes, resolvedRes)) : sendNodeResponse(nodeRes, res);
}
convertedNodeHandler.__fetchHandler = handler;
assignFnName(convertedNodeHandler, handler, " (converted to Node handler)");
return convertedNodeHandler;
}
/**
* Converts a Node.js HTTP handler into a Fetch API handler.
*
* @experimental Behavior might be unstable and won't work in Bun and Deno currently (tracker: https://github.com/h3js/srvx/issues/132)
*/
function toFetchHandler(handler) {
if (handler.__fetchHandler) return handler.__fetchHandler;
function convertedNodeHandler(req) {
return fetchNodeHandler(handler, req);
}
convertedNodeHandler.__nodeHandler = handler;
assignFnName(convertedNodeHandler, handler, " (converted to Web handler)");
return convertedNodeHandler;
}
function assignFnName(target, source, suffix) {
if (source.name) try {
Object.defineProperty(target, "name", { value: `${source.name}${suffix}` });
} catch {}
}
//#endregion
//#region src/adapters/node.ts
function serve(options) {
return new NodeServer(options);
}
var NodeServer = class {
runtime = "node";
options;
node;
serveOptions;
fetch;
#isSecure;
#listeningPromise;
#wait;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
errorPlugin(this);
gracefulShutdownPlugin(this);
const fetchHandler = this.fetch = wrapFetch(this);
this.#wait = createWaitUntil();
const handler = (nodeReq, nodeRes) => {
const request = new NodeRequest({
req: nodeReq,
res: nodeRes
});
request.waitUntil = this.#wait.waitUntil;
const res = fetchHandler(request);
return res instanceof Promise ? res.then((resolvedRes) => sendNodeResponse(nodeRes, resolvedRes)) : sendNodeResponse(nodeRes, res);
};
const tls = resolveTLSOptions(this.options);
const { port, hostname: host } = resolvePortAndHost(this.options);
this.serveOptions = {
port,
host,
exclusive: !this.options.reusePort,
...tls ? {
cert: tls.cert,
key: tls.key,
passphrase: tls.passphrase
} : {},
...this.options.node
};
let server;
this.#isSecure = !!this.serveOptions.cert && this.options.protocol !== "http";
if (this.options.node?.http2 ?? this.#isSecure) if (this.#isSecure) server = nodeHTTP2.createSecureServer({
allowHTTP1: true,
...this.serveOptions
}, handler);
else throw new Error("node.http2 option requires tls certificate!");
else if (this.#isSecure) server = nodeHTTPS.createServer(this.serveOptions, handler);
else server = nodeHTTP.createServer(this.serveOptions, handler);
this.node = {
server,
handler
};
if (!options.manual) this.serve();
}
serve() {
if (this.#listeningPromise) return Promise.resolve(this.#listeningPromise).then(() => this);
this.#listeningPromise = new Promise((resolve) => {
this.node.server.listen(this.serveOptions, () => {
printListening(this.options, this.url);
resolve();
});
});
}
get url() {
const addr = this.node?.server?.address();
if (!addr) return;
return typeof addr === "string" ? addr : fmtURL(addr.address, addr.port, this.#isSecure);
}
ready() {
return Promise.resolve(this.#listeningPromise).then(() => this);
}
async close(closeAll) {
await Promise.all([this.#wait.wait(), new Promise((resolve, reject) => {
const server = this.node?.server;
if (!server) return resolve();
if (closeAll && "closeAllConnections" in server) server.closeAllConnections();
server.close((error) => error ? reject(error) : resolve());
})]);
}
};
//#endregion
export { NodeResponse as FastResponse, NodeResponse, FastURL, NodeRequest, fetchNodeHandler, sendNodeResponse, serve, toFetchHandler, toNodeHandler };

View file

@ -0,0 +1,9 @@
import { _ as ServerRequest, f as Server, h as ServerOptions } from "../_chunks/types.mjs";
//#region src/adapters/service-worker.d.ts
declare const FastURL: typeof globalThis.URL;
declare const FastResponse: typeof globalThis.Response;
type ServiceWorkerHandler = (request: ServerRequest, event: FetchEvent) => Response | Promise<Response>;
declare function serve(options: ServerOptions): Server<ServiceWorkerHandler>;
//#endregion
export { FastResponse, FastURL, ServiceWorkerHandler, serve };

View file

@ -0,0 +1,80 @@
import { r as wrapFetch, t as errorPlugin } from "../_chunks/_plugins.mjs";
//#region src/adapters/service-worker.ts
const FastURL = URL;
const FastResponse = Response;
const isBrowserWindow = typeof window !== "undefined" && typeof navigator !== "undefined";
const isServiceWorker = /* @__PURE__ */ (() => typeof self !== "undefined" && "skipWaiting" in self)();
function serve(options) {
return new ServiceWorkerServer(options);
}
var ServiceWorkerServer = class {
runtime = "service-worker";
options;
fetch;
#fetchListener;
#listeningPromise;
constructor(options) {
this.options = {
...options,
middleware: [...options.middleware || []]
};
for (const plugin of options.plugins || []) plugin(this);
errorPlugin(this);
const fetchHandler = wrapFetch(this);
this.fetch = (request, event) => {
Object.defineProperties(request, { runtime: {
enumerable: true,
value: {
name: "service-worker",
serviceWorker: { event }
}
} });
return Promise.resolve(fetchHandler(request));
};
if (!options.manual) this.serve();
}
serve() {
if (isBrowserWindow) {
if (!navigator.serviceWorker) throw new Error("Service worker is not supported in the current window.");
const swURL = this.options.serviceWorker?.url;
if (!swURL) throw new Error("Service worker URL is not provided. Please set the `serviceWorker.url` serve option or manually register.");
this.#listeningPromise = navigator.serviceWorker.register(swURL, {
type: "module",
scope: this.options.serviceWorker?.scope
}).then((registration) => {
if (registration.active) location.replace(location.href);
else registration.addEventListener("updatefound", () => {
location.replace(location.href);
});
});
} else if (isServiceWorker) {
this.#fetchListener = async (event) => {
if (/\/[^/]*\.[a-zA-Z0-9]+$/.test(new URL(event.request.url).pathname)) return;
Object.defineProperty(event.request, "waitUntil", { value: event.waitUntil.bind(event) });
const response = await this.fetch(event.request, event);
if (response.status !== 404) event.respondWith(response);
};
addEventListener("fetch", this.#fetchListener);
self.addEventListener("install", () => {
self.skipWaiting();
});
self.addEventListener("activate", () => {
self.clients?.claim?.();
});
}
}
ready() {
return Promise.resolve(this.#listeningPromise).then(() => this);
}
async close() {
if (this.#fetchListener) removeEventListener("fetch", this.#fetchListener);
if (isBrowserWindow) {
const registrations = await navigator.serviceWorker.getRegistrations();
for (const registration of registrations) if (registration.active) await registration.unregister();
} else if (isServiceWorker) await self.registration.unregister();
}
};
//#endregion
export { FastResponse, FastURL, serve };

17
Frontend-Learner/node_modules/srvx/dist/cli.d.mts generated vendored Normal file
View file

@ -0,0 +1,17 @@
import { Server } from "srvx";
//#region src/cli.d.ts
declare function main(mainOpts: MainOpts): Promise<void>;
declare global {
var __srvx_version__: string | undefined;
var __srvx__: Server;
var __srvx_listen_cb__: () => void;
}
type MainOpts = {
command: string;
docs: string;
issues: string;
};
declare function usage(mainOpts: MainOpts): string;
//#endregion
export { main, usage };

350
Frontend-Learner/node_modules/srvx/dist/cli.mjs generated vendored Normal file
View file

@ -0,0 +1,350 @@
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/_color.mjs";
import { parseArgs } from "node:util";
import { fileURLToPath, pathToFileURL } from "node:url";
import * as nodeHTTP$1 from "node:http";
import { dirname, extname, relative, resolve } from "node:path";
import { fork } from "node:child_process";
import { existsSync } from "node:fs";
//#region src/cli.ts
const defaultEntries = [
"server",
"index",
"src/server",
"src/index",
"server/index"
];
const defaultExts = [
".mts",
".ts",
".cts",
".js",
".mjs",
".cjs",
".jsx",
".tsx"
];
const args = process.argv.slice(2);
const options = parseArgs$1(args);
if (process.send) {
setupProcessErrorHandlers();
await serve();
}
async function main(mainOpts) {
setupProcessErrorHandlers();
if (options._version) {
console.log(await version());
process.exit(0);
}
if (options._help) {
console.log(usage(mainOpts));
process.exit(options._help ? 0 : 1);
}
const isBun = !!process.versions.bun;
const isDeno = !!process.versions.deno;
const isNode = !isBun && !isDeno;
const runtimeArgs = [];
if (!options._prod) runtimeArgs.push("--watch");
if (isNode || isDeno) runtimeArgs.push(...[".env", options._prod ? ".env.production" : ".env.local"].filter((f) => existsSync(f)).map((f) => `--env-file=${f}`));
if (isNode) {
const [major, minor] = process.versions.node.split(".");
if (major === "22" && +minor >= 6) runtimeArgs.push("--experimental-strip-types");
if (options._import) runtimeArgs.push(`--import=${options._import}`);
}
const child = fork(fileURLToPath(import.meta.url), args, { execArgv: [...process.execArgv, ...runtimeArgs].filter(Boolean) });
child.on("error", (error) => {
console.error("Error in child process:", error);
process.exit(1);
});
child.on("exit", (code) => {
if (code !== 0) {
console.error(`Child process exited with code ${code}`);
process.exit(code);
}
});
let cleanupCalled = false;
const cleanup = (signal, exitCode) => {
if (cleanupCalled) return;
cleanupCalled = true;
try {
child.kill(signal || "SIGTERM");
} catch (error) {
console.error("Error killing child process:", error);
}
if (exitCode !== void 0) process.exit(exitCode);
};
process.on("exit", () => cleanup("SIGTERM"));
process.on("SIGINT", () => cleanup("SIGINT", 130));
process.on("SIGTERM", () => cleanup("SIGTERM", 143));
}
async function serve() {
try {
if (!process.env.NODE_ENV) process.env.NODE_ENV = options._prod ? "production" : "development";
const entry = await loadEntry(options);
const { serve: srvxServe } = entry._legacyNode ? await import("srvx/node") : await import("srvx");
const { serveStatic } = await import("srvx/static");
const { log } = await import("srvx/log");
const staticDir = resolve(options._dir, options._static);
options._static = existsSync(staticDir) ? staticDir : "";
const server = srvxServe({
error: (error) => {
console.error(error);
return renderError(error);
},
...entry,
middleware: [
log(),
options._static ? serveStatic({ dir: options._static }) : void 0,
...entry.middleware || []
].filter(Boolean)
});
globalThis.__srvx__ = server;
await server.ready();
await globalThis.__srvx_listen_cb__?.();
printInfo(entry);
} catch (error) {
console.error(error);
process.exit(1);
}
}
async function loadEntry(opts) {
try {
if (!opts._entry) for (const entry of defaultEntries) {
for (const ext of defaultExts) {
const entryPath = resolve(opts._dir, `${entry}${ext}`);
if (existsSync(entryPath)) {
opts._entry = entryPath;
break;
}
}
if (opts._entry) break;
}
if (!opts._entry) {
const _error$1 = `No server entry file found.\nPlease specify an entry file or ensure one of the default entries exists (${defaultEntries.join(", ")}).`;
return {
_error: _error$1,
fetch: () => renderError(_error$1, 404, "No Server Entry"),
...opts
};
}
const entryURL = opts._entry.startsWith("file://") ? opts._entry : pathToFileURL(resolve(opts._entry)).href;
const { res: mod, listenHandler } = await interceptListen(() => import(entryURL));
let fetchHandler = mod.fetch || mod.default?.fetch || mod.default?.default?.fetch;
let _legacyNode = false;
if (!fetchHandler) {
const nodeHandler = listenHandler || (typeof mod.default === "function" ? mod.default : void 0);
if (nodeHandler) {
_legacyNode = true;
const { callNodeHandler } = await import("./_chunks/call2.mjs");
fetchHandler = (webReq) => callNodeHandler(nodeHandler, webReq);
}
}
let _error;
if (!fetchHandler) {
_error = `The entry file "${relative(".", opts._entry)}" does not export a valid fetch handler.`;
fetchHandler = () => renderError(_error, 500, "Invalid Entry");
}
return {
...mod,
...mod.default,
...opts,
_error,
_legacyNode,
fetch: fetchHandler
};
} catch (error) {
if (error?.code === "ERR_UNKNOWN_FILE_EXTENSION") {
const message = String(error);
if (/"\.(m|c)?ts"/g.test(message)) console.error(red(`\nMake sure you're using Node.js v22.18+ or v24+ for TypeScript support (current version: ${process.versions.node})\n\n`));
else if (/"\.(m|c)?tsx"/g.test(message)) console.error(red(`\nYou need a compatible loader for JSX support (Deno, Bun or srvx --register jiti/register)\n\n`));
}
if (error instanceof Error) Error.captureStackTrace?.(error, serve);
throw error;
}
}
function renderError(error, status = 500, title = "Server Error") {
let html = `<!DOCTYPE html><html><head><title>${title}</title></head><body>`;
if (options._prod) html += `<h1>${title}</h1><p>Something went wrong while processing your request.</p>`;
else html += `
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f8f9fa; color: #333; }
h1 { color: #dc3545; }
pre { background: #fff; padding: 10px; border-radius: 5px; overflow: auto; }
code { font-family: monospace; }
#error { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100vh; }
</style>
<div id="error"><h1>${title}</h1><pre>${error instanceof Error ? error.stack || error.message : String(error)}</pre></div>
`;
return new Response(html, {
status,
headers: { "Content-Type": "text/html; charset=utf-8" }
});
}
function printInfo(entry) {
let entryInfo;
if (options._entry) entryInfo = cyan("./" + relative(".", options._entry));
else entryInfo = gray(`(create ${bold(`server.ts`)} to enable)`);
console.log(gray(`${bold(gray("λ"))} Server handler: ${entryInfo}`));
if (options._entry && entry._error) console.error(red(` ${entry._error}`));
let staticInfo;
if (options._static) staticInfo = cyan("./" + relative(".", options._static) + "/");
else staticInfo = gray(`(add ${bold("public/")} dir to enable)`);
console.log(gray(`${bold(gray("∘"))} Static files: ${staticInfo}`));
}
async function interceptListen(cb) {
const originalListen = nodeHTTP$1.Server.prototype.listen;
let res;
let listenHandler;
try {
nodeHTTP$1.Server.prototype.listen = function(arg1, arg2) {
listenHandler = this._events.request;
if (Array.isArray(listenHandler)) listenHandler = listenHandler[0];
nodeHTTP$1.Server.prototype.listen = originalListen;
globalThis.__srvx_listen_cb__ = [arg1, arg2].find((arg) => typeof arg === "function");
return new Proxy({}, { get(_, prop) {
return globalThis.__srvx__?.node?.server?.[prop];
} });
};
res = await cb();
} finally {
nodeHTTP$1.Server.prototype.listen = originalListen;
}
return {
res,
listenHandler
};
}
async function version() {
return `srvx ${globalThis.__srvx_version__ || "unknown"}\n${runtime()}`;
}
function runtime() {
if (process.versions.bun) return `bun ${process.versions.bun}`;
else if (process.versions.deno) return `deno ${process.versions.deno}`;
else return `node ${process.versions.node}`;
}
function parseArgs$1(args$1) {
const { values, positionals } = parseArgs({
args: args$1,
allowPositionals: true,
options: {
help: {
type: "boolean",
short: "h"
},
version: {
type: "boolean",
short: "v"
},
prod: { type: "boolean" },
port: {
type: "string",
short: "p"
},
host: {
type: "string",
short: "H"
},
static: {
type: "string",
short: "s"
},
import: { type: "string" },
tls: { type: "boolean" },
cert: { type: "string" },
key: { type: "string" }
}
});
const input = positionals[0] || ".";
let dir;
let entry = "";
if (extname(input) === "") dir = resolve(input);
else {
entry = resolve(input);
dir = dirname(entry);
}
if (!existsSync(dir)) {
console.error(red(`Directory "${dir}" does not exist.\n`));
process.exit(1);
}
return {
_dir: dir,
_entry: entry,
_prod: values.prod ?? process.env.NODE_ENV === "production",
_help: values.help,
_static: values.static || "public",
_version: values.version,
_import: values.import,
port: values.port ? Number.parseInt(values.port, 10) : void 0,
hostname: values.host,
tls: values.tls ? {
cert: values.cert,
key: values.key
} : void 0
};
}
function example() {
const useTs = !options._entry || options._entry.endsWith(".ts");
return `${bold(gray("// server.ts"))}
${magenta("export default")} {
${cyan("fetch")}(req${useTs ? ": Request" : ""}) {
${magenta("return")} new Response(${green("\"Hello, World!\"")});
}
}`;
}
function usage(mainOpts) {
const command = mainOpts.command;
return `
${cyan(command)} - Start an HTTP server with the specified entry path.
${bold("USAGE")}
${existsSync(options._entry) ? "" : `\n${example()}\n`}
${gray("# srvx [options] [entry]")}
${gray("$")} ${cyan(command)} ${gray("./server.ts")} ${gray("# Start development server")}
${gray("$")} ${cyan(command)} --prod ${gray("# Start production server")}
${gray("$")} ${cyan(command)} --port=8080 ${gray("# Listen on port 8080")}
${gray("$")} ${cyan(command)} --host=localhost ${gray("# Bind to localhost only")}
${gray("$")} ${cyan(command)} --import=jiti/register ${gray(`# Enable ${url("jiti", "https://github.com/unjs/jiti")} loader`)}
${gray("$")} ${cyan(command)} --tls --cert=cert.pem --key=key.pem ${gray("# Enable TLS (HTTPS/HTTP2)")}
${bold("ARGUMENTS")}
${yellow("<entry>")} Server entry path to serve.
Default: ${defaultEntries.map((e) => cyan(e)).join(", ")} ${gray(`(${defaultExts.join(",")})`)}
${bold("OPTIONS")}
${green("-p, --port")} ${yellow("<port>")} Port to listen on (default: ${yellow("3000")})
${green("--host")} ${yellow("<host>")} Host to bind to (default: all interfaces)
${green("-s, --static")} ${yellow("<dir>")} Serve static files from the specified directory (default: ${yellow("public")})
${green("--prod")} Run in production mode (no watch, no debug)
${green("--import")} ${yellow("<loader>")} ES module to preload
${green("--tls")} Enable TLS (HTTPS/HTTP2)
${green("--cert")} ${yellow("<file>")} TLS certificate file
${green("--key")} ${yellow("<file>")} TLS private key file
${green("-h, --help")} Show this help message
${green("-v, --version")} Show server and runtime versions
${bold("ENVIRONMENT")}
${green("PORT")} Override port
${green("HOST")} Override host
${green("NODE_ENV")} Set to ${yellow("production")} for production mode.
${url("Documentation", mainOpts.docs || "https://srvx.h3.dev")}
${url("Report issues", mainOpts.issues || "https://github.com/h3js/srvx/issues")}
`.trim();
}
function setupProcessErrorHandlers() {
process.on("uncaughtException", (error) => {
console.error("Uncaught exception:", error);
process.exit(1);
});
process.on("unhandledRejection", (reason) => {
console.error("Unhandled rejection:", reason);
process.exit(1);
});
}
//#endregion
export { main, usage };

7
Frontend-Learner/node_modules/srvx/dist/log.d.mts generated vendored Normal file
View file

@ -0,0 +1,7 @@
import { m as ServerMiddleware } from "./_chunks/types.mjs";
//#region src/log.d.ts
interface LogOptions {}
declare const log: (_options?: LogOptions) => ServerMiddleware;
//#endregion
export { LogOptions, log };

21
Frontend-Learner/node_modules/srvx/dist/log.mjs generated vendored Normal file
View file

@ -0,0 +1,21 @@
import { a as green, i as gray, l as yellow, n as bold, s as red, t as blue } from "./_chunks/_color.mjs";
//#region src/log.ts
const statusColors = {
1: blue,
2: green,
3: yellow
};
const log = (_options = {}) => {
return async (req, next) => {
const start = performance.now();
const res = await next();
const duration = performance.now() - start;
const statusColor = statusColors[Math.floor(res.status / 100)] || red;
console.log(`${gray(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}]`)} ${bold(req.method)} ${blue(req.url)} [${statusColor(res.status + "")}] ${gray(`(${duration.toFixed(2)}ms)`)}`);
return res;
};
};
//#endregion
export { log };

24
Frontend-Learner/node_modules/srvx/dist/static.d.mts generated vendored Normal file
View file

@ -0,0 +1,24 @@
import { m as ServerMiddleware } from "./_chunks/types.mjs";
//#region src/static.d.ts
interface ServeStaticOptions {
/**
* The directory to serve static files from.
*/
dir: string;
/**
* The HTTP methods to allow for serving static files.
*/
methods?: string[];
/**
* A function to modify the HTML content before serving it.
*/
renderHTML?: (ctx: {
request: Request;
html: string;
filename: string;
}) => Response | Promise<Response>;
}
declare const serveStatic: (options: ServeStaticOptions) => ServerMiddleware;
//#endregion
export { ServeStaticOptions, serveStatic };

78
Frontend-Learner/node_modules/srvx/dist/static.mjs generated vendored Normal file
View file

@ -0,0 +1,78 @@
import { t as FastURL } from "./_chunks/_url.mjs";
import { extname, join, resolve } from "node:path";
import { createReadStream } from "node:fs";
import { readFile, stat } from "node:fs/promises";
import { FastResponse } from "srvx";
import { createBrotliCompress, createGzip } from "node:zlib";
//#region src/static.ts
const COMMON_MIME_TYPES = {
".html": "text/html",
".htm": "text/html",
".css": "text/css",
".js": "text/javascript",
".mjs": "text/javascript",
".json": "application/json",
".txt": "text/plain",
".xml": "application/xml",
".gif": "image/gif",
".ico": "image/vnd.microsoft.icon",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".svg": "image/svg+xml",
".webp": "image/webp",
".woff": "font/woff",
".woff2": "font/woff2",
".mp4": "video/mp4",
".webm": "video/webm",
".zip": "application/zip",
".pdf": "application/pdf"
};
const serveStatic = (options) => {
const dir = resolve(options.dir) + "/";
const methods = new Set((options.methods || ["GET", "HEAD"]).map((m) => m.toUpperCase()));
return async (req, next) => {
if (!methods.has(req.method)) return next();
const path = (req._url ??= new FastURL(req.url)).pathname.slice(1).replace(/\/$/, "");
let paths;
if (path === "") paths = ["index.html"];
else if (extname(path) === "") paths = [`${path}.html`, `${path}/index.html`];
else paths = [path];
for (const path$1 of paths) {
const filePath = join(dir, path$1);
if (!filePath.startsWith(dir)) continue;
const fileStat = await stat(filePath).catch(() => null);
if (fileStat?.isFile()) {
const fileExt = extname(filePath);
const headers = {
"Content-Length": fileStat.size.toString(),
"Content-Type": COMMON_MIME_TYPES[fileExt] || "application/octet-stream"
};
if (options.renderHTML && fileExt === ".html") return options.renderHTML({
html: await readFile(filePath, "utf8"),
filename: filePath,
request: req
});
let stream = createReadStream(filePath);
const acceptEncoding = req.headers.get("accept-encoding") || "";
if (acceptEncoding.includes("br")) {
headers["Content-Encoding"] = "br";
delete headers["Content-Length"];
headers["Vary"] = "Accept-Encoding";
stream = stream.pipe(createBrotliCompress());
} else if (acceptEncoding.includes("gzip")) {
headers["Content-Encoding"] = "gzip";
delete headers["Content-Length"];
headers["Vary"] = "Accept-Encoding";
stream = stream.pipe(createGzip());
}
return new FastResponse(stream, { headers });
}
}
return next();
};
};
//#endregion
export { serveStatic };

42
Frontend-Learner/node_modules/srvx/dist/tracing.d.mts generated vendored Normal file
View file

@ -0,0 +1,42 @@
import { _ as ServerRequest, f as Server, g as ServerPlugin, m as ServerMiddleware } from "./_chunks/types.mjs";
//#region src/tracing.d.ts
/**
* @experimental Channel names, event types and config options may change in future releases.
*/
type RequestEvent = {
server: Server;
request: ServerRequest;
middleware?: {
index: number;
handler: ServerMiddleware;
};
};
/**
*
* @experimental Channel names, event types and config options may change in future releases.
*
* Tracing plugin that adds diagnostics channel tracing to middleware and fetch handlers.
*
* This plugin wraps all middleware and the fetch handler with tracing instrumentation,
* allowing you to subscribe to `srvx.fetch` and `srvx.middleware` tracing channels.
*
* @example
* ```ts
* import { serve } from "srvx";
* import { tracingPlugin } from "srvx/tracing";
*
* const server = serve({
* fetch: (req) => new Response("OK"),
* middleware: [myMiddleware],
* plugins: [tracingPlugin()],
* });
* ```
*/
declare function tracingPlugin(opts?: {
middleware?: boolean;
fetch?: boolean;
}): ServerPlugin;
//#endregion
export { RequestEvent, tracingPlugin };

58
Frontend-Learner/node_modules/srvx/dist/tracing.mjs generated vendored Normal file
View file

@ -0,0 +1,58 @@
//#region src/tracing.ts
/**
*
* @experimental Channel names, event types and config options may change in future releases.
*
* Tracing plugin that adds diagnostics channel tracing to middleware and fetch handlers.
*
* This plugin wraps all middleware and the fetch handler with tracing instrumentation,
* allowing you to subscribe to `srvx.fetch` and `srvx.middleware` tracing channels.
*
* @example
* ```ts
* import { serve } from "srvx";
* import { tracingPlugin } from "srvx/tracing";
*
* const server = serve({
* fetch: (req) => new Response("OK"),
* middleware: [myMiddleware],
* plugins: [tracingPlugin()],
* });
* ```
*/
function tracingPlugin(opts = {}) {
return (server) => {
const { tracingChannel } = globalThis.process?.getBuiltinModule?.("node:diagnostics_channel") || {};
if (!tracingChannel) return;
if (opts.fetch !== false) {
const fetchChannel = tracingChannel("srvx.fetch");
const originalFetch = server.options.fetch;
server.options.fetch = (request) => {
return fetchChannel.tracePromise(async () => await originalFetch(request), {
request,
server
});
};
}
if (opts.middleware !== false) {
const middlewareChannel = tracingChannel("srvx.middleware");
const wrappedMiddleware = server.options.middleware.map((handler, index) => {
const middleware = Object.freeze({
index,
handler
});
return (request, next) => {
return middlewareChannel.tracePromise(async () => await handler(request, next), {
request,
server,
middleware
});
};
});
server.options.middleware.splice(0, server.options.middleware.length, ...wrappedMiddleware);
}
};
}
//#endregion
export { tracingPlugin };

2
Frontend-Learner/node_modules/srvx/dist/types.d.mts generated vendored Normal file
View file

@ -0,0 +1,2 @@
import { _ as ServerRequest, a as FastResponse, b as serve, c as NodeHTTPMiddleware, d as NodeServerResponse, f as Server, g as ServerPlugin, h as ServerOptions, i as ErrorHandler, l as NodeHttpHandler, m as ServerMiddleware, n as CloudflareFetchHandler, o as FastURL, p as ServerHandler, r as DenoFetchHandler, s as FetchHandler, t as BunFetchHandler, u as NodeServerRequest, v as ServerRequestContext, y as ServerRuntimeContext } from "./_chunks/types.mjs";
export { BunFetchHandler, CloudflareFetchHandler, DenoFetchHandler, ErrorHandler, FastResponse, FastURL, FetchHandler, NodeHTTPMiddleware, NodeHttpHandler, NodeServerRequest, NodeServerResponse, Server, ServerHandler, ServerMiddleware, ServerOptions, ServerPlugin, ServerRequest, ServerRequestContext, ServerRuntimeContext, serve };

97
Frontend-Learner/node_modules/srvx/package.json generated vendored Normal file
View file

@ -0,0 +1,97 @@
{
"name": "srvx",
"version": "0.9.8",
"description": "Universal Server API based on web platform standards. Works seamlessly with Deno, Bun and Node.js.",
"homepage": "https://srvx.h3.dev",
"repository": "h3js/srvx",
"license": "MIT",
"sideEffects": false,
"type": "module",
"exports": {
"./deno": "./dist/adapters/deno.mjs",
"./bun": "./dist/adapters/bun.mjs",
"./node": "./dist/adapters/node.mjs",
"./cloudflare": "./dist/adapters/cloudflare.mjs",
"./generic": "./dist/adapters/generic.mjs",
"./service-worker": "./dist/adapters/service-worker.mjs",
"./cli": "./dist/cli.mjs",
"./static": "./dist/static.mjs",
"./log": "./dist/log.mjs",
"./tracing": "./dist/tracing.mjs",
".": {
"types": "./dist/types.d.mts",
"deno": "./dist/adapters/deno.mjs",
"bun": "./dist/adapters/bun.mjs",
"workerd": "./dist/adapters/cloudflare.mjs",
"browser": "./dist/adapters/service-worker.mjs",
"node": "./dist/adapters/node.mjs",
"default": "./dist/adapters/generic.mjs"
}
},
"types": "./dist/types.d.mts",
"bin": {
"srvx": "./bin/srvx.mjs"
},
"files": [
"bin",
"dist"
],
"scripts": {
"bench:node": "node test/bench-node/_run.mjs",
"bench:url:bun": "bun run ./test/url.bench.ts",
"bench:url:deno": "deno run -A ./test/url.bench.ts",
"bench:url:node": "pnpm --expose-gc --allow-natives-syntax node ./test/url.bench.ts",
"build": "obuild",
"dev": "vitest dev",
"lint": "eslint . && prettier -c .",
"lint:fix": "automd && eslint . --fix && prettier -w .",
"prepack": "pnpm build",
"play:mkcert": "openssl req -x509 -newkey rsa:2048 -nodes -keyout server.key -out server.crt -days 365 -subj /CN=srvx.local",
"release": "pnpm test && pnpm build && changelogen --release && npm publish && git push --follow-tags",
"srvx": "./bin/srvx.mjs",
"test": "pnpm lint && pnpm test:types && vitest run --coverage",
"test:node-compat:deno": "deno run vitest test/node.test",
"test:node-compat:bun": "bun test test/node.test.ts",
"test:types": "tsc --noEmit --skipLibCheck",
"vitest": "vitest"
},
"resolutions": {
"srvx": "link:."
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251210.0",
"@hono/node-server": "^1.19.7",
"@mitata/counters": "^0.0.8",
"@mjackson/node-fetch-server": "^0.7.0",
"@types/bun": "^1.3.4",
"@types/deno": "^2.5.0",
"@types/express": "^5.0.6",
"@types/node": "^25.0.0",
"@types/node-forge": "^1.3.14",
"@types/serviceworker": "^0.0.168",
"@vitest/coverage-v8": "^4.0.15",
"@whatwg-node/server": "^0.10.17",
"automd": "^0.4.2",
"changelogen": "^0.6.2",
"eslint": "^9.39.1",
"eslint-config-unjs": "^0.5.0",
"execa": "^9.6.1",
"express": "^5.2.1",
"fastify": "^5.6.2",
"get-port-please": "^3.2.0",
"mdbox": "^0.1.1",
"mitata": "^1.0.34",
"node-forge": "^1.3.3",
"obuild": "^0.4.7",
"prettier": "^3.7.4",
"srvx-release": "npm:srvx@^0.9.7",
"tslib": "^2.8.1",
"typescript": "^5.9.3",
"undici": "^7.16.0",
"vitest": "^4.0.15"
},
"packageManager": "pnpm@10.25.0",
"engines": {
"node": ">=20.16.0"
}
}