@nmtjs/ws-transport 0.12.9 → 0.13.1
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/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/injectables.d.ts +2 -10
- package/dist/runtimes/bun.d.ts +2 -0
- package/dist/runtimes/bun.js +68 -0
- package/dist/runtimes/deno.d.ts +2 -0
- package/dist/runtimes/deno.js +91 -0
- package/dist/runtimes/node.d.ts +2 -0
- package/dist/runtimes/node.js +127 -0
- package/dist/server.d.ts +19 -16
- package/dist/server.js +303 -322
- package/dist/types.d.ts +87 -21
- package/dist/utils.d.ts +3 -21
- package/dist/utils.js +14 -86
- package/package.json +35 -12
- package/dist/transport.d.ts +0 -2
- package/dist/transport.js +0 -5
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/injectables.d.ts
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
1
|
import { type LazyInjectable, Scope } from '@nmtjs/core';
|
|
2
|
+
import type { WsTransportServerRequest } from './types.ts';
|
|
2
3
|
export declare const WsTransportInjectables: {
|
|
3
|
-
readonly connectionData: LazyInjectable<
|
|
4
|
-
url: string;
|
|
5
|
-
origin: URL | null;
|
|
6
|
-
method: string;
|
|
7
|
-
headers: Headers;
|
|
8
|
-
querystring: string;
|
|
9
|
-
query: URLSearchParams;
|
|
10
|
-
remoteAddress: string;
|
|
11
|
-
proxiedRemoteAddress: string;
|
|
12
|
-
}>, Scope.Connection>;
|
|
4
|
+
readonly connectionData: LazyInjectable<WsTransportServerRequest, Scope.Connection>;
|
|
13
5
|
readonly httpResponseHeaders: LazyInjectable<Headers, Scope.Call>;
|
|
14
6
|
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createTransport } from '@nmtjs/protocol/server';
|
|
2
|
+
import createAdapter from 'crossws/adapters/bun';
|
|
3
|
+
import { WsTransportServer } from "../server.js";
|
|
4
|
+
import { InternalServerErrorHttpResponse, NotFoundHttpResponse, StatusResponse, } from "../utils.js";
|
|
5
|
+
function adapterFactory(params) {
|
|
6
|
+
const adapter = createAdapter({ hooks: params.wsHooks });
|
|
7
|
+
let server = null;
|
|
8
|
+
function createServer() {
|
|
9
|
+
return globalThis.Bun.serve({
|
|
10
|
+
...params.runtime?.server,
|
|
11
|
+
unix: params.listen.unix,
|
|
12
|
+
port: params.listen.port,
|
|
13
|
+
hostname: params.listen.hostname,
|
|
14
|
+
reusePort: params.listen.reusePort,
|
|
15
|
+
tls: params.tls
|
|
16
|
+
? {
|
|
17
|
+
cert: params.tls.cert,
|
|
18
|
+
key: params.tls.key,
|
|
19
|
+
passphrase: params.tls.passphrase,
|
|
20
|
+
}
|
|
21
|
+
: undefined,
|
|
22
|
+
websocket: {
|
|
23
|
+
...params.runtime?.ws,
|
|
24
|
+
...adapter.websocket,
|
|
25
|
+
},
|
|
26
|
+
routes: {
|
|
27
|
+
...params.runtime?.server?.routes,
|
|
28
|
+
'/healthy': StatusResponse(),
|
|
29
|
+
},
|
|
30
|
+
async fetch(request, server) {
|
|
31
|
+
const url = new URL(request.url);
|
|
32
|
+
if (url.pathname.startsWith(params.apiPath)) {
|
|
33
|
+
try {
|
|
34
|
+
if (request.headers.get('upgrade') === 'websocket') {
|
|
35
|
+
return await adapter.handleUpgrade(request, server);
|
|
36
|
+
}
|
|
37
|
+
const { body, headers, method } = request;
|
|
38
|
+
return await params.fetchHandler({
|
|
39
|
+
url,
|
|
40
|
+
method,
|
|
41
|
+
headers,
|
|
42
|
+
}, body, request.signal);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
params.logger.error({ err }, 'Error in fetch handler');
|
|
46
|
+
return InternalServerErrorHttpResponse();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return NotFoundHttpResponse();
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
start: async () => {
|
|
55
|
+
server = createServer();
|
|
56
|
+
return server.url.href;
|
|
57
|
+
},
|
|
58
|
+
stop: async () => {
|
|
59
|
+
if (server) {
|
|
60
|
+
await server.stop();
|
|
61
|
+
server = null;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export const WsTransport = createTransport('WsTransport', (context, options) => {
|
|
67
|
+
return new WsTransportServer(adapterFactory, context, options);
|
|
68
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { createTransport } from '@nmtjs/protocol/server';
|
|
2
|
+
import createAdapter from 'crossws/adapters/deno';
|
|
3
|
+
import { WsTransportServer } from "../server.js";
|
|
4
|
+
import { InternalServerErrorHttpResponse, NotFoundHttpResponse, StatusResponse, } from "../utils.js";
|
|
5
|
+
function adapterFactory(params) {
|
|
6
|
+
const adapter = createAdapter({ hooks: params.wsHooks });
|
|
7
|
+
let server = null;
|
|
8
|
+
function createServer() {
|
|
9
|
+
const listenOptions = params.listen.unix
|
|
10
|
+
? { path: params.listen.unix }
|
|
11
|
+
: {
|
|
12
|
+
port: params.listen.port,
|
|
13
|
+
hostname: params.listen.hostname,
|
|
14
|
+
reusePort: params.listen.reusePort,
|
|
15
|
+
};
|
|
16
|
+
const options = {
|
|
17
|
+
...listenOptions,
|
|
18
|
+
tls: params.tls
|
|
19
|
+
? {
|
|
20
|
+
cert: params.tls.cert,
|
|
21
|
+
key: params.tls.key,
|
|
22
|
+
passphrase: params.tls.passphrase,
|
|
23
|
+
}
|
|
24
|
+
: undefined,
|
|
25
|
+
};
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
const server = globalThis.Deno.serve({
|
|
28
|
+
...params.runtime?.server,
|
|
29
|
+
...options,
|
|
30
|
+
handler: async (request, info) => {
|
|
31
|
+
const url = new URL(request.url);
|
|
32
|
+
if (url.pathname.startsWith(params.apiPath)) {
|
|
33
|
+
try {
|
|
34
|
+
if (request.headers.get('upgrade') === 'websocket') {
|
|
35
|
+
return await adapter.handleUpgrade(request, info);
|
|
36
|
+
}
|
|
37
|
+
const { headers, method, body } = request;
|
|
38
|
+
return await params.fetchHandler({
|
|
39
|
+
url,
|
|
40
|
+
method,
|
|
41
|
+
headers,
|
|
42
|
+
}, body, request.signal);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
params.logger.error({ err }, 'Error in fetch handler');
|
|
46
|
+
return InternalServerErrorHttpResponse();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (url.pathname === '/healthy') {
|
|
50
|
+
return StatusResponse();
|
|
51
|
+
}
|
|
52
|
+
return NotFoundHttpResponse();
|
|
53
|
+
},
|
|
54
|
+
onListen(addr) {
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
resolve({ server, addr });
|
|
57
|
+
}, 1);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
start: async () => {
|
|
64
|
+
const { server: _server, addr } = await createServer();
|
|
65
|
+
server = _server;
|
|
66
|
+
switch (addr.transport) {
|
|
67
|
+
case 'unix':
|
|
68
|
+
case 'unixpacket':
|
|
69
|
+
return `unix://${addr.path}`;
|
|
70
|
+
case 'tcp':
|
|
71
|
+
case 'udp': {
|
|
72
|
+
const proto = params.tls ? 'https' : 'http';
|
|
73
|
+
return `${proto}://${addr.hostname}:${addr.port}`;
|
|
74
|
+
}
|
|
75
|
+
case 'vsock':
|
|
76
|
+
return `vsock://${addr.cid}:${addr.port}`;
|
|
77
|
+
default:
|
|
78
|
+
throw new Error(`Unsupported address transport`);
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
stop: async () => {
|
|
82
|
+
if (server) {
|
|
83
|
+
await server.shutdown();
|
|
84
|
+
server = null;
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
export const WsTransport = createTransport('WsTransport', (context, options) => {
|
|
90
|
+
return new WsTransportServer(adapterFactory, context, options);
|
|
91
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { App, SSLApp } from 'uWebSockets.js';
|
|
2
|
+
import { createTransport } from '@nmtjs/protocol/server';
|
|
3
|
+
import createAdapter from 'crossws/adapters/uws';
|
|
4
|
+
import { WsTransportServer } from "../server.js";
|
|
5
|
+
import { InternalServerErrorHttpResponse, NotFoundHttpResponse, StatusResponse, } from "../utils.js";
|
|
6
|
+
function adapterFactory(params) {
|
|
7
|
+
const adapter = createAdapter({ hooks: params.wsHooks });
|
|
8
|
+
const server = params.tls
|
|
9
|
+
? SSLApp({
|
|
10
|
+
passphrase: params.tls.passphrase,
|
|
11
|
+
key_file_name: params.tls.key,
|
|
12
|
+
cert_file_name: params.tls.cert,
|
|
13
|
+
})
|
|
14
|
+
: App();
|
|
15
|
+
server
|
|
16
|
+
.ws(params.apiPath, {
|
|
17
|
+
...params.runtime?.ws,
|
|
18
|
+
...adapter.websocket,
|
|
19
|
+
})
|
|
20
|
+
.get('/healthy', async (res) => {
|
|
21
|
+
res.onAborted(() => { });
|
|
22
|
+
const response = StatusResponse();
|
|
23
|
+
res.cork(async () => {
|
|
24
|
+
res
|
|
25
|
+
.writeStatus(`${response.status} ${response.statusText}`)
|
|
26
|
+
.end(await response.arrayBuffer());
|
|
27
|
+
});
|
|
28
|
+
})
|
|
29
|
+
.any('/*', async (res, req) => {
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
res.onAborted(() => controller.abort());
|
|
32
|
+
let response = NotFoundHttpResponse();
|
|
33
|
+
const headers = new Headers();
|
|
34
|
+
const method = req.getMethod();
|
|
35
|
+
req.forEach((k, v) => headers.append(k, v));
|
|
36
|
+
const host = headers.get('host') || 'localhost';
|
|
37
|
+
const proto = headers.get('x-forwarded-proto') || params.tls ? 'https' : 'http';
|
|
38
|
+
const url = new URL(req.getUrl(), `${proto}://${host}`);
|
|
39
|
+
if (url.pathname.startsWith(params.apiPath)) {
|
|
40
|
+
try {
|
|
41
|
+
const body = new ReadableStream({
|
|
42
|
+
start(controller) {
|
|
43
|
+
res.onData((chunk, isLast) => {
|
|
44
|
+
if (chunk)
|
|
45
|
+
controller.enqueue(Buffer.from(chunk.slice(0)));
|
|
46
|
+
if (isLast)
|
|
47
|
+
controller.close();
|
|
48
|
+
});
|
|
49
|
+
res.onAborted(() => controller.error());
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
response = await params.fetchHandler({
|
|
53
|
+
url,
|
|
54
|
+
method,
|
|
55
|
+
headers,
|
|
56
|
+
}, body, controller.signal);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
params.logger.error({ err }, 'Error in fetch handler');
|
|
60
|
+
response = InternalServerErrorHttpResponse();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (controller.signal.aborted)
|
|
64
|
+
return undefined;
|
|
65
|
+
else {
|
|
66
|
+
res.cork(() => {
|
|
67
|
+
res.writeStatus(`${response.status.toString()} ${response.statusText}`);
|
|
68
|
+
response.headers.forEach((v, k) => res.writeHeader(k, v));
|
|
69
|
+
});
|
|
70
|
+
if (response.body) {
|
|
71
|
+
try {
|
|
72
|
+
const reader = response.body.getReader();
|
|
73
|
+
let chunk = await reader.read();
|
|
74
|
+
do {
|
|
75
|
+
if (controller.signal.aborted)
|
|
76
|
+
break;
|
|
77
|
+
if (chunk.value)
|
|
78
|
+
res.write(chunk.value);
|
|
79
|
+
chunk = await reader.read();
|
|
80
|
+
} while (!chunk.done);
|
|
81
|
+
res.end();
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
res.close();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
res.end();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return {
|
|
93
|
+
start: () => new Promise((resolve, reject) => {
|
|
94
|
+
if (params.listen.unix) {
|
|
95
|
+
server.listen_unix((socket) => {
|
|
96
|
+
if (socket) {
|
|
97
|
+
resolve('unix://' + params.listen.unix);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
reject(new Error('Failed to start WebSockets server'));
|
|
101
|
+
}
|
|
102
|
+
}, params.listen.unix);
|
|
103
|
+
}
|
|
104
|
+
else if (typeof params.listen.port === 'number') {
|
|
105
|
+
const proto = params.tls ? 'https' : 'http';
|
|
106
|
+
const hostname = params.listen.hostname || '127.0.0.1';
|
|
107
|
+
server.listen(hostname, params.listen.port, (socket) => {
|
|
108
|
+
if (socket) {
|
|
109
|
+
resolve(`${proto}://${hostname}:${params.listen.port}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
reject(new Error('Failed to start WebSockets server'));
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
reject(new Error('Invalid listen parameters'));
|
|
118
|
+
}
|
|
119
|
+
}),
|
|
120
|
+
stop: () => {
|
|
121
|
+
server.close();
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export const WsTransport = createTransport('WsTransport', (context, options) => {
|
|
126
|
+
return new WsTransportServer(adapterFactory, context, options);
|
|
127
|
+
});
|
package/dist/server.d.ts
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import { type HttpRequest, type HttpResponse, type TemplatedApp } from 'uWebSockets.js';
|
|
2
1
|
import { ClientMessageType, type ServerMessageType } from '@nmtjs/protocol';
|
|
3
2
|
import { Connection, type Transport, type TransportPluginContext } from '@nmtjs/protocol/server';
|
|
4
|
-
import
|
|
3
|
+
import { type Peer } from 'crossws';
|
|
4
|
+
import type { WsAdapterServerFactory, WsConnectionData, WsTransportOptions, WsTransportServerRequest } from './types.ts';
|
|
5
5
|
export declare class WsTransportServer implements Transport<WsConnectionData> {
|
|
6
|
+
#private;
|
|
7
|
+
protected readonly adapterFactory: WsAdapterServerFactory<any>;
|
|
6
8
|
protected readonly context: TransportPluginContext;
|
|
7
9
|
protected readonly options: WsTransportOptions;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
constructor(context: TransportPluginContext, options: WsTransportOptions);
|
|
11
|
-
send(connection: Connection<WsConnectionData>, messageType: ServerMessageType, buffer: ArrayBuffer): void;
|
|
10
|
+
clients: Map<string, Peer<import("crossws").AdapterInternal>>;
|
|
11
|
+
constructor(adapterFactory: WsAdapterServerFactory<any>, context: TransportPluginContext, options: WsTransportOptions);
|
|
12
12
|
start(): Promise<void>;
|
|
13
13
|
stop(): Promise<void>;
|
|
14
|
-
|
|
14
|
+
send(connection: Connection<WsConnectionData>, messageType: ServerMessageType, buffer: ArrayBuffer): void;
|
|
15
|
+
httpHandler(request: WsTransportServerRequest, body: ReadableStream | null, requestSignal: AbortSignal): Promise<Response>;
|
|
16
|
+
private applyCors;
|
|
15
17
|
protected get protocol(): import("@nmtjs/protocol/server").Protocol;
|
|
16
18
|
protected get logger(): import("@nmtjs/core").Logger;
|
|
17
19
|
protected logError(cause: any, message?: string): Promise<void>;
|
|
18
|
-
protected
|
|
19
|
-
protected [ClientMessageType.
|
|
20
|
-
protected [ClientMessageType.
|
|
21
|
-
protected [ClientMessageType.
|
|
22
|
-
protected [ClientMessageType.
|
|
23
|
-
protected [ClientMessageType.
|
|
24
|
-
protected [ClientMessageType.
|
|
25
|
-
protected [ClientMessageType.
|
|
26
|
-
|
|
20
|
+
protected [ClientMessageType.Rpc](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
21
|
+
protected [ClientMessageType.RpcAbort](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
22
|
+
protected [ClientMessageType.RpcStreamAbort](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
23
|
+
protected [ClientMessageType.ClientStreamPush](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
24
|
+
protected [ClientMessageType.ClientStreamEnd](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
25
|
+
protected [ClientMessageType.ClientStreamAbort](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
26
|
+
protected [ClientMessageType.ServerStreamPull](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
27
|
+
protected [ClientMessageType.ServerStreamAbort](peer: Peer, buffer: ArrayBuffer, connectionId: string): void;
|
|
28
|
+
private createWsHooks;
|
|
29
|
+
private createServer;
|
|
27
30
|
}
|