@hono/node-server 0.2.4 → 0.4.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 +3 -1
- package/dist/listener.d.ts +3 -0
- package/dist/listener.js +97 -0
- package/dist/nextjs.d.ts +2 -0
- package/dist/nextjs.js +10 -0
- package/dist/serve-static.d.ts +3 -0
- package/dist/server.d.ts +1 -7
- package/dist/server.js +3 -62
- package/dist/stream.d.ts +0 -4
- package/dist/stream.js +15 -17
- package/dist/types.d.ts +15 -0
- package/dist/types.js +2 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Hono on Node.js
|
|
2
2
|
|
|
3
|
-
**`@honojs/node-server` is renamed to `@hono/node-
|
|
3
|
+
**`@honojs/node-server` is renamed to `@hono/node-server` !!**
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -89,6 +89,8 @@ import { serveStatic } from '@hono/node-server/serve-static'
|
|
|
89
89
|
app.use('/static/*', serveStatic({ root: './' }))
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
Note that `root` must be *relative* to the current working directory - absolute paths are not supported.
|
|
93
|
+
|
|
92
94
|
## Related projects
|
|
93
95
|
|
|
94
96
|
- Hono - <https://honojs.dev>
|
package/dist/listener.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRequestListener = void 0;
|
|
4
|
+
const fetch_1 = require("./fetch");
|
|
5
|
+
const stream_1 = require("./stream");
|
|
6
|
+
const getRequestListener = (fetchCallback) => {
|
|
7
|
+
return async (incoming, outgoing) => {
|
|
8
|
+
const method = incoming.method || 'GET';
|
|
9
|
+
const url = `http://${incoming.headers.host}${incoming.url}`;
|
|
10
|
+
const headerRecord = {};
|
|
11
|
+
const len = incoming.rawHeaders.length;
|
|
12
|
+
for (let i = 0; i < len; i++) {
|
|
13
|
+
if (i % 2 === 0) {
|
|
14
|
+
const key = incoming.rawHeaders[i];
|
|
15
|
+
headerRecord[key] = incoming.rawHeaders[i + 1];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const init = {
|
|
19
|
+
method: method,
|
|
20
|
+
headers: headerRecord,
|
|
21
|
+
};
|
|
22
|
+
if (!(method === 'GET' || method === 'HEAD')) {
|
|
23
|
+
// lazy-consume request body
|
|
24
|
+
init.body = new ReadableStream({
|
|
25
|
+
start: async (controller) => {
|
|
26
|
+
for await (const chunk of incoming) {
|
|
27
|
+
controller.enqueue(chunk);
|
|
28
|
+
}
|
|
29
|
+
controller.close();
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
let res;
|
|
34
|
+
try {
|
|
35
|
+
res = (await fetchCallback(new Request(url.toString(), init)));
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
res = new fetch_1.Response(null, { status: 500 });
|
|
39
|
+
if (e instanceof Error) {
|
|
40
|
+
// timeout error emits 504 timeout
|
|
41
|
+
if (e.name === 'TimeoutError' || e.constructor.name === 'TimeoutError') {
|
|
42
|
+
res = new fetch_1.Response(null, { status: 504 });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const contentType = res.headers.get('content-type') || '';
|
|
47
|
+
// nginx buffering variant
|
|
48
|
+
const buffering = res.headers.get('x-accel-buffering') || '';
|
|
49
|
+
const contentEncoding = res.headers.get('content-encoding');
|
|
50
|
+
const contentLength = res.headers.get('content-length');
|
|
51
|
+
const transferEncoding = res.headers.get('transfer-encoding');
|
|
52
|
+
for (const [k, v] of res.headers) {
|
|
53
|
+
if (k === 'set-cookie') {
|
|
54
|
+
outgoing.setHeader(k, res.headers.getAll(k));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
outgoing.setHeader(k, v);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
outgoing.statusCode = res.status;
|
|
61
|
+
if (res.body) {
|
|
62
|
+
try {
|
|
63
|
+
/**
|
|
64
|
+
* If content-encoding is set, we assume that the response should be not decoded.
|
|
65
|
+
* Else if transfer-encoding is set, we assume that the response should be streamed.
|
|
66
|
+
* Else if content-length is set, we assume that the response content has been taken care of.
|
|
67
|
+
* Else if x-accel-buffering is set to no, we assume that the response should be streamed.
|
|
68
|
+
* Else if content-type is not application/json nor text/* but can be text/event-stream,
|
|
69
|
+
* we assume that the response should be streamed.
|
|
70
|
+
*/
|
|
71
|
+
if (contentEncoding ||
|
|
72
|
+
transferEncoding ||
|
|
73
|
+
contentLength ||
|
|
74
|
+
/^no$/i.test(buffering) ||
|
|
75
|
+
!/^(application\/json\b|text\/(?!event-stream\b))/i.test(contentType)) {
|
|
76
|
+
await (0, stream_1.writeReadableStreamToWritable)(res.body, outgoing);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const text = await res.text();
|
|
80
|
+
outgoing.setHeader('Content-Length', Buffer.byteLength(text));
|
|
81
|
+
outgoing.end(text);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// try to catch any error, to avoid crash
|
|
86
|
+
console.error(e);
|
|
87
|
+
const err = e instanceof Error ? e : new Error('unknown error', { cause: e });
|
|
88
|
+
// destroy error must accept an instance of Error
|
|
89
|
+
outgoing.destroy(err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
outgoing.end();
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
exports.getRequestListener = getRequestListener;
|
package/dist/nextjs.d.ts
ADDED
package/dist/nextjs.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handle = void 0;
|
|
4
|
+
const hono_1 = require("hono");
|
|
5
|
+
const listener_1 = require("./listener");
|
|
6
|
+
// <E extends Hono<any, any>
|
|
7
|
+
const handle = (subApp, path = '/') => {
|
|
8
|
+
return (0, listener_1.getRequestListener)(new hono_1.Hono().route(path, subApp).fetch);
|
|
9
|
+
};
|
|
10
|
+
exports.handle = handle;
|
package/dist/serve-static.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { Next } from 'hono';
|
|
2
2
|
import { Context } from 'hono';
|
|
3
3
|
export declare type ServeStaticOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* Root path, relative to current working directory. (absolute paths are not supported)
|
|
6
|
+
*/
|
|
4
7
|
root?: string;
|
|
5
8
|
path?: string;
|
|
6
9
|
index?: string;
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,4 @@
|
|
|
1
1
|
import { Server } from 'node:http';
|
|
2
|
-
|
|
3
|
-
declare type Options = {
|
|
4
|
-
fetch: FetchCallback;
|
|
5
|
-
port?: number;
|
|
6
|
-
serverOptions?: Object;
|
|
7
|
-
};
|
|
2
|
+
import { Options } from './types';
|
|
8
3
|
export declare const createAdaptorServer: (options: Options) => Server;
|
|
9
4
|
export declare const serve: (options: Options) => Server;
|
|
10
|
-
export {};
|
package/dist/server.js
CHANGED
|
@@ -2,78 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.serve = exports.createAdaptorServer = void 0;
|
|
4
4
|
const node_http_1 = require("node:http");
|
|
5
|
-
const fetch_1 = require("./fetch");
|
|
6
|
-
const stream_1 = require("./stream");
|
|
7
5
|
const globals_1 = require("./globals");
|
|
6
|
+
const listener_1 = require("./listener");
|
|
8
7
|
(0, globals_1.installGlobals)();
|
|
9
8
|
const createAdaptorServer = (options) => {
|
|
10
9
|
const fetchCallback = options.fetch;
|
|
11
|
-
const requestListener = getRequestListener(fetchCallback);
|
|
10
|
+
const requestListener = (0, listener_1.getRequestListener)(fetchCallback);
|
|
12
11
|
const server = (0, node_http_1.createServer)(options.serverOptions || {}, requestListener);
|
|
13
12
|
return server;
|
|
14
13
|
};
|
|
15
14
|
exports.createAdaptorServer = createAdaptorServer;
|
|
16
15
|
const serve = (options) => {
|
|
17
16
|
const server = (0, exports.createAdaptorServer)(options);
|
|
18
|
-
server.listen(options.port || 3000);
|
|
17
|
+
server.listen(options.port || 3000, options.hostname || '0.0.0.0');
|
|
19
18
|
return server;
|
|
20
19
|
};
|
|
21
20
|
exports.serve = serve;
|
|
22
|
-
const getRequestListener = (fetchCallback) => {
|
|
23
|
-
return async (incoming, outgoing) => {
|
|
24
|
-
const method = incoming.method || 'GET';
|
|
25
|
-
const url = `http://${incoming.headers.host}${incoming.url}`;
|
|
26
|
-
const headerRecord = {};
|
|
27
|
-
const len = incoming.rawHeaders.length;
|
|
28
|
-
for (let i = 0; i < len; i++) {
|
|
29
|
-
if (i % 2 === 0) {
|
|
30
|
-
const key = incoming.rawHeaders[i];
|
|
31
|
-
headerRecord[key] = incoming.rawHeaders[i + 1];
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const init = {
|
|
35
|
-
method: method,
|
|
36
|
-
headers: headerRecord,
|
|
37
|
-
};
|
|
38
|
-
if (!(method === 'GET' || method === 'HEAD')) {
|
|
39
|
-
const buffers = [];
|
|
40
|
-
for await (const chunk of incoming) {
|
|
41
|
-
buffers.push(chunk);
|
|
42
|
-
}
|
|
43
|
-
const buffer = Buffer.concat(buffers);
|
|
44
|
-
init['body'] = buffer;
|
|
45
|
-
}
|
|
46
|
-
let res;
|
|
47
|
-
try {
|
|
48
|
-
res = (await fetchCallback(new Request(url.toString(), init)));
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
res = new fetch_1.Response(null, { status: 500 });
|
|
52
|
-
}
|
|
53
|
-
const contentType = res.headers.get('content-type') || '';
|
|
54
|
-
const contentEncoding = res.headers.get('content-encoding');
|
|
55
|
-
for (const [k, v] of res.headers) {
|
|
56
|
-
if (k === 'set-cookie') {
|
|
57
|
-
outgoing.setHeader(k, res.headers.getAll(k));
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
outgoing.setHeader(k, v);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
outgoing.statusCode = res.status;
|
|
64
|
-
if (res.body) {
|
|
65
|
-
if (!contentEncoding && contentType.startsWith('text')) {
|
|
66
|
-
outgoing.end(await res.text());
|
|
67
|
-
}
|
|
68
|
-
else if (!contentEncoding && contentType.startsWith('application/json')) {
|
|
69
|
-
outgoing.end(await res.text());
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
await (0, stream_1.writeReadableStreamToWritable)(res.body, outgoing);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
outgoing.end();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
};
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import type { Writable } from 'node:stream';
|
|
3
3
|
export declare function writeReadableStreamToWritable(stream: ReadableStream, writable: Writable): Promise<void>;
|
|
4
|
-
/**
|
|
5
|
-
* Credits:
|
|
6
|
-
* - https://github.com/remix-run/remix/blob/e77e2eb/packages/remix-node/stream.ts
|
|
7
|
-
*/
|
package/dist/stream.js
CHANGED
|
@@ -2,26 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.writeReadableStreamToWritable = void 0;
|
|
4
4
|
async function writeReadableStreamToWritable(stream, writable) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (done) {
|
|
9
|
-
writable.end();
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
writable.write(value);
|
|
13
|
-
await read();
|
|
5
|
+
const reader = stream.getReader();
|
|
6
|
+
function onClose() {
|
|
7
|
+
reader.cancel(new Error('Response writer closed'));
|
|
14
8
|
}
|
|
9
|
+
writable.once('close', onClose);
|
|
15
10
|
try {
|
|
16
|
-
|
|
11
|
+
while (true) {
|
|
12
|
+
const { done, value } = await reader.read();
|
|
13
|
+
if (done) {
|
|
14
|
+
writable.end();
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
writable.write(value);
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
writable.
|
|
20
|
-
|
|
20
|
+
finally {
|
|
21
|
+
writable.off('close', onClose);
|
|
22
|
+
reader.releaseLock();
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
exports.writeReadableStreamToWritable = writeReadableStreamToWritable;
|
|
24
|
-
/**
|
|
25
|
-
* Credits:
|
|
26
|
-
* - https://github.com/remix-run/remix/blob/e77e2eb/packages/remix-node/stream.ts
|
|
27
|
-
*/
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Hono } from 'hono';
|
|
2
|
+
import type { NextApiHandler } from 'next/types';
|
|
3
|
+
export declare type FetchCallback = (request: Request) => Promise<unknown> | unknown;
|
|
4
|
+
export declare type NextHandlerOption = {
|
|
5
|
+
fetch: FetchCallback;
|
|
6
|
+
};
|
|
7
|
+
export declare type Options = {
|
|
8
|
+
fetch: FetchCallback;
|
|
9
|
+
port?: number;
|
|
10
|
+
hostname?: string;
|
|
11
|
+
serverOptions?: Object;
|
|
12
|
+
};
|
|
13
|
+
export interface HandleInterface {
|
|
14
|
+
<E extends Hono<any, any>>(subApp: E, path?: string): NextApiHandler;
|
|
15
|
+
}
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/node-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "HTTP Server for Hono on Node.js",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -9,12 +9,16 @@
|
|
|
9
9
|
],
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./dist/index.js",
|
|
12
|
-
"./serve-static": "./dist/serve-static.js"
|
|
12
|
+
"./serve-static": "./dist/serve-static.js",
|
|
13
|
+
"./nextjs": "./dist/nextjs.js"
|
|
13
14
|
},
|
|
14
15
|
"typesVersions": {
|
|
15
16
|
"*": {
|
|
16
17
|
"serve-static": [
|
|
17
18
|
"./dist/serve-static.d.ts"
|
|
19
|
+
],
|
|
20
|
+
"nextjs": [
|
|
21
|
+
"./dist/nextjs.d.ts"
|
|
18
22
|
]
|
|
19
23
|
}
|
|
20
24
|
},
|
|
@@ -46,6 +50,7 @@
|
|
|
46
50
|
"@types/supertest": "^2.0.12",
|
|
47
51
|
"hono": "^2.7.1",
|
|
48
52
|
"jest": "^29.0.3",
|
|
53
|
+
"next": "13.1.6",
|
|
49
54
|
"np": "^7.6.2",
|
|
50
55
|
"rimraf": "^3.0.2",
|
|
51
56
|
"supertest": "^6.2.4",
|