@nymphjs/client 1.0.0-beta.7 → 1.0.0-beta.70
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 +300 -0
- package/README.md +4 -4
- package/asyncitertest.js +53 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/lib/Entity.d.ts +112 -8
- package/lib/Entity.js +158 -76
- package/lib/Entity.js.map +1 -1
- package/lib/Entity.types.d.ts +144 -9
- package/lib/EntityWeakCache.js +3 -2
- package/lib/EntityWeakCache.js.map +1 -1
- package/lib/HttpRequester.d.ts +44 -8
- package/lib/HttpRequester.js +236 -21
- package/lib/HttpRequester.js.map +1 -1
- package/lib/Nymph.d.ts +43 -11
- package/lib/Nymph.js +102 -19
- package/lib/Nymph.js.map +1 -1
- package/lib/Nymph.types.d.ts +48 -1
- package/lib/PubSub.d.ts +15 -9
- package/lib/PubSub.js +148 -87
- package/lib/PubSub.js.map +1 -1
- package/lib/PubSub.types.d.ts +6 -1
- package/lib/entityRefresh.js +16 -0
- package/lib/entityRefresh.js.map +1 -1
- package/lib/utils.js +11 -0
- package/lib/utils.js.map +1 -1
- package/package.json +17 -14
- package/src/Entity.ts +163 -103
- package/src/Entity.types.ts +10 -46
- package/src/EntityWeakCache.ts +7 -5
- package/src/HttpRequester.ts +302 -29
- package/src/Nymph.ts +140 -67
- package/src/Nymph.types.ts +15 -1
- package/src/PubSub.ts +205 -132
- package/src/PubSub.types.ts +8 -3
- package/src/entityRefresh.ts +5 -5
- package/tsconfig.json +1 -1
- package/typedoc.json +4 -0
package/src/PubSub.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import Nymph, { InvalidRequestError } from './Nymph';
|
|
2
|
-
import { NymphOptions, Options, Selector } from './Nymph.types';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import type { NymphOptions, Options, Selector } from './Nymph.types';
|
|
3
|
+
import type { EntityInstanceType } from './Entity';
|
|
4
|
+
import type {
|
|
5
|
+
EntityConstructor,
|
|
6
|
+
EntityInterface,
|
|
7
|
+
EntityJson,
|
|
8
|
+
} from './Entity.types';
|
|
9
|
+
import type {
|
|
5
10
|
PubSubCallbacks,
|
|
6
11
|
PubSubConnectCallback,
|
|
7
12
|
PubSubCountCallback,
|
|
@@ -9,15 +14,17 @@ import {
|
|
|
9
14
|
PubSubEventType,
|
|
10
15
|
PubSubRejectCallback,
|
|
11
16
|
PubSubResolveCallback,
|
|
17
|
+
PubSubErrorCallback,
|
|
12
18
|
PubSubSubscribable,
|
|
13
19
|
PubSubUpdate,
|
|
14
20
|
} from './PubSub.types';
|
|
15
21
|
import { entityConstructorsToClassNames } from './utils';
|
|
16
|
-
|
|
17
|
-
let authToken: string | null = null;
|
|
22
|
+
import { ClientError } from './HttpRequester';
|
|
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
29
|
private waitForConnectionTimeout: NodeJS.Timeout | undefined;
|
|
23
30
|
private pubsubUrl: string | undefined;
|
|
@@ -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,8 +215,8 @@ 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
|
}
|
|
@@ -242,31 +260,31 @@ export default class PubSub {
|
|
|
242
260
|
return;
|
|
243
261
|
}
|
|
244
262
|
|
|
245
|
-
this.connection.close(
|
|
263
|
+
this.connection.close(1000, 'Closure requested by application.');
|
|
246
264
|
}
|
|
247
265
|
|
|
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
|
-
|
|
266
|
+
private _waitForConnection(attempts = 1) {
|
|
267
|
+
// Wait 5 seconds, then check and attempt connection again if unsuccessful.
|
|
268
|
+
// Keep repeating, adding attempts^2*5 seconds each time to a max of ten
|
|
269
|
+
// minutes, until successful.
|
|
270
|
+
this.waitForConnectionTimeout = setTimeout(
|
|
271
|
+
() => {
|
|
272
|
+
if (this.connection) {
|
|
273
|
+
if (this.connection.readyState !== this.WebSocket.OPEN) {
|
|
274
|
+
if (this.connection.readyState !== this.WebSocket.CONNECTING) {
|
|
275
|
+
this.connection.close();
|
|
276
|
+
this._waitForConnection(attempts + 1);
|
|
277
|
+
this._attemptConnect();
|
|
278
|
+
} else {
|
|
279
|
+
this._waitForConnection(attempts + 1);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
263
282
|
} else {
|
|
264
|
-
this.
|
|
283
|
+
this._attemptConnect();
|
|
265
284
|
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}, 5000);
|
|
285
|
+
},
|
|
286
|
+
Math.max(Math.pow(attempts, 2) * 5000, 1000 * 60 * 10),
|
|
287
|
+
);
|
|
270
288
|
}
|
|
271
289
|
|
|
272
290
|
private _attemptConnect() {
|
|
@@ -282,6 +300,11 @@ export default class PubSub {
|
|
|
282
300
|
if (typeof console !== 'undefined' && !this.noConsole) {
|
|
283
301
|
console.log('Nymph-PubSub connection established!');
|
|
284
302
|
}
|
|
303
|
+
|
|
304
|
+
if (this.waitForConnectionTimeout) {
|
|
305
|
+
clearTimeout(this.waitForConnectionTimeout);
|
|
306
|
+
}
|
|
307
|
+
|
|
285
308
|
for (let i = 0; i < this.connectCallbacks.length; i++) {
|
|
286
309
|
const callback = this.connectCallbacks[i];
|
|
287
310
|
if (callback) {
|
|
@@ -289,10 +312,11 @@ export default class PubSub {
|
|
|
289
312
|
}
|
|
290
313
|
}
|
|
291
314
|
|
|
292
|
-
if (authToken != null) {
|
|
315
|
+
if (this.authToken != null) {
|
|
293
316
|
this._send({
|
|
294
317
|
action: 'authenticate',
|
|
295
|
-
|
|
318
|
+
authToken: this.authToken,
|
|
319
|
+
switchToken: this.switchToken,
|
|
296
320
|
});
|
|
297
321
|
}
|
|
298
322
|
|
|
@@ -340,17 +364,23 @@ export default class PubSub {
|
|
|
340
364
|
private _onmessage(e: WebSocketEventMap['message']) {
|
|
341
365
|
let data = JSON.parse(e.data);
|
|
342
366
|
let subs: PubSubCallbacks<any>[] = [];
|
|
367
|
+
let set = 'set' in data && data.set;
|
|
343
368
|
let count = 'count' in data;
|
|
369
|
+
let error = 'error' in data;
|
|
344
370
|
if (
|
|
345
371
|
data.hasOwnProperty('query') &&
|
|
346
372
|
this.subscriptions.queries.hasOwnProperty(data.query)
|
|
347
373
|
) {
|
|
348
374
|
subs = [...this.subscriptions.queries[data.query]];
|
|
349
|
-
if (!count) {
|
|
375
|
+
if (!count && !error) {
|
|
350
376
|
for (let i = 0; i < subs.length; i++) {
|
|
351
377
|
const callback = subs[i][0];
|
|
352
378
|
if (typeof callback === 'function') {
|
|
353
|
-
callback(
|
|
379
|
+
callback(
|
|
380
|
+
set
|
|
381
|
+
? data.data.map((e: EntityJson) => this.nymph.initEntity(e))
|
|
382
|
+
: data,
|
|
383
|
+
);
|
|
354
384
|
}
|
|
355
385
|
}
|
|
356
386
|
}
|
|
@@ -359,14 +389,36 @@ export default class PubSub {
|
|
|
359
389
|
this.subscriptions.uids.hasOwnProperty(data.uid)
|
|
360
390
|
) {
|
|
361
391
|
subs = [...this.subscriptions.uids[data.uid]];
|
|
362
|
-
if (!count) {
|
|
392
|
+
if (!count && !error) {
|
|
363
393
|
for (let i = 0; i < subs.length; i++) {
|
|
364
394
|
const callback = subs[i][0];
|
|
365
|
-
|
|
366
|
-
|
|
395
|
+
const errCallback = subs[i][1];
|
|
396
|
+
if (set && data.data == null) {
|
|
397
|
+
if (typeof errCallback === 'function') {
|
|
398
|
+
errCallback(
|
|
399
|
+
new ClientError(
|
|
400
|
+
{ status: 404, statusText: 'Not Found' } as Response,
|
|
401
|
+
{ textStatus: 'Not Found' },
|
|
402
|
+
),
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
} else if (typeof callback === 'function') {
|
|
406
|
+
if (set) {
|
|
407
|
+
callback(data.data);
|
|
408
|
+
} else {
|
|
409
|
+
callback(data.value ?? null, data.event);
|
|
410
|
+
}
|
|
367
411
|
}
|
|
368
412
|
}
|
|
369
413
|
}
|
|
414
|
+
} else if (error) {
|
|
415
|
+
for (let i = 0; i < this.errorCallbacks.length; i++) {
|
|
416
|
+
const callback = this.errorCallbacks[i];
|
|
417
|
+
if (callback) {
|
|
418
|
+
callback(data.error);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return;
|
|
370
422
|
}
|
|
371
423
|
if (count) {
|
|
372
424
|
for (let i = 0; i < subs.length; i++) {
|
|
@@ -376,6 +428,14 @@ export default class PubSub {
|
|
|
376
428
|
}
|
|
377
429
|
}
|
|
378
430
|
}
|
|
431
|
+
if (error) {
|
|
432
|
+
for (let i = 0; i < subs.length; i++) {
|
|
433
|
+
const callback = subs[i][1];
|
|
434
|
+
if (typeof callback === 'function') {
|
|
435
|
+
callback(data.error);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
379
439
|
}
|
|
380
440
|
|
|
381
441
|
private _onclose(e: WebSocketEventMap['close']) {
|
|
@@ -389,7 +449,7 @@ export default class PubSub {
|
|
|
389
449
|
}
|
|
390
450
|
}
|
|
391
451
|
if (
|
|
392
|
-
e.code !==
|
|
452
|
+
e.code !== 1000 &&
|
|
393
453
|
(typeof navigator === 'undefined' || navigator.onLine)
|
|
394
454
|
) {
|
|
395
455
|
if (this.connection) {
|
|
@@ -412,6 +472,17 @@ export default class PubSub {
|
|
|
412
472
|
);
|
|
413
473
|
}
|
|
414
474
|
|
|
475
|
+
public isConnectionConnecting() {
|
|
476
|
+
return !!(
|
|
477
|
+
this.connection &&
|
|
478
|
+
this.connection.readyState === this.WebSocket.CONNECTING
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
public isConnection() {
|
|
483
|
+
return this.isConnectionOpen() || this.isConnectionConnecting();
|
|
484
|
+
}
|
|
485
|
+
|
|
415
486
|
private _subscribeQuery(query: string, callbacks: PubSubCallbacks<any>) {
|
|
416
487
|
let isNewSubscription = false;
|
|
417
488
|
if (!this.subscriptions.queries.hasOwnProperty(query)) {
|
|
@@ -549,71 +620,59 @@ export default class PubSub {
|
|
|
549
620
|
}
|
|
550
621
|
|
|
551
622
|
public updateArray(
|
|
552
|
-
|
|
553
|
-
update: PubSubUpdate<EntityInterface[]
|
|
623
|
+
current: EntityInterface[],
|
|
624
|
+
update: PubSubUpdate<EntityInterface[]>,
|
|
554
625
|
) {
|
|
555
626
|
if (Array.isArray(update)) {
|
|
556
627
|
const newArr = [...update];
|
|
557
628
|
|
|
558
|
-
if (
|
|
629
|
+
if (current.length === 0) {
|
|
559
630
|
// This will happen on the first update from a subscribe.
|
|
560
|
-
|
|
631
|
+
current.splice(0, 0, ...newArr);
|
|
561
632
|
return;
|
|
562
633
|
}
|
|
563
634
|
|
|
564
|
-
const idMap: { [k: string]:
|
|
565
|
-
const
|
|
635
|
+
const idMap: { [k: string]: EntityInterface } = {};
|
|
636
|
+
const newEntities: EntityInterface[] = [];
|
|
637
|
+
while (current.length) {
|
|
638
|
+
const first = current.shift();
|
|
639
|
+
if (!first) {
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const guid = first.guid;
|
|
643
|
+
if (!guid) {
|
|
644
|
+
newEntities.push(first);
|
|
645
|
+
continue;
|
|
646
|
+
}
|
|
647
|
+
idMap[guid] = first;
|
|
648
|
+
}
|
|
649
|
+
|
|
566
650
|
for (let i = 0; i < newArr.length; i++) {
|
|
567
651
|
const entity = newArr[i];
|
|
568
|
-
|
|
569
|
-
|
|
652
|
+
const guid = entity.guid;
|
|
653
|
+
if (guid == null) {
|
|
654
|
+
continue;
|
|
570
655
|
}
|
|
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
|
-
}
|
|
656
|
+
|
|
657
|
+
if (!idMap.hasOwnProperty(guid)) {
|
|
658
|
+
// It was added.
|
|
659
|
+
current.push(entity);
|
|
660
|
+
} else if (idMap[guid].mdate !== entity.mdate) {
|
|
661
|
+
// It was modified.
|
|
662
|
+
idMap[guid].$init(entity.toJSON());
|
|
663
|
+
current.push(idMap[guid]);
|
|
664
|
+
} else {
|
|
665
|
+
// Item wasn't modified.
|
|
666
|
+
current.push(idMap[guid]);
|
|
595
667
|
}
|
|
596
668
|
}
|
|
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
|
-
}
|
|
669
|
+
|
|
670
|
+
current.splice(current.length, 0, ...newEntities);
|
|
612
671
|
} else if (update != null && update.hasOwnProperty('query')) {
|
|
613
672
|
if ('removed' in update) {
|
|
614
|
-
for (let i = 0; i <
|
|
615
|
-
if (
|
|
616
|
-
|
|
673
|
+
for (let i = 0; i < current.length; i++) {
|
|
674
|
+
if (current[i] != null && current[i].guid === update.removed) {
|
|
675
|
+
current.splice(i, 1);
|
|
617
676
|
return;
|
|
618
677
|
}
|
|
619
678
|
}
|
|
@@ -623,9 +682,9 @@ export default class PubSub {
|
|
|
623
682
|
let entity: EntityInterface | null = null;
|
|
624
683
|
if ('added' in update) {
|
|
625
684
|
// Check for it in the array already.
|
|
626
|
-
for (let i = 0; i <
|
|
627
|
-
if (
|
|
628
|
-
entity =
|
|
685
|
+
for (let i = 0; i < current.length; i++) {
|
|
686
|
+
if (current[i] != null && current[i].guid === update.added) {
|
|
687
|
+
entity = current.splice(i, 1)[0].$init(update.data);
|
|
629
688
|
}
|
|
630
689
|
}
|
|
631
690
|
if (entity == null) {
|
|
@@ -635,9 +694,9 @@ export default class PubSub {
|
|
|
635
694
|
}
|
|
636
695
|
if ('updated' in update) {
|
|
637
696
|
// Extract it from the array.
|
|
638
|
-
for (let i = 0; i <
|
|
639
|
-
if (
|
|
640
|
-
entity =
|
|
697
|
+
for (let i = 0; i < current.length; i++) {
|
|
698
|
+
if (current[i] != null && current[i].guid === update.updated) {
|
|
699
|
+
entity = current.splice(i, 1)[0].$init(update.data);
|
|
641
700
|
}
|
|
642
701
|
}
|
|
643
702
|
}
|
|
@@ -645,8 +704,7 @@ export default class PubSub {
|
|
|
645
704
|
const query = JSON.parse(update.query);
|
|
646
705
|
if (entity != null) {
|
|
647
706
|
// Insert the entity in order.
|
|
648
|
-
const sort =
|
|
649
|
-
'sort' in query[0] ? (query[0].sort as 'cdate' | 'mdate') : 'cdate';
|
|
707
|
+
const sort = 'sort' in query[0] ? (query[0].sort as string) : 'cdate';
|
|
650
708
|
const reverse = query[0].hasOwnProperty('reverse')
|
|
651
709
|
? query[0].reverse
|
|
652
710
|
: false;
|
|
@@ -655,20 +713,20 @@ export default class PubSub {
|
|
|
655
713
|
if (reverse) {
|
|
656
714
|
for (
|
|
657
715
|
i = 0;
|
|
658
|
-
((
|
|
659
|
-
i <
|
|
716
|
+
((current[i] ?? {})[sort] ?? 0) >= (entity[sort] ?? 0) &&
|
|
717
|
+
i < current.length;
|
|
660
718
|
i++
|
|
661
719
|
);
|
|
662
720
|
} else {
|
|
663
721
|
for (
|
|
664
722
|
i = 0;
|
|
665
|
-
((
|
|
666
|
-
i <
|
|
723
|
+
((current[i] ?? {})[sort] ?? 0) < (entity[sort] ?? 0) &&
|
|
724
|
+
i < current.length;
|
|
667
725
|
i++
|
|
668
726
|
);
|
|
669
727
|
}
|
|
670
728
|
|
|
671
|
-
|
|
729
|
+
current.splice(i, 0, entity);
|
|
672
730
|
}
|
|
673
731
|
}
|
|
674
732
|
}
|
|
@@ -679,16 +737,21 @@ export default class PubSub {
|
|
|
679
737
|
? PubSubConnectCallback
|
|
680
738
|
: T extends 'disconnect'
|
|
681
739
|
? PubSubDisconnectCallback
|
|
682
|
-
:
|
|
740
|
+
: T extends 'error'
|
|
741
|
+
? PubSubErrorCallback
|
|
742
|
+
: never,
|
|
683
743
|
) {
|
|
684
744
|
const prop = (event + 'Callbacks') as T extends 'connect'
|
|
685
745
|
? 'connectCallbacks'
|
|
686
746
|
: T extends 'disconnect'
|
|
687
747
|
? 'disconnectCallbacks'
|
|
748
|
+
: T extends 'error'
|
|
749
|
+
? 'errorCallbacks'
|
|
688
750
|
: never;
|
|
689
751
|
if (!(prop in this)) {
|
|
690
752
|
throw new Error('Invalid event type.');
|
|
691
753
|
}
|
|
754
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
692
755
|
this[prop].push(callback);
|
|
693
756
|
return () => this.off(event, callback);
|
|
694
757
|
}
|
|
@@ -699,16 +762,21 @@ export default class PubSub {
|
|
|
699
762
|
? PubSubConnectCallback
|
|
700
763
|
: T extends 'disconnect'
|
|
701
764
|
? PubSubDisconnectCallback
|
|
702
|
-
:
|
|
765
|
+
: T extends 'error'
|
|
766
|
+
? PubSubErrorCallback
|
|
767
|
+
: never,
|
|
703
768
|
) {
|
|
704
769
|
const prop = (event + 'Callbacks') as T extends 'connect'
|
|
705
770
|
? 'connectCallbacks'
|
|
706
771
|
: T extends 'disconnect'
|
|
707
772
|
? 'disconnectCallbacks'
|
|
773
|
+
: T extends 'error'
|
|
774
|
+
? 'errorCallbacks'
|
|
708
775
|
: never;
|
|
709
776
|
if (!(prop in this)) {
|
|
710
777
|
return false;
|
|
711
778
|
}
|
|
779
|
+
// @ts-ignore: The callback should always be the right type here.
|
|
712
780
|
const i = this[prop].indexOf(callback);
|
|
713
781
|
if (i > -1) {
|
|
714
782
|
this[prop].splice(i, 1);
|
|
@@ -716,12 +784,17 @@ export default class PubSub {
|
|
|
716
784
|
return true;
|
|
717
785
|
}
|
|
718
786
|
|
|
719
|
-
public
|
|
720
|
-
authToken
|
|
787
|
+
public authenticate(
|
|
788
|
+
authToken: string | null,
|
|
789
|
+
switchToken: string | null = null,
|
|
790
|
+
) {
|
|
791
|
+
this.authToken = authToken;
|
|
792
|
+
this.switchToken = switchToken;
|
|
721
793
|
if (this.isConnectionOpen()) {
|
|
722
794
|
this._send({
|
|
723
795
|
action: 'authenticate',
|
|
724
|
-
|
|
796
|
+
authToken: this.authToken,
|
|
797
|
+
switchToken: this.switchToken,
|
|
725
798
|
});
|
|
726
799
|
}
|
|
727
800
|
}
|
|
@@ -735,7 +808,7 @@ export class PubSubSubscription<T> {
|
|
|
735
808
|
constructor(
|
|
736
809
|
query: string,
|
|
737
810
|
callbacks: PubSubCallbacks<T>,
|
|
738
|
-
unsubscribe: () => void
|
|
811
|
+
unsubscribe: () => void,
|
|
739
812
|
) {
|
|
740
813
|
this.query = query;
|
|
741
814
|
this.callbacks = callbacks;
|
package/src/PubSub.types.ts
CHANGED
|
@@ -7,11 +7,16 @@ export type PubSubCountCallback = (count: number) => void;
|
|
|
7
7
|
export type PubSubCallbacks<T> = [
|
|
8
8
|
PubSubResolveCallback<T> | undefined,
|
|
9
9
|
PubSubRejectCallback | undefined,
|
|
10
|
-
PubSubCountCallback | undefined
|
|
10
|
+
PubSubCountCallback | undefined,
|
|
11
11
|
];
|
|
12
|
-
export type PubSubEventType = 'connect' | 'disconnect';
|
|
12
|
+
export type PubSubEventType = 'connect' | 'disconnect' | 'error';
|
|
13
13
|
export type PubSubConnectCallback = () => void;
|
|
14
14
|
export type PubSubDisconnectCallback = () => void;
|
|
15
|
+
/**
|
|
16
|
+
* The error event is for unknown errors. Query errors fire their own reject
|
|
17
|
+
* callbacks.
|
|
18
|
+
*/
|
|
19
|
+
export type PubSubErrorCallback = (err: any) => void;
|
|
15
20
|
export type PubSubUpdate<T> =
|
|
16
21
|
| T
|
|
17
22
|
| {
|
|
@@ -31,5 +36,5 @@ export type PubSubUpdate<T> =
|
|
|
31
36
|
export type PubSubSubscribable<T> = (
|
|
32
37
|
resolve?: PubSubResolveCallback<T> | undefined,
|
|
33
38
|
reject?: PubSubRejectCallback | undefined,
|
|
34
|
-
count?: PubSubCountCallback | undefined
|
|
39
|
+
count?: PubSubCountCallback | undefined,
|
|
35
40
|
) => PubSubSubscription<T>;
|