homey-api 3.17.4 → 3.17.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/HomeyAPI/HomeyAPIV3/Item.js +38 -81
- package/lib/HomeyAPI/HomeyAPIV3/Manager.js +91 -181
- package/lib/HomeyAPI/HomeyAPIV3/RealtimeConsumer.js +140 -0
- package/lib/HomeyAPI/HomeyAPIV3/SocketSession.js +617 -0
- package/lib/HomeyAPI/HomeyAPIV3/SubscriptionRegistry.js +341 -0
- package/lib/HomeyAPI/HomeyAPIV3.js +55 -332
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const EventEmitter = require('../../EventEmitter');
|
|
4
|
+
const RealtimeConsumer = require('./RealtimeConsumer');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* A superclass for all CRUD Items.
|
|
@@ -61,6 +62,37 @@ class Item extends EventEmitter {
|
|
|
61
62
|
writable: true,
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
this.__realtimeConsumer = new RealtimeConsumer({
|
|
66
|
+
subscribe: (uri, handlers) => this.homey.subscribe(uri, handlers),
|
|
67
|
+
getUri: () => this.uri,
|
|
68
|
+
debug: (...props) => this.__debug(...props),
|
|
69
|
+
setConnected: (connected) => {
|
|
70
|
+
this.__connected = connected;
|
|
71
|
+
},
|
|
72
|
+
onConnect: () => {
|
|
73
|
+
this.onConnect();
|
|
74
|
+
},
|
|
75
|
+
onDisconnect: () => {
|
|
76
|
+
this.onDisconnect();
|
|
77
|
+
},
|
|
78
|
+
onReconnect: () => {
|
|
79
|
+
this.onReconnect();
|
|
80
|
+
},
|
|
81
|
+
onEvent: (event, data) => {
|
|
82
|
+
if (event === 'update') {
|
|
83
|
+
this.__update(this.constructor.transformGet({ ...data }));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (event === 'delete') {
|
|
88
|
+
this.__delete();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
this.emit(event, data);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
64
96
|
// Set Properties
|
|
65
97
|
for (const [key, value] of Object.entries(properties)) {
|
|
66
98
|
if (key === 'id') continue;
|
|
@@ -93,7 +125,7 @@ class Item extends EventEmitter {
|
|
|
93
125
|
this.manager.__debug(`[${this.constructor.name}:${this.id}]`, ...props);
|
|
94
126
|
}
|
|
95
127
|
|
|
96
|
-
__update(properties) {
|
|
128
|
+
__update(properties, { emitEvent = true } = {}) {
|
|
97
129
|
for (const [key, value] of Object.entries(properties)) {
|
|
98
130
|
if (key === 'id') continue;
|
|
99
131
|
this[key] = value;
|
|
@@ -101,7 +133,9 @@ class Item extends EventEmitter {
|
|
|
101
133
|
|
|
102
134
|
this.__lastUpdated = new Date();
|
|
103
135
|
|
|
104
|
-
|
|
136
|
+
if (emitEvent) {
|
|
137
|
+
this.emit('update', properties);
|
|
138
|
+
}
|
|
105
139
|
}
|
|
106
140
|
|
|
107
141
|
__delete() {
|
|
@@ -113,91 +147,14 @@ class Item extends EventEmitter {
|
|
|
113
147
|
* Connect to this item's Socket.io namespace.
|
|
114
148
|
*/
|
|
115
149
|
async connect() {
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
// If disconnecting, await that first
|
|
119
|
-
try {
|
|
120
|
-
// Ensure all microtasks are done first. E.g. if disconnect is called in the same tick as
|
|
121
|
-
// connect. This way the disconnect is always started first so we can await the disconnect
|
|
122
|
-
// promise before we try to connect again.
|
|
123
|
-
await Promise.resolve();
|
|
124
|
-
await this.__disconnectPromise;
|
|
125
|
-
// eslint-disable-next-line no-empty
|
|
126
|
-
} catch (err) { }
|
|
127
|
-
|
|
128
|
-
this.__connectPromise = Promise.resolve().then(async () => {
|
|
129
|
-
if (!this.io) {
|
|
130
|
-
this.io = this.homey.subscribe(this.uri, {
|
|
131
|
-
onConnect: () => {
|
|
132
|
-
this.__debug('onConnect');
|
|
133
|
-
this.__connected = true;
|
|
134
|
-
|
|
135
|
-
this.onConnect();
|
|
136
|
-
},
|
|
137
|
-
onDisconnect: () => {
|
|
138
|
-
this.__debug('onDisconnect');
|
|
139
|
-
this.__connected = false;
|
|
140
|
-
|
|
141
|
-
this.onDisconnect();
|
|
142
|
-
},
|
|
143
|
-
onReconnect: () => {
|
|
144
|
-
this.__debug('onDisconnect');
|
|
145
|
-
|
|
146
|
-
this.onReconnect();
|
|
147
|
-
},
|
|
148
|
-
onEvent: (event, data) => {
|
|
149
|
-
this.__debug('onEvent', event, data);
|
|
150
|
-
|
|
151
|
-
this.emit(event, data);
|
|
152
|
-
},
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await this.io;
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
// Delete the connecting Promise
|
|
160
|
-
this.__connectPromise
|
|
161
|
-
.catch(() => { })
|
|
162
|
-
.finally(() => {
|
|
163
|
-
delete this.__connectPromise;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
await this.__connectPromise;
|
|
150
|
+
await this.__realtimeConsumer.connect();
|
|
167
151
|
}
|
|
168
152
|
|
|
169
153
|
/**
|
|
170
154
|
* Discconnect from this item's Socket.io namespace.
|
|
171
155
|
*/
|
|
172
156
|
async disconnect() {
|
|
173
|
-
this.
|
|
174
|
-
|
|
175
|
-
// If connecting, await that first
|
|
176
|
-
try {
|
|
177
|
-
await this.__connectPromise;
|
|
178
|
-
// eslint-disable-next-line no-empty
|
|
179
|
-
} catch (err) { }
|
|
180
|
-
|
|
181
|
-
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
182
|
-
this.__connected = false;
|
|
183
|
-
|
|
184
|
-
if (this.io) {
|
|
185
|
-
this.io
|
|
186
|
-
.then((io) => io.unsubscribe())
|
|
187
|
-
.catch((err) => this.__debug('Error Disconnecting:', err));
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// Delete the disconnecting Promise
|
|
192
|
-
this.__disconnectPromise
|
|
193
|
-
.catch(() => { })
|
|
194
|
-
.finally(() => {
|
|
195
|
-
delete this.__disconnectPromise;
|
|
196
|
-
// Delete this.io so connect can start new connections.
|
|
197
|
-
delete this.io;
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
await this.__disconnectPromise;
|
|
157
|
+
await this.__realtimeConsumer.disconnect();
|
|
201
158
|
}
|
|
202
159
|
|
|
203
160
|
onConnect() {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const EventEmitter = require('../../EventEmitter');
|
|
4
4
|
const Util = require('../../Util');
|
|
5
|
-
const HomeyAPIError = require('../HomeyAPIError');
|
|
6
5
|
const Item = require('./Item');
|
|
6
|
+
const RealtimeConsumer = require('./RealtimeConsumer');
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @class
|
|
@@ -87,6 +87,71 @@ class Manager extends EventEmitter {
|
|
|
87
87
|
writable: false,
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
+
this.__realtimeConsumer = new RealtimeConsumer({
|
|
91
|
+
subscribe: (uri, handlers) => this.homey.subscribe(uri, handlers),
|
|
92
|
+
getUri: () => this.uri,
|
|
93
|
+
debug: (...props) => this.__debug(...props),
|
|
94
|
+
setConnected: (connected) => {
|
|
95
|
+
this.__connected = connected;
|
|
96
|
+
},
|
|
97
|
+
onEvent: (event, data) => {
|
|
98
|
+
// Transform & add to cache if this is a CRUD event
|
|
99
|
+
if (event.endsWith('.create') || event.endsWith('.update') || event.endsWith('.delete')) {
|
|
100
|
+
const [itemId, operation] = event.split('.');
|
|
101
|
+
const itemName = this.itemNames[itemId];
|
|
102
|
+
const ItemClass = this.itemClasses[itemName];
|
|
103
|
+
|
|
104
|
+
switch (operation) {
|
|
105
|
+
case 'create': {
|
|
106
|
+
const props = ItemClass.transformGet(data);
|
|
107
|
+
|
|
108
|
+
const item = new ItemClass({
|
|
109
|
+
id: props.id,
|
|
110
|
+
homey: this.homey,
|
|
111
|
+
manager: this,
|
|
112
|
+
properties: props,
|
|
113
|
+
});
|
|
114
|
+
this.__cache[ItemClass.ID][props.id] = item;
|
|
115
|
+
|
|
116
|
+
this.emit(event, item);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
case 'update': {
|
|
120
|
+
const props = ItemClass.transformGet(data);
|
|
121
|
+
|
|
122
|
+
if (this.__cache[ItemClass.ID][props.id]) {
|
|
123
|
+
const item = this.__cache[ItemClass.ID][props.id];
|
|
124
|
+
item.__update(props);
|
|
125
|
+
this.emit(event, item);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'delete': {
|
|
132
|
+
const props = ItemClass.transformGet(data);
|
|
133
|
+
|
|
134
|
+
if (this.__cache[ItemClass.ID][props.id]) {
|
|
135
|
+
const item = this.__cache[ItemClass.ID][props.id];
|
|
136
|
+
item.__delete();
|
|
137
|
+
delete this.__cache[ItemClass.ID][item.id];
|
|
138
|
+
this.emit(event, {
|
|
139
|
+
id: item.id,
|
|
140
|
+
});
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
default:
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.emit(event, data);
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
90
155
|
// Create methods
|
|
91
156
|
for (const [operationId, operation] of Object.entries(operations)) {
|
|
92
157
|
Object.defineProperty(
|
|
@@ -244,6 +309,7 @@ class Manager extends EventEmitter {
|
|
|
244
309
|
|
|
245
310
|
async __request({ $cache, $updateCache, $timeout, $socket, operationId, operation, path, body, headers, shouldRetry, ...args }) {
|
|
246
311
|
let result;
|
|
312
|
+
let hasSocketResult = false;
|
|
247
313
|
const benchmark = Util.benchmark();
|
|
248
314
|
|
|
249
315
|
// If connected to Socket.io,
|
|
@@ -273,47 +339,24 @@ class Manager extends EventEmitter {
|
|
|
273
339
|
// If Homey is connected to Socket.io,
|
|
274
340
|
// send the API request to socket.io.
|
|
275
341
|
// This is about ~2x faster than HTTP
|
|
276
|
-
if (this.homey.isConnected()
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
this.
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
stack: err.stack,
|
|
293
|
-
error: err.error,
|
|
294
|
-
error_description: err.error_description,
|
|
295
|
-
},
|
|
296
|
-
err.statusCode || err.code || 500
|
|
297
|
-
);
|
|
298
|
-
} else if (typeof err === 'string') {
|
|
299
|
-
err = new HomeyAPIError(
|
|
300
|
-
{
|
|
301
|
-
error: err,
|
|
302
|
-
},
|
|
303
|
-
500
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return reject(err);
|
|
308
|
-
}
|
|
342
|
+
if ($socket === true && this.homey.isConnected()) {
|
|
343
|
+
try {
|
|
344
|
+
this.__debug(`IO ${operationId}`);
|
|
345
|
+
result = await this.homey.__apiRequest({
|
|
346
|
+
uri: this.uri,
|
|
347
|
+
operation: operationId,
|
|
348
|
+
args,
|
|
349
|
+
timeout: $timeout,
|
|
350
|
+
});
|
|
351
|
+
hasSocketResult = true;
|
|
352
|
+
} catch (err) {
|
|
353
|
+
if (err.code !== 'ERR_SOCKET_SESSION_NOT_READY') {
|
|
354
|
+
throw err;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
309
358
|
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
);
|
|
313
|
-
}),
|
|
314
|
-
$timeout
|
|
315
|
-
);
|
|
316
|
-
} else {
|
|
359
|
+
if (!hasSocketResult) {
|
|
317
360
|
// Get from HTTP
|
|
318
361
|
result = await this.homey.call({
|
|
319
362
|
$timeout,
|
|
@@ -396,7 +439,11 @@ class Manager extends EventEmitter {
|
|
|
396
439
|
|
|
397
440
|
if (this.isConnected() && $updateCache === true && this.__cache[ItemClass.ID][props.id]) {
|
|
398
441
|
item = this.__cache[ItemClass.ID][props.id];
|
|
399
|
-
item.__update(props
|
|
442
|
+
item.__update(props, {
|
|
443
|
+
// Local mutation results update the cached item immediately, but the
|
|
444
|
+
// realtime manager event is responsible for the public update event.
|
|
445
|
+
emitEvent: false,
|
|
446
|
+
});
|
|
400
447
|
} else {
|
|
401
448
|
item = new ItemClass({
|
|
402
449
|
id: props.id,
|
|
@@ -462,119 +509,7 @@ class Manager extends EventEmitter {
|
|
|
462
509
|
* @returns {Promise<void>}
|
|
463
510
|
*/
|
|
464
511
|
async connect() {
|
|
465
|
-
this.
|
|
466
|
-
|
|
467
|
-
// If disconnecting, await that first
|
|
468
|
-
try {
|
|
469
|
-
await this.__disconnectPromise;
|
|
470
|
-
// eslint-disable-next-line no-empty
|
|
471
|
-
} catch (err) { }
|
|
472
|
-
|
|
473
|
-
if (this.__connectPromise) {
|
|
474
|
-
await this.__connectPromise;
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
if (this.io) {
|
|
479
|
-
await this.io;
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
this.__connectPromise = Promise.resolve().then(async () => {
|
|
484
|
-
if (!this.io) {
|
|
485
|
-
this.io = this.homey.subscribe(this.uri, {
|
|
486
|
-
onConnect: () => {
|
|
487
|
-
this.__debug('onConnect');
|
|
488
|
-
this.__connected = true;
|
|
489
|
-
},
|
|
490
|
-
onDisconnect: reason => {
|
|
491
|
-
this.__debug(`onDisconnect Reason:${reason}`);
|
|
492
|
-
this.__connected = false;
|
|
493
|
-
|
|
494
|
-
// Disable for now. We should probably only set the cache to invalid.
|
|
495
|
-
|
|
496
|
-
// Clear CRUD Item cache
|
|
497
|
-
// for (const itemId of Object.keys(this.__cache)) {
|
|
498
|
-
// this.__cache[itemId] = {};
|
|
499
|
-
// this.__cacheAllComplete[itemId] = false;
|
|
500
|
-
// }
|
|
501
|
-
},
|
|
502
|
-
onReconnect: () => {
|
|
503
|
-
this.__debug(`onReconnect`);
|
|
504
|
-
this.__connected = true;
|
|
505
|
-
},
|
|
506
|
-
onEvent: (event, data) => {
|
|
507
|
-
this.__debug('onEvent', event);
|
|
508
|
-
|
|
509
|
-
// Transform & add to cache if this is a CRUD event
|
|
510
|
-
if (event.endsWith('.create') || event.endsWith('.update') || event.endsWith('.delete')) {
|
|
511
|
-
const [itemId, operation] = event.split('.');
|
|
512
|
-
const itemName = this.itemNames[itemId];
|
|
513
|
-
const ItemClass = this.itemClasses[itemName];
|
|
514
|
-
|
|
515
|
-
switch (operation) {
|
|
516
|
-
case 'create': {
|
|
517
|
-
const props = ItemClass.transformGet(data);
|
|
518
|
-
|
|
519
|
-
const item = new ItemClass({
|
|
520
|
-
id: props.id,
|
|
521
|
-
homey: this.homey,
|
|
522
|
-
manager: this,
|
|
523
|
-
properties: props,
|
|
524
|
-
});
|
|
525
|
-
this.__cache[ItemClass.ID][props.id] = item;
|
|
526
|
-
|
|
527
|
-
return this.emit(event, item);
|
|
528
|
-
}
|
|
529
|
-
case 'update': {
|
|
530
|
-
const props = ItemClass.transformGet(data);
|
|
531
|
-
|
|
532
|
-
if (this.__cache[ItemClass.ID][props.id]) {
|
|
533
|
-
const item = this.__cache[ItemClass.ID][props.id];
|
|
534
|
-
item.__update(props);
|
|
535
|
-
return this.emit(event, item);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
break;
|
|
539
|
-
}
|
|
540
|
-
case 'delete': {
|
|
541
|
-
const props = ItemClass.transformGet(data);
|
|
542
|
-
|
|
543
|
-
if (this.__cache[ItemClass.ID][props.id]) {
|
|
544
|
-
const item = this.__cache[ItemClass.ID][props.id];
|
|
545
|
-
item.__delete();
|
|
546
|
-
delete this.__cache[ItemClass.ID][item.id];
|
|
547
|
-
return this.emit(event, {
|
|
548
|
-
id: item.id,
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
break;
|
|
553
|
-
}
|
|
554
|
-
default:
|
|
555
|
-
break;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// Fire event listeners
|
|
560
|
-
this.emit(event, data);
|
|
561
|
-
},
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
await this.io;
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
// Delete the connecting Promise
|
|
569
|
-
this.__connectPromise
|
|
570
|
-
.catch(() => {
|
|
571
|
-
delete this.io;
|
|
572
|
-
})
|
|
573
|
-
.finally(() => {
|
|
574
|
-
delete this.__connectPromise;
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
await this.__connectPromise;
|
|
512
|
+
await this.__realtimeConsumer.connect();
|
|
578
513
|
}
|
|
579
514
|
|
|
580
515
|
/**
|
|
@@ -582,32 +517,7 @@ class Manager extends EventEmitter {
|
|
|
582
517
|
* @returns {Promise<void>}
|
|
583
518
|
*/
|
|
584
519
|
async disconnect() {
|
|
585
|
-
this.
|
|
586
|
-
|
|
587
|
-
// If connecting, await that first
|
|
588
|
-
try {
|
|
589
|
-
await this.__connectPromise;
|
|
590
|
-
// eslint-disable-next-line no-empty
|
|
591
|
-
} catch (err) { }
|
|
592
|
-
|
|
593
|
-
this.__disconnectPromise = Promise.resolve().then(async () => {
|
|
594
|
-
this.__connected = false;
|
|
595
|
-
|
|
596
|
-
if (this.io) {
|
|
597
|
-
await this.io.then(io => io.unsubscribe()).catch(err => this.__debug('Error Disconnecting:', err));
|
|
598
|
-
|
|
599
|
-
delete this.io;
|
|
600
|
-
}
|
|
601
|
-
});
|
|
602
|
-
|
|
603
|
-
// Delete the disconnecting Promise
|
|
604
|
-
this.__disconnectPromise
|
|
605
|
-
.catch(() => { })
|
|
606
|
-
.finally(() => {
|
|
607
|
-
delete this.__disconnectPromise;
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
await this.__disconnectPromise;
|
|
520
|
+
await this.__realtimeConsumer.disconnect();
|
|
611
521
|
}
|
|
612
522
|
|
|
613
523
|
/**
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class RealtimeConsumer {
|
|
4
|
+
constructor({
|
|
5
|
+
subscribe,
|
|
6
|
+
getUri,
|
|
7
|
+
debug,
|
|
8
|
+
onConnect = () => {},
|
|
9
|
+
onDisconnect = () => {},
|
|
10
|
+
onReconnect = () => {},
|
|
11
|
+
onReconnectError = () => {},
|
|
12
|
+
onEvent = () => {},
|
|
13
|
+
setConnected = () => {},
|
|
14
|
+
}) {
|
|
15
|
+
this.__subscribe = subscribe;
|
|
16
|
+
this.__getUri = getUri;
|
|
17
|
+
this.__debug = debug;
|
|
18
|
+
this.__onConnect = onConnect;
|
|
19
|
+
this.__onDisconnect = onDisconnect;
|
|
20
|
+
this.__onReconnect = onReconnect;
|
|
21
|
+
this.__onReconnectError = onReconnectError;
|
|
22
|
+
this.__onEvent = onEvent;
|
|
23
|
+
this.__setConnected = setConnected;
|
|
24
|
+
|
|
25
|
+
this.__subscriptionPromise = null;
|
|
26
|
+
this.__connectPromise = null;
|
|
27
|
+
this.__disconnectPromise = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async connect() {
|
|
31
|
+
this.__debug('connect');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
await Promise.resolve();
|
|
35
|
+
await this.__disconnectPromise;
|
|
36
|
+
// eslint-disable-next-line no-empty
|
|
37
|
+
} catch (err) {}
|
|
38
|
+
|
|
39
|
+
if (this.__connectPromise) {
|
|
40
|
+
await this.__connectPromise;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (this.__subscriptionPromise) {
|
|
45
|
+
await this.__subscriptionPromise;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const connectPromise = Promise.resolve().then(async () => {
|
|
50
|
+
if (!this.__subscriptionPromise) {
|
|
51
|
+
this.__subscriptionPromise = Promise.resolve(
|
|
52
|
+
this.__subscribe(this.__getUri(), {
|
|
53
|
+
onConnect: () => {
|
|
54
|
+
this.__debug('onConnect');
|
|
55
|
+
this.__setConnected(true);
|
|
56
|
+
this.__onConnect();
|
|
57
|
+
},
|
|
58
|
+
onDisconnect: (reason) => {
|
|
59
|
+
this.__debug(`onDisconnect Reason:${reason}`);
|
|
60
|
+
this.__setConnected(false);
|
|
61
|
+
this.__onDisconnect(reason);
|
|
62
|
+
},
|
|
63
|
+
onReconnect: () => {
|
|
64
|
+
this.__debug('onReconnect');
|
|
65
|
+
this.__setConnected(true);
|
|
66
|
+
this.__onReconnect();
|
|
67
|
+
},
|
|
68
|
+
onReconnectError: (err) => {
|
|
69
|
+
this.__debug('onReconnectError', err.message);
|
|
70
|
+
this.__onReconnectError(err);
|
|
71
|
+
},
|
|
72
|
+
onEvent: (event, data) => {
|
|
73
|
+
this.__debug('onEvent', event, data);
|
|
74
|
+
this.__onEvent(event, data);
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await this.__subscriptionPromise;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
this.__connectPromise = connectPromise;
|
|
84
|
+
|
|
85
|
+
connectPromise
|
|
86
|
+
.catch(() => {
|
|
87
|
+
this.__subscriptionPromise = null;
|
|
88
|
+
})
|
|
89
|
+
.finally(() => {
|
|
90
|
+
if (this.__connectPromise === connectPromise) {
|
|
91
|
+
this.__connectPromise = null;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await connectPromise;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async disconnect() {
|
|
99
|
+
this.__debug('disconnect');
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
await this.__connectPromise;
|
|
103
|
+
// eslint-disable-next-line no-empty
|
|
104
|
+
} catch (err) {}
|
|
105
|
+
|
|
106
|
+
if (this.__disconnectPromise) {
|
|
107
|
+
await this.__disconnectPromise;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const disconnectPromise = Promise.resolve().then(async () => {
|
|
112
|
+
this.__setConnected(false);
|
|
113
|
+
|
|
114
|
+
// A failed connect clears __subscriptionPromise in connect(). In that case
|
|
115
|
+
// disconnect() becomes a safe no-op because there is no subscription handle
|
|
116
|
+
// left to unsubscribe.
|
|
117
|
+
if (this.__subscriptionPromise) {
|
|
118
|
+
await this.__subscriptionPromise
|
|
119
|
+
.then((subscription) => subscription.unsubscribe())
|
|
120
|
+
.catch((err) => this.__debug('Error Disconnecting:', err));
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
this.__disconnectPromise = disconnectPromise;
|
|
125
|
+
|
|
126
|
+
disconnectPromise
|
|
127
|
+
.catch(() => {})
|
|
128
|
+
.finally(() => {
|
|
129
|
+
if (this.__disconnectPromise === disconnectPromise) {
|
|
130
|
+
this.__disconnectPromise = null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.__subscriptionPromise = null;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await disconnectPromise;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = RealtimeConsumer;
|