@aws-amplify/pubsub 4.2.5 → 4.2.6-custom-pk.86

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 (63) hide show
  1. package/CHANGELOG.md +149 -0
  2. package/dist/aws-amplify-pubsub.js +404 -339
  3. package/dist/aws-amplify-pubsub.js.map +1 -1
  4. package/dist/aws-amplify-pubsub.min.js +2 -2
  5. package/dist/aws-amplify-pubsub.min.js.map +1 -1
  6. package/lib/Providers/AWSAppSyncProvider.d.ts +3 -0
  7. package/lib/Providers/AWSAppSyncProvider.js +3 -0
  8. package/lib/Providers/AWSAppSyncProvider.js.map +1 -1
  9. package/lib/Providers/AWSAppSyncRealTimeProvider/constants.d.ts +82 -0
  10. package/lib/Providers/AWSAppSyncRealTimeProvider/constants.js +90 -0
  11. package/lib/Providers/AWSAppSyncRealTimeProvider/constants.js.map +1 -0
  12. package/lib/Providers/AWSAppSyncRealTimeProvider/index.d.ts +64 -0
  13. package/lib/Providers/{AWSAppSyncRealTimeProvider.js → AWSAppSyncRealTimeProvider/index.js} +214 -273
  14. package/lib/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -0
  15. package/lib/Providers/AWSIotProvider.d.ts +6 -1
  16. package/lib/Providers/AWSIotProvider.js +3 -2
  17. package/lib/Providers/AWSIotProvider.js.map +1 -1
  18. package/lib/Providers/MqttOverWSProvider.d.ts +14 -11
  19. package/lib/Providers/MqttOverWSProvider.js +17 -10
  20. package/lib/Providers/MqttOverWSProvider.js.map +1 -1
  21. package/lib/Providers/PubSubProvider.d.ts +7 -7
  22. package/lib/Providers/PubSubProvider.js.map +1 -1
  23. package/lib/PubSub.d.ts +6 -6
  24. package/lib/PubSub.js +2 -2
  25. package/lib/PubSub.js.map +1 -1
  26. package/lib/types/Provider.d.ts +3 -3
  27. package/lib/types/PubSub.d.ts +5 -1
  28. package/lib-esm/Providers/AWSAppSyncProvider.d.ts +3 -0
  29. package/lib-esm/Providers/AWSAppSyncProvider.js +3 -0
  30. package/lib-esm/Providers/AWSAppSyncProvider.js.map +1 -1
  31. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/constants.d.ts +82 -0
  32. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/constants.js +88 -0
  33. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/constants.js.map +1 -0
  34. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.d.ts +64 -0
  35. package/lib-esm/Providers/{AWSAppSyncRealTimeProvider.js → AWSAppSyncRealTimeProvider/index.js} +188 -247
  36. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -0
  37. package/lib-esm/Providers/AWSIotProvider.d.ts +6 -1
  38. package/lib-esm/Providers/AWSIotProvider.js +3 -2
  39. package/lib-esm/Providers/AWSIotProvider.js.map +1 -1
  40. package/lib-esm/Providers/MqttOverWSProvider.d.ts +14 -11
  41. package/lib-esm/Providers/MqttOverWSProvider.js +17 -10
  42. package/lib-esm/Providers/MqttOverWSProvider.js.map +1 -1
  43. package/lib-esm/Providers/PubSubProvider.d.ts +7 -7
  44. package/lib-esm/Providers/PubSubProvider.js.map +1 -1
  45. package/lib-esm/PubSub.d.ts +6 -6
  46. package/lib-esm/PubSub.js +2 -2
  47. package/lib-esm/PubSub.js.map +1 -1
  48. package/lib-esm/types/Provider.d.ts +3 -3
  49. package/lib-esm/types/PubSub.d.ts +5 -1
  50. package/package.json +13 -7
  51. package/src/Providers/AWSAppSyncProvider.ts +27 -26
  52. package/src/Providers/AWSAppSyncRealTimeProvider/constants.ts +95 -0
  53. package/src/Providers/{AWSAppSyncRealTimeProvider.ts → AWSAppSyncRealTimeProvider/index.ts} +276 -303
  54. package/src/Providers/AWSIotProvider.ts +10 -1
  55. package/src/Providers/MqttOverWSProvider.ts +52 -33
  56. package/src/Providers/PubSubProvider.ts +8 -8
  57. package/src/PubSub.ts +10 -10
  58. package/src/types/Provider.ts +3 -7
  59. package/src/types/PubSub.ts +6 -1
  60. package/lib/Providers/AWSAppSyncRealTimeProvider.d.ts +0 -38
  61. package/lib/Providers/AWSAppSyncRealTimeProvider.js.map +0 -1
  62. package/lib-esm/Providers/AWSAppSyncRealTimeProvider.d.ts +0 -38
  63. package/lib-esm/Providers/AWSAppSyncRealTimeProvider.js.map +0 -1
@@ -15,7 +15,7 @@ import { GraphQLError } from 'graphql';
15
15
  import * as url from 'url';
16
16
  import { v4 as uuid } from 'uuid';
17
17
  import { Buffer } from 'buffer';
18
- import { ProvidertOptions } from '../types';
18
+ import { ProviderOptions } from '../../types';
19
19
  import {
20
20
  Logger,
21
21
  Credentials,
@@ -25,135 +25,77 @@ import {
25
25
  USER_AGENT_HEADER,
26
26
  jitteredExponentialRetry,
27
27
  NonRetryableError,
28
+ ICredentials,
28
29
  } from '@aws-amplify/core';
29
30
  import Cache from '@aws-amplify/cache';
30
- import Auth from '@aws-amplify/auth';
31
- import { AbstractPubSubProvider } from './PubSubProvider';
32
- import { CONTROL_MSG } from '../index';
31
+ import Auth, { GRAPHQL_AUTH_MODE } from '@aws-amplify/auth';
32
+ import { AbstractPubSubProvider } from '../PubSubProvider';
33
+ import { CONTROL_MSG } from '../../index';
34
+ import {
35
+ AMPLIFY_SYMBOL,
36
+ AWS_APPSYNC_REALTIME_HEADERS,
37
+ CONNECTION_INIT_TIMEOUT,
38
+ DEFAULT_KEEP_ALIVE_TIMEOUT,
39
+ MAX_DELAY_MS,
40
+ MESSAGE_TYPES,
41
+ NON_RETRYABLE_CODES,
42
+ SOCKET_STATUS,
43
+ START_ACK_TIMEOUT,
44
+ SUBSCRIPTION_STATUS,
45
+ } from './constants';
33
46
 
34
47
  const logger = new Logger('AWSAppSyncRealTimeProvider');
35
48
 
36
- const AMPLIFY_SYMBOL = (typeof Symbol !== 'undefined' &&
37
- typeof Symbol.for === 'function'
38
- ? Symbol.for('amplify_default')
39
- : '@@amplify_default') as Symbol;
40
-
41
49
  const dispatchApiEvent = (event: string, data: any, message: string) => {
42
50
  Hub.dispatch('api', { event, data, message }, 'PubSub', AMPLIFY_SYMBOL);
43
51
  };
44
52
 
45
- const MAX_DELAY_MS = 5000;
46
-
47
- const NON_RETRYABLE_CODES = [400, 401, 403];
48
-
49
- type ObserverQuery = {
53
+ export type ObserverQuery = {
50
54
  observer: ZenObservable.SubscriptionObserver<any>;
51
55
  query: string;
52
56
  variables: object;
53
57
  subscriptionState: SUBSCRIPTION_STATUS;
54
58
  subscriptionReadyCallback?: Function;
55
59
  subscriptionFailedCallback?: Function;
56
- startAckTimeoutId?;
60
+ startAckTimeoutId?: ReturnType<typeof setTimeout>;
57
61
  };
58
62
 
59
- enum MESSAGE_TYPES {
60
- /**
61
- * Client -> Server message.
62
- * This message type is the first message after handshake and this will initialize AWS AppSync RealTime communication
63
- */
64
- GQL_CONNECTION_INIT = 'connection_init',
65
- /**
66
- * Server -> Client message
67
- * This message type is in case there is an issue with AWS AppSync RealTime when establishing connection
68
- */
69
- GQL_CONNECTION_ERROR = 'connection_error',
70
- /**
71
- * Server -> Client message.
72
- * This message type is for the ack response from AWS AppSync RealTime for GQL_CONNECTION_INIT message
73
- */
74
- GQL_CONNECTION_ACK = 'connection_ack',
75
- /**
76
- * Client -> Server message.
77
- * This message type is for register subscriptions with AWS AppSync RealTime
78
- */
79
- GQL_START = 'start',
80
- /**
81
- * Server -> Client message.
82
- * This message type is for the ack response from AWS AppSync RealTime for GQL_START message
83
- */
84
- GQL_START_ACK = 'start_ack',
85
- /**
86
- * Server -> Client message.
87
- * This message type is for subscription message from AWS AppSync RealTime
88
- */
89
- GQL_DATA = 'data',
90
- /**
91
- * Server -> Client message.
92
- * This message type helps the client to know is still receiving messages from AWS AppSync RealTime
93
- */
94
- GQL_CONNECTION_KEEP_ALIVE = 'ka',
95
- /**
96
- * Client -> Server message.
97
- * This message type is for unregister subscriptions with AWS AppSync RealTime
98
- */
99
- GQL_STOP = 'stop',
100
- /**
101
- * Server -> Client message.
102
- * This message type is for the ack response from AWS AppSync RealTime for GQL_STOP message
103
- */
104
- GQL_COMPLETE = 'complete',
105
- /**
106
- * Server -> Client message.
107
- * This message type is for sending error messages from AWS AppSync RealTime to the client
108
- */
109
- GQL_ERROR = 'error', // Server -> Client
110
- }
63
+ const standardDomainPattern =
64
+ /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com\/graphql$/i;
111
65
 
112
- enum SUBSCRIPTION_STATUS {
113
- PENDING,
114
- CONNECTED,
115
- FAILED,
116
- }
66
+ const customDomainPath = '/realtime';
117
67
 
118
- enum SOCKET_STATUS {
119
- CLOSED,
120
- READY,
121
- CONNECTING,
68
+ type GraphqlAuthModes = keyof typeof GRAPHQL_AUTH_MODE;
69
+
70
+ export interface AWSAppSyncRealTimeProviderOptions extends ProviderOptions {
71
+ appSyncGraphqlEndpoint?: string;
72
+ authenticationType?: GraphqlAuthModes;
73
+ query?: string;
74
+ variables?: object;
75
+ apiKey?: string;
76
+ region?: string;
77
+ graphql_headers?: () => {} | (() => Promise<{}>);
78
+ additionalHeaders?: { [key: string]: string };
122
79
  }
123
80
 
124
- const AWS_APPSYNC_REALTIME_HEADERS = {
125
- accept: 'application/json, text/javascript',
126
- 'content-encoding': 'amz-1.0',
127
- 'content-type': 'application/json; charset=UTF-8',
128
- };
129
-
130
- /**
131
- * Time in milleseconds to wait for GQL_CONNECTION_INIT message
132
- */
133
- const CONNECTION_INIT_TIMEOUT = 15000;
134
-
135
- /**
136
- * Time in milleseconds to wait for GQL_START_ACK message
137
- */
138
- const START_ACK_TIMEOUT = 15000;
139
-
140
- /**
141
- * Default Time in milleseconds to wait for GQL_CONNECTION_KEEP_ALIVE message
142
- */
143
- const DEFAULT_KEEP_ALIVE_TIMEOUT = 5 * 60 * 1000;
144
-
145
- const standardDomainPattern = /^https:\/\/\w{26}\.appsync\-api\.\w{2}(?:(?:\-\w{2,})+)\-\d\.amazonaws.com\/graphql$/i;
146
-
147
- const customDomainPath = '/realtime';
81
+ type AWSAppSyncRealTimeAuthInput =
82
+ Partial<AWSAppSyncRealTimeProviderOptions> & {
83
+ canonicalUri: string;
84
+ payload: string;
85
+ };
148
86
 
149
87
  export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
150
- private awsRealTimeSocket: WebSocket;
88
+ private awsRealTimeSocket?: WebSocket;
151
89
  private socketStatus: SOCKET_STATUS = SOCKET_STATUS.CLOSED;
152
- private keepAliveTimeoutId;
90
+ private keepAliveTimeoutId?: ReturnType<typeof setTimeout>;
153
91
  private keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
154
92
  private subscriptionObserverMap: Map<string, ObserverQuery> = new Map();
155
93
  private promiseArray: Array<{ res: Function; rej: Function }> = [];
156
94
 
95
+ getNewWebSocket(url, protocol) {
96
+ return new WebSocket(url, protocol);
97
+ }
98
+
157
99
  getProviderName() {
158
100
  return 'AWSAppSyncRealTimeProvider';
159
101
  }
@@ -173,12 +115,12 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
173
115
 
174
116
  subscribe(
175
117
  _topics: string[] | string,
176
- options?: ProvidertOptions
118
+ options?: AWSAppSyncRealTimeProviderOptions
177
119
  ): Observable<any> {
178
- const { appSyncGraphqlEndpoint } = options;
120
+ const appSyncGraphqlEndpoint = options?.appSyncGraphqlEndpoint;
179
121
 
180
122
  return new Observable(observer => {
181
- if (!appSyncGraphqlEndpoint) {
123
+ if (!options || !appSyncGraphqlEndpoint) {
182
124
  observer.error({
183
125
  errors: [
184
126
  {
@@ -195,7 +137,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
195
137
  options,
196
138
  observer,
197
139
  subscriptionId,
198
- }).catch(err => {
140
+ }).catch<any>(err => {
199
141
  observer.error({
200
142
  errors: [
201
143
  {
@@ -241,10 +183,15 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
241
183
  return !this.options
242
184
  .aws_appsync_dangerously_connect_to_http_endpoint_for_testing;
243
185
  }
186
+
244
187
  private async _startSubscriptionWithAWSAppSyncRealTime({
245
188
  options,
246
189
  observer,
247
190
  subscriptionId,
191
+ }: {
192
+ options: AWSAppSyncRealTimeProviderOptions;
193
+ observer: ZenObservable.SubscriptionObserver<any>;
194
+ subscriptionId: string;
248
195
  }) {
249
196
  const {
250
197
  appSyncGraphqlEndpoint,
@@ -265,10 +212,10 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
265
212
  // Having a subscription id map will make it simple to forward messages received
266
213
  this.subscriptionObserverMap.set(subscriptionId, {
267
214
  observer,
268
- query,
269
- variables,
215
+ query: query ?? '',
216
+ variables: variables ?? {},
270
217
  subscriptionState,
271
- startAckTimeoutId: null,
218
+ startAckTimeoutId: undefined,
272
219
  });
273
220
 
274
221
  // Preparing payload for subscription message
@@ -314,7 +261,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
314
261
  });
315
262
  } catch (err) {
316
263
  logger.debug({ err });
317
- const { message = '' } = err;
264
+ const message = err['message'] ?? '';
318
265
  observer.error({
319
266
  errors: [
320
267
  {
@@ -323,7 +270,6 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
323
270
  ],
324
271
  });
325
272
  observer.complete();
326
-
327
273
  const { subscriptionFailedCallback } =
328
274
  this.subscriptionObserverMap.get(subscriptionId) || {};
329
275
 
@@ -338,17 +284,15 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
338
284
  // E.g.unsubscribe gets invoked prior to finishing WebSocket handshake or START_ACK.
339
285
  // Both subscriptionFailedCallback and subscriptionReadyCallback are used to synchronized this.
340
286
 
341
- const {
342
- subscriptionFailedCallback,
343
- subscriptionReadyCallback,
344
- } = this.subscriptionObserverMap.get(subscriptionId);
287
+ const { subscriptionFailedCallback, subscriptionReadyCallback } =
288
+ this.subscriptionObserverMap.get(subscriptionId) ?? {};
345
289
 
346
290
  // This must be done before sending the message in order to be listening immediately
347
291
  this.subscriptionObserverMap.set(subscriptionId, {
348
292
  observer,
349
293
  subscriptionState,
350
- variables,
351
- query,
294
+ query: query ?? '',
295
+ variables: variables ?? {},
352
296
  subscriptionReadyCallback,
353
297
  subscriptionFailedCallback,
354
298
  startAckTimeoutId: setTimeout(() => {
@@ -361,32 +305,30 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
361
305
  }
362
306
 
363
307
  // Waiting that subscription has been connected before trying to unsubscribe
364
- private async _waitForSubscriptionToBeConnected(subscriptionId) {
365
- const { subscriptionState } = this.subscriptionObserverMap.get(
366
- subscriptionId
367
- );
368
- // This in case unsubscribe is invoked before sending start subscription message
369
- if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
370
- return new Promise((res, rej) => {
371
- const {
372
- observer,
373
- subscriptionState,
374
- variables,
375
- query,
376
- } = this.subscriptionObserverMap.get(subscriptionId);
377
- this.subscriptionObserverMap.set(subscriptionId, {
378
- observer,
379
- subscriptionState,
380
- variables,
381
- query,
382
- subscriptionReadyCallback: res,
383
- subscriptionFailedCallback: rej,
308
+ private async _waitForSubscriptionToBeConnected(subscriptionId: string) {
309
+ const subscriptionObserver =
310
+ this.subscriptionObserverMap.get(subscriptionId);
311
+ if (subscriptionObserver) {
312
+ const { subscriptionState } = subscriptionObserver;
313
+ // This in case unsubscribe is invoked before sending start subscription message
314
+ if (subscriptionState === SUBSCRIPTION_STATUS.PENDING) {
315
+ return new Promise((res, rej) => {
316
+ const { observer, subscriptionState, variables, query } =
317
+ subscriptionObserver;
318
+ this.subscriptionObserverMap.set(subscriptionId, {
319
+ observer,
320
+ subscriptionState,
321
+ variables,
322
+ query,
323
+ subscriptionReadyCallback: res,
324
+ subscriptionFailedCallback: rej,
325
+ });
384
326
  });
385
- });
327
+ }
386
328
  }
387
329
  }
388
330
 
389
- private _sendUnsubscriptionMessage(subscriptionId) {
331
+ private _sendUnsubscriptionMessage(subscriptionId: string) {
390
332
  try {
391
333
  if (
392
334
  this.awsRealTimeSocket &&
@@ -407,7 +349,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
407
349
  }
408
350
  }
409
351
 
410
- private _removeSubscriptionObserver(subscriptionId) {
352
+ private _removeSubscriptionObserver(subscriptionId: string) {
411
353
  this.subscriptionObserverMap.delete(subscriptionId);
412
354
 
413
355
  // Verifying 1000ms after removing subscription in case there are new subscription unmount/mount
@@ -429,13 +371,13 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
429
371
  setTimeout(this._closeSocketIfRequired.bind(this), 1000);
430
372
  } else {
431
373
  logger.debug('closing WebSocket...');
432
- clearTimeout(this.keepAliveTimeoutId);
374
+ if (this.keepAliveTimeoutId) clearTimeout(this.keepAliveTimeoutId);
433
375
  const tempSocket = this.awsRealTimeSocket;
434
376
  // Cleaning callbacks to avoid race condition, socket still exists
435
- tempSocket.onclose = undefined;
436
- tempSocket.onerror = undefined;
377
+ tempSocket.onclose = null;
378
+ tempSocket.onerror = null;
437
379
  tempSocket.close(1000);
438
- this.awsRealTimeSocket = null;
380
+ this.awsRealTimeSocket = undefined;
439
381
  this.socketStatus = SOCKET_STATUS.CLOSED;
440
382
  }
441
383
  }
@@ -472,29 +414,31 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
472
414
  if (typeof subscriptionReadyCallback === 'function') {
473
415
  subscriptionReadyCallback();
474
416
  }
475
- clearTimeout(startAckTimeoutId);
417
+ if (startAckTimeoutId) clearTimeout(startAckTimeoutId);
476
418
  dispatchApiEvent(
477
419
  CONTROL_MSG.SUBSCRIPTION_ACK,
478
420
  { query, variables },
479
421
  'Connection established for subscription'
480
422
  );
481
423
  const subscriptionState = SUBSCRIPTION_STATUS.CONNECTED;
482
- this.subscriptionObserverMap.set(id, {
483
- observer,
484
- query,
485
- variables,
486
- startAckTimeoutId: null,
487
- subscriptionState,
488
- subscriptionReadyCallback,
489
- subscriptionFailedCallback,
490
- });
424
+ if (observer) {
425
+ this.subscriptionObserverMap.set(id, {
426
+ observer,
427
+ query,
428
+ variables,
429
+ startAckTimeoutId: undefined,
430
+ subscriptionState,
431
+ subscriptionReadyCallback,
432
+ subscriptionFailedCallback,
433
+ });
434
+ }
491
435
 
492
436
  // TODO: emit event on hub but it requires to store the id first
493
437
  return;
494
438
  }
495
439
 
496
440
  if (type === MESSAGE_TYPES.GQL_CONNECTION_KEEP_ALIVE) {
497
- clearTimeout(this.keepAliveTimeoutId);
441
+ if (this.keepAliveTimeoutId) clearTimeout(this.keepAliveTimeoutId);
498
442
  this.keepAliveTimeoutId = setTimeout(
499
443
  this._errorDisconnect.bind(this, CONTROL_MSG.TIMEOUT_DISCONNECT),
500
444
  this.keepAliveTimeout
@@ -504,30 +448,32 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
504
448
 
505
449
  if (type === MESSAGE_TYPES.GQL_ERROR) {
506
450
  const subscriptionState = SUBSCRIPTION_STATUS.FAILED;
507
- this.subscriptionObserverMap.set(id, {
508
- observer,
509
- query,
510
- variables,
511
- startAckTimeoutId,
512
- subscriptionReadyCallback,
513
- subscriptionFailedCallback,
514
- subscriptionState,
515
- });
451
+ if (observer) {
452
+ this.subscriptionObserverMap.set(id, {
453
+ observer,
454
+ query,
455
+ variables,
456
+ startAckTimeoutId,
457
+ subscriptionReadyCallback,
458
+ subscriptionFailedCallback,
459
+ subscriptionState,
460
+ });
516
461
 
517
- observer.error({
518
- errors: [
519
- {
520
- ...new GraphQLError(
521
- `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`
522
- ),
523
- },
524
- ],
525
- });
526
- clearTimeout(startAckTimeoutId);
462
+ observer.error({
463
+ errors: [
464
+ {
465
+ ...new GraphQLError(
466
+ `${CONTROL_MSG.CONNECTION_FAILED}: ${JSON.stringify(payload)}`
467
+ ),
468
+ },
469
+ ],
470
+ });
471
+ if (startAckTimeoutId) clearTimeout(startAckTimeoutId);
527
472
 
528
- observer.complete();
529
- if (typeof subscriptionFailedCallback === 'function') {
530
- subscriptionFailedCallback();
473
+ observer.complete();
474
+ if (typeof subscriptionFailedCallback === 'function') {
475
+ subscriptionFailedCallback();
476
+ }
531
477
  }
532
478
  }
533
479
  }
@@ -549,39 +495,42 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
549
495
  this.socketStatus = SOCKET_STATUS.CLOSED;
550
496
  }
551
497
 
552
- private _timeoutStartSubscriptionAck(subscriptionId) {
553
- const { observer, query, variables } =
554
- this.subscriptionObserverMap.get(subscriptionId) || {};
555
- if (!observer) {
556
- return;
557
- }
558
- this.subscriptionObserverMap.set(subscriptionId, {
559
- observer,
560
- query,
561
- variables,
562
- subscriptionState: SUBSCRIPTION_STATUS.FAILED,
563
- });
564
-
565
- if (observer && !observer.closed) {
566
- observer.error({
567
- errors: [
568
- {
569
- ...new GraphQLError(
570
- `Subscription timeout ${JSON.stringify({
571
- query,
572
- variables,
573
- })}`
574
- ),
575
- },
576
- ],
498
+ private _timeoutStartSubscriptionAck(subscriptionId: string) {
499
+ const subscriptionObserver =
500
+ this.subscriptionObserverMap.get(subscriptionId);
501
+ if (subscriptionObserver) {
502
+ const { observer, query, variables } = subscriptionObserver;
503
+ if (!observer) {
504
+ return;
505
+ }
506
+ this.subscriptionObserverMap.set(subscriptionId, {
507
+ observer,
508
+ query,
509
+ variables,
510
+ subscriptionState: SUBSCRIPTION_STATUS.FAILED,
577
511
  });
578
- // Cleanup will be automatically executed
579
- observer.complete();
512
+
513
+ if (observer && !observer.closed) {
514
+ observer.error({
515
+ errors: [
516
+ {
517
+ ...new GraphQLError(
518
+ `Subscription timeout ${JSON.stringify({
519
+ query,
520
+ variables,
521
+ })}`
522
+ ),
523
+ },
524
+ ],
525
+ });
526
+ // Cleanup will be automatically executed
527
+ observer.complete();
528
+ }
529
+ logger.debug(
530
+ 'timeoutStartSubscription',
531
+ JSON.stringify({ query, variables })
532
+ );
580
533
  }
581
- logger.debug(
582
- 'timeoutStartSubscription',
583
- JSON.stringify({ query, variables })
584
- );
585
534
  }
586
535
 
587
536
  private _initializeWebSocketConnection({
@@ -590,7 +539,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
590
539
  apiKey,
591
540
  region,
592
541
  additionalHeaders,
593
- }) {
542
+ }: AWSAppSyncRealTimeProviderOptions) {
594
543
  if (this.socketStatus === SOCKET_STATUS.READY) {
595
544
  return;
596
545
  }
@@ -617,17 +566,18 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
617
566
 
618
567
  const payloadQs = Buffer.from(payloadString).toString('base64');
619
568
 
620
- let discoverableEndpoint = appSyncGraphqlEndpoint;
569
+ let discoverableEndpoint = appSyncGraphqlEndpoint ?? '';
621
570
 
622
571
  if (this.isCustomDomain(discoverableEndpoint)) {
623
- discoverableEndpoint = discoverableEndpoint.concat(
624
- customDomainPath
625
- );
572
+ discoverableEndpoint =
573
+ discoverableEndpoint.concat(customDomainPath);
626
574
  } else {
627
- discoverableEndpoint = discoverableEndpoint.replace('appsync-api', 'appsync-realtime-api').replace('gogi-beta', 'grt-beta');
575
+ discoverableEndpoint = discoverableEndpoint
576
+ .replace('appsync-api', 'appsync-realtime-api')
577
+ .replace('gogi-beta', 'grt-beta');
628
578
  }
629
579
 
630
- // Creating websocket url with required query strings
580
+ // Creating websocket url with required query strings
631
581
  const protocol = this.isSSLEnabled ? 'wss://' : 'ws://';
632
582
  discoverableEndpoint = discoverableEndpoint
633
583
  .replace('https://', protocol)
@@ -635,7 +585,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
635
585
 
636
586
  const awsRealTimeUrl = `${discoverableEndpoint}?header=${headerQs}&payload=${payloadQs}`;
637
587
 
638
- await this._initializeRetryableHandshake({ awsRealTimeUrl });
588
+ await this._initializeRetryableHandshake(awsRealTimeUrl);
639
589
 
640
590
  this.promiseArray.forEach(({ res }) => {
641
591
  logger.debug('Notifying connection successful');
@@ -652,30 +602,30 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
652
602
  ) {
653
603
  this.awsRealTimeSocket.close(3001);
654
604
  }
655
- this.awsRealTimeSocket = null;
605
+ this.awsRealTimeSocket = undefined;
656
606
  this.socketStatus = SOCKET_STATUS.CLOSED;
657
607
  }
658
608
  }
659
609
  });
660
610
  }
661
611
 
662
- private async _initializeRetryableHandshake({ awsRealTimeUrl }) {
612
+ private async _initializeRetryableHandshake(awsRealTimeUrl: string) {
663
613
  logger.debug(`Initializaling retryable Handshake`);
664
614
  await jitteredExponentialRetry(
665
615
  this._initializeHandshake.bind(this),
666
- [{ awsRealTimeUrl }],
616
+ [awsRealTimeUrl],
667
617
  MAX_DELAY_MS
668
618
  );
669
619
  }
670
620
 
671
- private async _initializeHandshake({ awsRealTimeUrl }) {
621
+ private async _initializeHandshake(awsRealTimeUrl: string) {
672
622
  logger.debug(`Initializing handshake ${awsRealTimeUrl}`);
673
623
  // Because connecting the socket is async, is waiting until connection is open
674
624
  // Step 1: connect websocket
675
625
  try {
676
626
  await (() => {
677
627
  return new Promise<void>((res, rej) => {
678
- const newSocket = new WebSocket(awsRealTimeUrl, 'graphql-ws');
628
+ const newSocket = this.getNewWebSocket(awsRealTimeUrl, 'graphql-ws');
679
629
  newSocket.onerror = () => {
680
630
  logger.debug(`WebSocket connection error`);
681
631
  };
@@ -692,61 +642,66 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
692
642
  // Step 2: wait for ack from AWS AppSyncReaTime after sending init
693
643
  await (() => {
694
644
  return new Promise((res, rej) => {
695
- let ackOk = false;
696
- this.awsRealTimeSocket.onerror = error => {
697
- logger.debug(`WebSocket error ${JSON.stringify(error)}`);
698
- };
699
- this.awsRealTimeSocket.onclose = event => {
700
- logger.debug(`WebSocket closed ${event.reason}`);
701
- rej(new Error(JSON.stringify(event)));
702
- };
703
-
704
- this.awsRealTimeSocket.onmessage = (message: MessageEvent) => {
705
- logger.debug(
706
- `subscription message from AWS AppSyncRealTime: ${message.data} `
707
- );
708
- const data = JSON.parse(message.data);
709
- const {
710
- type,
711
- payload: {
712
- connectionTimeoutMs = DEFAULT_KEEP_ALIVE_TIMEOUT,
713
- } = {},
714
- } = data;
715
- if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
716
- ackOk = true;
717
- this.keepAliveTimeout = connectionTimeoutMs;
718
- this.awsRealTimeSocket.onmessage = this._handleIncomingSubscriptionMessage.bind(
719
- this
645
+ if (this.awsRealTimeSocket) {
646
+ let ackOk = false;
647
+ this.awsRealTimeSocket.onerror = error => {
648
+ logger.debug(`WebSocket error ${JSON.stringify(error)}`);
649
+ };
650
+ this.awsRealTimeSocket.onclose = event => {
651
+ logger.debug(`WebSocket closed ${event.reason}`);
652
+ rej(new Error(JSON.stringify(event)));
653
+ };
654
+
655
+ this.awsRealTimeSocket.onmessage = (message: MessageEvent) => {
656
+ logger.debug(
657
+ `subscription message from AWS AppSyncRealTime: ${message.data} `
720
658
  );
721
- this.awsRealTimeSocket.onerror = err => {
722
- logger.debug(err);
723
- this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
724
- };
725
- this.awsRealTimeSocket.onclose = event => {
726
- logger.debug(`WebSocket closed ${event.reason}`);
727
- this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
728
- };
729
- res('Cool, connected to AWS AppSyncRealTime');
730
- return;
731
- }
732
-
733
- if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
659
+ const data = JSON.parse(message.data);
734
660
  const {
661
+ type,
735
662
  payload: {
736
- errors: [{ errorType = '', errorCode = 0 } = {}] = [],
663
+ connectionTimeoutMs = DEFAULT_KEEP_ALIVE_TIMEOUT,
737
664
  } = {},
738
665
  } = data;
666
+ if (type === MESSAGE_TYPES.GQL_CONNECTION_ACK) {
667
+ ackOk = true;
668
+ if (this.awsRealTimeSocket) {
669
+ this.keepAliveTimeout = connectionTimeoutMs;
670
+ this.awsRealTimeSocket.onmessage =
671
+ this._handleIncomingSubscriptionMessage.bind(this);
672
+ this.awsRealTimeSocket.onerror = err => {
673
+ logger.debug(err);
674
+ this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
675
+ };
676
+ this.awsRealTimeSocket.onclose = event => {
677
+ logger.debug(`WebSocket closed ${event.reason}`);
678
+ this._errorDisconnect(CONTROL_MSG.CONNECTION_CLOSED);
679
+ };
680
+ }
681
+ res('Cool, connected to AWS AppSyncRealTime');
682
+ return;
683
+ }
684
+
685
+ if (type === MESSAGE_TYPES.GQL_CONNECTION_ERROR) {
686
+ const {
687
+ payload: {
688
+ errors: [{ errorType = '', errorCode = 0 } = {}] = [],
689
+ } = {},
690
+ } = data;
691
+
692
+ rej({ errorType, errorCode });
693
+ }
694
+ };
695
+
696
+ const gqlInit = {
697
+ type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
698
+ };
699
+ this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
700
+
701
+ setTimeout(checkAckOk.bind(this, ackOk), CONNECTION_INIT_TIMEOUT);
702
+ }
739
703
 
740
- rej({ errorType, errorCode });
741
- }
742
- };
743
-
744
- const gqlInit = {
745
- type: MESSAGE_TYPES.GQL_CONNECTION_INIT,
746
- };
747
- this.awsRealTimeSocket.send(JSON.stringify(gqlInit));
748
-
749
- function checkAckOk() {
704
+ function checkAckOk(ackOk: boolean) {
750
705
  if (!ackOk) {
751
706
  rej(
752
707
  new Error(
@@ -755,12 +710,13 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
755
710
  );
756
711
  }
757
712
  }
758
-
759
- setTimeout(checkAckOk.bind(this), CONNECTION_INIT_TIMEOUT);
760
713
  });
761
714
  })();
762
715
  } catch (err) {
763
- const { errorType, errorCode } = err;
716
+ const { errorType, errorCode } = err as {
717
+ errorType: string;
718
+ errorCode: number;
719
+ };
764
720
 
765
721
  if (NON_RETRYABLE_CODES.includes(errorCode)) {
766
722
  throw new NonRetryableError(errorType);
@@ -780,8 +736,10 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
780
736
  apiKey,
781
737
  region,
782
738
  additionalHeaders,
783
- }): Promise<any> {
784
- const headerHandler = {
739
+ }: AWSAppSyncRealTimeProviderOptions): Promise<any> {
740
+ const headerHandler: {
741
+ [key in GraphqlAuthModes]: (AWSAppSyncRealTimeAuthInput) => {};
742
+ } = {
785
743
  API_KEY: this._awsRealTimeApiKeyHeader.bind(this),
786
744
  AWS_IAM: this._awsRealTimeIAMHeader.bind(this),
787
745
  OPENID_CONNECT: this._awsRealTimeOPENIDHeader.bind(this),
@@ -789,29 +747,31 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
789
747
  AWS_LAMBDA: this._customAuthHeader,
790
748
  };
791
749
 
792
- const handler = headerHandler[authenticationType];
793
-
794
- if (typeof handler !== 'function') {
750
+ if (!authenticationType || !headerHandler[authenticationType]) {
795
751
  logger.debug(`Authentication type ${authenticationType} not supported`);
796
752
  return '';
797
- }
753
+ } else {
754
+ const handler = headerHandler[authenticationType];
798
755
 
799
- const { host } = url.parse(appSyncGraphqlEndpoint);
756
+ const { host } = url.parse(appSyncGraphqlEndpoint ?? '');
800
757
 
801
- const result = await handler({
802
- payload,
803
- canonicalUri,
804
- appSyncGraphqlEndpoint,
805
- apiKey,
806
- region,
807
- host,
808
- additionalHeaders,
809
- });
758
+ logger.debug(`Authenticating with ${authenticationType}`);
810
759
 
811
- return result;
760
+ const result = await handler({
761
+ payload,
762
+ canonicalUri,
763
+ appSyncGraphqlEndpoint,
764
+ apiKey,
765
+ region,
766
+ host,
767
+ additionalHeaders,
768
+ });
769
+
770
+ return result;
771
+ }
812
772
  }
813
773
 
814
- private async _awsRealTimeCUPHeader({ host }) {
774
+ private async _awsRealTimeCUPHeader({ host }: AWSAppSyncRealTimeAuthInput) {
815
775
  const session = await Auth.currentSession();
816
776
  return {
817
777
  Authorization: session.getAccessToken().getJwtToken(),
@@ -819,7 +779,9 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
819
779
  };
820
780
  }
821
781
 
822
- private async _awsRealTimeOPENIDHeader({ host }) {
782
+ private async _awsRealTimeOPENIDHeader({
783
+ host,
784
+ }: AWSAppSyncRealTimeAuthInput) {
823
785
  let token;
824
786
  // backwards compatibility
825
787
  const federatedInfo = await Cache.getItem('federatedInfo');
@@ -840,7 +802,10 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
840
802
  };
841
803
  }
842
804
 
843
- private async _awsRealTimeApiKeyHeader({ apiKey, host }) {
805
+ private async _awsRealTimeApiKeyHeader({
806
+ apiKey,
807
+ host,
808
+ }: AWSAppSyncRealTimeAuthInput) {
844
809
  const dt = new Date();
845
810
  const dtStr = dt.toISOString().replace(/[:\-]|\.\d{3}/g, '');
846
811
 
@@ -856,7 +821,7 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
856
821
  canonicalUri,
857
822
  appSyncGraphqlEndpoint,
858
823
  region,
859
- }) {
824
+ }: AWSAppSyncRealTimeAuthInput) {
860
825
  const endpointInfo = {
861
826
  region,
862
827
  service: 'appsync',
@@ -866,11 +831,16 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
866
831
  if (!credentialsOK) {
867
832
  throw new Error('No credentials');
868
833
  }
869
- const creds = await Credentials.get().then(credentials => ({
870
- secret_key: credentials.secretAccessKey,
871
- access_key: credentials.accessKeyId,
872
- session_token: credentials.sessionToken,
873
- }));
834
+ const creds = await Credentials.get().then((credentials: any) => {
835
+ const { secretAccessKey, accessKeyId, sessionToken } =
836
+ credentials as ICredentials;
837
+
838
+ return {
839
+ secret_key: secretAccessKey,
840
+ access_key: accessKeyId,
841
+ session_token: sessionToken,
842
+ };
843
+ });
874
844
 
875
845
  const request = {
876
846
  url: `${appSyncGraphqlEndpoint}${canonicalUri}`,
@@ -883,8 +853,11 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
883
853
  return signed_params.headers;
884
854
  }
885
855
 
886
- private _customAuthHeader({ host, additionalHeaders }) {
887
- if (!additionalHeaders.Authorization) {
856
+ private _customAuthHeader({
857
+ host,
858
+ additionalHeaders,
859
+ }: AWSAppSyncRealTimeAuthInput) {
860
+ if (!additionalHeaders || !additionalHeaders['Authorization']) {
888
861
  throw new Error('No auth token specified');
889
862
  }
890
863
 
@@ -899,14 +872,14 @@ export class AWSAppSyncRealTimeProvider extends AbstractPubSubProvider {
899
872
  */
900
873
  _ensureCredentials() {
901
874
  return Credentials.get()
902
- .then(credentials => {
875
+ .then((credentials: any) => {
903
876
  if (!credentials) return false;
904
877
  const cred = Credentials.shear(credentials);
905
878
  logger.debug('set credentials for AWSAppSyncRealTimeProvider', cred);
906
879
 
907
880
  return true;
908
881
  })
909
- .catch(err => {
882
+ .catch((err: any) => {
910
883
  logger.warn('ensure credentials error', err);
911
884
  return false;
912
885
  });