@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.
Files changed (71) hide show
  1. package/CHANGELOG.md +496 -0
  2. package/README.md +6 -6
  3. package/asyncitertest.js +53 -0
  4. package/dist/Entity.d.ts +156 -0
  5. package/{lib → dist}/Entity.js +202 -104
  6. package/dist/Entity.js.map +1 -0
  7. package/dist/Entity.types.d.ts +218 -0
  8. package/dist/Entity.types.js +2 -0
  9. package/{lib → dist}/EntityWeakCache.d.ts +1 -1
  10. package/{lib → dist}/EntityWeakCache.js +5 -9
  11. package/dist/EntityWeakCache.js.map +1 -0
  12. package/{lib → dist}/HttpRequester.d.ts +18 -4
  13. package/dist/HttpRequester.js +365 -0
  14. package/dist/HttpRequester.js.map +1 -0
  15. package/{lib → dist}/Nymph.d.ts +48 -11
  16. package/{lib → dist}/Nymph.js +167 -51
  17. package/dist/Nymph.js.map +1 -0
  18. package/dist/Nymph.types.d.ts +227 -0
  19. package/dist/Nymph.types.js +45 -0
  20. package/dist/Nymph.types.js.map +1 -0
  21. package/{lib → dist}/PubSub.d.ts +16 -10
  22. package/{lib → dist}/PubSub.js +208 -135
  23. package/dist/PubSub.js.map +1 -0
  24. package/{lib → dist}/PubSub.types.d.ts +19 -6
  25. package/dist/PubSub.types.js +2 -0
  26. package/{lib → dist}/entityRefresh.d.ts +1 -1
  27. package/{lib → dist}/entityRefresh.js +21 -13
  28. package/dist/entityRefresh.js.map +1 -0
  29. package/dist/index.d.ts +13 -0
  30. package/dist/index.js +13 -2
  31. package/dist/index.js.map +1 -1
  32. package/{lib → dist}/utils.d.ts +1 -1
  33. package/{lib → dist}/utils.js +28 -21
  34. package/dist/utils.js.map +1 -0
  35. package/jest.config.js +31 -5
  36. package/package.json +31 -30
  37. package/src/Entity.ts +174 -108
  38. package/src/Entity.types.ts +29 -47
  39. package/src/EntityWeakCache.ts +8 -6
  40. package/src/HttpRequester.ts +268 -31
  41. package/src/Nymph.ts +188 -88
  42. package/src/Nymph.types.ts +102 -2
  43. package/src/PubSub.ts +262 -179
  44. package/src/PubSub.types.ts +33 -19
  45. package/src/entityRefresh.ts +6 -6
  46. package/src/index.ts +10 -10
  47. package/src/utils.ts +12 -5
  48. package/tsconfig.json +7 -4
  49. package/typedoc.json +4 -0
  50. package/dist/index.js.LICENSE.txt +0 -8
  51. package/lib/Entity.d.ts +0 -51
  52. package/lib/Entity.js.map +0 -1
  53. package/lib/Entity.types.d.ts +0 -65
  54. package/lib/Entity.types.js +0 -3
  55. package/lib/EntityWeakCache.js.map +0 -1
  56. package/lib/HttpRequester.js +0 -190
  57. package/lib/HttpRequester.js.map +0 -1
  58. package/lib/Nymph.js.map +0 -1
  59. package/lib/Nymph.types.d.ts +0 -97
  60. package/lib/Nymph.types.js +0 -3
  61. package/lib/Nymph.types.js.map +0 -1
  62. package/lib/PubSub.js.map +0 -1
  63. package/lib/PubSub.types.js +0 -3
  64. package/lib/entityRefresh.js.map +0 -1
  65. package/lib/index.d.ts +0 -13
  66. package/lib/index.js +0 -34
  67. package/lib/index.js.map +0 -1
  68. package/lib/utils.js.map +0 -1
  69. package/webpack.config.js +0 -28
  70. /package/{lib → dist}/Entity.types.js.map +0 -0
  71. /package/{lib → dist}/PubSub.types.js.map +0 -0
@@ -0,0 +1,45 @@
1
+ export var TilmeldAccessRequest;
2
+ (function (TilmeldAccessRequest) {
3
+ /**
4
+ * The default level, any possible entity readable by the user.
5
+ *
6
+ * This is the same as or-ing together all of the levels listed below.
7
+ */
8
+ TilmeldAccessRequest[TilmeldAccessRequest["ALL_LEVELS"] = 0] = "ALL_LEVELS";
9
+ /**
10
+ * Owned by and accessible to the user.
11
+ *
12
+ * This is always enabled, so not including it has no effect. It's here to
13
+ * allow the selection of _only_ user owned entities.
14
+ */
15
+ TilmeldAccessRequest[TilmeldAccessRequest["USER_OWNED"] = 1] = "USER_OWNED";
16
+ /**
17
+ * Owned by and accessible to the user's primary group.
18
+ */
19
+ TilmeldAccessRequest[TilmeldAccessRequest["PRIMARY_GROUP_OWNED"] = 2] = "PRIMARY_GROUP_OWNED";
20
+ /**
21
+ * Owned by and accessible to one of the user's secondary groups.
22
+ */
23
+ TilmeldAccessRequest[TilmeldAccessRequest["SECONDARY_GROUP_OWNED"] = 4] = "SECONDARY_GROUP_OWNED";
24
+ /**
25
+ * The user is listed in an access control list.
26
+ */
27
+ TilmeldAccessRequest[TilmeldAccessRequest["USER_ACCESSIBLE"] = 8] = "USER_ACCESSIBLE";
28
+ /**
29
+ * The user's primary group is listed in an access control list.
30
+ */
31
+ TilmeldAccessRequest[TilmeldAccessRequest["PRIMARY_GROUP_ACCESSIBLE"] = 16] = "PRIMARY_GROUP_ACCESSIBLE";
32
+ /**
33
+ * One of the user's secondary groups is listed in an access control list.
34
+ */
35
+ TilmeldAccessRequest[TilmeldAccessRequest["SECONDARY_GROUP_ACCESSIBLE"] = 32] = "SECONDARY_GROUP_ACCESSIBLE";
36
+ /**
37
+ * The entity is accessible to any user.
38
+ */
39
+ TilmeldAccessRequest[TilmeldAccessRequest["OTHER_ACCESSIBLE"] = 64] = "OTHER_ACCESSIBLE";
40
+ /**
41
+ * The entity is not owned by anyone.
42
+ */
43
+ TilmeldAccessRequest[TilmeldAccessRequest["UNOWNED"] = 128] = "UNOWNED";
44
+ })(TilmeldAccessRequest || (TilmeldAccessRequest = {}));
45
+ //# sourceMappingURL=Nymph.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Nymph.types.js","sourceRoot":"","sources":["../src/Nymph.types.ts"],"names":[],"mappings":"AAoOA,MAAM,CAAN,IAAY,oBA0CX;AA1CD,WAAY,oBAAoB;IAC9B;;;;OAIG;IACH,2EAAc,CAAA;IACd;;;;;OAKG;IACH,2EAAc,CAAA;IACd;;OAEG;IACH,6FAAuB,CAAA;IACvB;;OAEG;IACH,iGAAyB,CAAA;IACzB;;OAEG;IACH,qFAAmB,CAAA;IACnB;;OAEG;IACH,wGAA6B,CAAA;IAC7B;;OAEG;IACH,4GAA+B,CAAA;IAC/B;;OAEG;IACH,wFAAqB,CAAA;IACrB;;OAEG;IACH,uEAAa,CAAA;AACf,CAAC,EA1CW,oBAAoB,KAApB,oBAAoB,QA0C/B"}
@@ -1,9 +1,12 @@
1
- import Nymph from './Nymph';
2
- import { NymphOptions, Options, Selector } from './Nymph.types';
3
- import { EntityConstructor, EntityInterface } from './Entity.types';
4
- import { PubSubCallbacks, PubSubConnectCallback, PubSubCountCallback, PubSubDisconnectCallback, PubSubEventType, PubSubRejectCallback, PubSubResolveCallback, PubSubSubscribable, PubSubUpdate } from './PubSub.types';
1
+ import Nymph from './Nymph.js';
2
+ import type { NymphOptions, Options, Selector } from './Nymph.types.js';
3
+ import type { EntityInstanceType } from './Entity.js';
4
+ import type { EntityConstructor, EntityInterface } from './Entity.types.js';
5
+ import type { PubSubCallbacks, PubSubConnectCallback, PubSubCountCallback, PubSubDisconnectCallback, PubSubEventType, PubSubRejectCallback, PubSubResolveCallback, PubSubErrorCallback, PubSubSubscribable, PubSubUpdate } from './PubSub.types.js';
5
6
  export default class PubSub {
6
7
  private nymph;
8
+ private authToken;
9
+ private switchToken;
7
10
  private connection;
8
11
  private waitForConnectionTimeout;
9
12
  private pubsubUrl;
@@ -11,16 +14,17 @@ export default class PubSub {
11
14
  private subscriptions;
12
15
  private connectCallbacks;
13
16
  private disconnectCallbacks;
17
+ private errorCallbacks;
14
18
  private noConsole;
15
19
  constructor(nymphOptions: NymphOptions, nymph: Nymph);
16
20
  subscribeEntities<T extends EntityConstructor = EntityConstructor>(options: Options<T> & {
17
21
  return: 'guid';
18
22
  }, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<string[]>>;
19
- subscribeEntities<T extends EntityConstructor = EntityConstructor>(options: Options<T>, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<ReturnType<T['factorySync']>[]>>;
23
+ subscribeEntities<T extends EntityConstructor = EntityConstructor>(options: Options<T>, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<EntityInstanceType<T>[]>>;
20
24
  subscribeEntity<T extends EntityConstructor = EntityConstructor>(options: Options<T> & {
21
25
  return: 'guid';
22
26
  }, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<string | null>>;
23
- subscribeEntity<T extends EntityConstructor = EntityConstructor>(options: Options<T>, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<ReturnType<T['factorySync']> | null>>;
27
+ subscribeEntity<T extends EntityConstructor = EntityConstructor>(options: Options<T>, ...selectors: Selector[]): PubSubSubscribable<PubSubUpdate<EntityInstanceType<T> | null>>;
24
28
  subscribeUID(name: string): (resolve?: PubSubResolveCallback<number> | undefined, reject?: PubSubRejectCallback | undefined, count?: PubSubCountCallback | undefined) => {
25
29
  unsubscribe: () => void;
26
30
  };
@@ -34,6 +38,8 @@ export default class PubSub {
34
38
  private _onclose;
35
39
  private _send;
36
40
  isConnectionOpen(): boolean;
41
+ isConnectionConnecting(): boolean;
42
+ isConnection(): boolean;
37
43
  private _subscribeQuery;
38
44
  private _subscribeUID;
39
45
  private _sendQuery;
@@ -44,10 +50,10 @@ export default class PubSub {
44
50
  private _unsubscribeUID;
45
51
  private _sendUnQuery;
46
52
  private _sendUnUID;
47
- updateArray(oldArr: EntityInterface[], update: PubSubUpdate<EntityInterface[]>): void;
48
- on<T extends PubSubEventType>(event: T, callback: T extends 'connect' ? PubSubConnectCallback : T extends 'disconnect' ? PubSubDisconnectCallback : never): () => boolean;
49
- off<T extends PubSubEventType>(event: T, callback: T extends 'connect' ? PubSubConnectCallback : T extends 'disconnect' ? PubSubDisconnectCallback : never): boolean;
50
- setToken(token: string | null): void;
53
+ updateArray(current: EntityInterface[], update: PubSubUpdate<EntityInterface[]>): void;
54
+ on<T extends PubSubEventType>(event: T, callback: T extends 'connect' ? PubSubConnectCallback : T extends 'disconnect' ? PubSubDisconnectCallback : T extends 'error' ? PubSubErrorCallback : never): () => boolean;
55
+ off<T extends PubSubEventType>(event: T, callback: T extends 'connect' ? PubSubConnectCallback : T extends 'disconnect' ? PubSubDisconnectCallback : T extends 'error' ? PubSubErrorCallback : never): boolean;
56
+ authenticate(authToken: string | null, switchToken?: string | null): void;
51
57
  }
52
58
  export declare class PubSubSubscription<T> {
53
59
  query: string;
@@ -1,18 +1,23 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PubSubSubscription = void 0;
4
- const Nymph_1 = require("./Nymph");
5
- const utils_1 = require("./utils");
6
- let authToken = null;
7
- class PubSub {
1
+ import { InvalidRequestError } from './Nymph.js';
2
+ import { entityConstructorsToClassNames } from './utils.js';
3
+ import { ClientError } from './HttpRequester.js';
4
+ export default class PubSub {
5
+ nymph;
6
+ authToken = null;
7
+ switchToken = null;
8
+ connection;
9
+ waitForConnectionTimeout;
10
+ pubsubUrl;
11
+ WebSocket;
12
+ subscriptions = {
13
+ queries: {},
14
+ uids: {},
15
+ };
16
+ connectCallbacks = [];
17
+ disconnectCallbacks = [];
18
+ errorCallbacks = [];
19
+ noConsole = false;
8
20
  constructor(nymphOptions, nymph) {
9
- this.subscriptions = {
10
- queries: {},
11
- uids: {},
12
- };
13
- this.connectCallbacks = [];
14
- this.disconnectCallbacks = [];
15
- this.noConsole = false;
16
21
  this.nymph = nymph;
17
22
  this.nymph.pubsub = this;
18
23
  this.pubsubUrl = nymphOptions.pubsubUrl;
@@ -24,20 +29,23 @@ class PubSub {
24
29
  if (typeof addEventListener !== 'undefined') {
25
30
  addEventListener('online', () => this.connect());
26
31
  }
27
- if (typeof navigator === 'undefined' || navigator.onLine) {
32
+ if (!nymphOptions.noAutoconnect &&
33
+ (typeof navigator === 'undefined' || navigator.onLine)) {
28
34
  this.connect();
29
35
  }
30
36
  }
31
37
  subscribeEntities(options, ...selectors) {
32
- const promise = this.nymph.getEntities(options, ...selectors);
33
38
  const query = [
34
- (0, utils_1.entityConstructorsToClassNames)(options),
35
- ...(0, utils_1.entityConstructorsToClassNames)(selectors),
39
+ entityConstructorsToClassNames(options),
40
+ ...entityConstructorsToClassNames(selectors),
36
41
  ];
37
42
  const jsonQuery = JSON.stringify(query);
38
43
  const subscribe = (resolve, reject, count) => {
39
44
  const callbacks = [resolve, reject, count];
40
- promise.then(resolve, reject);
45
+ if (!this.isConnection()) {
46
+ // Fall back to a regular query if we're not connected.
47
+ this.nymph.getEntities(options, ...selectors).then(resolve, reject);
48
+ }
41
49
  this._subscribeQuery(jsonQuery, callbacks);
42
50
  return new PubSubSubscription(jsonQuery, callbacks, () => {
43
51
  this._unsubscribeQuery(jsonQuery, callbacks);
@@ -46,10 +54,9 @@ class PubSub {
46
54
  return subscribe;
47
55
  }
48
56
  subscribeEntity(options, ...selectors) {
49
- const promise = this.nymph.getEntity(options, ...selectors);
50
57
  const query = [
51
- { ...(0, utils_1.entityConstructorsToClassNames)(options), limit: 1 },
52
- ...(0, utils_1.entityConstructorsToClassNames)(selectors),
58
+ { ...entityConstructorsToClassNames(options), limit: 1 },
59
+ ...entityConstructorsToClassNames(selectors),
53
60
  ];
54
61
  const jsonQuery = JSON.stringify(query);
55
62
  const subscribe = (resolve, reject, count) => {
@@ -66,7 +73,10 @@ class PubSub {
66
73
  }
67
74
  };
68
75
  const callbacks = [newResolve, reject, count];
69
- promise.then(resolve, reject);
76
+ if (!this.isConnection()) {
77
+ // Fall back to a regular query if we're not connected.
78
+ this.nymph.getEntity(options, ...selectors).then(resolve, reject);
79
+ }
70
80
  this._subscribeQuery(jsonQuery, callbacks);
71
81
  return new PubSubSubscription(jsonQuery, callbacks, () => {
72
82
  this._unsubscribeQuery(jsonQuery, callbacks);
@@ -75,10 +85,12 @@ class PubSub {
75
85
  return subscribe;
76
86
  }
77
87
  subscribeUID(name) {
78
- const promise = this.nymph.getUID(name);
79
88
  const subscribe = (resolve, reject, count) => {
80
89
  const callbacks = [resolve, reject, count];
81
- promise.then(resolve, reject);
90
+ if (!this.isConnection()) {
91
+ // Fall back to a regular query if we're not connected.
92
+ this.nymph.getUID(name).then(resolve, reject);
93
+ }
82
94
  this._subscribeUID(name, callbacks);
83
95
  return {
84
96
  unsubscribe: () => {
@@ -90,7 +102,7 @@ class PubSub {
90
102
  }
91
103
  subscribeWith(entity, resolve, reject, count) {
92
104
  if (!entity.guid) {
93
- throw new Nymph_1.InvalidRequestError("You can't subscribe to an entity with no GUID.");
105
+ throw new InvalidRequestError("You can't subscribe to an entity with no GUID.");
94
106
  }
95
107
  const query = [
96
108
  { class: entity.constructor.class, limit: 1 },
@@ -99,18 +111,24 @@ class PubSub {
99
111
  const jsonQuery = JSON.stringify(query);
100
112
  const newResolve = (args) => {
101
113
  if (Array.isArray(args)) {
102
- if (args[0].length) {
103
- entity.$init(args[0]);
114
+ if (args.length) {
115
+ entity.$init(args[0].toJSON());
104
116
  }
105
117
  else {
106
118
  entity.guid = null;
107
119
  }
108
120
  }
109
- else if ('removed' in args) {
110
- entity.guid = null;
111
- }
112
121
  else {
113
- entity.$init(args.data);
122
+ let update = args;
123
+ if ('multiple' in args && args.multiple) {
124
+ update = args.messages.pop();
125
+ }
126
+ if ('removed' in update) {
127
+ entity.guid = null;
128
+ }
129
+ else {
130
+ entity.$init(update.data);
131
+ }
114
132
  }
115
133
  if (resolve) {
116
134
  resolve(entity);
@@ -123,6 +141,7 @@ class PubSub {
123
141
  });
124
142
  }
125
143
  connect() {
144
+ // Are we already connected?
126
145
  if (this.connection &&
127
146
  (this.connection.readyState === this.WebSocket.OPEN ||
128
147
  this.connection.readyState === this.WebSocket.CONNECTING)) {
@@ -138,28 +157,32 @@ class PubSub {
138
157
  if (!this.connection) {
139
158
  return;
140
159
  }
141
- this.connection.close(4200, 'Closure requested by application.');
160
+ this.connection.close(1000, 'Closure requested by application.');
142
161
  }
143
- _waitForConnection(attempts = 0) {
162
+ _waitForConnection(attempts = 1) {
163
+ // Wait 5 seconds, then check and attempt connection again if unsuccessful.
164
+ // Keep repeating, adding attempts^2*5 seconds each time to a max of ten
165
+ // minutes, until successful.
144
166
  this.waitForConnectionTimeout = setTimeout(() => {
145
- if (this.connection &&
146
- this.connection.readyState !== this.WebSocket.OPEN) {
147
- if (this.connection.readyState !== this.WebSocket.CONNECTING ||
148
- attempts >= 5) {
149
- this.connection.close();
150
- this._waitForConnection();
151
- this._attemptConnect();
152
- }
153
- else {
154
- this._waitForConnection(attempts + 1);
167
+ if (this.connection) {
168
+ if (this.connection.readyState !== this.WebSocket.OPEN) {
169
+ if (this.connection.readyState !== this.WebSocket.CONNECTING) {
170
+ this.connection.close();
171
+ this._waitForConnection(attempts + 1);
172
+ this._attemptConnect();
173
+ }
174
+ else {
175
+ this._waitForConnection(attempts + 1);
176
+ }
155
177
  }
156
178
  }
157
179
  else {
158
180
  this._attemptConnect();
159
181
  }
160
- }, 5000);
182
+ }, Math.max(Math.pow(attempts, 2) * 5000, 1000 * 60 * 10));
161
183
  }
162
184
  _attemptConnect() {
185
+ // Attempt to connect.
163
186
  if (this.pubsubUrl != null) {
164
187
  this.connection = new this.WebSocket(this.pubsubUrl, 'nymph');
165
188
  this.connection.onopen = this._onopen.bind(this);
@@ -170,16 +193,20 @@ class PubSub {
170
193
  if (typeof console !== 'undefined' && !this.noConsole) {
171
194
  console.log('Nymph-PubSub connection established!');
172
195
  }
196
+ if (this.waitForConnectionTimeout) {
197
+ clearTimeout(this.waitForConnectionTimeout);
198
+ }
173
199
  for (let i = 0; i < this.connectCallbacks.length; i++) {
174
200
  const callback = this.connectCallbacks[i];
175
201
  if (callback) {
176
202
  callback();
177
203
  }
178
204
  }
179
- if (authToken != null) {
205
+ if (this.authToken != null) {
180
206
  this._send({
181
207
  action: 'authenticate',
182
- token: authToken,
208
+ authToken: this.authToken,
209
+ switchToken: this.switchToken,
183
210
  });
184
211
  }
185
212
  for (let query in this.subscriptions.queries) {
@@ -215,15 +242,19 @@ class PubSub {
215
242
  _onmessage(e) {
216
243
  let data = JSON.parse(e.data);
217
244
  let subs = [];
245
+ let set = 'set' in data && data.set;
218
246
  let count = 'count' in data;
247
+ let error = 'error' in data;
219
248
  if (data.hasOwnProperty('query') &&
220
249
  this.subscriptions.queries.hasOwnProperty(data.query)) {
221
250
  subs = [...this.subscriptions.queries[data.query]];
222
- if (!count) {
251
+ if (!count && !error) {
223
252
  for (let i = 0; i < subs.length; i++) {
224
253
  const callback = subs[i][0];
225
254
  if (typeof callback === 'function') {
226
- callback(data);
255
+ callback(set
256
+ ? data.data.map((e) => this.nymph.initEntity(e))
257
+ : data);
227
258
  }
228
259
  }
229
260
  }
@@ -231,15 +262,35 @@ class PubSub {
231
262
  else if (data.hasOwnProperty('uid') &&
232
263
  this.subscriptions.uids.hasOwnProperty(data.uid)) {
233
264
  subs = [...this.subscriptions.uids[data.uid]];
234
- if (!count) {
265
+ if (!count && !error) {
235
266
  for (let i = 0; i < subs.length; i++) {
236
267
  const callback = subs[i][0];
237
- if (typeof callback === 'function') {
238
- callback(data.value ?? null, data.event);
268
+ const errCallback = subs[i][1];
269
+ if (set && data.data == null) {
270
+ if (typeof errCallback === 'function') {
271
+ errCallback(new ClientError({ status: 404, statusText: 'Not Found' }, { textStatus: 'Not Found' }));
272
+ }
273
+ }
274
+ else if (typeof callback === 'function') {
275
+ if (set) {
276
+ callback(data.data);
277
+ }
278
+ else {
279
+ callback(data.value ?? null, data.event);
280
+ }
239
281
  }
240
282
  }
241
283
  }
242
284
  }
285
+ else if (error) {
286
+ for (let i = 0; i < this.errorCallbacks.length; i++) {
287
+ const callback = this.errorCallbacks[i];
288
+ if (callback) {
289
+ callback(data.error);
290
+ }
291
+ }
292
+ return;
293
+ }
243
294
  if (count) {
244
295
  for (let i = 0; i < subs.length; i++) {
245
296
  const callback = subs[i][2];
@@ -248,6 +299,14 @@ class PubSub {
248
299
  }
249
300
  }
250
301
  }
302
+ if (error) {
303
+ for (let i = 0; i < subs.length; i++) {
304
+ const callback = subs[i][1];
305
+ if (typeof callback === 'function') {
306
+ callback(data.error);
307
+ }
308
+ }
309
+ }
251
310
  }
252
311
  _onclose(e) {
253
312
  if (typeof console !== 'undefined' && !this.noConsole) {
@@ -259,7 +318,7 @@ class PubSub {
259
318
  callback();
260
319
  }
261
320
  }
262
- if (e.code !== 4200 &&
321
+ if (e.code !== 1000 &&
263
322
  (typeof navigator === 'undefined' || navigator.onLine)) {
264
323
  if (this.connection) {
265
324
  this.connection.close();
@@ -276,6 +335,13 @@ class PubSub {
276
335
  isConnectionOpen() {
277
336
  return !!(this.connection && this.connection.readyState === this.WebSocket.OPEN);
278
337
  }
338
+ isConnectionConnecting() {
339
+ return !!(this.connection &&
340
+ this.connection.readyState === this.WebSocket.CONNECTING);
341
+ }
342
+ isConnection() {
343
+ return this.isConnectionOpen() || this.isConnectionConnecting();
344
+ }
279
345
  _subscribeQuery(query, callbacks) {
280
346
  let isNewSubscription = false;
281
347
  if (!this.subscriptions.queries.hasOwnProperty(query)) {
@@ -396,101 +462,103 @@ class PubSub {
396
462
  uid: name,
397
463
  });
398
464
  }
399
- updateArray(oldArr, update) {
465
+ updateArray(current, update) {
400
466
  if (Array.isArray(update)) {
401
467
  const newArr = [...update];
402
- if (oldArr.length === 0) {
403
- oldArr.splice(0, 0, ...newArr);
468
+ if (current.length === 0) {
469
+ // This will happen on the first update from a subscribe.
470
+ current.splice(0, 0, ...newArr);
404
471
  return;
405
472
  }
406
473
  const idMap = {};
407
- const Entity = this.nymph.getEntityClass('Entity');
474
+ const newEntities = [];
475
+ while (current.length) {
476
+ const first = current.shift();
477
+ if (!first) {
478
+ continue;
479
+ }
480
+ const guid = first.guid;
481
+ if (!guid) {
482
+ newEntities.push(first);
483
+ continue;
484
+ }
485
+ idMap[guid] = first;
486
+ }
408
487
  for (let i = 0; i < newArr.length; i++) {
409
488
  const entity = newArr[i];
410
- if (entity instanceof Entity && entity.guid != null) {
411
- idMap[entity.guid] = i;
489
+ const guid = entity.guid;
490
+ if (guid == null) {
491
+ continue;
412
492
  }
413
- }
414
- const remove = [];
415
- for (let k in oldArr) {
416
- if (typeof k === 'number' &&
417
- k <= 4294967294 &&
418
- /^0$|^[1-9]\d*$/.test(k) &&
419
- oldArr.hasOwnProperty(k)) {
420
- const guid = oldArr[k].guid;
421
- if (guid != null) {
422
- if (!idMap.hasOwnProperty(guid)) {
423
- remove.push(k);
424
- }
425
- else if (newArr[idMap[guid]].mdate !== oldArr[k].mdate) {
426
- oldArr[k].$init(newArr[idMap[guid]].toJSON());
427
- delete idMap[guid];
428
- }
429
- else {
430
- delete idMap[guid];
431
- }
432
- }
493
+ if (!idMap.hasOwnProperty(guid)) {
494
+ // It was added.
495
+ current.push(entity);
496
+ }
497
+ else if (idMap[guid].mdate !== entity.mdate) {
498
+ // It was modified.
499
+ idMap[guid].$init(entity.toJSON());
500
+ current.push(idMap[guid]);
501
+ }
502
+ else {
503
+ // Item wasn't modified.
504
+ current.push(idMap[guid]);
433
505
  }
434
506
  }
435
- remove.sort(function (a, b) {
436
- if (a > b)
437
- return -1;
438
- if (a < b)
439
- return 1;
440
- return 0;
441
- });
442
- for (let n = 0; n < remove.length; n++) {
443
- oldArr.splice(remove[n], 1);
444
- }
445
- for (let value of Object.values(idMap)) {
446
- oldArr.splice(oldArr.length, 0, newArr[value]);
447
- }
507
+ current.splice(current.length, 0, ...newEntities);
448
508
  }
449
509
  else if (update != null && update.hasOwnProperty('query')) {
450
- if ('removed' in update) {
451
- for (let i = 0; i < oldArr.length; i++) {
452
- if (oldArr[i] != null && oldArr[i].guid === update.removed) {
453
- oldArr.splice(i, 1);
454
- return;
510
+ const updates = 'multiple' in update && update.multiple ? update.messages : [update];
511
+ for (let curUpdate of updates) {
512
+ if ('removed' in curUpdate) {
513
+ for (let i = 0; i < current.length; i++) {
514
+ if (current[i] != null && current[i].guid === curUpdate.removed) {
515
+ current.splice(i, 1);
516
+ continue;
517
+ }
455
518
  }
456
519
  }
457
- }
458
- let entity = null;
459
- if ('added' in update) {
460
- for (let i = 0; i < oldArr.length; i++) {
461
- if (oldArr[i] != null && oldArr[i].guid === update.added) {
462
- entity = oldArr.splice(i, 1)[0].$init(update.data);
520
+ // Get the entity.
521
+ let entity = null;
522
+ if ('added' in curUpdate) {
523
+ // Check for it in the array already.
524
+ for (let i = 0; i < current.length; i++) {
525
+ if (current[i] != null && current[i].guid === curUpdate.added) {
526
+ entity = current.splice(i, 1)[0].$init(curUpdate.data);
527
+ }
463
528
  }
464
- }
465
- if (entity == null) {
466
- entity = this.nymph.initEntity(update.data);
467
- }
468
- }
469
- if ('updated' in update) {
470
- for (let i = 0; i < oldArr.length; i++) {
471
- if (oldArr[i] != null && oldArr[i].guid === update.updated) {
472
- entity = oldArr.splice(i, 1)[0].$init(update.data);
529
+ if (entity == null) {
530
+ // A new entity.
531
+ entity = this.nymph.initEntity(curUpdate.data);
473
532
  }
474
533
  }
475
- }
476
- const query = JSON.parse(update.query);
477
- if (entity != null) {
478
- const sort = 'sort' in query[0] ? query[0].sort : 'cdate';
479
- const reverse = query[0].hasOwnProperty('reverse')
480
- ? query[0].reverse
481
- : false;
482
- let i;
483
- if (reverse) {
484
- for (i = 0; ((oldArr[i] ?? {})[sort] ?? 0) >= (entity[sort] ?? 0) &&
485
- i < oldArr.length; i++)
486
- ;
534
+ if ('updated' in curUpdate) {
535
+ // Extract it from the array.
536
+ for (let i = 0; i < current.length; i++) {
537
+ if (current[i] != null && current[i].guid === curUpdate.updated) {
538
+ entity = current.splice(i, 1)[0].$init(curUpdate.data);
539
+ }
540
+ }
487
541
  }
488
- else {
489
- for (i = 0; ((oldArr[i] ?? {})[sort] ?? 0) < (entity[sort] ?? 0) &&
490
- i < oldArr.length; i++)
491
- ;
542
+ if (entity != null) {
543
+ const query = JSON.parse(curUpdate.query);
544
+ // Insert the entity in order.
545
+ const sort = 'sort' in query[0] ? query[0].sort : 'cdate';
546
+ const reverse = query[0].hasOwnProperty('reverse')
547
+ ? query[0].reverse
548
+ : false;
549
+ let i;
550
+ if (reverse) {
551
+ for (i = 0; ((current[i] ?? {})[sort] ?? 0) >= (entity[sort] ?? 0) &&
552
+ i < current.length; i++)
553
+ ;
554
+ }
555
+ else {
556
+ for (i = 0; ((current[i] ?? {})[sort] ?? 0) < (entity[sort] ?? 0) &&
557
+ i < current.length; i++)
558
+ ;
559
+ }
560
+ current.splice(i, 0, entity);
492
561
  }
493
- oldArr.splice(i, 0, entity);
494
562
  }
495
563
  }
496
564
  }
@@ -499,6 +567,7 @@ class PubSub {
499
567
  if (!(prop in this)) {
500
568
  throw new Error('Invalid event type.');
501
569
  }
570
+ // @ts-ignore: The callback should always be the right type here.
502
571
  this[prop].push(callback);
503
572
  return () => this.off(event, callback);
504
573
  }
@@ -507,29 +576,33 @@ class PubSub {
507
576
  if (!(prop in this)) {
508
577
  return false;
509
578
  }
579
+ // @ts-ignore: The callback should always be the right type here.
510
580
  const i = this[prop].indexOf(callback);
511
581
  if (i > -1) {
512
582
  this[prop].splice(i, 1);
513
583
  }
514
584
  return true;
515
585
  }
516
- setToken(token) {
517
- authToken = token;
586
+ authenticate(authToken, switchToken = null) {
587
+ this.authToken = authToken;
588
+ this.switchToken = switchToken;
518
589
  if (this.isConnectionOpen()) {
519
590
  this._send({
520
591
  action: 'authenticate',
521
- token: authToken,
592
+ authToken: this.authToken,
593
+ switchToken: this.switchToken,
522
594
  });
523
595
  }
524
596
  }
525
597
  }
526
- exports.default = PubSub;
527
- class PubSubSubscription {
598
+ export class PubSubSubscription {
599
+ query;
600
+ callbacks;
601
+ unsubscribe;
528
602
  constructor(query, callbacks, unsubscribe) {
529
603
  this.query = query;
530
604
  this.callbacks = callbacks;
531
605
  this.unsubscribe = unsubscribe;
532
606
  }
533
607
  }
534
- exports.PubSubSubscription = PubSubSubscription;
535
608
  //# sourceMappingURL=PubSub.js.map