@mjackson/node-fetch-server
Advanced tools
| export interface ClientAddress { | ||
| /** | ||
| * The IP address of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteaddress) | ||
| */ | ||
| address: string; | ||
| /** | ||
| * The family of the client IP address. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremotefamily) | ||
| */ | ||
| family: 'IPv4' | 'IPv6'; | ||
| /** | ||
| * The remote port of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteport) | ||
| */ | ||
| port: number; | ||
| } | ||
| /** | ||
| * A function that handles an error that occurred during request handling. May return a response to | ||
| * send to the client, or `undefined` to allow the server to send a default error response. | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| export interface ErrorHandler { | ||
| (error: unknown): void | Response | Promise<void | Response>; | ||
| } | ||
| /** | ||
| * A function that handles an incoming request and returns a response. | ||
| * | ||
| * [MDN `Request` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request) | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| export interface FetchHandler { | ||
| (request: Request, client: ClientAddress): Response | Promise<Response>; | ||
| } | ||
| //# sourceMappingURL=fetch-handler.d.ts.map |
| {"version":3,"file":"fetch-handler.d.ts","sourceRoot":"","sources":["../../src/lib/fetch-handler.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC;CAC9D;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACzE"} |
| export declare function readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array>; | ||
| //# sourceMappingURL=read-stream.d.ts.map |
| {"version":3,"file":"read-stream.d.ts","sourceRoot":"","sources":["../../src/lib/read-stream.ts"],"names":[],"mappings":"AAAA,wBAAuB,UAAU,CAAC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,CAQ/F"} |
| import type * as http from 'node:http'; | ||
| import type * as http2 from 'node:http2'; | ||
| import type { ErrorHandler, FetchHandler } from './fetch-handler.ts'; | ||
| export interface RequestListenerOptions { | ||
| /** | ||
| * Overrides the host portion of the incoming request URL. By default the request URL host is | ||
| * derived from the HTTP `Host` header. | ||
| * | ||
| * For example, if you have a `$HOST` environment variable that contains the hostname of your | ||
| * server, you can use it to set the host of all incoming request URLs like so: | ||
| * | ||
| * ```ts | ||
| * createRequestListener(handler, { host: process.env.HOST }) | ||
| * ``` | ||
| */ | ||
| host?: string; | ||
| /** | ||
| * An error handler that determines the response when the request handler throws an error. By | ||
| * default a 500 Internal Server Error response will be sent. | ||
| */ | ||
| onError?: ErrorHandler; | ||
| /** | ||
| * Overrides the protocol of the incoming request URL. By default the request URL protocol is | ||
| * derived from the connection protocol. So e.g. when serving over HTTPS (using | ||
| * `https.createServer()`), the request URL will begin with `https:`. | ||
| */ | ||
| protocol?: string; | ||
| } | ||
| /** | ||
| * Wraps a fetch handler in a Node.js request listener that can be used with: | ||
| * | ||
| * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener) | ||
| * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener) | ||
| * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler) | ||
| * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler) | ||
| * | ||
| * Example: | ||
| * | ||
| * ```ts | ||
| * import * as http from 'node:http'; | ||
| * import { createRequestListener } from '@mjackson/node-fetch-server'; | ||
| * | ||
| * async function handler(request) { | ||
| * return new Response('Hello, world!'); | ||
| * } | ||
| * | ||
| * let server = http.createServer( | ||
| * createRequestListener(handler) | ||
| * ); | ||
| * | ||
| * server.listen(3000); | ||
| * ``` | ||
| * | ||
| * @param handler The fetch handler to use for processing incoming requests. | ||
| * @param options Request listener options. | ||
| * @returns A Node.js request listener function. | ||
| */ | ||
| export declare function createRequestListener(handler: FetchHandler, options?: RequestListenerOptions): http.RequestListener; | ||
| export type RequestOptions = Omit<RequestListenerOptions, 'onError'>; | ||
| /** | ||
| * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from | ||
| * | ||
| * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair | ||
| * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair | ||
| * | ||
| * @param req The incoming request object. | ||
| * @param res The server response object. | ||
| * @param options | ||
| * @returns A request object. | ||
| */ | ||
| export declare function createRequest(req: http.IncomingMessage | http2.Http2ServerRequest, res: http.ServerResponse | http2.Http2ServerResponse, options?: RequestOptions): Request; | ||
| /** | ||
| * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js | ||
| * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest). | ||
| * | ||
| * @param req The incoming request object. | ||
| * @returns A headers object. | ||
| */ | ||
| export declare function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers; | ||
| /** | ||
| * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js | ||
| * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) | ||
| * object. | ||
| * | ||
| * @param res The server response object. | ||
| * @param response The response to send. | ||
| */ | ||
| export declare function sendResponse(res: http.ServerResponse | http2.Http2ServerResponse, response: Response): Promise<void>; | ||
| //# sourceMappingURL=request-listener.d.ts.map |
| {"version":3,"file":"request-listener.d.ts","sourceRoot":"","sources":["../../src/lib/request-listener.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,KAAK,KAAK,MAAM,YAAY,CAAC;AAEzC,OAAO,KAAK,EAAiB,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGpF,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;;;OAUG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,IAAI,CAAC,eAAe,CAyBtB;AAuBD,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,CAAC;AAErE;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,kBAAkB,EACpD,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,mBAAmB,EACpD,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAoCT;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,kBAAkB,GAAG,OAAO,CAU3F;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,mBAAmB,EACpD,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,IAAI,CAAC,CA2Bf"} |
| { | ||
| "version": 3, | ||
| "sources": ["../src/node-fetch-server.ts", "../src/lib/read-stream.ts", "../src/lib/request-listener.ts"], | ||
| "sourcesContent": ["export { type ClientAddress, type ErrorHandler, type FetchHandler } from './lib/fetch-handler.ts';\nexport {\n type RequestListenerOptions,\n createRequestListener,\n type RequestOptions,\n createRequest,\n createHeaders,\n sendResponse,\n} from './lib/request-listener.ts';\n", "export async function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> {\n let reader = stream.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n yield value;\n }\n}\n", "import type * as http from 'node:http';\nimport type * as http2 from 'node:http2';\n\nimport type { ClientAddress, ErrorHandler, FetchHandler } from './fetch-handler.ts';\nimport { readStream } from './read-stream.ts';\n\nexport interface RequestListenerOptions {\n /**\n * Overrides the host portion of the incoming request URL. By default the request URL host is\n * derived from the HTTP `Host` header.\n *\n * For example, if you have a `$HOST` environment variable that contains the hostname of your\n * server, you can use it to set the host of all incoming request URLs like so:\n *\n * ```ts\n * createRequestListener(handler, { host: process.env.HOST })\n * ```\n */\n host?: string;\n /**\n * An error handler that determines the response when the request handler throws an error. By\n * default a 500 Internal Server Error response will be sent.\n */\n onError?: ErrorHandler;\n /**\n * Overrides the protocol of the incoming request URL. By default the request URL protocol is\n * derived from the connection protocol. So e.g. when serving over HTTPS (using\n * `https.createServer()`), the request URL will begin with `https:`.\n */\n protocol?: string;\n}\n\n/**\n * Wraps a fetch handler in a Node.js request listener that can be used with:\n *\n * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener)\n * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener)\n * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler)\n * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler)\n *\n * Example:\n *\n * ```ts\n * import * as http from 'node:http';\n * import { createRequestListener } from '@mjackson/node-fetch-server';\n *\n * async function handler(request) {\n * return new Response('Hello, world!');\n * }\n *\n * let server = http.createServer(\n * createRequestListener(handler)\n * );\n *\n * server.listen(3000);\n * ```\n *\n * @param handler The fetch handler to use for processing incoming requests.\n * @param options Request listener options.\n * @returns A Node.js request listener function.\n */\nexport function createRequestListener(\n handler: FetchHandler,\n options?: RequestListenerOptions,\n): http.RequestListener {\n let onError = options?.onError ?? defaultErrorHandler;\n\n return async (req, res) => {\n let request = createRequest(req, res, options);\n let client = {\n address: req.socket.remoteAddress!,\n family: req.socket.remoteFamily! as ClientAddress['family'],\n port: req.socket.remotePort!,\n };\n\n let response: Response;\n try {\n response = await handler(request, client);\n } catch (error) {\n try {\n response = (await onError(error)) ?? internalServerError();\n } catch (error) {\n console.error(`There was an error in the error handler: ${error}`);\n response = internalServerError();\n }\n }\n\n await sendResponse(res, response);\n };\n}\n\nfunction defaultErrorHandler(error: unknown): Response {\n console.error(error);\n return internalServerError();\n}\n\nfunction internalServerError(): Response {\n return new Response(\n // \"Internal Server Error\"\n new Uint8Array([\n 73, 110, 116, 101, 114, 110, 97, 108, 32, 83, 101, 114, 118, 101, 114, 32, 69, 114, 114, 111,\n 114,\n ]),\n {\n status: 500,\n headers: {\n 'Content-Type': 'text/plain',\n },\n },\n );\n}\n\nexport type RequestOptions = Omit<RequestListenerOptions, 'onError'>;\n\n/**\n * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from\n *\n * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair\n * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair\n *\n * @param req The incoming request object.\n * @param res The server response object.\n * @param options\n * @returns A request object.\n */\nexport function createRequest(\n req: http.IncomingMessage | http2.Http2ServerRequest,\n res: http.ServerResponse | http2.Http2ServerResponse,\n options?: RequestOptions,\n): Request {\n let controller = new AbortController();\n res.on('close', () => {\n controller.abort();\n });\n\n let method = req.method ?? 'GET';\n let headers = createHeaders(req);\n\n let protocol =\n options?.protocol ?? ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:');\n let host = options?.host ?? headers.get('Host') ?? 'localhost';\n let url = new URL(req.url!, `${protocol}//${host}`);\n\n let init: RequestInit = { method, headers, signal: controller.signal };\n\n if (method !== 'GET' && method !== 'HEAD') {\n init.body = new ReadableStream({\n start(controller) {\n req.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n });\n req.on('end', () => {\n controller.close();\n });\n },\n });\n\n // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec.\n // However, this property is not defined in the TypeScript types for RequestInit, so we have\n // to cast it here in order to set it without a type error.\n // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex\n (init as { duplex: 'half' }).duplex = 'half';\n }\n\n return new Request(url, init);\n}\n\n/**\n * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js\n * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest).\n *\n * @param req The incoming request object.\n * @returns A headers object.\n */\nexport function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers {\n let headers = new Headers();\n\n let rawHeaders = req.rawHeaders;\n for (let i = 0; i < rawHeaders.length; i += 2) {\n if (rawHeaders[i].startsWith(':')) continue;\n headers.append(rawHeaders[i], rawHeaders[i + 1]);\n }\n\n return headers;\n}\n\n/**\n * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js\n * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse)\n * object.\n *\n * @param res The server response object.\n * @param response The response to send.\n */\nexport async function sendResponse(\n res: http.ServerResponse | http2.Http2ServerResponse,\n response: Response,\n): Promise<void> {\n // Iterate over response.headers so we are sure to send multiple Set-Cookie headers correctly.\n // These would incorrectly be merged into a single header if we tried to use\n // `Object.fromEntries(response.headers.entries())`.\n let headers: Record<string, string | string[]> = {};\n for (let [key, value] of response.headers) {\n if (key in headers) {\n if (Array.isArray(headers[key])) {\n headers[key].push(value);\n } else {\n headers[key] = [headers[key] as string, value];\n }\n } else {\n headers[key] = value;\n }\n }\n\n res.writeHead(response.status, headers);\n\n if (response.body != null && res.req.method !== 'HEAD') {\n for await (let chunk of readStream(response.body)) {\n // @ts-expect-error - Node typings for http2 require a 2nd parameter to write but it's optional\n res.write(chunk);\n }\n }\n\n res.end();\n}\n"], | ||
| "mappings": "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", | ||
| "names": ["error", "controller"] | ||
| } |
| {"version":3,"file":"node-fetch-server.d.ts","sourceRoot":"","sources":["../src/node-fetch-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAClG,OAAO,EACL,KAAK,sBAAsB,EAC3B,qBAAqB,EACrB,KAAK,cAAc,EACnB,aAAa,EACb,aAAa,EACb,YAAY,GACb,MAAM,2BAA2B,CAAC"} |
| { | ||
| "version": 3, | ||
| "sources": ["../src/lib/read-stream.ts", "../src/lib/request-listener.ts"], | ||
| "sourcesContent": ["export async function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> {\n let reader = stream.getReader();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n yield value;\n }\n}\n", "import type * as http from 'node:http';\nimport type * as http2 from 'node:http2';\n\nimport type { ClientAddress, ErrorHandler, FetchHandler } from './fetch-handler.ts';\nimport { readStream } from './read-stream.ts';\n\nexport interface RequestListenerOptions {\n /**\n * Overrides the host portion of the incoming request URL. By default the request URL host is\n * derived from the HTTP `Host` header.\n *\n * For example, if you have a `$HOST` environment variable that contains the hostname of your\n * server, you can use it to set the host of all incoming request URLs like so:\n *\n * ```ts\n * createRequestListener(handler, { host: process.env.HOST })\n * ```\n */\n host?: string;\n /**\n * An error handler that determines the response when the request handler throws an error. By\n * default a 500 Internal Server Error response will be sent.\n */\n onError?: ErrorHandler;\n /**\n * Overrides the protocol of the incoming request URL. By default the request URL protocol is\n * derived from the connection protocol. So e.g. when serving over HTTPS (using\n * `https.createServer()`), the request URL will begin with `https:`.\n */\n protocol?: string;\n}\n\n/**\n * Wraps a fetch handler in a Node.js request listener that can be used with:\n *\n * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener)\n * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener)\n * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler)\n * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler)\n *\n * Example:\n *\n * ```ts\n * import * as http from 'node:http';\n * import { createRequestListener } from '@mjackson/node-fetch-server';\n *\n * async function handler(request) {\n * return new Response('Hello, world!');\n * }\n *\n * let server = http.createServer(\n * createRequestListener(handler)\n * );\n *\n * server.listen(3000);\n * ```\n *\n * @param handler The fetch handler to use for processing incoming requests.\n * @param options Request listener options.\n * @returns A Node.js request listener function.\n */\nexport function createRequestListener(\n handler: FetchHandler,\n options?: RequestListenerOptions,\n): http.RequestListener {\n let onError = options?.onError ?? defaultErrorHandler;\n\n return async (req, res) => {\n let request = createRequest(req, res, options);\n let client = {\n address: req.socket.remoteAddress!,\n family: req.socket.remoteFamily! as ClientAddress['family'],\n port: req.socket.remotePort!,\n };\n\n let response: Response;\n try {\n response = await handler(request, client);\n } catch (error) {\n try {\n response = (await onError(error)) ?? internalServerError();\n } catch (error) {\n console.error(`There was an error in the error handler: ${error}`);\n response = internalServerError();\n }\n }\n\n await sendResponse(res, response);\n };\n}\n\nfunction defaultErrorHandler(error: unknown): Response {\n console.error(error);\n return internalServerError();\n}\n\nfunction internalServerError(): Response {\n return new Response(\n // \"Internal Server Error\"\n new Uint8Array([\n 73, 110, 116, 101, 114, 110, 97, 108, 32, 83, 101, 114, 118, 101, 114, 32, 69, 114, 114, 111,\n 114,\n ]),\n {\n status: 500,\n headers: {\n 'Content-Type': 'text/plain',\n },\n },\n );\n}\n\nexport type RequestOptions = Omit<RequestListenerOptions, 'onError'>;\n\n/**\n * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from\n *\n * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair\n * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair\n *\n * @param req The incoming request object.\n * @param res The server response object.\n * @param options\n * @returns A request object.\n */\nexport function createRequest(\n req: http.IncomingMessage | http2.Http2ServerRequest,\n res: http.ServerResponse | http2.Http2ServerResponse,\n options?: RequestOptions,\n): Request {\n let controller = new AbortController();\n res.on('close', () => {\n controller.abort();\n });\n\n let method = req.method ?? 'GET';\n let headers = createHeaders(req);\n\n let protocol =\n options?.protocol ?? ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:');\n let host = options?.host ?? headers.get('Host') ?? 'localhost';\n let url = new URL(req.url!, `${protocol}//${host}`);\n\n let init: RequestInit = { method, headers, signal: controller.signal };\n\n if (method !== 'GET' && method !== 'HEAD') {\n init.body = new ReadableStream({\n start(controller) {\n req.on('data', (chunk) => {\n controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength));\n });\n req.on('end', () => {\n controller.close();\n });\n },\n });\n\n // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec.\n // However, this property is not defined in the TypeScript types for RequestInit, so we have\n // to cast it here in order to set it without a type error.\n // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex\n (init as { duplex: 'half' }).duplex = 'half';\n }\n\n return new Request(url, init);\n}\n\n/**\n * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js\n * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest).\n *\n * @param req The incoming request object.\n * @returns A headers object.\n */\nexport function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers {\n let headers = new Headers();\n\n let rawHeaders = req.rawHeaders;\n for (let i = 0; i < rawHeaders.length; i += 2) {\n if (rawHeaders[i].startsWith(':')) continue;\n headers.append(rawHeaders[i], rawHeaders[i + 1]);\n }\n\n return headers;\n}\n\n/**\n * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js\n * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse)\n * object.\n *\n * @param res The server response object.\n * @param response The response to send.\n */\nexport async function sendResponse(\n res: http.ServerResponse | http2.Http2ServerResponse,\n response: Response,\n): Promise<void> {\n // Iterate over response.headers so we are sure to send multiple Set-Cookie headers correctly.\n // These would incorrectly be merged into a single header if we tried to use\n // `Object.fromEntries(response.headers.entries())`.\n let headers: Record<string, string | string[]> = {};\n for (let [key, value] of response.headers) {\n if (key in headers) {\n if (Array.isArray(headers[key])) {\n headers[key].push(value);\n } else {\n headers[key] = [headers[key] as string, value];\n }\n } else {\n headers[key] = value;\n }\n }\n\n res.writeHead(response.status, headers);\n\n if (response.body != null && res.req.method !== 'HEAD') {\n for await (let chunk of readStream(response.body)) {\n // @ts-expect-error - Node typings for http2 require a 2nd parameter to write but it's optional\n res.write(chunk);\n }\n }\n\n res.end();\n}\n"], | ||
| "mappings": "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", | ||
| "names": ["error", "controller"] | ||
| } |
| export interface ClientAddress { | ||
| /** | ||
| * The IP address of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteaddress) | ||
| */ | ||
| address: string; | ||
| /** | ||
| * The family of the client IP address. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremotefamily) | ||
| */ | ||
| family: 'IPv4' | 'IPv6'; | ||
| /** | ||
| * The remote port of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteport) | ||
| */ | ||
| port: number; | ||
| } | ||
| /** | ||
| * A function that handles an error that occurred during request handling. May return a response to | ||
| * send to the client, or `undefined` to allow the server to send a default error response. | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| export interface ErrorHandler { | ||
| (error: unknown): void | Response | Promise<void | Response>; | ||
| } | ||
| /** | ||
| * A function that handles an incoming request and returns a response. | ||
| * | ||
| * [MDN `Request` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request) | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| export interface FetchHandler { | ||
| (request: Request, client: ClientAddress): Response | Promise<Response>; | ||
| } |
| export async function* readStream(stream: ReadableStream<Uint8Array>): AsyncIterable<Uint8Array> { | ||
| let reader = stream.getReader(); | ||
| while (true) { | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| yield value; | ||
| } | ||
| } |
| import * as assert from 'node:assert/strict'; | ||
| import { describe, it, mock } from 'node:test'; | ||
| import * as http from 'node:http'; | ||
| import * as stream from 'node:stream'; | ||
| import { type FetchHandler } from './fetch-handler.ts'; | ||
| import { createRequestListener } from './request-listener.ts'; | ||
| describe('createRequestListener', () => { | ||
| it('returns a request listener', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async () => { | ||
| return new Response('Hello, world!'); | ||
| }; | ||
| let listener = createRequestListener(handler); | ||
| assert.ok(listener); | ||
| let req = createMockRequest(); | ||
| let res = createMockResponse({ req }); | ||
| let chunks: Uint8Array[] = []; | ||
| mock.method(res, 'write', (chunk: Uint8Array) => { | ||
| chunks.push(chunk); | ||
| }); | ||
| mock.method(res, 'end', () => { | ||
| let body = Buffer.concat(chunks).toString(); | ||
| assert.equal(body, 'Hello, world!'); | ||
| resolve(); | ||
| }); | ||
| listener(req, res); | ||
| }); | ||
| }); | ||
| it('calls onError when an error is thrown in the request handler', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async () => { | ||
| throw new Error('boom!'); | ||
| }; | ||
| let errorHandler = mock.fn(); | ||
| let listener = createRequestListener(handler, { onError: errorHandler }); | ||
| assert.ok(listener); | ||
| let req = createMockRequest(); | ||
| let res = createMockResponse({ req }); | ||
| mock.method(res, 'end', () => { | ||
| assert.equal(errorHandler.mock.calls.length, 1); | ||
| resolve(); | ||
| }); | ||
| listener(req, res); | ||
| }); | ||
| }); | ||
| it('returns a 500 "Internal Server Error" response when an error is thrown in the request handler', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async () => { | ||
| throw new Error('boom!'); | ||
| }; | ||
| let errorHandler = async () => { | ||
| // ignore | ||
| }; | ||
| let listener = createRequestListener(handler, { onError: errorHandler }); | ||
| assert.ok(listener); | ||
| let req = createMockRequest(); | ||
| let res = createMockResponse({ req }); | ||
| let status: number | undefined; | ||
| mock.method(res, 'writeHead', (statusCode: number) => { | ||
| status = statusCode; | ||
| }); | ||
| let chunks: Uint8Array[] = []; | ||
| mock.method(res, 'write', (chunk: Uint8Array) => { | ||
| chunks.push(chunk); | ||
| }); | ||
| mock.method(res, 'end', () => { | ||
| assert.equal(status, 500); | ||
| let body = Buffer.concat(chunks).toString(); | ||
| assert.equal(body, 'Internal Server Error'); | ||
| resolve(); | ||
| }); | ||
| listener(req, res); | ||
| }); | ||
| }); | ||
| it('uses the `Host` header to construct the URL by default', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async (request) => { | ||
| assert.equal(request.url, 'http://example.com/'); | ||
| return new Response('Hello, world!'); | ||
| }; | ||
| let listener = createRequestListener(handler); | ||
| assert.ok(listener); | ||
| let req = createMockRequest({ headers: { host: 'example.com' } }); | ||
| let res = createMockResponse({ req }); | ||
| listener(req, res); | ||
| resolve(); | ||
| }); | ||
| }); | ||
| it('uses the `host` option to override the `Host` header', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async (request) => { | ||
| assert.equal(request.url, 'http://remix.run/'); | ||
| return new Response('Hello, world!'); | ||
| }; | ||
| let listener = createRequestListener(handler, { host: 'remix.run' }); | ||
| assert.ok(listener); | ||
| let req = createMockRequest({ headers: { host: 'example.com' } }); | ||
| let res = createMockResponse({ req }); | ||
| listener(req, res); | ||
| resolve(); | ||
| }); | ||
| }); | ||
| it('uses the `protocol` option to construct the URL', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async (request) => { | ||
| assert.equal(request.url, 'https://example.com/'); | ||
| return new Response('Hello, world!'); | ||
| }; | ||
| let listener = createRequestListener(handler, { protocol: 'https:' }); | ||
| assert.ok(listener); | ||
| let req = createMockRequest({ headers: { host: 'example.com' } }); | ||
| let res = createMockResponse({ req }); | ||
| listener(req, res); | ||
| resolve(); | ||
| }); | ||
| }); | ||
| it('sets multiple Set-Cookie headers', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async () => { | ||
| let headers = new Headers(); | ||
| headers.set('Content-Type', 'text/plain'); | ||
| headers.append('Set-Cookie', 'a=1'); | ||
| headers.append('Set-Cookie', 'b=2'); | ||
| return new Response('Hello, world!', { headers }); | ||
| }; | ||
| let listener = createRequestListener(handler); | ||
| assert.ok(listener); | ||
| let req = createMockRequest(); | ||
| let res = createMockResponse({ req }); | ||
| let headers: string[]; | ||
| mock.method(res, 'writeHead', (_status: number, headersArray: string[]) => { | ||
| headers = headersArray; | ||
| }); | ||
| mock.method(res, 'end', () => { | ||
| assert.deepEqual(headers, { | ||
| 'content-type': 'text/plain', | ||
| 'set-cookie': ['a=1', 'b=2'], | ||
| }); | ||
| resolve(); | ||
| }); | ||
| listener(req, res); | ||
| }); | ||
| }); | ||
| it('truncates the response body when the request method is HEAD', async () => { | ||
| await new Promise<void>((resolve) => { | ||
| let handler: FetchHandler = async () => { | ||
| return new Response('Hello, world!'); | ||
| }; | ||
| let listener = createRequestListener(handler); | ||
| assert.ok(listener); | ||
| let req = createMockRequest({ method: 'HEAD' }); | ||
| let res = createMockResponse({ req }); | ||
| let chunks: Uint8Array[] = []; | ||
| mock.method(res, 'write', (chunk: Uint8Array) => { | ||
| chunks.push(chunk); | ||
| }); | ||
| mock.method(res, 'end', () => { | ||
| assert.equal(chunks.length, 0); | ||
| resolve(); | ||
| }); | ||
| listener(req, res); | ||
| }); | ||
| }); | ||
| }); | ||
| function createMockRequest({ | ||
| url = '/', | ||
| method = 'GET', | ||
| headers = {}, | ||
| socket = {}, | ||
| body, | ||
| }: { | ||
| method?: string; | ||
| url?: string; | ||
| headers?: Record<string, string>; | ||
| socket?: { | ||
| encrypted?: boolean; | ||
| remoteAddress?: string; | ||
| }; | ||
| body?: string | Buffer; | ||
| } = {}): http.IncomingMessage { | ||
| let rawHeaders = Object.entries(headers).flatMap(([key, value]) => [key, value]); | ||
| return Object.assign( | ||
| new stream.Readable({ | ||
| read() { | ||
| if (body != null) this.push(Buffer.from(body)); | ||
| this.push(null); | ||
| }, | ||
| }), | ||
| { | ||
| url, | ||
| method, | ||
| rawHeaders, | ||
| socket, | ||
| }, | ||
| ) as http.IncomingMessage; | ||
| } | ||
| function createMockResponse({ | ||
| req = createMockRequest(), | ||
| }: { | ||
| req: http.IncomingMessage; | ||
| }): http.ServerResponse { | ||
| return Object.assign(new stream.Writable(), { | ||
| req, | ||
| writeHead() {}, | ||
| write() {}, | ||
| end() {}, | ||
| }) as unknown as http.ServerResponse; | ||
| } |
| import type * as http from 'node:http'; | ||
| import type * as http2 from 'node:http2'; | ||
| import type { ClientAddress, ErrorHandler, FetchHandler } from './fetch-handler.ts'; | ||
| import { readStream } from './read-stream.ts'; | ||
| export interface RequestListenerOptions { | ||
| /** | ||
| * Overrides the host portion of the incoming request URL. By default the request URL host is | ||
| * derived from the HTTP `Host` header. | ||
| * | ||
| * For example, if you have a `$HOST` environment variable that contains the hostname of your | ||
| * server, you can use it to set the host of all incoming request URLs like so: | ||
| * | ||
| * ```ts | ||
| * createRequestListener(handler, { host: process.env.HOST }) | ||
| * ``` | ||
| */ | ||
| host?: string; | ||
| /** | ||
| * An error handler that determines the response when the request handler throws an error. By | ||
| * default a 500 Internal Server Error response will be sent. | ||
| */ | ||
| onError?: ErrorHandler; | ||
| /** | ||
| * Overrides the protocol of the incoming request URL. By default the request URL protocol is | ||
| * derived from the connection protocol. So e.g. when serving over HTTPS (using | ||
| * `https.createServer()`), the request URL will begin with `https:`. | ||
| */ | ||
| protocol?: string; | ||
| } | ||
| /** | ||
| * Wraps a fetch handler in a Node.js request listener that can be used with: | ||
| * | ||
| * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener) | ||
| * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener) | ||
| * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler) | ||
| * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler) | ||
| * | ||
| * Example: | ||
| * | ||
| * ```ts | ||
| * import * as http from 'node:http'; | ||
| * import { createRequestListener } from '@mjackson/node-fetch-server'; | ||
| * | ||
| * async function handler(request) { | ||
| * return new Response('Hello, world!'); | ||
| * } | ||
| * | ||
| * let server = http.createServer( | ||
| * createRequestListener(handler) | ||
| * ); | ||
| * | ||
| * server.listen(3000); | ||
| * ``` | ||
| * | ||
| * @param handler The fetch handler to use for processing incoming requests. | ||
| * @param options Request listener options. | ||
| * @returns A Node.js request listener function. | ||
| */ | ||
| export function createRequestListener( | ||
| handler: FetchHandler, | ||
| options?: RequestListenerOptions, | ||
| ): http.RequestListener { | ||
| let onError = options?.onError ?? defaultErrorHandler; | ||
| return async (req, res) => { | ||
| let request = createRequest(req, res, options); | ||
| let client = { | ||
| address: req.socket.remoteAddress!, | ||
| family: req.socket.remoteFamily! as ClientAddress['family'], | ||
| port: req.socket.remotePort!, | ||
| }; | ||
| let response: Response; | ||
| try { | ||
| response = await handler(request, client); | ||
| } catch (error) { | ||
| try { | ||
| response = (await onError(error)) ?? internalServerError(); | ||
| } catch (error) { | ||
| console.error(`There was an error in the error handler: ${error}`); | ||
| response = internalServerError(); | ||
| } | ||
| } | ||
| await sendResponse(res, response); | ||
| }; | ||
| } | ||
| function defaultErrorHandler(error: unknown): Response { | ||
| console.error(error); | ||
| return internalServerError(); | ||
| } | ||
| function internalServerError(): Response { | ||
| return new Response( | ||
| // "Internal Server Error" | ||
| new Uint8Array([ | ||
| 73, 110, 116, 101, 114, 110, 97, 108, 32, 83, 101, 114, 118, 101, 114, 32, 69, 114, 114, 111, | ||
| 114, | ||
| ]), | ||
| { | ||
| status: 500, | ||
| headers: { | ||
| 'Content-Type': 'text/plain', | ||
| }, | ||
| }, | ||
| ); | ||
| } | ||
| export type RequestOptions = Omit<RequestListenerOptions, 'onError'>; | ||
| /** | ||
| * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from | ||
| * | ||
| * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair | ||
| * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair | ||
| * | ||
| * @param req The incoming request object. | ||
| * @param res The server response object. | ||
| * @param options | ||
| * @returns A request object. | ||
| */ | ||
| export function createRequest( | ||
| req: http.IncomingMessage | http2.Http2ServerRequest, | ||
| res: http.ServerResponse | http2.Http2ServerResponse, | ||
| options?: RequestOptions, | ||
| ): Request { | ||
| let controller = new AbortController(); | ||
| res.on('close', () => { | ||
| controller.abort(); | ||
| }); | ||
| let method = req.method ?? 'GET'; | ||
| let headers = createHeaders(req); | ||
| let protocol = | ||
| options?.protocol ?? ('encrypted' in req.socket && req.socket.encrypted ? 'https:' : 'http:'); | ||
| let host = options?.host ?? headers.get('Host') ?? 'localhost'; | ||
| let url = new URL(req.url!, `${protocol}//${host}`); | ||
| let init: RequestInit = { method, headers, signal: controller.signal }; | ||
| if (method !== 'GET' && method !== 'HEAD') { | ||
| init.body = new ReadableStream({ | ||
| start(controller) { | ||
| req.on('data', (chunk) => { | ||
| controller.enqueue(new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength)); | ||
| }); | ||
| req.on('end', () => { | ||
| controller.close(); | ||
| }); | ||
| }, | ||
| }); | ||
| // init.duplex = 'half' must be set when body is a ReadableStream, and Node follows the spec. | ||
| // However, this property is not defined in the TypeScript types for RequestInit, so we have | ||
| // to cast it here in order to set it without a type error. | ||
| // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex | ||
| (init as { duplex: 'half' }).duplex = 'half'; | ||
| } | ||
| return new Request(url, init); | ||
| } | ||
| /** | ||
| * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js | ||
| * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest). | ||
| * | ||
| * @param req The incoming request object. | ||
| * @returns A headers object. | ||
| */ | ||
| export function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers { | ||
| let headers = new Headers(); | ||
| let rawHeaders = req.rawHeaders; | ||
| for (let i = 0; i < rawHeaders.length; i += 2) { | ||
| if (rawHeaders[i].startsWith(':')) continue; | ||
| headers.append(rawHeaders[i], rawHeaders[i + 1]); | ||
| } | ||
| return headers; | ||
| } | ||
| /** | ||
| * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js | ||
| * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) | ||
| * object. | ||
| * | ||
| * @param res The server response object. | ||
| * @param response The response to send. | ||
| */ | ||
| export async function sendResponse( | ||
| res: http.ServerResponse | http2.Http2ServerResponse, | ||
| response: Response, | ||
| ): Promise<void> { | ||
| // Iterate over response.headers so we are sure to send multiple Set-Cookie headers correctly. | ||
| // These would incorrectly be merged into a single header if we tried to use | ||
| // `Object.fromEntries(response.headers.entries())`. | ||
| let headers: Record<string, string | string[]> = {}; | ||
| for (let [key, value] of response.headers) { | ||
| if (key in headers) { | ||
| if (Array.isArray(headers[key])) { | ||
| headers[key].push(value); | ||
| } else { | ||
| headers[key] = [headers[key] as string, value]; | ||
| } | ||
| } else { | ||
| headers[key] = value; | ||
| } | ||
| } | ||
| res.writeHead(response.status, headers); | ||
| if (response.body != null && res.req.method !== 'HEAD') { | ||
| for await (let chunk of readStream(response.body)) { | ||
| // @ts-expect-error - Node typings for http2 require a 2nd parameter to write but it's optional | ||
| res.write(chunk); | ||
| } | ||
| } | ||
| res.end(); | ||
| } |
| export { type ClientAddress, type ErrorHandler, type FetchHandler } from './lib/fetch-handler.ts'; | ||
| export { | ||
| type RequestListenerOptions, | ||
| createRequestListener, | ||
| type RequestOptions, | ||
| createRequest, | ||
| createHeaders, | ||
| sendResponse, | ||
| } from './lib/request-listener.ts'; |
@@ -35,3 +35,4 @@ "use strict"; | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| if (done) | ||
| break; | ||
| yield value; | ||
@@ -133,3 +134,4 @@ } | ||
| for (let i = 0; i < rawHeaders.length; i += 2) { | ||
| if (rawHeaders[i].startsWith(":")) continue; | ||
| if (rawHeaders[i].startsWith(":")) | ||
| continue; | ||
| headers.append(rawHeaders[i], rawHeaders[i + 1]); | ||
@@ -167,1 +169,2 @@ } | ||
| }); | ||
| //# sourceMappingURL=node-fetch-server.cjs.map |
@@ -1,130 +0,3 @@ | ||
| import * as http from 'node:http'; | ||
| import * as http2 from 'node:http2'; | ||
| interface ClientAddress { | ||
| /** | ||
| * The IP address of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteaddress) | ||
| */ | ||
| address: string; | ||
| /** | ||
| * The family of the client IP address. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremotefamily) | ||
| */ | ||
| family: 'IPv4' | 'IPv6'; | ||
| /** | ||
| * The remote port of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteport) | ||
| */ | ||
| port: number; | ||
| } | ||
| /** | ||
| * A function that handles an error that occurred during request handling. May return a response to | ||
| * send to the client, or `undefined` to allow the server to send a default error response. | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| interface ErrorHandler { | ||
| (error: unknown): void | Response | Promise<void | Response>; | ||
| } | ||
| /** | ||
| * A function that handles an incoming request and returns a response. | ||
| * | ||
| * [MDN `Request` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request) | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| interface FetchHandler { | ||
| (request: Request, client: ClientAddress): Response | Promise<Response>; | ||
| } | ||
| interface RequestListenerOptions { | ||
| /** | ||
| * Overrides the host portion of the incoming request URL. By default the request URL host is | ||
| * derived from the HTTP `Host` header. | ||
| * | ||
| * For example, if you have a `$HOST` environment variable that contains the hostname of your | ||
| * server, you can use it to set the host of all incoming request URLs like so: | ||
| * | ||
| * ```ts | ||
| * createRequestListener(handler, { host: process.env.HOST }) | ||
| * ``` | ||
| */ | ||
| host?: string; | ||
| /** | ||
| * An error handler that determines the response when the request handler throws an error. By | ||
| * default a 500 Internal Server Error response will be sent. | ||
| */ | ||
| onError?: ErrorHandler; | ||
| /** | ||
| * Overrides the protocol of the incoming request URL. By default the request URL protocol is | ||
| * derived from the connection protocol. So e.g. when serving over HTTPS (using | ||
| * `https.createServer()`), the request URL will begin with `https:`. | ||
| */ | ||
| protocol?: string; | ||
| } | ||
| /** | ||
| * Wraps a fetch handler in a Node.js request listener that can be used with: | ||
| * | ||
| * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener) | ||
| * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener) | ||
| * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler) | ||
| * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler) | ||
| * | ||
| * Example: | ||
| * | ||
| * ```ts | ||
| * import * as http from 'node:http'; | ||
| * import { createRequestListener } from '@mjackson/node-fetch-server'; | ||
| * | ||
| * async function handler(request) { | ||
| * return new Response('Hello, world!'); | ||
| * } | ||
| * | ||
| * let server = http.createServer( | ||
| * createRequestListener(handler) | ||
| * ); | ||
| * | ||
| * server.listen(3000); | ||
| * ``` | ||
| * | ||
| * @param handler The fetch handler to use for processing incoming requests. | ||
| * @param options Request listener options. | ||
| * @returns A Node.js request listener function. | ||
| */ | ||
| declare function createRequestListener(handler: FetchHandler, options?: RequestListenerOptions): http.RequestListener; | ||
| type RequestOptions = Omit<RequestListenerOptions, 'onError'>; | ||
| /** | ||
| * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from | ||
| * | ||
| * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair | ||
| * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair | ||
| * | ||
| * @param req The incoming request object. | ||
| * @param res The server response object. | ||
| * @param options | ||
| * @returns A request object. | ||
| */ | ||
| declare function createRequest(req: http.IncomingMessage | http2.Http2ServerRequest, res: http.ServerResponse | http2.Http2ServerResponse, options?: RequestOptions): Request; | ||
| /** | ||
| * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js | ||
| * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest). | ||
| * | ||
| * @param req The incoming request object. | ||
| * @returns A headers object. | ||
| */ | ||
| declare function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers; | ||
| /** | ||
| * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js | ||
| * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) | ||
| * object. | ||
| * | ||
| * @param res The server response object. | ||
| * @param response The response to send. | ||
| */ | ||
| declare function sendResponse(res: http.ServerResponse | http2.Http2ServerResponse, response: Response): Promise<void>; | ||
| export { type ClientAddress, type ErrorHandler, type FetchHandler, type RequestListenerOptions, type RequestOptions, createHeaders, createRequest, createRequestListener, sendResponse }; | ||
| export { type ClientAddress, type ErrorHandler, type FetchHandler } from './lib/fetch-handler.ts'; | ||
| export { type RequestListenerOptions, createRequestListener, type RequestOptions, createRequest, createHeaders, sendResponse, } from './lib/request-listener.ts'; | ||
| //# sourceMappingURL=node-fetch-server.d.ts.map |
@@ -6,3 +6,4 @@ // src/lib/read-stream.ts | ||
| const { done, value } = await reader.read(); | ||
| if (done) break; | ||
| if (done) | ||
| break; | ||
| yield value; | ||
@@ -104,3 +105,4 @@ } | ||
| for (let i = 0; i < rawHeaders.length; i += 2) { | ||
| if (rawHeaders[i].startsWith(":")) continue; | ||
| if (rawHeaders[i].startsWith(":")) | ||
| continue; | ||
| headers.append(rawHeaders[i], rawHeaders[i + 1]); | ||
@@ -137,1 +139,2 @@ } | ||
| }; | ||
| //# sourceMappingURL=node-fetch-server.js.map |
+16
-22
| { | ||
| "name": "@mjackson/node-fetch-server", | ||
| "version": "0.6.1", | ||
| "version": "0.7.0", | ||
| "description": "Build servers for Node.js using the web fetch API", | ||
@@ -14,27 +14,17 @@ "author": "Michael Jackson <mjijackson@gmail.com>", | ||
| "files": [ | ||
| "LICENSE", | ||
| "README.md", | ||
| "dist", | ||
| "LICENSE", | ||
| "README.md" | ||
| "src" | ||
| ], | ||
| "type": "module", | ||
| "types": "./dist/node-fetch-server.d.ts", | ||
| "main": "./dist/node-fetch-server.js", | ||
| "module": "./dist/node-fetch-server.js", | ||
| "main": "./dist/node-fetch-server.cjs", | ||
| "exports": { | ||
| ".": { | ||
| "module-sync": { | ||
| "types": "./dist/node-fetch-server.d.ts", | ||
| "default": "./dist/node-fetch-server.js" | ||
| }, | ||
| "import": { | ||
| "types": "./dist/node-fetch-server.d.ts", | ||
| "default": "./dist/node-fetch-server.js" | ||
| }, | ||
| "require": { | ||
| "types": "./dist/node-fetch-server.d.cts", | ||
| "default": "./dist/node-fetch-server.cjs" | ||
| }, | ||
| "default": { | ||
| "types": "./dist/node-fetch-server.d.ts", | ||
| "default": "./dist/node-fetch-server.js" | ||
| } | ||
| "types": "./dist/node-fetch-server.d.ts", | ||
| "import": "./dist/node-fetch-server.js", | ||
| "require": "./dist/node-fetch-server.cjs", | ||
| "default": "./dist/node-fetch-server.js" | ||
| }, | ||
@@ -45,3 +35,3 @@ "./package.json": "./package.json" | ||
| "@types/node": "^22.5.0", | ||
| "tsup": "^8.3.5" | ||
| "esbuild": "^0.20.2" | ||
| }, | ||
@@ -58,5 +48,9 @@ "keywords": [ | ||
| "bench": "bash ./bench/runner.sh", | ||
| "build": "tsup", | ||
| "build:types": "tsc --project tsconfig.build.json", | ||
| "build:esm": "esbuild src/node-fetch-server.ts --bundle --outfile=dist/node-fetch-server.js --format=esm --platform=node --sourcemap", | ||
| "build:cjs": "esbuild src/node-fetch-server.ts --bundle --outfile=dist/node-fetch-server.cjs --format=cjs --platform=node --sourcemap", | ||
| "build": "pnpm run build:types && pnpm run build:esm && pnpm run build:cjs", | ||
| "clean": "rm -rf dist", | ||
| "test": "node --experimental-strip-types --disable-warning=ExperimentalWarning --test ./src/**/*.test.ts" | ||
| } | ||
| } |
| import * as http from 'node:http'; | ||
| import * as http2 from 'node:http2'; | ||
| interface ClientAddress { | ||
| /** | ||
| * The IP address of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteaddress) | ||
| */ | ||
| address: string; | ||
| /** | ||
| * The family of the client IP address. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremotefamily) | ||
| */ | ||
| family: 'IPv4' | 'IPv6'; | ||
| /** | ||
| * The remote port of the client that sent the request. | ||
| * | ||
| * [Node.js Reference](https://nodejs.org/api/net.html#socketremoteport) | ||
| */ | ||
| port: number; | ||
| } | ||
| /** | ||
| * A function that handles an error that occurred during request handling. May return a response to | ||
| * send to the client, or `undefined` to allow the server to send a default error response. | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| interface ErrorHandler { | ||
| (error: unknown): void | Response | Promise<void | Response>; | ||
| } | ||
| /** | ||
| * A function that handles an incoming request and returns a response. | ||
| * | ||
| * [MDN `Request` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Request) | ||
| * | ||
| * [MDN `Response` Reference](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
| */ | ||
| interface FetchHandler { | ||
| (request: Request, client: ClientAddress): Response | Promise<Response>; | ||
| } | ||
| interface RequestListenerOptions { | ||
| /** | ||
| * Overrides the host portion of the incoming request URL. By default the request URL host is | ||
| * derived from the HTTP `Host` header. | ||
| * | ||
| * For example, if you have a `$HOST` environment variable that contains the hostname of your | ||
| * server, you can use it to set the host of all incoming request URLs like so: | ||
| * | ||
| * ```ts | ||
| * createRequestListener(handler, { host: process.env.HOST }) | ||
| * ``` | ||
| */ | ||
| host?: string; | ||
| /** | ||
| * An error handler that determines the response when the request handler throws an error. By | ||
| * default a 500 Internal Server Error response will be sent. | ||
| */ | ||
| onError?: ErrorHandler; | ||
| /** | ||
| * Overrides the protocol of the incoming request URL. By default the request URL protocol is | ||
| * derived from the connection protocol. So e.g. when serving over HTTPS (using | ||
| * `https.createServer()`), the request URL will begin with `https:`. | ||
| */ | ||
| protocol?: string; | ||
| } | ||
| /** | ||
| * Wraps a fetch handler in a Node.js request listener that can be used with: | ||
| * | ||
| * - [`http.createServer()`](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener) | ||
| * - [`https.createServer()`](https://nodejs.org/api/https.html#httpscreateserveroptions-requestlistener) | ||
| * - [`http2.createServer()`](https://nodejs.org/api/http2.html#http2createserveroptions-onrequesthandler) | ||
| * - [`http2.createSecureServer()`](https://nodejs.org/api/http2.html#http2createsecureserveroptions-onrequesthandler) | ||
| * | ||
| * Example: | ||
| * | ||
| * ```ts | ||
| * import * as http from 'node:http'; | ||
| * import { createRequestListener } from '@mjackson/node-fetch-server'; | ||
| * | ||
| * async function handler(request) { | ||
| * return new Response('Hello, world!'); | ||
| * } | ||
| * | ||
| * let server = http.createServer( | ||
| * createRequestListener(handler) | ||
| * ); | ||
| * | ||
| * server.listen(3000); | ||
| * ``` | ||
| * | ||
| * @param handler The fetch handler to use for processing incoming requests. | ||
| * @param options Request listener options. | ||
| * @returns A Node.js request listener function. | ||
| */ | ||
| declare function createRequestListener(handler: FetchHandler, options?: RequestListenerOptions): http.RequestListener; | ||
| type RequestOptions = Omit<RequestListenerOptions, 'onError'>; | ||
| /** | ||
| * Creates a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) object from | ||
| * | ||
| * - a [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse) pair | ||
| * - a [`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) pair | ||
| * | ||
| * @param req The incoming request object. | ||
| * @param res The server response object. | ||
| * @param options | ||
| * @returns A request object. | ||
| */ | ||
| declare function createRequest(req: http.IncomingMessage | http2.Http2ServerRequest, res: http.ServerResponse | http2.Http2ServerResponse, options?: RequestOptions): Request; | ||
| /** | ||
| * Creates a [`Headers`](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object from the headers in a Node.js | ||
| * [`http.IncomingMessage`](https://nodejs.org/api/http.html#class-httpincomingmessage)/[`http2.Http2ServerRequest`](https://nodejs.org/api/http2.html#class-http2http2serverrequest). | ||
| * | ||
| * @param req The incoming request object. | ||
| * @returns A headers object. | ||
| */ | ||
| declare function createHeaders(req: http.IncomingMessage | http2.Http2ServerRequest): Headers; | ||
| /** | ||
| * Sends a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to the client using a Node.js | ||
| * [`http.ServerResponse`](https://nodejs.org/api/http.html#class-httpserverresponse)/[`http2.Http2ServerResponse`](https://nodejs.org/api/http2.html#class-http2http2serverresponse) | ||
| * object. | ||
| * | ||
| * @param res The server response object. | ||
| * @param response The response to send. | ||
| */ | ||
| declare function sendResponse(res: http.ServerResponse | http2.Http2ServerResponse, response: Response): Promise<void>; | ||
| export { type ClientAddress, type ErrorHandler, type FetchHandler, type RequestListenerOptions, type RequestOptions, createHeaders, createRequest, createRequestListener, sendResponse }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
63728
130.46%20
185.71%896
112.83%3
50%18
157.14%