@nymphjs/pubsub 1.0.0-beta.11 → 1.0.0-beta.111
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 +454 -0
- package/README.md +8 -8
- package/dist/PubSub.d.ts +77 -12
- package/dist/PubSub.js +467 -103
- package/dist/PubSub.js.map +1 -1
- package/dist/PubSub.test.js +394 -65
- package/dist/PubSub.test.js.map +1 -1
- package/dist/PubSub.types.d.ts +6 -1
- package/dist/PubSub.types.js +1 -2
- package/dist/conf/d.d.ts +23 -0
- package/dist/conf/d.js +1 -2
- package/dist/conf/defaults.d.ts +1 -1
- package/dist/conf/defaults.js +4 -4
- package/dist/conf/defaults.js.map +1 -1
- package/dist/conf/index.d.ts +2 -2
- package/dist/conf/index.js +2 -8
- package/dist/conf/index.js.map +1 -1
- package/dist/createServer.d.ts +2 -2
- package/dist/createServer.js +14 -15
- package/dist/createServer.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -27
- package/dist/index.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +23 -23
- package/src/PubSub.test.ts +467 -101
- package/src/PubSub.ts +548 -286
- package/src/PubSub.types.ts +2 -1
- package/src/conf/defaults.ts +2 -2
- package/src/conf/index.ts +2 -2
- package/src/createServer.ts +8 -8
- package/src/index.ts +4 -4
- package/tsconfig.json +5 -3
- package/typedoc.json +4 -0
package/dist/PubSub.js
CHANGED
|
@@ -1,125 +1,252 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { classNamesToEntityConstructors, } from '@nymphjs/nymph';
|
|
2
|
+
import ws from 'websocket';
|
|
3
|
+
import { difference } from 'lodash-es';
|
|
4
|
+
import { ConfigDefaults as defaults } from './conf/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* A publish/subscribe server for Nymph.
|
|
7
|
+
*
|
|
8
|
+
* Written by Hunter Perrin for SciActive.
|
|
9
|
+
*
|
|
10
|
+
* @author Hunter Perrin <hperrin@gmail.com>
|
|
11
|
+
* @copyright SciActive Inc
|
|
12
|
+
* @see http://nymph.io/
|
|
13
|
+
*/
|
|
14
|
+
export default class PubSub {
|
|
15
|
+
/**
|
|
16
|
+
* The Nymph instance.
|
|
17
|
+
*/
|
|
18
|
+
nymph;
|
|
19
|
+
/**
|
|
20
|
+
* The PubSub config.
|
|
21
|
+
*/
|
|
22
|
+
config;
|
|
23
|
+
/**
|
|
24
|
+
* The WebSocket server.
|
|
25
|
+
*/
|
|
26
|
+
server;
|
|
27
|
+
sessions = new Map();
|
|
28
|
+
querySubs = {};
|
|
29
|
+
uidSubs = {};
|
|
30
|
+
static transactionPublishes = [];
|
|
8
31
|
static initPublisher(config, nymph) {
|
|
9
|
-
const configWithDefaults = { ...
|
|
10
|
-
|
|
32
|
+
const configWithDefaults = { ...defaults, ...config };
|
|
33
|
+
const unsubscribers = [];
|
|
34
|
+
unsubscribers.push(nymph.on('beforeSaveEntity', async (enymph, entity) => {
|
|
11
35
|
const guid = entity.guid;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
36
|
+
const EntityClass = entity.constructor;
|
|
37
|
+
const etype = EntityClass.ETYPE;
|
|
38
|
+
if (!EntityClass.pubSubEnabled) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const off = enymph.on('afterSaveEntity', async (curNymph, result) => {
|
|
14
42
|
off();
|
|
43
|
+
off2();
|
|
15
44
|
if (!(await result)) {
|
|
16
45
|
return;
|
|
17
46
|
}
|
|
18
|
-
|
|
47
|
+
const payload = JSON.stringify({
|
|
19
48
|
action: 'publish',
|
|
20
49
|
event: guid == null ? 'create' : 'update',
|
|
21
50
|
guid: entity.guid,
|
|
22
51
|
entity: entity.toJSON(),
|
|
23
52
|
etype: etype,
|
|
24
|
-
})
|
|
53
|
+
});
|
|
54
|
+
this.transactionPublishes.push({
|
|
55
|
+
nymph: curNymph,
|
|
56
|
+
payload,
|
|
57
|
+
config: configWithDefaults,
|
|
58
|
+
});
|
|
59
|
+
await this.publishTransactionPublishes(curNymph);
|
|
25
60
|
});
|
|
26
|
-
|
|
27
|
-
|
|
61
|
+
const off2 = enymph.on('failedSaveEntity', async () => {
|
|
62
|
+
off();
|
|
63
|
+
off2();
|
|
64
|
+
});
|
|
65
|
+
}));
|
|
66
|
+
unsubscribers.push(nymph.on('beforeDeleteEntity', async (enymph, entity) => {
|
|
28
67
|
const guid = entity.guid;
|
|
29
|
-
const
|
|
30
|
-
const
|
|
68
|
+
const EntityClass = entity.constructor;
|
|
69
|
+
const etype = EntityClass.ETYPE;
|
|
70
|
+
if (!EntityClass.pubSubEnabled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const off = enymph.on('afterDeleteEntity', async (curNymph, result) => {
|
|
31
74
|
off();
|
|
75
|
+
off2();
|
|
32
76
|
if (!(await result)) {
|
|
33
77
|
return;
|
|
34
78
|
}
|
|
35
|
-
|
|
79
|
+
const payload = JSON.stringify({
|
|
36
80
|
action: 'publish',
|
|
37
81
|
event: 'delete',
|
|
38
82
|
guid: guid,
|
|
39
83
|
etype: etype,
|
|
40
|
-
})
|
|
84
|
+
});
|
|
85
|
+
this.transactionPublishes.push({
|
|
86
|
+
nymph: curNymph,
|
|
87
|
+
payload,
|
|
88
|
+
config: configWithDefaults,
|
|
89
|
+
});
|
|
90
|
+
await this.publishTransactionPublishes(curNymph);
|
|
41
91
|
});
|
|
42
|
-
|
|
43
|
-
|
|
92
|
+
const off2 = enymph.on('failedDeleteEntity', async () => {
|
|
93
|
+
off();
|
|
94
|
+
off2();
|
|
95
|
+
});
|
|
96
|
+
}));
|
|
97
|
+
unsubscribers.push(nymph.on('beforeDeleteEntityByID', async (enymph, guid, className) => {
|
|
44
98
|
try {
|
|
45
|
-
const
|
|
46
|
-
const
|
|
99
|
+
const EntityClass = enymph.getEntityClass(className ?? 'Entity');
|
|
100
|
+
const etype = EntityClass.ETYPE;
|
|
101
|
+
if (!EntityClass.pubSubEnabled) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const off = enymph.on('afterDeleteEntityByID', async (curNymph, result) => {
|
|
47
105
|
off();
|
|
106
|
+
off2();
|
|
48
107
|
if (!(await result)) {
|
|
49
108
|
return;
|
|
50
109
|
}
|
|
51
|
-
|
|
110
|
+
const payload = JSON.stringify({
|
|
52
111
|
action: 'publish',
|
|
53
112
|
event: 'delete',
|
|
54
113
|
guid: guid,
|
|
55
114
|
etype: etype,
|
|
56
|
-
})
|
|
115
|
+
});
|
|
116
|
+
this.transactionPublishes.push({
|
|
117
|
+
nymph: curNymph,
|
|
118
|
+
payload,
|
|
119
|
+
config: configWithDefaults,
|
|
120
|
+
});
|
|
121
|
+
await this.publishTransactionPublishes(curNymph);
|
|
122
|
+
});
|
|
123
|
+
const off2 = enymph.on('failedDeleteEntityByID', async () => {
|
|
124
|
+
off();
|
|
125
|
+
off2();
|
|
57
126
|
});
|
|
58
127
|
}
|
|
59
128
|
catch (e) {
|
|
60
129
|
return;
|
|
61
130
|
}
|
|
62
|
-
});
|
|
63
|
-
nymph.on('beforeNewUID', async (
|
|
64
|
-
const off =
|
|
131
|
+
}));
|
|
132
|
+
unsubscribers.push(nymph.on('beforeNewUID', async (enymph, name) => {
|
|
133
|
+
const off = enymph.on('afterNewUID', async (curNymph, result) => {
|
|
65
134
|
off();
|
|
135
|
+
off2();
|
|
66
136
|
const value = await result;
|
|
67
137
|
if (value == null) {
|
|
68
138
|
return;
|
|
69
139
|
}
|
|
70
|
-
|
|
140
|
+
const payload = JSON.stringify({
|
|
71
141
|
action: 'publish',
|
|
72
142
|
event: 'newUID',
|
|
73
143
|
name: name,
|
|
74
144
|
value: value,
|
|
75
|
-
})
|
|
145
|
+
});
|
|
146
|
+
this.transactionPublishes.push({
|
|
147
|
+
nymph: curNymph,
|
|
148
|
+
payload,
|
|
149
|
+
config: configWithDefaults,
|
|
150
|
+
});
|
|
151
|
+
await this.publishTransactionPublishes(curNymph);
|
|
76
152
|
});
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
153
|
+
const off2 = enymph.on('failedNewUID', async () => {
|
|
154
|
+
off();
|
|
155
|
+
off2();
|
|
156
|
+
});
|
|
157
|
+
}));
|
|
158
|
+
unsubscribers.push(nymph.on('beforeSetUID', async (enymph, name, value) => {
|
|
159
|
+
const off = enymph.on('afterSetUID', async (curNymph, result) => {
|
|
80
160
|
off();
|
|
161
|
+
off2();
|
|
81
162
|
if (!(await result)) {
|
|
82
163
|
return;
|
|
83
164
|
}
|
|
84
|
-
|
|
165
|
+
const payload = JSON.stringify({
|
|
85
166
|
action: 'publish',
|
|
86
167
|
event: 'setUID',
|
|
87
168
|
name: name,
|
|
88
169
|
value: value,
|
|
89
|
-
})
|
|
170
|
+
});
|
|
171
|
+
this.transactionPublishes.push({
|
|
172
|
+
nymph: curNymph,
|
|
173
|
+
payload,
|
|
174
|
+
config: configWithDefaults,
|
|
175
|
+
});
|
|
176
|
+
await this.publishTransactionPublishes(curNymph);
|
|
90
177
|
});
|
|
91
|
-
|
|
92
|
-
nymph.on('beforeRenameUID', async (nymph, oldName, newName) => {
|
|
93
|
-
const off = nymph.on('afterRenameUID', async (_nymph, result) => {
|
|
178
|
+
const off2 = enymph.on('failedSetUID', async () => {
|
|
94
179
|
off();
|
|
180
|
+
off2();
|
|
181
|
+
});
|
|
182
|
+
}));
|
|
183
|
+
unsubscribers.push(nymph.on('beforeRenameUID', async (enymph, oldName, newName) => {
|
|
184
|
+
const off = enymph.on('afterRenameUID', async (curNymph, result) => {
|
|
185
|
+
off();
|
|
186
|
+
off2();
|
|
95
187
|
if (!(await result)) {
|
|
96
188
|
return;
|
|
97
189
|
}
|
|
98
|
-
|
|
190
|
+
const payload = JSON.stringify({
|
|
99
191
|
action: 'publish',
|
|
100
192
|
event: 'renameUID',
|
|
101
193
|
oldName: oldName,
|
|
102
194
|
newName: newName,
|
|
103
|
-
})
|
|
195
|
+
});
|
|
196
|
+
this.transactionPublishes.push({
|
|
197
|
+
nymph: curNymph,
|
|
198
|
+
payload,
|
|
199
|
+
config: configWithDefaults,
|
|
200
|
+
});
|
|
201
|
+
await this.publishTransactionPublishes(curNymph);
|
|
104
202
|
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
203
|
+
const off2 = enymph.on('failedRenameUID', async () => {
|
|
204
|
+
off();
|
|
205
|
+
off2();
|
|
206
|
+
});
|
|
207
|
+
}));
|
|
208
|
+
unsubscribers.push(nymph.on('beforeDeleteUID', async (enymph, name) => {
|
|
209
|
+
const off = enymph.on('afterDeleteUID', async (curNymph, result) => {
|
|
108
210
|
off();
|
|
211
|
+
off2();
|
|
109
212
|
if (!(await result)) {
|
|
110
213
|
return;
|
|
111
214
|
}
|
|
112
|
-
|
|
215
|
+
const payload = JSON.stringify({
|
|
113
216
|
action: 'publish',
|
|
114
217
|
event: 'deleteUID',
|
|
115
218
|
name: name,
|
|
116
|
-
})
|
|
219
|
+
});
|
|
220
|
+
this.transactionPublishes.push({
|
|
221
|
+
nymph: curNymph,
|
|
222
|
+
payload,
|
|
223
|
+
config: configWithDefaults,
|
|
224
|
+
});
|
|
225
|
+
await this.publishTransactionPublishes(curNymph);
|
|
117
226
|
});
|
|
118
|
-
|
|
227
|
+
const off2 = enymph.on('failedDeleteUID', async () => {
|
|
228
|
+
off();
|
|
229
|
+
off2();
|
|
230
|
+
});
|
|
231
|
+
}));
|
|
232
|
+
unsubscribers.push(nymph.on('afterCommitTransaction', async (enymph, _name, result) => {
|
|
233
|
+
if (result) {
|
|
234
|
+
await this.publishTransactionPublishes(enymph);
|
|
235
|
+
}
|
|
236
|
+
}));
|
|
237
|
+
unsubscribers.push(nymph.on('afterRollbackTransaction', async (enymph) => {
|
|
238
|
+
this.removeTransactionPublishes(enymph);
|
|
239
|
+
}));
|
|
240
|
+
return () => {
|
|
241
|
+
for (let unsubscribe of unsubscribers) {
|
|
242
|
+
unsubscribe();
|
|
243
|
+
}
|
|
244
|
+
this.transactionPublishes = [];
|
|
245
|
+
};
|
|
119
246
|
}
|
|
120
247
|
static publish(message, config) {
|
|
121
248
|
for (let host of config.entries ?? []) {
|
|
122
|
-
const client = new
|
|
249
|
+
const client = new ws.client();
|
|
123
250
|
client.on('connectFailed', (error) => {
|
|
124
251
|
if (config.logger) {
|
|
125
252
|
config.logger('error', new Date().toISOString(), `Publish connection failed. (${error.toString()}, ${host})`);
|
|
@@ -139,12 +266,48 @@ class PubSub {
|
|
|
139
266
|
client.connect(host, 'nymph');
|
|
140
267
|
}
|
|
141
268
|
}
|
|
269
|
+
static isOrIsDescendant(parent, child) {
|
|
270
|
+
let check = child;
|
|
271
|
+
while (check) {
|
|
272
|
+
if (check === parent) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
check = check.parent;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
static async publishTransactionPublishes(nymph) {
|
|
280
|
+
if (await nymph.inTransaction()) {
|
|
281
|
+
// This instance is still in a transaction, so nothing gets published yet.
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
this.transactionPublishes = (await Promise.all(this.transactionPublishes.map(async (publish) => {
|
|
285
|
+
// Check that this instance is a parent and the instance is not in a
|
|
286
|
+
// transaction.
|
|
287
|
+
if (!this.isOrIsDescendant(nymph, publish.nymph) ||
|
|
288
|
+
(await publish.nymph.inTransaction())) {
|
|
289
|
+
return publish;
|
|
290
|
+
}
|
|
291
|
+
this.publish(publish.payload, publish.config);
|
|
292
|
+
return null;
|
|
293
|
+
}))).filter((value) => value != null);
|
|
294
|
+
}
|
|
295
|
+
static removeTransactionPublishes(nymph) {
|
|
296
|
+
this.transactionPublishes = this.transactionPublishes.filter((publish) => {
|
|
297
|
+
if (this.isOrIsDescendant(nymph, publish.nymph)) {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Initialize Nymph PubSub.
|
|
305
|
+
*
|
|
306
|
+
* @param config The PubSub configuration.
|
|
307
|
+
*/
|
|
142
308
|
constructor(config, nymph, server) {
|
|
143
|
-
this.sessions = new Map();
|
|
144
|
-
this.querySubs = {};
|
|
145
|
-
this.uidSubs = {};
|
|
146
309
|
this.nymph = nymph;
|
|
147
|
-
this.config = { ...
|
|
310
|
+
this.config = { ...defaults, ...config };
|
|
148
311
|
this.server = server;
|
|
149
312
|
this.server.on('request', this.handleRequest.bind(this));
|
|
150
313
|
}
|
|
@@ -153,6 +316,7 @@ class PubSub {
|
|
|
153
316
|
}
|
|
154
317
|
handleRequest(request) {
|
|
155
318
|
if (!this.config.originIsAllowed(request.origin)) {
|
|
319
|
+
// Make sure we only accept requests from an allowed origin
|
|
156
320
|
request.reject();
|
|
157
321
|
this.config.logger('log', new Date().toISOString(), 'Client from origin ' + request.origin + ' was kicked by the bouncer.');
|
|
158
322
|
return;
|
|
@@ -169,6 +333,9 @@ class PubSub {
|
|
|
169
333
|
this.onClose(connection, description);
|
|
170
334
|
});
|
|
171
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Handle a message from a client.
|
|
338
|
+
*/
|
|
172
339
|
async onMessage(from, msg) {
|
|
173
340
|
if (msg.type !== 'utf8') {
|
|
174
341
|
throw new Error("This server doesn't accept binary messages.");
|
|
@@ -191,6 +358,9 @@ class PubSub {
|
|
|
191
358
|
break;
|
|
192
359
|
}
|
|
193
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Clean up after users who leave.
|
|
363
|
+
*/
|
|
194
364
|
onClose(conn, description) {
|
|
195
365
|
this.config.logger('log', new Date().toISOString(), `Client skedaddled. (${description}, ${conn.remoteAddress})`);
|
|
196
366
|
let mess = 0;
|
|
@@ -209,6 +379,7 @@ class PubSub {
|
|
|
209
379
|
}
|
|
210
380
|
else {
|
|
211
381
|
if (this.config.broadcastCounts) {
|
|
382
|
+
// Notify clients of the subscription count.
|
|
212
383
|
for (let key of curClients.keys()) {
|
|
213
384
|
const curData = curClients.get(key);
|
|
214
385
|
if (curData && curData.count) {
|
|
@@ -234,6 +405,7 @@ class PubSub {
|
|
|
234
405
|
}
|
|
235
406
|
else {
|
|
236
407
|
if (this.config.broadcastCounts) {
|
|
408
|
+
// Notify clients of the subscription count.
|
|
237
409
|
for (const key of curClients.keys()) {
|
|
238
410
|
const curData = curClients.get(key);
|
|
239
411
|
if (curData && curData.count) {
|
|
@@ -259,34 +431,56 @@ class PubSub {
|
|
|
259
431
|
onError(conn, e) {
|
|
260
432
|
this.config.logger('error', new Date().toISOString(), `An error occured. (${e.message}, ${conn.remoteAddress})`);
|
|
261
433
|
}
|
|
434
|
+
/**
|
|
435
|
+
* Handle an authentication from a client.
|
|
436
|
+
*/
|
|
262
437
|
handleAuthentication(from, data) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
438
|
+
// Save the user's auth token in session storage.
|
|
439
|
+
const authToken = data.authToken;
|
|
440
|
+
const switchToken = data.switchToken;
|
|
441
|
+
if (authToken != null) {
|
|
442
|
+
this.sessions.set(from, { authToken, switchToken });
|
|
266
443
|
}
|
|
267
444
|
else if (this.sessions.has(from)) {
|
|
268
445
|
this.sessions.delete(from);
|
|
269
446
|
}
|
|
270
447
|
}
|
|
448
|
+
/**
|
|
449
|
+
* Handle a subscribe or unsubscribe from a client.
|
|
450
|
+
*/
|
|
271
451
|
async handleSubscription(from, data) {
|
|
272
|
-
|
|
273
|
-
|
|
452
|
+
try {
|
|
453
|
+
if ('query' in data && data.query != null) {
|
|
454
|
+
// Request is for a query.
|
|
455
|
+
await this.handleSubscriptionQuery(from, data);
|
|
456
|
+
}
|
|
457
|
+
else if ('uid' in data &&
|
|
458
|
+
data.uid != null &&
|
|
459
|
+
typeof data.uid == 'string') {
|
|
460
|
+
// Request is for a UID.
|
|
461
|
+
await this.handleSubscriptionUid(from, data);
|
|
462
|
+
}
|
|
274
463
|
}
|
|
275
|
-
|
|
276
|
-
data.
|
|
277
|
-
|
|
278
|
-
|
|
464
|
+
catch (e) {
|
|
465
|
+
if ('query' in data && data.query != null) {
|
|
466
|
+
from.sendUTF(JSON.stringify({ query: data.query, error: e.message }));
|
|
467
|
+
}
|
|
468
|
+
else if ('uid' in data && data.uid != null) {
|
|
469
|
+
from.sendUTF(JSON.stringify({ uid: data.uid, error: e.message }));
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
from.sendUTF(JSON.stringify({ error: e.message }));
|
|
473
|
+
}
|
|
279
474
|
}
|
|
280
475
|
}
|
|
476
|
+
/**
|
|
477
|
+
* Handle a subscribe or unsubscribe for a query from a client.
|
|
478
|
+
*/
|
|
281
479
|
async handleSubscriptionQuery(from, data, qrefParent) {
|
|
282
|
-
let args;
|
|
283
|
-
let EntityClass;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
EntityClass = this.nymph.getEntityClass(args[0].class);
|
|
287
|
-
}
|
|
288
|
-
catch (e) {
|
|
289
|
-
return;
|
|
480
|
+
let args = JSON.parse(data.query);
|
|
481
|
+
let EntityClass = this.nymph.getEntityClass(args[0].class);
|
|
482
|
+
if (!EntityClass.restEnabled) {
|
|
483
|
+
throw new Error('Not accessible.');
|
|
290
484
|
}
|
|
291
485
|
const etype = EntityClass.ETYPE;
|
|
292
486
|
const serialArgs = JSON.stringify(args);
|
|
@@ -294,11 +488,22 @@ class PubSub {
|
|
|
294
488
|
const options = {
|
|
295
489
|
...clientOptions,
|
|
296
490
|
class: EntityClass,
|
|
297
|
-
return: '
|
|
491
|
+
return: 'entity',
|
|
298
492
|
source: 'client',
|
|
299
493
|
};
|
|
494
|
+
// Find qref queries.
|
|
300
495
|
const qrefQueries = this.findQRefQueries(clientOptions, ...selectors);
|
|
496
|
+
// Check that all qref queries are accessible classes.
|
|
497
|
+
for (const qrefQuery of qrefQueries) {
|
|
498
|
+
const args = qrefQuery;
|
|
499
|
+
const EntityClass = this.nymph.getEntityClass(args[0].class);
|
|
500
|
+
if (!EntityClass.restEnabled) {
|
|
501
|
+
throw new Error('Not accessible.');
|
|
502
|
+
}
|
|
503
|
+
}
|
|
301
504
|
if (data.action === 'subscribe') {
|
|
505
|
+
// Client is subscribing to a query.
|
|
506
|
+
// First subscribe to qrefQueries, giving this one as a reference.
|
|
302
507
|
for (const qrefQuery of qrefQueries) {
|
|
303
508
|
await this.handleSubscriptionQuery(from, {
|
|
304
509
|
action: 'subscribe',
|
|
@@ -308,23 +513,39 @@ class PubSub {
|
|
|
308
513
|
query: serialArgs,
|
|
309
514
|
});
|
|
310
515
|
}
|
|
516
|
+
// Now subscribe to this query.
|
|
311
517
|
if (!(etype in this.querySubs)) {
|
|
312
518
|
this.querySubs[etype] = {};
|
|
313
519
|
}
|
|
314
520
|
if (!(serialArgs in this.querySubs[etype])) {
|
|
315
521
|
this.querySubs[etype][serialArgs] = new Map();
|
|
316
522
|
}
|
|
317
|
-
let
|
|
523
|
+
let authToken = null;
|
|
524
|
+
let switchToken = null;
|
|
318
525
|
if (this.sessions.has(from)) {
|
|
319
|
-
|
|
526
|
+
const session = this.sessions.get(from);
|
|
527
|
+
authToken = session?.authToken;
|
|
528
|
+
switchToken = session?.switchToken;
|
|
320
529
|
}
|
|
321
530
|
const nymph = this.nymph.clone();
|
|
322
|
-
if (nymph.tilmeld != null &&
|
|
323
|
-
const user = await nymph.tilmeld.extractToken(
|
|
324
|
-
if (user) {
|
|
325
|
-
|
|
531
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
532
|
+
const user = await nymph.tilmeld.extractToken(authToken);
|
|
533
|
+
if (user && user.enabled) {
|
|
534
|
+
if (switchToken != null) {
|
|
535
|
+
const switchUser = await nymph.tilmeld.extractToken(switchToken);
|
|
536
|
+
if (switchUser) {
|
|
537
|
+
// Log in the switchUser for access controls.
|
|
538
|
+
await nymph.tilmeld.fillSession(switchUser);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// Log in the user for access controls.
|
|
543
|
+
await nymph.tilmeld.fillSession(user);
|
|
544
|
+
}
|
|
326
545
|
}
|
|
327
546
|
}
|
|
547
|
+
let entities = [];
|
|
548
|
+
let sendEntities = false;
|
|
328
549
|
const existingSub = this.querySubs[etype][serialArgs].get(from);
|
|
329
550
|
if (existingSub) {
|
|
330
551
|
if (qrefParent) {
|
|
@@ -336,21 +557,42 @@ class PubSub {
|
|
|
336
557
|
if (data.count && !existingSub.count) {
|
|
337
558
|
existingSub.count = true;
|
|
338
559
|
}
|
|
560
|
+
sendEntities = existingSub.direct;
|
|
561
|
+
if (sendEntities) {
|
|
562
|
+
entities = existingSub.current.length
|
|
563
|
+
? await nymph.getEntities({
|
|
564
|
+
class: EntityClass,
|
|
565
|
+
source: 'client',
|
|
566
|
+
}, { type: '|', guid: existingSub.current })
|
|
567
|
+
: [];
|
|
568
|
+
}
|
|
339
569
|
}
|
|
340
570
|
else {
|
|
571
|
+
entities = await nymph.getEntities(options, ...selectors);
|
|
341
572
|
this.querySubs[etype][serialArgs].set(from, {
|
|
342
|
-
current:
|
|
573
|
+
current: entities.map((e) => e.guid),
|
|
343
574
|
query: data.query,
|
|
344
575
|
qrefParents: qrefParent ? [qrefParent] : [],
|
|
345
576
|
direct: !qrefParent,
|
|
346
577
|
count: !!data.count,
|
|
347
578
|
});
|
|
579
|
+
sendEntities = !qrefParent;
|
|
580
|
+
}
|
|
581
|
+
if (sendEntities) {
|
|
582
|
+
// Notify the client of the current value.
|
|
583
|
+
from.sendUTF(JSON.stringify({
|
|
584
|
+
query: data.query,
|
|
585
|
+
set: true,
|
|
586
|
+
data: entities,
|
|
587
|
+
}));
|
|
348
588
|
}
|
|
349
|
-
if (nymph.tilmeld != null &&
|
|
589
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
590
|
+
// Clear the user that was temporarily logged in.
|
|
350
591
|
nymph.tilmeld.clearSession();
|
|
351
592
|
}
|
|
352
593
|
this.config.logger('log', new Date().toISOString(), `Client subscribed to a query! (${serialArgs}, ${from.remoteAddress})`);
|
|
353
594
|
if (this.config.broadcastCounts) {
|
|
595
|
+
// Notify clients of the subscription count.
|
|
354
596
|
const count = this.querySubs[etype][serialArgs].size;
|
|
355
597
|
for (let key of this.querySubs[etype][serialArgs].keys()) {
|
|
356
598
|
const curData = this.querySubs[etype][serialArgs].get(key);
|
|
@@ -364,6 +606,8 @@ class PubSub {
|
|
|
364
606
|
}
|
|
365
607
|
}
|
|
366
608
|
if (data.action === 'unsubscribe') {
|
|
609
|
+
// Client is unsubscribing from a query.
|
|
610
|
+
// First unsubscribe from qrefQueries.
|
|
367
611
|
for (const qrefQuery of qrefQueries) {
|
|
368
612
|
await this.handleSubscriptionQuery(from, {
|
|
369
613
|
action: 'unsubscribe',
|
|
@@ -373,6 +617,7 @@ class PubSub {
|
|
|
373
617
|
query: serialArgs,
|
|
374
618
|
});
|
|
375
619
|
}
|
|
620
|
+
// Now unsubscribe from this query.
|
|
376
621
|
if (!(etype in this.querySubs)) {
|
|
377
622
|
return;
|
|
378
623
|
}
|
|
@@ -398,6 +643,7 @@ class PubSub {
|
|
|
398
643
|
this.config.logger('log', new Date().toISOString(), `Client unsubscribed from a query! (${serialArgs}, ${from.remoteAddress})`);
|
|
399
644
|
const count = this.querySubs[etype][serialArgs].size;
|
|
400
645
|
if (count === 0) {
|
|
646
|
+
// No more subscribed clients.
|
|
401
647
|
delete this.querySubs[etype][serialArgs];
|
|
402
648
|
if (Object.keys(this.querySubs[etype]).length === 0) {
|
|
403
649
|
delete this.querySubs[etype];
|
|
@@ -405,6 +651,7 @@ class PubSub {
|
|
|
405
651
|
return;
|
|
406
652
|
}
|
|
407
653
|
if (this.config.broadcastCounts) {
|
|
654
|
+
// Notify clients of the subscription count.
|
|
408
655
|
for (let key of this.querySubs[etype][serialArgs].keys()) {
|
|
409
656
|
const curData = this.querySubs[etype][serialArgs].get(key);
|
|
410
657
|
if (curData && curData.count) {
|
|
@@ -417,16 +664,55 @@ class PubSub {
|
|
|
417
664
|
}
|
|
418
665
|
}
|
|
419
666
|
}
|
|
667
|
+
/**
|
|
668
|
+
* Handle a subscribe or unsubscribe for a UID from a client.
|
|
669
|
+
*/
|
|
420
670
|
async handleSubscriptionUid(from, data) {
|
|
421
671
|
if (data.action === 'subscribe') {
|
|
672
|
+
// Client is subscribing to a UID.
|
|
422
673
|
if (!(data.uid in this.uidSubs)) {
|
|
423
674
|
this.uidSubs[data.uid] = new Map();
|
|
424
675
|
}
|
|
425
676
|
this.uidSubs[data['uid']].set(from, {
|
|
426
677
|
count: !!data.count,
|
|
427
678
|
});
|
|
679
|
+
let authToken = null;
|
|
680
|
+
let switchToken = null;
|
|
681
|
+
if (this.sessions.has(from)) {
|
|
682
|
+
const session = this.sessions.get(from);
|
|
683
|
+
authToken = session?.authToken;
|
|
684
|
+
switchToken = session?.switchToken;
|
|
685
|
+
}
|
|
686
|
+
const nymph = this.nymph.clone();
|
|
687
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
688
|
+
const user = await nymph.tilmeld.extractToken(authToken);
|
|
689
|
+
if (user && user.enabled) {
|
|
690
|
+
if (switchToken != null) {
|
|
691
|
+
const switchUser = await nymph.tilmeld.extractToken(switchToken);
|
|
692
|
+
if (switchUser) {
|
|
693
|
+
// Log in the switchUser for access controls.
|
|
694
|
+
await nymph.tilmeld.fillSession(switchUser);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
else {
|
|
698
|
+
// Log in the user for access controls.
|
|
699
|
+
await nymph.tilmeld.fillSession(user);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
// Notify the client of the current value.
|
|
704
|
+
from.sendUTF(JSON.stringify({
|
|
705
|
+
uid: data.uid,
|
|
706
|
+
set: true,
|
|
707
|
+
data: await this.nymph.getUID(data.uid),
|
|
708
|
+
}));
|
|
709
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
710
|
+
// Clear the user that was temporarily logged in.
|
|
711
|
+
nymph.tilmeld.clearSession();
|
|
712
|
+
}
|
|
428
713
|
this.config.logger('log', new Date().toISOString(), `Client subscribed to a UID! (${data.uid}, ${from.remoteAddress})`);
|
|
429
714
|
if (this.config.broadcastCounts) {
|
|
715
|
+
// Notify clients of the subscription count.
|
|
430
716
|
const count = this.uidSubs[data.uid].size;
|
|
431
717
|
for (let key of this.uidSubs[data.uid].keys()) {
|
|
432
718
|
const curData = this.uidSubs[data.uid].get(key);
|
|
@@ -440,6 +726,7 @@ class PubSub {
|
|
|
440
726
|
}
|
|
441
727
|
}
|
|
442
728
|
if (data.action === 'unsubscribe') {
|
|
729
|
+
// Client is unsubscribing from a UID.
|
|
443
730
|
if (!(data.uid in this.uidSubs)) {
|
|
444
731
|
return;
|
|
445
732
|
}
|
|
@@ -450,10 +737,12 @@ class PubSub {
|
|
|
450
737
|
this.config.logger('log', new Date().toISOString(), `Client unsubscribed from a UID! (${data.uid}, ${from.remoteAddress})`);
|
|
451
738
|
const count = this.uidSubs[data.uid].size;
|
|
452
739
|
if (count === 0) {
|
|
740
|
+
// No more subscribed clients.
|
|
453
741
|
delete this.uidSubs[data.uid];
|
|
454
742
|
return;
|
|
455
743
|
}
|
|
456
744
|
if (this.config.broadcastCounts) {
|
|
745
|
+
// Notify clients of the subscription count.
|
|
457
746
|
for (let key of this.uidSubs[data.uid].keys()) {
|
|
458
747
|
const curData = this.uidSubs[data.uid].get(key);
|
|
459
748
|
if (curData && curData.count) {
|
|
@@ -466,6 +755,9 @@ class PubSub {
|
|
|
466
755
|
}
|
|
467
756
|
}
|
|
468
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Handle a publish from a client.
|
|
760
|
+
*/
|
|
469
761
|
async handlePublish(from, msg, data) {
|
|
470
762
|
if ('guid' in data &&
|
|
471
763
|
typeof data.guid === 'string' &&
|
|
@@ -474,7 +766,9 @@ class PubSub {
|
|
|
474
766
|
((data.event === 'create' || data.event === 'update') &&
|
|
475
767
|
'entity' in data &&
|
|
476
768
|
data.entity != null))) {
|
|
769
|
+
// Publish is an entity.
|
|
477
770
|
await this.handlePublishEntity(from, data);
|
|
771
|
+
// Relay the publish to other servers.
|
|
478
772
|
this.relay(msg);
|
|
479
773
|
}
|
|
480
774
|
if ((('name' in data && typeof data.name === 'string') ||
|
|
@@ -483,10 +777,15 @@ class PubSub {
|
|
|
483
777
|
'newName' in data &&
|
|
484
778
|
typeof data.newName === 'string')) &&
|
|
485
779
|
['newUID', 'setUID', 'renameUID', 'deleteUID'].indexOf(data.event) !== -1) {
|
|
780
|
+
// Publish is a UID.
|
|
486
781
|
await this.handlePublishUid(from, data);
|
|
782
|
+
// Relay the publish to other servers.
|
|
487
783
|
this.relay(msg);
|
|
488
784
|
}
|
|
489
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Handle an entity publish from a client.
|
|
788
|
+
*/
|
|
490
789
|
async handlePublishEntity(from, data) {
|
|
491
790
|
this.config.logger('log', new Date().toISOString(), `Received an entity publish! (${data.guid}, ${data.event}, ${from.remoteAddress})`);
|
|
492
791
|
const etype = data.etype;
|
|
@@ -497,6 +796,7 @@ class PubSub {
|
|
|
497
796
|
const curClients = this.querySubs[etype][curQuery];
|
|
498
797
|
const updatedClients = new Set();
|
|
499
798
|
if (data.event === 'delete' || data.event === 'update') {
|
|
799
|
+
// Check if it is in any client's currents.
|
|
500
800
|
try {
|
|
501
801
|
for (const curClient of curClients.keys()) {
|
|
502
802
|
const curData = curClients.get(curClient);
|
|
@@ -514,6 +814,7 @@ class PubSub {
|
|
|
514
814
|
}
|
|
515
815
|
}
|
|
516
816
|
if ((data.event === 'create' || data.event === 'update') && data.entity) {
|
|
817
|
+
// Check if it matches the query.
|
|
517
818
|
try {
|
|
518
819
|
const [clientOptions, ...selectors] = JSON.parse(curQuery);
|
|
519
820
|
const qrefQueries = this.findQRefQueries(clientOptions, ...selectors);
|
|
@@ -528,18 +829,23 @@ class PubSub {
|
|
|
528
829
|
const DataEntityClass = this.nymph.getEntityClass(data.entity.class);
|
|
529
830
|
if (EntityClass.ETYPE === DataEntityClass.ETYPE &&
|
|
530
831
|
(qrefQueries.length ||
|
|
531
|
-
this.nymph.driver.checkData(entityData, entitySData, selectors, data.guid, data.entity?.tags ?? []))) {
|
|
832
|
+
this.nymph.driver.checkData(EntityClass.ETYPE, entityData, entitySData, selectors, data.guid, data.entity?.tags ?? []))) {
|
|
833
|
+
// It either matches the query, or there are qref queries.
|
|
532
834
|
for (let curClient of curClients.keys()) {
|
|
533
835
|
if (updatedClients.has(curClient)) {
|
|
836
|
+
// The user was already notified. (Of an update.)
|
|
534
837
|
continue;
|
|
535
838
|
}
|
|
536
839
|
const curData = curClients.get(curClient);
|
|
537
840
|
if (!curData) {
|
|
538
841
|
continue;
|
|
539
842
|
}
|
|
843
|
+
// If there are qref queries, we need to dive into the user's
|
|
844
|
+
// current data and translate them before running checkData.
|
|
540
845
|
if (qrefQueries.length) {
|
|
541
846
|
const translatedSelectors = this.translateQRefSelectors(curClient, selectors);
|
|
542
|
-
if (!this.nymph.driver.checkData(entityData, entitySData, translatedSelectors, data.guid, data.entity?.tags ?? [])) {
|
|
847
|
+
if (!this.nymph.driver.checkData(EntityClass.ETYPE, entityData, entitySData, translatedSelectors, data.guid, data.entity?.tags ?? [])) {
|
|
848
|
+
// The query doesn't match when the qref queries are filled.
|
|
543
849
|
continue;
|
|
544
850
|
}
|
|
545
851
|
}
|
|
@@ -554,8 +860,10 @@ class PubSub {
|
|
|
554
860
|
}
|
|
555
861
|
}
|
|
556
862
|
async updateClient(curClient, curData, data) {
|
|
863
|
+
// Update currents list.
|
|
557
864
|
let current;
|
|
558
|
-
let
|
|
865
|
+
let authToken;
|
|
866
|
+
let switchToken;
|
|
559
867
|
const nymph = this.nymph.clone();
|
|
560
868
|
try {
|
|
561
869
|
const [clientOptions, ...clientSelectors] = JSON.parse(curData.query);
|
|
@@ -566,14 +874,26 @@ class PubSub {
|
|
|
566
874
|
source: 'client',
|
|
567
875
|
skipAc: false,
|
|
568
876
|
};
|
|
569
|
-
const selectors =
|
|
877
|
+
const selectors = classNamesToEntityConstructors(nymph, clientSelectors, true);
|
|
570
878
|
if (this.sessions.has(curClient)) {
|
|
571
|
-
|
|
879
|
+
const session = this.sessions.get(curClient);
|
|
880
|
+
authToken = session?.authToken;
|
|
881
|
+
switchToken = session?.switchToken;
|
|
572
882
|
}
|
|
573
|
-
if (nymph.tilmeld != null &&
|
|
574
|
-
const user = await nymph.tilmeld.extractToken(
|
|
575
|
-
if (user) {
|
|
576
|
-
|
|
883
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
884
|
+
const user = await nymph.tilmeld.extractToken(authToken);
|
|
885
|
+
if (user && user.enabled) {
|
|
886
|
+
if (switchToken != null) {
|
|
887
|
+
const switchUser = await nymph.tilmeld.extractToken(switchToken);
|
|
888
|
+
if (switchUser) {
|
|
889
|
+
// Log in the switchUser for access controls.
|
|
890
|
+
await nymph.tilmeld.fillSession(switchUser);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
else {
|
|
894
|
+
// Log in the user for access controls.
|
|
895
|
+
await nymph.tilmeld.fillSession(user);
|
|
896
|
+
}
|
|
577
897
|
}
|
|
578
898
|
}
|
|
579
899
|
current = await nymph.getEntities(options, ...selectors);
|
|
@@ -584,10 +904,11 @@ class PubSub {
|
|
|
584
904
|
}
|
|
585
905
|
const entityMap = Object.fromEntries(current.map((entity) => [entity.guid, entity]));
|
|
586
906
|
const currentGuids = current.map((entity) => entity.guid ?? '');
|
|
587
|
-
const removed =
|
|
588
|
-
const added =
|
|
907
|
+
const removed = difference(curData.current, currentGuids);
|
|
908
|
+
const added = difference(currentGuids, curData.current);
|
|
589
909
|
if (curData.direct) {
|
|
590
910
|
for (let guid of removed) {
|
|
911
|
+
// Notify subscriber.
|
|
591
912
|
this.config.logger('log', new Date().toISOString(), `Notifying client of removal! (${curClient.remoteAddress})`);
|
|
592
913
|
curClient.sendUTF(JSON.stringify({
|
|
593
914
|
query: curData.query,
|
|
@@ -596,6 +917,7 @@ class PubSub {
|
|
|
596
917
|
}
|
|
597
918
|
for (let guid of added) {
|
|
598
919
|
const entity = entityMap[guid];
|
|
920
|
+
// Notify client.
|
|
599
921
|
this.config.logger('log', new Date().toISOString(), `Notifying client of new match! (${curClient.remoteAddress})`);
|
|
600
922
|
if (typeof entity.updateDataProtection === 'function') {
|
|
601
923
|
entity.updateDataProtection();
|
|
@@ -608,6 +930,7 @@ class PubSub {
|
|
|
608
930
|
}
|
|
609
931
|
if (data.event === 'update' && data.guid in entityMap) {
|
|
610
932
|
const entity = entityMap[data.guid];
|
|
933
|
+
// Notify subscriber.
|
|
611
934
|
this.config.logger('log', new Date().toISOString(), `Notifying client of update! (${curClient.remoteAddress})`);
|
|
612
935
|
if (typeof entity.updateDataProtection === 'function') {
|
|
613
936
|
entity.updateDataProtection();
|
|
@@ -619,11 +942,14 @@ class PubSub {
|
|
|
619
942
|
}));
|
|
620
943
|
}
|
|
621
944
|
}
|
|
945
|
+
// Update curData.
|
|
622
946
|
curData.current = currentGuids;
|
|
623
|
-
if (nymph.tilmeld != null &&
|
|
947
|
+
if (nymph.tilmeld != null && authToken != null) {
|
|
948
|
+
// Clear the user that was temporarily logged in.
|
|
624
949
|
nymph.tilmeld.clearSession();
|
|
625
950
|
}
|
|
626
951
|
if ((removed.length || added.length) && curData.qrefParents.length) {
|
|
952
|
+
// All qref parents need to be rerun.
|
|
627
953
|
for (const qrefParent of curData.qrefParents) {
|
|
628
954
|
const subData = this.querySubs[qrefParent.etype][qrefParent.query].get(curClient);
|
|
629
955
|
if (subData) {
|
|
@@ -632,6 +958,9 @@ class PubSub {
|
|
|
632
958
|
}
|
|
633
959
|
}
|
|
634
960
|
}
|
|
961
|
+
/**
|
|
962
|
+
* Handle a UID publish from a client.
|
|
963
|
+
*/
|
|
635
964
|
async handlePublishUid(from, data) {
|
|
636
965
|
this.config.logger('log', new Date().toISOString(), `Received a UID publish! (${'name' in data ? data.name : `${data.oldName} => ${data.newName}`}, ${data.event}, ${from.remoteAddress})`);
|
|
637
966
|
const names = [data.name, data.oldName].filter((name) => name != null);
|
|
@@ -668,13 +997,16 @@ class PubSub {
|
|
|
668
997
|
}
|
|
669
998
|
}
|
|
670
999
|
}
|
|
1000
|
+
/**
|
|
1001
|
+
* Relay publish data to other servers.
|
|
1002
|
+
*/
|
|
671
1003
|
relay(message) {
|
|
672
1004
|
if (message.type !== 'utf8') {
|
|
673
1005
|
this.config.logger('error', new Date().toISOString(), `Can't relay non UTF8 message.`);
|
|
674
1006
|
return;
|
|
675
1007
|
}
|
|
676
1008
|
for (let host of this.config.relays) {
|
|
677
|
-
const client = new
|
|
1009
|
+
const client = new ws.client();
|
|
678
1010
|
client.on('connectFailed', (error) => {
|
|
679
1011
|
this.config.logger('error', new Date().toISOString(), `Relay connection failed. (${error.toString()}, ${host})`);
|
|
680
1012
|
});
|
|
@@ -718,6 +1050,10 @@ class PubSub {
|
|
|
718
1050
|
}
|
|
719
1051
|
return qrefQueries;
|
|
720
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* This translates qref selectors into ref selectors using the "current" GUID
|
|
1055
|
+
* list in the existing subscriptions.
|
|
1056
|
+
*/
|
|
721
1057
|
translateQRefSelectors(client, selectors) {
|
|
722
1058
|
const newSelectors = [];
|
|
723
1059
|
for (const curSelector of selectors) {
|
|
@@ -740,27 +1076,55 @@ class PubSub {
|
|
|
740
1076
|
const name = tmpArr[i][0];
|
|
741
1077
|
const [qrefOptions, ...qrefSelectors] = tmpArr[i][1];
|
|
742
1078
|
const query = JSON.stringify(tmpArr[i][1]);
|
|
743
|
-
const QrefEntityClass =
|
|
1079
|
+
const QrefEntityClass = qrefOptions.class
|
|
744
1080
|
? this.nymph.getEntityClass(qrefOptions.class)
|
|
745
|
-
:
|
|
1081
|
+
: this.nymph.getEntityClass('Entity');
|
|
746
1082
|
const data = this.querySubs[QrefEntityClass.ETYPE][query].get(client);
|
|
747
1083
|
if (data) {
|
|
748
1084
|
const guids = data.current;
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1085
|
+
if (newKey === '!ref') {
|
|
1086
|
+
// Insert the qref results as a not ref clause.
|
|
1087
|
+
let newValue;
|
|
1088
|
+
const oldValue = newSelector[newKey];
|
|
1089
|
+
if (oldValue == null ||
|
|
1090
|
+
(Array.isArray(oldValue) && !oldValue.length)) {
|
|
1091
|
+
newValue = [];
|
|
1092
|
+
}
|
|
1093
|
+
else if (Array.isArray(oldValue) &&
|
|
1094
|
+
!Array.isArray(oldValue[0])) {
|
|
1095
|
+
newValue = [oldValue];
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
newValue = oldValue;
|
|
1099
|
+
}
|
|
1100
|
+
newValue.push(...guids.map((guid) => [name, guid]));
|
|
1101
|
+
newSelector[newKey] = newValue;
|
|
756
1102
|
}
|
|
757
1103
|
else {
|
|
758
|
-
|
|
1104
|
+
// Insert the qref results as an "or" selector clause with a ref
|
|
1105
|
+
// selector.
|
|
1106
|
+
let newValue;
|
|
1107
|
+
const oldValue = newSelector['selector'];
|
|
1108
|
+
delete newSelector[key];
|
|
1109
|
+
if (oldValue == null ||
|
|
1110
|
+
(Array.isArray(oldValue) && !oldValue.length)) {
|
|
1111
|
+
newValue = [];
|
|
1112
|
+
}
|
|
1113
|
+
else if (!Array.isArray(oldValue)) {
|
|
1114
|
+
newValue = [oldValue];
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
newValue = oldValue;
|
|
1118
|
+
}
|
|
1119
|
+
newValue.push({
|
|
1120
|
+
type: '|',
|
|
1121
|
+
ref: guids.map((guid) => [name, guid]),
|
|
1122
|
+
});
|
|
1123
|
+
newSelector['selector'] = newValue;
|
|
759
1124
|
}
|
|
760
|
-
newValue.push(...guids.map((guid) => [name, guid]));
|
|
761
|
-
newSelector[newKey] = newValue;
|
|
762
1125
|
}
|
|
763
1126
|
else {
|
|
1127
|
+
// Can't translate, so put the original back in.
|
|
764
1128
|
if (!newSelector[key]) {
|
|
765
1129
|
newSelector[key] = [];
|
|
766
1130
|
}
|
|
@@ -785,6 +1149,7 @@ class PubSub {
|
|
|
785
1149
|
newSelector[key].push(...tmpArr);
|
|
786
1150
|
}
|
|
787
1151
|
else {
|
|
1152
|
+
// @ts-ignore: ts doesn't know what value is here.
|
|
788
1153
|
newSelector[key] = value;
|
|
789
1154
|
}
|
|
790
1155
|
}
|
|
@@ -793,5 +1158,4 @@ class PubSub {
|
|
|
793
1158
|
return newSelectors;
|
|
794
1159
|
}
|
|
795
1160
|
}
|
|
796
|
-
exports.default = PubSub;
|
|
797
1161
|
//# sourceMappingURL=PubSub.js.map
|