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