@nymphjs/client 1.0.0-beta.12 → 1.0.0-beta.120
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/CHANGELOG.md +496 -0
- package/README.md +6 -6
- package/asyncitertest.js +53 -0
- package/dist/Entity.d.ts +156 -0
- package/{lib → dist}/Entity.js +202 -104
- package/dist/Entity.js.map +1 -0
- package/dist/Entity.types.d.ts +218 -0
- package/dist/Entity.types.js +2 -0
- package/{lib → dist}/EntityWeakCache.d.ts +1 -1
- package/{lib → dist}/EntityWeakCache.js +5 -9
- package/dist/EntityWeakCache.js.map +1 -0
- package/{lib → dist}/HttpRequester.d.ts +18 -4
- package/dist/HttpRequester.js +365 -0
- package/dist/HttpRequester.js.map +1 -0
- package/{lib → dist}/Nymph.d.ts +48 -11
- package/{lib → dist}/Nymph.js +167 -51
- package/dist/Nymph.js.map +1 -0
- package/dist/Nymph.types.d.ts +227 -0
- package/dist/Nymph.types.js +45 -0
- package/dist/Nymph.types.js.map +1 -0
- package/{lib → dist}/PubSub.d.ts +16 -10
- package/{lib → dist}/PubSub.js +208 -135
- package/dist/PubSub.js.map +1 -0
- package/{lib → dist}/PubSub.types.d.ts +19 -6
- package/dist/PubSub.types.js +2 -0
- package/{lib → dist}/entityRefresh.d.ts +1 -1
- package/{lib → dist}/entityRefresh.js +21 -13
- package/dist/entityRefresh.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/{lib → dist}/utils.d.ts +1 -1
- package/{lib → dist}/utils.js +28 -21
- package/dist/utils.js.map +1 -0
- package/jest.config.js +31 -5
- package/package.json +31 -30
- package/src/Entity.ts +174 -108
- package/src/Entity.types.ts +29 -47
- package/src/EntityWeakCache.ts +8 -6
- package/src/HttpRequester.ts +268 -31
- package/src/Nymph.ts +188 -88
- package/src/Nymph.types.ts +102 -2
- package/src/PubSub.ts +262 -179
- package/src/PubSub.types.ts +33 -19
- package/src/entityRefresh.ts +6 -6
- package/src/index.ts +10 -10
- package/src/utils.ts +12 -5
- package/tsconfig.json +7 -4
- package/typedoc.json +4 -0
- package/dist/index.js.LICENSE.txt +0 -8
- package/lib/Entity.d.ts +0 -51
- package/lib/Entity.js.map +0 -1
- package/lib/Entity.types.d.ts +0 -65
- package/lib/Entity.types.js +0 -3
- package/lib/EntityWeakCache.js.map +0 -1
- package/lib/HttpRequester.js +0 -190
- package/lib/HttpRequester.js.map +0 -1
- package/lib/Nymph.js.map +0 -1
- package/lib/Nymph.types.d.ts +0 -97
- package/lib/Nymph.types.js +0 -3
- package/lib/Nymph.types.js.map +0 -1
- package/lib/PubSub.js.map +0 -1
- package/lib/PubSub.types.js +0 -3
- package/lib/entityRefresh.js.map +0 -1
- package/lib/index.d.ts +0 -13
- package/lib/index.js +0 -34
- package/lib/index.js.map +0 -1
- package/lib/utils.js.map +0 -1
- package/webpack.config.js +0 -28
- /package/{lib → dist}/Entity.types.js.map +0 -0
- /package/{lib → dist}/PubSub.types.js.map +0 -0
package/src/PubSub.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import Nymph, { InvalidRequestError } from './Nymph';
|
|
2
|
-
import { NymphOptions, Options, Selector } from './Nymph.types';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import Nymph, { InvalidRequestError } from './Nymph.js';
|
|
2
|
+
import type { NymphOptions, Options, Selector } from './Nymph.types.js';
|
|
3
|
+
import type { EntityInstanceType } from './Entity.js';
|
|
4
|
+
import type {
|
|
5
|
+
EntityConstructor,
|
|
6
|
+
EntityInterface,
|
|
7
|
+
EntityJson,
|
|
8
|
+
} from './Entity.types.js';
|
|
9
|
+
import type {
|
|
5
10
|
PubSubCallbacks,
|
|
6
11
|
PubSubConnectCallback,
|
|
7
12
|
PubSubCountCallback,
|
|
@@ -9,17 +14,19 @@ import {
|
|
|
9
14
|
PubSubEventType,
|
|
10
15
|
PubSubRejectCallback,
|
|
11
16
|
PubSubResolveCallback,
|
|
17
|
+
PubSubErrorCallback,
|
|
12
18
|
PubSubSubscribable,
|
|
13
19
|
PubSubUpdate,
|
|
14
|
-
} from './PubSub.types';
|
|
15
|
-
import { entityConstructorsToClassNames } from './utils';
|
|
16
|
-
|
|
17
|
-
let authToken: string | null = null;
|
|
20
|
+
} from './PubSub.types.js';
|
|
21
|
+
import { entityConstructorsToClassNames } from './utils.js';
|
|
22
|
+
import { ClientError } from './HttpRequester.js';
|
|
18
23
|
|
|
19
24
|
export default class PubSub {
|
|
20
25
|
private nymph: Nymph;
|
|
26
|
+
private authToken: string | null = null;
|
|
27
|
+
private switchToken: string | null = null;
|
|
21
28
|
private connection: WebSocket | undefined;
|
|
22
|
-
private waitForConnectionTimeout:
|
|
29
|
+
private waitForConnectionTimeout: number | undefined;
|
|
23
30
|
private pubsubUrl: string | undefined;
|
|
24
31
|
private WebSocket: typeof WebSocket;
|
|
25
32
|
private subscriptions: {
|
|
@@ -35,6 +42,7 @@ export default class PubSub {
|
|
|
35
42
|
};
|
|
36
43
|
private connectCallbacks: PubSubConnectCallback[] = [];
|
|
37
44
|
private disconnectCallbacks: PubSubDisconnectCallback[] = [];
|
|
45
|
+
private errorCallbacks: PubSubErrorCallback[] = [];
|
|
38
46
|
private noConsole = false;
|
|
39
47
|
|
|
40
48
|
public constructor(nymphOptions: NymphOptions, nymph: Nymph) {
|
|
@@ -51,11 +59,16 @@ export default class PubSub {
|
|
|
51
59
|
if (typeof addEventListener !== 'undefined') {
|
|
52
60
|
addEventListener('online', () => this.connect());
|
|
53
61
|
}
|
|
54
|
-
if (
|
|
62
|
+
if (
|
|
63
|
+
!nymphOptions.noAutoconnect &&
|
|
64
|
+
(typeof navigator === 'undefined' || navigator.onLine)
|
|
65
|
+
) {
|
|
55
66
|
this.connect();
|
|
56
67
|
}
|
|
57
68
|
}
|
|
58
69
|
|
|
70
|
+
// TODO: return: 'guid' doesn't work here because the server always returns
|
|
71
|
+
// entities.
|
|
59
72
|
public subscribeEntities<T extends EntityConstructor = EntityConstructor>(
|
|
60
73
|
options: Options<T> & { return: 'guid' },
|
|
61
74
|
...selectors: Selector[]
|
|
@@ -63,14 +76,13 @@ export default class PubSub {
|
|
|
63
76
|
public subscribeEntities<T extends EntityConstructor = EntityConstructor>(
|
|
64
77
|
options: Options<T>,
|
|
65
78
|
...selectors: Selector[]
|
|
66
|
-
): PubSubSubscribable<PubSubUpdate<
|
|
79
|
+
): PubSubSubscribable<PubSubUpdate<EntityInstanceType<T>[]>>;
|
|
67
80
|
public subscribeEntities<T extends EntityConstructor = EntityConstructor>(
|
|
68
81
|
options: Options<T>,
|
|
69
82
|
...selectors: Selector[]
|
|
70
|
-
):
|
|
71
|
-
PubSubUpdate<
|
|
72
|
-
|
|
73
|
-
const promise = this.nymph.getEntities(options, ...selectors);
|
|
83
|
+
):
|
|
84
|
+
| PubSubSubscribable<PubSubUpdate<EntityInstanceType<T>[]>>
|
|
85
|
+
| PubSubSubscribable<PubSubUpdate<string[]>> {
|
|
74
86
|
const query = [
|
|
75
87
|
entityConstructorsToClassNames(options),
|
|
76
88
|
...entityConstructorsToClassNames(selectors),
|
|
@@ -79,18 +91,20 @@ export default class PubSub {
|
|
|
79
91
|
const subscribe = (
|
|
80
92
|
resolve?:
|
|
81
93
|
| PubSubResolveCallback<
|
|
82
|
-
|
|
83
|
-
| PubSubUpdate<string[]>
|
|
94
|
+
PubSubUpdate<EntityInstanceType<T>[]> | PubSubUpdate<string[]>
|
|
84
95
|
>
|
|
85
96
|
| undefined,
|
|
86
97
|
reject?: PubSubRejectCallback | undefined,
|
|
87
|
-
count?: PubSubCountCallback | undefined
|
|
98
|
+
count?: PubSubCountCallback | undefined,
|
|
88
99
|
) => {
|
|
89
100
|
const callbacks: PubSubCallbacks<
|
|
90
|
-
PubSubUpdate<
|
|
101
|
+
PubSubUpdate<EntityInstanceType<T>[]> | PubSubUpdate<string[]>
|
|
91
102
|
> = [resolve, reject, count];
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
if (!this.isConnection()) {
|
|
105
|
+
// Fall back to a regular query if we're not connected.
|
|
106
|
+
this.nymph.getEntities(options, ...selectors).then(resolve, reject);
|
|
107
|
+
}
|
|
94
108
|
|
|
95
109
|
this._subscribeQuery(jsonQuery, callbacks);
|
|
96
110
|
return new PubSubSubscription(jsonQuery, callbacks, () => {
|
|
@@ -100,6 +114,8 @@ export default class PubSub {
|
|
|
100
114
|
return subscribe;
|
|
101
115
|
}
|
|
102
116
|
|
|
117
|
+
// TODO: return: 'guid' doesn't work here because the server always returns
|
|
118
|
+
// entities.
|
|
103
119
|
public subscribeEntity<T extends EntityConstructor = EntityConstructor>(
|
|
104
120
|
options: Options<T> & { return: 'guid' },
|
|
105
121
|
...selectors: Selector[]
|
|
@@ -107,15 +123,13 @@ export default class PubSub {
|
|
|
107
123
|
public subscribeEntity<T extends EntityConstructor = EntityConstructor>(
|
|
108
124
|
options: Options<T>,
|
|
109
125
|
...selectors: Selector[]
|
|
110
|
-
): PubSubSubscribable<PubSubUpdate<
|
|
126
|
+
): PubSubSubscribable<PubSubUpdate<EntityInstanceType<T> | null>>;
|
|
111
127
|
public subscribeEntity<T extends EntityConstructor = EntityConstructor>(
|
|
112
128
|
options: Options<T>,
|
|
113
129
|
...selectors: Selector[]
|
|
114
|
-
):
|
|
115
|
-
| PubSubUpdate<
|
|
116
|
-
| PubSubUpdate<string | null
|
|
117
|
-
> {
|
|
118
|
-
const promise = this.nymph.getEntity(options, ...selectors);
|
|
130
|
+
):
|
|
131
|
+
| PubSubSubscribable<PubSubUpdate<EntityInstanceType<T> | null>>
|
|
132
|
+
| PubSubSubscribable<PubSubUpdate<string | null>> {
|
|
119
133
|
const query = [
|
|
120
134
|
{ ...entityConstructorsToClassNames(options), limit: 1 },
|
|
121
135
|
...entityConstructorsToClassNames(selectors),
|
|
@@ -124,12 +138,12 @@ export default class PubSub {
|
|
|
124
138
|
const subscribe = (
|
|
125
139
|
resolve?:
|
|
126
140
|
| PubSubResolveCallback<
|
|
127
|
-
| PubSubUpdate<
|
|
141
|
+
| PubSubUpdate<EntityInstanceType<T> | null>
|
|
128
142
|
| PubSubUpdate<string | null>
|
|
129
143
|
>
|
|
130
144
|
| undefined,
|
|
131
145
|
reject?: PubSubRejectCallback | undefined,
|
|
132
|
-
count?: PubSubCountCallback | undefined
|
|
146
|
+
count?: PubSubCountCallback | undefined,
|
|
133
147
|
) => {
|
|
134
148
|
const newResolve = (args: any) => {
|
|
135
149
|
if (!args.length) {
|
|
@@ -143,11 +157,13 @@ export default class PubSub {
|
|
|
143
157
|
}
|
|
144
158
|
};
|
|
145
159
|
const callbacks: PubSubCallbacks<
|
|
146
|
-
|
|
147
|
-
| PubSubUpdate<string | null>
|
|
160
|
+
PubSubUpdate<EntityInstanceType<T> | null> | PubSubUpdate<string | null>
|
|
148
161
|
> = [newResolve, reject, count];
|
|
149
162
|
|
|
150
|
-
|
|
163
|
+
if (!this.isConnection()) {
|
|
164
|
+
// Fall back to a regular query if we're not connected.
|
|
165
|
+
this.nymph.getEntity(options, ...selectors).then(resolve, reject);
|
|
166
|
+
}
|
|
151
167
|
|
|
152
168
|
this._subscribeQuery(jsonQuery, callbacks);
|
|
153
169
|
return new PubSubSubscription(jsonQuery, callbacks, () => {
|
|
@@ -158,15 +174,17 @@ export default class PubSub {
|
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
public subscribeUID(name: string) {
|
|
161
|
-
const promise = this.nymph.getUID(name);
|
|
162
177
|
const subscribe = (
|
|
163
178
|
resolve?: PubSubResolveCallback<number> | undefined,
|
|
164
179
|
reject?: PubSubRejectCallback | undefined,
|
|
165
|
-
count?: PubSubCountCallback | undefined
|
|
180
|
+
count?: PubSubCountCallback | undefined,
|
|
166
181
|
) => {
|
|
167
182
|
const callbacks: PubSubCallbacks<number> = [resolve, reject, count];
|
|
168
183
|
|
|
169
|
-
|
|
184
|
+
if (!this.isConnection()) {
|
|
185
|
+
// Fall back to a regular query if we're not connected.
|
|
186
|
+
this.nymph.getUID(name).then(resolve, reject);
|
|
187
|
+
}
|
|
170
188
|
|
|
171
189
|
this._subscribeUID(name, callbacks);
|
|
172
190
|
return {
|
|
@@ -182,11 +200,11 @@ export default class PubSub {
|
|
|
182
200
|
entity: T,
|
|
183
201
|
resolve?: PubSubResolveCallback<T> | undefined,
|
|
184
202
|
reject?: PubSubRejectCallback | undefined,
|
|
185
|
-
count?: PubSubCountCallback | undefined
|
|
203
|
+
count?: PubSubCountCallback | undefined,
|
|
186
204
|
) {
|
|
187
205
|
if (!entity.guid) {
|
|
188
206
|
throw new InvalidRequestError(
|
|
189
|
-
"You can't subscribe to an entity with no GUID."
|
|
207
|
+
"You can't subscribe to an entity with no GUID.",
|
|
190
208
|
);
|
|
191
209
|
}
|
|
192
210
|
const query = [
|
|
@@ -197,15 +215,21 @@ export default class PubSub {
|
|
|
197
215
|
|
|
198
216
|
const newResolve = (args: any) => {
|
|
199
217
|
if (Array.isArray(args)) {
|
|
200
|
-
if (args
|
|
201
|
-
entity.$init(args[0]);
|
|
218
|
+
if (args.length) {
|
|
219
|
+
entity.$init(args[0].toJSON());
|
|
202
220
|
} else {
|
|
203
221
|
entity.guid = null;
|
|
204
222
|
}
|
|
205
|
-
} else if ('removed' in args) {
|
|
206
|
-
entity.guid = null;
|
|
207
223
|
} else {
|
|
208
|
-
|
|
224
|
+
let update = args;
|
|
225
|
+
if ('multiple' in args && args.multiple) {
|
|
226
|
+
update = args.messages.pop();
|
|
227
|
+
}
|
|
228
|
+
if ('removed' in update) {
|
|
229
|
+
entity.guid = null;
|
|
230
|
+
} else {
|
|
231
|
+
entity.$init(update.data);
|
|
232
|
+
}
|
|
209
233
|
}
|
|
210
234
|
|
|
211
235
|
if (resolve) {
|
|
@@ -242,31 +266,31 @@ export default class PubSub {
|
|
|
242
266
|
return;
|
|
243
267
|
}
|
|
244
268
|
|
|
245
|
-
this.connection.close(
|
|
269
|
+
this.connection.close(1000, 'Closure requested by application.');
|
|
246
270
|
}
|
|
247
271
|
|
|
248
|
-
private _waitForConnection(attempts =
|
|
249
|
-
// Wait 5 seconds, then check and attempt connection again if
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
this.connection
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
private _waitForConnection(attempts = 1) {
|
|
273
|
+
// Wait 5 seconds, then check and attempt connection again if unsuccessful.
|
|
274
|
+
// Keep repeating, adding attempts^2*5 seconds each time to a max of ten
|
|
275
|
+
// minutes, until successful.
|
|
276
|
+
this.waitForConnectionTimeout = setTimeout(
|
|
277
|
+
() => {
|
|
278
|
+
if (this.connection) {
|
|
279
|
+
if (this.connection.readyState !== this.WebSocket.OPEN) {
|
|
280
|
+
if (this.connection.readyState !== this.WebSocket.CONNECTING) {
|
|
281
|
+
this.connection.close();
|
|
282
|
+
this._waitForConnection(attempts + 1);
|
|
283
|
+
this._attemptConnect();
|
|
284
|
+
} else {
|
|
285
|
+
this._waitForConnection(attempts + 1);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
263
288
|
} else {
|
|
264
|
-
this.
|
|
289
|
+
this._attemptConnect();
|
|
265
290
|
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}, 5000);
|
|
291
|
+
},
|
|
292
|
+
Math.max(Math.pow(attempts, 2) * 5000, 1000 * 60 * 10),
|
|
293
|
+
);
|
|
270
294
|
}
|
|
271
295
|
|
|
272
296
|
private _attemptConnect() {
|
|
@@ -282,6 +306,11 @@ export default class PubSub {
|
|
|
282
306
|
if (typeof console !== 'undefined' && !this.noConsole) {
|
|
283
307
|
console.log('Nymph-PubSub connection established!');
|
|
284
308
|
}
|
|
309
|
+
|
|
310
|
+
if (this.waitForConnectionTimeout) {
|
|
311
|
+
clearTimeout(this.waitForConnectionTimeout);
|
|
312
|
+
}
|
|
313
|
+
|
|
285
314
|
for (let i = 0; i < this.connectCallbacks.length; i++) {
|
|
286
315
|
const callback = this.connectCallbacks[i];
|
|
287
316
|
if (callback) {
|
|
@@ -289,10 +318,11 @@ export default class PubSub {
|
|
|
289
318
|
}
|
|
290
319
|
}
|
|
291
320
|
|
|
292
|
-
if (authToken != null) {
|
|
321
|
+
if (this.authToken != null) {
|
|
293
322
|
this._send({
|
|
294
323
|
action: 'authenticate',
|
|
295
|
-
|
|
324
|
+
authToken: this.authToken,
|
|
325
|
+
switchToken: this.switchToken,
|
|
296
326
|
});
|
|
297
327
|
}
|
|
298
328
|
|
|
@@ -340,17 +370,23 @@ export default class PubSub {
|
|
|
340
370
|
private _onmessage(e: WebSocketEventMap['message']) {
|
|
341
371
|
let data = JSON.parse(e.data);
|
|
342
372
|
let subs: PubSubCallbacks<any>[] = [];
|
|
373
|
+
let set = 'set' in data && data.set;
|
|
343
374
|
let count = 'count' in data;
|
|
375
|
+
let error = 'error' in data;
|
|
344
376
|
if (
|
|
345
377
|
data.hasOwnProperty('query') &&
|
|
346
378
|
this.subscriptions.queries.hasOwnProperty(data.query)
|
|
347
379
|
) {
|
|
348
380
|
subs = [...this.subscriptions.queries[data.query]];
|
|
349
|
-
if (!count) {
|
|
381
|
+
if (!count && !error) {
|
|
350
382
|
for (let i = 0; i < subs.length; i++) {
|
|
351
383
|
const callback = subs[i][0];
|
|
352
384
|
if (typeof callback === 'function') {
|
|
353
|
-
callback(
|
|
385
|
+
callback(
|
|
386
|
+
set
|
|
387
|
+
? data.data.map((e: EntityJson) => this.nymph.initEntity(e))
|
|
388
|
+
: data,
|
|
389
|
+
);
|
|
354
390
|
}
|
|
355
391
|
}
|
|
356
392
|
}
|
|
@@ -359,14 +395,36 @@ export default class PubSub {
|
|
|
359
395
|
this.subscriptions.uids.hasOwnProperty(data.uid)
|
|
360
396
|
) {
|
|
361
397
|
subs = [...this.subscriptions.uids[data.uid]];
|
|
362
|
-
if (!count) {
|
|
398
|
+
if (!count && !error) {
|
|
363
399
|
for (let i = 0; i < subs.length; i++) {
|
|
364
400
|
const callback = subs[i][0];
|
|
365
|
-
|
|
366
|
-
|
|
401
|
+
const errCallback = subs[i][1];
|
|
402
|
+
if (set && data.data == null) {
|
|
403
|
+
if (typeof errCallback === 'function') {
|
|
404
|
+
errCallback(
|
|
405
|
+
new ClientError(
|
|
406
|
+
{ status: 404, statusText: 'Not Found' } as Response,
|
|
407
|
+
{ textStatus: 'Not Found' },
|
|
408
|
+
),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
} else if (typeof callback === 'function') {
|
|
412
|
+
if (set) {
|
|
413
|
+
callback(data.data);
|
|
414
|
+
} else {
|
|
415
|
+
callback(data.value ?? null, data.event);
|
|
416
|
+
}
|
|
367
417
|
}
|
|
368
418
|
}
|
|
369
419
|
}
|
|
420
|
+
} else if (error) {
|
|
421
|
+
for (let i = 0; i < this.errorCallbacks.length; i++) {
|
|
422
|
+
const callback = this.errorCallbacks[i];
|
|
423
|
+
if (callback) {
|
|
424
|
+
callback(data.error);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return;
|
|
370
428
|
}
|
|
371
429
|
if (count) {
|
|
372
430
|
for (let i = 0; i < subs.length; i++) {
|
|
@@ -376,6 +434,14 @@ export default class PubSub {
|
|
|
376
434
|
}
|
|
377
435
|
}
|
|
378
436
|
}
|
|
437
|
+
if (error) {
|
|
438
|
+
for (let i = 0; i < subs.length; i++) {
|
|
439
|
+
const callback = subs[i][1];
|
|
440
|
+
if (typeof callback === 'function') {
|
|
441
|
+
callback(data.error);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
379
445
|
}
|
|
380
446
|
|
|
381
447
|
private _onclose(e: WebSocketEventMap['close']) {
|
|
@@ -389,7 +455,7 @@ export default class PubSub {
|
|
|
389
455
|
}
|
|
390
456
|
}
|
|
391
457
|
if (
|
|
392
|
-
e.code !==
|
|
458
|
+
e.code !== 1000 &&
|
|
393
459
|
(typeof navigator === 'undefined' || navigator.onLine)
|
|
394
460
|
) {
|
|
395
461
|
if (this.connection) {
|
|
@@ -412,6 +478,17 @@ export default class PubSub {
|
|
|
412
478
|
);
|
|
413
479
|
}
|
|
414
480
|
|
|
481
|
+
public isConnectionConnecting() {
|
|
482
|
+
return !!(
|
|
483
|
+
this.connection &&
|
|
484
|
+
this.connection.readyState === this.WebSocket.CONNECTING
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
public isConnection() {
|
|
489
|
+
return this.isConnectionOpen() || this.isConnectionConnecting();
|
|
490
|
+
}
|
|
491
|
+
|
|
415
492
|
private _subscribeQuery(query: string, callbacks: PubSubCallbacks<any>) {
|
|
416
493
|
let isNewSubscription = false;
|
|
417
494
|
if (!this.subscriptions.queries.hasOwnProperty(query)) {
|
|
@@ -549,126 +626,117 @@ export default class PubSub {
|
|
|
549
626
|
}
|
|
550
627
|
|
|
551
628
|
public updateArray(
|
|
552
|
-
|
|
553
|
-
update: PubSubUpdate<EntityInterface[]
|
|
629
|
+
current: EntityInterface[],
|
|
630
|
+
update: PubSubUpdate<EntityInterface[]>,
|
|
554
631
|
) {
|
|
555
632
|
if (Array.isArray(update)) {
|
|
556
633
|
const newArr = [...update];
|
|
557
634
|
|
|
558
|
-
if (
|
|
635
|
+
if (current.length === 0) {
|
|
559
636
|
// This will happen on the first update from a subscribe.
|
|
560
|
-
|
|
637
|
+
current.splice(0, 0, ...newArr);
|
|
561
638
|
return;
|
|
562
639
|
}
|
|
563
640
|
|
|
564
|
-
const idMap: { [k: string]:
|
|
565
|
-
const
|
|
641
|
+
const idMap: { [k: string]: EntityInterface } = {};
|
|
642
|
+
const newEntities: EntityInterface[] = [];
|
|
643
|
+
while (current.length) {
|
|
644
|
+
const first = current.shift();
|
|
645
|
+
if (!first) {
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
const guid = first.guid;
|
|
649
|
+
if (!guid) {
|
|
650
|
+
newEntities.push(first);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
idMap[guid] = first;
|
|
654
|
+
}
|
|
655
|
+
|
|
566
656
|
for (let i = 0; i < newArr.length; i++) {
|
|
567
657
|
const entity = newArr[i];
|
|
568
|
-
|
|
569
|
-
|
|
658
|
+
const guid = entity.guid;
|
|
659
|
+
if (guid == null) {
|
|
660
|
+
continue;
|
|
570
661
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
if (guid != null) {
|
|
583
|
-
if (!idMap.hasOwnProperty(guid)) {
|
|
584
|
-
// It was deleted.
|
|
585
|
-
remove.push(k);
|
|
586
|
-
} else if (newArr[idMap[guid]].mdate !== oldArr[k].mdate) {
|
|
587
|
-
// It was modified.
|
|
588
|
-
oldArr[k].$init(newArr[idMap[guid]].toJSON());
|
|
589
|
-
delete idMap[guid];
|
|
590
|
-
} else {
|
|
591
|
-
// Item wasn't modified.
|
|
592
|
-
delete idMap[guid];
|
|
593
|
-
}
|
|
594
|
-
}
|
|
662
|
+
|
|
663
|
+
if (!idMap.hasOwnProperty(guid)) {
|
|
664
|
+
// It was added.
|
|
665
|
+
current.push(entity);
|
|
666
|
+
} else if (idMap[guid].mdate !== entity.mdate) {
|
|
667
|
+
// It was modified.
|
|
668
|
+
idMap[guid].$init(entity.toJSON());
|
|
669
|
+
current.push(idMap[guid]);
|
|
670
|
+
} else {
|
|
671
|
+
// Item wasn't modified.
|
|
672
|
+
current.push(idMap[guid]);
|
|
595
673
|
}
|
|
596
674
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
// Sort backwards so we can remove in reverse order. (Preserves
|
|
600
|
-
// indices.)
|
|
601
|
-
if (a > b) return -1;
|
|
602
|
-
if (a < b) return 1;
|
|
603
|
-
return 0;
|
|
604
|
-
});
|
|
605
|
-
for (let n = 0; n < remove.length; n++) {
|
|
606
|
-
oldArr.splice(remove[n], 1);
|
|
607
|
-
}
|
|
608
|
-
// And add the new ones.
|
|
609
|
-
for (let value of Object.values(idMap)) {
|
|
610
|
-
oldArr.splice(oldArr.length, 0, newArr[value]);
|
|
611
|
-
}
|
|
675
|
+
|
|
676
|
+
current.splice(current.length, 0, ...newEntities);
|
|
612
677
|
} else if (update != null && update.hasOwnProperty('query')) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
678
|
+
const updates =
|
|
679
|
+
'multiple' in update && update.multiple ? update.messages : [update];
|
|
680
|
+
for (let curUpdate of updates) {
|
|
681
|
+
if ('removed' in curUpdate) {
|
|
682
|
+
for (let i = 0; i < current.length; i++) {
|
|
683
|
+
if (current[i] != null && current[i].guid === curUpdate.removed) {
|
|
684
|
+
current.splice(i, 1);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
618
687
|
}
|
|
619
688
|
}
|
|
620
|
-
}
|
|
621
689
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
690
|
+
// Get the entity.
|
|
691
|
+
let entity: EntityInterface | null = null;
|
|
692
|
+
if ('added' in curUpdate) {
|
|
693
|
+
// Check for it in the array already.
|
|
694
|
+
for (let i = 0; i < current.length; i++) {
|
|
695
|
+
if (current[i] != null && current[i].guid === curUpdate.added) {
|
|
696
|
+
entity = current.splice(i, 1)[0].$init(curUpdate.data);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (entity == null) {
|
|
700
|
+
// A new entity.
|
|
701
|
+
entity = this.nymph.initEntity(curUpdate.data);
|
|
629
702
|
}
|
|
630
703
|
}
|
|
631
|
-
if (
|
|
632
|
-
//
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// Extract it from the array.
|
|
638
|
-
for (let i = 0; i < oldArr.length; i++) {
|
|
639
|
-
if (oldArr[i] != null && oldArr[i].guid === update.updated) {
|
|
640
|
-
entity = oldArr.splice(i, 1)[0].$init(update.data);
|
|
704
|
+
if ('updated' in curUpdate) {
|
|
705
|
+
// Extract it from the array.
|
|
706
|
+
for (let i = 0; i < current.length; i++) {
|
|
707
|
+
if (current[i] != null && current[i].guid === curUpdate.updated) {
|
|
708
|
+
entity = current.splice(i, 1)[0].$init(curUpdate.data);
|
|
709
|
+
}
|
|
641
710
|
}
|
|
642
711
|
}
|
|
643
|
-
}
|
|
644
712
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
713
|
+
if (entity != null) {
|
|
714
|
+
const query = JSON.parse(curUpdate.query);
|
|
715
|
+
// Insert the entity in order.
|
|
716
|
+
const sort = 'sort' in query[0] ? (query[0].sort as string) : 'cdate';
|
|
717
|
+
const reverse = query[0].hasOwnProperty('reverse')
|
|
718
|
+
? query[0].reverse
|
|
719
|
+
: false;
|
|
720
|
+
let i;
|
|
721
|
+
|
|
722
|
+
if (reverse) {
|
|
723
|
+
for (
|
|
724
|
+
i = 0;
|
|
725
|
+
((current[i] ?? {})[sort] ?? 0) >= (entity[sort] ?? 0) &&
|
|
726
|
+
i < current.length;
|
|
727
|
+
i++
|
|
728
|
+
);
|
|
729
|
+
} else {
|
|
730
|
+
for (
|
|
731
|
+
i = 0;
|
|
732
|
+
((current[i] ?? {})[sort] ?? 0) < (entity[sort] ?? 0) &&
|
|
733
|
+
i < current.length;
|
|
734
|
+
i++
|
|
735
|
+
);
|
|
736
|
+
}
|
|
670
737
|
|
|
671
|
-
|
|
738
|
+
current.splice(i, 0, entity);
|
|
739
|
+
}
|
|
672
740
|
}
|
|
673
741
|
}
|
|
674
742
|
}
|
|
@@ -678,17 +746,22 @@ export default class PubSub {
|
|
|
678
746
|
callback: T extends 'connect'
|
|
679
747
|
? PubSubConnectCallback
|
|
680
748
|
: T extends 'disconnect'
|
|
681
|
-
|
|
682
|
-
|
|
749
|
+
? PubSubDisconnectCallback
|
|
750
|
+
: T extends 'error'
|
|
751
|
+
? PubSubErrorCallback
|
|
752
|
+
: never,
|
|
683
753
|
) {
|
|
684
754
|
const prop = (event + 'Callbacks') as T extends 'connect'
|
|
685
755
|
? 'connectCallbacks'
|
|
686
756
|
: T extends 'disconnect'
|
|
687
|
-
|
|
688
|
-
|
|
757
|
+
? 'disconnectCallbacks'
|
|
758
|
+
: T extends 'error'
|
|
759
|
+
? 'errorCallbacks'
|
|
760
|
+
: never;
|
|
689
761
|
if (!(prop in this)) {
|
|
690
762
|
throw new Error('Invalid event type.');
|
|
691
763
|
}
|
|
764
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
692
765
|
this[prop].push(callback);
|
|
693
766
|
return () => this.off(event, callback);
|
|
694
767
|
}
|
|
@@ -698,17 +771,22 @@ export default class PubSub {
|
|
|
698
771
|
callback: T extends 'connect'
|
|
699
772
|
? PubSubConnectCallback
|
|
700
773
|
: T extends 'disconnect'
|
|
701
|
-
|
|
702
|
-
|
|
774
|
+
? PubSubDisconnectCallback
|
|
775
|
+
: T extends 'error'
|
|
776
|
+
? PubSubErrorCallback
|
|
777
|
+
: never,
|
|
703
778
|
) {
|
|
704
779
|
const prop = (event + 'Callbacks') as T extends 'connect'
|
|
705
780
|
? 'connectCallbacks'
|
|
706
781
|
: T extends 'disconnect'
|
|
707
|
-
|
|
708
|
-
|
|
782
|
+
? 'disconnectCallbacks'
|
|
783
|
+
: T extends 'error'
|
|
784
|
+
? 'errorCallbacks'
|
|
785
|
+
: never;
|
|
709
786
|
if (!(prop in this)) {
|
|
710
787
|
return false;
|
|
711
788
|
}
|
|
789
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
712
790
|
const i = this[prop].indexOf(callback);
|
|
713
791
|
if (i > -1) {
|
|
714
792
|
this[prop].splice(i, 1);
|
|
@@ -716,12 +794,17 @@ export default class PubSub {
|
|
|
716
794
|
return true;
|
|
717
795
|
}
|
|
718
796
|
|
|
719
|
-
public
|
|
720
|
-
authToken
|
|
797
|
+
public authenticate(
|
|
798
|
+
authToken: string | null,
|
|
799
|
+
switchToken: string | null = null,
|
|
800
|
+
) {
|
|
801
|
+
this.authToken = authToken;
|
|
802
|
+
this.switchToken = switchToken;
|
|
721
803
|
if (this.isConnectionOpen()) {
|
|
722
804
|
this._send({
|
|
723
805
|
action: 'authenticate',
|
|
724
|
-
|
|
806
|
+
authToken: this.authToken,
|
|
807
|
+
switchToken: this.switchToken,
|
|
725
808
|
});
|
|
726
809
|
}
|
|
727
810
|
}
|
|
@@ -735,7 +818,7 @@ export class PubSubSubscription<T> {
|
|
|
735
818
|
constructor(
|
|
736
819
|
query: string,
|
|
737
820
|
callbacks: PubSubCallbacks<T>,
|
|
738
|
-
unsubscribe: () => void
|
|
821
|
+
unsubscribe: () => void,
|
|
739
822
|
) {
|
|
740
823
|
this.query = query;
|
|
741
824
|
this.callbacks = callbacks;
|