@aws-amplify/pubsub 4.4.9 → 4.4.10-next.13

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 (84) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/dist/aws-amplify-pubsub.js +713 -378
  3. package/dist/aws-amplify-pubsub.js.map +1 -1
  4. package/dist/aws-amplify-pubsub.min.js +3 -3
  5. package/dist/aws-amplify-pubsub.min.js.map +1 -1
  6. package/lib/Providers/AWSAppSyncRealTimeProvider/index.d.ts +10 -2
  7. package/lib/Providers/AWSAppSyncRealTimeProvider/index.js +133 -90
  8. package/lib/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -1
  9. package/lib/Providers/MqttOverWSProvider.d.ts +5 -1
  10. package/lib/Providers/MqttOverWSProvider.js +152 -87
  11. package/lib/Providers/MqttOverWSProvider.js.map +1 -1
  12. package/lib/Providers/PubSubProvider.d.ts +1 -1
  13. package/lib/Providers/{AWSAppSyncRealTimeProvider/constants.d.ts → constants.d.ts} +9 -0
  14. package/lib/Providers/{AWSAppSyncRealTimeProvider/constants.js → constants.js} +9 -0
  15. package/lib/Providers/constants.js.map +1 -0
  16. package/lib/index.d.ts +3 -10
  17. package/lib/index.js +3 -10
  18. package/lib/index.js.map +1 -1
  19. package/lib/types/Provider.d.ts +10 -1
  20. package/lib/types/PubSub.d.ts +22 -8
  21. package/lib/types/PubSub.js +56 -0
  22. package/lib/types/PubSub.js.map +1 -1
  23. package/lib/types/index.d.ts +0 -17
  24. package/lib/types/index.js +4 -36
  25. package/lib/types/index.js.map +1 -1
  26. package/lib/utils/ConnectionStateMonitor.d.ts +2 -1
  27. package/lib/utils/ConnectionStateMonitor.js +21 -13
  28. package/lib/utils/ConnectionStateMonitor.js.map +1 -1
  29. package/lib/utils/ReachabilityMonitor/index.d.ts +3 -0
  30. package/lib/utils/ReachabilityMonitor/index.js +5 -0
  31. package/lib/utils/ReachabilityMonitor/index.js.map +1 -0
  32. package/lib/utils/ReachabilityMonitor/index.native.d.ts +3 -0
  33. package/lib/utils/ReachabilityMonitor/index.native.js +11 -0
  34. package/lib/utils/ReachabilityMonitor/index.native.js.map +1 -0
  35. package/lib/utils/ReconnectionMonitor.d.ts +29 -0
  36. package/lib/utils/ReconnectionMonitor.js +73 -0
  37. package/lib/utils/ReconnectionMonitor.js.map +1 -0
  38. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.d.ts +10 -2
  39. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js +129 -86
  40. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/index.js.map +1 -1
  41. package/lib-esm/Providers/MqttOverWSProvider.d.ts +5 -1
  42. package/lib-esm/Providers/MqttOverWSProvider.js +153 -88
  43. package/lib-esm/Providers/MqttOverWSProvider.js.map +1 -1
  44. package/lib-esm/Providers/PubSubProvider.d.ts +1 -1
  45. package/lib-esm/Providers/{AWSAppSyncRealTimeProvider/constants.d.ts → constants.d.ts} +9 -0
  46. package/lib-esm/Providers/{AWSAppSyncRealTimeProvider/constants.js → constants.js} +9 -0
  47. package/lib-esm/Providers/constants.js.map +1 -0
  48. package/lib-esm/index.d.ts +3 -10
  49. package/lib-esm/index.js +3 -11
  50. package/lib-esm/index.js.map +1 -1
  51. package/lib-esm/types/Provider.d.ts +10 -1
  52. package/lib-esm/types/PubSub.d.ts +22 -8
  53. package/lib-esm/types/PubSub.js +56 -0
  54. package/lib-esm/types/PubSub.js.map +1 -1
  55. package/lib-esm/types/index.d.ts +0 -17
  56. package/lib-esm/types/index.js +1 -36
  57. package/lib-esm/types/index.js.map +1 -1
  58. package/lib-esm/utils/ConnectionStateMonitor.d.ts +2 -1
  59. package/lib-esm/utils/ConnectionStateMonitor.js +13 -5
  60. package/lib-esm/utils/ConnectionStateMonitor.js.map +1 -1
  61. package/lib-esm/utils/ReachabilityMonitor/index.d.ts +3 -0
  62. package/lib-esm/utils/ReachabilityMonitor/index.js +3 -0
  63. package/lib-esm/utils/ReachabilityMonitor/index.js.map +1 -0
  64. package/lib-esm/utils/ReachabilityMonitor/index.native.d.ts +3 -0
  65. package/lib-esm/utils/ReachabilityMonitor/index.native.js +6 -0
  66. package/lib-esm/utils/ReachabilityMonitor/index.native.js.map +1 -0
  67. package/lib-esm/utils/ReconnectionMonitor.d.ts +29 -0
  68. package/lib-esm/utils/ReconnectionMonitor.js +71 -0
  69. package/lib-esm/utils/ReconnectionMonitor.js.map +1 -0
  70. package/package.json +5 -5
  71. package/src/Providers/AWSAppSyncRealTimeProvider/index.ts +154 -90
  72. package/src/Providers/MqttOverWSProvider.ts +120 -53
  73. package/src/Providers/PubSubProvider.ts +1 -1
  74. package/src/Providers/{AWSAppSyncRealTimeProvider/constants.ts → constants.ts} +12 -0
  75. package/src/index.ts +3 -10
  76. package/src/types/Provider.ts +13 -1
  77. package/src/types/PubSub.ts +47 -8
  78. package/src/types/index.ts +0 -43
  79. package/src/utils/ConnectionStateMonitor.ts +20 -5
  80. package/src/utils/ReachabilityMonitor/index.native.ts +5 -0
  81. package/src/utils/ReachabilityMonitor/index.ts +3 -0
  82. package/src/utils/ReconnectionMonitor.ts +74 -0
  83. package/lib/Providers/AWSAppSyncRealTimeProvider/constants.js.map +0 -1
  84. package/lib-esm/Providers/AWSAppSyncRealTimeProvider/constants.js.map +0 -1
@@ -12,11 +12,21 @@
12
12
  */
13
13
  import * as Paho from 'paho-mqtt';
14
14
  import { v4 as uuid } from 'uuid';
15
- import Observable from 'zen-observable-ts';
15
+ import Observable, { ZenObservable } from 'zen-observable-ts';
16
16
 
17
17
  import { AbstractPubSubProvider } from './PubSubProvider';
18
- import { ProviderOptions, SubscriptionObserver } from '../types';
19
- import { ConsoleLogger as Logger } from '@aws-amplify/core';
18
+ import { SubscriptionObserver, ConnectionState } from '../types/PubSub';
19
+ import { ProviderOptions } from '../types/Provider';
20
+ import { ConsoleLogger as Logger, Hub } from '@aws-amplify/core';
21
+ import {
22
+ ConnectionStateMonitor,
23
+ CONNECTION_CHANGE,
24
+ } from '../utils/ConnectionStateMonitor';
25
+ import {
26
+ ReconnectEvent,
27
+ ReconnectionMonitor,
28
+ } from '../utils/ReconnectionMonitor';
29
+ import { AMPLIFY_SYMBOL, CONNECTION_STATE_CHANGE } from './constants';
20
30
 
21
31
  const logger = new Logger('MqttOverWSProvider');
22
32
 
@@ -49,17 +59,15 @@ class ClientsQueue {
49
59
 
50
60
  async get(clientId: string, clientFactory?: (input: string) => Promise<any>) {
51
61
  const cachedPromise = this.promises.get(clientId);
52
- if (cachedPromise) {
53
- return cachedPromise;
54
- }
62
+ if (cachedPromise) return cachedPromise;
55
63
 
56
64
  if (clientFactory) {
57
65
  const newPromise = clientFactory(clientId);
58
-
59
66
  this.promises.set(clientId, newPromise);
60
-
67
+ newPromise.catch(v => this.promises.delete(clientId));
61
68
  return newPromise;
62
69
  }
70
+
63
71
  return undefined;
64
72
  }
65
73
 
@@ -72,13 +80,44 @@ class ClientsQueue {
72
80
  }
73
81
  }
74
82
 
83
+ const dispatchPubSubEvent = (event: string, data: any, message: string) => {
84
+ Hub.dispatch('pubsub', { event, data, message }, 'PubSub', AMPLIFY_SYMBOL);
85
+ };
86
+
75
87
  const topicSymbol = typeof Symbol !== 'undefined' ? Symbol('topic') : '@@topic';
76
88
 
77
89
  export class MqttOverWSProvider extends AbstractPubSubProvider {
78
90
  private _clientsQueue = new ClientsQueue();
91
+ private connectionState: ConnectionState;
92
+ private readonly connectionStateMonitor = new ConnectionStateMonitor();
93
+ private readonly reconnectionMonitor = new ReconnectionMonitor();
79
94
 
80
95
  constructor(options: MqttProviderOptions = {}) {
81
96
  super({ ...options, clientId: options.clientId || uuid() });
97
+
98
+ // Monitor the connection health state and pass changes along to Hub
99
+ this.connectionStateMonitor.connectionStateObservable.subscribe(
100
+ connectionStateChange => {
101
+ dispatchPubSubEvent(
102
+ CONNECTION_STATE_CHANGE,
103
+ {
104
+ provider: this,
105
+ connectionState: connectionStateChange,
106
+ },
107
+ `Connection state is ${connectionStateChange}`
108
+ );
109
+
110
+ this.connectionState = connectionStateChange;
111
+
112
+ // Trigger reconnection when the connection is disrupted
113
+ if (connectionStateChange === ConnectionState.ConnectionDisrupted) {
114
+ this.reconnectionMonitor.record(ReconnectEvent.START_RECONNECT);
115
+ } else if (connectionStateChange !== ConnectionState.Connecting) {
116
+ // Trigger connected to halt reconnection attempts
117
+ this.reconnectionMonitor.record(ReconnectEvent.HALT_RECONNECT);
118
+ }
119
+ }
120
+ );
82
121
  }
83
122
 
84
123
  protected get clientId() {
@@ -117,7 +156,6 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
117
156
  if (errorCode !== 0) {
118
157
  logger.warn(clientId, JSON.stringify({ errorCode, ...args }, null, 2));
119
158
 
120
- const topicsToDelete: string[] = [];
121
159
  if (!clientId) {
122
160
  return;
123
161
  }
@@ -125,33 +163,17 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
125
163
  if (!clientIdObservers) {
126
164
  return;
127
165
  }
128
- clientIdObservers.forEach(observer => {
129
- observer.error('Disconnected, error code: ' + errorCode);
130
- // removing observers for disconnected clientId
131
- this._topicObservers.forEach((observerForTopic, observerTopic) => {
132
- observerForTopic.delete(observer);
133
- if (observerForTopic.size === 0) {
134
- topicsToDelete.push(observerTopic);
135
- }
136
- });
137
- });
138
-
139
- // forgiving any trace of clientId
140
- this._clientIdObservers.delete(clientId);
141
-
142
- // Removing topics that are not listen by an observer
143
- topicsToDelete.forEach(topic => {
144
- this._topicObservers.delete(topic);
145
- });
166
+ this.disconnect(clientId);
146
167
  }
147
168
  }
148
169
 
149
170
  public async newClient({ url, clientId }: MqttProviderOptions): Promise<any> {
150
171
  logger.debug('Creating new MQTT client', clientId);
151
172
 
173
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.OPENING_CONNECTION);
152
174
  // @ts-ignore
153
175
  const client = new Paho.Client(url, clientId);
154
- // client.trace = (args) => logger.debug(clientId, JSON.stringify(args, null, 2));
176
+
155
177
  client.onMessageArrived = ({
156
178
  destinationName: topic,
157
179
  payloadString: msg,
@@ -168,17 +190,28 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
168
190
  errorCode: number;
169
191
  }) => {
170
192
  this.onDisconnect({ clientId, errorCode, ...args });
193
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
171
194
  };
172
195
 
173
- await new Promise((resolve, reject) => {
196
+ const connected = await new Promise((resolve, reject) => {
174
197
  client.connect({
175
198
  useSSL: this.isSSLEnabled,
176
199
  mqttVersion: 3,
177
- onSuccess: () => resolve(client),
178
- onFailure: reject,
200
+ onSuccess: () => resolve(true),
201
+ onFailure: x => {
202
+ if (clientId) this._clientsQueue.remove(clientId);
203
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
204
+ resolve(false);
205
+ },
179
206
  });
180
207
  });
181
208
 
209
+ if (connected) {
210
+ this.connectionStateMonitor.record(
211
+ CONNECTION_CHANGE.CONNECTION_ESTABLISHED
212
+ );
213
+ }
214
+
182
215
  return client;
183
216
  }
184
217
 
@@ -186,9 +219,19 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
186
219
  clientId: string,
187
220
  options: MqttProviderOptions = {}
188
221
  ): Promise<any> {
189
- return await this.clientsQueue.get(clientId, clientId =>
190
- this.newClient({ ...options, clientId })
191
- );
222
+ return await this.clientsQueue.get(clientId, async clientId => {
223
+ const client = await this.newClient({ ...options, clientId });
224
+
225
+ if (client) {
226
+ // Once connected, subscribe to all topics registered observers
227
+ this._topicObservers.forEach(
228
+ (_value: Set<SubscriptionObserver<any>>, key: string) => {
229
+ client.subscribe(key);
230
+ }
231
+ );
232
+ }
233
+ return client;
234
+ });
192
235
  }
193
236
 
194
237
  protected async disconnect(clientId: string): Promise<void> {
@@ -198,18 +241,25 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
198
241
  client.disconnect();
199
242
  }
200
243
  this.clientsQueue.remove(clientId);
244
+ this.connectionStateMonitor.record(CONNECTION_CHANGE.CLOSED);
201
245
  }
202
246
 
203
247
  async publish(topics: string[] | string, msg: any) {
204
248
  const targetTopics = ([] as string[]).concat(topics);
205
249
  const message = JSON.stringify(msg);
206
250
 
207
- const url = await this.endpoint;
208
-
209
- const client = await this.connect(this.clientId, { url });
210
-
211
- logger.debug('Publishing to topic(s)', targetTopics.join(','), message);
212
- targetTopics.forEach(topic => client.send(topic, message));
251
+ const client = await this.clientsQueue.get(this.clientId);
252
+
253
+ if (client) {
254
+ logger.debug('Publishing to topic(s)', targetTopics.join(','), message);
255
+ targetTopics.forEach(topic => client.send(topic, message));
256
+ } else {
257
+ logger.debug(
258
+ 'Publishing to topic(s) failed',
259
+ targetTopics.join(','),
260
+ message
261
+ );
262
+ }
213
263
  }
214
264
 
215
265
  protected _topicObservers: Map<string, Set<SubscriptionObserver<any>>> =
@@ -246,6 +296,7 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
246
296
  ): Observable<any> {
247
297
  const targetTopics = ([] as string[]).concat(topics);
248
298
  logger.debug('Subscribing to topic(s)', targetTopics.join(','));
299
+ let reconnectSubscription: ZenObservable.Subscription;
249
300
 
250
301
  return new Observable(observer => {
251
302
  targetTopics.forEach(topic => {
@@ -261,8 +312,6 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
261
312
  observersForTopic.add(observer);
262
313
  });
263
314
 
264
- // @ts-ignore
265
- let client: Paho.Client;
266
315
  const { clientId = this.clientId } = options;
267
316
 
268
317
  // this._clientIdObservers is used to close observers when client gets disconnected
@@ -274,26 +323,44 @@ export class MqttOverWSProvider extends AbstractPubSubProvider {
274
323
  this._clientIdObservers.set(clientId, observersForClientId);
275
324
 
276
325
  (async () => {
277
- const { url = await this.endpoint } = options;
326
+ const getClient = async () => {
327
+ try {
328
+ const { url = await this.endpoint } = options;
329
+ const client = await this.connect(clientId, { url });
330
+ if (client !== undefined) {
331
+ targetTopics.forEach(topic => {
332
+ client.subscribe(topic);
333
+ });
334
+ }
335
+ } catch (e) {
336
+ logger.debug('Error forming connection', e);
337
+ }
338
+ };
278
339
 
279
- try {
280
- client = await this.connect(clientId, { url });
281
- targetTopics.forEach(topic => {
282
- client.subscribe(topic);
283
- });
284
- } catch (e) {
285
- observer.error(e);
286
- }
340
+ // Establish the initial connection
341
+ await getClient();
342
+
343
+ // Add an observable to the reconnection list to manage reconnection for this subscription
344
+ reconnectSubscription = new Observable(observer => {
345
+ this.reconnectionMonitor.addObserver(observer);
346
+ }).subscribe(() => {
347
+ getClient();
348
+ });
287
349
  })();
288
350
 
289
- return () => {
290
- logger.debug('Unsubscribing from topic(s)', targetTopics.join(','));
351
+ return async () => {
352
+ const client = await this.clientsQueue.get(clientId);
353
+
354
+ reconnectSubscription?.unsubscribe();
291
355
 
292
356
  if (client) {
293
357
  this._clientIdObservers.get(clientId)?.delete(observer);
294
358
  // No more observers per client => client not needed anymore
295
359
  if (this._clientIdObservers.get(clientId)?.size === 0) {
296
360
  this.disconnect(clientId);
361
+ this.connectionStateMonitor.record(
362
+ CONNECTION_CHANGE.CLOSING_CONNECTION
363
+ );
297
364
  this._clientIdObservers.delete(clientId);
298
365
  }
299
366
 
@@ -11,7 +11,7 @@
11
11
  * and limitations under the License.
12
12
  */
13
13
  import Observable from 'zen-observable-ts';
14
- import { PubSubProvider, ProviderOptions } from '../types';
14
+ import { PubSubProvider, ProviderOptions } from '../types/Provider';
15
15
  import { ConsoleLogger as Logger } from '@aws-amplify/core';
16
16
 
17
17
  const logger = new Logger('AbstractPubSubProvider');
@@ -2,6 +2,8 @@ export const MAX_DELAY_MS = 5000;
2
2
 
3
3
  export const NON_RETRYABLE_CODES = [400, 401, 403];
4
4
 
5
+ export const CONNECTION_STATE_CHANGE = 'ConnectionStateChange';
6
+
5
7
  export enum MESSAGE_TYPES {
6
8
  /**
7
9
  * Client -> Server message.
@@ -98,3 +100,13 @@ export const DEFAULT_KEEP_ALIVE_TIMEOUT = 5 * 60 * 1000;
98
100
  * Default Time in milleseconds to alert for missed GQL_CONNECTION_KEEP_ALIVE message
99
101
  */
100
102
  export const DEFAULT_KEEP_ALIVE_ALERT_TIMEOUT = 65 * 1000;
103
+
104
+ /**
105
+ * Default delay time in milleseconds between when reconnect is triggered vs when it is attempted
106
+ */
107
+ export const RECONNECT_DELAY = 5 * 1000;
108
+
109
+ /**
110
+ * Default interval time in milleseconds between when reconnect is re-attempted
111
+ */
112
+ export const RECONNECT_INTERVAL = 60 * 1000;
package/src/index.ts CHANGED
@@ -14,18 +14,11 @@ import { PubSub } from './PubSub';
14
14
 
15
15
  export * from './Providers';
16
16
 
17
- enum CONTROL_MSG {
18
- CONNECTION_CLOSED = 'Connection closed',
19
- CONNECTION_FAILED = 'Connection failed',
20
- REALTIME_SUBSCRIPTION_INIT_ERROR = 'AppSync Realtime subscription init error',
21
- SUBSCRIPTION_ACK = 'Subscription ack',
22
- TIMEOUT_DISCONNECT = 'Timeout disconnect',
23
- }
17
+ export { CONNECTION_STATE_CHANGE } from './Providers/constants';
24
18
 
25
- export const CONNECTION_STATE_CHANGE = 'ConnectionStateChange';
26
- export { ConnectionState } from './types';
19
+ export { ConnectionState, CONTROL_MSG } from './types';
27
20
 
28
- export { PubSub, CONTROL_MSG };
21
+ export { PubSub };
29
22
 
30
23
  /**
31
24
  * @deprecated use named import
@@ -11,7 +11,19 @@
11
11
  * and limitations under the License.
12
12
  */
13
13
  import Observable from 'zen-observable-ts';
14
- import { ProviderOptions } from './PubSub';
14
+
15
+ export interface PubSubOptions {
16
+ [key: string]: any;
17
+ }
18
+
19
+ export interface ProviderOptions {
20
+ [key: string]: any;
21
+ }
22
+
23
+ /**
24
+ * @deprecated Migrated to ProviderOptions
25
+ */
26
+ export type ProvidertOptions = ProviderOptions;
15
27
 
16
28
  export interface PubSubProvider {
17
29
  // configure your provider
@@ -10,15 +10,54 @@
10
10
  * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11
11
  * and limitations under the License.
12
12
  */
13
- export interface PubSubOptions {
14
- [key: string]: any;
13
+
14
+ export interface SubscriptionObserver<T> {
15
+ closed: boolean;
16
+ next(value: T): void;
17
+ error(errorValue: any): void;
18
+ complete(): void;
15
19
  }
16
20
 
17
- export interface ProviderOptions {
18
- [key: string]: any;
21
+ export enum CONTROL_MSG {
22
+ CONNECTION_CLOSED = 'Connection closed',
23
+ CONNECTION_FAILED = 'Connection failed',
24
+ REALTIME_SUBSCRIPTION_INIT_ERROR = 'AppSync Realtime subscription init error',
25
+ SUBSCRIPTION_ACK = 'Subscription ack',
26
+ TIMEOUT_DISCONNECT = 'Timeout disconnect',
19
27
  }
20
28
 
21
- /**
22
- * @deprecated Migrated to ProviderOptions
23
- */
24
- export type ProvidertOptions = ProviderOptions;
29
+ /** @enum {string} */
30
+ export enum ConnectionState {
31
+ /*
32
+ * The connection is alive and healthy
33
+ */
34
+ Connected = 'Connected',
35
+ /*
36
+ * The connection is alive, but the connection is offline
37
+ */
38
+ ConnectedPendingNetwork = 'ConnectedPendingNetwork',
39
+ /*
40
+ * The connection has been disconnected while in use
41
+ */
42
+ ConnectionDisrupted = 'ConnectionDisrupted',
43
+ /*
44
+ * The connection has been disconnected and the network is offline
45
+ */
46
+ ConnectionDisruptedPendingNetwork = 'ConnectionDisruptedPendingNetwork',
47
+ /*
48
+ * The connection is in the process of connecting
49
+ */
50
+ Connecting = 'Connecting',
51
+ /*
52
+ * The connection is not in use and is being disconnected
53
+ */
54
+ ConnectedPendingDisconnect = 'ConnectedPendingDisconnect',
55
+ /*
56
+ * The connection is not in use and has been disconnected
57
+ */
58
+ Disconnected = 'Disconnected',
59
+ /*
60
+ * The connection is alive, but a keep alive message has been missed
61
+ */
62
+ ConnectedPendingKeepAlive = 'ConnectedPendingKeepAlive',
63
+ }
@@ -12,46 +12,3 @@
12
12
  */
13
13
  export * from './Provider';
14
14
  export * from './PubSub';
15
-
16
- export interface SubscriptionObserver<T> {
17
- closed: boolean;
18
- next(value: T): void;
19
- error(errorValue: any): void;
20
- complete(): void;
21
- }
22
-
23
- /** @enum {string} */
24
- export enum ConnectionState {
25
- /*
26
- * The connection is alive and healthy
27
- */
28
- Connected = 'Connected',
29
- /*
30
- * The connection is alive, but the connection is offline
31
- */
32
- ConnectedPendingNetwork = 'ConnectedPendingNetwork',
33
- /*
34
- * The connection has been disconnected while in use
35
- */
36
- ConnectionDisrupted = 'ConnectionDisrupted',
37
- /*
38
- * The connection has been disconnected and the network is offline
39
- */
40
- ConnectionDisruptedPendingNetwork = 'ConnectionDisruptedPendingNetwork',
41
- /*
42
- * The connection is in the process of connecting
43
- */
44
- Connecting = 'Connecting',
45
- /*
46
- * The connection is not in use and is being disconnected
47
- */
48
- ConnectedPendingDisconnect = 'ConnectedPendingDisconnect',
49
- /*
50
- * The connection is not in use and has been disconnected
51
- */
52
- Disconnected = 'Disconnected',
53
- /*
54
- * The connection is alive, but a keep alive message has been missed
55
- */
56
- ConnectedPendingKeepAlive = 'ConnectedPendingKeepAlive',
57
- }
@@ -13,7 +13,8 @@
13
13
 
14
14
  import { Reachability } from '@aws-amplify/core';
15
15
  import Observable, { ZenObservable } from 'zen-observable-ts';
16
- import { ConnectionState } from '../index';
16
+ import { ConnectionState } from '../types/PubSub';
17
+ import { ReachabilityMonitor } from './ReachabilityMonitor';
17
18
 
18
19
  // Internal types for tracking different connection states
19
20
  type LinkedConnectionState = 'connected' | 'disconnected';
@@ -62,6 +63,7 @@ export class ConnectionStateMonitor {
62
63
  private _linkedConnectionStateObservable: Observable<LinkedConnectionStates>;
63
64
  private _linkedConnectionStateObserver: ZenObservable.SubscriptionObserver<LinkedConnectionStates>;
64
65
  private _networkMonitoringSubscription?: ZenObservable.Subscription;
66
+ private _initialNetworkStateSubscription?: ZenObservable.Subscription;
65
67
 
66
68
  constructor() {
67
69
  this._networkMonitoringSubscription = undefined;
@@ -72,6 +74,16 @@ export class ConnectionStateMonitor {
72
74
  keepAliveState: 'healthy',
73
75
  };
74
76
 
77
+ // Attempt to update the state with the current actual network state
78
+ this._initialNetworkStateSubscription = ReachabilityMonitor().subscribe(
79
+ ({ online }) => {
80
+ this.record(
81
+ online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE
82
+ );
83
+ this._initialNetworkStateSubscription?.unsubscribe();
84
+ }
85
+ );
86
+
75
87
  this._linkedConnectionStateObservable =
76
88
  new Observable<LinkedConnectionStates>(connectionStateObserver => {
77
89
  connectionStateObserver.next(this._linkedConnectionState);
@@ -83,15 +95,18 @@ export class ConnectionStateMonitor {
83
95
  * Turn network state monitoring on if it isn't on already
84
96
  */
85
97
  private enableNetworkMonitoring() {
98
+ // If no initial network state was discovered, stop trying
99
+ this._initialNetworkStateSubscription?.unsubscribe();
100
+
86
101
  // Maintain the network state based on the reachability monitor
87
102
  if (this._networkMonitoringSubscription === undefined) {
88
- this._networkMonitoringSubscription = new Reachability()
89
- .networkMonitor()
90
- .subscribe(({ online }) => {
103
+ this._networkMonitoringSubscription = ReachabilityMonitor().subscribe(
104
+ ({ online }) => {
91
105
  this.record(
92
106
  online ? CONNECTION_CHANGE.ONLINE : CONNECTION_CHANGE.OFFLINE
93
107
  );
94
- });
108
+ }
109
+ );
95
110
  }
96
111
  }
97
112
 
@@ -0,0 +1,5 @@
1
+ import { Reachability } from '@aws-amplify/core';
2
+ import { default as NetInfo } from '@react-native-community/netinfo';
3
+
4
+ export const ReachabilityMonitor = () =>
5
+ new Reachability().networkMonitor(NetInfo);
@@ -0,0 +1,3 @@
1
+ import { Reachability } from '@aws-amplify/core';
2
+
3
+ export const ReachabilityMonitor = () => new Reachability().networkMonitor();
@@ -0,0 +1,74 @@
1
+ import { Observer } from 'zen-observable-ts';
2
+ import { RECONNECT_DELAY, RECONNECT_INTERVAL } from '../Providers/constants';
3
+
4
+ export enum ReconnectEvent {
5
+ START_RECONNECT = 'START_RECONNECT',
6
+ HALT_RECONNECT = 'HALT_RECONNECT',
7
+ }
8
+
9
+ /**
10
+ * Captures the reconnect event logic used to determine when to reconnect to PubSub providers.
11
+ * Reconnnect attempts are delayed by 5 seconds to let the interface settle.
12
+ * Attempting to reconnect only once creates unrecoverable states when the network state isn't
13
+ * supported by the browser, so this keeps retrying every minute until halted.
14
+ */
15
+ export class ReconnectionMonitor {
16
+ private reconnectObservers: Observer<void>[] = [];
17
+ private reconnectIntervalId?: ReturnType<typeof setInterval>;
18
+ private reconnectSetTimeoutId?: ReturnType<typeof setTimeout>;
19
+
20
+ /**
21
+ * Add reconnect observer to the list of observers to alert on reconnect
22
+ */
23
+ addObserver(reconnectObserver: Observer<void>) {
24
+ this.reconnectObservers.push(reconnectObserver);
25
+ }
26
+
27
+ /**
28
+ * Given a reconnect event, start the appropriate behavior
29
+ */
30
+ record(event: ReconnectEvent) {
31
+ if (event === ReconnectEvent.START_RECONNECT) {
32
+ // If the reconnection hasn't been started
33
+ if (
34
+ this.reconnectSetTimeoutId === undefined &&
35
+ this.reconnectIntervalId === undefined
36
+ ) {
37
+ this.reconnectSetTimeoutId = setTimeout(() => {
38
+ // Reconnect now
39
+ this._triggerReconnect();
40
+ // Retry reconnect every periodically until it works
41
+ this.reconnectIntervalId = setInterval(() => {
42
+ this._triggerReconnect();
43
+ }, RECONNECT_INTERVAL);
44
+ }, RECONNECT_DELAY);
45
+ }
46
+ }
47
+
48
+ if (event === ReconnectEvent.HALT_RECONNECT) {
49
+ if (this.reconnectIntervalId) {
50
+ clearInterval(this.reconnectIntervalId);
51
+ this.reconnectIntervalId = undefined;
52
+ }
53
+ if (this.reconnectSetTimeoutId) {
54
+ clearTimeout(this.reconnectSetTimeoutId);
55
+ this.reconnectSetTimeoutId = undefined;
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Complete all reconnect observers
62
+ */
63
+ close() {
64
+ this.reconnectObservers.forEach(reconnectObserver => {
65
+ reconnectObserver.complete?.();
66
+ });
67
+ }
68
+
69
+ private _triggerReconnect() {
70
+ this.reconnectObservers.forEach(reconnectObserver => {
71
+ reconnectObserver.next?.();
72
+ });
73
+ }
74
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/Providers/AWSAppSyncRealTimeProvider/constants.ts"],"names":[],"mappings":";;AAAa,QAAA,YAAY,GAAG,IAAI,CAAC;AAEpB,QAAA,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEnD,IAAY,aAmDX;AAnDD,WAAY,aAAa;IACxB;;;OAGG;IACH,wDAAuC,CAAA;IACvC;;;OAGG;IACH,0DAAyC,CAAA;IACzC;;;OAGG;IACH,sDAAqC,CAAA;IACrC;;;OAGG;IACH,oCAAmB,CAAA;IACnB;;;OAGG;IACH,4CAA2B,CAAA;IAC3B;;;OAGG;IACH,kCAAiB,CAAA;IACjB;;;OAGG;IACH,iDAAgC,CAAA;IAChC;;;OAGG;IACH,kCAAiB,CAAA;IACjB;;;OAGG;IACH,0CAAyB,CAAA;IACzB;;;OAGG;IACH,oCAAmB,CAAA;AACpB,CAAC,EAnDW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAmDxB;AAED,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC9B,mEAAO,CAAA;IACP,uEAAS,CAAA;IACT,iEAAM,CAAA;AACP,CAAC,EAJW,mBAAmB,GAAnB,2BAAmB,KAAnB,2BAAmB,QAI9B;AAED,IAAY,aAIX;AAJD,WAAY,aAAa;IACxB,qDAAM,CAAA;IACN,mDAAK,CAAA;IACL,6DAAU,CAAA;AACX,CAAC,EAJW,aAAa,GAAb,qBAAa,KAAb,qBAAa,QAIxB;AAEY,QAAA,cAAc,GAAG,CAC7B,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,UAAU;IAChE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC/B,CAAC,CAAC,mBAAmB,CACZ,CAAC;AAEC,QAAA,4BAA4B,GAAG;IAC3C,MAAM,EAAE,mCAAmC;IAC3C,kBAAkB,EAAE,SAAS;IAC7B,cAAc,EAAE,iCAAiC;CACjD,CAAC;AAEF;;GAEG;AACU,QAAA,uBAAuB,GAAG,KAAK,CAAC;AAE7C;;GAEG;AACU,QAAA,iBAAiB,GAAG,KAAK,CAAC;AAEvC;;GAEG;AACU,QAAA,0BAA0B,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAExD;;GAEG;AACU,QAAA,gCAAgC,GAAG,EAAE,GAAG,IAAI,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/Providers/AWSAppSyncRealTimeProvider/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,IAAM,YAAY,GAAG,IAAI,CAAC;AAEjC,MAAM,CAAC,IAAM,mBAAmB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAEnD,MAAM,CAAN,IAAY,aAmDX;AAnDD,WAAY,aAAa;IACxB;;;OAGG;IACH,wDAAuC,CAAA;IACvC;;;OAGG;IACH,0DAAyC,CAAA;IACzC;;;OAGG;IACH,sDAAqC,CAAA;IACrC;;;OAGG;IACH,oCAAmB,CAAA;IACnB;;;OAGG;IACH,4CAA2B,CAAA;IAC3B;;;OAGG;IACH,kCAAiB,CAAA;IACjB;;;OAGG;IACH,iDAAgC,CAAA;IAChC;;;OAGG;IACH,kCAAiB,CAAA;IACjB;;;OAGG;IACH,0CAAyB,CAAA;IACzB;;;OAGG;IACH,oCAAmB,CAAA;AACpB,CAAC,EAnDW,aAAa,KAAb,aAAa,QAmDxB;AAED,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC9B,mEAAO,CAAA;IACP,uEAAS,CAAA;IACT,iEAAM,CAAA;AACP,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B;AAED,MAAM,CAAN,IAAY,aAIX;AAJD,WAAY,aAAa;IACxB,qDAAM,CAAA;IACN,mDAAK,CAAA;IACL,6DAAU,CAAA;AACX,CAAC,EAJW,aAAa,KAAb,aAAa,QAIxB;AAED,MAAM,CAAC,IAAM,cAAc,GAAG,CAC7B,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,UAAU;IAChE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC/B,CAAC,CAAC,mBAAmB,CACZ,CAAC;AAEZ,MAAM,CAAC,IAAM,4BAA4B,GAAG;IAC3C,MAAM,EAAE,mCAAmC;IAC3C,kBAAkB,EAAE,SAAS;IAC7B,cAAc,EAAE,iCAAiC;CACjD,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,IAAM,uBAAuB,GAAG,KAAK,CAAC;AAE7C;;GAEG;AACH,MAAM,CAAC,IAAM,iBAAiB,GAAG,KAAK,CAAC;AAEvC;;GAEG;AACH,MAAM,CAAC,IAAM,0BAA0B,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAExD;;GAEG;AACH,MAAM,CAAC,IAAM,gCAAgC,GAAG,EAAE,GAAG,IAAI,CAAC"}