@ailura/nestjs-hono-adapter 1.0.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/LICENSE +21 -0
- package/README.md +411 -0
- package/dist/body.d.ts +35 -0
- package/dist/body.d.ts.map +1 -0
- package/dist/body.js +180 -0
- package/dist/body.js.map +1 -0
- package/dist/bridge.d.ts +64 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.js +168 -0
- package/dist/bridge.js.map +1 -0
- package/dist/closing.d.ts +13 -0
- package/dist/closing.d.ts.map +1 -0
- package/dist/closing.js +30 -0
- package/dist/closing.js.map +1 -0
- package/dist/context.d.ts +22 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/cors-middleware.d.ts +64 -0
- package/dist/cors-middleware.d.ts.map +1 -0
- package/dist/cors-middleware.js +211 -0
- package/dist/cors-middleware.js.map +1 -0
- package/dist/handler-bridge.d.ts +51 -0
- package/dist/handler-bridge.d.ts.map +1 -0
- package/dist/handler-bridge.js +122 -0
- package/dist/handler-bridge.js.map +1 -0
- package/dist/hono-lifecycle.d.ts +90 -0
- package/dist/hono-lifecycle.d.ts.map +1 -0
- package/dist/hono-lifecycle.js +169 -0
- package/dist/hono-lifecycle.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/path.d.ts +3 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +144 -0
- package/dist/path.js.map +1 -0
- package/dist/query.d.ts +19 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +238 -0
- package/dist/query.js.map +1 -0
- package/dist/response-helpers.d.ts +16 -0
- package/dist/response-helpers.d.ts.map +1 -0
- package/dist/response-helpers.js +45 -0
- package/dist/response-helpers.js.map +1 -0
- package/dist/response-writer.d.ts +28 -0
- package/dist/response-writer.d.ts.map +1 -0
- package/dist/response-writer.js +52 -0
- package/dist/response-writer.js.map +1 -0
- package/dist/route-adapter.d.ts +53 -0
- package/dist/route-adapter.d.ts.map +1 -0
- package/dist/route-adapter.js +141 -0
- package/dist/route-adapter.js.map +1 -0
- package/dist/server-adapter.d.ts +106 -0
- package/dist/server-adapter.d.ts.map +1 -0
- package/dist/server-adapter.js +153 -0
- package/dist/server-adapter.js.map +1 -0
- package/dist/sse.d.ts +67 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +211 -0
- package/dist/sse.js.map +1 -0
- package/dist/static-assets.d.ts +39 -0
- package/dist/static-assets.d.ts.map +1 -0
- package/dist/static-assets.js +155 -0
- package/dist/static-assets.js.map +1 -0
- package/dist/version-filter.d.ts +24 -0
- package/dist/version-filter.d.ts.map +1 -0
- package/dist/version-filter.js +107 -0
- package/dist/version-filter.js.map +1 -0
- package/dist/versioned-route.d.ts +21 -0
- package/dist/versioned-route.d.ts.map +1 -0
- package/dist/versioned-route.js +15 -0
- package/dist/versioned-route.js.map +1 -0
- package/dist/views.d.ts +42 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +110 -0
- package/dist/views.js.map +1 -0
- package/dist/ws-adapter.d.ts +81 -0
- package/dist/ws-adapter.d.ts.map +1 -0
- package/dist/ws-adapter.js +214 -0
- package/dist/ws-adapter.js.map +1 -0
- package/dist/ws-client.d.ts +68 -0
- package/dist/ws-client.d.ts.map +1 -0
- package/dist/ws-client.js +135 -0
- package/dist/ws-client.js.map +1 -0
- package/dist/ws-server.d.ts +23 -0
- package/dist/ws-server.d.ts.map +1 -0
- package/dist/ws-server.js +37 -0
- package/dist/ws-server.js.map +1 -0
- package/dist/ws.d.ts +14 -0
- package/dist/ws.d.ts.map +1 -0
- package/dist/ws.js +12 -0
- package/dist/ws.js.map +1 -0
- package/package.json +99 -0
- package/src/body.ts +251 -0
- package/src/bridge.ts +308 -0
- package/src/closing.ts +38 -0
- package/src/context.ts +25 -0
- package/src/cors-middleware.ts +347 -0
- package/src/handler-bridge.ts +226 -0
- package/src/hono-lifecycle.ts +259 -0
- package/src/index.ts +27 -0
- package/src/path.ts +169 -0
- package/src/query.ts +304 -0
- package/src/response-helpers.ts +60 -0
- package/src/response-writer.ts +100 -0
- package/src/route-adapter.ts +261 -0
- package/src/server-adapter.ts +294 -0
- package/src/sse.ts +274 -0
- package/src/static-assets.ts +247 -0
- package/src/version-filter.ts +170 -0
- package/src/versioned-route.ts +30 -0
- package/src/views.ts +188 -0
- package/src/ws-adapter.ts +329 -0
- package/src/ws-client.ts +190 -0
- package/src/ws-server.ts +40 -0
- package/src/ws.ts +13 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createNodeWebSocket } from '@hono/node-ws';
|
|
2
|
+
import { Logger } from '@nestjs/common';
|
|
3
|
+
import { AbstractWsAdapter } from '@nestjs/websockets';
|
|
4
|
+
import { EMPTY, catchError, filter, first, fromEvent, map, mergeMap, share, takeUntil, } from 'rxjs';
|
|
5
|
+
import { CLOSE_EVENT, HonoSocket, MESSAGE_EVENT, isReply, parseFrame, toReply, } from './ws-client.js';
|
|
6
|
+
import { GatewayServer } from './ws-server.js';
|
|
7
|
+
/** The port a gateway asks for when it keeps the HTTP one. */
|
|
8
|
+
const UNDERLYING_PORT = 0;
|
|
9
|
+
/** The path a gateway listens on when it named none. */
|
|
10
|
+
const ROOT_PATH = '/';
|
|
11
|
+
/** The event Node reports a protocol upgrade on. */
|
|
12
|
+
const UPGRADE_EVENT = 'upgrade';
|
|
13
|
+
/** Gives a gateway path the leading slash a route needs. */
|
|
14
|
+
function normalizePath(path) {
|
|
15
|
+
if (path === undefined || path === '') {
|
|
16
|
+
return ROOT_PATH;
|
|
17
|
+
}
|
|
18
|
+
if (path.startsWith(ROOT_PATH)) {
|
|
19
|
+
return path;
|
|
20
|
+
}
|
|
21
|
+
return `/${path}`;
|
|
22
|
+
}
|
|
23
|
+
/** The handlers of one gateway, by the event each answers. */
|
|
24
|
+
function indexHandlers(handlers) {
|
|
25
|
+
const byEvent = new Map();
|
|
26
|
+
for (const handler of handlers) {
|
|
27
|
+
byEvent.set(handler.message, handler);
|
|
28
|
+
}
|
|
29
|
+
return byEvent;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Removes the upgrade listeners `@hono/node-ws` installed on
|
|
33
|
+
* the server. Node types a listener as `Function`, which `off`
|
|
34
|
+
* does not accept, so the call goes through `Reflect.apply`,
|
|
35
|
+
* whose argument list carries no type.
|
|
36
|
+
*/
|
|
37
|
+
function detachUpgradeListeners(server, kept) {
|
|
38
|
+
const off = server.off.bind(server);
|
|
39
|
+
for (const listener of server.listeners(UPGRADE_EVENT)) {
|
|
40
|
+
if (!kept.includes(listener)) {
|
|
41
|
+
Reflect.apply(off, undefined, [UPGRADE_EVENT, listener]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* WebSocket adapter that runs Nest's gateways on the Hono
|
|
47
|
+
* application the HTTP adapter already serves.
|
|
48
|
+
*
|
|
49
|
+
* `@hono/node-ws` upgrades a request by asking the Hono
|
|
50
|
+
* application for the gateway path, so the upgrade happens on
|
|
51
|
+
* one server rather than on a second one opened beside it. One
|
|
52
|
+
* route is registered per path a gateway named, and each route
|
|
53
|
+
* hands Nest a {@link HonoSocket} to answer on.
|
|
54
|
+
*
|
|
55
|
+
* A gateway that asked for its own port or for a namespace
|
|
56
|
+
* cannot be served this way; both are refused rather than
|
|
57
|
+
* quietly served on the HTTP server.
|
|
58
|
+
*/
|
|
59
|
+
class HonoWsAdapter extends AbstractWsAdapter {
|
|
60
|
+
constructor(httpAdapter) {
|
|
61
|
+
super(httpAdapter);
|
|
62
|
+
this.servers = new Map();
|
|
63
|
+
this.logger = new Logger(HonoWsAdapter.name);
|
|
64
|
+
this.injected = false;
|
|
65
|
+
this.adapter = httpAdapter;
|
|
66
|
+
this.node = createNodeWebSocket({
|
|
67
|
+
app: httpAdapter.getHono(),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
create(port, options = {}) {
|
|
71
|
+
this.ensureSupported(port, options);
|
|
72
|
+
return this.open(normalizePath(options.path));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Bridges the handlers Nest explored for one client onto the
|
|
76
|
+
* frames that client sends. Every subscription is tied to the
|
|
77
|
+
* socket's own close event, so a connection that goes away
|
|
78
|
+
* takes its subscriptions with it.
|
|
79
|
+
*/
|
|
80
|
+
bindMessageHandlers(client, handlers, transform) {
|
|
81
|
+
const byEvent = indexHandlers(handlers);
|
|
82
|
+
const closed = fromEvent(client, CLOSE_EVENT).pipe(share(), first());
|
|
83
|
+
const answers = fromEvent(client, MESSAGE_EVENT).pipe(mergeMap((raw) => this.answer(raw, byEvent, transform)), takeUntil(closed));
|
|
84
|
+
answers.subscribe((reply) => {
|
|
85
|
+
client.sendJson(reply);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
bindClientDisconnect(client, callback) {
|
|
89
|
+
client.once(CLOSE_EVENT, callback);
|
|
90
|
+
}
|
|
91
|
+
/** Closes every socket a gateway path is serving. */
|
|
92
|
+
close(server) {
|
|
93
|
+
server.close();
|
|
94
|
+
return Promise.resolve();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Releases the paths, the sockets and the WebSocket server
|
|
98
|
+
* this adapter created, and takes the upgrade listeners back
|
|
99
|
+
* off the Node server.
|
|
100
|
+
*/
|
|
101
|
+
dispose() {
|
|
102
|
+
this.release();
|
|
103
|
+
return Promise.resolve();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Releases the paths, the sockets, the WebSocket server and
|
|
107
|
+
* the upgrade listeners this adapter registered.
|
|
108
|
+
*/
|
|
109
|
+
release() {
|
|
110
|
+
const servers = [...this.servers.values()];
|
|
111
|
+
this.servers.clear();
|
|
112
|
+
for (const server of servers) {
|
|
113
|
+
server.close();
|
|
114
|
+
}
|
|
115
|
+
this.node.wss.close();
|
|
116
|
+
const { detach } = this;
|
|
117
|
+
if (detach !== undefined) {
|
|
118
|
+
detach();
|
|
119
|
+
}
|
|
120
|
+
this.detach = undefined;
|
|
121
|
+
this.injected = false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Serves one path, reusing the server already serving it:
|
|
125
|
+
* Nest asks once per path, and a repeated ask must not
|
|
126
|
+
* register the route twice.
|
|
127
|
+
*/
|
|
128
|
+
open(path) {
|
|
129
|
+
const known = this.servers.get(path);
|
|
130
|
+
if (known !== undefined) {
|
|
131
|
+
return known;
|
|
132
|
+
}
|
|
133
|
+
const server = new GatewayServer();
|
|
134
|
+
this.servers.set(path, server);
|
|
135
|
+
this.serve(path, server);
|
|
136
|
+
this.inject();
|
|
137
|
+
return server;
|
|
138
|
+
}
|
|
139
|
+
/** Registers the upgrade route one gateway path needs. */
|
|
140
|
+
serve(path, server) {
|
|
141
|
+
const upgrade = this.node.upgradeWebSocket(() => {
|
|
142
|
+
const client = new HonoSocket();
|
|
143
|
+
return {
|
|
144
|
+
onClose: () => {
|
|
145
|
+
client.disconnect();
|
|
146
|
+
},
|
|
147
|
+
onMessage: (event) => {
|
|
148
|
+
client.deliver(event.data);
|
|
149
|
+
},
|
|
150
|
+
onOpen: (_event, socket) => {
|
|
151
|
+
client.use(socket);
|
|
152
|
+
server.accept(client);
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
this.adapter.getHono().get(path, upgrade);
|
|
157
|
+
}
|
|
158
|
+
/** Lets the Node server hand upgrades to the Hono app. */
|
|
159
|
+
inject() {
|
|
160
|
+
if (this.injected) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const server = this.adapter.getHttpServer();
|
|
164
|
+
const before = server.listeners(UPGRADE_EVENT);
|
|
165
|
+
this.node.injectWebSocket(server);
|
|
166
|
+
this.detach = () => {
|
|
167
|
+
detachUpgradeListeners(server, before);
|
|
168
|
+
};
|
|
169
|
+
this.injected = true;
|
|
170
|
+
}
|
|
171
|
+
/** Answers one frame, when a handler claims its event. */
|
|
172
|
+
answer(raw, byEvent, transform) {
|
|
173
|
+
const frame = parseFrame(raw);
|
|
174
|
+
if (frame === undefined) {
|
|
175
|
+
return EMPTY;
|
|
176
|
+
}
|
|
177
|
+
const handler = byEvent.get(frame.event);
|
|
178
|
+
if (handler === undefined) {
|
|
179
|
+
return EMPTY;
|
|
180
|
+
}
|
|
181
|
+
return this.invoke(handler, frame, transform);
|
|
182
|
+
}
|
|
183
|
+
/** Runs one handler and turns its answer into frames. */
|
|
184
|
+
invoke(handler, frame, transform) {
|
|
185
|
+
try {
|
|
186
|
+
return this.replies(transform(handler.callback(frame.data)), frame);
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
return this.report(error);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** The frames one handler's stream answers with. */
|
|
193
|
+
replies(stream, frame) {
|
|
194
|
+
return stream.pipe(map((value) => toReply(frame, value)), filter(isReply), catchError((error) => this.report(error)));
|
|
195
|
+
}
|
|
196
|
+
/** Reports a handler that failed and answers nothing. */
|
|
197
|
+
report(error) {
|
|
198
|
+
this.logger.error(error);
|
|
199
|
+
return EMPTY;
|
|
200
|
+
}
|
|
201
|
+
/** Refuses the gateways this adapter cannot serve. */
|
|
202
|
+
ensureSupported(port, options) {
|
|
203
|
+
if (port !== UNDERLYING_PORT) {
|
|
204
|
+
throw new TypeError('HonoWsAdapter serves every gateway on the HTTP ' +
|
|
205
|
+
`adapter's server, so the gateway port ${port} ` +
|
|
206
|
+
'cannot be honoured.');
|
|
207
|
+
}
|
|
208
|
+
if (options.namespace !== undefined) {
|
|
209
|
+
throw new TypeError('HonoWsAdapter does not support WebSocket namespaces.');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export { HonoWsAdapter };
|
|
214
|
+
//# sourceMappingURL=ws-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-adapter.js","sourceRoot":"","sources":["../src/ws-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { WSContext } from 'hono/ws';
|
|
3
|
+
/** The event a frame from the client arrives on. */
|
|
4
|
+
declare const MESSAGE_EVENT = "message";
|
|
5
|
+
/** The event that says the socket has gone away. */
|
|
6
|
+
declare const CLOSE_EVENT = "close";
|
|
7
|
+
/**
|
|
8
|
+
* One frame from a client: the event it names, the payload it
|
|
9
|
+
* carries, and the correlation it asked to have repeated.
|
|
10
|
+
*/
|
|
11
|
+
interface WsFrame {
|
|
12
|
+
/** The event the gateway answers. */
|
|
13
|
+
readonly event: string;
|
|
14
|
+
/** What the handler is given. */
|
|
15
|
+
readonly data?: unknown;
|
|
16
|
+
/** The correlation the answer repeats, when there is one. */
|
|
17
|
+
readonly id?: string | number;
|
|
18
|
+
}
|
|
19
|
+
/** What a gateway answers with. */
|
|
20
|
+
interface WsReply {
|
|
21
|
+
/** The event being answered. */
|
|
22
|
+
readonly event: string;
|
|
23
|
+
/** What the handler returned, when it returned anything. */
|
|
24
|
+
readonly data?: unknown;
|
|
25
|
+
/** The correlation the client asked to have repeated. */
|
|
26
|
+
readonly id?: string | number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Reads a frame from what the socket delivered. Text frames
|
|
30
|
+
* arrive as text, binary frames as bytes, and a socket may hand
|
|
31
|
+
* over an already parsed frame when one is replayed.
|
|
32
|
+
*/
|
|
33
|
+
declare function parseFrame(raw: unknown): WsFrame | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The frame that answers one, or nothing when the handler
|
|
36
|
+
* answered with nothing and the client asked for no
|
|
37
|
+
* acknowledgement.
|
|
38
|
+
*
|
|
39
|
+
* A handler that answered with its own `{ event, data }` keeps
|
|
40
|
+
* that shape, which is what the `ws` platform sends; anything
|
|
41
|
+
* else is wrapped in the event being answered.
|
|
42
|
+
*/
|
|
43
|
+
declare function toReply(frame: WsFrame, value: unknown): WsReply | undefined;
|
|
44
|
+
/** Says whether an answer is worth sending. */
|
|
45
|
+
declare function isReply(value: WsReply | undefined): value is WsReply;
|
|
46
|
+
/**
|
|
47
|
+
* One client connection, in the shape Nest's WebSocket layer
|
|
48
|
+
* reads: a `message` event for every frame the client sends, a
|
|
49
|
+
* `close` event once, and `send()` for the answers. Nest
|
|
50
|
+
* subscribes with `on` and `once`, so this follows the Node
|
|
51
|
+
* emitter its own adapters use rather than an `EventTarget`.
|
|
52
|
+
*/
|
|
53
|
+
declare class HonoSocket extends EventEmitter {
|
|
54
|
+
private socket;
|
|
55
|
+
/** Binds this client to the socket the upgrade produced. */
|
|
56
|
+
use(socket: WSContext): void;
|
|
57
|
+
/** Emits the frame the client sent. */
|
|
58
|
+
deliver(data: unknown): void;
|
|
59
|
+
/** Says the socket is gone, and only ever once. */
|
|
60
|
+
disconnect(): void;
|
|
61
|
+
/** Sends one answer, unless the socket is no longer open. */
|
|
62
|
+
sendJson(reply: unknown): void;
|
|
63
|
+
/** Closes the socket from this side. */
|
|
64
|
+
close(): void;
|
|
65
|
+
}
|
|
66
|
+
export { CLOSE_EVENT, MESSAGE_EVENT, HonoSocket, isReply, parseFrame, toReply, };
|
|
67
|
+
export type { WsFrame, WsReply };
|
|
68
|
+
//# sourceMappingURL=ws-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,oDAAoD;AACpD,QAAA,MAAM,aAAa,YAAY,CAAC;AAEhC,oDAAoD;AACpD,QAAA,MAAM,WAAW,UAAU,CAAC;AAK5B;;;GAGG;AACH,UAAU,OAAO;IACf,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED,mCAAmC;AACnC,UAAU,OAAO;IACf,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAsCD;;;;GAIG;AACH,iBAAS,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAQrD;AAiBD;;;;;;;;GAQG;AACH,iBAAS,OAAO,CACd,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,OAAO,GACb,OAAO,GAAG,SAAS,CAYrB;AAED,+CAA+C;AAC/C,iBAAS,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,IAAI,OAAO,CAE7D;AAED;;;;;;GAMG;AACH,cAAM,UAAW,SAAQ,YAAY;IACnC,OAAO,CAAC,MAAM,CAAwB;IAEtC,4DAA4D;IACrD,GAAG,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAElC;IAED,uCAAuC;IAChC,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAElC;IAED,mDAAmD;IAC5C,UAAU,IAAI,IAAI,CAGxB;IAED,6DAA6D;IACtD,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CASpC;IAED,wCAAwC;IACjC,KAAK,IAAI,IAAI,CAMnB;CACF;AAED,OAAO,EACL,WAAW,EACX,aAAa,EACb,UAAU,EACV,OAAO,EACP,UAAU,EACV,OAAO,GACR,CAAC;AACF,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
/** The event a frame from the client arrives on. */
|
|
3
|
+
const MESSAGE_EVENT = 'message';
|
|
4
|
+
/** The event that says the socket has gone away. */
|
|
5
|
+
const CLOSE_EVENT = 'close';
|
|
6
|
+
/** The state a socket is in once it accepts a send. */
|
|
7
|
+
const OPEN_STATE = 1;
|
|
8
|
+
/** Says whether a value is a frame, by the event it names. */
|
|
9
|
+
function isFrame(value) {
|
|
10
|
+
if (typeof value !== 'object' || value === null) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
return 'event' in value && typeof value.event === 'string';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads the frame a parsed payload carries. A payload that is
|
|
17
|
+
* only a string names the event and carries no data, which is
|
|
18
|
+
* how a gateway is called without arguments.
|
|
19
|
+
*/
|
|
20
|
+
function frameFrom(value) {
|
|
21
|
+
if (isFrame(value)) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
return { event: value };
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reads a frame from text. Text that is not JSON is taken as
|
|
31
|
+
* the event name itself, so a client may send `ping` as well as
|
|
32
|
+
* `{"event":"ping"}`.
|
|
33
|
+
*/
|
|
34
|
+
function parseText(text) {
|
|
35
|
+
try {
|
|
36
|
+
return frameFrom(JSON.parse(text));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return { event: text };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Reads a frame from what the socket delivered. Text frames
|
|
44
|
+
* arrive as text, binary frames as bytes, and a socket may hand
|
|
45
|
+
* over an already parsed frame when one is replayed.
|
|
46
|
+
*/
|
|
47
|
+
function parseFrame(raw) {
|
|
48
|
+
if (typeof raw === 'string') {
|
|
49
|
+
return parseText(raw);
|
|
50
|
+
}
|
|
51
|
+
if (raw instanceof Uint8Array) {
|
|
52
|
+
return parseText(new TextDecoder().decode(raw));
|
|
53
|
+
}
|
|
54
|
+
return frameFrom(raw);
|
|
55
|
+
}
|
|
56
|
+
/** Repeats the correlation the client asked for, when it did. */
|
|
57
|
+
function withCorrelation(answer, frame) {
|
|
58
|
+
if (frame.id === undefined) {
|
|
59
|
+
return answer;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
data: answer.data,
|
|
63
|
+
event: answer.event,
|
|
64
|
+
id: frame.id,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The frame that answers one, or nothing when the handler
|
|
69
|
+
* answered with nothing and the client asked for no
|
|
70
|
+
* acknowledgement.
|
|
71
|
+
*
|
|
72
|
+
* A handler that answered with its own `{ event, data }` keeps
|
|
73
|
+
* that shape, which is what the `ws` platform sends; anything
|
|
74
|
+
* else is wrapped in the event being answered.
|
|
75
|
+
*/
|
|
76
|
+
function toReply(frame, value) {
|
|
77
|
+
if (value === undefined || value === null) {
|
|
78
|
+
if (frame.id === undefined) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return { event: frame.event, id: frame.id };
|
|
82
|
+
}
|
|
83
|
+
if (isFrame(value)) {
|
|
84
|
+
return withCorrelation(value, frame);
|
|
85
|
+
}
|
|
86
|
+
const answer = { data: value, event: frame.event };
|
|
87
|
+
return withCorrelation(answer, frame);
|
|
88
|
+
}
|
|
89
|
+
/** Says whether an answer is worth sending. */
|
|
90
|
+
function isReply(value) {
|
|
91
|
+
return value !== undefined;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One client connection, in the shape Nest's WebSocket layer
|
|
95
|
+
* reads: a `message` event for every frame the client sends, a
|
|
96
|
+
* `close` event once, and `send()` for the answers. Nest
|
|
97
|
+
* subscribes with `on` and `once`, so this follows the Node
|
|
98
|
+
* emitter its own adapters use rather than an `EventTarget`.
|
|
99
|
+
*/
|
|
100
|
+
class HonoSocket extends EventEmitter {
|
|
101
|
+
/** Binds this client to the socket the upgrade produced. */
|
|
102
|
+
use(socket) {
|
|
103
|
+
this.socket = socket;
|
|
104
|
+
}
|
|
105
|
+
/** Emits the frame the client sent. */
|
|
106
|
+
deliver(data) {
|
|
107
|
+
this.emit(MESSAGE_EVENT, data);
|
|
108
|
+
}
|
|
109
|
+
/** Says the socket is gone, and only ever once. */
|
|
110
|
+
disconnect() {
|
|
111
|
+
this.socket = undefined;
|
|
112
|
+
this.emit(CLOSE_EVENT);
|
|
113
|
+
}
|
|
114
|
+
/** Sends one answer, unless the socket is no longer open. */
|
|
115
|
+
sendJson(reply) {
|
|
116
|
+
const { socket } = this;
|
|
117
|
+
if (socket === undefined) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (socket.readyState !== OPEN_STATE) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
socket.send(JSON.stringify(reply));
|
|
124
|
+
}
|
|
125
|
+
/** Closes the socket from this side. */
|
|
126
|
+
close() {
|
|
127
|
+
const { socket } = this;
|
|
128
|
+
if (socket === undefined) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
socket.close();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export { CLOSE_EVENT, MESSAGE_EVENT, HonoSocket, isReply, parseFrame, toReply, };
|
|
135
|
+
//# sourceMappingURL=ws-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { HonoSocket } from './ws-client.ts';
|
|
3
|
+
/** The event Nest listens on for every new socket. */
|
|
4
|
+
declare const CONNECTION_EVENT = "connection";
|
|
5
|
+
/**
|
|
6
|
+
* The sockets of one gateway path.
|
|
7
|
+
*
|
|
8
|
+
* Nest binds a connection handler to the server `create()`
|
|
9
|
+
* returns and then, per socket, its message handlers and its
|
|
10
|
+
* disconnect hook. This is the emitter those bindings need: it
|
|
11
|
+
* emits `connection` with each socket and holds that socket
|
|
12
|
+
* until the path is closed, so no listener outlives the
|
|
13
|
+
* connection it was bound to.
|
|
14
|
+
*/
|
|
15
|
+
declare class GatewayServer extends EventEmitter {
|
|
16
|
+
private readonly sockets;
|
|
17
|
+
/** Tracks a socket and hands it to Nest. */
|
|
18
|
+
accept(socket: HonoSocket): void;
|
|
19
|
+
/** Closes every socket on the path. */
|
|
20
|
+
close(): void;
|
|
21
|
+
}
|
|
22
|
+
export { CONNECTION_EVENT, GatewayServer };
|
|
23
|
+
//# sourceMappingURL=ws-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-server.d.ts","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,sDAAsD;AACtD,QAAA,MAAM,gBAAgB,eAAe,CAAC;AAEtC;;;;;;;;;GASG;AACH,cAAM,aAAc,SAAQ,YAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IAEjD,4CAA4C;IACrC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAMtC;IAED,uCAAuC;IAChC,KAAK,IAAI,IAAI,CAKnB;CACF;AAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { CLOSE_EVENT } from './ws-client.js';
|
|
3
|
+
/** The event Nest listens on for every new socket. */
|
|
4
|
+
const CONNECTION_EVENT = 'connection';
|
|
5
|
+
/**
|
|
6
|
+
* The sockets of one gateway path.
|
|
7
|
+
*
|
|
8
|
+
* Nest binds a connection handler to the server `create()`
|
|
9
|
+
* returns and then, per socket, its message handlers and its
|
|
10
|
+
* disconnect hook. This is the emitter those bindings need: it
|
|
11
|
+
* emits `connection` with each socket and holds that socket
|
|
12
|
+
* until the path is closed, so no listener outlives the
|
|
13
|
+
* connection it was bound to.
|
|
14
|
+
*/
|
|
15
|
+
class GatewayServer extends EventEmitter {
|
|
16
|
+
constructor() {
|
|
17
|
+
super(...arguments);
|
|
18
|
+
this.sockets = new Set();
|
|
19
|
+
}
|
|
20
|
+
/** Tracks a socket and hands it to Nest. */
|
|
21
|
+
accept(socket) {
|
|
22
|
+
this.sockets.add(socket);
|
|
23
|
+
socket.once(CLOSE_EVENT, () => {
|
|
24
|
+
this.sockets.delete(socket);
|
|
25
|
+
});
|
|
26
|
+
this.emit(CONNECTION_EVENT, socket);
|
|
27
|
+
}
|
|
28
|
+
/** Closes every socket on the path. */
|
|
29
|
+
close() {
|
|
30
|
+
for (const socket of this.sockets) {
|
|
31
|
+
socket.close();
|
|
32
|
+
}
|
|
33
|
+
this.sockets.clear();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export { CONNECTION_EVENT, GatewayServer };
|
|
37
|
+
//# sourceMappingURL=ws-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-server.js","sourceRoot":"","sources":["../src/ws-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C,sDAAsD;AACtD,MAAM,gBAAgB,GAAG,YAAY,CAAC;AAEtC;;;;;;;;;GASG;AACH,MAAM,aAAc,SAAQ,YAAY;IAAxC;;QACmB,YAAO,GAAG,IAAI,GAAG,EAAc,CAAC;IAkBnD,CAAC;IAhBC,4CAA4C;IACrC,MAAM,CAAC,MAAkB;QAC9B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,EAAE;YAC5B,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAED,uCAAuC;IAChC,KAAK;QACV,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF;AAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/ws.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WebSocket entry point, kept out of the root module so a
|
|
3
|
+
* deployment that only serves HTTP never resolves
|
|
4
|
+
* `@nestjs/websockets` or `@hono/node-ws`: both are optional
|
|
5
|
+
* peers, and neither is needed to run the HTTP adapter.
|
|
6
|
+
*
|
|
7
|
+
* It is published as the `@ailura/nestjs-hono-adapter/ws`
|
|
8
|
+
* subpath rather than re-exported from `index.ts`, because an
|
|
9
|
+
* ESM re-export resolves eagerly and would load them anyway.
|
|
10
|
+
*/
|
|
11
|
+
export { HonoWsAdapter } from './ws-adapter.ts';
|
|
12
|
+
export type { HonoGatewayOptions } from './ws-adapter.ts';
|
|
13
|
+
export type { HonoSocket } from './ws-client.ts';
|
|
14
|
+
//# sourceMappingURL=ws.d.ts.map
|
package/dist/ws.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/ws.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WebSocket entry point, kept out of the root module so a
|
|
3
|
+
* deployment that only serves HTTP never resolves
|
|
4
|
+
* `@nestjs/websockets` or `@hono/node-ws`: both are optional
|
|
5
|
+
* peers, and neither is needed to run the HTTP adapter.
|
|
6
|
+
*
|
|
7
|
+
* It is published as the `@ailura/nestjs-hono-adapter/ws`
|
|
8
|
+
* subpath rather than re-exported from `index.ts`, because an
|
|
9
|
+
* ESM re-export resolves eagerly and would load them anyway.
|
|
10
|
+
*/
|
|
11
|
+
export { HonoWsAdapter } from './ws-adapter.js';
|
|
12
|
+
//# sourceMappingURL=ws.js.map
|
package/dist/ws.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ailura/nestjs-hono-adapter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "NestJS HTTP adapter that runs an application on Hono.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"adapter",
|
|
7
|
+
"hono",
|
|
8
|
+
"http",
|
|
9
|
+
"nestjs",
|
|
10
|
+
"node"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/ailuracollective/nestjs-hono-adapter#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ailuracollective/nestjs-hono-adapter/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Ailura Code",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/ailuracollective/nestjs-hono-adapter.git"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./ws": {
|
|
36
|
+
"types": "./dist/ws.d.ts",
|
|
37
|
+
"default": "./dist/ws.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"check": "bun run lint && bun run format:check && bun run typecheck && bun run test && bun run build",
|
|
46
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
47
|
+
"format": "oxfmt --write .",
|
|
48
|
+
"format:check": "oxfmt --check .",
|
|
49
|
+
"lint": "oxlint .",
|
|
50
|
+
"lint:fix": "oxlint --fix .",
|
|
51
|
+
"prepack": "rm -rf dist *.tsbuildinfo && tsc -p tsconfig.json",
|
|
52
|
+
"prepare": "tsc -p tsconfig.json",
|
|
53
|
+
"release": "semantic-release",
|
|
54
|
+
"release:dry": "semantic-release --dry-run",
|
|
55
|
+
"test": "bun test",
|
|
56
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@hono/node-server": "^2.1.1",
|
|
60
|
+
"@hono/node-ws": "^1.3.1",
|
|
61
|
+
"@nestjs/common": "^12.0.3",
|
|
62
|
+
"@nestjs/core": "^12.0.3",
|
|
63
|
+
"@nestjs/microservices": "^12.0.3",
|
|
64
|
+
"@nestjs/websockets": "^12.0.3",
|
|
65
|
+
"@semantic-release/changelog": "^7",
|
|
66
|
+
"@semantic-release/git": "^11",
|
|
67
|
+
"@types/bun": "^1.4.2",
|
|
68
|
+
"@types/node": "^22.20.3",
|
|
69
|
+
"@types/ws": "^8.18.1",
|
|
70
|
+
"conventional-changelog-conventionalcommits": "^10",
|
|
71
|
+
"hono": "^4.13.8",
|
|
72
|
+
"oxfmt": "^0.68.0",
|
|
73
|
+
"oxlint": "^1.83.0",
|
|
74
|
+
"oxlint-tsgolint": "^7.0.2002",
|
|
75
|
+
"reflect-metadata": "^0.2.2",
|
|
76
|
+
"rxjs": "^7.8.2",
|
|
77
|
+
"semantic-release": "^25",
|
|
78
|
+
"typescript": "^7.0.2"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"@hono/node-server": "^2.1.1",
|
|
82
|
+
"@hono/node-ws": "^1.3.1",
|
|
83
|
+
"@nestjs/common": ">=11 <13",
|
|
84
|
+
"@nestjs/core": ">=11 <13",
|
|
85
|
+
"@nestjs/websockets": ">=11 <13",
|
|
86
|
+
"hono": "^4.13.8"
|
|
87
|
+
},
|
|
88
|
+
"peerDependenciesMeta": {
|
|
89
|
+
"@hono/node-ws": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"@nestjs/websockets": {
|
|
93
|
+
"optional": true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"engines": {
|
|
97
|
+
"node": ">=22.12.0"
|
|
98
|
+
}
|
|
99
|
+
}
|