@znight/http 0.1.1 → 0.1.3
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/LICENSE +1 -1
- package/README.md +83 -32
- package/dist/{adapter-CGSceVEN.d.mts → adapter-J0UXFnNM.d.mts} +7 -1
- package/dist/compiler.d.mts +1 -1
- package/dist/compiler.mjs +82 -13
- package/dist/compiler.mjs.map +1 -1
- package/dist/express.d.mts +1 -1
- package/dist/express.mjs +3 -2
- package/dist/express.mjs.map +1 -1
- package/dist/fastify.d.mts +1 -1
- package/dist/fastify.mjs +254 -42
- package/dist/fastify.mjs.map +1 -1
- package/dist/hono.d.mts +1 -1
- package/dist/hono.mjs +272 -67
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +10 -2
- package/dist/index.mjs +930 -278
- package/dist/index.mjs.map +1 -1
- package/dist/{node-BUnkdSsa.mjs → node-DnYv_AMI.mjs} +4 -3
- package/dist/{node-BUnkdSsa.mjs.map → node-DnYv_AMI.mjs.map} +1 -1
- package/dist/{node-io-BY1ZDLRQ.mjs → node-io-BkdRSQeg.mjs} +151 -86
- package/dist/{node-io-BY1ZDLRQ.mjs.map → node-io-BkdRSQeg.mjs.map} +1 -1
- package/dist/node.d.mts +1 -1
- package/dist/node.mjs +1 -1
- package/dist/utils-DIWdzVht.mjs +46 -0
- package/dist/utils-DIWdzVht.mjs.map +1 -0
- package/package.json +4 -4
- package/src/adapter.ts +6 -0
- package/src/adapters/express.ts +4 -1
- package/src/adapters/fastify.ts +329 -44
- package/src/adapters/hono.ts +341 -86
- package/src/adapters/node-io.ts +185 -82
- package/src/adapters/node.ts +4 -1
- package/src/body.ts +31 -2
- package/src/coerce.ts +31 -0
- package/src/compiler/generate.ts +127 -22
- package/src/context.ts +151 -78
- package/src/index.ts +1 -1
- package/src/pipeline.ts +809 -208
- package/src/plugin.ts +10 -4
- package/src/query.ts +7 -1
- package/src/response.ts +6 -8
- package/src/serialize.ts +83 -16
- package/src/utils.ts +25 -2
- package/src/validate.ts +28 -12
- package/dist/utils-DCbu3a4w.mjs +0 -27
- package/dist/utils-DCbu3a4w.mjs.map +0 -1
package/dist/hono.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { F as discardStream, S as webBodyStream, V as BadRequestException, a as nodeBodyStream, b as readWebStream, c as readNodeBody, f as bufferInjectStream, m as injectFetchStream, n as closeServer, p as exceedsDeclaredLength, s as onResponseAbort, t as ConnectionTracker, x as serverAddress, y as payloadTooLarge } from "./node-io-BkdRSQeg.mjs";
|
|
2
|
+
import { i as isBun } from "./utils-DIWdzVht.mjs";
|
|
3
3
|
import { toHonoPath } from "./path.mjs";
|
|
4
4
|
import { Hono } from "hono";
|
|
5
5
|
// src/adapters/hono.ts
|
|
@@ -17,6 +17,43 @@ function honoAdapter(options = {}) {
|
|
|
17
17
|
}
|
|
18
18
|
/** Default `idleTimeout` of `Bun.serve()`, in seconds. */
|
|
19
19
|
const BUN_IDLE_TIMEOUT = 10;
|
|
20
|
+
/**
|
|
21
|
+
* A streamed response body built for a request of `Bun.serve()` (see `#bunStream`); with middleware,
|
|
22
|
+
* pending until `#bunFetch` takes it or it ends.
|
|
23
|
+
*/
|
|
24
|
+
var BunBody = class {
|
|
25
|
+
/** The `Blob` (file) the body reads. */
|
|
26
|
+
blob;
|
|
27
|
+
#settle;
|
|
28
|
+
#onEnd;
|
|
29
|
+
#ended = false;
|
|
30
|
+
/** `settle` is called once, when the body is taken by `#bunFetch` or ends, whichever comes first. */
|
|
31
|
+
constructor(blob, settle) {
|
|
32
|
+
this.blob = blob;
|
|
33
|
+
this.#settle = settle;
|
|
34
|
+
}
|
|
35
|
+
/** Whether the body ended (read to its end, failed or cancelled). */
|
|
36
|
+
get ended() {
|
|
37
|
+
return this.#ended;
|
|
38
|
+
}
|
|
39
|
+
/** The body is no longer pending (taken by `#bunFetch`). */
|
|
40
|
+
settle() {
|
|
41
|
+
const settle = this.#settle;
|
|
42
|
+
this.#settle = void 0;
|
|
43
|
+
settle?.();
|
|
44
|
+
}
|
|
45
|
+
/** Sets the function called when the body ends. */
|
|
46
|
+
onEnd(callback) {
|
|
47
|
+
this.#onEnd = callback;
|
|
48
|
+
}
|
|
49
|
+
/** The body ended (read to its end, failed or cancelled). */
|
|
50
|
+
end() {
|
|
51
|
+
if (this.#ended) return;
|
|
52
|
+
this.#ended = true;
|
|
53
|
+
this.settle();
|
|
54
|
+
this.#onEnd?.();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
20
57
|
/** Response bodies built while serving with `Bun.serve()`, by body (Hono may copy a `Response`, not its body). */
|
|
21
58
|
const BUN_BODIES = /* @__PURE__ */ new WeakMap();
|
|
22
59
|
/** Bindings of injected requests (the remote address the other adapters report for `inject()`). */
|
|
@@ -25,9 +62,21 @@ var HonoHttpAdapter = class {
|
|
|
25
62
|
name = "hono";
|
|
26
63
|
app;
|
|
27
64
|
#serveOptions;
|
|
65
|
+
/** Number of routes of `app` registered by the adapter (see {@link #middleware}). */
|
|
66
|
+
#routes = 0;
|
|
67
|
+
/** The GET and HEAD handlers of each path shape, served by one route of `app` (see {@link registerRoute}). */
|
|
68
|
+
#reads = /* @__PURE__ */ new Map();
|
|
28
69
|
#fallback;
|
|
29
70
|
#prepared = false;
|
|
30
71
|
#bun;
|
|
72
|
+
/**
|
|
73
|
+
* Streamed bodies of `Bun.serve()` replies (with middleware) neither taken by `#bunFetch` nor ended
|
|
74
|
+
* (see {@link BunBody}): while there is none, `#bunFetch` returns responses untouched (reading
|
|
75
|
+
* `response.body` would turn a text body into a stream that Bun then sends as one).
|
|
76
|
+
*/
|
|
77
|
+
#pendingBodies = 0;
|
|
78
|
+
/** Idle timeout of the `Bun.serve()` server, in seconds (`0`: none). */
|
|
79
|
+
#idleTimeout = BUN_IDLE_TIMEOUT;
|
|
31
80
|
#node;
|
|
32
81
|
#tracker;
|
|
33
82
|
#closing;
|
|
@@ -42,10 +91,29 @@ var HonoHttpAdapter = class {
|
|
|
42
91
|
registerRoute(route) {
|
|
43
92
|
const handler = route.handler;
|
|
44
93
|
const path = toHonoPath(route.path);
|
|
45
|
-
if (route.method === "HEAD") {
|
|
46
|
-
|
|
94
|
+
if (route.method === "GET" || route.method === "HEAD") {
|
|
95
|
+
const existing = this.#reads.get(route.path.shape);
|
|
96
|
+
if (existing !== void 0) {
|
|
97
|
+
if (route.method === "GET") existing.get = handler;
|
|
98
|
+
else existing.head = handler;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const reads = route.method === "GET" ? {
|
|
102
|
+
get: handler,
|
|
103
|
+
head: void 0
|
|
104
|
+
} : {
|
|
105
|
+
get: void 0,
|
|
106
|
+
head: handler
|
|
107
|
+
};
|
|
108
|
+
this.#reads.set(route.path.shape, reads);
|
|
109
|
+
this.#routes++;
|
|
110
|
+
this.app.get(path, (c, next) => {
|
|
111
|
+
const read = c.req.raw.method === "HEAD" ? reads.head : reads.get;
|
|
112
|
+
return read === void 0 ? next().then(() => c.res) : this.#serve(c, read);
|
|
113
|
+
});
|
|
47
114
|
return;
|
|
48
115
|
}
|
|
116
|
+
this.#routes++;
|
|
49
117
|
this.app.on(route.method, path, (c) => this.#serve(c, handler));
|
|
50
118
|
}
|
|
51
119
|
setFallbackHandler(handler) {
|
|
@@ -58,15 +126,31 @@ var HonoHttpAdapter = class {
|
|
|
58
126
|
this.app.onError((error, c) => this.#serveFallback(c, error));
|
|
59
127
|
}
|
|
60
128
|
async #serve(c, handler) {
|
|
61
|
-
const request =
|
|
62
|
-
const reply = this.#fallback !== void 0 && pathOf(request.url) !== pathOf(pathAndQuery(c.req.raw.url)) ? await this.#fallback(request) : await handler(request);
|
|
129
|
+
const request = this.#request(c);
|
|
130
|
+
const reply = this.#fallback !== void 0 && request.rawTarget && pathOf(request.url) !== pathOf(pathAndQuery(c.req.raw.url)) ? await this.#fallback(request) : await handler(request);
|
|
63
131
|
return this.#toResponse(c, reply);
|
|
64
132
|
}
|
|
65
133
|
async #serveFallback(c, error) {
|
|
66
134
|
if (this.#fallback === void 0) throw error ?? /* @__PURE__ */ new Error("No fallback handler");
|
|
67
|
-
return this.#toResponse(c, await this.#fallback(
|
|
135
|
+
return this.#toResponse(c, await this.#fallback(this.#request(c), error));
|
|
136
|
+
}
|
|
137
|
+
#request(c) {
|
|
138
|
+
return new HonoAdapterRequest(c, this.#servedByBun(c) ? this.#bun : void 0);
|
|
139
|
+
}
|
|
140
|
+
/** Whether the request of `c` was received by the `Bun.serve()` server of the adapter. */
|
|
141
|
+
#servedByBun(c) {
|
|
142
|
+
return this.#bun !== void 0 && c.env === this.#bun;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Whether `app` has routes the adapter did not register (middleware, other routes): only they can
|
|
146
|
+
* set headers on the context or read the response.
|
|
147
|
+
*/
|
|
148
|
+
#middleware() {
|
|
149
|
+
return this.app.routes.length !== this.#routes;
|
|
68
150
|
}
|
|
69
151
|
#toResponse(c, reply) {
|
|
152
|
+
const middleware = this.#middleware();
|
|
153
|
+
if (this.#servedByBun(c)) return reply.body instanceof ReadableStream ? this.#bunStream(c, reply, middleware) : toResponse(c, reply, middleware);
|
|
70
154
|
const outgoing = nodeOutgoing(c);
|
|
71
155
|
if (outgoing !== void 0) {
|
|
72
156
|
const body = reply.body instanceof ReadableStream ? reply.body : null;
|
|
@@ -75,61 +159,70 @@ var HonoHttpAdapter = class {
|
|
|
75
159
|
return toResponse(c, {
|
|
76
160
|
...reply,
|
|
77
161
|
body: null
|
|
78
|
-
});
|
|
162
|
+
}, middleware);
|
|
79
163
|
}
|
|
80
164
|
if (body !== null) outgoing.once("close", () => {
|
|
81
165
|
if (!outgoing.writableFinished) discardStream(body);
|
|
82
166
|
});
|
|
83
167
|
if (reply.eventStream === true && typeof outgoing.setTimeout === "function") outgoing.setTimeout(0);
|
|
84
168
|
}
|
|
85
|
-
|
|
169
|
+
return toResponse(c, reply, middleware);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The response of a streamed reply to a request of `Bun.serve()`: the end of its body is observed
|
|
173
|
+
* (idle timeout, native blob: see {@link #bunSend}).
|
|
174
|
+
*/
|
|
175
|
+
#bunStream(c, reply, middleware) {
|
|
86
176
|
const reader = reply.body.getReader();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (ended) return;
|
|
91
|
-
ended = true;
|
|
92
|
-
onEnd();
|
|
93
|
-
};
|
|
177
|
+
const served = new BunBody(reply.blob, middleware ? () => {
|
|
178
|
+
this.#pendingBodies--;
|
|
179
|
+
} : void 0);
|
|
94
180
|
const body = new ReadableStream({
|
|
95
181
|
async pull(controller) {
|
|
96
182
|
let result;
|
|
97
183
|
try {
|
|
98
184
|
result = await reader.read();
|
|
99
185
|
} catch (error) {
|
|
100
|
-
end();
|
|
186
|
+
served.end();
|
|
101
187
|
throw error;
|
|
102
188
|
}
|
|
103
189
|
if (result.done) {
|
|
104
|
-
end();
|
|
190
|
+
served.end();
|
|
105
191
|
controller.close();
|
|
106
192
|
return;
|
|
107
193
|
}
|
|
108
194
|
controller.enqueue(result.value);
|
|
109
195
|
},
|
|
110
196
|
cancel(reason) {
|
|
111
|
-
end();
|
|
197
|
+
served.end();
|
|
112
198
|
return reader.cancel(reason);
|
|
113
199
|
}
|
|
114
200
|
}, { highWaterMark: 0 });
|
|
115
|
-
|
|
116
|
-
blob: reply.blob,
|
|
117
|
-
onEnd: (callback) => {
|
|
118
|
-
onEnd = callback;
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
return toResponse(c, {
|
|
201
|
+
const response = toResponse(c, {
|
|
122
202
|
...reply,
|
|
123
203
|
body
|
|
124
|
-
});
|
|
204
|
+
}, middleware);
|
|
205
|
+
if (!middleware) return served.ended ? response : this.#bunSend(response, body, served, c.req.raw);
|
|
206
|
+
this.#pendingBodies++;
|
|
207
|
+
BUN_BODIES.set(body, served);
|
|
208
|
+
return response;
|
|
125
209
|
}
|
|
126
|
-
/** The fetch handler given to `Bun.serve()
|
|
127
|
-
async #bunFetch(request, server
|
|
210
|
+
/** The fetch handler given to `Bun.serve()` when `app` has middleware (see {@link #bunStream}). */
|
|
211
|
+
async #bunFetch(request, server) {
|
|
128
212
|
const response = await this.app.fetch(request, server);
|
|
213
|
+
if (this.#pendingBodies === 0) return response;
|
|
129
214
|
const body = response.body;
|
|
130
215
|
const served = body === null ? void 0 : BUN_BODIES.get(body);
|
|
131
216
|
if (body === null || served === void 0) return response;
|
|
132
217
|
BUN_BODIES.delete(body);
|
|
218
|
+
served.settle();
|
|
219
|
+
return this.#bunSend(response, body, served, request);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* The response Bun sends for a streamed reply, once final: a file `Blob` is sent natively, a
|
|
223
|
+
* stream with the idle timeout of the connection lifted.
|
|
224
|
+
*/
|
|
225
|
+
#bunSend(response, body, served, request) {
|
|
133
226
|
if (served.blob !== void 0 && !body.locked) {
|
|
134
227
|
const native = new Response(served.blob, {
|
|
135
228
|
status: response.status,
|
|
@@ -139,7 +232,9 @@ var HonoHttpAdapter = class {
|
|
|
139
232
|
setImmediate(() => discardStream(body));
|
|
140
233
|
return native;
|
|
141
234
|
}
|
|
142
|
-
|
|
235
|
+
const server = this.#bun;
|
|
236
|
+
const idleTimeout = this.#idleTimeout;
|
|
237
|
+
if (idleTimeout > 0 && !served.ended && typeof server?.timeout === "function") {
|
|
143
238
|
server.timeout(request, 0);
|
|
144
239
|
served.onEnd(() => {
|
|
145
240
|
try {
|
|
@@ -155,13 +250,13 @@ var HonoHttpAdapter = class {
|
|
|
155
250
|
const bun = globalThis.Bun;
|
|
156
251
|
if (isBun() && bun !== void 0) {
|
|
157
252
|
const options = this.#serveOptions;
|
|
158
|
-
|
|
253
|
+
this.#idleTimeout = typeof options.idleTimeout === "number" ? options.idleTimeout : BUN_IDLE_TIMEOUT;
|
|
159
254
|
const server = bun.serve({
|
|
160
255
|
maxRequestBodySize: Number.MAX_SAFE_INTEGER,
|
|
161
256
|
...options,
|
|
162
257
|
port,
|
|
163
258
|
hostname: host,
|
|
164
|
-
fetch: (request, server) => this.#bunFetch(request, server,
|
|
259
|
+
fetch: (request, server) => this.#middleware() ? this.#bunFetch(request, server) : this.app.fetch(request, server)
|
|
165
260
|
});
|
|
166
261
|
this.#bun = server;
|
|
167
262
|
return serverAddress(server.hostname, server.port);
|
|
@@ -208,39 +303,134 @@ var HonoHttpAdapter = class {
|
|
|
208
303
|
return injectFetchStream((request) => this.app.request(request, void 0, INJECT_ENV), request);
|
|
209
304
|
}
|
|
210
305
|
};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
306
|
+
/** A Hono request as an {@link AdapterRequest} (a class: no closure per request). */
|
|
307
|
+
var HonoAdapterRequest = class {
|
|
308
|
+
method;
|
|
309
|
+
url;
|
|
310
|
+
headers;
|
|
311
|
+
ip;
|
|
312
|
+
remoteAddress;
|
|
313
|
+
encrypted;
|
|
314
|
+
raw;
|
|
315
|
+
rawResponse;
|
|
316
|
+
/** `url` is the raw request target (Node.js), not the WHATWG-normalised URL of `raw`. */
|
|
317
|
+
rawTarget;
|
|
318
|
+
/** The Node.js request of `@hono/node-server`, absent on Bun and for `inject()`. */
|
|
319
|
+
#incoming;
|
|
320
|
+
/**
|
|
321
|
+
* The same request over HTTP/1.1, read directly (Node.js streams): no Fetch `Request` and body
|
|
322
|
+
* stream are built for it.
|
|
323
|
+
*/
|
|
324
|
+
#http1;
|
|
325
|
+
/** Received by the `Bun.serve()` server of the adapter: its body is framed by the HTTP parser. */
|
|
326
|
+
#bun;
|
|
327
|
+
#body;
|
|
328
|
+
constructor(c, bun) {
|
|
329
|
+
const request = c.req.raw;
|
|
330
|
+
this.method = request.method;
|
|
331
|
+
this.raw = request;
|
|
332
|
+
this.rawResponse = c;
|
|
333
|
+
this.#bun = bun !== void 0;
|
|
334
|
+
if (bun !== void 0) {
|
|
335
|
+
const url = request.url;
|
|
336
|
+
const address = bun.requestIP?.(request)?.address ?? void 0;
|
|
337
|
+
this.url = pathAndQuery(url);
|
|
338
|
+
this.rawTarget = false;
|
|
339
|
+
this.headers = fetchHeaders(request.headers);
|
|
340
|
+
this.ip = address;
|
|
341
|
+
this.remoteAddress = address;
|
|
342
|
+
this.encrypted = url.startsWith("https:");
|
|
343
|
+
this.#incoming = void 0;
|
|
344
|
+
this.#http1 = void 0;
|
|
345
|
+
return;
|
|
242
346
|
}
|
|
243
|
-
|
|
347
|
+
const incoming = nodeIncoming(c);
|
|
348
|
+
const http1 = incoming !== void 0 && incoming.httpVersionMajor < 2 ? incoming : void 0;
|
|
349
|
+
const address = remoteAddress(c);
|
|
350
|
+
const target = incoming?.url;
|
|
351
|
+
this.rawTarget = typeof target === "string" && target !== "";
|
|
352
|
+
this.url = pathAndQuery(this.rawTarget ? target : request.url);
|
|
353
|
+
this.headers = (http1 === void 0 ? void 0 : distinctHeaders(http1)) ?? fetchHeaders(request.headers);
|
|
354
|
+
this.ip = address;
|
|
355
|
+
this.remoteAddress = address;
|
|
356
|
+
this.encrypted = incoming === void 0 ? request.url.startsWith("https:") : incoming.socket?.encrypted === true;
|
|
357
|
+
this.#incoming = incoming;
|
|
358
|
+
this.#http1 = http1;
|
|
359
|
+
}
|
|
360
|
+
readBody(limit) {
|
|
361
|
+
if (this.#body === void 0) {
|
|
362
|
+
const http1 = this.#http1;
|
|
363
|
+
if (http1 !== void 0) this.#body = readNodeBody(http1, limit);
|
|
364
|
+
else if (this.#bun && this.#framed()) this.#body = readFramedBody(this.raw, limit, this.headers["content-length"]);
|
|
365
|
+
else this.#body = readWebStream(() => this.raw.body, limit, this.headers["content-length"]);
|
|
366
|
+
}
|
|
367
|
+
return this.#body;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Whether the whole body can be read at once from `Bun.serve()`: its length is declared (the
|
|
371
|
+
* HTTP parser then reads no more) and nothing read it yet.
|
|
372
|
+
*/
|
|
373
|
+
#framed() {
|
|
374
|
+
const headers = this.headers;
|
|
375
|
+
return headers["content-length"] !== void 0 && headers["transfer-encoding"] === void 0 && !this.raw.bodyUsed;
|
|
376
|
+
}
|
|
377
|
+
streamBody(limit) {
|
|
378
|
+
const http1 = this.#http1;
|
|
379
|
+
if (http1 !== void 0) return nodeBodyStream(http1, limit);
|
|
380
|
+
return webBodyStream(() => this.raw.body, limit, this.headers["content-length"]);
|
|
381
|
+
}
|
|
382
|
+
hasBody() {
|
|
383
|
+
const incoming = this.#incoming;
|
|
384
|
+
return incoming === void 0 ? this.raw.body !== null : incoming.httpVersionMajor >= 2 && incoming.stream?.endAfterHeaders === false;
|
|
385
|
+
}
|
|
386
|
+
onAbort(listener) {
|
|
387
|
+
const outgoing = this.#bun ? void 0 : nodeOutgoing(this.rawResponse);
|
|
388
|
+
if (outgoing !== void 0) {
|
|
389
|
+
onResponseAbort(outgoing, listener);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
const signal = this.raw.signal;
|
|
393
|
+
if (signal.aborted) queueMicrotask(listener);
|
|
394
|
+
else signal.addEventListener("abort", listener, { once: true });
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
/** The headers of a Fetch request: lower-case names, repeated headers joined (`Headers` semantics). */
|
|
398
|
+
function fetchHeaders(source) {
|
|
399
|
+
const headers = {};
|
|
400
|
+
source.forEach((value, name) => {
|
|
401
|
+
headers[name] = value;
|
|
402
|
+
});
|
|
403
|
+
return headers;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Reads at once (`request.bytes()`, no stream) the body of a request received by `Bun.serve()`
|
|
407
|
+
* whose `content-length` is declared: Bun reads exactly that many bytes, so checking the declared
|
|
408
|
+
* length enforces `limit`. Failures are those of {@link readWebStream}: 413 above `limit`, 400 when
|
|
409
|
+
* the client aborts.
|
|
410
|
+
*/
|
|
411
|
+
async function readFramedBody(request, limit, contentLength) {
|
|
412
|
+
if (exceedsDeclaredLength(contentLength, limit)) throw payloadTooLarge(limit);
|
|
413
|
+
let bytes;
|
|
414
|
+
try {
|
|
415
|
+
bytes = await request.bytes();
|
|
416
|
+
} catch (error) {
|
|
417
|
+
throw new BadRequestException("Failed to read the request body", void 0, { cause: error });
|
|
418
|
+
}
|
|
419
|
+
if (bytes.byteLength > limit) throw payloadTooLarge(limit);
|
|
420
|
+
return bytes;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* The headers of a Node.js request when none is repeated: then they hold the values of its Fetch
|
|
424
|
+
* `Headers` (Node.js keeps the first of some repeated headers where `Headers` join them all).
|
|
425
|
+
*/
|
|
426
|
+
function distinctHeaders(request) {
|
|
427
|
+
const headers = request.headers;
|
|
428
|
+
let count = 0;
|
|
429
|
+
for (const name in headers) {
|
|
430
|
+
if (typeof headers[name] !== "string") return void 0;
|
|
431
|
+
count++;
|
|
432
|
+
}
|
|
433
|
+
return count * 2 === request.rawHeaders.length ? headers : void 0;
|
|
244
434
|
}
|
|
245
435
|
/** The Node.js response of `@hono/node-server` (`c.env.outgoing`), absent on Bun and for `inject()`. */
|
|
246
436
|
function nodeOutgoing(c) {
|
|
@@ -268,11 +458,20 @@ function remoteAddress(c) {
|
|
|
268
458
|
if (typeof env.requestIP === "function") return env.requestIP(c.req.raw)?.address ?? void 0;
|
|
269
459
|
return env.incoming?.socket?.remoteAddress;
|
|
270
460
|
}
|
|
271
|
-
|
|
461
|
+
/**
|
|
462
|
+
* The `Response` of a reply. Without `middleware` (see `#middleware`), nothing set headers on the
|
|
463
|
+
* context and nothing reads the response: headers given as a record of strings are sent as is by
|
|
464
|
+
* `@hono/node-server` (no `Headers` object), and a text body as a string.
|
|
465
|
+
*/
|
|
466
|
+
function toResponse(c, reply, middleware) {
|
|
272
467
|
if (reply.raw === true) {
|
|
273
468
|
discardStream(reply.body instanceof ReadableStream ? reply.body : null);
|
|
274
469
|
return c.res;
|
|
275
470
|
}
|
|
471
|
+
if (!middleware && isStringRecord(reply.headers)) return new Response(reply.text ?? reply.body, {
|
|
472
|
+
status: reply.status,
|
|
473
|
+
headers: reply.headers
|
|
474
|
+
});
|
|
276
475
|
const headers = new Headers();
|
|
277
476
|
try {
|
|
278
477
|
for (const name of Object.keys(reply.headers)) {
|
|
@@ -287,12 +486,18 @@ function toResponse(c, reply) {
|
|
|
287
486
|
headers: { "content-type": "application/json; charset=utf-8" }
|
|
288
487
|
});
|
|
289
488
|
}
|
|
290
|
-
keepContextCookies(c, headers);
|
|
291
|
-
return new Response(reply.body, {
|
|
489
|
+
if (middleware) keepContextCookies(c, headers);
|
|
490
|
+
return new Response(reply.text ?? reply.body, {
|
|
292
491
|
status: reply.status,
|
|
293
492
|
headers
|
|
294
493
|
});
|
|
295
494
|
}
|
|
495
|
+
/** Whether every header has a single value (a record of strings is a valid `HeadersInit`). */
|
|
496
|
+
function isStringRecord(headers) {
|
|
497
|
+
const names = Object.keys(headers);
|
|
498
|
+
for (let index = 0; index < names.length; index++) if (typeof headers[names[index]] !== "string") return false;
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
296
501
|
/**
|
|
297
502
|
* Keeps the cookies Hono middleware set before the Znight route (`setCookie(c, ...)`,
|
|
298
503
|
* `c.header('set-cookie', ..., { append: true })`) before the reply's own, like the other adapters.
|
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 { 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'\n\nimport { type Context, Hono, type Next } from 'hono'\n\nimport { toHonoPath } from '../path.ts'\nimport { discardStream } from '../stream.ts'\nimport { isBun } from '../utils.ts'\nimport { ConnectionTracker, closeServer, onResponseAbort } from './node-io.ts'\nimport { bufferInjectStream, injectFetchStream, readWebStream, serverAddress, webBodyStream } 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/** A response body built while serving with `Bun.serve()` (see `#bunFetch`). */\ninterface BunBody {\n /** The `Blob` (file) the body reads. */\n readonly blob: Blob | undefined\n /** Sets the function called when the body ends (read to its end, failed or cancelled). */\n onEnd(callback: () => void): void\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\nclass HonoHttpAdapter implements HonoAdapter {\n readonly name = 'hono' as const\n readonly app: Hono\n readonly #serveOptions: HonoServeOptions\n #fallback: FallbackHandler | undefined\n #prepared = false\n #bun: BunServer | undefined\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 === 'HEAD') {\n // Hono routes HEAD requests to GET handlers: serve them from a GET handler that only answers HEAD\n this.app.get(path, (c: Context, next: Next) =>\n c.req.raw.method === 'HEAD' ? this.#serve(c, handler) : next().then(() => c.res),\n )\n return\n }\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 = adapterRequest(c)\n // Hono routes the WHATWG-normalised URL (`/a/../b` → `/b`, `%2e%2e` resolved): when the raw\n // target differs (Node.js), let the plugin route the raw path, like on the other adapters\n const reply =\n this.#fallback !== undefined && 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(adapterRequest(c), error))\n }\n\n #toResponse(c: Context, reply: AdapterReply): Response {\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 })\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 if (this.#bun === undefined || !(reply.body instanceof ReadableStream)) return toResponse(c, reply)\n // served by Bun.serve(): observe the end of the body (idle timeout, native blob: see #bunFetch)\n const reader = reply.body.getReader()\n let ended = false\n let onEnd = (): void => undefined\n const end = (): void => {\n if (ended) return\n ended = true\n onEnd()\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 end()\n throw error\n }\n if (result.done) {\n 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 end()\n return reader.cancel(reason)\n },\n },\n { highWaterMark: 0 },\n )\n BUN_BODIES.set(body, {\n blob: reply.blob,\n onEnd: callback => {\n onEnd = callback\n },\n })\n return toResponse(c, { ...reply, body })\n }\n\n /** The fetch handler given to `Bun.serve()`. */\n async #bunFetch(request: Request, server: BunServer, idleTimeout: number): Promise<Response> {\n const response = await this.app.fetch(request, server)\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 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 if (idleTimeout > 0 && 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\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 const 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 fetch: (request, server) => this.#bunFetch(request, server, idleTimeout),\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\nfunction adapterRequest(c: Context): AdapterRequest {\n const request = c.req.raw\n const headers: Record<string, string> = {}\n for (const [name, value] of request.headers) headers[name] = value\n const incoming = nodeIncoming(c)\n const address = remoteAddress(c)\n let body: Promise<Uint8Array> | undefined\n return {\n method: request.method,\n // the raw target on Node.js; on Bun only the normalised URL is known\n url: pathAndQuery(typeof incoming?.url === 'string' && incoming.url !== '' ? incoming.url : request.url),\n headers,\n ip: address,\n remoteAddress: address,\n encrypted: incoming === undefined ? request.url.startsWith('https:') : incoming.socket?.encrypted === true,\n raw: request,\n rawResponse: c,\n readBody: (limit: number): Promise<Uint8Array> => {\n body ??= readWebStream(() => request.body, limit, headers['content-length'])\n return body\n },\n streamBody: (limit: number): ReadableStream<Uint8Array> =>\n webBodyStream(() => request.body, limit, headers['content-length']),\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 incoming === undefined\n ? request.body !== null\n : incoming.httpVersionMajor >= 2 && incoming.stream?.endAfterHeaders === false,\n onAbort: (listener: () => void): void => {\n const outgoing = nodeOutgoing(c)\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 = request.signal\n if (signal.aborted) queueMicrotask(listener)\n else signal.addEventListener('abort', listener, { once: true })\n },\n }\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\nfunction toResponse(c: Context, reply: AdapterReply): Response {\n if (reply.raw === true) {\n discardStream(reply.body instanceof ReadableStream ? reply.body : null)\n return c.res\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 keepContextCookies(c, headers)\n return new Response(reply.body, { status: reply.status, headers })\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'\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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"}
|