@qikdev/sdk 1.0.10 → 1.0.13
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/dist/main.js +1294 -1229
- package/package.json +1 -1
- package/src/api/qik.access.js +177 -201
- package/src/api/qik.api.js +55 -28
- package/src/api/qik.auth.js +181 -113
- package/src/api/qik.cache.js +29 -7
- package/src/api/qik.content.js +275 -147
- package/src/api/qik.core.js +19 -28
- package/src/api/qik.files.js +48 -38
- package/src/api/qik.filter.js +148 -207
- package/src/api/qik.geo.js +59 -42
- package/src/api/qik.socket.js +228 -62
- package/src/api/qik.storage.js +17 -8
- package/src/api/qik.system.js +6 -5
- package/src/api/qik.utils.js +244 -199
- package/src/version.js +1 -1
package/src/api/qik.socket.js
CHANGED
|
@@ -3,12 +3,37 @@ import { EventDispatcher } from './qik.utils.js';
|
|
|
3
3
|
///////////////////////////////////////////////////
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Realtime updates over a WebSocket. Subscribe to a channel to be told when something changes, then
|
|
7
|
+
* re-read what you need with sdk.content.get / list: content messages say THAT a record changed, not what it
|
|
8
|
+
* now contains (the read applies the viewer's field permissions). Needs a signed-in user or application
|
|
9
|
+
* token; the socket connects on first use, reconnects automatically with back-off, resubscribes its channels
|
|
10
|
+
* after reconnecting, and reconnects when the user switches organisation. Changes made from this same
|
|
11
|
+
* browser window are not echoed back to it. Subscriptions are permission-checked by the server: a channel
|
|
12
|
+
* you may not use is simply never delivered.
|
|
13
|
+
* Channels (use these names; don't invent others):
|
|
14
|
+
* '<recordID>:edit' and '<recordID>:delete' — that record saved or deleted (needs view access to it);
|
|
15
|
+
* '<type or definition key>:create' / ':edit' / ':delete' — any record of that type, e.g. 'profile:edit' (needs viewany or editany on it);
|
|
16
|
+
* '<recordID>:restore' and '<type or definition key>:restore' — restored from the trash;
|
|
17
|
+
* '<eventID>:checkins', '<profileID>:checkins', '<eventID>:assignments' — check-ins and roster assignments;
|
|
18
|
+
* '<recordID>:comment' — comments posted on a record (the full comment);
|
|
19
|
+
* '<type or definition key>:definition' — that type's fields changed;
|
|
20
|
+
* 'notifications:<your persona, profile or user id>' and 'presence:<your organisation id>'.
|
|
21
|
+
* Each message is { channel, message }. For create and edit, message is
|
|
22
|
+
* { user: { name, _id, persona }, item: { _id, meta: { type, definition, uhash, updated, deleted } }, truncated: true }:
|
|
23
|
+
* compare item.meta.uhash with your copy and refetch when it differs. For delete and restore, message.item is just the id.
|
|
24
|
+
* Connection events: sdk.socket.addEventListener('connected' | 'disconnected' | 'error' | 'message', handler);
|
|
25
|
+
* 'message' receives every incoming message on every channel.
|
|
8
26
|
* @name socket
|
|
9
27
|
* @constructor
|
|
10
28
|
* @hideconstructor
|
|
11
29
|
* @param {QikAPI} qik A reference to the parent instance of the QikCore module. This module is usually created by a QikCore instance that passes itself in as the first argument.
|
|
30
|
+
* @example
|
|
31
|
+
* const channel = await sdk.socket.channel(`${recordID}:edit`);
|
|
32
|
+
* channel.addEventListener('message', async ({ message }) => {
|
|
33
|
+
* record = await sdk.content.get(recordID);
|
|
34
|
+
* });
|
|
35
|
+
* // when finished
|
|
36
|
+
* channel.destroy();
|
|
12
37
|
*/
|
|
13
38
|
|
|
14
39
|
const QikSocket = function(qik, mode) {
|
|
@@ -34,9 +59,19 @@ const QikSocket = function(qik, mode) {
|
|
|
34
59
|
|
|
35
60
|
let socket;
|
|
36
61
|
let timer;
|
|
62
|
+
let connecting;
|
|
63
|
+
let reconnectTimer;
|
|
64
|
+
let reconnectAttempts = 0;
|
|
65
|
+
let pendingSubscribes = [];
|
|
66
|
+
let flushScheduled = false;
|
|
37
67
|
let buckets = {};
|
|
38
68
|
const channels = {};
|
|
39
69
|
|
|
70
|
+
// Reconnect backoff: 1s, 2s, 4s ... capped, with jitter so a fleet of tabs
|
|
71
|
+
// that lost the socket together doesn't return in lockstep.
|
|
72
|
+
const RECONNECT_BASE_MS = 1000;
|
|
73
|
+
const RECONNECT_MAX_MS = 30000;
|
|
74
|
+
|
|
40
75
|
|
|
41
76
|
|
|
42
77
|
//Create a new dispatcher
|
|
@@ -53,19 +88,27 @@ const QikSocket = function(qik, mode) {
|
|
|
53
88
|
|
|
54
89
|
|
|
55
90
|
/**
|
|
56
|
-
* @description
|
|
57
|
-
* This
|
|
91
|
+
* @description Subscribes to a channel and returns a listener object for it, connecting first if needed.
|
|
92
|
+
* This is the easiest way to use the socket. Calling channel() again with the same key returns the same
|
|
93
|
+
* object, so it is shared by everything on the page that uses that key.
|
|
94
|
+
* The returned object has addEventListener('message', handler), removeEventListener('message', handler),
|
|
95
|
+
* removeAllListeners() and destroy(). Handlers receive { channel, message }. destroy() unsubscribes the
|
|
96
|
+
* channel and removes every handler on it — including other components' — so when the key may be shared,
|
|
97
|
+
* remove just your own handler instead.
|
|
58
98
|
* @alias socket.channel
|
|
59
|
-
* @param
|
|
99
|
+
* @param {String} key The channel name, e.g. `${recordID}:edit` or 'profile:create' (see the channel list above).
|
|
100
|
+
* @returns {Promise<Object>} The channel's listener object. If nobody is signed in the socket can't connect, and the returned object never receives messages.
|
|
60
101
|
* @example
|
|
61
|
-
* const
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* }
|
|
102
|
+
* const channel = await sdk.socket.channel('profile:create');
|
|
103
|
+
* async function onCreated({ message }) {
|
|
104
|
+
* const profile = await sdk.content.get(message.item._id);
|
|
105
|
+
* }
|
|
106
|
+
* channel.addEventListener('message', onCreated);
|
|
65
107
|
*
|
|
66
|
-
* //
|
|
67
|
-
*
|
|
68
|
-
*
|
|
108
|
+
* // Later: stop listening (keeps the channel for anyone else using it)
|
|
109
|
+
* channel.removeEventListener('message', onCreated);
|
|
110
|
+
* // Or: unsubscribe and remove all listeners on this channel
|
|
111
|
+
* channel.destroy();
|
|
69
112
|
*/
|
|
70
113
|
service.channel = async function(key) {
|
|
71
114
|
if (buckets[key]) {
|
|
@@ -138,17 +181,14 @@ const QikSocket = function(qik, mode) {
|
|
|
138
181
|
function socketOpened(event) {
|
|
139
182
|
service.debug ? console.log("[socket] Connection open", event) : null;
|
|
140
183
|
service.connected = true;
|
|
184
|
+
reconnectAttempts = 0;
|
|
141
185
|
|
|
142
186
|
dispatcher.dispatch('connected', event);
|
|
143
187
|
|
|
144
188
|
// Replay every recorded subscription — frames broadcast before the
|
|
145
189
|
// connection opened were dropped, and reconnects need them resent.
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
action: 'subscribe',
|
|
149
|
-
channel,
|
|
150
|
-
});
|
|
151
|
-
});
|
|
190
|
+
// One batched frame: the server writes them in a single update.
|
|
191
|
+
queueSubscribe(Object.keys(channels));
|
|
152
192
|
|
|
153
193
|
// startHeartbeat();
|
|
154
194
|
}
|
|
@@ -167,12 +207,28 @@ const QikSocket = function(qik, mode) {
|
|
|
167
207
|
socket = null;
|
|
168
208
|
|
|
169
209
|
if(service.autoreconnect) {
|
|
170
|
-
|
|
210
|
+
scheduleReconnect();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function scheduleReconnect() {
|
|
215
|
+
if (reconnectTimer) {
|
|
216
|
+
return;
|
|
171
217
|
}
|
|
218
|
+
|
|
219
|
+
const delay = Math.min(RECONNECT_MAX_MS, RECONNECT_BASE_MS * Math.pow(2, reconnectAttempts));
|
|
220
|
+
const jittered = Math.round(delay * (0.5 + Math.random() / 2));
|
|
221
|
+
reconnectAttempts++;
|
|
222
|
+
|
|
223
|
+
service.debug ? console.log(`[socket] reconnecting in ${jittered}ms`) : null;
|
|
224
|
+
reconnectTimer = setTimeout(function() {
|
|
225
|
+
reconnectTimer = null;
|
|
226
|
+
service.reconnect();
|
|
227
|
+
}, jittered);
|
|
172
228
|
}
|
|
173
229
|
|
|
174
230
|
function socketError(error) {
|
|
175
|
-
console.log('[event] socketError',
|
|
231
|
+
console.log('[event] socketError', error);
|
|
176
232
|
service.debug ? console.log("[socket] Error", error) : null;
|
|
177
233
|
dispatcher.dispatch('error', error);
|
|
178
234
|
}
|
|
@@ -187,6 +243,14 @@ const QikSocket = function(qik, mode) {
|
|
|
187
243
|
if (eventData.channel) {
|
|
188
244
|
dispatcher.dispatch(eventData.channel, eventData);
|
|
189
245
|
}
|
|
246
|
+
|
|
247
|
+
// The server says this person's access changed (a role or access pass
|
|
248
|
+
// they hold was edited). Reload the session so what they can see
|
|
249
|
+
// follows; a failed reload keeps them signed in as they were.
|
|
250
|
+
const message = eventData.message;
|
|
251
|
+
if (message && message.type === 'session' && message.action === 'changed') {
|
|
252
|
+
Promise.resolve(qik.auth.sync({ keepOnError: true })).catch(function() {});
|
|
253
|
+
}
|
|
190
254
|
}
|
|
191
255
|
|
|
192
256
|
async function broadcast(data) {
|
|
@@ -203,28 +267,34 @@ const QikSocket = function(qik, mode) {
|
|
|
203
267
|
|
|
204
268
|
///////////////////////////////////////////////////
|
|
205
269
|
|
|
270
|
+
// Resolves once the socket is open. Waits on the 'connected' event rather
|
|
271
|
+
// than polling: a polling timer kept running forever if the socket was
|
|
272
|
+
// closed before it opened, and added up to a second to every connect.
|
|
206
273
|
function wait() {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return resolve(socket);
|
|
211
|
-
}
|
|
274
|
+
if (service.connected) {
|
|
275
|
+
return Promise.resolve(socket);
|
|
276
|
+
}
|
|
212
277
|
|
|
213
|
-
|
|
278
|
+
return new Promise(function(resolve) {
|
|
279
|
+
function opened() {
|
|
280
|
+
service.removeEventListener('connected', opened);
|
|
281
|
+
resolve(socket);
|
|
214
282
|
}
|
|
215
283
|
|
|
216
|
-
|
|
284
|
+
service.addEventListener('connected', opened);
|
|
217
285
|
})
|
|
218
286
|
}
|
|
219
287
|
|
|
220
288
|
///////////////////////////////////////////////////
|
|
221
289
|
|
|
222
290
|
/**
|
|
223
|
-
* @description
|
|
291
|
+
* @description Opens the socket connection, first refreshing the access token if it has expired. Resolves once
|
|
292
|
+
* connected. Calls made while a connection is being opened share it. channel() connects for you, so this is
|
|
293
|
+
* rarely needed. Resolves with nothing (and doesn't connect) when there's no access token.
|
|
224
294
|
* @alias socket.connect
|
|
295
|
+
* @returns {Promise<WebSocket|undefined>} The underlying WebSocket once open, or undefined without a token.
|
|
225
296
|
* @example
|
|
226
|
-
*
|
|
227
|
-
*
|
|
297
|
+
* await sdk.socket.connect();
|
|
228
298
|
*/
|
|
229
299
|
service.connect = async function() {
|
|
230
300
|
if (socket) {
|
|
@@ -237,6 +307,31 @@ const QikSocket = function(qik, mode) {
|
|
|
237
307
|
return socket;
|
|
238
308
|
}
|
|
239
309
|
|
|
310
|
+
// Callers arriving while we refresh the token share this attempt
|
|
311
|
+
// instead of each opening their own socket.
|
|
312
|
+
if (connecting) {
|
|
313
|
+
return connecting;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
connecting = openSocket();
|
|
317
|
+
try {
|
|
318
|
+
return await connecting;
|
|
319
|
+
} finally {
|
|
320
|
+
connecting = null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async function openSocket() {
|
|
325
|
+
// The socket authenticates once, with the token in its URL. Tokens
|
|
326
|
+
// expire (3 h), and a reconnect after the connection went idle would
|
|
327
|
+
// otherwise present a stale one: the server refuses to register it
|
|
328
|
+
// and the socket silently receives nothing. Refresh first if needed.
|
|
329
|
+
try {
|
|
330
|
+
await qik.auth.ensureValidToken();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
service.debug ? console.log('[socket] - could not refresh token before connecting', err) : null;
|
|
333
|
+
}
|
|
334
|
+
|
|
240
335
|
const accessToken = qik.auth.getCurrentToken();
|
|
241
336
|
|
|
242
337
|
if (!accessToken) {
|
|
@@ -256,11 +351,12 @@ const QikSocket = function(qik, mode) {
|
|
|
256
351
|
|
|
257
352
|
|
|
258
353
|
/**
|
|
259
|
-
* @description
|
|
354
|
+
* @description Connects again after the connection was lost or closed; does nothing if already connected.
|
|
355
|
+
* Channels that were subscribed are subscribed again once the connection opens.
|
|
260
356
|
* @alias socket.reconnect
|
|
357
|
+
* @returns {Promise<WebSocket|undefined>} As for connect(); undefined when already connected.
|
|
261
358
|
* @example
|
|
262
|
-
*
|
|
263
|
-
*
|
|
359
|
+
* await sdk.socket.reconnect();
|
|
264
360
|
*/
|
|
265
361
|
service.reconnect = async function() {
|
|
266
362
|
if(service.connected) {
|
|
@@ -278,11 +374,12 @@ const QikSocket = function(qik, mode) {
|
|
|
278
374
|
///////////////////////////////////////////////////
|
|
279
375
|
|
|
280
376
|
/**
|
|
281
|
-
* @description
|
|
377
|
+
* @description Closes the connection without reconnecting automatically. Channel subscriptions are
|
|
378
|
+
* remembered and resent by a later connect() or reconnect(). Also available as sdk.socket.disconnect().
|
|
282
379
|
* @alias socket.close
|
|
380
|
+
* @returns {Promise<undefined>}
|
|
283
381
|
* @example
|
|
284
|
-
*
|
|
285
|
-
*
|
|
382
|
+
* await sdk.socket.close();
|
|
286
383
|
*/
|
|
287
384
|
service.close = async function() {
|
|
288
385
|
if (!socket) {
|
|
@@ -290,6 +387,11 @@ const QikSocket = function(qik, mode) {
|
|
|
290
387
|
return;
|
|
291
388
|
}
|
|
292
389
|
|
|
390
|
+
if (reconnectTimer) {
|
|
391
|
+
clearTimeout(reconnectTimer);
|
|
392
|
+
reconnectTimer = null;
|
|
393
|
+
}
|
|
394
|
+
|
|
293
395
|
const previousSetting = service.autoreconnect;
|
|
294
396
|
service.autoreconnect = false;
|
|
295
397
|
socket.close();
|
|
@@ -302,17 +404,68 @@ const QikSocket = function(qik, mode) {
|
|
|
302
404
|
service.autoreconnect = previousSetting;
|
|
303
405
|
}
|
|
304
406
|
|
|
407
|
+
/**
|
|
408
|
+
* @description Same as close(): closes the connection without reconnecting automatically.
|
|
409
|
+
* @alias socket.disconnect
|
|
410
|
+
* @returns {Promise<undefined>}
|
|
411
|
+
* @example
|
|
412
|
+
* await sdk.socket.disconnect();
|
|
413
|
+
*/
|
|
305
414
|
service.disconnect = service.close;
|
|
306
415
|
|
|
307
416
|
///////////////////////////////////////////////////
|
|
308
417
|
|
|
418
|
+
// Subscribes requested in the same tick go out as one frame. A page opening
|
|
419
|
+
// several channels at once used to send one frame each; the server then
|
|
420
|
+
// updated the same connection row from several places at the same moment
|
|
421
|
+
// and those requests queued behind each other for seconds.
|
|
422
|
+
function queueSubscribe(list) {
|
|
423
|
+
list.forEach(function(channel) {
|
|
424
|
+
if (!pendingSubscribes.includes(channel)) {
|
|
425
|
+
pendingSubscribes.push(channel);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
if (flushScheduled) {
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
flushScheduled = true;
|
|
434
|
+
setTimeout(flushSubscribes, 0);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function flushSubscribes() {
|
|
438
|
+
flushScheduled = false;
|
|
439
|
+
const list = pendingSubscribes.filter(function(channel) {
|
|
440
|
+
return channels[channel];
|
|
441
|
+
});
|
|
442
|
+
pendingSubscribes = [];
|
|
443
|
+
|
|
444
|
+
if (!list.length) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (list.length === 1) {
|
|
449
|
+
broadcast({ action: 'subscribe', channel: list[0] });
|
|
450
|
+
} else {
|
|
451
|
+
broadcast({ action: 'subscribe', channels: list });
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
///////////////////////////////////////////////////
|
|
456
|
+
|
|
309
457
|
/**
|
|
310
|
-
* @description
|
|
458
|
+
* @description Asks the server to send this channel's messages to this connection. Takes no callback: listen with
|
|
459
|
+
* sdk.socket.addEventListener(channel, handler), which receives { channel, message }. Only works while the
|
|
460
|
+
* socket is connected (otherwise it logs and does nothing) — channel() handles connecting, subscribing and
|
|
461
|
+
* listening in one step and is usually the better choice. Subscriptions made in the same moment are sent together.
|
|
311
462
|
* @alias socket.subscribe
|
|
312
|
-
* @param
|
|
463
|
+
* @param {String} channel The channel name.
|
|
464
|
+
* @returns {Promise<undefined>}
|
|
313
465
|
* @example
|
|
314
|
-
*
|
|
315
|
-
*
|
|
466
|
+
* await sdk.socket.connect();
|
|
467
|
+
* sdk.socket.subscribe(`${eventID}:checkins`);
|
|
468
|
+
* sdk.socket.addEventListener(`${eventID}:checkins`, ({ message }) => refresh());
|
|
316
469
|
*/
|
|
317
470
|
service.subscribe = async function(channel) {
|
|
318
471
|
if (!socket) {
|
|
@@ -320,12 +473,8 @@ const QikSocket = function(qik, mode) {
|
|
|
320
473
|
return;
|
|
321
474
|
}
|
|
322
475
|
|
|
323
|
-
broadcast({
|
|
324
|
-
action: 'subscribe',
|
|
325
|
-
channel,
|
|
326
|
-
})
|
|
327
|
-
|
|
328
476
|
channels[channel] = true;
|
|
477
|
+
queueSubscribe([channel]);
|
|
329
478
|
|
|
330
479
|
dispatcher.dispatch('subscribe', channel)
|
|
331
480
|
service.debug ? console.log(`[socket] - subscribed to ${channel}`) : null;
|
|
@@ -334,12 +483,13 @@ const QikSocket = function(qik, mode) {
|
|
|
334
483
|
///////////////////////////////////////////////////
|
|
335
484
|
|
|
336
485
|
/**
|
|
337
|
-
* @description
|
|
486
|
+
* @description Tells the server to stop sending this channel's messages. Listeners added with addEventListener
|
|
487
|
+
* stay attached (remove them yourself). Only works while connected.
|
|
338
488
|
* @alias socket.unsubscribe
|
|
339
|
-
* @param
|
|
489
|
+
* @param {String} channel The channel name.
|
|
490
|
+
* @returns {Promise<undefined>}
|
|
340
491
|
* @example
|
|
341
|
-
*
|
|
342
|
-
*
|
|
492
|
+
* sdk.socket.unsubscribe(`${eventID}:checkins`);
|
|
343
493
|
*/
|
|
344
494
|
service.unsubscribe = async function(channel) {
|
|
345
495
|
if (!socket) {
|
|
@@ -360,31 +510,47 @@ const QikSocket = function(qik, mode) {
|
|
|
360
510
|
|
|
361
511
|
///////////////////////////////////////////////////
|
|
362
512
|
|
|
363
|
-
// Reconnect the socket whenever
|
|
364
|
-
//
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
// organisation's events. Reconnecting re-authenticates with
|
|
369
|
-
|
|
513
|
+
// Reconnect the socket whenever who is signed in changes: signing in,
|
|
514
|
+
// signing out, switching organisation or switching person. The WebSocket
|
|
515
|
+
// authenticates with an access token at connect time, so the connection
|
|
516
|
+
// stays bound to whoever was signed in when it opened — channels opened
|
|
517
|
+
// before sign-in stay refused, and an organisation switch keeps streaming
|
|
518
|
+
// the previous organisation's events. Reconnecting re-authenticates with
|
|
519
|
+
// the new token and replays every channel.
|
|
520
|
+
//
|
|
521
|
+
// The stored user is { session, token }; who someone is lives on the
|
|
522
|
+
// session. Only these ids are read — sessions and tokens are untouched.
|
|
523
|
+
function identityOf(user) {
|
|
524
|
+
const session = user && user.session;
|
|
525
|
+
if (!session) {
|
|
526
|
+
return '';
|
|
527
|
+
}
|
|
528
|
+
return [
|
|
529
|
+
qik.utils.id(session.organisation),
|
|
530
|
+
qik.utils.id(session.persona),
|
|
531
|
+
qik.utils.id(session._id),
|
|
532
|
+
].join('|');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
let currentIdentity = identityOf(qik.auth.getCurrentUser());
|
|
370
536
|
|
|
371
537
|
qik.auth.addEventListener('change', async function(user) {
|
|
372
|
-
const
|
|
538
|
+
const identity = identityOf(user);
|
|
373
539
|
|
|
374
|
-
// Ignore auth changes that
|
|
375
|
-
// access token refreshes) so we don't churn the connection
|
|
376
|
-
if (
|
|
540
|
+
// Ignore auth changes that leave the same person signed in (e.g.
|
|
541
|
+
// routine access token refreshes) so we don't churn the connection.
|
|
542
|
+
if (identity === currentIdentity) {
|
|
377
543
|
return;
|
|
378
544
|
}
|
|
379
545
|
|
|
380
|
-
|
|
546
|
+
currentIdentity = identity;
|
|
381
547
|
|
|
382
548
|
// Nothing to reconnect if the socket was never opened.
|
|
383
549
|
if (!socket) {
|
|
384
550
|
return;
|
|
385
551
|
}
|
|
386
552
|
|
|
387
|
-
service.debug ? console.log('[socket] -
|
|
553
|
+
service.debug ? console.log('[socket] - signed-in identity changed, reconnecting') : null;
|
|
388
554
|
await service.close();
|
|
389
555
|
await service.reconnect();
|
|
390
556
|
});
|
package/src/api/qik.storage.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Where the SDK keeps the signed-in session. Not a namespace of its own: sdk.auth creates one adapter,
|
|
3
|
+
* available as sdk.auth.storageAdapter, and uses it for getCurrentUser, set and logout.
|
|
4
|
+
* LocalStorageAdapter (the default, despite its name) keeps the session in memory only, so it is lost on page
|
|
5
|
+
* reload unless your app saves and restores it with sdk.auth.set. CookieStorageAdapter (useHttpOnlyCookies)
|
|
6
|
+
* keeps the session details in memory and leaves the tokens to httpOnly cookies set by the server.
|
|
3
7
|
* @module QikStorage
|
|
4
8
|
*/
|
|
5
9
|
|
|
@@ -70,7 +74,7 @@ class StorageAdapter {
|
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
/**
|
|
73
|
-
*
|
|
77
|
+
* The default adapter. Despite the name it does not use window.localStorage: the session is held in memory.
|
|
74
78
|
*/
|
|
75
79
|
class LocalStorageAdapter extends StorageAdapter {
|
|
76
80
|
constructor() {
|
|
@@ -111,7 +115,8 @@ class LocalStorageAdapter extends StorageAdapter {
|
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
/**
|
|
114
|
-
*
|
|
118
|
+
* Adapter used with useHttpOnlyCookies: session details in memory, tokens in httpOnly cookies the server sets
|
|
119
|
+
* (so getAccessToken, getRefreshToken and getTokenExpiry always return null).
|
|
115
120
|
*/
|
|
116
121
|
class CookieStorageAdapter extends StorageAdapter {
|
|
117
122
|
constructor(options = {}) {
|
|
@@ -281,11 +286,15 @@ class CookieStorageAdapter extends StorageAdapter {
|
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
/**
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
* @param {
|
|
287
|
-
* @param {
|
|
288
|
-
* @
|
|
289
|
+
* Creates the adapter sdk.auth uses: a CookieStorageAdapter when useHttpOnlyCookies is set and the browser
|
|
290
|
+
* accepts cookies, otherwise a LocalStorageAdapter (in memory).
|
|
291
|
+
* @param {Object} [options] Configuration.
|
|
292
|
+
* @param {Boolean} [options.useHttpOnlyCookies] Use cookie mode.
|
|
293
|
+
* @param {Object} [options.cookieConfig] Cookie settings: { domain, secure, sameSite }.
|
|
294
|
+
* @returns {StorageAdapter} The adapter.
|
|
295
|
+
* @example
|
|
296
|
+
* const adapter = createStorageAdapter({ useHttpOnlyCookies: false });
|
|
297
|
+
* adapter.setUser(session);
|
|
289
298
|
*/
|
|
290
299
|
export function createStorageAdapter(options = {}) {
|
|
291
300
|
if (options.useHttpOnlyCookies) {
|
package/src/api/qik.system.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
///////////////////////////////////////////////////
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* This module provides a number of helper functions for the system
|
|
4
|
+
* Platform reference data that isn't specific to an organisation.
|
|
6
5
|
* @alias system
|
|
7
6
|
* @constructor
|
|
8
7
|
* @hideconstructor
|
|
@@ -21,10 +20,12 @@ export default function(qik) {
|
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* @alias system.countries
|
|
24
|
-
* @description
|
|
25
|
-
* and
|
|
23
|
+
* @description Retrieves every country (GET /system/countries) with its ISO codes, name, phone calling
|
|
24
|
+
* codes and currencies, e.g. to fill a country picker. Loaded once and shared by later calls.
|
|
25
|
+
* @returns {Promise<Array<Object>>} Countries, each with alpha2 (e.g. 'AU'), alpha3, name, countryCallingCodes (e.g. ['+61']) and currencies.
|
|
26
26
|
* @example
|
|
27
|
-
* const countries = await sdk.system.countries()
|
|
27
|
+
* const countries = await sdk.system.countries();
|
|
28
|
+
* const options = countries.map((country) => ({ title: country.name, value: country.alpha2 }));
|
|
28
29
|
*/
|
|
29
30
|
service.countries = async function() {
|
|
30
31
|
|