@socket-mesh/client 18.0.9 → 18.1.2
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 +2 -2
- package/dist/client-auth-engine.d.ts +22 -0
- package/dist/client-auth-engine.js +52 -0
- package/dist/client-channels.d.ts +22 -0
- package/dist/client-channels.js +149 -0
- package/dist/client-socket-options.d.ts +24 -0
- package/dist/client-socket-options.js +6 -0
- package/dist/client-socket.d.ts +26 -0
- package/dist/client-socket.js +100 -0
- package/dist/client-transport.d.ts +52 -0
- package/dist/client-transport.js +262 -0
- package/dist/handlers/index.d.ts +4 -0
- package/dist/handlers/index.js +4 -0
- package/dist/handlers/kickout.d.ts +6 -0
- package/dist/handlers/kickout.js +5 -0
- package/dist/handlers/publish.d.ts +7 -0
- package/dist/handlers/publish.js +3 -0
- package/dist/handlers/remove-auth-token.d.ts +2 -0
- package/dist/handlers/remove-auth-token.js +3 -0
- package/dist/handlers/set-auth-token.d.ts +3 -0
- package/dist/handlers/set-auth-token.js +3 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/maps/client-map.d.ts +21 -0
- package/dist/maps/client-map.js +1 -0
- package/dist/maps/index.d.ts +3 -0
- package/dist/maps/index.js +3 -0
- package/dist/maps/server-map.d.ts +27 -0
- package/dist/maps/server-map.js +1 -0
- package/dist/maps/socket-map.d.ts +17 -0
- package/dist/maps/socket-map.js +1 -0
- package/dist/plugins/batching-plugin.d.ts +41 -0
- package/dist/plugins/batching-plugin.js +111 -0
- package/dist/plugins/in-order-plugin.d.ts +10 -0
- package/dist/plugins/in-order-plugin.js +70 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/offline-plugin.d.ts +13 -0
- package/dist/plugins/offline-plugin.js +47 -0
- package/package.json +59 -56
- package/auth.d.ts +0 -21
- package/auth.js +0 -49
- package/auth.js.map +0 -1
- package/client-options.d.ts +0 -53
- package/client-options.js +0 -2
- package/client-options.js.map +0 -1
- package/clientsocket.d.ts +0 -135
- package/clientsocket.js +0 -898
- package/clientsocket.js.map +0 -1
- package/events.d.ts +0 -54
- package/events.js +0 -2
- package/events.js.map +0 -1
- package/factory.d.ts +0 -3
- package/factory.js +0 -37
- package/factory.js.map +0 -1
- package/index.d.ts +0 -7
- package/index.js +0 -10
- package/index.js.map +0 -1
- package/remote-procedure.d.ts +0 -6
- package/remote-procedure.js +0 -10
- package/remote-procedure.js.map +0 -1
- package/socket-state.d.ts +0 -5
- package/socket-state.js +0 -7
- package/socket-state.js.map +0 -1
- package/transport-handlers.d.ts +0 -37
- package/transport-handlers.js +0 -2
- package/transport-handlers.js.map +0 -1
- package/transport.d.ts +0 -84
- package/transport.js +0 -441
- package/transport.js.map +0 -1
- package/wait.d.ts +0 -1
- package/wait.js +0 -8
- package/wait.js.map +0 -1
- package/ws-browser.d.ts +0 -15
- package/ws-browser.js +0 -40
- package/ws-browser.js.map +0 -1
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { SocketTransport } from "@socket-mesh/core";
|
|
2
|
+
import ws from "isomorphic-ws";
|
|
3
|
+
import { LocalStorageAuthEngine, isAuthEngine } from "./client-auth-engine.js";
|
|
4
|
+
import { hydrateError, socketProtocolErrorStatuses } from "@socket-mesh/errors";
|
|
5
|
+
export class ClientTransport extends SocketTransport {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
super(options);
|
|
8
|
+
this.type = 'client';
|
|
9
|
+
this._uri = typeof options.address === 'string' ? new URL(options.address) : options.address;
|
|
10
|
+
this.authEngine =
|
|
11
|
+
isAuthEngine(options.authEngine) ?
|
|
12
|
+
options.authEngine :
|
|
13
|
+
new LocalStorageAuthEngine(Object.assign({ authTokenName: `socketmesh.authToken${this._uri.protocol}${this._uri.hostname}` }, options.authEngine));
|
|
14
|
+
this.connectTimeoutMs = options.connectTimeoutMs ?? 20000;
|
|
15
|
+
this._pingTimeoutMs = this.connectTimeoutMs;
|
|
16
|
+
if (options.wsOptions) {
|
|
17
|
+
this._wsOptions = options.wsOptions;
|
|
18
|
+
}
|
|
19
|
+
this._connectAttempts = 0;
|
|
20
|
+
this._pendingReconnectTimeout = null;
|
|
21
|
+
this.autoReconnect = options.autoReconnect;
|
|
22
|
+
this.isPingTimeoutDisabled = (options.isPingTimeoutDisabled === true);
|
|
23
|
+
}
|
|
24
|
+
get autoReconnect() {
|
|
25
|
+
return this._autoReconnect;
|
|
26
|
+
}
|
|
27
|
+
set autoReconnect(value) {
|
|
28
|
+
if (value) {
|
|
29
|
+
this._autoReconnect = Object.assign({
|
|
30
|
+
initialDelay: 10000,
|
|
31
|
+
randomness: 10000,
|
|
32
|
+
multiplier: 1.5,
|
|
33
|
+
maxDelayMs: 60000
|
|
34
|
+
}, value === true ? {} : value);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
this._autoReconnect = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
connect(options) {
|
|
41
|
+
let timeoutMs = this.connectTimeoutMs;
|
|
42
|
+
if (options) {
|
|
43
|
+
let changeOptions = false;
|
|
44
|
+
if (options.connectTimeoutMs) {
|
|
45
|
+
timeoutMs = options.connectTimeoutMs;
|
|
46
|
+
}
|
|
47
|
+
if (options.address) {
|
|
48
|
+
changeOptions = true;
|
|
49
|
+
this._uri = typeof options.address === 'string' ? new URL(options.address) : options.address;
|
|
50
|
+
}
|
|
51
|
+
if (options.wsOptions) {
|
|
52
|
+
changeOptions = true;
|
|
53
|
+
this._wsOptions = options.wsOptions;
|
|
54
|
+
}
|
|
55
|
+
if (changeOptions && this.status !== 'closed') {
|
|
56
|
+
this.disconnect(1000, 'Socket was disconnected by the client to initiate a new connection');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (this.status === 'closed') {
|
|
60
|
+
this.webSocket = new ws(this._uri, this._wsOptions);
|
|
61
|
+
this.socket.emit('connecting', {});
|
|
62
|
+
this._connectTimeoutRef = setTimeout(() => {
|
|
63
|
+
this.disconnect(4007);
|
|
64
|
+
}, timeoutMs);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
get connectAttempts() {
|
|
68
|
+
return this._connectAttempts;
|
|
69
|
+
}
|
|
70
|
+
disconnect(code = 1000, reason) {
|
|
71
|
+
if (code !== 4007) {
|
|
72
|
+
this.resetReconnect();
|
|
73
|
+
}
|
|
74
|
+
super.disconnect(code, reason);
|
|
75
|
+
}
|
|
76
|
+
async handshake() {
|
|
77
|
+
const token = await this.authEngine.loadToken();
|
|
78
|
+
// Don't wait for this.state to be 'ready'.
|
|
79
|
+
// The underlying WebSocket (this.socket) is already open.
|
|
80
|
+
// The casting to HandshakeStatus has to be here or typescript freaks out
|
|
81
|
+
const status = await this.invoke('#handshake', { authToken: token })[0];
|
|
82
|
+
if ('authError' in status) {
|
|
83
|
+
status.authError = hydrateError(status.authError);
|
|
84
|
+
}
|
|
85
|
+
return status;
|
|
86
|
+
}
|
|
87
|
+
onClose(code, reason) {
|
|
88
|
+
const status = this.status;
|
|
89
|
+
let reconnecting = false;
|
|
90
|
+
super.onClose(code, reason);
|
|
91
|
+
// Try to reconnect
|
|
92
|
+
// on server ping timeout (4000)
|
|
93
|
+
// or on client pong timeout (4001)
|
|
94
|
+
// or on close without status (1005)
|
|
95
|
+
// or on handshake failure (4003)
|
|
96
|
+
// or on handshake rejection (4008)
|
|
97
|
+
// or on socket hung up (1006)
|
|
98
|
+
if (this.autoReconnect) {
|
|
99
|
+
if (code === 4000 || code === 4001 || code === 1005) {
|
|
100
|
+
// If there is a ping or pong timeout or socket closes without
|
|
101
|
+
// status, don't wait before trying to reconnect - These could happen
|
|
102
|
+
// if the client wakes up after a period of inactivity and in this case we
|
|
103
|
+
// want to re-establish the connection as soon as possible.
|
|
104
|
+
reconnecting = !!this.autoReconnect;
|
|
105
|
+
this.tryReconnect(0);
|
|
106
|
+
// Codes 4500 and above will be treated as permanent disconnects.
|
|
107
|
+
// Socket will not try to auto-reconnect.
|
|
108
|
+
}
|
|
109
|
+
else if (code !== 1000 && code < 4500) {
|
|
110
|
+
reconnecting = !!this.autoReconnect;
|
|
111
|
+
this.tryReconnect();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (!reconnecting) {
|
|
115
|
+
const strReason = reason?.toString() || socketProtocolErrorStatuses[code];
|
|
116
|
+
this.onDisconnect(status, code, strReason);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
onOpen() {
|
|
120
|
+
super.onOpen();
|
|
121
|
+
clearTimeout(this._connectTimeoutRef);
|
|
122
|
+
this._connectTimeoutRef = null;
|
|
123
|
+
this.resetReconnect();
|
|
124
|
+
this.resetPingTimeout(this.isPingTimeoutDisabled ? false : this.pingTimeoutMs, 4000);
|
|
125
|
+
let authError;
|
|
126
|
+
this.handshake()
|
|
127
|
+
.then(status => {
|
|
128
|
+
this.id = status.id;
|
|
129
|
+
this.pingTimeoutMs = status.pingTimeoutMs;
|
|
130
|
+
if ('authToken' in status && status.authToken) {
|
|
131
|
+
return this.setAuthorization(status.authToken);
|
|
132
|
+
}
|
|
133
|
+
if ('authError' in status) {
|
|
134
|
+
authError = status.authError;
|
|
135
|
+
}
|
|
136
|
+
return this.changeToUnauthenticatedState();
|
|
137
|
+
})
|
|
138
|
+
.then(() => {
|
|
139
|
+
this.setReadyStatus(this.pingTimeoutMs, authError);
|
|
140
|
+
})
|
|
141
|
+
.catch(err => {
|
|
142
|
+
if (err.statusCode == null) {
|
|
143
|
+
err.statusCode = 4003;
|
|
144
|
+
}
|
|
145
|
+
this.onError(err);
|
|
146
|
+
this.disconnect(err.statusCode, err.toString());
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
onPingPong() {
|
|
150
|
+
this.send('');
|
|
151
|
+
this.resetPingTimeout(this.isPingTimeoutDisabled ? false : this.pingTimeoutMs, 4000);
|
|
152
|
+
this.socket.emit('ping', {});
|
|
153
|
+
}
|
|
154
|
+
get pendingReconnect() {
|
|
155
|
+
return (this._pendingReconnectTimeout !== null);
|
|
156
|
+
}
|
|
157
|
+
get pingTimeoutMs() {
|
|
158
|
+
return this._pingTimeoutMs;
|
|
159
|
+
}
|
|
160
|
+
set pingTimeoutMs(value) {
|
|
161
|
+
this._pingTimeoutMs = value;
|
|
162
|
+
this.resetPingTimeout(this.isPingTimeoutDisabled ? false : this.pingTimeoutMs, 4000);
|
|
163
|
+
}
|
|
164
|
+
resetReconnect() {
|
|
165
|
+
this._pendingReconnectTimeout = null;
|
|
166
|
+
this._connectAttempts = 0;
|
|
167
|
+
}
|
|
168
|
+
async send(data) {
|
|
169
|
+
this.webSocket.send(data);
|
|
170
|
+
}
|
|
171
|
+
async setAuthorization(signedAuthToken, authToken) {
|
|
172
|
+
const wasAuthenticated = !!this.signedAuthToken;
|
|
173
|
+
const changed = await super.setAuthorization(signedAuthToken, authToken);
|
|
174
|
+
if (changed) {
|
|
175
|
+
this.triggerAuthenticationEvents(false, wasAuthenticated);
|
|
176
|
+
// Even if saving the auth token failes we do NOT want to throw an exception.
|
|
177
|
+
this.authEngine.saveToken(this.signedAuthToken)
|
|
178
|
+
.catch(err => {
|
|
179
|
+
this.onError(err);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return changed;
|
|
183
|
+
}
|
|
184
|
+
get status() {
|
|
185
|
+
if (this.pendingReconnect) {
|
|
186
|
+
return 'connecting';
|
|
187
|
+
}
|
|
188
|
+
return super.status;
|
|
189
|
+
}
|
|
190
|
+
tryReconnect(initialDelay) {
|
|
191
|
+
if (!this.autoReconnect) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const exponent = this._connectAttempts++;
|
|
195
|
+
const reconnectOptions = this.autoReconnect;
|
|
196
|
+
let timeoutMs;
|
|
197
|
+
if (initialDelay == null || exponent > 0) {
|
|
198
|
+
const initialTimeout = Math.round(reconnectOptions.initialDelay + (reconnectOptions.randomness || 0) * Math.random());
|
|
199
|
+
timeoutMs = Math.round(initialTimeout * Math.pow(reconnectOptions.multiplier, exponent));
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
timeoutMs = initialDelay;
|
|
203
|
+
}
|
|
204
|
+
if (timeoutMs > reconnectOptions.maxDelayMs) {
|
|
205
|
+
timeoutMs = reconnectOptions.maxDelayMs;
|
|
206
|
+
}
|
|
207
|
+
this._pendingReconnectTimeout = timeoutMs;
|
|
208
|
+
this.connect({ connectTimeoutMs: timeoutMs });
|
|
209
|
+
}
|
|
210
|
+
get uri() {
|
|
211
|
+
return this._uri;
|
|
212
|
+
}
|
|
213
|
+
get webSocket() {
|
|
214
|
+
return super.webSocket;
|
|
215
|
+
}
|
|
216
|
+
set webSocket(value) {
|
|
217
|
+
if (this.webSocket) {
|
|
218
|
+
if (this._connectTimeoutRef) {
|
|
219
|
+
clearTimeout(this._connectTimeoutRef);
|
|
220
|
+
this._connectTimeoutRef = null;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
super.webSocket = value;
|
|
224
|
+
if (this.webSocket && this.webSocket.on) {
|
|
225
|
+
// WebSockets will throw an error if they are closed before they are completely open.
|
|
226
|
+
// We hook into these events to supress those errors and clean them up after a connection is established.
|
|
227
|
+
function onOpenCloseError() {
|
|
228
|
+
this.off('open', onOpenCloseError);
|
|
229
|
+
this.off('close', onOpenCloseError);
|
|
230
|
+
this.off('error', onOpenCloseError);
|
|
231
|
+
}
|
|
232
|
+
this.webSocket.on('open', onOpenCloseError);
|
|
233
|
+
this.webSocket.on('close', onOpenCloseError);
|
|
234
|
+
this.webSocket.on('error', onOpenCloseError);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
async transmit(serviceAndMethod, arg) {
|
|
238
|
+
if (this.status === 'closed') {
|
|
239
|
+
this.connect();
|
|
240
|
+
await this.socket.listen('connect').once();
|
|
241
|
+
}
|
|
242
|
+
await super.transmit(serviceAndMethod, arg);
|
|
243
|
+
}
|
|
244
|
+
invoke(methodOptions, arg) {
|
|
245
|
+
let abort;
|
|
246
|
+
return [
|
|
247
|
+
Promise.resolve()
|
|
248
|
+
.then(() => {
|
|
249
|
+
if (this.status === 'closed') {
|
|
250
|
+
this.connect();
|
|
251
|
+
return this.socket.listen('connect').once();
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
.then(() => {
|
|
255
|
+
const result = super.invoke(methodOptions, arg);
|
|
256
|
+
abort = result[1];
|
|
257
|
+
return result[0];
|
|
258
|
+
}),
|
|
259
|
+
abort
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { RequestHandlerArgs } from "@socket-mesh/core";
|
|
2
|
+
import { ClientSocket } from "../client-socket.js";
|
|
3
|
+
import { ClientTransport } from "../client-transport.js";
|
|
4
|
+
import { ClientMap, KickOutOptions } from "../maps/client-map.js";
|
|
5
|
+
import { BasicSocketMapClient } from "../maps/socket-map.js";
|
|
6
|
+
export declare function kickOutHandler({ socket, options }: RequestHandlerArgs<KickOutOptions, BasicSocketMapClient, ClientSocket<ClientMap>, ClientTransport<ClientMap>>): Promise<void>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PublishOptions } from "@socket-mesh/channels";
|
|
2
|
+
import { RequestHandlerArgs } from "@socket-mesh/core";
|
|
3
|
+
import { ClientSocket } from "../client-socket.js";
|
|
4
|
+
import { ClientTransport } from "../client-transport.js";
|
|
5
|
+
import { ClientMap } from "../maps/client-map.js";
|
|
6
|
+
import { BasicSocketMapClient } from "../maps/socket-map.js";
|
|
7
|
+
export declare function publishHandler({ socket, options }: RequestHandlerArgs<PublishOptions, BasicSocketMapClient, ClientSocket<ClientMap>, ClientTransport<ClientMap>>): Promise<void>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./handlers/index.js";
|
|
2
|
+
export * from "./maps/index.js";
|
|
3
|
+
export * from "./plugins/index.js";
|
|
4
|
+
export * from "./client-auth-engine.js";
|
|
5
|
+
export * from "./client-channels.js";
|
|
6
|
+
export * from "./client-socket.js";
|
|
7
|
+
export * from "./client-socket-options.js";
|
|
8
|
+
export * from "./client-transport.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./handlers/index.js";
|
|
2
|
+
export * from "./maps/index.js";
|
|
3
|
+
export * from "./plugins/index.js";
|
|
4
|
+
export * from "./client-auth-engine.js";
|
|
5
|
+
export * from "./client-channels.js";
|
|
6
|
+
export * from "./client-socket.js";
|
|
7
|
+
export * from "./client-socket-options.js";
|
|
8
|
+
export * from "./client-transport.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SignedAuthToken } from "@socket-mesh/auth";
|
|
2
|
+
import { ChannelMap, PublishOptions } from "@socket-mesh/channels";
|
|
3
|
+
import { MethodMap, PrivateMethodMap, PublicMethodMap, ServiceMap } from "@socket-mesh/core";
|
|
4
|
+
export interface ClientMap {
|
|
5
|
+
Channel: ChannelMap;
|
|
6
|
+
Incoming: MethodMap;
|
|
7
|
+
Service: ServiceMap;
|
|
8
|
+
Outgoing: PublicMethodMap;
|
|
9
|
+
PrivateOutgoing: PrivateMethodMap;
|
|
10
|
+
State: object;
|
|
11
|
+
}
|
|
12
|
+
export interface KickOutOptions {
|
|
13
|
+
channel: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export type ClientPrivateMap = {
|
|
17
|
+
'#kickOut': (options: KickOutOptions) => void;
|
|
18
|
+
'#setAuthToken': (token: SignedAuthToken) => void;
|
|
19
|
+
'#removeAuthToken': () => void;
|
|
20
|
+
'#publish': (options: PublishOptions) => void;
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SignedAuthToken } from "@socket-mesh/auth";
|
|
2
|
+
import { ChannelOptions, PublishOptions } from "@socket-mesh/channels";
|
|
3
|
+
export interface HandshakeOptions {
|
|
4
|
+
authToken: SignedAuthToken;
|
|
5
|
+
}
|
|
6
|
+
export type HandshakeStatus = HandshakeErrorStatus | HandshakeAuthenticatedStatus;
|
|
7
|
+
export interface HandshakeErrorStatus {
|
|
8
|
+
id: string;
|
|
9
|
+
pingTimeoutMs: number;
|
|
10
|
+
authError: Error;
|
|
11
|
+
}
|
|
12
|
+
export interface HandshakeAuthenticatedStatus {
|
|
13
|
+
id: string;
|
|
14
|
+
pingTimeoutMs: number;
|
|
15
|
+
authToken: SignedAuthToken;
|
|
16
|
+
}
|
|
17
|
+
export interface SubscribeOptions extends ChannelOptions {
|
|
18
|
+
channel: string;
|
|
19
|
+
}
|
|
20
|
+
export type ServerPrivateMap = {
|
|
21
|
+
'#authenticate': (authToken: string) => void;
|
|
22
|
+
'#handshake': (options: HandshakeOptions) => HandshakeStatus;
|
|
23
|
+
'#publish': (options: PublishOptions) => void;
|
|
24
|
+
'#removeAuthToken': () => void;
|
|
25
|
+
'#subscribe': (options: SubscribeOptions) => void;
|
|
26
|
+
'#unsubscribe': (channelName: string) => void;
|
|
27
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ClientMap, ClientPrivateMap } from "./client-map.js";
|
|
2
|
+
import { PublicMethodMap } from "@socket-mesh/core";
|
|
3
|
+
import { ServerPrivateMap } from "./server-map.js";
|
|
4
|
+
export interface SocketMapFromClient<T extends ClientMap> {
|
|
5
|
+
Incoming: T['Incoming'] & ClientPrivateMap;
|
|
6
|
+
Service: T['Service'];
|
|
7
|
+
Outgoing: T['Outgoing'];
|
|
8
|
+
PrivateOutgoing: T['PrivateOutgoing'] & ServerPrivateMap;
|
|
9
|
+
State: T['State'];
|
|
10
|
+
}
|
|
11
|
+
export interface BasicSocketMapClient<TOutgoing extends PublicMethodMap = {}, TState extends object = {}> {
|
|
12
|
+
Incoming: ClientPrivateMap;
|
|
13
|
+
Service: {};
|
|
14
|
+
Outgoing: TOutgoing;
|
|
15
|
+
PrivateOutgoing: ServerPrivateMap;
|
|
16
|
+
State: TState;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { EmptySocketMap, SocketMap } from "@socket-mesh/core";
|
|
2
|
+
import { Plugin, SendRequestPluginArgs, SendResponsePluginArgs } from "@socket-mesh/core";
|
|
3
|
+
export interface BatchingPluginOptions {
|
|
4
|
+
batchOnHandshakeDuration?: number | false;
|
|
5
|
+
batchInterval?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare abstract class BatchingPlugin<T extends SocketMap = EmptySocketMap> implements Plugin<T> {
|
|
8
|
+
batchOnHandshakeDuration: number | boolean;
|
|
9
|
+
batchInterval: number;
|
|
10
|
+
private _batchingIntervalId;
|
|
11
|
+
private _handshakeTimeoutId;
|
|
12
|
+
private _isBatching;
|
|
13
|
+
constructor(options?: BatchingPluginOptions);
|
|
14
|
+
type: string;
|
|
15
|
+
cancelBatching(): void;
|
|
16
|
+
protected abstract flush(): void;
|
|
17
|
+
get isBatching(): boolean;
|
|
18
|
+
onReady(): void;
|
|
19
|
+
onDisconnected(): void;
|
|
20
|
+
startBatching(): void;
|
|
21
|
+
private start;
|
|
22
|
+
stopBatching(): void;
|
|
23
|
+
private stop;
|
|
24
|
+
}
|
|
25
|
+
export declare class RequestBatchingPlugin<T extends SocketMap = EmptySocketMap> extends BatchingPlugin<T> {
|
|
26
|
+
private _requests;
|
|
27
|
+
private _continue;
|
|
28
|
+
constructor(options?: BatchingPluginOptions);
|
|
29
|
+
cancelBatching(): void;
|
|
30
|
+
protected flush(): void;
|
|
31
|
+
sendRequest({ requests, cont }: SendRequestPluginArgs<T>): void;
|
|
32
|
+
type: 'requestBatching';
|
|
33
|
+
}
|
|
34
|
+
export declare class ResponseBatchingPlugin<T extends SocketMap = EmptySocketMap> extends BatchingPlugin<T> {
|
|
35
|
+
private _responses;
|
|
36
|
+
private _continue;
|
|
37
|
+
constructor(options?: BatchingPluginOptions);
|
|
38
|
+
protected flush(): void;
|
|
39
|
+
sendResponse({ responses, cont }: SendResponsePluginArgs<T>): void;
|
|
40
|
+
type: 'responseBatching';
|
|
41
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export class BatchingPlugin {
|
|
2
|
+
constructor(options) {
|
|
3
|
+
this._isBatching = false;
|
|
4
|
+
this.batchInterval = options?.batchInterval || 50;
|
|
5
|
+
this.batchOnHandshakeDuration = options?.batchOnHandshakeDuration ?? false;
|
|
6
|
+
this._batchingIntervalId = null;
|
|
7
|
+
this._handshakeTimeoutId = null;
|
|
8
|
+
}
|
|
9
|
+
cancelBatching() {
|
|
10
|
+
if (this._batchingIntervalId !== null) {
|
|
11
|
+
clearInterval(this._batchingIntervalId);
|
|
12
|
+
}
|
|
13
|
+
this._isBatching = false;
|
|
14
|
+
this._batchingIntervalId = null;
|
|
15
|
+
}
|
|
16
|
+
get isBatching() {
|
|
17
|
+
return this._isBatching || this._batchingIntervalId !== null;
|
|
18
|
+
}
|
|
19
|
+
onReady() {
|
|
20
|
+
if (this._isBatching) {
|
|
21
|
+
this.start();
|
|
22
|
+
}
|
|
23
|
+
else if (typeof this.batchOnHandshakeDuration === 'number' && this.batchOnHandshakeDuration > 0) {
|
|
24
|
+
this.start();
|
|
25
|
+
this._handshakeTimeoutId = setTimeout(() => {
|
|
26
|
+
this.stop();
|
|
27
|
+
}, this.batchOnHandshakeDuration);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
onDisconnected() {
|
|
31
|
+
this.cancelBatching();
|
|
32
|
+
}
|
|
33
|
+
startBatching() {
|
|
34
|
+
this._isBatching = true;
|
|
35
|
+
this.start();
|
|
36
|
+
}
|
|
37
|
+
start() {
|
|
38
|
+
if (this._batchingIntervalId !== null) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this._batchingIntervalId = setInterval(() => {
|
|
42
|
+
this.flush();
|
|
43
|
+
}, this.batchInterval);
|
|
44
|
+
}
|
|
45
|
+
stopBatching() {
|
|
46
|
+
this._isBatching = false;
|
|
47
|
+
this.stop();
|
|
48
|
+
}
|
|
49
|
+
stop() {
|
|
50
|
+
if (this._batchingIntervalId !== null) {
|
|
51
|
+
clearInterval(this._batchingIntervalId);
|
|
52
|
+
}
|
|
53
|
+
this._batchingIntervalId = null;
|
|
54
|
+
if (this._handshakeTimeoutId !== null) {
|
|
55
|
+
clearTimeout(this._handshakeTimeoutId);
|
|
56
|
+
this._handshakeTimeoutId = null;
|
|
57
|
+
}
|
|
58
|
+
this.flush();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class RequestBatchingPlugin extends BatchingPlugin {
|
|
62
|
+
constructor(options) {
|
|
63
|
+
super(options);
|
|
64
|
+
this.type = 'requestBatching';
|
|
65
|
+
this._requests = [];
|
|
66
|
+
this._continue = null;
|
|
67
|
+
}
|
|
68
|
+
cancelBatching() {
|
|
69
|
+
super.cancelBatching();
|
|
70
|
+
this._requests = [];
|
|
71
|
+
this._continue = null;
|
|
72
|
+
}
|
|
73
|
+
flush() {
|
|
74
|
+
if (this._requests.length) {
|
|
75
|
+
this._continue(this._requests);
|
|
76
|
+
this._requests = [];
|
|
77
|
+
this._continue = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
sendRequest({ requests, cont }) {
|
|
81
|
+
if (!this.isBatching) {
|
|
82
|
+
cont(requests);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
this._continue = cont;
|
|
86
|
+
this._requests.push(...requests);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class ResponseBatchingPlugin extends BatchingPlugin {
|
|
90
|
+
constructor(options) {
|
|
91
|
+
super(options);
|
|
92
|
+
this.type = 'responseBatching';
|
|
93
|
+
this._responses = [];
|
|
94
|
+
this._continue = null;
|
|
95
|
+
}
|
|
96
|
+
flush() {
|
|
97
|
+
if (this._responses.length) {
|
|
98
|
+
this._continue(this._responses);
|
|
99
|
+
this._responses = [];
|
|
100
|
+
this._continue = null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
sendResponse({ responses, cont }) {
|
|
104
|
+
if (!this.isBatching) {
|
|
105
|
+
cont(responses);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this._continue = cont;
|
|
109
|
+
this._responses.push(...responses);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { RawData } from "ws";
|
|
2
|
+
import { EmptySocketMap, MessageRawPluginArgs, Plugin, PluginArgs, SocketMap } from "@socket-mesh/core";
|
|
3
|
+
export declare class InOrderPlugin<T extends SocketMap = EmptySocketMap> implements Plugin<T> {
|
|
4
|
+
type: 'inOrder';
|
|
5
|
+
private readonly _inboundMessageStream;
|
|
6
|
+
constructor();
|
|
7
|
+
handleInboundMessageStream(): void;
|
|
8
|
+
onEnd({ transport }: PluginArgs<T>): void;
|
|
9
|
+
onMessageRaw(options: MessageRawPluginArgs<T>): Promise<string | RawData>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { WritableConsumableStream } from "@socket-mesh/writable-consumable-stream";
|
|
2
|
+
export class InOrderPlugin {
|
|
3
|
+
//private readonly _outboundMessageStream: WritableConsumableStream<SendRequestPluginArgs<T>>;
|
|
4
|
+
constructor() {
|
|
5
|
+
this._inboundMessageStream = new WritableConsumableStream();
|
|
6
|
+
//this._outboundMessageStream = new WritableConsumableStream<SendRequestPluginArgs<T>>;
|
|
7
|
+
this.handleInboundMessageStream();
|
|
8
|
+
//this.handleOutboundMessageStream();
|
|
9
|
+
}
|
|
10
|
+
handleInboundMessageStream() {
|
|
11
|
+
(async () => {
|
|
12
|
+
for await (let { message, callback, promise } of this._inboundMessageStream) {
|
|
13
|
+
callback(null, message);
|
|
14
|
+
try {
|
|
15
|
+
await promise;
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
// Dont throw it is handled in the socket transport
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
}
|
|
23
|
+
/*
|
|
24
|
+
handleOutboundMessageStream(): void {
|
|
25
|
+
(async () => {
|
|
26
|
+
for await (let { requests, cont } of this._outboundMessageStream) {
|
|
27
|
+
await new Promise<void>((resolve) => {
|
|
28
|
+
const reqCol = new RequestCollection(requests);
|
|
29
|
+
|
|
30
|
+
if (reqCol.isDone()) {
|
|
31
|
+
resolve();
|
|
32
|
+
cont(requests);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
reqCol.listen(() => {
|
|
37
|
+
resolve();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
cont(requests);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
}
|
|
45
|
+
*/
|
|
46
|
+
onEnd({ transport }) {
|
|
47
|
+
if (transport.streamCleanupMode === 'close') {
|
|
48
|
+
this._inboundMessageStream.close();
|
|
49
|
+
//this._outboundMessageStream.close();
|
|
50
|
+
}
|
|
51
|
+
else if (transport.streamCleanupMode === 'kill') {
|
|
52
|
+
this._inboundMessageStream.kill();
|
|
53
|
+
//this._outboundMessageStream.kill();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
onMessageRaw(options) {
|
|
57
|
+
let callback;
|
|
58
|
+
const promise = new Promise((resolve, reject) => {
|
|
59
|
+
callback = (err, data) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
reject(err);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
resolve(data);
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
this._inboundMessageStream.write({ callback, ...options });
|
|
68
|
+
return promise;
|
|
69
|
+
}
|
|
70
|
+
}
|