@hono/node-server 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -5
- package/dist/globals.d.ts +3 -0
- package/dist/globals.js +3 -0
- package/dist/listener.js +2 -8
- package/dist/server.d.ts +2 -1
- package/dist/server.js +7 -3
- package/dist/stream.d.ts +5 -1
- package/dist/stream.js +99 -17
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
# Hono on Node.js
|
|
2
2
|
|
|
3
|
-
**`@honojs/node-server` is renamed to `@hono/node-server` !!**
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
3
|
This is **HTTP Server for Hono on Node.js**.
|
|
8
4
|
Hono is ultrafast web framework for Cloudflare Workers, Deno, and Bun.
|
|
9
5
|
**It's not for Node.js**.
|
|
@@ -39,7 +35,9 @@ import { Hono } from 'hono'
|
|
|
39
35
|
const app = new Hono()
|
|
40
36
|
app.get('/', (c) => c.text('Hono meets Node.js'))
|
|
41
37
|
|
|
42
|
-
serve(app)
|
|
38
|
+
serve(app, (info) => {
|
|
39
|
+
console.log(`Listening on http://localhost:${info.port}`) // Listening on http://localhost:3000
|
|
40
|
+
})
|
|
43
41
|
```
|
|
44
42
|
|
|
45
43
|
For example, run it using `ts-node`. Then an HTTP server will be launched. The default port is `3000`.
|
|
@@ -52,6 +50,8 @@ Open `http://localhost:3000` with your browser.
|
|
|
52
50
|
|
|
53
51
|
## Options
|
|
54
52
|
|
|
53
|
+
### `port`
|
|
54
|
+
|
|
55
55
|
```ts
|
|
56
56
|
serve({
|
|
57
57
|
fetch: app.fetch,
|
|
@@ -59,6 +59,24 @@ serve({
|
|
|
59
59
|
})
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
+
### `createServer`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { createServer } from 'node:https'
|
|
66
|
+
import fs from 'node:fs'
|
|
67
|
+
|
|
68
|
+
//...
|
|
69
|
+
|
|
70
|
+
serve({
|
|
71
|
+
fetch: app.fetch,
|
|
72
|
+
createServer: createServer,
|
|
73
|
+
serverOptions: {
|
|
74
|
+
key: fs.readFileSync('test/fixtures/keys/agent1-key.pem'),
|
|
75
|
+
cert: fs.readFileSync('test/fixtures/keys/agent1-cert.pem'),
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
62
80
|
## Middleware
|
|
63
81
|
|
|
64
82
|
Most built-in middleware also works with Node.js.
|
package/dist/globals.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ declare global {
|
|
|
16
16
|
FormData: typeof FormData;
|
|
17
17
|
ReadableStream: typeof ReadableStream;
|
|
18
18
|
WritableStream: typeof WritableStream;
|
|
19
|
+
TransformStream: typeof TransformStream;
|
|
20
|
+
TextDecoderStream: typeof TextDecoderStream;
|
|
21
|
+
TextEncoderStream: typeof TextEncoderStream;
|
|
19
22
|
crypto: Crypto;
|
|
20
23
|
}
|
|
21
24
|
}
|
package/dist/globals.js
CHANGED
|
@@ -23,6 +23,9 @@ function installGlobals() {
|
|
|
23
23
|
global.FormData = fetch_1.FormData;
|
|
24
24
|
global.ReadableStream = web_stream_1.ReadableStream;
|
|
25
25
|
global.WritableStream = web_stream_1.WritableStream;
|
|
26
|
+
global.TransformStream = web_stream_1.TransformStream;
|
|
27
|
+
global.TextDecoderStream = web_stream_1.TextDecoderStream;
|
|
28
|
+
global.TextEncoderStream = web_stream_1.TextEncoderStream;
|
|
26
29
|
if (typeof global.crypto === 'undefined') {
|
|
27
30
|
// If crypto.subtle is undefined, we're in a Node.js v16 environment
|
|
28
31
|
if (typeof node_crypto_1.default.subtle === 'undefined') {
|
package/dist/listener.js
CHANGED
|
@@ -18,17 +18,11 @@ const getRequestListener = (fetchCallback) => {
|
|
|
18
18
|
const init = {
|
|
19
19
|
method: method,
|
|
20
20
|
headers: headerRecord,
|
|
21
|
+
// duplex: 'half', should used in nodejs 18
|
|
21
22
|
};
|
|
22
23
|
if (!(method === 'GET' || method === 'HEAD')) {
|
|
23
24
|
// lazy-consume request body
|
|
24
|
-
init.body =
|
|
25
|
-
start: async (controller) => {
|
|
26
|
-
for await (const chunk of incoming) {
|
|
27
|
-
controller.enqueue(chunk);
|
|
28
|
-
}
|
|
29
|
-
controller.close();
|
|
30
|
-
}
|
|
31
|
-
});
|
|
25
|
+
init.body = (0, stream_1.nodeReadableToWebReadableStream)(incoming);
|
|
32
26
|
}
|
|
33
27
|
let res;
|
|
34
28
|
try {
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Server } from 'node:http';
|
|
2
2
|
import { Options } from './types';
|
|
3
|
+
import type { AddressInfo } from 'node:net';
|
|
3
4
|
export declare const createAdaptorServer: (options: Options) => Server;
|
|
4
|
-
export declare const serve: (options: Options) => Server;
|
|
5
|
+
export declare const serve: (options: Options, listeningListener?: ((info: AddressInfo) => void) | undefined) => Server;
|
package/dist/server.js
CHANGED
|
@@ -8,13 +8,17 @@ const listener_1 = require("./listener");
|
|
|
8
8
|
const createAdaptorServer = (options) => {
|
|
9
9
|
const fetchCallback = options.fetch;
|
|
10
10
|
const requestListener = (0, listener_1.getRequestListener)(fetchCallback);
|
|
11
|
-
const
|
|
11
|
+
const createServer = options.createServer || node_http_1.createServer;
|
|
12
|
+
const server = createServer(options.serverOptions || {}, requestListener);
|
|
12
13
|
return server;
|
|
13
14
|
};
|
|
14
15
|
exports.createAdaptorServer = createAdaptorServer;
|
|
15
|
-
const serve = (options) => {
|
|
16
|
+
const serve = (options, listeningListener) => {
|
|
16
17
|
const server = (0, exports.createAdaptorServer)(options);
|
|
17
|
-
server.listen(options
|
|
18
|
+
server.listen(options?.port ?? 3000, options.hostname ?? '0.0.0.0', () => {
|
|
19
|
+
const serverInfo = server.address();
|
|
20
|
+
listeningListener && listeningListener(serverInfo);
|
|
21
|
+
});
|
|
18
22
|
return server;
|
|
19
23
|
};
|
|
20
24
|
exports.serve = serve;
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import
|
|
2
|
+
import { Writable, Readable } from 'node:stream';
|
|
3
|
+
/** pipeline will assure the backpressure and reduce huge memory usage */
|
|
3
4
|
export declare function writeReadableStreamToWritable(stream: ReadableStream, writable: Writable): Promise<void>;
|
|
5
|
+
/** This implementation use nodejs Readable::fromWeb as references */
|
|
6
|
+
export declare function webReadableStreamToNodeReadable(stream: ReadableStream): Readable;
|
|
7
|
+
export declare function nodeReadableToWebReadableStream(readable: Readable): ReadableStream<Uint8Array>;
|
package/dist/stream.js
CHANGED
|
@@ -1,25 +1,107 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.writeReadableStreamToWritable = void 0;
|
|
3
|
+
exports.nodeReadableToWebReadableStream = exports.webReadableStreamToNodeReadable = exports.writeReadableStreamToWritable = void 0;
|
|
4
|
+
const node_stream_1 = require("node:stream");
|
|
5
|
+
const node_util_1 = require("node:util");
|
|
6
|
+
const pipelinePromise = (0, node_util_1.promisify)(node_stream_1.pipeline);
|
|
7
|
+
/** pipeline will assure the backpressure and reduce huge memory usage */
|
|
4
8
|
async function writeReadableStreamToWritable(stream, writable) {
|
|
9
|
+
const readable = webReadableStreamToNodeReadable(stream);
|
|
10
|
+
return pipelinePromise(readable, writable);
|
|
11
|
+
}
|
|
12
|
+
exports.writeReadableStreamToWritable = writeReadableStreamToWritable;
|
|
13
|
+
/** This implementation use nodejs Readable::fromWeb as references */
|
|
14
|
+
function webReadableStreamToNodeReadable(stream) {
|
|
5
15
|
const reader = stream.getReader();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
let closed = false;
|
|
17
|
+
const readable = new node_stream_1.Readable({
|
|
18
|
+
read() {
|
|
19
|
+
reader
|
|
20
|
+
.read()
|
|
21
|
+
.then(({ done, value }) => {
|
|
22
|
+
if (done) {
|
|
23
|
+
this.push(null);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.push(value);
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
.catch(e => {
|
|
30
|
+
readable.destroy(e);
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
destroy(error, callback) {
|
|
34
|
+
const done = () => {
|
|
35
|
+
try {
|
|
36
|
+
callback(error);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
process.nextTick(() => {
|
|
40
|
+
throw err;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
if (!closed) {
|
|
45
|
+
reader.cancel(error).then(done, done);
|
|
15
46
|
return;
|
|
16
47
|
}
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
done();
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
reader.closed.then(() => {
|
|
52
|
+
closed = true;
|
|
53
|
+
}, error => {
|
|
54
|
+
readable.destroy(error);
|
|
55
|
+
});
|
|
56
|
+
return readable;
|
|
57
|
+
}
|
|
58
|
+
exports.webReadableStreamToNodeReadable = webReadableStreamToNodeReadable;
|
|
59
|
+
function nodeReadableToWebReadableStream(readable) {
|
|
60
|
+
if (readable.destroyed) {
|
|
61
|
+
const stream = new ReadableStream();
|
|
62
|
+
stream.cancel();
|
|
63
|
+
return stream;
|
|
23
64
|
}
|
|
65
|
+
const highWaterMark = readable.readableHighWaterMark;
|
|
66
|
+
const strategy = { highWaterMark };
|
|
67
|
+
let controller;
|
|
68
|
+
const onData = (chunk) => {
|
|
69
|
+
// Copy the Buffer to detach it from the pool.
|
|
70
|
+
if (Buffer.isBuffer(chunk)) {
|
|
71
|
+
chunk = new Uint8Array(chunk);
|
|
72
|
+
}
|
|
73
|
+
controller.enqueue(chunk);
|
|
74
|
+
if (controller.desiredSize !== null && controller.desiredSize <= 0) {
|
|
75
|
+
readable.pause();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
readable.pause();
|
|
79
|
+
const cleanup = (0, node_stream_1.finished)(readable, error => {
|
|
80
|
+
if (error?.code === 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
81
|
+
const err = new Error(undefined, { cause: error });
|
|
82
|
+
Object.defineProperty(err, 'name', 'AbortError');
|
|
83
|
+
error = err;
|
|
84
|
+
}
|
|
85
|
+
cleanup();
|
|
86
|
+
// This is a protection against non-standard, legacy streams
|
|
87
|
+
// that happen to emit an error event again after finished is called.
|
|
88
|
+
readable.on('error', () => { });
|
|
89
|
+
if (error) {
|
|
90
|
+
return controller.error(error);
|
|
91
|
+
}
|
|
92
|
+
controller.close();
|
|
93
|
+
});
|
|
94
|
+
readable.on('data', onData);
|
|
95
|
+
return new ReadableStream({
|
|
96
|
+
start(c) {
|
|
97
|
+
controller = c;
|
|
98
|
+
},
|
|
99
|
+
pull() {
|
|
100
|
+
readable.resume();
|
|
101
|
+
},
|
|
102
|
+
cancel(reason) {
|
|
103
|
+
readable.destroy(reason);
|
|
104
|
+
},
|
|
105
|
+
}, strategy);
|
|
24
106
|
}
|
|
25
|
-
exports.
|
|
107
|
+
exports.nodeReadableToWebReadableStream = nodeReadableToWebReadableStream;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
1
2
|
import type { Hono } from 'hono';
|
|
2
3
|
import type { NextApiHandler } from 'next/types';
|
|
4
|
+
import type { createServer } from 'node:http';
|
|
3
5
|
export declare type FetchCallback = (request: Request) => Promise<unknown> | unknown;
|
|
4
6
|
export declare type NextHandlerOption = {
|
|
5
7
|
fetch: FetchCallback;
|
|
@@ -9,6 +11,7 @@ export declare type Options = {
|
|
|
9
11
|
port?: number;
|
|
10
12
|
hostname?: string;
|
|
11
13
|
serverOptions?: Object;
|
|
14
|
+
createServer?: typeof createServer;
|
|
12
15
|
};
|
|
13
16
|
export interface HandleInterface {
|
|
14
17
|
<E extends Hono<any, any>>(subApp: E, path?: string): NextApiHandler;
|