@hono/node-server 1.5.0 → 1.7.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 CHANGED
@@ -178,6 +178,40 @@ app.use(
178
178
  )
179
179
  ```
180
180
 
181
+ ## Accessing Node.js API
182
+
183
+ You can access the Node.js API from `c.env` in Node.js. For example, if you want to specify a type, you can write the following.
184
+
185
+ ```ts
186
+ import { serve } from '@hono/node-server'
187
+ import type { HttpBindings } from '@hono/node-server'
188
+ import { Hono } from 'hono'
189
+
190
+ const app = new Hono<{ Bindings: HttpBindings }>()
191
+
192
+ app.get('/', (c) => {
193
+ return c.json({
194
+ remoteAddress: c.env.incoming.socket.remoteAddress,
195
+ })
196
+ })
197
+
198
+ serve(app)
199
+ ```
200
+
201
+ The APIs that you can get from `c.env` are as follows.
202
+
203
+ ```ts
204
+ type HttpBindings = {
205
+ incoming: IncomingMessage
206
+ outgoing: ServerResponse
207
+ }
208
+
209
+ type Http2Bindings = {
210
+ incoming: Http2ServerRequest
211
+ outgoing: Http2ServerResponse
212
+ }
213
+ ```
214
+
181
215
  ## Related projects
182
216
 
183
217
  - Hono - <https://hono.dev>
package/dist/globals.js CHANGED
@@ -24,95 +24,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
 
25
25
  // src/globals.ts
26
26
  var import_node_crypto = __toESM(require("crypto"));
27
-
28
- // src/utils.ts
29
- var buildOutgoingHttpHeaders = (headers) => {
30
- const res = {};
31
- const cookies = [];
32
- for (const [k, v] of headers) {
33
- if (k === "set-cookie") {
34
- cookies.push(v);
35
- } else {
36
- res[k] = v;
37
- }
38
- }
39
- if (cookies.length > 0) {
40
- res["set-cookie"] = cookies;
41
- }
42
- res["content-type"] ??= "text/plain;charset=UTF-8";
43
- return res;
44
- };
45
-
46
- // src/response.ts
47
- var responseCache = Symbol("responseCache");
48
- var cacheKey = Symbol("cache");
49
- var GlobalResponse = global.Response;
50
- var Response = class _Response {
51
- #body;
52
- #init;
53
- get cache() {
54
- delete this[cacheKey];
55
- return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
56
- }
57
- constructor(body, init) {
58
- this.#body = body;
59
- if (init instanceof _Response) {
60
- const cachedGlobalResponse = init[responseCache];
61
- if (cachedGlobalResponse) {
62
- this.#init = cachedGlobalResponse;
63
- this.cache;
64
- return;
65
- } else {
66
- this.#init = init.#init;
67
- }
68
- } else {
69
- this.#init = init;
70
- }
71
- if (typeof body === "string" || body instanceof ReadableStream) {
72
- let headers = init?.headers || { "content-type": "text/plain;charset=UTF-8" };
73
- if (headers instanceof Headers) {
74
- headers = buildOutgoingHttpHeaders(headers);
75
- }
76
- ;
77
- this[cacheKey] = [init?.status || 200, body, headers];
78
- }
79
- }
80
- };
81
- [
82
- "body",
83
- "bodyUsed",
84
- "headers",
85
- "ok",
86
- "redirected",
87
- "status",
88
- "statusText",
89
- "trailers",
90
- "type",
91
- "url"
92
- ].forEach((k) => {
93
- Object.defineProperty(Response.prototype, k, {
94
- get() {
95
- return this.cache[k];
96
- }
97
- });
98
- });
99
- ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
100
- Object.defineProperty(Response.prototype, k, {
101
- value: function() {
102
- return this.cache[k]();
103
- }
104
- });
105
- });
106
- Object.setPrototypeOf(Response, GlobalResponse);
107
- Object.setPrototypeOf(Response.prototype, GlobalResponse.prototype);
108
- Object.defineProperty(global, "Response", {
109
- value: Response
110
- });
111
-
112
- // src/globals.ts
113
- Object.defineProperty(global, "Response", {
114
- value: Response
115
- });
116
27
  var webFetch = global.fetch;
117
28
  if (typeof global.crypto === "undefined") {
118
29
  global.crypto = import_node_crypto.default;
package/dist/globals.mjs CHANGED
@@ -1,94 +1,5 @@
1
1
  // src/globals.ts
2
2
  import crypto from "crypto";
3
-
4
- // src/utils.ts
5
- var buildOutgoingHttpHeaders = (headers) => {
6
- const res = {};
7
- const cookies = [];
8
- for (const [k, v] of headers) {
9
- if (k === "set-cookie") {
10
- cookies.push(v);
11
- } else {
12
- res[k] = v;
13
- }
14
- }
15
- if (cookies.length > 0) {
16
- res["set-cookie"] = cookies;
17
- }
18
- res["content-type"] ??= "text/plain;charset=UTF-8";
19
- return res;
20
- };
21
-
22
- // src/response.ts
23
- var responseCache = Symbol("responseCache");
24
- var cacheKey = Symbol("cache");
25
- var GlobalResponse = global.Response;
26
- var Response = class _Response {
27
- #body;
28
- #init;
29
- get cache() {
30
- delete this[cacheKey];
31
- return this[responseCache] ||= new GlobalResponse(this.#body, this.#init);
32
- }
33
- constructor(body, init) {
34
- this.#body = body;
35
- if (init instanceof _Response) {
36
- const cachedGlobalResponse = init[responseCache];
37
- if (cachedGlobalResponse) {
38
- this.#init = cachedGlobalResponse;
39
- this.cache;
40
- return;
41
- } else {
42
- this.#init = init.#init;
43
- }
44
- } else {
45
- this.#init = init;
46
- }
47
- if (typeof body === "string" || body instanceof ReadableStream) {
48
- let headers = init?.headers || { "content-type": "text/plain;charset=UTF-8" };
49
- if (headers instanceof Headers) {
50
- headers = buildOutgoingHttpHeaders(headers);
51
- }
52
- ;
53
- this[cacheKey] = [init?.status || 200, body, headers];
54
- }
55
- }
56
- };
57
- [
58
- "body",
59
- "bodyUsed",
60
- "headers",
61
- "ok",
62
- "redirected",
63
- "status",
64
- "statusText",
65
- "trailers",
66
- "type",
67
- "url"
68
- ].forEach((k) => {
69
- Object.defineProperty(Response.prototype, k, {
70
- get() {
71
- return this.cache[k];
72
- }
73
- });
74
- });
75
- ["arrayBuffer", "blob", "clone", "formData", "json", "text"].forEach((k) => {
76
- Object.defineProperty(Response.prototype, k, {
77
- value: function() {
78
- return this.cache[k]();
79
- }
80
- });
81
- });
82
- Object.setPrototypeOf(Response, GlobalResponse);
83
- Object.setPrototypeOf(Response.prototype, GlobalResponse.prototype);
84
- Object.defineProperty(global, "Response", {
85
- value: Response
86
- });
87
-
88
- // src/globals.ts
89
- Object.defineProperty(global, "Response", {
90
- value: Response
91
- });
92
3
  var webFetch = global.fetch;
93
4
  if (typeof global.crypto === "undefined") {
94
5
  global.crypto = crypto;
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createAdaptorServer, serve } from './server.mjs';
2
2
  export { getRequestListener } from './listener.mjs';
3
+ export { Http2Bindings, HttpBindings } from './types.mjs';
3
4
  import 'node:net';
4
- import './types.mjs';
5
5
  import 'node:http';
6
6
  import 'node:http2';
7
7
  import 'node:https';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createAdaptorServer, serve } from './server.js';
2
2
  export { getRequestListener } from './listener.js';
3
+ export { Http2Bindings, HttpBindings } from './types.js';
3
4
  import 'node:net';
4
- import './types.js';
5
5
  import 'node:http';
6
6
  import 'node:http2';
7
7
  import 'node:https';
package/dist/index.js CHANGED
@@ -42,6 +42,22 @@ var import_node_http = require("http");
42
42
  // src/request.ts
43
43
  var import_node_http2 = require("http2");
44
44
  var import_node_stream = require("stream");
45
+ var GlobalRequest = global.Request;
46
+ var Request = class extends GlobalRequest {
47
+ constructor(input, options) {
48
+ if (typeof input === "object" && getRequestCache in input) {
49
+ input = input[getRequestCache]();
50
+ }
51
+ if (options?.body instanceof ReadableStream) {
52
+ ;
53
+ options.duplex = "half";
54
+ }
55
+ super(input, options);
56
+ }
57
+ };
58
+ Object.defineProperty(global, "Request", {
59
+ value: Request
60
+ });
45
61
  var newRequestFromIncoming = (method, url, incoming) => {
46
62
  const headerRecord = [];
47
63
  const rawHeaders = incoming.rawHeaders;
@@ -58,7 +74,6 @@ var newRequestFromIncoming = (method, url, incoming) => {
58
74
  };
59
75
  if (!(method === "GET" || method === "HEAD")) {
60
76
  init.body = import_node_stream.Readable.toWeb(incoming);
61
- init.duplex = "half";
62
77
  }
63
78
  return new Request(url, init);
64
79
  };
@@ -108,7 +123,7 @@ var requestPrototype = {
108
123
  }
109
124
  });
110
125
  });
111
- Object.setPrototypeOf(requestPrototype, global.Request.prototype);
126
+ Object.setPrototypeOf(requestPrototype, Request.prototype);
112
127
  var newRequest = (incoming) => {
113
128
  const req = Object.create(requestPrototype);
114
129
  req[incomingKey] = incoming;
@@ -137,8 +152,9 @@ function writeFromReadableStream(stream, writable) {
137
152
  function cancel(error) {
138
153
  reader.cancel(error).catch(() => {
139
154
  });
140
- if (error)
155
+ if (error) {
141
156
  writable.destroy(error);
157
+ }
142
158
  }
143
159
  function onDrain() {
144
160
  reader.read().then(flow, cancel);
@@ -242,9 +258,6 @@ Object.defineProperty(global, "Response", {
242
258
 
243
259
  // src/globals.ts
244
260
  var import_node_crypto = __toESM(require("crypto"));
245
- Object.defineProperty(global, "Response", {
246
- value: Response2
247
- });
248
261
  var webFetch = global.fetch;
249
262
  if (typeof global.crypto === "undefined") {
250
263
  global.crypto = import_node_crypto.default;
@@ -271,8 +284,9 @@ var handleResponseError = (e, outgoing) => {
271
284
  console.info("The user aborted a request.");
272
285
  } else {
273
286
  console.error(e);
274
- if (!outgoing.headersSent)
287
+ if (!outgoing.headersSent) {
275
288
  outgoing.writeHead(500, { "Content-Type": "text/plain" });
289
+ }
276
290
  outgoing.end(`Error: ${err.message}`);
277
291
  outgoing.destroy(err);
278
292
  }
@@ -290,12 +304,25 @@ var responseViaCache = (res, outgoing) => {
290
304
  );
291
305
  }
292
306
  };
293
- var responseViaResponseObject = async (res, outgoing) => {
307
+ var responseViaResponseObject = async (res, outgoing, options = {}) => {
294
308
  if (res instanceof Promise) {
295
- res = await res.catch(handleFetchError);
309
+ if (options.errorHandler) {
310
+ try {
311
+ res = await res;
312
+ } catch (err) {
313
+ const errRes = await options.errorHandler(err);
314
+ if (!errRes) {
315
+ return;
316
+ }
317
+ res = errRes;
318
+ }
319
+ } else {
320
+ res = await res.catch(handleFetchError);
321
+ }
296
322
  }
297
323
  try {
298
- if (cacheKey in res) {
324
+ const isCached = cacheKey in res;
325
+ if (isCached) {
299
326
  return responseViaCache(res, outgoing);
300
327
  }
301
328
  } catch (e) {
@@ -304,8 +331,15 @@ var responseViaResponseObject = async (res, outgoing) => {
304
331
  const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
305
332
  if (res.body) {
306
333
  try {
307
- if (resHeaderRecord["transfer-encoding"] || resHeaderRecord["content-encoding"] || resHeaderRecord["content-length"] || // nginx buffering variant
308
- resHeaderRecord["x-accel-buffering"] && regBuffer.test(resHeaderRecord["x-accel-buffering"]) || !regContentType.test(resHeaderRecord["content-type"])) {
334
+ const {
335
+ "transfer-encoding": transferEncoding,
336
+ "content-encoding": contentEncoding,
337
+ "content-length": contentLength,
338
+ "x-accel-buffering": accelBuffering,
339
+ "content-type": contentType
340
+ } = resHeaderRecord;
341
+ if (transferEncoding || contentEncoding || contentLength || // nginx buffering variant
342
+ accelBuffering && regBuffer.test(accelBuffering) || !regContentType.test(contentType)) {
309
343
  outgoing.writeHead(res.status, resHeaderRecord);
310
344
  await writeFromReadableStream(res.body, outgoing);
311
345
  } else {
@@ -322,23 +356,30 @@ var responseViaResponseObject = async (res, outgoing) => {
322
356
  outgoing.end();
323
357
  }
324
358
  };
325
- var getRequestListener = (fetchCallback) => {
326
- return (incoming, outgoing) => {
359
+ var getRequestListener = (fetchCallback, options = {}) => {
360
+ return async (incoming, outgoing) => {
327
361
  let res;
328
362
  const req = newRequest(incoming);
329
363
  try {
330
- res = fetchCallback(req);
364
+ res = fetchCallback(req, { incoming, outgoing });
331
365
  if (cacheKey in res) {
332
366
  return responseViaCache(res, outgoing);
333
367
  }
334
368
  } catch (e) {
335
369
  if (!res) {
336
- res = handleFetchError(e);
370
+ if (options.errorHandler) {
371
+ res = await options.errorHandler(e);
372
+ if (!res) {
373
+ return;
374
+ }
375
+ } else {
376
+ res = handleFetchError(e);
377
+ }
337
378
  } else {
338
379
  return handleResponseError(e, outgoing);
339
380
  }
340
381
  }
341
- return responseViaResponseObject(res, outgoing);
382
+ return responseViaResponseObject(res, outgoing, options);
342
383
  };
343
384
  };
344
385
 
package/dist/index.mjs CHANGED
@@ -4,6 +4,22 @@ import { createServer as createServerHTTP } from "http";
4
4
  // src/request.ts
5
5
  import { Http2ServerRequest } from "http2";
6
6
  import { Readable } from "stream";
7
+ var GlobalRequest = global.Request;
8
+ var Request = class extends GlobalRequest {
9
+ constructor(input, options) {
10
+ if (typeof input === "object" && getRequestCache in input) {
11
+ input = input[getRequestCache]();
12
+ }
13
+ if (options?.body instanceof ReadableStream) {
14
+ ;
15
+ options.duplex = "half";
16
+ }
17
+ super(input, options);
18
+ }
19
+ };
20
+ Object.defineProperty(global, "Request", {
21
+ value: Request
22
+ });
7
23
  var newRequestFromIncoming = (method, url, incoming) => {
8
24
  const headerRecord = [];
9
25
  const rawHeaders = incoming.rawHeaders;
@@ -20,7 +36,6 @@ var newRequestFromIncoming = (method, url, incoming) => {
20
36
  };
21
37
  if (!(method === "GET" || method === "HEAD")) {
22
38
  init.body = Readable.toWeb(incoming);
23
- init.duplex = "half";
24
39
  }
25
40
  return new Request(url, init);
26
41
  };
@@ -70,7 +85,7 @@ var requestPrototype = {
70
85
  }
71
86
  });
72
87
  });
73
- Object.setPrototypeOf(requestPrototype, global.Request.prototype);
88
+ Object.setPrototypeOf(requestPrototype, Request.prototype);
74
89
  var newRequest = (incoming) => {
75
90
  const req = Object.create(requestPrototype);
76
91
  req[incomingKey] = incoming;
@@ -99,8 +114,9 @@ function writeFromReadableStream(stream, writable) {
99
114
  function cancel(error) {
100
115
  reader.cancel(error).catch(() => {
101
116
  });
102
- if (error)
117
+ if (error) {
103
118
  writable.destroy(error);
119
+ }
104
120
  }
105
121
  function onDrain() {
106
122
  reader.read().then(flow, cancel);
@@ -204,9 +220,6 @@ Object.defineProperty(global, "Response", {
204
220
 
205
221
  // src/globals.ts
206
222
  import crypto from "crypto";
207
- Object.defineProperty(global, "Response", {
208
- value: Response2
209
- });
210
223
  var webFetch = global.fetch;
211
224
  if (typeof global.crypto === "undefined") {
212
225
  global.crypto = crypto;
@@ -233,8 +246,9 @@ var handleResponseError = (e, outgoing) => {
233
246
  console.info("The user aborted a request.");
234
247
  } else {
235
248
  console.error(e);
236
- if (!outgoing.headersSent)
249
+ if (!outgoing.headersSent) {
237
250
  outgoing.writeHead(500, { "Content-Type": "text/plain" });
251
+ }
238
252
  outgoing.end(`Error: ${err.message}`);
239
253
  outgoing.destroy(err);
240
254
  }
@@ -252,12 +266,25 @@ var responseViaCache = (res, outgoing) => {
252
266
  );
253
267
  }
254
268
  };
255
- var responseViaResponseObject = async (res, outgoing) => {
269
+ var responseViaResponseObject = async (res, outgoing, options = {}) => {
256
270
  if (res instanceof Promise) {
257
- res = await res.catch(handleFetchError);
271
+ if (options.errorHandler) {
272
+ try {
273
+ res = await res;
274
+ } catch (err) {
275
+ const errRes = await options.errorHandler(err);
276
+ if (!errRes) {
277
+ return;
278
+ }
279
+ res = errRes;
280
+ }
281
+ } else {
282
+ res = await res.catch(handleFetchError);
283
+ }
258
284
  }
259
285
  try {
260
- if (cacheKey in res) {
286
+ const isCached = cacheKey in res;
287
+ if (isCached) {
261
288
  return responseViaCache(res, outgoing);
262
289
  }
263
290
  } catch (e) {
@@ -266,8 +293,15 @@ var responseViaResponseObject = async (res, outgoing) => {
266
293
  const resHeaderRecord = buildOutgoingHttpHeaders(res.headers);
267
294
  if (res.body) {
268
295
  try {
269
- if (resHeaderRecord["transfer-encoding"] || resHeaderRecord["content-encoding"] || resHeaderRecord["content-length"] || // nginx buffering variant
270
- resHeaderRecord["x-accel-buffering"] && regBuffer.test(resHeaderRecord["x-accel-buffering"]) || !regContentType.test(resHeaderRecord["content-type"])) {
296
+ const {
297
+ "transfer-encoding": transferEncoding,
298
+ "content-encoding": contentEncoding,
299
+ "content-length": contentLength,
300
+ "x-accel-buffering": accelBuffering,
301
+ "content-type": contentType
302
+ } = resHeaderRecord;
303
+ if (transferEncoding || contentEncoding || contentLength || // nginx buffering variant
304
+ accelBuffering && regBuffer.test(accelBuffering) || !regContentType.test(contentType)) {
271
305
  outgoing.writeHead(res.status, resHeaderRecord);
272
306
  await writeFromReadableStream(res.body, outgoing);
273
307
  } else {
@@ -284,23 +318,30 @@ var responseViaResponseObject = async (res, outgoing) => {
284
318
  outgoing.end();
285
319
  }
286
320
  };
287
- var getRequestListener = (fetchCallback) => {
288
- return (incoming, outgoing) => {
321
+ var getRequestListener = (fetchCallback, options = {}) => {
322
+ return async (incoming, outgoing) => {
289
323
  let res;
290
324
  const req = newRequest(incoming);
291
325
  try {
292
- res = fetchCallback(req);
326
+ res = fetchCallback(req, { incoming, outgoing });
293
327
  if (cacheKey in res) {
294
328
  return responseViaCache(res, outgoing);
295
329
  }
296
330
  } catch (e) {
297
331
  if (!res) {
298
- res = handleFetchError(e);
332
+ if (options.errorHandler) {
333
+ res = await options.errorHandler(e);
334
+ if (!res) {
335
+ return;
336
+ }
337
+ } else {
338
+ res = handleFetchError(e);
339
+ }
299
340
  } else {
300
341
  return handleResponseError(e, outgoing);
301
342
  }
302
343
  }
303
- return responseViaResponseObject(res, outgoing);
344
+ return responseViaResponseObject(res, outgoing, options);
304
345
  };
305
346
  };
306
347
 
@@ -1,8 +1,10 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { Http2ServerRequest, Http2ServerResponse } from 'node:http2';
3
- import { FetchCallback } from './types.mjs';
3
+ import { FetchCallback, CustomErrorHandler } from './types.mjs';
4
4
  import 'node:https';
5
5
 
6
- declare const getRequestListener: (fetchCallback: FetchCallback) => (incoming: IncomingMessage | Http2ServerRequest, outgoing: ServerResponse | Http2ServerResponse) => void | Promise<void>;
6
+ declare const getRequestListener: (fetchCallback: FetchCallback, options?: {
7
+ errorHandler?: CustomErrorHandler;
8
+ }) => (incoming: IncomingMessage | Http2ServerRequest, outgoing: ServerResponse | Http2ServerResponse) => Promise<void>;
7
9
 
8
10
  export { getRequestListener };
@@ -1,8 +1,10 @@
1
1
  import { IncomingMessage, ServerResponse } from 'node:http';
2
2
  import { Http2ServerRequest, Http2ServerResponse } from 'node:http2';
3
- import { FetchCallback } from './types.js';
3
+ import { FetchCallback, CustomErrorHandler } from './types.js';
4
4
  import 'node:https';
5
5
 
6
- declare const getRequestListener: (fetchCallback: FetchCallback) => (incoming: IncomingMessage | Http2ServerRequest, outgoing: ServerResponse | Http2ServerResponse) => void | Promise<void>;
6
+ declare const getRequestListener: (fetchCallback: FetchCallback, options?: {
7
+ errorHandler?: CustomErrorHandler;
8
+ }) => (incoming: IncomingMessage | Http2ServerRequest, outgoing: ServerResponse | Http2ServerResponse) => Promise<void>;
7
9
 
8
10
  export { getRequestListener };