@socket-mesh/client 18.0.8 → 18.1.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.
Files changed (76) hide show
  1. package/README.md +2 -2
  2. package/dist/client-auth-engine.d.ts +22 -0
  3. package/dist/client-auth-engine.js +52 -0
  4. package/dist/client-channels.d.ts +22 -0
  5. package/dist/client-channels.js +149 -0
  6. package/dist/client-socket-options.d.ts +24 -0
  7. package/dist/client-socket-options.js +6 -0
  8. package/dist/client-socket.d.ts +26 -0
  9. package/dist/client-socket.js +100 -0
  10. package/dist/client-transport.d.ts +52 -0
  11. package/dist/client-transport.js +261 -0
  12. package/dist/handlers/index.d.ts +4 -0
  13. package/dist/handlers/index.js +4 -0
  14. package/dist/handlers/kickout.d.ts +6 -0
  15. package/dist/handlers/kickout.js +5 -0
  16. package/dist/handlers/publish.d.ts +7 -0
  17. package/dist/handlers/publish.js +3 -0
  18. package/dist/handlers/remove-auth-token.d.ts +2 -0
  19. package/dist/handlers/remove-auth-token.js +3 -0
  20. package/dist/handlers/set-auth-token.d.ts +3 -0
  21. package/dist/handlers/set-auth-token.js +3 -0
  22. package/dist/index.d.ts +8 -0
  23. package/dist/index.js +8 -0
  24. package/dist/maps/client-map.d.ts +21 -0
  25. package/dist/maps/client-map.js +1 -0
  26. package/dist/maps/index.d.ts +3 -0
  27. package/dist/maps/index.js +3 -0
  28. package/dist/maps/server-map.d.ts +27 -0
  29. package/dist/maps/server-map.js +1 -0
  30. package/dist/maps/socket-map.d.ts +17 -0
  31. package/dist/maps/socket-map.js +1 -0
  32. package/dist/plugins/batching-plugin.d.ts +41 -0
  33. package/dist/plugins/batching-plugin.js +111 -0
  34. package/dist/plugins/in-order-plugin.d.ts +10 -0
  35. package/dist/plugins/in-order-plugin.js +70 -0
  36. package/dist/plugins/index.d.ts +3 -0
  37. package/dist/plugins/index.js +3 -0
  38. package/dist/plugins/offline-plugin.d.ts +13 -0
  39. package/dist/plugins/offline-plugin.js +47 -0
  40. package/package.json +54 -58
  41. package/auth.d.ts +0 -21
  42. package/auth.js +0 -49
  43. package/auth.js.map +0 -1
  44. package/client-options.d.ts +0 -53
  45. package/client-options.js +0 -2
  46. package/client-options.js.map +0 -1
  47. package/clientsocket.d.ts +0 -135
  48. package/clientsocket.js +0 -898
  49. package/clientsocket.js.map +0 -1
  50. package/events.d.ts +0 -54
  51. package/events.js +0 -2
  52. package/events.js.map +0 -1
  53. package/factory.d.ts +0 -3
  54. package/factory.js +0 -37
  55. package/factory.js.map +0 -1
  56. package/index.d.ts +0 -7
  57. package/index.js +0 -10
  58. package/index.js.map +0 -1
  59. package/remote-procedure.d.ts +0 -6
  60. package/remote-procedure.js +0 -10
  61. package/remote-procedure.js.map +0 -1
  62. package/socket-state.d.ts +0 -5
  63. package/socket-state.js +0 -7
  64. package/socket-state.js.map +0 -1
  65. package/transport-handlers.d.ts +0 -37
  66. package/transport-handlers.js +0 -2
  67. package/transport-handlers.js.map +0 -1
  68. package/transport.d.ts +0 -84
  69. package/transport.js +0 -441
  70. package/transport.js.map +0 -1
  71. package/wait.d.ts +0 -1
  72. package/wait.js +0 -8
  73. package/wait.js.map +0 -1
  74. package/ws-browser.d.ts +0 -15
  75. package/ws-browser.js +0 -40
  76. package/ws-browser.js.map +0 -1
package/clientsocket.js DELETED
@@ -1,898 +0,0 @@
1
- import { StreamDemux, StreamDemuxWrapper } from "@socket-mesh/stream-demux";
2
- import { LocalStorageAuthEngine } from "./auth.js";
3
- import { AuthState } from "@socket-mesh/auth";
4
- import formatter from "@socket-mesh/formatter";
5
- import { Transport } from "./transport.js";
6
- import { List, Item } from "linked-list";
7
- import cloneDeep from "clone-deep";
8
- import { Buffer } from "buffer";
9
- import { wait } from "./wait.js";
10
- import { ChannelState, Client } from "@socket-mesh/channel";
11
- import { SocketState } from "./socket-state.js";
12
- import { hydrateError, InvalidArgumentsError, InvalidMessageError, SocketProtocolError, TimeoutError, BadConnectionError, socketProtocolIgnoreStatuses, socketProtocolErrorStatuses } from "@socket-mesh/errors";
13
- import { RemoteProcedure } from "./remote-procedure.js";
14
- class EventNode extends Item {
15
- constructor() {
16
- super();
17
- }
18
- }
19
- export class ClientSocket extends Client {
20
- constructor(socketOptions) {
21
- super();
22
- this.authState = AuthState.UNAUTHENTICATED;
23
- this.id = null;
24
- this.state = SocketState.CLOSED;
25
- let opts = Object.assign({
26
- path: '/socketcluster/',
27
- secure: false,
28
- protocolScheme: null,
29
- socketPath: null,
30
- autoConnect: true,
31
- autoReconnect: true,
32
- autoSubscribeOnConnect: true,
33
- connectTimeout: 20000,
34
- ackTimeout: 10000,
35
- timestampRequests: false,
36
- timestampParam: 't',
37
- binaryType: 'arraybuffer',
38
- batchOnHandshake: false,
39
- batchOnHandshakeDuration: 100,
40
- batchInterval: 50,
41
- protocolVersion: 2,
42
- wsOptions: {},
43
- cloneData: false
44
- }, socketOptions);
45
- if (opts.authTokenName == null) {
46
- const authHostString = opts.host ? `.${opts.host}` : `.${opts.hostname || 'localhost'}${opts.port ? `:${opts.port}` : ''}`;
47
- opts.authTokenName = `socketcluster.authToken${authHostString}`;
48
- }
49
- this.version = opts.version || null;
50
- this.protocolVersion = opts.protocolVersion;
51
- this.signedAuthToken = null;
52
- this.authToken = null;
53
- this.pendingReconnect = false;
54
- this.pendingReconnectTimeout = null;
55
- this.preparingPendingSubscriptions = false;
56
- this.clientId = opts.clientId;
57
- this.wsOptions = opts.wsOptions;
58
- this.connectTimeout = opts.connectTimeout;
59
- this.ackTimeout = opts.ackTimeout;
60
- this.channelPrefix = opts.channelPrefix || null;
61
- this.authTokenName = opts.authTokenName;
62
- // pingTimeout will be connectTimeout at the start, but it will
63
- // be updated with values provided by the 'connect' event
64
- opts.pingTimeout = opts.connectTimeout;
65
- this.pingTimeout = opts.pingTimeout;
66
- this.pingTimeoutDisabled = !!opts.pingTimeoutDisabled;
67
- const maxTimeout = Math.pow(2, 31) - 1;
68
- if (this.connectTimeout > maxTimeout) {
69
- throw new InvalidArgumentsError('The connectTimeout value provided exceeded the maximum amount allowed');
70
- }
71
- if (this.ackTimeout > maxTimeout) {
72
- throw new InvalidArgumentsError('The ackTimeout value provided exceeded the maximum amount allowed');
73
- }
74
- if (this.pingTimeout > maxTimeout) {
75
- throw new InvalidArgumentsError('The pingTimeout value provided exceeded the maximum amount allowed');
76
- }
77
- this.connectAttempts = 0;
78
- this.isBatching = false;
79
- this.batchOnHandshake = opts.batchOnHandshake;
80
- this.batchOnHandshakeDuration = opts.batchOnHandshakeDuration;
81
- this._batchingIntervalId = null;
82
- this._outboundBuffer = new List();
83
- this._receiverDemux = new StreamDemux();
84
- this.receiver = new StreamDemuxWrapper(this._receiverDemux);
85
- this._procedureDemux = new StreamDemux();
86
- this.procedure = new RemoteProcedure(this._procedureDemux);
87
- this.options = opts;
88
- this._cid = 1;
89
- this.options.callIdGenerator = () => {
90
- return this._cid++;
91
- };
92
- if (this.options.autoReconnect) {
93
- if (this.options.autoReconnectOptions == null) {
94
- this.options.autoReconnectOptions = {};
95
- }
96
- // Add properties to the this.options.autoReconnectOptions object.
97
- // We assign the reference to a reconnectOptions variable to avoid repetition.
98
- let reconnectOptions = this.options.autoReconnectOptions;
99
- if (reconnectOptions.initialDelay == null) {
100
- reconnectOptions.initialDelay = 10000;
101
- }
102
- if (reconnectOptions.randomness == null) {
103
- reconnectOptions.randomness = 10000;
104
- }
105
- if (reconnectOptions.multiplier == null) {
106
- reconnectOptions.multiplier = 1.5;
107
- }
108
- if (reconnectOptions.maxDelay == null) {
109
- reconnectOptions.maxDelay = 60000;
110
- }
111
- }
112
- if (this.options.subscriptionRetryOptions == null) {
113
- this.options.subscriptionRetryOptions = {};
114
- }
115
- if (this.options.authEngine) {
116
- this.auth = this.options.authEngine;
117
- }
118
- else {
119
- this.auth = new LocalStorageAuthEngine();
120
- }
121
- if (this.options.codecEngine) {
122
- this.codec = this.options.codecEngine;
123
- }
124
- else {
125
- // Default codec engine
126
- this.codec = formatter;
127
- }
128
- if (this.options.protocol) {
129
- let protocolOptionError = new InvalidArgumentsError('The protocol option does not affect socketcluster-client - ' +
130
- 'If you want to utilize SSL/TLS, use the secure option instead');
131
- this._onError(protocolOptionError);
132
- }
133
- this.options.query = opts.query || {};
134
- if (this.options.autoConnect) {
135
- this.connect();
136
- }
137
- }
138
- uri() {
139
- return Transport.computeUri(this.options);
140
- }
141
- ;
142
- getBackpressure() {
143
- return Math.max(this.getListenerBackpressure(), this.receiver.getBackpressure(), this.procedure.getBackpressure(), this.getChannelBackpressure());
144
- }
145
- _setAuthToken(data) {
146
- this._changeToAuthenticatedState(data.token);
147
- (async () => {
148
- try {
149
- await this.auth.saveToken(this.authTokenName, data.token, {});
150
- }
151
- catch (err) {
152
- this._onError(err);
153
- }
154
- })();
155
- }
156
- _removeAuthToken() {
157
- (async () => {
158
- let oldAuthToken;
159
- try {
160
- oldAuthToken = await this.auth.removeToken(this.authTokenName);
161
- }
162
- catch (err) {
163
- // Non-fatal error - Do not close the connection
164
- this._onError(err);
165
- return;
166
- }
167
- this.emit('removeAuthToken', { oldAuthToken });
168
- })();
169
- this._changeToUnauthenticatedStateAndClearTokens();
170
- }
171
- getState() {
172
- return this.state;
173
- }
174
- getBytesReceived() {
175
- return this.transport.getBytesReceived();
176
- }
177
- async deauthenticate() {
178
- (async () => {
179
- let oldAuthToken;
180
- try {
181
- oldAuthToken = await this.auth.removeToken(this.authTokenName);
182
- }
183
- catch (err) {
184
- this._onError(err);
185
- return;
186
- }
187
- this.emit('removeAuthToken', { oldAuthToken });
188
- })();
189
- if (this.state !== SocketState.CLOSED) {
190
- this.transmit('#removeAuthToken');
191
- }
192
- this._changeToUnauthenticatedStateAndClearTokens();
193
- await wait(0);
194
- }
195
- connect(socketOptions) {
196
- if (socketOptions) {
197
- if (this.state !== SocketState.CLOSED) {
198
- this.disconnect(1000, 'Socket was disconnected by the client to initiate a new connection');
199
- }
200
- this.options = {
201
- ...this.options,
202
- ...socketOptions
203
- };
204
- }
205
- if (this.state === SocketState.CLOSED) {
206
- this.pendingReconnect = false;
207
- this.pendingReconnectTimeout = null;
208
- clearTimeout(this._reconnectTimeoutRef);
209
- this.state = SocketState.CONNECTING;
210
- this.emit('connecting', {});
211
- if (this.transport) {
212
- this.transport.clearAllListeners();
213
- }
214
- const transportHandlers = {
215
- onOpen: (value) => {
216
- this.state = SocketState.OPEN;
217
- this._onOpen(value);
218
- },
219
- onOpenAbort: (value) => {
220
- if (this.state !== SocketState.CLOSED) {
221
- this.state = SocketState.CLOSED;
222
- this._destroy(value.code, value.reason, true);
223
- }
224
- },
225
- onClose: (value) => {
226
- if (this.state !== SocketState.CLOSED) {
227
- this.state = SocketState.CLOSED;
228
- this._destroy(value.code, value.reason);
229
- }
230
- },
231
- onEvent: (value) => {
232
- this.emit(value.event, value.data);
233
- },
234
- onError: (value) => {
235
- this._onError(value.error);
236
- },
237
- onInboundInvoke: (value) => {
238
- this._onInboundInvoke(value);
239
- },
240
- onInboundTransmit: (value) => {
241
- this._onInboundTransmit(value.event, value.data);
242
- }
243
- };
244
- this.transport = new Transport(this.auth, this.codec, this.options, this.wsOptions, transportHandlers);
245
- }
246
- }
247
- reconnect(code, reason) {
248
- this.disconnect(code, reason);
249
- this.connect();
250
- }
251
- disconnect(code = 1000, reason) {
252
- if (typeof code !== 'number') {
253
- throw new InvalidArgumentsError('If specified, the code argument must be a number');
254
- }
255
- let isConnecting = this.state === SocketState.CONNECTING;
256
- if (isConnecting || this.state === SocketState.OPEN) {
257
- this.state = SocketState.CLOSED;
258
- this._destroy(code, reason, isConnecting);
259
- this.transport.close(code, reason);
260
- }
261
- else {
262
- this.pendingReconnect = false;
263
- this.pendingReconnectTimeout = null;
264
- clearTimeout(this._reconnectTimeoutRef);
265
- }
266
- }
267
- _changeToUnauthenticatedStateAndClearTokens() {
268
- if (this.authState !== AuthState.UNAUTHENTICATED) {
269
- let oldAuthState = this.authState;
270
- let oldAuthToken = this.authToken;
271
- let oldSignedAuthToken = this.signedAuthToken;
272
- this.authState = AuthState.UNAUTHENTICATED;
273
- this.signedAuthToken = null;
274
- this.authToken = null;
275
- let stateChangeData = {
276
- oldAuthState,
277
- newAuthState: this.authState
278
- };
279
- this.emit('authStateChange', stateChangeData);
280
- this.emit('deauthenticate', { oldSignedAuthToken, oldAuthToken });
281
- }
282
- }
283
- _changeToAuthenticatedState(signedAuthToken) {
284
- this.signedAuthToken = signedAuthToken;
285
- this.authToken = this._extractAuthTokenData(signedAuthToken);
286
- if (this.authState !== AuthState.AUTHENTICATED) {
287
- let oldAuthState = this.authState;
288
- this.authState = AuthState.AUTHENTICATED;
289
- let stateChangeData = {
290
- oldAuthState,
291
- newAuthState: this.authState,
292
- signedAuthToken: signedAuthToken,
293
- authToken: this.authToken
294
- };
295
- if (!this.preparingPendingSubscriptions) {
296
- this.processPendingSubscriptions();
297
- }
298
- this.emit('authStateChange', stateChangeData);
299
- }
300
- this.emit('authenticate', { signedAuthToken, authToken: this.authToken });
301
- }
302
- decodeBase64(encodedString) {
303
- return Buffer.from(encodedString, 'base64').toString('utf8');
304
- }
305
- encodeBase64(decodedString) {
306
- return Buffer.from(decodedString, 'utf8').toString('base64');
307
- }
308
- _extractAuthTokenData(signedAuthToken) {
309
- if (typeof signedAuthToken !== 'string')
310
- return null;
311
- let tokenParts = signedAuthToken.split('.');
312
- let encodedTokenData = tokenParts[1];
313
- if (encodedTokenData != null) {
314
- let tokenData = encodedTokenData;
315
- try {
316
- tokenData = this.decodeBase64(tokenData);
317
- return JSON.parse(tokenData);
318
- }
319
- catch (e) {
320
- return tokenData;
321
- }
322
- }
323
- return null;
324
- }
325
- getAuthToken() {
326
- return this.authToken;
327
- }
328
- getSignedAuthToken() {
329
- return this.signedAuthToken;
330
- }
331
- // Perform client-initiated authentication by providing an encrypted token string.
332
- async authenticate(signedAuthToken) {
333
- let authStatus;
334
- try {
335
- authStatus = await this.invoke('#authenticate', signedAuthToken);
336
- }
337
- catch (err) {
338
- if (err.name !== 'BadConnectionError' && err.name !== 'TimeoutError') {
339
- // In case of a bad/closed connection or a timeout, we maintain the last
340
- // known auth state since those errors don't mean that the token is invalid.
341
- this._changeToUnauthenticatedStateAndClearTokens();
342
- }
343
- await wait(0);
344
- throw err;
345
- }
346
- if (authStatus?.isAuthenticated != null) {
347
- // If authStatus is correctly formatted (has an isAuthenticated property),
348
- // then we will rehydrate the authError.
349
- if (authStatus.authError) {
350
- authStatus.authError = hydrateError(authStatus.authError);
351
- }
352
- }
353
- else {
354
- // Some errors like BadConnectionError and TimeoutError will not pass a valid
355
- // authStatus object to the current function, so we need to create it ourselves.
356
- authStatus = {
357
- isAuthenticated: this.authState,
358
- authError: null
359
- };
360
- }
361
- if (authStatus.isAuthenticated) {
362
- this._changeToAuthenticatedState(signedAuthToken);
363
- }
364
- else {
365
- this._changeToUnauthenticatedStateAndClearTokens();
366
- }
367
- (async () => {
368
- try {
369
- await this.auth.saveToken(this.authTokenName, signedAuthToken, {});
370
- }
371
- catch (err) {
372
- this._onError(err);
373
- }
374
- })();
375
- await wait(0);
376
- return authStatus;
377
- }
378
- _tryReconnect(initialDelay) {
379
- let exponent = this.connectAttempts++;
380
- let reconnectOptions = this.options.autoReconnectOptions;
381
- let timeout;
382
- if (initialDelay == null || exponent > 0) {
383
- let initialTimeout = Math.round(reconnectOptions.initialDelay + (reconnectOptions.randomness || 0) * Math.random());
384
- timeout = Math.round(initialTimeout * Math.pow(reconnectOptions.multiplier, exponent));
385
- }
386
- else {
387
- timeout = initialDelay;
388
- }
389
- if (timeout > reconnectOptions.maxDelay) {
390
- timeout = reconnectOptions.maxDelay;
391
- }
392
- clearTimeout(this._reconnectTimeoutRef);
393
- this.pendingReconnect = true;
394
- this.pendingReconnectTimeout = timeout;
395
- this._reconnectTimeoutRef = setTimeout(() => {
396
- this.connect();
397
- }, timeout);
398
- }
399
- _onOpen(status) {
400
- if (this.isBatching) {
401
- this._startBatching();
402
- }
403
- else if (this.batchOnHandshake) {
404
- this._startBatching();
405
- setTimeout(() => {
406
- if (!this.isBatching) {
407
- this._stopBatching();
408
- }
409
- }, this.batchOnHandshakeDuration);
410
- }
411
- this.preparingPendingSubscriptions = true;
412
- if (status) {
413
- this.id = status.id;
414
- this.pingTimeout = status.pingTimeout;
415
- if (status.isAuthenticated) {
416
- this._changeToAuthenticatedState(status.authToken);
417
- }
418
- else {
419
- this._changeToUnauthenticatedStateAndClearTokens();
420
- }
421
- }
422
- else {
423
- // This can happen if auth.loadToken (in transport.js) fails with
424
- // an error - This means that the signedAuthToken cannot be loaded by
425
- // the auth engine and therefore, we need to unauthenticate the client.
426
- this._changeToUnauthenticatedStateAndClearTokens();
427
- }
428
- this.connectAttempts = 0;
429
- if (this.options.autoSubscribeOnConnect) {
430
- this.processPendingSubscriptions();
431
- }
432
- // If the user invokes the callback while in autoSubscribeOnConnect mode, it
433
- // won't break anything.
434
- this.emit('connect', {
435
- ...status,
436
- processPendingSubscriptions: () => {
437
- this.processPendingSubscriptions();
438
- }
439
- });
440
- if (this.state === SocketState.OPEN) {
441
- this._flushOutboundBuffer();
442
- }
443
- }
444
- _onError(error) {
445
- this.emit('error', { error });
446
- }
447
- _suspendSubscriptions() {
448
- Object.keys(this._channelMap).forEach((channelName) => {
449
- let channel = this._channelMap[channelName];
450
- this._triggerChannelUnsubscribe(channel, true);
451
- });
452
- }
453
- _abortAllPendingEventsDueToBadConnection(failureType) {
454
- let currentNode = this._outboundBuffer.head;
455
- let nextNode;
456
- while (currentNode) {
457
- nextNode = currentNode.next;
458
- let eventObject = currentNode.data;
459
- clearTimeout(eventObject.timeout);
460
- delete eventObject.timeout;
461
- currentNode.detach();
462
- currentNode = nextNode;
463
- let callback = eventObject.callback;
464
- if (callback) {
465
- delete eventObject.callback;
466
- let errorMessage = `Event ${eventObject.event} was aborted due to a bad connection`;
467
- let error = new BadConnectionError(errorMessage, failureType);
468
- callback.call(eventObject, error, eventObject);
469
- }
470
- // Cleanup any pending response callback in the transport layer too.
471
- if (eventObject.cid) {
472
- this.transport.cancelPendingResponse(eventObject.cid);
473
- }
474
- }
475
- }
476
- _destroy(code, reason, openAbort) {
477
- this.id = null;
478
- this._cancelBatching();
479
- if (this.transport) {
480
- this.transport.clearAllListeners();
481
- }
482
- this.pendingReconnect = false;
483
- this.pendingReconnectTimeout = null;
484
- clearTimeout(this._reconnectTimeoutRef);
485
- this._suspendSubscriptions();
486
- if (openAbort) {
487
- this.emit('connectAbort', { code, reason });
488
- }
489
- else {
490
- this.emit('disconnect', { code, reason });
491
- }
492
- this.emit('close', { code, reason });
493
- if (!socketProtocolIgnoreStatuses[code]) {
494
- let closeMessage;
495
- if (typeof reason === 'string') {
496
- closeMessage = 'Socket connection closed with status code ' + code + ' and reason: ' + reason;
497
- }
498
- else {
499
- closeMessage = 'Socket connection closed with status code ' + code;
500
- }
501
- let err = new SocketProtocolError(socketProtocolErrorStatuses[code] || closeMessage, code);
502
- this._onError(err);
503
- }
504
- this._abortAllPendingEventsDueToBadConnection(openAbort ? 'connectAbort' : 'disconnect');
505
- // Try to reconnect
506
- // on server ping timeout (4000)
507
- // or on client pong timeout (4001)
508
- // or on close without status (1005)
509
- // or on handshake failure (4003)
510
- // or on handshake rejection (4008)
511
- // or on socket hung up (1006)
512
- if (this.options.autoReconnect) {
513
- if (code === 4000 || code === 4001 || code === 1005) {
514
- // If there is a ping or pong timeout or socket closes without
515
- // status, don't wait before trying to reconnect - These could happen
516
- // if the client wakes up after a period of inactivity and in this case we
517
- // want to re-establish the connection as soon as possible.
518
- this._tryReconnect(0);
519
- // Codes 4500 and above will be treated as permanent disconnects.
520
- // Socket will not try to auto-reconnect.
521
- }
522
- else if (code !== 1000 && code < 4500) {
523
- this._tryReconnect();
524
- }
525
- }
526
- }
527
- _onInboundTransmit(event, data) {
528
- data = data || {};
529
- if (event === '#publish') {
530
- if (typeof data.channel !== 'string')
531
- return;
532
- let undecoratedChannelName = this._undecorateChannelName(data.channel);
533
- let isSubscribed = this.isSubscribed(undecoratedChannelName, true);
534
- if (isSubscribed) {
535
- this._channelDataDemux.write(undecoratedChannelName, data.data);
536
- }
537
- }
538
- else if (event === '#kickOut') {
539
- if (typeof data.channel !== 'string')
540
- return;
541
- let undecoratedChannelName = this._undecorateChannelName(data.channel);
542
- let channel = this._channelMap[undecoratedChannelName];
543
- if (channel) {
544
- this.emit('kickOut', {
545
- channel: undecoratedChannelName,
546
- message: data.message
547
- });
548
- this._channelEventDemux.write(`${undecoratedChannelName}/kickOut`, { message: data.message });
549
- this._triggerChannelUnsubscribe(channel);
550
- }
551
- }
552
- else if (event === '#setAuthToken') {
553
- if (data) {
554
- this._setAuthToken(data);
555
- }
556
- }
557
- else if (event === '#removeAuthToken') {
558
- this._removeAuthToken();
559
- }
560
- else {
561
- this._receiverDemux.write(event, data);
562
- }
563
- }
564
- _onInboundInvoke(request) {
565
- let { procedure, data } = request;
566
- if (procedure === '#setAuthToken') {
567
- if (data) {
568
- this._setAuthToken(data);
569
- request.end();
570
- }
571
- else {
572
- let error = new InvalidMessageError('No token data provided by #setAuthToken event');
573
- delete error.stack;
574
- request.error(error);
575
- }
576
- }
577
- else if (procedure === '#removeAuthToken') {
578
- this._removeAuthToken();
579
- request.end();
580
- }
581
- else {
582
- this._procedureDemux.write(procedure, request);
583
- }
584
- }
585
- decode(message) {
586
- return this.transport.decode(message);
587
- }
588
- encode(object) {
589
- return this.transport.encode(object);
590
- }
591
- _flushOutboundBuffer() {
592
- let currentNode = this._outboundBuffer.head;
593
- let nextNode;
594
- while (currentNode) {
595
- nextNode = currentNode.next;
596
- let eventObject = currentNode.data;
597
- currentNode.detach();
598
- this.transport.transmitObject(eventObject);
599
- currentNode = nextNode;
600
- }
601
- }
602
- _handleEventAckTimeout(eventObject, eventNode) {
603
- if (eventNode) {
604
- eventNode.detach();
605
- }
606
- delete eventObject.timeout;
607
- let callback = eventObject.callback;
608
- if (callback) {
609
- delete eventObject.callback;
610
- let error = new TimeoutError(`Event response for ${eventObject.event} event timed out`);
611
- callback.call(eventObject, error, eventObject);
612
- }
613
- // Cleanup any pending response callback in the transport layer too.
614
- if (eventObject.cid) {
615
- this.transport.cancelPendingResponse(eventObject.cid);
616
- }
617
- }
618
- _processOutboundEvent(event, data, options, expectResponse) {
619
- options = options || {};
620
- if (this.state === SocketState.CLOSED) {
621
- this.connect();
622
- }
623
- let eventObject = {
624
- event,
625
- data: this.options.cloneData ? cloneDeep(data) : data
626
- };
627
- let promise;
628
- if (expectResponse) {
629
- promise = new Promise((resolve, reject) => {
630
- eventObject.callback = (err, data) => {
631
- if (err) {
632
- reject(err);
633
- return;
634
- }
635
- resolve(data);
636
- };
637
- });
638
- }
639
- else {
640
- promise = Promise.resolve();
641
- }
642
- let eventNode = new EventNode();
643
- eventNode.data = eventObject;
644
- let ackTimeout = options.ackTimeout == null ? this.ackTimeout : options.ackTimeout;
645
- eventObject.timeout = setTimeout(() => {
646
- this._handleEventAckTimeout(eventObject, eventNode);
647
- }, ackTimeout);
648
- this._outboundBuffer.append(eventNode);
649
- if (this.state === SocketState.OPEN) {
650
- this._flushOutboundBuffer();
651
- }
652
- return promise;
653
- }
654
- send(data) {
655
- this.transport.send(data);
656
- }
657
- ;
658
- transmit(event, data, options) {
659
- return this._processOutboundEvent(event, data, options);
660
- }
661
- invoke(event, data, options) {
662
- return this._processOutboundEvent(event, data, options, true);
663
- }
664
- transmitPublish(channelName, data) {
665
- let pubData = {
666
- channel: this._decorateChannelName(channelName),
667
- data
668
- };
669
- return this.transmit('#publish', pubData);
670
- }
671
- invokePublish(channelName, data) {
672
- let pubData = {
673
- channel: this._decorateChannelName(channelName),
674
- data
675
- };
676
- return this.invoke('#publish', pubData);
677
- }
678
- _triggerChannelSubscribe(channel, subscriptionOptions) {
679
- let channelName = channel.name;
680
- if (channel.state !== ChannelState.SUBSCRIBED) {
681
- let oldChannelState = channel.state;
682
- channel.state = ChannelState.SUBSCRIBED;
683
- let stateChangeData = {
684
- oldChannelState,
685
- newChannelState: channel.state,
686
- subscriptionOptions
687
- };
688
- this._channelEventDemux.write(`${channelName}/subscribeStateChange`, stateChangeData);
689
- this._channelEventDemux.write(`${channelName}/subscribe`, {
690
- subscriptionOptions
691
- });
692
- this.emit('subscribeStateChange', {
693
- channel: channelName,
694
- ...stateChangeData
695
- });
696
- this.emit('subscribe', {
697
- channel: channelName,
698
- subscriptionOptions
699
- });
700
- }
701
- }
702
- _triggerChannelSubscribeFail(err, channel, subscriptionOptions) {
703
- let channelName = channel.name;
704
- let meetsAuthRequirements = !channel.options.waitForAuth || this.authState === AuthState.AUTHENTICATED;
705
- let hasChannel = !!this._channelMap[channelName];
706
- if (hasChannel && meetsAuthRequirements) {
707
- delete this._channelMap[channelName];
708
- this._channelEventDemux.write(`${channelName}/subscribeFail`, {
709
- error: err,
710
- subscriptionOptions
711
- });
712
- this.emit('subscribeFail', {
713
- error: err,
714
- channel: channelName,
715
- subscriptionOptions: subscriptionOptions
716
- });
717
- }
718
- }
719
- // Cancel any pending subscribe callback
720
- _cancelPendingSubscribeCallback(channel) {
721
- if (channel._pendingSubscriptionCid != null) {
722
- this.transport.cancelPendingResponse(channel._pendingSubscriptionCid);
723
- delete channel._pendingSubscriptionCid;
724
- }
725
- }
726
- _decorateChannelName(channelName) {
727
- if (this.channelPrefix) {
728
- channelName = this.channelPrefix + channelName;
729
- }
730
- return channelName;
731
- }
732
- _undecorateChannelName(decoratedChannelName) {
733
- if (this.channelPrefix && decoratedChannelName.indexOf(this.channelPrefix) === 0) {
734
- return decoratedChannelName.replace(this.channelPrefix, '');
735
- }
736
- return decoratedChannelName;
737
- }
738
- startBatch() {
739
- this.transport.startBatch();
740
- }
741
- flushBatch() {
742
- this.transport.flushBatch();
743
- }
744
- cancelBatch() {
745
- this.transport.cancelBatch();
746
- }
747
- _startBatching() {
748
- if (this._batchingIntervalId != null) {
749
- return;
750
- }
751
- this.startBatch();
752
- this._batchingIntervalId = setInterval(() => {
753
- this.flushBatch();
754
- this.startBatch();
755
- }, this.options.batchInterval);
756
- }
757
- startBatching() {
758
- this.isBatching = true;
759
- this._startBatching();
760
- }
761
- _stopBatching() {
762
- if (this._batchingIntervalId != null) {
763
- clearInterval(this._batchingIntervalId);
764
- }
765
- this._batchingIntervalId = null;
766
- this.flushBatch();
767
- }
768
- stopBatching() {
769
- this.isBatching = false;
770
- this._stopBatching();
771
- }
772
- _cancelBatching() {
773
- if (this._batchingIntervalId != null) {
774
- clearInterval(this._batchingIntervalId);
775
- }
776
- this._batchingIntervalId = null;
777
- this.cancelBatch();
778
- }
779
- cancelBatching() {
780
- this.isBatching = false;
781
- this._cancelBatching();
782
- }
783
- _trySubscribe(channel) {
784
- let meetsAuthRequirements = !channel.options.waitForAuth || this.authState === AuthState.AUTHENTICATED;
785
- // We can only ever have one pending subscribe action at any given time on a channel
786
- if (this.state === SocketState.OPEN &&
787
- !this.preparingPendingSubscriptions &&
788
- channel._pendingSubscriptionCid == null &&
789
- meetsAuthRequirements) {
790
- let options = {
791
- noTimeout: true
792
- };
793
- let subscriptionOptions = {};
794
- if (channel.options.waitForAuth) {
795
- subscriptionOptions.waitForAuth = true;
796
- }
797
- if (channel.options.data) {
798
- subscriptionOptions.data = channel.options.data;
799
- }
800
- channel._pendingSubscriptionCid = this.transport.invokeRaw('#subscribe', {
801
- channel: this._decorateChannelName(channel.name),
802
- ...subscriptionOptions
803
- }, options, (err) => {
804
- if (err) {
805
- if (err.name === 'BadConnectionError') {
806
- // In case of a failed connection, keep the subscription
807
- // as pending; it will try again on reconnect.
808
- return;
809
- }
810
- delete channel._pendingSubscriptionCid;
811
- this._triggerChannelSubscribeFail(err, channel, subscriptionOptions);
812
- }
813
- else {
814
- delete channel._pendingSubscriptionCid;
815
- this._triggerChannelSubscribe(channel, subscriptionOptions);
816
- }
817
- });
818
- this.emit('subscribeRequest', {
819
- channel: channel.name,
820
- subscriptionOptions
821
- });
822
- }
823
- }
824
- emit(eventName, data) {
825
- super.emit(eventName, data);
826
- }
827
- listen(eventName) {
828
- return super.listen(eventName);
829
- }
830
- _triggerChannelUnsubscribe(channel, setAsPending) {
831
- let channelName = channel.name;
832
- this._cancelPendingSubscribeCallback(channel);
833
- if (channel.state === ChannelState.SUBSCRIBED) {
834
- let stateChangeData = {
835
- oldChannelState: channel.state,
836
- newChannelState: setAsPending ? ChannelState.PENDING : ChannelState.UNSUBSCRIBED
837
- };
838
- this._channelEventDemux.write(`${channelName}/subscribeStateChange`, stateChangeData);
839
- this._channelEventDemux.write(`${channelName}/unsubscribe`, {});
840
- this.emit('subscribeStateChange', {
841
- channel: channelName,
842
- ...stateChangeData
843
- });
844
- this.emit('unsubscribe', { channel: channelName });
845
- }
846
- if (setAsPending) {
847
- channel.state = ChannelState.PENDING;
848
- }
849
- else {
850
- delete this._channelMap[channelName];
851
- }
852
- }
853
- _tryUnsubscribe(channel) {
854
- this._triggerChannelUnsubscribe(channel);
855
- if (this.state === SocketState.OPEN) {
856
- let options = {
857
- noTimeout: true
858
- };
859
- // If there is a pending subscribe action, cancel the callback
860
- this._cancelPendingSubscribeCallback(channel);
861
- // This operation cannot fail because the TCP protocol guarantees delivery
862
- // so long as the connection remains open. If the connection closes,
863
- // the server will automatically unsubscribe the client and thus complete
864
- // the operation on the server side.
865
- let decoratedChannelName = this._decorateChannelName(channel.name);
866
- this.transport.transmit('#unsubscribe', decoratedChannelName, options);
867
- }
868
- }
869
- // ---- Channel logic ----
870
- processPendingSubscriptions() {
871
- this.preparingPendingSubscriptions = false;
872
- let pendingChannels = [];
873
- Object.keys(this._channelMap).forEach((channelName) => {
874
- let channel = this._channelMap[channelName];
875
- if (channel.state === ChannelState.PENDING) {
876
- pendingChannels.push(channel);
877
- }
878
- });
879
- pendingChannels.sort((a, b) => {
880
- let ap = a.options.priority || 0;
881
- let bp = b.options.priority || 0;
882
- if (ap > bp) {
883
- return -1;
884
- }
885
- if (ap < bp) {
886
- return 1;
887
- }
888
- return 0;
889
- });
890
- pendingChannels.forEach((channel) => {
891
- this._trySubscribe(channel);
892
- });
893
- }
894
- get isBufferingBatch() {
895
- return this.transport.isBufferingBatch;
896
- }
897
- }
898
- //# sourceMappingURL=clientsocket.js.map