@znight/http 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/adapter-6qC95ViO.d.mts +2163 -0
- package/dist/compiler.d.mts +1 -1
- package/dist/compiler.mjs +1 -1
- package/dist/express.d.mts +1 -1
- package/dist/express.mjs +4 -1
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +1 -1
- package/dist/fastify.mjs +4 -1
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +1 -1
- package/dist/hono.mjs +38 -3
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +3 -1117
- package/dist/index.mjs +263 -269
- package/dist/index.mjs.map +1 -1
- package/dist/{node-DnYv_AMI.mjs → node-npGnu7IW.mjs} +5 -2
- package/dist/node-npGnu7IW.mjs.map +1 -0
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{node-io-BkdRSQeg.mjs → upgrade-io-dTMDDmBi.mjs} +625 -27
- package/dist/upgrade-io-dTMDDmBi.mjs.map +1 -0
- package/package.json +4 -4
- package/src/adapter.ts +7 -0
- package/src/adapters/express.ts +4 -0
- package/src/adapters/fastify.ts +4 -0
- package/src/adapters/hono.ts +48 -2
- package/src/adapters/node-io.ts +8 -0
- package/src/adapters/node.ts +4 -0
- package/src/adapters/upgrade-io.ts +255 -0
- package/src/index.ts +14 -0
- package/src/pipeline.ts +108 -2
- package/src/plugin-routes.ts +6 -0
- package/src/plugin.ts +197 -16
- package/src/upgrade.ts +341 -0
- package/dist/adapter-DmhQGqAR.d.mts +0 -895
- package/dist/node-DnYv_AMI.mjs.map +0 -1
- package/dist/node-io-BkdRSQeg.mjs.map +0 -1
package/dist/compiler.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { cn as HttpParamSource, on as HttpMethod } from "./adapter-6qC95ViO.mjs";
|
|
2
2
|
import { AnalyzeContext, ClassInfo, ClassRef, CompilerExtension, Location, MethodInfo, StaticValue, ValidateContext, ValueRef } from "@znight/compiler";
|
|
3
3
|
/** Primitive coercions (`coerce.<kind>`). */
|
|
4
4
|
type ScalarCoercion = "string" | "number" | "integer" | "boolean" | "bigint" | "date";
|
package/dist/compiler.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as isMediaTypePattern, n as parseByteSize } from "./size-D8rsQcV0.mjs";
|
|
2
1
|
import { checkRoutePath, compareStrings, joinPaths, normalizePath, parseRoutePath } from "./path.mjs";
|
|
2
|
+
import { a as isMediaTypePattern, n as parseByteSize } from "./size-D8rsQcV0.mjs";
|
|
3
3
|
// src/compiler/ast.ts
|
|
4
4
|
/** `true` for an object shaped like an AST node. */
|
|
5
5
|
function isNode(value) {
|
package/dist/express.d.mts
CHANGED
package/dist/express.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { H as closeServer, Q as bufferInjectStream, V as ConnectionTracker, W as nodeAdapterRequest, X as writeNodeFailure, Z as writeNodeReply, lt as serverAddress, r as NodeUpgrades, tt as injectNodeStream } from "./upgrade-io-dTMDDmBi.mjs";
|
|
2
2
|
import { toRegExpSource } from "./path.mjs";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import express from "express";
|
|
@@ -19,6 +19,8 @@ function expressAdapter(options = {}) {
|
|
|
19
19
|
}
|
|
20
20
|
var ExpressHttpAdapter = class {
|
|
21
21
|
name = "express";
|
|
22
|
+
/** WebSocket upgrades (RFC 0003), routed by the server of {@link listen}. */
|
|
23
|
+
upgrades = new NodeUpgrades();
|
|
22
24
|
app;
|
|
23
25
|
router;
|
|
24
26
|
#fallback;
|
|
@@ -73,6 +75,7 @@ var ExpressHttpAdapter = class {
|
|
|
73
75
|
this.#mount();
|
|
74
76
|
const server = this.#givenServer ?? createServer();
|
|
75
77
|
if (!server.listeners("request").includes(this.#listener)) server.on("request", this.#listener);
|
|
78
|
+
this.upgrades.attach(server);
|
|
76
79
|
this.#server = server;
|
|
77
80
|
this.#tracker ??= new ConnectionTracker(server, { requests: false });
|
|
78
81
|
this.#closing = void 0;
|
package/dist/express.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"express.mjs","names":[],"sources":["../src/adapters/express.ts"],"sourcesContent":["/**\n * `@znight/http/express`: serves Znight routes with [Express](https://expressjs.com) 5 (optional\n * peer dependency `express`). Routes are mounted on a dedicated router, after the middleware of the\n * application you pass. `inject()` uses the optional peer `light-my-request`.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\n\nimport { createServer, type Server } from 'node:http'\nimport express, { type Express, type NextFunction, type Request, type Response, type Router } from 'express'\n\nimport { toRegExpSource } from '../path.ts'\nimport { ConnectionTracker, closeServer, nodeAdapterRequest, writeNodeFailure, writeNodeReply } from './node-io.ts'\nimport { bufferInjectStream, injectNodeStream, serverAddress } from './shared.ts'\n\n/** Options of {@link expressAdapter}. */\nexport interface ExpressAdapterOptions {\n /**\n * An existing application: its middleware (CORS, helmet, sessions...) runs before the Znight\n * routes. Do not add body parsers for Znight routes: Znight reads bodies itself.\n */\n readonly app?: Express\n /**\n * The server to listen with (e.g. `https.createServer({ key, cert })`): the adapter adds its\n * `request` listener. Default: a new `http.createServer()`.\n */\n readonly server?: Server\n}\n\n/** The Express adapter. */\nexport interface ExpressAdapter extends HttpAdapter {\n readonly name: 'express'\n /** The Express application. */\n readonly app: Express\n /** The router holding the Znight routes (mounted on {@link app}). */\n readonly router: Router\n /** The HTTP server, once listening. */\n readonly server: Server | undefined\n}\n\n/**\n * Creates an Express adapter.\n *\n * ```ts\n * import { expressAdapter } from '@znight/http/express'\n *\n * const app = express()\n * app.use(cors())\n * HttpPlugin.configure({ adapter: expressAdapter({ app }) })\n * ```\n */\nexport function expressAdapter(options: ExpressAdapterOptions = {}): ExpressAdapter {\n return new ExpressHttpAdapter(options)\n}\n\ntype Method = 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete' | 'options'\n\nclass ExpressHttpAdapter implements ExpressAdapter {\n readonly name = 'express' as const\n readonly app: Express\n readonly router: Router\n #fallback: FallbackHandler | undefined\n #mounted = false\n #server: Server | undefined\n #tracker: ConnectionTracker | undefined\n #closing: Promise<void> | undefined\n\n readonly #givenServer: Server | undefined\n\n constructor(options: ExpressAdapterOptions) {\n this.#givenServer = options.server\n if (options.app === undefined) {\n this.app = express()\n this.app.disable('x-powered-by')\n } else {\n this.app = options.app\n }\n this.router = express.Router({ caseSensitive: true, strict: true })\n }\n\n get server(): Server | undefined {\n return this.#server\n }\n\n registerRoute(route: AdapterRoute): void {\n const handler = route.handler\n // anchored regular expressions without capture groups: Express decodes nothing, Znight extracts parameters\n this.router[route.method.toLowerCase() as Method](new RegExp(toRegExpSource(route.path)), (request, response) => {\n handler(adapterRequest(request, response)).then(\n reply => writeNodeReply(response, reply),\n () => writeNodeFailure(response),\n )\n })\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #mount(): void {\n if (this.#mounted) return\n this.#mounted = true\n // Express answers unmatched OPTIONS requests itself (200): route them to the Znight fallback\n this.router.options(/^.*$/, (request: Request, response: Response) =>\n this.#dispatchFallback(request, response, undefined),\n )\n this.app.use(this.router)\n this.app.use((request: Request, response: Response) => this.#dispatchFallback(request, response, undefined))\n this.app.use((error: unknown, request: Request, response: Response, _next: NextFunction) =>\n this.#dispatchFallback(request, response, error),\n )\n }\n\n #dispatchFallback(request: Request, response: Response, error: unknown): void {\n if (this.#fallback === undefined) {\n writeNodeFailure(response)\n return\n }\n this.#fallback(adapterRequest(request, response), error).then(\n reply => writeNodeReply(response, reply),\n () => writeNodeFailure(response),\n )\n }\n\n readonly #listener = (request: IncomingMessage, response: ServerResponse): void => {\n // the listening server's connections (an injected request is ignored)\n this.#tracker?.track(request, response)\n this.app(request as Request, response as Response)\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n this.#mount()\n const server = this.#givenServer ?? createServer()\n if (!server.listeners('request').includes(this.#listener)) server.on('request', this.#listener)\n this.#server = server\n // `#listener` counts the requests\n this.#tracker ??= new ConnectionTracker(server, { requests: false })\n this.#closing = undefined\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error): void => reject(error)\n server.once('error', onError)\n server.listen(port, host, () => {\n server.off('error', onError)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n const server = this.#server\n const tracker = this.#tracker\n if (server === undefined || tracker === undefined) return Promise.resolve()\n this.#closing = closeServer(server, tracker, this.#closing, options.force === true)\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n this.#mount()\n return injectNodeStream('express', this.#injectListener as (req: never, res: never) => void, request)\n }\n\n /**\n * Express replaces the prototype of `req`/`res` with its own, which drops the overrides of the\n * light-my-request fakes (payload capture, body streaming): pin them as own properties first.\n */\n readonly #injectListener = (request: IncomingMessage, response: ServerResponse): void => {\n pinPrototype(request)\n pinPrototype(response)\n this.#listener(request, response)\n }\n}\n\nfunction pinPrototype(target: object): void {\n const prototype: unknown = Object.getPrototypeOf(target)\n if (prototype === null || typeof prototype !== 'object') return\n for (const key of Object.getOwnPropertyNames(prototype)) {\n if (key === 'constructor' || Object.hasOwn(target, key)) continue\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key)\n if (typeof descriptor?.value === 'function') {\n Object.defineProperty(target, key, { value: descriptor.value, writable: true, configurable: true })\n }\n }\n}\n\nfunction adapterRequest(request: Request, response: Response): ReturnType<typeof nodeAdapterRequest> {\n return nodeAdapterRequest(request, response, { url: request.originalUrl ?? request.url, ip: request.ip })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"express.mjs","names":[],"sources":["../src/adapters/express.ts"],"sourcesContent":["/**\n * `@znight/http/express`: serves Znight routes with [Express](https://expressjs.com) 5 (optional\n * peer dependency `express`). Routes are mounted on a dedicated router, after the middleware of the\n * application you pass. `inject()` uses the optional peer `light-my-request`.\n */\nimport type { IncomingMessage, ServerResponse } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\n\nimport { createServer, type Server } from 'node:http'\nimport express, { type Express, type NextFunction, type Request, type Response, type Router } from 'express'\n\nimport { toRegExpSource } from '../path.ts'\nimport { ConnectionTracker, closeServer, nodeAdapterRequest, writeNodeFailure, writeNodeReply } from './node-io.ts'\nimport { bufferInjectStream, injectNodeStream, serverAddress } from './shared.ts'\nimport { NodeUpgrades } from './upgrade-io.ts'\n\n/** Options of {@link expressAdapter}. */\nexport interface ExpressAdapterOptions {\n /**\n * An existing application: its middleware (CORS, helmet, sessions...) runs before the Znight\n * routes. Do not add body parsers for Znight routes: Znight reads bodies itself.\n */\n readonly app?: Express\n /**\n * The server to listen with (e.g. `https.createServer({ key, cert })`): the adapter adds its\n * `request` listener. Default: a new `http.createServer()`.\n */\n readonly server?: Server\n}\n\n/** The Express adapter. */\nexport interface ExpressAdapter extends HttpAdapter {\n readonly name: 'express'\n /** The Express application. */\n readonly app: Express\n /** The router holding the Znight routes (mounted on {@link app}). */\n readonly router: Router\n /** The HTTP server, once listening. */\n readonly server: Server | undefined\n}\n\n/**\n * Creates an Express adapter.\n *\n * ```ts\n * import { expressAdapter } from '@znight/http/express'\n *\n * const app = express()\n * app.use(cors())\n * HttpPlugin.configure({ adapter: expressAdapter({ app }) })\n * ```\n */\nexport function expressAdapter(options: ExpressAdapterOptions = {}): ExpressAdapter {\n return new ExpressHttpAdapter(options)\n}\n\ntype Method = 'get' | 'head' | 'post' | 'put' | 'patch' | 'delete' | 'options'\n\nclass ExpressHttpAdapter implements ExpressAdapter {\n readonly name = 'express' as const\n /** WebSocket upgrades (RFC 0003), routed by the server of {@link listen}. */\n readonly upgrades: NodeUpgrades = new NodeUpgrades()\n readonly app: Express\n readonly router: Router\n #fallback: FallbackHandler | undefined\n #mounted = false\n #server: Server | undefined\n #tracker: ConnectionTracker | undefined\n #closing: Promise<void> | undefined\n\n readonly #givenServer: Server | undefined\n\n constructor(options: ExpressAdapterOptions) {\n this.#givenServer = options.server\n if (options.app === undefined) {\n this.app = express()\n this.app.disable('x-powered-by')\n } else {\n this.app = options.app\n }\n this.router = express.Router({ caseSensitive: true, strict: true })\n }\n\n get server(): Server | undefined {\n return this.#server\n }\n\n registerRoute(route: AdapterRoute): void {\n const handler = route.handler\n // anchored regular expressions without capture groups: Express decodes nothing, Znight extracts parameters\n this.router[route.method.toLowerCase() as Method](new RegExp(toRegExpSource(route.path)), (request, response) => {\n handler(adapterRequest(request, response)).then(\n reply => writeNodeReply(response, reply),\n () => writeNodeFailure(response),\n )\n })\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #mount(): void {\n if (this.#mounted) return\n this.#mounted = true\n // Express answers unmatched OPTIONS requests itself (200): route them to the Znight fallback\n this.router.options(/^.*$/, (request: Request, response: Response) =>\n this.#dispatchFallback(request, response, undefined),\n )\n this.app.use(this.router)\n this.app.use((request: Request, response: Response) => this.#dispatchFallback(request, response, undefined))\n this.app.use((error: unknown, request: Request, response: Response, _next: NextFunction) =>\n this.#dispatchFallback(request, response, error),\n )\n }\n\n #dispatchFallback(request: Request, response: Response, error: unknown): void {\n if (this.#fallback === undefined) {\n writeNodeFailure(response)\n return\n }\n this.#fallback(adapterRequest(request, response), error).then(\n reply => writeNodeReply(response, reply),\n () => writeNodeFailure(response),\n )\n }\n\n readonly #listener = (request: IncomingMessage, response: ServerResponse): void => {\n // the listening server's connections (an injected request is ignored)\n this.#tracker?.track(request, response)\n this.app(request as Request, response as Response)\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n this.#mount()\n const server = this.#givenServer ?? createServer()\n if (!server.listeners('request').includes(this.#listener)) server.on('request', this.#listener)\n this.upgrades.attach(server)\n this.#server = server\n // `#listener` counts the requests\n this.#tracker ??= new ConnectionTracker(server, { requests: false })\n this.#closing = undefined\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error): void => reject(error)\n server.once('error', onError)\n server.listen(port, host, () => {\n server.off('error', onError)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n const server = this.#server\n const tracker = this.#tracker\n if (server === undefined || tracker === undefined) return Promise.resolve()\n this.#closing = closeServer(server, tracker, this.#closing, options.force === true)\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n this.#mount()\n return injectNodeStream('express', this.#injectListener as (req: never, res: never) => void, request)\n }\n\n /**\n * Express replaces the prototype of `req`/`res` with its own, which drops the overrides of the\n * light-my-request fakes (payload capture, body streaming): pin them as own properties first.\n */\n readonly #injectListener = (request: IncomingMessage, response: ServerResponse): void => {\n pinPrototype(request)\n pinPrototype(response)\n this.#listener(request, response)\n }\n}\n\nfunction pinPrototype(target: object): void {\n const prototype: unknown = Object.getPrototypeOf(target)\n if (prototype === null || typeof prototype !== 'object') return\n for (const key of Object.getOwnPropertyNames(prototype)) {\n if (key === 'constructor' || Object.hasOwn(target, key)) continue\n const descriptor = Object.getOwnPropertyDescriptor(prototype, key)\n if (typeof descriptor?.value === 'function') {\n Object.defineProperty(target, key, { value: descriptor.value, writable: true, configurable: true })\n }\n }\n}\n\nfunction adapterRequest(request: Request, response: Response): ReturnType<typeof nodeAdapterRequest> {\n return nodeAdapterRequest(request, response, { url: request.originalUrl ?? request.url, ip: request.ip })\n}\n"],"mappings":"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"}
|
package/dist/fastify.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as HttpAdapter } from "./adapter-
|
|
1
|
+
import { s as HttpAdapter } from "./adapter-6qC95ViO.mjs";
|
|
2
2
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
3
3
|
/** Options of {@link fastifyAdapter}. */
|
|
4
4
|
export interface FastifyAdapterOptions {
|
package/dist/fastify.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Ct as discardStream, G as nodeBodyStream, J as readNodeBody, K as normalizeNodeHeaders, Q as bufferInjectStream, U as isEncrypted, V as ConnectionTracker, Y as withNativeCookies, Z as writeNodeReply, at as lightStreamResponse, it as lightInjectOptions, lt as serverAddress, q as onResponseAbort, r as NodeUpgrades } from "./upgrade-io-dTMDDmBi.mjs";
|
|
2
2
|
import { toFastifyPath } from "./path.mjs";
|
|
3
3
|
import { Readable } from "node:stream";
|
|
4
4
|
import { tracingChannel } from "node:diagnostics_channel";
|
|
@@ -35,6 +35,8 @@ const HANDLER_HOOKS = [
|
|
|
35
35
|
];
|
|
36
36
|
var FastifyHttpAdapter = class {
|
|
37
37
|
name = "fastify";
|
|
38
|
+
/** WebSocket upgrades (RFC 0003), routed by `instance.server`. */
|
|
39
|
+
upgrades = new NodeUpgrades();
|
|
38
40
|
instance;
|
|
39
41
|
#routes = [];
|
|
40
42
|
#tracker;
|
|
@@ -260,6 +262,7 @@ var FastifyHttpAdapter = class {
|
|
|
260
262
|
async listen(port, host) {
|
|
261
263
|
await this.#prepare();
|
|
262
264
|
this.#closing = void 0;
|
|
265
|
+
this.upgrades.attach(this.instance.server);
|
|
263
266
|
await this.instance.listen({
|
|
264
267
|
port,
|
|
265
268
|
host
|
package/dist/fastify.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fastify.mjs","names":[],"sources":["../src/adapters/fastify.ts"],"sourcesContent":["/**\n * `@znight/http/fastify`: serves Znight routes with [Fastify](https://fastify.dev) 5 (optional\n * peer dependency `fastify`). Routing uses Fastify's router; body parsing, validation and\n * serialisation stay Znight's, so responses are identical to the other adapters.\n */\n\nimport type { OutgoingHttpHeaders } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterHandler,\n AdapterReply,\n AdapterRequest,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\nimport type { HeaderValue } from '../exceptions.ts'\n\nimport { tracingChannel } from 'node:diagnostics_channel'\nimport { Readable } from 'node:stream'\nimport Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n type FastifyServerOptions,\n type HookHandlerDoneFunction,\n type RouteOptions,\n} from 'fastify'\n\nimport { toFastifyPath } from '../path.ts'\nimport { discardStream } from '../stream.ts'\nimport {\n ConnectionTracker,\n isEncrypted,\n nodeBodyStream,\n normalizeNodeHeaders,\n onResponseAbort,\n readNodeBody,\n withNativeCookies,\n writeNodeReply,\n} from './node-io.ts'\nimport {\n bufferInjectStream,\n type LightInjectOptions,\n lightInjectOptions,\n lightStreamResponse,\n serverAddress,\n} from './shared.ts'\n\n/** Options of {@link fastifyAdapter}. */\nexport interface FastifyAdapterOptions {\n /**\n * An existing instance (with your plugins and hooks). Znight registers its routes in an\n * encapsulated child context (its own body handling and 404 handler) and closes the instance on\n * shutdown. Malformed URLs are then answered by Fastify's `frameworkErrors` handler.\n */\n readonly instance?: FastifyInstance\n /**\n * Options of the instance created when `instance` is absent. Znight sets `exposeHeadRoutes: false`\n * (HEAD is served by Znight), a `maxParamLength` of 8 KiB and a `frameworkErrors` handler.\n */\n readonly options?: FastifyServerOptions\n}\n\n/** The Fastify adapter. */\nexport interface FastifyAdapter extends HttpAdapter {\n readonly name: 'fastify'\n /** The Fastify instance. */\n readonly instance: FastifyInstance\n}\n\n/**\n * Creates a Fastify adapter.\n *\n * ```ts\n * import { fastifyAdapter } from '@znight/http/fastify'\n *\n * HttpPlugin.configure({ adapter: fastifyAdapter({ options: { trustProxy: true } }) })\n * ```\n */\nexport function fastifyAdapter(options: FastifyAdapterOptions = {}): FastifyAdapter {\n return new FastifyHttpAdapter(options)\n}\n\nconst MAX_PARAM_LENGTH = 8192\n\n/** `content-type` headers hidden from Fastify (see `#register`), by request. */\nconst HIDDEN_CONTENT_TYPES = new WeakMap<object, string>()\n\n/**\n * Fastify's tracing channel of route handlers: while it has subscribers (an APM), every request runs\n * Fastify's whole lifecycle, so that the handler is traced.\n */\nconst HANDLER_CHANNEL = tracingChannel('fastify.request.handler')\n\n/** The `config` key marking a Znight route that may be served from the `onRequest` hook (see `#onRoute`). */\nconst DIRECT = Symbol('znight.direct')\n\n/** The hooks that run between `onRequest` and the route handler. */\nconst HANDLER_HOOKS = ['onRequest', 'preParsing', 'preValidation', 'preHandler'] as const\n\n/** What `#onRoute` records about a Znight route (in its `config`). */\ninterface DirectRoute {\n readonly handler: AdapterHandler\n /** No route-level `onSend` or `onResponse` hook. */\n readonly bare: boolean\n}\n\n/**\n * How the requests of a Fastify route context are served, decided on its first request (see\n * `#lane`): `handler` from the `onRequest` hook, and `raw` when a text reply may be written on the\n * raw response.\n */\ninterface Lane {\n readonly handler: AdapterHandler\n readonly raw: boolean\n}\n\n/**\n * The fields of Fastify's route context (`request[Symbol('fastify.context')]`) read by the adapter.\n * They are internal: the adapter only skips Fastify's lifecycle when they have the expected shape.\n */\ninterface RouteContext {\n readonly config?: Readonly<Record<PropertyKey, unknown>>\n readonly preParsing?: unknown\n readonly preValidation?: unknown\n readonly preHandler?: unknown\n readonly onSend?: unknown\n readonly onResponse?: unknown\n}\n\n/** The keys of Fastify's internal request and reply state read by the adapter (`null`: not found). */\ninterface InternalKeys {\n /** The route context of a request. */\n readonly context: symbol | null\n /** The headers set with `reply.header()`. */\n readonly headers: symbol | null\n /** The trailers set with `reply.trailer()`. */\n readonly trailers: symbol | null\n /** The serializer set with `reply.serializer()` (`null` when none). */\n readonly serializer: symbol | null\n}\n\nclass FastifyHttpAdapter implements FastifyAdapter {\n readonly name = 'fastify' as const\n readonly instance: FastifyInstance\n readonly #routes: AdapterRoute[] = []\n readonly #tracker: ConnectionTracker\n /** The Fastify handler of each Znight route (before Fastify binds it). */\n readonly #handlers = new Map<unknown, AdapterHandler>()\n /** By Fastify route context: how its requests are served (`null`: through Fastify's lifecycle). */\n readonly #lanes = new WeakMap<object, Lane | null>()\n #keys: InternalKeys | undefined\n #fallback: FallbackHandler | undefined\n #ready: Promise<void> | undefined\n #closing: Promise<void> | undefined\n\n constructor(options: FastifyAdapterOptions) {\n if (options.instance === undefined) {\n const serverOptions = options.options ?? {}\n this.instance = Fastify({\n exposeHeadRoutes: false,\n frameworkErrors: (error, request, reply) => {\n this.#reply(reply, this.#dispatchFallback(request, reply, error)).catch(() => undefined)\n },\n ...serverOptions,\n routerOptions: { maxParamLength: MAX_PARAM_LENGTH, ...serverOptions.routerOptions },\n })\n } else {\n this.instance = options.instance\n }\n this.#tracker = new ConnectionTracker(this.instance.server)\n }\n\n registerRoute(route: AdapterRoute): void {\n if (this.#ready !== undefined) throw new Error('fastifyAdapter: routes must be registered before listen()/inject()')\n this.#routes.push(route)\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #prepare(): Promise<void> {\n this.#ready ??= this.#register()\n return this.#ready\n }\n\n async #register(): Promise<void> {\n const routes = this.#routes\n await this.instance.register((child, _options, done) => {\n // Znight reads and parses bodies itself: accept every content type without consuming it. The\n // RegExp parser matches every content type like '*' does, but Fastify caches what a RegExp\n // matched (it parses the content type again on every request to find the '*' parser)\n child.removeAllContentTypeParsers()\n child.addContentTypeParser('*', ignoreBody)\n child.addContentTypeParser(/^/, ignoreBody)\n child.addHook('onRequest', this.#onRequest)\n // added last: sees the route options once every other `onRoute` hook changed them\n child.addHook('onRoute', this.#onRoute)\n for (const route of routes) {\n const handler = route.handler\n const fastifyHandler = (request: FastifyRequest, reply: FastifyReply): Promise<FastifyReply> =>\n this.#reply(reply, handler(adapterRequest(request, reply)))\n this.#handlers.set(fastifyHandler, handler)\n child.route({\n method: route.method,\n url: toFastifyPath(route.path),\n exposeHeadRoute: false,\n handler: fastifyHandler,\n })\n }\n child.setNotFoundHandler((request, reply) =>\n this.#reply(reply, this.#dispatchFallback(request, reply, undefined)),\n )\n child.setErrorHandler((error, request, reply) =>\n this.#reply(reply, this.#dispatchFallback(request, reply, error)),\n )\n done()\n })\n await this.instance.ready()\n }\n\n /**\n * Records in the `config` of a Znight route whether it may be served from the `onRequest` hook:\n * its handler is still the adapter's (no `onRoute` hook wrapped it) and no route-level hook or\n * schema runs between `onRequest` and the handler. The hooks of the instance are checked on the\n * first request (see `#lane`).\n */\n readonly #onRoute = (options: RouteOptions): void => {\n const handler = this.#handlers.get(options.handler)\n if (handler === undefined || options.schema !== undefined) return\n if (HANDLER_HOOKS.some(hook => hasHooks(options[hook]))) return\n const route: DirectRoute = { handler, bare: !hasHooks(options.onSend) && !hasHooks(options.onResponse) }\n options.config = { ...options.config, [DIRECT]: route }\n }\n\n /**\n * The `onRequest` hook of the Znight context. A route request that no later hook, schema or\n * tracing subscriber needs (see `#lane`) is served from here: Fastify skips its content type\n * parsing and the steps between this hook and the handler, which do nothing for Znight routes.\n * Otherwise the request goes on through Fastify's lifecycle.\n */\n readonly #onRequest = (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction): void => {\n const lane = this.#lane(request, reply)\n if (lane !== null && !HANDLER_CHANNEL.hasSubscribers) {\n this.#serve(lane, request, reply)\n return\n }\n // Fastify answers 415 to a content type it cannot parse (`foo`, an empty one) before the route\n // runs: hide such a header from Fastify (Znight reads it, and parses the body as bytes)\n const contentType = request.headers['content-type']\n if (contentType !== undefined && request.mediaType === undefined) {\n HIDDEN_CONTENT_TYPES.set(request.raw, contentType)\n delete request.raw.headers['content-type']\n }\n done()\n }\n\n /**\n * How the requests of the route of `request` are served, decided once per route context: from the\n * `onRequest` hook when `#onRoute` marked the route and the instance has no `preParsing`,\n * `preValidation` or `preHandler` hook (the `onRequest` hook of the Znight context is then the\n * last one); with a text reply written on the raw response when no `onSend` or `onResponse` hook\n * runs either (see `#writeRaw`). `null`: through Fastify's lifecycle (404s, hooks, a Fastify\n * version whose route context differs).\n */\n #lane(request: FastifyRequest, reply: FastifyReply): Lane | null {\n this.#keys ??= {\n context: ownSymbol(request, 'fastify.context'),\n headers: ownSymbol(reply, 'fastify.reply.headers'),\n trailers: ownSymbol(reply, 'fastify.reply.trailers'),\n serializer: ownSymbol(reply, 'fastify.reply.serializer'),\n }\n const key = this.#keys.context\n const context = key === null ? undefined : (request as unknown as Record<symbol, unknown>)[key]\n if (typeof context !== 'object' || context === null) return null\n let lane = this.#lanes.get(context)\n if (lane === undefined) {\n lane = laneOf(context as RouteContext, this.#keys)\n this.#lanes.set(context, lane)\n }\n return lane\n }\n\n /** Serves a route request from the `onRequest` hook (see `#lane`). */\n #serve(lane: Lane, request: FastifyRequest, reply: FastifyReply): void {\n let pending: Promise<AdapterReply>\n try {\n pending = lane.handler(new FastifyAdapterRequest(request, reply, reply))\n } catch (error) {\n // what Fastify does when a route handler throws\n reply.send(asError(error))\n return\n }\n pending.then(\n result => this.#settle(lane, reply, result),\n () => this.#settle(lane, reply, internalError()),\n )\n }\n\n /** Writes the reply of a request served from the `onRequest` hook; a failure is handled like Fastify does. */\n #settle(lane: Lane, reply: FastifyReply, result: AdapterReply): void {\n try {\n if (!lane.raw || !this.#writeRaw(reply, result)) this.#write(reply, result)\n } catch (error) {\n // what Fastify does when the promise of a route handler rejects\n if (reply.sent) reply.log.error({ err: error }, 'Promise errored, but reply.sent = true was set')\n else reply.send(asError(error))\n }\n }\n\n /**\n * Writes a text reply on the raw response, as `reply.send(text)` would without `onSend` hook: one\n * `writeHead()` with the same headers in the same order, then the text. Only when nothing set\n * reply headers or trailers before (an `onRequest` hook) and Fastify would send the headers and\n * text unchanged (content type, exact `content-length`). Returns whether it wrote the reply.\n */\n #writeRaw(reply: FastifyReply, result: AdapterReply): boolean {\n const text = result.text\n const body = result.body\n if (text === undefined || !(body instanceof Uint8Array) || result.raw === true) return false\n const headers = result.headers\n const length = headers['content-length']\n if (typeof length !== 'string' || Number(length) !== body.byteLength) return false\n // a content type Fastify cannot parse is replaced by `reply.send()` (text/plain)\n const type = headers['content-type']\n if (!sendsTextAsIs(type) || (type !== JSON_TYPE && !MEDIA_TYPE.test(type as string))) return false\n const response = reply.raw\n if (reply.sent || response.destroyed || response.headersSent || reply.request.raw.httpVersionMajor !== 1) {\n return false\n }\n const keys = this.#keys as InternalKeys\n const state = reply as unknown as Record<symbol, unknown>\n if (keys.headers === null || keys.trailers === null || state[keys.trailers] !== null) return false\n // a serializer set by a hook transforms text sent with `reply.send()`\n if (keys.serializer === null || state[keys.serializer] !== null) return false\n if (!isEmptyRecord(state[keys.headers])) return false\n response.writeHead(result.status, headers as OutgoingHttpHeaders)\n response.end(text)\n return true\n }\n\n #dispatchFallback(request: FastifyRequest, reply: FastifyReply, error: unknown): Promise<AdapterReply> {\n if (this.#fallback === undefined) return Promise.reject(error ?? new Error('No fallback handler'))\n return this.#fallback(adapterRequest(request, undefined, reply), error)\n }\n\n async #reply(reply: FastifyReply, pending: ReturnType<AdapterHandler>): Promise<FastifyReply> {\n let result: AdapterReply\n try {\n result = await pending\n } catch {\n result = internalError()\n }\n this.#write(reply, result)\n return reply\n }\n\n /** Writes a reply through Fastify (`reply.send()`: `onSend` hooks...). */\n #write(reply: FastifyReply, result: AdapterReply): void {\n if (result.raw === true || reply.sent || reply.raw.destroyed) {\n discardStream(result.body instanceof ReadableStream ? result.body : null)\n return\n }\n if (result.eventStream === true) {\n // no onSend hook (compression, ETag...) may rewrite an event stream: write it on the raw\n // response, with the headers Fastify hooks already set (`onRequest`...) under the plugin's,\n // and their cookies before the plugin's (as reply.header('set-cookie') does on the other path)\n const native = lowerCaseHeaders(reply.getHeaders())\n const headers = { ...native, ...withNativeCookies(native['set-cookie'], result.headers) }\n reply.hijack()\n writeNodeReply(reply.raw, { ...result, headers })\n return\n }\n reply.code(result.status)\n const headers = result.headers\n for (const name of Object.keys(headers)) reply.header(name, headers[name])\n const body = result.body\n if (body === null) {\n reply.send()\n return\n }\n if (body instanceof Uint8Array) {\n // a string is written with the headers at once (bytes take a second write)\n if (result.text !== undefined && sendsTextAsIs(headers['content-type'])) reply.send(result.text)\n else reply.send(Buffer.isBuffer(body) ? body : Buffer.from(body.buffer, body.byteOffset, body.byteLength))\n return\n }\n const stream = body instanceof Blob ? body.stream() : body\n reply.send(Readable.fromWeb(stream as import('node:stream/web').ReadableStream))\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n await this.#prepare()\n this.#closing = undefined\n await this.instance.listen({ port, host })\n const address = this.instance.server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n if (this.#closing === undefined) {\n this.#tracker.closeIdle()\n this.#closing = this.instance.close()\n }\n if (options.force === true) {\n this.instance.server.closeAllConnections?.()\n this.#tracker.destroyAll()\n }\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n async injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n await this.#prepare()\n const options: LightInjectOptions = await lightInjectOptions(request)\n // Fastify forwards the options to light-my-request (`payloadAsStream`)\n const inject = this.instance.inject as unknown as (options: LightInjectOptions) => Promise<unknown>\n const response = await inject.call(this.instance, options)\n return lightStreamResponse(response as Parameters<typeof lightStreamResponse>[0], request.signal)\n }\n}\n\n/**\n * The lane of a route context (see `FastifyHttpAdapter.#lane`). Once the instance is ready, every\n * lifecycle hook list of a context is `null` or a non-empty array.\n */\nfunction laneOf(context: RouteContext, keys: InternalKeys): Lane | null {\n const route = context.config?.[DIRECT] as DirectRoute | undefined\n if (route === undefined) return null\n if (context.preParsing !== null || context.preValidation !== null || context.preHandler !== null) return null\n const raw = route.bare && context.onSend === null && context.onResponse === null\n return { handler: route.handler, raw: raw && keys.headers !== null && keys.trailers !== null }\n}\n\n/** The content type parser of the Znight context: the body is left unread (Znight reads it). */\nfunction ignoreBody(_request: FastifyRequest, _payload: unknown, done: (error: null, body: undefined) => void): void {\n done(null, undefined)\n}\n\n/** Whether a route option holds hooks (a function or a non-empty array). */\nfunction hasHooks(value: unknown): boolean {\n return value !== undefined && value !== null && !(Array.isArray(value) && value.length === 0)\n}\n\n/** An own symbol-keyed property of a Fastify object, by the symbol's description (internal state). */\nfunction ownSymbol(target: object, description: string): symbol | null {\n for (const symbol of Object.getOwnPropertySymbols(target)) if (symbol.description === description) return symbol\n return null\n}\n\nfunction isEmptyRecord(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false\n for (const _key in value) return false\n return true\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error), { cause: error })\n}\n\n/** The last-resort 500 of a handler promise that rejected (the pipeline maps errors: should not happen). */\nfunction internalError(): AdapterReply {\n return {\n status: 500,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n body: new TextEncoder().encode('{\"statusCode\":500,\"error\":\"Internal Server Error\"}'),\n }\n}\n\n/** The JSON content type of the plugin (with a charset). */\nconst JSON_TYPE = 'application/json; charset=utf-8'\n\n/**\n * Whether Fastify sends a string with this content type unchanged: it adds a charset to a JSON\n * type without one, and a content type to a string without one.\n */\n/** A `type/subtype` media type, optionally followed by parameters: what Fastify's `ContentType` accepts. */\nconst MEDIA_TYPE = /^\\s*[\\w!#$%&'*+.^`|~-]+\\/[\\w!#$%&'*+.^`|~-]+\\s*(?:;|$)/\n\nfunction sendsTextAsIs(type: HeaderValue | undefined): boolean {\n if (typeof type !== 'string') return false\n if (type === JSON_TYPE) return true\n const semicolon = type.indexOf(';')\n return !(semicolon === -1 ? type : type.slice(0, semicolon)).toLowerCase().includes('json')\n}\n\nfunction lowerCaseHeaders(\n headers: Readonly<Record<string, string | number | readonly string[] | undefined>>,\n): Record<string, string | string[]> {\n const result: Record<string, string | string[]> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (value !== undefined) result[name.toLowerCase()] = typeof value === 'object' ? [...value] : String(value)\n }\n return result\n}\n\nfunction adapterRequest(\n request: FastifyRequest,\n reply: FastifyReply | undefined,\n response: FastifyReply | undefined = reply,\n): AdapterRequest {\n const hidden = HIDDEN_CONTENT_TYPES.get(request.raw)\n if (hidden !== undefined) {\n // Fastify is done with the content type: restore it (for Znight and `@Req()`)\n HIDDEN_CONTENT_TYPES.delete(request.raw)\n request.raw.headers['content-type'] = hidden\n }\n return new FastifyAdapterRequest(request, reply, response)\n}\n\n/** A Fastify request as an {@link AdapterRequest} (a class: no closure per request). */\nclass FastifyAdapterRequest implements AdapterRequest {\n readonly method: string\n readonly url: string\n readonly headers: Readonly<Record<string, string | undefined>>\n readonly ip: string | undefined\n readonly remoteAddress: string | undefined\n readonly encrypted: boolean\n readonly raw: FastifyRequest\n /** `@Res()`: the reply of a route, `undefined` for the fallback handler. */\n readonly rawResponse: FastifyReply | undefined\n /** The reply whose disconnection aborts the request. */\n readonly #response: FastifyReply | undefined\n #body: Promise<Uint8Array> | undefined\n\n constructor(request: FastifyRequest, reply: FastifyReply | undefined, response: FastifyReply | undefined) {\n this.method = request.raw.method ?? request.method\n this.url = request.raw.url ?? request.url\n this.headers = normalizeNodeHeaders(request.headers)\n this.ip = request.ip\n this.remoteAddress = request.raw.socket?.remoteAddress\n this.encrypted = isEncrypted(request.raw)\n this.raw = request\n this.rawResponse = reply\n this.#response = response\n }\n\n readBody(limit: number): Promise<Uint8Array> {\n this.#body ??= readNodeBody(this.raw.raw, limit)\n return this.#body\n }\n\n streamBody(limit: number): ReadableStream<Uint8Array> {\n return nodeBodyStream(this.raw.raw, limit)\n }\n\n rawStatus(): number | undefined {\n return this.rawResponse?.statusCode\n }\n\n onAbort(listener: () => void): void {\n if (this.#response !== undefined) onResponseAbort(this.#response.raw, listener)\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fastify.mjs","names":[],"sources":["../src/adapters/fastify.ts"],"sourcesContent":["/**\n * `@znight/http/fastify`: serves Znight routes with [Fastify](https://fastify.dev) 5 (optional\n * peer dependency `fastify`). Routing uses Fastify's router; body parsing, validation and\n * serialisation stay Znight's, so responses are identical to the other adapters.\n */\n\nimport type { OutgoingHttpHeaders } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterHandler,\n AdapterReply,\n AdapterRequest,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\nimport type { HeaderValue } from '../exceptions.ts'\n\nimport { tracingChannel } from 'node:diagnostics_channel'\nimport { Readable } from 'node:stream'\nimport Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n type FastifyServerOptions,\n type HookHandlerDoneFunction,\n type RouteOptions,\n} from 'fastify'\n\nimport { toFastifyPath } from '../path.ts'\nimport { discardStream } from '../stream.ts'\nimport {\n ConnectionTracker,\n isEncrypted,\n nodeBodyStream,\n normalizeNodeHeaders,\n onResponseAbort,\n readNodeBody,\n withNativeCookies,\n writeNodeReply,\n} from './node-io.ts'\nimport {\n bufferInjectStream,\n type LightInjectOptions,\n lightInjectOptions,\n lightStreamResponse,\n serverAddress,\n} from './shared.ts'\nimport { NodeUpgrades } from './upgrade-io.ts'\n\n/** Options of {@link fastifyAdapter}. */\nexport interface FastifyAdapterOptions {\n /**\n * An existing instance (with your plugins and hooks). Znight registers its routes in an\n * encapsulated child context (its own body handling and 404 handler) and closes the instance on\n * shutdown. Malformed URLs are then answered by Fastify's `frameworkErrors` handler.\n */\n readonly instance?: FastifyInstance\n /**\n * Options of the instance created when `instance` is absent. Znight sets `exposeHeadRoutes: false`\n * (HEAD is served by Znight), a `maxParamLength` of 8 KiB and a `frameworkErrors` handler.\n */\n readonly options?: FastifyServerOptions\n}\n\n/** The Fastify adapter. */\nexport interface FastifyAdapter extends HttpAdapter {\n readonly name: 'fastify'\n /** The Fastify instance. */\n readonly instance: FastifyInstance\n}\n\n/**\n * Creates a Fastify adapter.\n *\n * ```ts\n * import { fastifyAdapter } from '@znight/http/fastify'\n *\n * HttpPlugin.configure({ adapter: fastifyAdapter({ options: { trustProxy: true } }) })\n * ```\n */\nexport function fastifyAdapter(options: FastifyAdapterOptions = {}): FastifyAdapter {\n return new FastifyHttpAdapter(options)\n}\n\nconst MAX_PARAM_LENGTH = 8192\n\n/** `content-type` headers hidden from Fastify (see `#register`), by request. */\nconst HIDDEN_CONTENT_TYPES = new WeakMap<object, string>()\n\n/**\n * Fastify's tracing channel of route handlers: while it has subscribers (an APM), every request runs\n * Fastify's whole lifecycle, so that the handler is traced.\n */\nconst HANDLER_CHANNEL = tracingChannel('fastify.request.handler')\n\n/** The `config` key marking a Znight route that may be served from the `onRequest` hook (see `#onRoute`). */\nconst DIRECT = Symbol('znight.direct')\n\n/** The hooks that run between `onRequest` and the route handler. */\nconst HANDLER_HOOKS = ['onRequest', 'preParsing', 'preValidation', 'preHandler'] as const\n\n/** What `#onRoute` records about a Znight route (in its `config`). */\ninterface DirectRoute {\n readonly handler: AdapterHandler\n /** No route-level `onSend` or `onResponse` hook. */\n readonly bare: boolean\n}\n\n/**\n * How the requests of a Fastify route context are served, decided on its first request (see\n * `#lane`): `handler` from the `onRequest` hook, and `raw` when a text reply may be written on the\n * raw response.\n */\ninterface Lane {\n readonly handler: AdapterHandler\n readonly raw: boolean\n}\n\n/**\n * The fields of Fastify's route context (`request[Symbol('fastify.context')]`) read by the adapter.\n * They are internal: the adapter only skips Fastify's lifecycle when they have the expected shape.\n */\ninterface RouteContext {\n readonly config?: Readonly<Record<PropertyKey, unknown>>\n readonly preParsing?: unknown\n readonly preValidation?: unknown\n readonly preHandler?: unknown\n readonly onSend?: unknown\n readonly onResponse?: unknown\n}\n\n/** The keys of Fastify's internal request and reply state read by the adapter (`null`: not found). */\ninterface InternalKeys {\n /** The route context of a request. */\n readonly context: symbol | null\n /** The headers set with `reply.header()`. */\n readonly headers: symbol | null\n /** The trailers set with `reply.trailer()`. */\n readonly trailers: symbol | null\n /** The serializer set with `reply.serializer()` (`null` when none). */\n readonly serializer: symbol | null\n}\n\nclass FastifyHttpAdapter implements FastifyAdapter {\n readonly name = 'fastify' as const\n /** WebSocket upgrades (RFC 0003), routed by `instance.server`. */\n readonly upgrades: NodeUpgrades = new NodeUpgrades()\n readonly instance: FastifyInstance\n readonly #routes: AdapterRoute[] = []\n readonly #tracker: ConnectionTracker\n /** The Fastify handler of each Znight route (before Fastify binds it). */\n readonly #handlers = new Map<unknown, AdapterHandler>()\n /** By Fastify route context: how its requests are served (`null`: through Fastify's lifecycle). */\n readonly #lanes = new WeakMap<object, Lane | null>()\n #keys: InternalKeys | undefined\n #fallback: FallbackHandler | undefined\n #ready: Promise<void> | undefined\n #closing: Promise<void> | undefined\n\n constructor(options: FastifyAdapterOptions) {\n if (options.instance === undefined) {\n const serverOptions = options.options ?? {}\n this.instance = Fastify({\n exposeHeadRoutes: false,\n frameworkErrors: (error, request, reply) => {\n this.#reply(reply, this.#dispatchFallback(request, reply, error)).catch(() => undefined)\n },\n ...serverOptions,\n routerOptions: { maxParamLength: MAX_PARAM_LENGTH, ...serverOptions.routerOptions },\n })\n } else {\n this.instance = options.instance\n }\n this.#tracker = new ConnectionTracker(this.instance.server)\n }\n\n registerRoute(route: AdapterRoute): void {\n if (this.#ready !== undefined) throw new Error('fastifyAdapter: routes must be registered before listen()/inject()')\n this.#routes.push(route)\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #prepare(): Promise<void> {\n this.#ready ??= this.#register()\n return this.#ready\n }\n\n async #register(): Promise<void> {\n const routes = this.#routes\n await this.instance.register((child, _options, done) => {\n // Znight reads and parses bodies itself: accept every content type without consuming it. The\n // RegExp parser matches every content type like '*' does, but Fastify caches what a RegExp\n // matched (it parses the content type again on every request to find the '*' parser)\n child.removeAllContentTypeParsers()\n child.addContentTypeParser('*', ignoreBody)\n child.addContentTypeParser(/^/, ignoreBody)\n child.addHook('onRequest', this.#onRequest)\n // added last: sees the route options once every other `onRoute` hook changed them\n child.addHook('onRoute', this.#onRoute)\n for (const route of routes) {\n const handler = route.handler\n const fastifyHandler = (request: FastifyRequest, reply: FastifyReply): Promise<FastifyReply> =>\n this.#reply(reply, handler(adapterRequest(request, reply)))\n this.#handlers.set(fastifyHandler, handler)\n child.route({\n method: route.method,\n url: toFastifyPath(route.path),\n exposeHeadRoute: false,\n handler: fastifyHandler,\n })\n }\n child.setNotFoundHandler((request, reply) =>\n this.#reply(reply, this.#dispatchFallback(request, reply, undefined)),\n )\n child.setErrorHandler((error, request, reply) =>\n this.#reply(reply, this.#dispatchFallback(request, reply, error)),\n )\n done()\n })\n await this.instance.ready()\n }\n\n /**\n * Records in the `config` of a Znight route whether it may be served from the `onRequest` hook:\n * its handler is still the adapter's (no `onRoute` hook wrapped it) and no route-level hook or\n * schema runs between `onRequest` and the handler. The hooks of the instance are checked on the\n * first request (see `#lane`).\n */\n readonly #onRoute = (options: RouteOptions): void => {\n const handler = this.#handlers.get(options.handler)\n if (handler === undefined || options.schema !== undefined) return\n if (HANDLER_HOOKS.some(hook => hasHooks(options[hook]))) return\n const route: DirectRoute = { handler, bare: !hasHooks(options.onSend) && !hasHooks(options.onResponse) }\n options.config = { ...options.config, [DIRECT]: route }\n }\n\n /**\n * The `onRequest` hook of the Znight context. A route request that no later hook, schema or\n * tracing subscriber needs (see `#lane`) is served from here: Fastify skips its content type\n * parsing and the steps between this hook and the handler, which do nothing for Znight routes.\n * Otherwise the request goes on through Fastify's lifecycle.\n */\n readonly #onRequest = (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction): void => {\n const lane = this.#lane(request, reply)\n if (lane !== null && !HANDLER_CHANNEL.hasSubscribers) {\n this.#serve(lane, request, reply)\n return\n }\n // Fastify answers 415 to a content type it cannot parse (`foo`, an empty one) before the route\n // runs: hide such a header from Fastify (Znight reads it, and parses the body as bytes)\n const contentType = request.headers['content-type']\n if (contentType !== undefined && request.mediaType === undefined) {\n HIDDEN_CONTENT_TYPES.set(request.raw, contentType)\n delete request.raw.headers['content-type']\n }\n done()\n }\n\n /**\n * How the requests of the route of `request` are served, decided once per route context: from the\n * `onRequest` hook when `#onRoute` marked the route and the instance has no `preParsing`,\n * `preValidation` or `preHandler` hook (the `onRequest` hook of the Znight context is then the\n * last one); with a text reply written on the raw response when no `onSend` or `onResponse` hook\n * runs either (see `#writeRaw`). `null`: through Fastify's lifecycle (404s, hooks, a Fastify\n * version whose route context differs).\n */\n #lane(request: FastifyRequest, reply: FastifyReply): Lane | null {\n this.#keys ??= {\n context: ownSymbol(request, 'fastify.context'),\n headers: ownSymbol(reply, 'fastify.reply.headers'),\n trailers: ownSymbol(reply, 'fastify.reply.trailers'),\n serializer: ownSymbol(reply, 'fastify.reply.serializer'),\n }\n const key = this.#keys.context\n const context = key === null ? undefined : (request as unknown as Record<symbol, unknown>)[key]\n if (typeof context !== 'object' || context === null) return null\n let lane = this.#lanes.get(context)\n if (lane === undefined) {\n lane = laneOf(context as RouteContext, this.#keys)\n this.#lanes.set(context, lane)\n }\n return lane\n }\n\n /** Serves a route request from the `onRequest` hook (see `#lane`). */\n #serve(lane: Lane, request: FastifyRequest, reply: FastifyReply): void {\n let pending: Promise<AdapterReply>\n try {\n pending = lane.handler(new FastifyAdapterRequest(request, reply, reply))\n } catch (error) {\n // what Fastify does when a route handler throws\n reply.send(asError(error))\n return\n }\n pending.then(\n result => this.#settle(lane, reply, result),\n () => this.#settle(lane, reply, internalError()),\n )\n }\n\n /** Writes the reply of a request served from the `onRequest` hook; a failure is handled like Fastify does. */\n #settle(lane: Lane, reply: FastifyReply, result: AdapterReply): void {\n try {\n if (!lane.raw || !this.#writeRaw(reply, result)) this.#write(reply, result)\n } catch (error) {\n // what Fastify does when the promise of a route handler rejects\n if (reply.sent) reply.log.error({ err: error }, 'Promise errored, but reply.sent = true was set')\n else reply.send(asError(error))\n }\n }\n\n /**\n * Writes a text reply on the raw response, as `reply.send(text)` would without `onSend` hook: one\n * `writeHead()` with the same headers in the same order, then the text. Only when nothing set\n * reply headers or trailers before (an `onRequest` hook) and Fastify would send the headers and\n * text unchanged (content type, exact `content-length`). Returns whether it wrote the reply.\n */\n #writeRaw(reply: FastifyReply, result: AdapterReply): boolean {\n const text = result.text\n const body = result.body\n if (text === undefined || !(body instanceof Uint8Array) || result.raw === true) return false\n const headers = result.headers\n const length = headers['content-length']\n if (typeof length !== 'string' || Number(length) !== body.byteLength) return false\n // a content type Fastify cannot parse is replaced by `reply.send()` (text/plain)\n const type = headers['content-type']\n if (!sendsTextAsIs(type) || (type !== JSON_TYPE && !MEDIA_TYPE.test(type as string))) return false\n const response = reply.raw\n if (reply.sent || response.destroyed || response.headersSent || reply.request.raw.httpVersionMajor !== 1) {\n return false\n }\n const keys = this.#keys as InternalKeys\n const state = reply as unknown as Record<symbol, unknown>\n if (keys.headers === null || keys.trailers === null || state[keys.trailers] !== null) return false\n // a serializer set by a hook transforms text sent with `reply.send()`\n if (keys.serializer === null || state[keys.serializer] !== null) return false\n if (!isEmptyRecord(state[keys.headers])) return false\n response.writeHead(result.status, headers as OutgoingHttpHeaders)\n response.end(text)\n return true\n }\n\n #dispatchFallback(request: FastifyRequest, reply: FastifyReply, error: unknown): Promise<AdapterReply> {\n if (this.#fallback === undefined) return Promise.reject(error ?? new Error('No fallback handler'))\n return this.#fallback(adapterRequest(request, undefined, reply), error)\n }\n\n async #reply(reply: FastifyReply, pending: ReturnType<AdapterHandler>): Promise<FastifyReply> {\n let result: AdapterReply\n try {\n result = await pending\n } catch {\n result = internalError()\n }\n this.#write(reply, result)\n return reply\n }\n\n /** Writes a reply through Fastify (`reply.send()`: `onSend` hooks...). */\n #write(reply: FastifyReply, result: AdapterReply): void {\n if (result.raw === true || reply.sent || reply.raw.destroyed) {\n discardStream(result.body instanceof ReadableStream ? result.body : null)\n return\n }\n if (result.eventStream === true) {\n // no onSend hook (compression, ETag...) may rewrite an event stream: write it on the raw\n // response, with the headers Fastify hooks already set (`onRequest`...) under the plugin's,\n // and their cookies before the plugin's (as reply.header('set-cookie') does on the other path)\n const native = lowerCaseHeaders(reply.getHeaders())\n const headers = { ...native, ...withNativeCookies(native['set-cookie'], result.headers) }\n reply.hijack()\n writeNodeReply(reply.raw, { ...result, headers })\n return\n }\n reply.code(result.status)\n const headers = result.headers\n for (const name of Object.keys(headers)) reply.header(name, headers[name])\n const body = result.body\n if (body === null) {\n reply.send()\n return\n }\n if (body instanceof Uint8Array) {\n // a string is written with the headers at once (bytes take a second write)\n if (result.text !== undefined && sendsTextAsIs(headers['content-type'])) reply.send(result.text)\n else reply.send(Buffer.isBuffer(body) ? body : Buffer.from(body.buffer, body.byteOffset, body.byteLength))\n return\n }\n const stream = body instanceof Blob ? body.stream() : body\n reply.send(Readable.fromWeb(stream as import('node:stream/web').ReadableStream))\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n await this.#prepare()\n this.#closing = undefined\n this.upgrades.attach(this.instance.server)\n await this.instance.listen({ port, host })\n const address = this.instance.server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n if (this.#closing === undefined) {\n this.#tracker.closeIdle()\n this.#closing = this.instance.close()\n }\n if (options.force === true) {\n this.instance.server.closeAllConnections?.()\n this.#tracker.destroyAll()\n }\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n async injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n await this.#prepare()\n const options: LightInjectOptions = await lightInjectOptions(request)\n // Fastify forwards the options to light-my-request (`payloadAsStream`)\n const inject = this.instance.inject as unknown as (options: LightInjectOptions) => Promise<unknown>\n const response = await inject.call(this.instance, options)\n return lightStreamResponse(response as Parameters<typeof lightStreamResponse>[0], request.signal)\n }\n}\n\n/**\n * The lane of a route context (see `FastifyHttpAdapter.#lane`). Once the instance is ready, every\n * lifecycle hook list of a context is `null` or a non-empty array.\n */\nfunction laneOf(context: RouteContext, keys: InternalKeys): Lane | null {\n const route = context.config?.[DIRECT] as DirectRoute | undefined\n if (route === undefined) return null\n if (context.preParsing !== null || context.preValidation !== null || context.preHandler !== null) return null\n const raw = route.bare && context.onSend === null && context.onResponse === null\n return { handler: route.handler, raw: raw && keys.headers !== null && keys.trailers !== null }\n}\n\n/** The content type parser of the Znight context: the body is left unread (Znight reads it). */\nfunction ignoreBody(_request: FastifyRequest, _payload: unknown, done: (error: null, body: undefined) => void): void {\n done(null, undefined)\n}\n\n/** Whether a route option holds hooks (a function or a non-empty array). */\nfunction hasHooks(value: unknown): boolean {\n return value !== undefined && value !== null && !(Array.isArray(value) && value.length === 0)\n}\n\n/** An own symbol-keyed property of a Fastify object, by the symbol's description (internal state). */\nfunction ownSymbol(target: object, description: string): symbol | null {\n for (const symbol of Object.getOwnPropertySymbols(target)) if (symbol.description === description) return symbol\n return null\n}\n\nfunction isEmptyRecord(value: unknown): boolean {\n if (typeof value !== 'object' || value === null) return false\n for (const _key in value) return false\n return true\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error), { cause: error })\n}\n\n/** The last-resort 500 of a handler promise that rejected (the pipeline maps errors: should not happen). */\nfunction internalError(): AdapterReply {\n return {\n status: 500,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n body: new TextEncoder().encode('{\"statusCode\":500,\"error\":\"Internal Server Error\"}'),\n }\n}\n\n/** The JSON content type of the plugin (with a charset). */\nconst JSON_TYPE = 'application/json; charset=utf-8'\n\n/**\n * Whether Fastify sends a string with this content type unchanged: it adds a charset to a JSON\n * type without one, and a content type to a string without one.\n */\n/** A `type/subtype` media type, optionally followed by parameters: what Fastify's `ContentType` accepts. */\nconst MEDIA_TYPE = /^\\s*[\\w!#$%&'*+.^`|~-]+\\/[\\w!#$%&'*+.^`|~-]+\\s*(?:;|$)/\n\nfunction sendsTextAsIs(type: HeaderValue | undefined): boolean {\n if (typeof type !== 'string') return false\n if (type === JSON_TYPE) return true\n const semicolon = type.indexOf(';')\n return !(semicolon === -1 ? type : type.slice(0, semicolon)).toLowerCase().includes('json')\n}\n\nfunction lowerCaseHeaders(\n headers: Readonly<Record<string, string | number | readonly string[] | undefined>>,\n): Record<string, string | string[]> {\n const result: Record<string, string | string[]> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (value !== undefined) result[name.toLowerCase()] = typeof value === 'object' ? [...value] : String(value)\n }\n return result\n}\n\nfunction adapterRequest(\n request: FastifyRequest,\n reply: FastifyReply | undefined,\n response: FastifyReply | undefined = reply,\n): AdapterRequest {\n const hidden = HIDDEN_CONTENT_TYPES.get(request.raw)\n if (hidden !== undefined) {\n // Fastify is done with the content type: restore it (for Znight and `@Req()`)\n HIDDEN_CONTENT_TYPES.delete(request.raw)\n request.raw.headers['content-type'] = hidden\n }\n return new FastifyAdapterRequest(request, reply, response)\n}\n\n/** A Fastify request as an {@link AdapterRequest} (a class: no closure per request). */\nclass FastifyAdapterRequest implements AdapterRequest {\n readonly method: string\n readonly url: string\n readonly headers: Readonly<Record<string, string | undefined>>\n readonly ip: string | undefined\n readonly remoteAddress: string | undefined\n readonly encrypted: boolean\n readonly raw: FastifyRequest\n /** `@Res()`: the reply of a route, `undefined` for the fallback handler. */\n readonly rawResponse: FastifyReply | undefined\n /** The reply whose disconnection aborts the request. */\n readonly #response: FastifyReply | undefined\n #body: Promise<Uint8Array> | undefined\n\n constructor(request: FastifyRequest, reply: FastifyReply | undefined, response: FastifyReply | undefined) {\n this.method = request.raw.method ?? request.method\n this.url = request.raw.url ?? request.url\n this.headers = normalizeNodeHeaders(request.headers)\n this.ip = request.ip\n this.remoteAddress = request.raw.socket?.remoteAddress\n this.encrypted = isEncrypted(request.raw)\n this.raw = request\n this.rawResponse = reply\n this.#response = response\n }\n\n readBody(limit: number): Promise<Uint8Array> {\n this.#body ??= readNodeBody(this.raw.raw, limit)\n return this.#body\n }\n\n streamBody(limit: number): ReadableStream<Uint8Array> {\n return nodeBodyStream(this.raw.raw, limit)\n }\n\n rawStatus(): number | undefined {\n return this.rawResponse?.statusCode\n }\n\n onAbort(listener: () => void): void {\n if (this.#response !== undefined) onResponseAbort(this.#response.raw, listener)\n }\n}\n"],"mappings":"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"}
|
package/dist/hono.d.mts
CHANGED
package/dist/hono.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as isBun } from "./utils-DIWdzVht.mjs";
|
|
1
|
+
import { $ as exceedsDeclaredLength, Ct as discardStream, G as nodeBodyStream, H as closeServer, J as readNodeBody, Q as bufferInjectStream, V as ConnectionTracker, a as refusalResponse, ct as readWebStream, et as injectFetchStream, i as bunWebSocketOption, kt as BadRequestException, lt as serverAddress, q as onResponseAbort, r as NodeUpgrades, st as payloadTooLarge, t as FetchUpgradeRequest, u as isWebSocketUpgrade, ut as webBodyStream } from "./upgrade-io-dTMDDmBi.mjs";
|
|
3
2
|
import { toHonoPath } from "./path.mjs";
|
|
3
|
+
import { i as isBun } from "./utils-DIWdzVht.mjs";
|
|
4
4
|
import { Hono } from "hono";
|
|
5
5
|
// src/adapters/hono.ts
|
|
6
6
|
/**
|
|
@@ -15,6 +15,19 @@ import { Hono } from "hono";
|
|
|
15
15
|
function honoAdapter(options = {}) {
|
|
16
16
|
return new HonoHttpAdapter(options);
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* The upgrade capability of the Hono adapter (RFC 0003): `Bun.serve()` upgrades on Bun (checked in
|
|
20
|
+
* the fetch handler before Hono routing), `node:http` upgrades of `@hono/node-server` on Node.js.
|
|
21
|
+
*/
|
|
22
|
+
var HonoUpgrades = class {
|
|
23
|
+
kind = isBun() ? "bun" : "node";
|
|
24
|
+
node = new NodeUpgrades();
|
|
25
|
+
options;
|
|
26
|
+
enable(options) {
|
|
27
|
+
this.options = options;
|
|
28
|
+
this.node.enable(options);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
18
31
|
/** Default `idleTimeout` of `Bun.serve()`, in seconds. */
|
|
19
32
|
const BUN_IDLE_TIMEOUT = 10;
|
|
20
33
|
/**
|
|
@@ -61,6 +74,8 @@ const INJECT_ENV = Object.freeze({ incoming: Object.freeze({ socket: Object.free
|
|
|
61
74
|
var HonoHttpAdapter = class {
|
|
62
75
|
name = "hono";
|
|
63
76
|
app;
|
|
77
|
+
/** WebSocket upgrades (RFC 0003). */
|
|
78
|
+
upgrades = new HonoUpgrades();
|
|
64
79
|
#serveOptions;
|
|
65
80
|
/** Number of routes of `app` registered by the adapter (see {@link #middleware}). */
|
|
66
81
|
#routes = 0;
|
|
@@ -244,6 +259,23 @@ var HonoHttpAdapter = class {
|
|
|
244
259
|
}
|
|
245
260
|
return response;
|
|
246
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* A WebSocket upgrade of a gateway path received by `Bun.serve()`, run before Hono routing:
|
|
264
|
+
* `undefined` for any other request; otherwise the refusal, or `undefined` once upgraded.
|
|
265
|
+
*/
|
|
266
|
+
#bunUpgrade(options, request, server) {
|
|
267
|
+
const upgrade = request.headers.get("upgrade");
|
|
268
|
+
if (upgrade === null || !isWebSocketUpgrade({ upgrade })) return void 0;
|
|
269
|
+
const url = pathAndQuery(request.url);
|
|
270
|
+
if (!options.match(pathOf(url))) return void 0;
|
|
271
|
+
const address = server.requestIP?.(request)?.address ?? void 0;
|
|
272
|
+
const adapterRequest = new FetchUpgradeRequest(request, url, fetchHeaders(request.headers), address);
|
|
273
|
+
return options.handle(adapterRequest, {
|
|
274
|
+
kind: "bun",
|
|
275
|
+
request,
|
|
276
|
+
server
|
|
277
|
+
}).then((reply) => reply === void 0 ? void 0 : refusalResponse(reply), () => new Response(null, { status: 500 }));
|
|
278
|
+
}
|
|
247
279
|
async listen(port, host) {
|
|
248
280
|
this.#prepare();
|
|
249
281
|
this.#closing = void 0;
|
|
@@ -251,12 +283,14 @@ var HonoHttpAdapter = class {
|
|
|
251
283
|
if (isBun() && bun !== void 0) {
|
|
252
284
|
const options = this.#serveOptions;
|
|
253
285
|
this.#idleTimeout = typeof options.idleTimeout === "number" ? options.idleTimeout : BUN_IDLE_TIMEOUT;
|
|
286
|
+
const upgrades = this.upgrades.options;
|
|
254
287
|
const server = bun.serve({
|
|
255
288
|
maxRequestBodySize: Number.MAX_SAFE_INTEGER,
|
|
256
289
|
...options,
|
|
290
|
+
...upgrades === void 0 ? {} : { websocket: bunWebSocketOption(upgrades.bunWebSocket) },
|
|
257
291
|
port,
|
|
258
292
|
hostname: host,
|
|
259
|
-
fetch: (request, server) => this.#middleware() ? this.#bunFetch(request, server) : this.app.fetch(request, server)
|
|
293
|
+
fetch: (request, server) => (upgrades === void 0 ? void 0 : this.#bunUpgrade(upgrades, request, server)) ?? (this.#middleware() ? this.#bunFetch(request, server) : this.app.fetch(request, server))
|
|
260
294
|
});
|
|
261
295
|
this.#bun = server;
|
|
262
296
|
return serverAddress(server.hostname, server.port);
|
|
@@ -268,6 +302,7 @@ var HonoHttpAdapter = class {
|
|
|
268
302
|
throw new Error("honoAdapter on Node.js requires the optional peer dependency \"@hono/node-server\" (npm i @hono/node-server)", { cause: error });
|
|
269
303
|
}
|
|
270
304
|
const server = nodeServer.createAdaptorServer({ fetch: this.app.fetch });
|
|
305
|
+
this.upgrades.node.attach(server);
|
|
271
306
|
this.#node = server;
|
|
272
307
|
this.#tracker = new ConnectionTracker(server);
|
|
273
308
|
await new Promise((resolve, reject) => {
|
package/dist/hono.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono.mjs","names":[],"sources":["../src/adapters/hono.ts"],"sourcesContent":["/**\n * `@znight/http/hono`: serves Znight routes with [Hono](https://hono.dev) 4 (optional peer\n * dependency `hono`). On Bun the server is `Bun.serve()`; on Node.js it is `@hono/node-server`\n * (optional peer dependency, loaded on `listen()`). `inject()` uses `app.request()`.\n */\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterHandler,\n AdapterReply,\n AdapterRequest,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\nimport type { HeaderValue } from '../exceptions.ts'\n\nimport { type Context, Hono, type Next } from 'hono'\n\nimport { BadRequestException } from '../exceptions.ts'\nimport { toHonoPath } from '../path.ts'\nimport { discardStream } from '../stream.ts'\nimport { isBun } from '../utils.ts'\nimport { ConnectionTracker, closeServer, nodeBodyStream, onResponseAbort, readNodeBody } from './node-io.ts'\nimport {\n bufferInjectStream,\n exceedsDeclaredLength,\n injectFetchStream,\n payloadTooLarge,\n readWebStream,\n serverAddress,\n webBodyStream,\n} from './shared.ts'\n\n/** Options of {@link honoAdapter}. */\nexport interface HonoAdapterOptions {\n /**\n * An existing application: its middleware (`app.use(...)`) runs before the Znight routes. Znight\n * sets its `notFound` and `onError` handlers.\n */\n readonly app?: Hono\n /**\n * Options of `Bun.serve()` on Bun (`tls`, `idleTimeout`, `development`, `error`...); Znight sets\n * `port`, `hostname` and `fetch`. Ignored on Node.js.\n */\n readonly serveOptions?: HonoServeOptions\n}\n\n/**\n * Options of `Bun.serve()` (see the Bun documentation; Bun types are not required). Defaults set by\n * the adapter: `maxRequestBodySize` unlimited (Znight enforces `bodyLimit` / `@BodyLimit` itself)\n * and Bun's `idleTimeout` (10 s), lifted while a streamed response is sent.\n */\nexport interface HonoServeOptions {\n /** Seconds a connection may stay idle (`0`: never closed). Default: 10. */\n readonly idleTimeout?: number\n /** Request bodies above this size are refused by Bun (413). Default: unlimited. */\n readonly maxRequestBodySize?: number\n /** TLS options (`{ key, cert }`). */\n readonly tls?: unknown\n /** Any other `Bun.serve()` option. */\n readonly [option: string]: unknown\n}\n\n/** The Hono adapter. */\nexport interface HonoAdapter extends HttpAdapter {\n readonly name: 'hono'\n /** The Hono application. */\n readonly app: Hono\n /** Fetch handler of the application (`export default { fetch: adapter.fetch }` on edge runtimes). */\n readonly fetch: (request: Request, env?: unknown) => Response | Promise<Response>\n}\n\n/**\n * Creates a Hono adapter.\n *\n * ```ts\n * import { honoAdapter } from '@znight/http/hono'\n *\n * HttpPlugin.configure({ adapter: honoAdapter() })\n * ```\n */\nexport function honoAdapter(options: HonoAdapterOptions = {}): HonoAdapter {\n return new HonoHttpAdapter(options)\n}\n\n/** The parts of a Bun server used here (Bun types are not available to the package sources). */\ninterface BunServer {\n readonly port: number\n readonly hostname: string\n stop(closeActiveConnections?: boolean): Promise<void> | void\n requestIP?(request: Request): { readonly address: string } | null\n /** Idle timeout of one request, in seconds (`0`: none). */\n timeout?(request: Request, seconds: number): void\n}\n\ninterface BunRuntime {\n serve(\n options: HonoServeOptions & {\n port: number\n hostname: string\n fetch: (request: Request, server: BunServer) => Response | Promise<Response>\n },\n ): BunServer\n}\n\n/** Default `idleTimeout` of `Bun.serve()`, in seconds. */\nconst BUN_IDLE_TIMEOUT = 10\n\n/**\n * A streamed response body built for a request of `Bun.serve()` (see `#bunStream`); with middleware,\n * pending until `#bunFetch` takes it or it ends.\n */\nclass BunBody {\n /** The `Blob` (file) the body reads. */\n readonly blob: Blob | undefined\n #settle: (() => void) | undefined\n #onEnd: (() => void) | undefined\n #ended = false\n\n /** `settle` is called once, when the body is taken by `#bunFetch` or ends, whichever comes first. */\n constructor(blob: Blob | undefined, settle: (() => void) | undefined) {\n this.blob = blob\n this.#settle = settle\n }\n\n /** Whether the body ended (read to its end, failed or cancelled). */\n get ended(): boolean {\n return this.#ended\n }\n\n /** The body is no longer pending (taken by `#bunFetch`). */\n settle(): void {\n const settle = this.#settle\n this.#settle = undefined\n settle?.()\n }\n\n /** Sets the function called when the body ends. */\n onEnd(callback: () => void): void {\n this.#onEnd = callback\n }\n\n /** The body ended (read to its end, failed or cancelled). */\n end(): void {\n if (this.#ended) return\n this.#ended = true\n this.settle()\n this.#onEnd?.()\n }\n}\n\n/** Response bodies built while serving with `Bun.serve()`, by body (Hono may copy a `Response`, not its body). */\nconst BUN_BODIES = new WeakMap<ReadableStream<Uint8Array>, BunBody>()\n\n/** The Node.js request behind a request of `@hono/node-server`. */\ninterface NodeIncoming {\n readonly url?: string\n readonly socket?: { readonly encrypted?: unknown }\n readonly httpVersionMajor: number\n readonly stream?: { readonly endAfterHeaders?: boolean }\n}\n\n/** Bindings of injected requests (the remote address the other adapters report for `inject()`). */\nconst INJECT_ENV = Object.freeze({ incoming: Object.freeze({ socket: Object.freeze({ remoteAddress: '127.0.0.1' }) }) })\n\ntype HonoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'\n\n/** The handlers of the GET and HEAD routes of one path. */\ninterface ReadHandlers {\n get: AdapterHandler | undefined\n head: AdapterHandler | undefined\n}\n\nclass HonoHttpAdapter implements HonoAdapter {\n readonly name = 'hono' as const\n readonly app: Hono\n readonly #serveOptions: HonoServeOptions\n /** Number of routes of `app` registered by the adapter (see {@link #middleware}). */\n #routes = 0\n /** The GET and HEAD handlers of each path shape, served by one route of `app` (see {@link registerRoute}). */\n readonly #reads = new Map<string, ReadHandlers>()\n #fallback: FallbackHandler | undefined\n #prepared = false\n #bun: BunServer | undefined\n /**\n * Streamed bodies of `Bun.serve()` replies (with middleware) neither taken by `#bunFetch` nor ended\n * (see {@link BunBody}): while there is none, `#bunFetch` returns responses untouched (reading\n * `response.body` would turn a text body into a stream that Bun then sends as one).\n */\n #pendingBodies = 0\n /** Idle timeout of the `Bun.serve()` server, in seconds (`0`: none). */\n #idleTimeout = BUN_IDLE_TIMEOUT\n #node: Server | undefined\n #tracker: ConnectionTracker | undefined\n #closing: Promise<void> | undefined\n\n constructor(options: HonoAdapterOptions) {\n this.app = options.app ?? new Hono()\n this.#serveOptions = options.serveOptions ?? {}\n }\n\n readonly fetch = (request: Request, env?: unknown): Response | Promise<Response> => {\n this.#prepare()\n return this.app.fetch(request, env)\n }\n\n registerRoute(route: AdapterRoute): void {\n const handler = route.handler\n const path = toHonoPath(route.path)\n if (route.method === 'GET' || route.method === 'HEAD') {\n // Hono routes HEAD requests to GET handlers: one GET handler per path serves both methods (the\n // plugin registers HEAD routes first), so that a request matches a single handler and Hono\n // skips its middleware composition. Paths are keyed by their shape (`/users/:` for\n // `/users/:id` and `/users/:userId`): a GET route joins the HEAD route matching the same\n // requests, at its rank (Znight reads the parameters itself, never Hono's names)\n const existing = this.#reads.get(route.path.shape)\n if (existing !== undefined) {\n if (route.method === 'GET') existing.get = handler\n else existing.head = handler\n return\n }\n const reads: ReadHandlers =\n route.method === 'GET' ? { get: handler, head: undefined } : { get: undefined, head: handler }\n this.#reads.set(route.path.shape, reads)\n this.#routes++\n this.app.get(path, (c: Context, next: Next) => {\n const read = c.req.raw.method === 'HEAD' ? reads.head : reads.get\n return read === undefined ? next().then(() => c.res) : this.#serve(c, read)\n })\n return\n }\n this.#routes++\n this.app.on(route.method as HonoMethod, path, (c: Context) => this.#serve(c, handler))\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #prepare(): void {\n if (this.#prepared) return\n this.#prepared = true\n this.app.notFound(c => this.#serveFallback(c, undefined))\n this.app.onError((error, c) => this.#serveFallback(c, error))\n }\n\n async #serve(c: Context, handler: AdapterHandler): Promise<Response> {\n const request = this.#request(c)\n // Hono routes the WHATWG-normalised URL (`/a/../b` → `/b`, `%2e%2e` resolved): when the raw\n // target differs (Node.js; on Bun only the normalised URL is known), let the plugin route the raw\n // path, like on the other adapters\n const reply =\n this.#fallback !== undefined && request.rawTarget && pathOf(request.url) !== pathOf(pathAndQuery(c.req.raw.url))\n ? await this.#fallback(request)\n : await handler(request)\n return this.#toResponse(c, reply)\n }\n\n async #serveFallback(c: Context, error: unknown): Promise<Response> {\n if (this.#fallback === undefined) throw error ?? new Error('No fallback handler')\n return this.#toResponse(c, await this.#fallback(this.#request(c), error))\n }\n\n #request(c: Context): HonoAdapterRequest {\n return new HonoAdapterRequest(c, this.#servedByBun(c) ? this.#bun : undefined)\n }\n\n /** Whether the request of `c` was received by the `Bun.serve()` server of the adapter. */\n #servedByBun(c: Context): boolean {\n return this.#bun !== undefined && c.env === this.#bun\n }\n\n /**\n * Whether `app` has routes the adapter did not register (middleware, other routes): only they can\n * set headers on the context or read the response.\n */\n #middleware(): boolean {\n return this.app.routes.length !== this.#routes\n }\n\n #toResponse(c: Context, reply: AdapterReply): Response {\n const middleware = this.#middleware()\n if (this.#servedByBun(c)) {\n return reply.body instanceof ReadableStream\n ? this.#bunStream(c, reply, middleware)\n : toResponse(c, reply, middleware)\n }\n const outgoing = nodeOutgoing(c)\n if (outgoing !== undefined) {\n const body = reply.body instanceof ReadableStream ? reply.body : null\n if (outgoing.destroyed) {\n // @hono/node-server: the client left before the reply was ready (an async pre-flight). The\n // server would neither read nor cancel the body: release it (event source, request scope)\n discardStream(body)\n return toResponse(c, { ...reply, body: null }, middleware)\n }\n if (body !== null) {\n // the client may leave before @hono/node-server reads the body (its own listener then comes\n // too late): a body nobody locked is cancelled when the response closes unfinished\n outgoing.once('close', () => {\n if (!outgoing.writableFinished) discardStream(body)\n })\n }\n // a quiet event stream must outlive a socket timeout\n if (reply.eventStream === true && typeof outgoing.setTimeout === 'function') outgoing.setTimeout(0)\n }\n return toResponse(c, reply, middleware)\n }\n\n /**\n * The response of a streamed reply to a request of `Bun.serve()`: the end of its body is observed\n * (idle timeout, native blob: see {@link #bunSend}).\n */\n #bunStream(c: Context, reply: AdapterReply, middleware: boolean): Response {\n const reader = (reply.body as ReadableStream<Uint8Array>).getReader()\n const served = new BunBody(\n reply.blob,\n middleware\n ? () => {\n this.#pendingBodies--\n }\n : undefined,\n )\n const body = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n let result: Awaited<ReturnType<typeof reader.read>>\n try {\n result = await reader.read()\n } catch (error) {\n served.end()\n throw error\n }\n if (result.done) {\n served.end() // before closing: Bun ignores timeout() once the response is complete\n controller.close()\n return\n }\n controller.enqueue(result.value)\n },\n cancel(reason) {\n served.end()\n return reader.cancel(reason)\n },\n },\n { highWaterMark: 0 },\n )\n const response = toResponse(c, { ...reply, body }, middleware)\n if (!middleware) {\n // nothing else handles the request (see the fetch handler given to Bun.serve()): Bun sends this\n // response, unless it does not carry the body (an invalid header: the body was discarded)\n return served.ended ? response : this.#bunSend(response, body, served, c.req.raw)\n }\n // middleware may copy the response (not its body) or replace it: #bunFetch finds the body\n this.#pendingBodies++\n BUN_BODIES.set(body, served)\n return response\n }\n\n /** The fetch handler given to `Bun.serve()` when `app` has middleware (see {@link #bunStream}). */\n async #bunFetch(request: Request, server: BunServer): Promise<Response> {\n const response = await this.app.fetch(request, server)\n // only a streamed reply needs more: leave the other responses untouched (see #pendingBodies)\n if (this.#pendingBodies === 0) return response\n const body = response.body\n const served = body === null ? undefined : BUN_BODIES.get(body)\n if (body === null || served === undefined) return response\n BUN_BODIES.delete(body)\n served.settle()\n return this.#bunSend(response, body, served, request)\n }\n\n /**\n * The response Bun sends for a streamed reply, once final: a file `Blob` is sent natively, a\n * stream with the idle timeout of the connection lifted.\n */\n #bunSend(response: Response, body: ReadableStream<Uint8Array>, served: BunBody, request: Request): Response {\n if (served.blob !== undefined && !body.locked) {\n // a Blob is sent by Bun itself (sendfile, exact Content-Length; a stream would be chunked). Bun\n // opens its file as soon as it takes the response: release the stream (and with it the request\n // scope, which may delete the file) right after\n const native = new Response(served.blob, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n setImmediate(() => discardStream(body))\n return native\n }\n const server = this.#bun\n const idleTimeout = this.#idleTimeout\n if (idleTimeout > 0 && !served.ended && typeof server?.timeout === 'function') {\n // the idle timeout of Bun.serve() also cuts a response whose stream pauses (SSE, slow producers):\n // lift it while the stream is sent, then restore it for the keep-alive connection (a body that\n // already ended would never restore it)\n server.timeout(request, 0)\n served.onEnd(() => {\n try {\n server.timeout?.(request, idleTimeout)\n } catch {\n // the connection is gone\n }\n })\n }\n return response\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n this.#prepare()\n this.#closing = undefined\n const bun = (globalThis as { Bun?: BunRuntime }).Bun\n if (isBun() && bun !== undefined) {\n const options = this.#serveOptions\n this.#idleTimeout = typeof options.idleTimeout === 'number' ? options.idleTimeout : BUN_IDLE_TIMEOUT\n const server = bun.serve({\n // Znight enforces the body limits (route @BodyLimit, plugin bodyLimit): not Bun's 128 MiB default\n maxRequestBodySize: Number.MAX_SAFE_INTEGER,\n ...options,\n port,\n hostname: host,\n // without middleware, the response of a Znight route is the one Bun sends (no wrapper needed)\n fetch: (request, server) =>\n this.#middleware() ? this.#bunFetch(request, server) : this.app.fetch(request, server),\n })\n this.#bun = server\n return serverAddress(server.hostname, server.port)\n }\n let nodeServer: typeof import('@hono/node-server')\n try {\n nodeServer = await import('@hono/node-server')\n } catch (error) {\n throw new Error(\n 'honoAdapter on Node.js requires the optional peer dependency \"@hono/node-server\" (npm i @hono/node-server)',\n { cause: error },\n )\n }\n const server = nodeServer.createAdaptorServer({ fetch: this.app.fetch }) as Server\n this.#node = server\n this.#tracker = new ConnectionTracker(server)\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error): void => reject(error)\n server.once('error', onError)\n server.listen(port, host, () => {\n server.off('error', onError)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n const bun = this.#bun\n if (bun !== undefined) {\n if (options.force === true) {\n this.#closing = Promise.resolve(bun.stop(true))\n } else {\n this.#closing ??= Promise.resolve(bun.stop(false))\n }\n return this.#closing\n }\n const server = this.#node\n const tracker = this.#tracker\n if (server === undefined || tracker === undefined) return Promise.resolve()\n this.#closing = closeServer(server, tracker, this.#closing, options.force === true)\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n this.#prepare()\n return injectFetchStream(request => this.app.request(request, undefined, INJECT_ENV), request)\n }\n}\n\n/** A Hono request as an {@link AdapterRequest} (a class: no closure per request). */\nclass HonoAdapterRequest implements AdapterRequest {\n readonly method: string\n readonly url: string\n readonly headers: Readonly<Record<string, string | undefined>>\n readonly ip: string | undefined\n readonly remoteAddress: string | undefined\n readonly encrypted: boolean\n readonly raw: Request\n readonly rawResponse: Context\n /** `url` is the raw request target (Node.js), not the WHATWG-normalised URL of `raw`. */\n readonly rawTarget: boolean\n /** The Node.js request of `@hono/node-server`, absent on Bun and for `inject()`. */\n readonly #incoming: NodeIncoming | undefined\n /**\n * The same request over HTTP/1.1, read directly (Node.js streams): no Fetch `Request` and body\n * stream are built for it.\n */\n readonly #http1: IncomingMessage | undefined\n /** Received by the `Bun.serve()` server of the adapter: its body is framed by the HTTP parser. */\n readonly #bun: boolean\n #body: Promise<Uint8Array> | undefined\n\n constructor(c: Context, bun: BunServer | undefined) {\n const request = c.req.raw\n this.method = request.method\n this.raw = request\n this.rawResponse = c\n this.#bun = bun !== undefined\n if (bun !== undefined) {\n // Bun.serve(): only the normalised URL is known; the address is asked now (Bun no longer knows\n // it once the response is sent)\n const url = request.url\n const address = bun.requestIP?.(request)?.address ?? undefined\n this.url = pathAndQuery(url)\n this.rawTarget = false\n this.headers = fetchHeaders(request.headers)\n this.ip = address\n this.remoteAddress = address\n this.encrypted = url.startsWith('https:')\n this.#incoming = undefined\n this.#http1 = undefined\n return\n }\n const incoming = nodeIncoming(c)\n const http1 = incoming !== undefined && incoming.httpVersionMajor < 2 ? (incoming as IncomingMessage) : undefined\n const address = remoteAddress(c)\n const target = incoming?.url\n // the raw target on Node.js; for a Web request only the normalised URL is known\n this.rawTarget = typeof target === 'string' && target !== ''\n this.url = pathAndQuery(this.rawTarget ? (target as string) : request.url)\n this.headers = (http1 === undefined ? undefined : distinctHeaders(http1)) ?? fetchHeaders(request.headers)\n this.ip = address\n this.remoteAddress = address\n this.encrypted = incoming === undefined ? request.url.startsWith('https:') : incoming.socket?.encrypted === true\n this.#incoming = incoming\n this.#http1 = http1\n }\n\n readBody(limit: number): Promise<Uint8Array> {\n if (this.#body === undefined) {\n const http1 = this.#http1\n if (http1 !== undefined) this.#body = readNodeBody(http1, limit)\n else if (this.#bun && this.#framed()) this.#body = readFramedBody(this.raw, limit, this.headers['content-length'])\n else this.#body = readWebStream(() => this.raw.body, limit, this.headers['content-length'])\n }\n return this.#body\n }\n\n /**\n * Whether the whole body can be read at once from `Bun.serve()`: its length is declared (the\n * HTTP parser then reads no more) and nothing read it yet.\n */\n #framed(): boolean {\n const headers = this.headers\n return headers['content-length'] !== undefined && headers['transfer-encoding'] === undefined && !this.raw.bodyUsed\n }\n\n streamBody(limit: number): ReadableStream<Uint8Array> {\n const http1 = this.#http1\n if (http1 !== undefined) return nodeBodyStream(http1, limit)\n return webBodyStream(() => this.raw.body, limit, this.headers['content-length'])\n }\n\n // asked only without content-length / transfer-encoding: a request read from an HTTP/1.1\n // connection then has no body (`@hono/node-server` still gives every POST a body stream), an\n // HTTP/2 one unless its HEADERS frame ended the stream; a Web `Request` (Bun, inject) says it\n hasBody(): boolean {\n const incoming = this.#incoming\n return incoming === undefined\n ? this.raw.body !== null\n : incoming.httpVersionMajor >= 2 && incoming.stream?.endAfterHeaders === false\n }\n\n onAbort(listener: () => void): void {\n const outgoing = this.#bun ? undefined : nodeOutgoing(this.rawResponse)\n if (outgoing !== undefined) {\n onResponseAbort(outgoing as unknown as ServerResponse, listener)\n return\n }\n // Bun.serve() and inject(): the request signal aborts when the client leaves\n const signal = this.raw.signal\n if (signal.aborted) queueMicrotask(listener)\n else signal.addEventListener('abort', listener, { once: true })\n }\n}\n\n/** The headers of a Fetch request: lower-case names, repeated headers joined (`Headers` semantics). */\nfunction fetchHeaders(source: Headers): Record<string, string> {\n const headers: Record<string, string> = {}\n // forEach: no iterator nor entry array per header\n source.forEach((value, name) => {\n headers[name] = value\n })\n return headers\n}\n\n/**\n * Reads at once (`request.bytes()`, no stream) the body of a request received by `Bun.serve()`\n * whose `content-length` is declared: Bun reads exactly that many bytes, so checking the declared\n * length enforces `limit`. Failures are those of {@link readWebStream}: 413 above `limit`, 400 when\n * the client aborts.\n */\nasync function readFramedBody(request: Request, limit: number, contentLength: string | undefined): Promise<Uint8Array> {\n if (exceedsDeclaredLength(contentLength, limit)) throw payloadTooLarge(limit)\n let bytes: Uint8Array\n try {\n bytes = await request.bytes()\n } catch (error) {\n throw new BadRequestException('Failed to read the request body', undefined, { cause: error })\n }\n if (bytes.byteLength > limit) throw payloadTooLarge(limit)\n return bytes\n}\n\n/**\n * The headers of a Node.js request when none is repeated: then they hold the values of its Fetch\n * `Headers` (Node.js keeps the first of some repeated headers where `Headers` join them all).\n */\nfunction distinctHeaders(request: IncomingMessage): Record<string, string | undefined> | undefined {\n const headers = request.headers\n let count = 0\n for (const name in headers) {\n if (typeof headers[name] !== 'string') return undefined\n count++\n }\n return count * 2 === request.rawHeaders.length ? (headers as Record<string, string | undefined>) : undefined\n}\n\n/** The part of a Node.js `ServerResponse` used here. */\ninterface NodeOutgoing {\n readonly writableFinished: boolean\n readonly destroyed: boolean\n once(event: 'close', listener: () => void): unknown\n setTimeout?(ms: number): unknown\n}\n\n/** The Node.js response of `@hono/node-server` (`c.env.outgoing`), absent on Bun and for `inject()`. */\nfunction nodeOutgoing(c: Context): NodeOutgoing | undefined {\n const outgoing = (c.env as { outgoing?: Partial<NodeOutgoing> } | undefined)?.outgoing\n return typeof outgoing?.once === 'function' ? (outgoing as NodeOutgoing) : undefined\n}\n\n/** The Node.js request of `@hono/node-server` (`c.env.incoming`), absent on Bun and for `inject()`. */\nfunction nodeIncoming(c: Context): NodeIncoming | undefined {\n const incoming = (c.env as { incoming?: Partial<NodeIncoming> } | undefined)?.incoming\n return typeof incoming?.httpVersionMajor === 'number' ? (incoming as NodeIncoming) : undefined\n}\n\nfunction pathAndQuery(url: string): string {\n if (url.startsWith('/')) return url\n const scheme = url.indexOf('://')\n const start = url.indexOf('/', scheme === -1 ? 0 : scheme + 3)\n return start === -1 ? '/' : url.slice(start)\n}\n\nfunction pathOf(url: string): string {\n const query = url.indexOf('?')\n return query === -1 ? url : url.slice(0, query)\n}\n\nfunction remoteAddress(c: Context): string | undefined {\n const env = c.env as\n | { incoming?: { socket?: { remoteAddress?: string } }; requestIP?: BunServer['requestIP'] }\n | undefined\n if (env === undefined || env === null) return undefined\n if (typeof env.requestIP === 'function') return env.requestIP(c.req.raw)?.address ?? undefined\n return env.incoming?.socket?.remoteAddress\n}\n\n/**\n * The `Response` of a reply. Without `middleware` (see `#middleware`), nothing set headers on the\n * context and nothing reads the response: headers given as a record of strings are sent as is by\n * `@hono/node-server` (no `Headers` object), and a text body as a string.\n */\nfunction toResponse(c: Context, reply: AdapterReply, middleware: boolean): Response {\n if (reply.raw === true) {\n discardStream(reply.body instanceof ReadableStream ? reply.body : null)\n return c.res\n }\n if (!middleware && isStringRecord(reply.headers)) {\n return new Response(reply.text ?? reply.body, {\n status: reply.status,\n headers: reply.headers as Readonly<Record<string, string>>,\n })\n }\n const headers = new Headers()\n try {\n for (const name of Object.keys(reply.headers)) {\n const value = reply.headers[name]\n if (typeof value === 'string') headers.set(name, value)\n else if (value !== undefined) for (const item of value) headers.append(name, item)\n }\n } catch {\n // a header Headers refuses (the plugin validates them): never rethrow into Hono's onError, which\n // would run the pipeline again; release the body and answer a plain 500, like writeNodeReply\n discardStream(reply.body instanceof ReadableStream ? reply.body : null)\n return new Response('{\"statusCode\":500,\"error\":\"Internal Server Error\"}', {\n status: 500,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n })\n }\n if (middleware) keepContextCookies(c, headers)\n return new Response(reply.text ?? reply.body, { status: reply.status, headers })\n}\n\n/** Whether every header has a single value (a record of strings is a valid `HeadersInit`). */\nfunction isStringRecord(headers: Readonly<Record<string, HeaderValue>>): boolean {\n // Object.keys: faster than for...in on the null-prototype records of the pipeline (V8)\n const names = Object.keys(headers)\n for (let index = 0; index < names.length; index++) {\n if (typeof headers[names[index] as string] !== 'string') return false\n }\n return true\n}\n\n/**\n * Keeps the cookies Hono middleware set before the Znight route (`setCookie(c, ...)`,\n * `c.header('set-cookie', ..., { append: true })`) before the reply's own, like the other adapters.\n * Hono drops the headers of the context when a handler returns its own `Response` (or, when a\n * middleware read `c.res`, replaces the response's cookies with the context's): the reply's cookies\n * are added to the context too, so that both ways end with every cookie, in order.\n */\nfunction keepContextCookies(c: Context, headers: Headers): void {\n let context: string[]\n try {\n // the headers of the context without changing it (`c.res` would create the context response)\n context = c.newResponse(null).headers.getSetCookie()\n } catch {\n return // a status a bodyless Response refuses (`c.status(101)`): nothing to keep\n }\n if (context.length === 0) return\n const own = headers.getSetCookie()\n for (const line of own) c.header('set-cookie', line, { append: true })\n headers.delete('set-cookie')\n for (const line of context) headers.append('set-cookie', line)\n for (const line of own) headers.append('set-cookie', line)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hono.mjs","names":[],"sources":["../src/adapters/hono.ts"],"sourcesContent":["/**\n * `@znight/http/hono`: serves Znight routes with [Hono](https://hono.dev) 4 (optional peer\n * dependency `hono`). On Bun the server is `Bun.serve()`; on Node.js it is `@hono/node-server`\n * (optional peer dependency, loaded on `listen()`). `inject()` uses `app.request()`.\n */\nimport type { IncomingMessage, Server, ServerResponse } from 'node:http'\nimport type {\n AdapterCloseOptions,\n AdapterHandler,\n AdapterReply,\n AdapterRequest,\n AdapterRoute,\n FallbackHandler,\n HttpAdapter,\n HttpServerAddress,\n InjectRequest,\n InjectResponse,\n InjectStreamRequest,\n InjectStreamResponse,\n} from '../adapter.ts'\nimport type { HeaderValue } from '../exceptions.ts'\nimport type { AdapterUpgrades, UpgradeHandlerOptions } from '../upgrade.ts'\n\nimport { type Context, Hono, type Next } from 'hono'\n\nimport { BadRequestException } from '../exceptions.ts'\nimport { toHonoPath } from '../path.ts'\nimport { discardStream } from '../stream.ts'\nimport { isWebSocketUpgrade } from '../upgrade.ts'\nimport { isBun } from '../utils.ts'\nimport { ConnectionTracker, closeServer, nodeBodyStream, onResponseAbort, readNodeBody } from './node-io.ts'\nimport {\n bufferInjectStream,\n exceedsDeclaredLength,\n injectFetchStream,\n payloadTooLarge,\n readWebStream,\n serverAddress,\n webBodyStream,\n} from './shared.ts'\nimport { bunWebSocketOption, FetchUpgradeRequest, NodeUpgrades, refusalResponse } from './upgrade-io.ts'\n\n/** Options of {@link honoAdapter}. */\nexport interface HonoAdapterOptions {\n /**\n * An existing application: its middleware (`app.use(...)`) runs before the Znight routes. Znight\n * sets its `notFound` and `onError` handlers.\n */\n readonly app?: Hono\n /**\n * Options of `Bun.serve()` on Bun (`tls`, `idleTimeout`, `development`, `error`...); Znight sets\n * `port`, `hostname` and `fetch`. Ignored on Node.js.\n */\n readonly serveOptions?: HonoServeOptions\n}\n\n/**\n * Options of `Bun.serve()` (see the Bun documentation; Bun types are not required). Defaults set by\n * the adapter: `maxRequestBodySize` unlimited (Znight enforces `bodyLimit` / `@BodyLimit` itself)\n * and Bun's `idleTimeout` (10 s), lifted while a streamed response is sent.\n */\nexport interface HonoServeOptions {\n /** Seconds a connection may stay idle (`0`: never closed). Default: 10. */\n readonly idleTimeout?: number\n /** Request bodies above this size are refused by Bun (413). Default: unlimited. */\n readonly maxRequestBodySize?: number\n /** TLS options (`{ key, cert }`). */\n readonly tls?: unknown\n /** Any other `Bun.serve()` option. */\n readonly [option: string]: unknown\n}\n\n/** The Hono adapter. */\nexport interface HonoAdapter extends HttpAdapter {\n readonly name: 'hono'\n /** The Hono application. */\n readonly app: Hono\n /** Fetch handler of the application (`export default { fetch: adapter.fetch }` on edge runtimes). */\n readonly fetch: (request: Request, env?: unknown) => Response | Promise<Response>\n}\n\n/**\n * Creates a Hono adapter.\n *\n * ```ts\n * import { honoAdapter } from '@znight/http/hono'\n *\n * HttpPlugin.configure({ adapter: honoAdapter() })\n * ```\n */\nexport function honoAdapter(options: HonoAdapterOptions = {}): HonoAdapter {\n return new HonoHttpAdapter(options)\n}\n\n/** The parts of a Bun server used here (Bun types are not available to the package sources). */\ninterface BunServer {\n readonly port: number\n readonly hostname: string\n stop(closeActiveConnections?: boolean): Promise<void> | void\n requestIP?(request: Request): { readonly address: string } | null\n /** Idle timeout of one request, in seconds (`0`: none). */\n timeout?(request: Request, seconds: number): void\n}\n\ninterface BunRuntime {\n serve(\n options: HonoServeOptions & {\n port: number\n hostname: string\n fetch: (request: Request, server: BunServer) => Response | undefined | Promise<Response | undefined>\n websocket?: unknown\n },\n ): BunServer\n}\n\n/**\n * The upgrade capability of the Hono adapter (RFC 0003): `Bun.serve()` upgrades on Bun (checked in\n * the fetch handler before Hono routing), `node:http` upgrades of `@hono/node-server` on Node.js.\n */\nclass HonoUpgrades implements AdapterUpgrades {\n readonly kind: 'node' | 'bun' = isBun() ? 'bun' : 'node'\n readonly node: NodeUpgrades = new NodeUpgrades()\n options: UpgradeHandlerOptions | undefined\n\n enable(options: UpgradeHandlerOptions): void {\n this.options = options\n this.node.enable(options)\n }\n}\n\n/** Default `idleTimeout` of `Bun.serve()`, in seconds. */\nconst BUN_IDLE_TIMEOUT = 10\n\n/**\n * A streamed response body built for a request of `Bun.serve()` (see `#bunStream`); with middleware,\n * pending until `#bunFetch` takes it or it ends.\n */\nclass BunBody {\n /** The `Blob` (file) the body reads. */\n readonly blob: Blob | undefined\n #settle: (() => void) | undefined\n #onEnd: (() => void) | undefined\n #ended = false\n\n /** `settle` is called once, when the body is taken by `#bunFetch` or ends, whichever comes first. */\n constructor(blob: Blob | undefined, settle: (() => void) | undefined) {\n this.blob = blob\n this.#settle = settle\n }\n\n /** Whether the body ended (read to its end, failed or cancelled). */\n get ended(): boolean {\n return this.#ended\n }\n\n /** The body is no longer pending (taken by `#bunFetch`). */\n settle(): void {\n const settle = this.#settle\n this.#settle = undefined\n settle?.()\n }\n\n /** Sets the function called when the body ends. */\n onEnd(callback: () => void): void {\n this.#onEnd = callback\n }\n\n /** The body ended (read to its end, failed or cancelled). */\n end(): void {\n if (this.#ended) return\n this.#ended = true\n this.settle()\n this.#onEnd?.()\n }\n}\n\n/** Response bodies built while serving with `Bun.serve()`, by body (Hono may copy a `Response`, not its body). */\nconst BUN_BODIES = new WeakMap<ReadableStream<Uint8Array>, BunBody>()\n\n/** The Node.js request behind a request of `@hono/node-server`. */\ninterface NodeIncoming {\n readonly url?: string\n readonly socket?: { readonly encrypted?: unknown }\n readonly httpVersionMajor: number\n readonly stream?: { readonly endAfterHeaders?: boolean }\n}\n\n/** Bindings of injected requests (the remote address the other adapters report for `inject()`). */\nconst INJECT_ENV = Object.freeze({ incoming: Object.freeze({ socket: Object.freeze({ remoteAddress: '127.0.0.1' }) }) })\n\ntype HonoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'\n\n/** The handlers of the GET and HEAD routes of one path. */\ninterface ReadHandlers {\n get: AdapterHandler | undefined\n head: AdapterHandler | undefined\n}\n\nclass HonoHttpAdapter implements HonoAdapter {\n readonly name = 'hono' as const\n readonly app: Hono\n /** WebSocket upgrades (RFC 0003). */\n readonly upgrades: HonoUpgrades = new HonoUpgrades()\n readonly #serveOptions: HonoServeOptions\n /** Number of routes of `app` registered by the adapter (see {@link #middleware}). */\n #routes = 0\n /** The GET and HEAD handlers of each path shape, served by one route of `app` (see {@link registerRoute}). */\n readonly #reads = new Map<string, ReadHandlers>()\n #fallback: FallbackHandler | undefined\n #prepared = false\n #bun: BunServer | undefined\n /**\n * Streamed bodies of `Bun.serve()` replies (with middleware) neither taken by `#bunFetch` nor ended\n * (see {@link BunBody}): while there is none, `#bunFetch` returns responses untouched (reading\n * `response.body` would turn a text body into a stream that Bun then sends as one).\n */\n #pendingBodies = 0\n /** Idle timeout of the `Bun.serve()` server, in seconds (`0`: none). */\n #idleTimeout = BUN_IDLE_TIMEOUT\n #node: Server | undefined\n #tracker: ConnectionTracker | undefined\n #closing: Promise<void> | undefined\n\n constructor(options: HonoAdapterOptions) {\n this.app = options.app ?? new Hono()\n this.#serveOptions = options.serveOptions ?? {}\n }\n\n readonly fetch = (request: Request, env?: unknown): Response | Promise<Response> => {\n this.#prepare()\n return this.app.fetch(request, env)\n }\n\n registerRoute(route: AdapterRoute): void {\n const handler = route.handler\n const path = toHonoPath(route.path)\n if (route.method === 'GET' || route.method === 'HEAD') {\n // Hono routes HEAD requests to GET handlers: one GET handler per path serves both methods (the\n // plugin registers HEAD routes first), so that a request matches a single handler and Hono\n // skips its middleware composition. Paths are keyed by their shape (`/users/:` for\n // `/users/:id` and `/users/:userId`): a GET route joins the HEAD route matching the same\n // requests, at its rank (Znight reads the parameters itself, never Hono's names)\n const existing = this.#reads.get(route.path.shape)\n if (existing !== undefined) {\n if (route.method === 'GET') existing.get = handler\n else existing.head = handler\n return\n }\n const reads: ReadHandlers =\n route.method === 'GET' ? { get: handler, head: undefined } : { get: undefined, head: handler }\n this.#reads.set(route.path.shape, reads)\n this.#routes++\n this.app.get(path, (c: Context, next: Next) => {\n const read = c.req.raw.method === 'HEAD' ? reads.head : reads.get\n return read === undefined ? next().then(() => c.res) : this.#serve(c, read)\n })\n return\n }\n this.#routes++\n this.app.on(route.method as HonoMethod, path, (c: Context) => this.#serve(c, handler))\n }\n\n setFallbackHandler(handler: FallbackHandler): void {\n this.#fallback = handler\n }\n\n #prepare(): void {\n if (this.#prepared) return\n this.#prepared = true\n this.app.notFound(c => this.#serveFallback(c, undefined))\n this.app.onError((error, c) => this.#serveFallback(c, error))\n }\n\n async #serve(c: Context, handler: AdapterHandler): Promise<Response> {\n const request = this.#request(c)\n // Hono routes the WHATWG-normalised URL (`/a/../b` → `/b`, `%2e%2e` resolved): when the raw\n // target differs (Node.js; on Bun only the normalised URL is known), let the plugin route the raw\n // path, like on the other adapters\n const reply =\n this.#fallback !== undefined && request.rawTarget && pathOf(request.url) !== pathOf(pathAndQuery(c.req.raw.url))\n ? await this.#fallback(request)\n : await handler(request)\n return this.#toResponse(c, reply)\n }\n\n async #serveFallback(c: Context, error: unknown): Promise<Response> {\n if (this.#fallback === undefined) throw error ?? new Error('No fallback handler')\n return this.#toResponse(c, await this.#fallback(this.#request(c), error))\n }\n\n #request(c: Context): HonoAdapterRequest {\n return new HonoAdapterRequest(c, this.#servedByBun(c) ? this.#bun : undefined)\n }\n\n /** Whether the request of `c` was received by the `Bun.serve()` server of the adapter. */\n #servedByBun(c: Context): boolean {\n return this.#bun !== undefined && c.env === this.#bun\n }\n\n /**\n * Whether `app` has routes the adapter did not register (middleware, other routes): only they can\n * set headers on the context or read the response.\n */\n #middleware(): boolean {\n return this.app.routes.length !== this.#routes\n }\n\n #toResponse(c: Context, reply: AdapterReply): Response {\n const middleware = this.#middleware()\n if (this.#servedByBun(c)) {\n return reply.body instanceof ReadableStream\n ? this.#bunStream(c, reply, middleware)\n : toResponse(c, reply, middleware)\n }\n const outgoing = nodeOutgoing(c)\n if (outgoing !== undefined) {\n const body = reply.body instanceof ReadableStream ? reply.body : null\n if (outgoing.destroyed) {\n // @hono/node-server: the client left before the reply was ready (an async pre-flight). The\n // server would neither read nor cancel the body: release it (event source, request scope)\n discardStream(body)\n return toResponse(c, { ...reply, body: null }, middleware)\n }\n if (body !== null) {\n // the client may leave before @hono/node-server reads the body (its own listener then comes\n // too late): a body nobody locked is cancelled when the response closes unfinished\n outgoing.once('close', () => {\n if (!outgoing.writableFinished) discardStream(body)\n })\n }\n // a quiet event stream must outlive a socket timeout\n if (reply.eventStream === true && typeof outgoing.setTimeout === 'function') outgoing.setTimeout(0)\n }\n return toResponse(c, reply, middleware)\n }\n\n /**\n * The response of a streamed reply to a request of `Bun.serve()`: the end of its body is observed\n * (idle timeout, native blob: see {@link #bunSend}).\n */\n #bunStream(c: Context, reply: AdapterReply, middleware: boolean): Response {\n const reader = (reply.body as ReadableStream<Uint8Array>).getReader()\n const served = new BunBody(\n reply.blob,\n middleware\n ? () => {\n this.#pendingBodies--\n }\n : undefined,\n )\n const body = new ReadableStream<Uint8Array>(\n {\n async pull(controller) {\n let result: Awaited<ReturnType<typeof reader.read>>\n try {\n result = await reader.read()\n } catch (error) {\n served.end()\n throw error\n }\n if (result.done) {\n served.end() // before closing: Bun ignores timeout() once the response is complete\n controller.close()\n return\n }\n controller.enqueue(result.value)\n },\n cancel(reason) {\n served.end()\n return reader.cancel(reason)\n },\n },\n { highWaterMark: 0 },\n )\n const response = toResponse(c, { ...reply, body }, middleware)\n if (!middleware) {\n // nothing else handles the request (see the fetch handler given to Bun.serve()): Bun sends this\n // response, unless it does not carry the body (an invalid header: the body was discarded)\n return served.ended ? response : this.#bunSend(response, body, served, c.req.raw)\n }\n // middleware may copy the response (not its body) or replace it: #bunFetch finds the body\n this.#pendingBodies++\n BUN_BODIES.set(body, served)\n return response\n }\n\n /** The fetch handler given to `Bun.serve()` when `app` has middleware (see {@link #bunStream}). */\n async #bunFetch(request: Request, server: BunServer): Promise<Response> {\n const response = await this.app.fetch(request, server)\n // only a streamed reply needs more: leave the other responses untouched (see #pendingBodies)\n if (this.#pendingBodies === 0) return response\n const body = response.body\n const served = body === null ? undefined : BUN_BODIES.get(body)\n if (body === null || served === undefined) return response\n BUN_BODIES.delete(body)\n served.settle()\n return this.#bunSend(response, body, served, request)\n }\n\n /**\n * The response Bun sends for a streamed reply, once final: a file `Blob` is sent natively, a\n * stream with the idle timeout of the connection lifted.\n */\n #bunSend(response: Response, body: ReadableStream<Uint8Array>, served: BunBody, request: Request): Response {\n if (served.blob !== undefined && !body.locked) {\n // a Blob is sent by Bun itself (sendfile, exact Content-Length; a stream would be chunked). Bun\n // opens its file as soon as it takes the response: release the stream (and with it the request\n // scope, which may delete the file) right after\n const native = new Response(served.blob, {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n })\n setImmediate(() => discardStream(body))\n return native\n }\n const server = this.#bun\n const idleTimeout = this.#idleTimeout\n if (idleTimeout > 0 && !served.ended && typeof server?.timeout === 'function') {\n // the idle timeout of Bun.serve() also cuts a response whose stream pauses (SSE, slow producers):\n // lift it while the stream is sent, then restore it for the keep-alive connection (a body that\n // already ended would never restore it)\n server.timeout(request, 0)\n served.onEnd(() => {\n try {\n server.timeout?.(request, idleTimeout)\n } catch {\n // the connection is gone\n }\n })\n }\n return response\n }\n\n /**\n * A WebSocket upgrade of a gateway path received by `Bun.serve()`, run before Hono routing:\n * `undefined` for any other request; otherwise the refusal, or `undefined` once upgraded.\n */\n #bunUpgrade(\n options: UpgradeHandlerOptions,\n request: Request,\n server: BunServer,\n ): Promise<Response | undefined> | undefined {\n const upgrade = request.headers.get('upgrade')\n if (upgrade === null || !isWebSocketUpgrade({ upgrade })) return undefined\n const url = pathAndQuery(request.url)\n if (!options.match(pathOf(url))) return undefined\n const address = server.requestIP?.(request)?.address ?? undefined\n const adapterRequest = new FetchUpgradeRequest(request, url, fetchHeaders(request.headers), address)\n return options.handle(adapterRequest, { kind: 'bun', request, server }).then(\n reply => (reply === undefined ? undefined : refusalResponse(reply)),\n () => new Response(null, { status: 500 }),\n )\n }\n\n async listen(port: number, host: string): Promise<HttpServerAddress> {\n this.#prepare()\n this.#closing = undefined\n const bun = (globalThis as { Bun?: BunRuntime }).Bun\n if (isBun() && bun !== undefined) {\n const options = this.#serveOptions\n this.#idleTimeout = typeof options.idleTimeout === 'number' ? options.idleTimeout : BUN_IDLE_TIMEOUT\n const upgrades = this.upgrades.options\n const server = bun.serve({\n // Znight enforces the body limits (route @BodyLimit, plugin bodyLimit): not Bun's 128 MiB default\n maxRequestBodySize: Number.MAX_SAFE_INTEGER,\n ...options,\n ...(upgrades === undefined ? {} : { websocket: bunWebSocketOption(upgrades.bunWebSocket) }),\n port,\n hostname: host,\n // without middleware, the response of a Znight route is the one Bun sends (no wrapper needed)\n fetch: (request, server) =>\n (upgrades === undefined ? undefined : this.#bunUpgrade(upgrades, request, server)) ??\n (this.#middleware() ? this.#bunFetch(request, server) : this.app.fetch(request, server)),\n })\n this.#bun = server\n return serverAddress(server.hostname, server.port)\n }\n let nodeServer: typeof import('@hono/node-server')\n try {\n nodeServer = await import('@hono/node-server')\n } catch (error) {\n throw new Error(\n 'honoAdapter on Node.js requires the optional peer dependency \"@hono/node-server\" (npm i @hono/node-server)',\n { cause: error },\n )\n }\n const server = nodeServer.createAdaptorServer({ fetch: this.app.fetch }) as Server\n this.upgrades.node.attach(server)\n this.#node = server\n this.#tracker = new ConnectionTracker(server)\n await new Promise<void>((resolve, reject) => {\n const onError = (error: Error): void => reject(error)\n server.once('error', onError)\n server.listen(port, host, () => {\n server.off('error', onError)\n resolve()\n })\n })\n const address = server.address()\n if (address === null || typeof address === 'string') return serverAddress(host, port)\n return serverAddress(address.address, address.port, address.family)\n }\n\n close(options: AdapterCloseOptions = {}): Promise<void> {\n const bun = this.#bun\n if (bun !== undefined) {\n if (options.force === true) {\n this.#closing = Promise.resolve(bun.stop(true))\n } else {\n this.#closing ??= Promise.resolve(bun.stop(false))\n }\n return this.#closing\n }\n const server = this.#node\n const tracker = this.#tracker\n if (server === undefined || tracker === undefined) return Promise.resolve()\n this.#closing = closeServer(server, tracker, this.#closing, options.force === true)\n return this.#closing\n }\n\n inject(request: InjectRequest): Promise<InjectResponse> {\n return bufferInjectStream(stream => this.injectStream(stream), request)\n }\n\n injectStream(request: InjectStreamRequest): Promise<InjectStreamResponse> {\n this.#prepare()\n return injectFetchStream(request => this.app.request(request, undefined, INJECT_ENV), request)\n }\n}\n\n/** A Hono request as an {@link AdapterRequest} (a class: no closure per request). */\nclass HonoAdapterRequest implements AdapterRequest {\n readonly method: string\n readonly url: string\n readonly headers: Readonly<Record<string, string | undefined>>\n readonly ip: string | undefined\n readonly remoteAddress: string | undefined\n readonly encrypted: boolean\n readonly raw: Request\n readonly rawResponse: Context\n /** `url` is the raw request target (Node.js), not the WHATWG-normalised URL of `raw`. */\n readonly rawTarget: boolean\n /** The Node.js request of `@hono/node-server`, absent on Bun and for `inject()`. */\n readonly #incoming: NodeIncoming | undefined\n /**\n * The same request over HTTP/1.1, read directly (Node.js streams): no Fetch `Request` and body\n * stream are built for it.\n */\n readonly #http1: IncomingMessage | undefined\n /** Received by the `Bun.serve()` server of the adapter: its body is framed by the HTTP parser. */\n readonly #bun: boolean\n #body: Promise<Uint8Array> | undefined\n\n constructor(c: Context, bun: BunServer | undefined) {\n const request = c.req.raw\n this.method = request.method\n this.raw = request\n this.rawResponse = c\n this.#bun = bun !== undefined\n if (bun !== undefined) {\n // Bun.serve(): only the normalised URL is known; the address is asked now (Bun no longer knows\n // it once the response is sent)\n const url = request.url\n const address = bun.requestIP?.(request)?.address ?? undefined\n this.url = pathAndQuery(url)\n this.rawTarget = false\n this.headers = fetchHeaders(request.headers)\n this.ip = address\n this.remoteAddress = address\n this.encrypted = url.startsWith('https:')\n this.#incoming = undefined\n this.#http1 = undefined\n return\n }\n const incoming = nodeIncoming(c)\n const http1 = incoming !== undefined && incoming.httpVersionMajor < 2 ? (incoming as IncomingMessage) : undefined\n const address = remoteAddress(c)\n const target = incoming?.url\n // the raw target on Node.js; for a Web request only the normalised URL is known\n this.rawTarget = typeof target === 'string' && target !== ''\n this.url = pathAndQuery(this.rawTarget ? (target as string) : request.url)\n this.headers = (http1 === undefined ? undefined : distinctHeaders(http1)) ?? fetchHeaders(request.headers)\n this.ip = address\n this.remoteAddress = address\n this.encrypted = incoming === undefined ? request.url.startsWith('https:') : incoming.socket?.encrypted === true\n this.#incoming = incoming\n this.#http1 = http1\n }\n\n readBody(limit: number): Promise<Uint8Array> {\n if (this.#body === undefined) {\n const http1 = this.#http1\n if (http1 !== undefined) this.#body = readNodeBody(http1, limit)\n else if (this.#bun && this.#framed()) this.#body = readFramedBody(this.raw, limit, this.headers['content-length'])\n else this.#body = readWebStream(() => this.raw.body, limit, this.headers['content-length'])\n }\n return this.#body\n }\n\n /**\n * Whether the whole body can be read at once from `Bun.serve()`: its length is declared (the\n * HTTP parser then reads no more) and nothing read it yet.\n */\n #framed(): boolean {\n const headers = this.headers\n return headers['content-length'] !== undefined && headers['transfer-encoding'] === undefined && !this.raw.bodyUsed\n }\n\n streamBody(limit: number): ReadableStream<Uint8Array> {\n const http1 = this.#http1\n if (http1 !== undefined) return nodeBodyStream(http1, limit)\n return webBodyStream(() => this.raw.body, limit, this.headers['content-length'])\n }\n\n // asked only without content-length / transfer-encoding: a request read from an HTTP/1.1\n // connection then has no body (`@hono/node-server` still gives every POST a body stream), an\n // HTTP/2 one unless its HEADERS frame ended the stream; a Web `Request` (Bun, inject) says it\n hasBody(): boolean {\n const incoming = this.#incoming\n return incoming === undefined\n ? this.raw.body !== null\n : incoming.httpVersionMajor >= 2 && incoming.stream?.endAfterHeaders === false\n }\n\n onAbort(listener: () => void): void {\n const outgoing = this.#bun ? undefined : nodeOutgoing(this.rawResponse)\n if (outgoing !== undefined) {\n onResponseAbort(outgoing as unknown as ServerResponse, listener)\n return\n }\n // Bun.serve() and inject(): the request signal aborts when the client leaves\n const signal = this.raw.signal\n if (signal.aborted) queueMicrotask(listener)\n else signal.addEventListener('abort', listener, { once: true })\n }\n}\n\n/** The headers of a Fetch request: lower-case names, repeated headers joined (`Headers` semantics). */\nfunction fetchHeaders(source: Headers): Record<string, string> {\n const headers: Record<string, string> = {}\n // forEach: no iterator nor entry array per header\n source.forEach((value, name) => {\n headers[name] = value\n })\n return headers\n}\n\n/**\n * Reads at once (`request.bytes()`, no stream) the body of a request received by `Bun.serve()`\n * whose `content-length` is declared: Bun reads exactly that many bytes, so checking the declared\n * length enforces `limit`. Failures are those of {@link readWebStream}: 413 above `limit`, 400 when\n * the client aborts.\n */\nasync function readFramedBody(request: Request, limit: number, contentLength: string | undefined): Promise<Uint8Array> {\n if (exceedsDeclaredLength(contentLength, limit)) throw payloadTooLarge(limit)\n let bytes: Uint8Array\n try {\n bytes = await request.bytes()\n } catch (error) {\n throw new BadRequestException('Failed to read the request body', undefined, { cause: error })\n }\n if (bytes.byteLength > limit) throw payloadTooLarge(limit)\n return bytes\n}\n\n/**\n * The headers of a Node.js request when none is repeated: then they hold the values of its Fetch\n * `Headers` (Node.js keeps the first of some repeated headers where `Headers` join them all).\n */\nfunction distinctHeaders(request: IncomingMessage): Record<string, string | undefined> | undefined {\n const headers = request.headers\n let count = 0\n for (const name in headers) {\n if (typeof headers[name] !== 'string') return undefined\n count++\n }\n return count * 2 === request.rawHeaders.length ? (headers as Record<string, string | undefined>) : undefined\n}\n\n/** The part of a Node.js `ServerResponse` used here. */\ninterface NodeOutgoing {\n readonly writableFinished: boolean\n readonly destroyed: boolean\n once(event: 'close', listener: () => void): unknown\n setTimeout?(ms: number): unknown\n}\n\n/** The Node.js response of `@hono/node-server` (`c.env.outgoing`), absent on Bun and for `inject()`. */\nfunction nodeOutgoing(c: Context): NodeOutgoing | undefined {\n const outgoing = (c.env as { outgoing?: Partial<NodeOutgoing> } | undefined)?.outgoing\n return typeof outgoing?.once === 'function' ? (outgoing as NodeOutgoing) : undefined\n}\n\n/** The Node.js request of `@hono/node-server` (`c.env.incoming`), absent on Bun and for `inject()`. */\nfunction nodeIncoming(c: Context): NodeIncoming | undefined {\n const incoming = (c.env as { incoming?: Partial<NodeIncoming> } | undefined)?.incoming\n return typeof incoming?.httpVersionMajor === 'number' ? (incoming as NodeIncoming) : undefined\n}\n\nfunction pathAndQuery(url: string): string {\n if (url.startsWith('/')) return url\n const scheme = url.indexOf('://')\n const start = url.indexOf('/', scheme === -1 ? 0 : scheme + 3)\n return start === -1 ? '/' : url.slice(start)\n}\n\nfunction pathOf(url: string): string {\n const query = url.indexOf('?')\n return query === -1 ? url : url.slice(0, query)\n}\n\nfunction remoteAddress(c: Context): string | undefined {\n const env = c.env as\n | { incoming?: { socket?: { remoteAddress?: string } }; requestIP?: BunServer['requestIP'] }\n | undefined\n if (env === undefined || env === null) return undefined\n if (typeof env.requestIP === 'function') return env.requestIP(c.req.raw)?.address ?? undefined\n return env.incoming?.socket?.remoteAddress\n}\n\n/**\n * The `Response` of a reply. Without `middleware` (see `#middleware`), nothing set headers on the\n * context and nothing reads the response: headers given as a record of strings are sent as is by\n * `@hono/node-server` (no `Headers` object), and a text body as a string.\n */\nfunction toResponse(c: Context, reply: AdapterReply, middleware: boolean): Response {\n if (reply.raw === true) {\n discardStream(reply.body instanceof ReadableStream ? reply.body : null)\n return c.res\n }\n if (!middleware && isStringRecord(reply.headers)) {\n return new Response(reply.text ?? reply.body, {\n status: reply.status,\n headers: reply.headers as Readonly<Record<string, string>>,\n })\n }\n const headers = new Headers()\n try {\n for (const name of Object.keys(reply.headers)) {\n const value = reply.headers[name]\n if (typeof value === 'string') headers.set(name, value)\n else if (value !== undefined) for (const item of value) headers.append(name, item)\n }\n } catch {\n // a header Headers refuses (the plugin validates them): never rethrow into Hono's onError, which\n // would run the pipeline again; release the body and answer a plain 500, like writeNodeReply\n discardStream(reply.body instanceof ReadableStream ? reply.body : null)\n return new Response('{\"statusCode\":500,\"error\":\"Internal Server Error\"}', {\n status: 500,\n headers: { 'content-type': 'application/json; charset=utf-8' },\n })\n }\n if (middleware) keepContextCookies(c, headers)\n return new Response(reply.text ?? reply.body, { status: reply.status, headers })\n}\n\n/** Whether every header has a single value (a record of strings is a valid `HeadersInit`). */\nfunction isStringRecord(headers: Readonly<Record<string, HeaderValue>>): boolean {\n // Object.keys: faster than for...in on the null-prototype records of the pipeline (V8)\n const names = Object.keys(headers)\n for (let index = 0; index < names.length; index++) {\n if (typeof headers[names[index] as string] !== 'string') return false\n }\n return true\n}\n\n/**\n * Keeps the cookies Hono middleware set before the Znight route (`setCookie(c, ...)`,\n * `c.header('set-cookie', ..., { append: true })`) before the reply's own, like the other adapters.\n * Hono drops the headers of the context when a handler returns its own `Response` (or, when a\n * middleware read `c.res`, replaces the response's cookies with the context's): the reply's cookies\n * are added to the context too, so that both ways end with every cookie, in order.\n */\nfunction keepContextCookies(c: Context, headers: Headers): void {\n let context: string[]\n try {\n // the headers of the context without changing it (`c.res` would create the context response)\n context = c.newResponse(null).headers.getSetCookie()\n } catch {\n return // a status a bodyless Response refuses (`c.status(101)`): nothing to keep\n }\n if (context.length === 0) return\n const own = headers.getSetCookie()\n for (const line of own) c.header('set-cookie', line, { append: true })\n headers.delete('set-cookie')\n for (const line of context) headers.append('set-cookie', line)\n for (const line of own) headers.append('set-cookie', line)\n}\n"],"mappings":"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"}
|