react-native-mosquito-transport 0.0.51 → 0.0.52
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/package.json +3 -3
- package/src/helpers/variables.js +0 -1
- package/src/index.js +181 -72
- package/src/products/auth/accessor.js +24 -17
- package/src/products/auth/index.js +13 -7
- package/src/products/database/index.js +179 -37
- package/src/products/http_callable/index.js +3 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mosquito-transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"description": "React native javascript sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"guard-object": "^1.1.4",
|
|
42
42
|
"poke-object": "^1.0.1",
|
|
43
43
|
"simplify-error": "^1.0.1",
|
|
44
|
-
"socket.io-client": "^4.8.
|
|
44
|
+
"socket.io-client": "^4.8.3",
|
|
45
45
|
"subscription-listener": "^1.1.3",
|
|
46
46
|
"tweetnacl": "^1.0.3"
|
|
47
47
|
},
|
|
@@ -51,4 +51,4 @@
|
|
|
51
51
|
"react-native-get-random-values": "*",
|
|
52
52
|
"react-native-sha256": "*"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|
package/src/helpers/variables.js
CHANGED
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import 'react-native-get-random-values';
|
|
2
2
|
import { deserializeE2E, listenReachableServer, serializeE2E } from "./helpers/peripherals";
|
|
3
|
-
import { awaitStore, releaseCacheStore } from "./helpers/utils";
|
|
3
|
+
import { awaitReachableServer, awaitStore, releaseCacheStore } from "./helpers/utils";
|
|
4
4
|
import { CacheStore, Scoped } from "./helpers/variables";
|
|
5
5
|
import { MTCollection, batchWrite, onCollectionConnect, trySendPendingWrite } from "./products/database";
|
|
6
6
|
import { MTStorage } from "./products/storage";
|
|
7
|
-
import { ServerReachableListener
|
|
8
|
-
import { initTokenRefresher, listenToken, listenTokenReady, triggerAuthToken } from "./products/auth/accessor";
|
|
7
|
+
import { ServerReachableListener } from "./helpers/listeners";
|
|
8
|
+
import { awaitRefreshToken, initTokenRefresher, listenToken, listenTokenReady, triggerAuthToken } from "./products/auth/accessor";
|
|
9
9
|
import { TIMESTAMP, DOCUMENT_EXTRACTION, FIND_GEO_JSON, GEO_JSON, TIMESTAMP_OFFSET } from "./products/database/types";
|
|
10
10
|
import { mfetch } from "./products/http_callable";
|
|
11
11
|
import { io } from "socket.io-client";
|
|
@@ -73,34 +73,49 @@ class RNMT {
|
|
|
73
73
|
_from_base: true
|
|
74
74
|
}
|
|
75
75
|
});
|
|
76
|
+
|
|
76
77
|
let connectionIte = 0;
|
|
78
|
+
let chainedPromise;
|
|
79
|
+
const setConnected = c => {
|
|
80
|
+
isConnected = c;
|
|
81
|
+
Scoped.IS_CONNECTED[projectUrl] = isConnected;
|
|
82
|
+
ServerReachableListener.dispatchPersist(projectUrl, isConnected);
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
const onConnect = () => {
|
|
78
86
|
++connectionIte;
|
|
79
|
-
|
|
80
|
-
Scoped.IS_CONNECTED[projectUrl] = true;
|
|
87
|
+
setConnected(true);
|
|
81
88
|
if (recentToken) updateMountedToken();
|
|
82
|
-
ServerReachableListener.dispatchPersist(projectUrl, true);
|
|
83
89
|
awaitStore().then(() => {
|
|
84
90
|
if (isConnected) trySendPendingWrite(projectUrl);
|
|
85
91
|
});
|
|
86
92
|
};
|
|
93
|
+
|
|
87
94
|
const onDisconnect = () => {
|
|
88
95
|
++connectionIte;
|
|
89
|
-
|
|
90
|
-
Scoped.IS_CONNECTED[projectUrl] = isConnected;
|
|
91
|
-
ServerReachableListener.dispatchPersist(projectUrl, isConnected);
|
|
96
|
+
setConnected(isVirtualMachineFocused ? false : null);
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
const manualCheckConnection = () => {
|
|
100
|
+
if (chainedPromise) return;
|
|
95
101
|
const ref = ++connectionIte;
|
|
96
|
-
|
|
102
|
+
const signal = new AbortController();
|
|
103
|
+
const timer = setTimeout(() => {
|
|
104
|
+
signal.abort();
|
|
105
|
+
}, 7000);
|
|
106
|
+
chainedPromise = fetch(_areYouOk(projectUrl), { credentials: 'omit', signal }).then(async r => {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
chainedPromise = undefined;
|
|
97
109
|
if ((await r.json()).status === 'yes') {
|
|
98
110
|
if (ref === connectionIte) onConnect();
|
|
99
111
|
} else throw null;
|
|
100
112
|
}).catch(() => {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
chainedPromise = undefined;
|
|
101
115
|
if (ref === connectionIte) onDisconnect();
|
|
102
116
|
});
|
|
103
117
|
}
|
|
118
|
+
|
|
104
119
|
manualCheckConnection();
|
|
105
120
|
|
|
106
121
|
socket.on('_signal_signout', () => {
|
|
@@ -112,7 +127,7 @@ class RNMT {
|
|
|
112
127
|
manualCheckConnection();
|
|
113
128
|
});
|
|
114
129
|
|
|
115
|
-
AppState.addEventListener('change',
|
|
130
|
+
AppState.addEventListener('change', s => {
|
|
116
131
|
isVirtualMachineFocused = s === 'active';
|
|
117
132
|
manualCheckConnection();
|
|
118
133
|
});
|
|
@@ -125,10 +140,6 @@ class RNMT {
|
|
|
125
140
|
recentToken = token;
|
|
126
141
|
if (isConnected) updateMountedToken();
|
|
127
142
|
}, projectUrl);
|
|
128
|
-
|
|
129
|
-
TokenRefreshListener.listenTo(projectUrl, v => {
|
|
130
|
-
Scoped.IS_TOKEN_READY[projectUrl] = v;
|
|
131
|
-
});
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
145
|
|
|
@@ -181,16 +192,31 @@ class RNMT {
|
|
|
181
192
|
_listenUserVerification
|
|
182
193
|
].map(v => [v(), v(true)]).flat();
|
|
183
194
|
|
|
184
|
-
const makeSocketCallback = () =>
|
|
185
|
-
new Promise(resolve => {
|
|
186
|
-
socketReadyCallback = resolve;
|
|
187
|
-
});
|
|
188
|
-
|
|
189
195
|
let socketReadyCallback,
|
|
190
|
-
socketReadyPromise
|
|
196
|
+
socketReadyPromise,
|
|
191
197
|
socketListenerList = [],
|
|
192
198
|
socketListenerIte = 0;
|
|
193
199
|
|
|
200
|
+
const makeSocketCallback = () => {
|
|
201
|
+
const prevCallback = socketReadyCallback;
|
|
202
|
+
|
|
203
|
+
socketReadyPromise = new Promise((resolve, reject) => {
|
|
204
|
+
|
|
205
|
+
socketReadyCallback = [
|
|
206
|
+
() => {
|
|
207
|
+
prevCallback?.[0]?.();
|
|
208
|
+
resolve();
|
|
209
|
+
},
|
|
210
|
+
() => {
|
|
211
|
+
prevCallback?.[1]?.();
|
|
212
|
+
reject();
|
|
213
|
+
}
|
|
214
|
+
];
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
makeSocketCallback();
|
|
219
|
+
|
|
194
220
|
/**
|
|
195
221
|
* @type {import('socket.io-client').Socket}
|
|
196
222
|
*/
|
|
@@ -225,67 +251,88 @@ class RNMT {
|
|
|
225
251
|
}] : []);
|
|
226
252
|
};
|
|
227
253
|
|
|
228
|
-
const emit = ({ timeout, promise, emittion: emittionx }) =>
|
|
229
|
-
|
|
254
|
+
const emit = ({ timeout, promise, emittion: emittionx }) =>
|
|
255
|
+
new Promise(async (resolve, reject) => {
|
|
256
|
+
const [route, ...emittion] = emittionx;
|
|
230
257
|
|
|
231
|
-
|
|
232
|
-
|
|
258
|
+
if (typeof route !== 'string')
|
|
259
|
+
throw `expected ${promise ? 'emitWithAck' : 'emit'} first argument to be a string type`;
|
|
233
260
|
|
|
234
|
-
|
|
235
|
-
|
|
261
|
+
if (restrictedRoute.includes(route))
|
|
262
|
+
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
263
|
+
|
|
264
|
+
let hasResolved, stime = Date.now();
|
|
236
265
|
|
|
237
|
-
|
|
266
|
+
const timer = timeout ? setTimeout(() => {
|
|
267
|
+
hasResolved = true;
|
|
268
|
+
reject(new Error('emittion timeout'));
|
|
269
|
+
}, timeout) : undefined;
|
|
238
270
|
|
|
239
|
-
|
|
240
|
-
hasResolved
|
|
241
|
-
|
|
242
|
-
}, timeout) : undefined;
|
|
271
|
+
await socketReadyPromise;
|
|
272
|
+
if (hasResolved) return;
|
|
273
|
+
clearTimeout(timer);
|
|
243
274
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
clearTimeout(timer);
|
|
275
|
+
try {
|
|
276
|
+
const thisSocket = timeout ? socket.timeout(Math.max(timeout - (Date.now() - stime), 0)) : socket;
|
|
247
277
|
|
|
248
|
-
|
|
249
|
-
|
|
278
|
+
const lastEmit = emittion.slice(-1)[0];
|
|
279
|
+
const hasEmitable = typeof lastEmit === 'function';
|
|
280
|
+
const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
|
|
250
281
|
|
|
251
|
-
|
|
252
|
-
const hasEmitable = typeof lastEmit === 'function';
|
|
253
|
-
const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
|
|
282
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(mit, undefined, serverE2E_PublicKey) : [undefined, []];
|
|
254
283
|
|
|
255
|
-
|
|
284
|
+
if (hasEmitable && promise)
|
|
285
|
+
throw 'emitWithAck cannot have function in it argument';
|
|
256
286
|
|
|
257
|
-
|
|
258
|
-
|
|
287
|
+
const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
|
|
288
|
+
[uglify ? reqBuilder : mit, not_encrypted],
|
|
289
|
+
...hasEmitable ? [async function () {
|
|
290
|
+
const [[args, not_encrypted]] = [...arguments];
|
|
291
|
+
let res;
|
|
259
292
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const [[args, not_encrypted]] = [...arguments];
|
|
264
|
-
let res;
|
|
293
|
+
if (uglify) {
|
|
294
|
+
res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
|
|
295
|
+
} else res = args;
|
|
265
296
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
297
|
+
lastEmit(...discloseSocketArguments([res, not_encrypted]));
|
|
298
|
+
}] : []
|
|
299
|
+
);
|
|
300
|
+
if (promise && result) {
|
|
301
|
+
resolve(discloseSocketArguments([uglify ? await deserializeE2E(result[0], serverE2E_PublicKey, privateKey) : result[0], result[1]])[0]);
|
|
302
|
+
} else resolve();
|
|
303
|
+
} catch (e) {
|
|
304
|
+
reject(e);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
269
307
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
308
|
+
let initIte = 0;
|
|
309
|
+
let foregroundListener;
|
|
310
|
+
const clearForegroundListener = () => {
|
|
311
|
+
if (!foregroundListener) return;
|
|
312
|
+
foregroundListener.remove();
|
|
313
|
+
foregroundListener = undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const clearSocket = () => {
|
|
317
|
+
if (socket) {
|
|
318
|
+
socket.close();
|
|
319
|
+
socket = undefined;
|
|
278
320
|
}
|
|
279
|
-
}
|
|
321
|
+
}
|
|
280
322
|
|
|
281
323
|
const init = async () => {
|
|
324
|
+
clearForegroundListener();
|
|
282
325
|
if (hasCancelled) return;
|
|
326
|
+
const instance_id = ++initIte;
|
|
327
|
+
|
|
283
328
|
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
284
329
|
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
285
330
|
|
|
286
331
|
const getWsPrefix = url => url.startsWith('https') ? 'wss' : 'ws';
|
|
287
332
|
const wsUrl = overidenUrl ? `${getWsPrefix(overidenUrl)}://${overidenUrl.split('://')[1]}` : `${wsPrefix}://${projectUrl.split('://')[1]}`;
|
|
288
333
|
|
|
334
|
+
if (instance_id !== initIte) return;
|
|
335
|
+
|
|
289
336
|
socket = io(wsUrl, {
|
|
290
337
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
291
338
|
extraHeaders,
|
|
@@ -295,32 +342,88 @@ class RNMT {
|
|
|
295
342
|
} : {
|
|
296
343
|
...mtoken ? { mtoken } : {},
|
|
297
344
|
a_extras: authHandshake
|
|
345
|
+
},
|
|
346
|
+
reconnection: false
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const reconnect = (timeout) => {
|
|
350
|
+
if (initIte !== instance_id || hasCancelled) return;
|
|
351
|
+
|
|
352
|
+
makeSocketCallback();
|
|
353
|
+
const reloadIntance = async () => {
|
|
354
|
+
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
355
|
+
if (initIte === instance_id) remountInit();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (AppState.currentState === 'active') {
|
|
359
|
+
setTimeout(() => {
|
|
360
|
+
awaitReachableServer(projectUrl).then(reloadIntance);
|
|
361
|
+
}, timeout);
|
|
362
|
+
} else {
|
|
363
|
+
foregroundListener = AppState.addEventListener('change', s => {
|
|
364
|
+
if (s === 'active') {
|
|
365
|
+
clearForegroundListener();
|
|
366
|
+
reloadIntance();
|
|
367
|
+
}
|
|
368
|
+
});
|
|
298
369
|
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
let wasHandled;
|
|
373
|
+
socket.on('connect', () => {
|
|
374
|
+
if (initIte !== instance_id) return;
|
|
375
|
+
socketReadyCallback[0]();
|
|
376
|
+
socketReadyCallback = undefined;
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
socket.on('connect_error', () => {
|
|
380
|
+
if (initIte !== instance_id || wasHandled) return;
|
|
381
|
+
wasHandled = true;
|
|
382
|
+
clearSocket();
|
|
383
|
+
reconnect(3000);
|
|
299
384
|
});
|
|
300
|
-
clientPrivateKey = privateKey;
|
|
301
385
|
|
|
302
|
-
|
|
386
|
+
socket.on('disconnect', r => {
|
|
387
|
+
if (initIte !== instance_id || wasHandled) return;
|
|
388
|
+
wasHandled = true;
|
|
389
|
+
clearSocket();
|
|
390
|
+
if (r === 'io client disconnect') return;
|
|
391
|
+
if (r === 'io server disconnect') {
|
|
392
|
+
resultant.destroy();
|
|
393
|
+
} else reconnect(0);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
clientPrivateKey = privateKey;
|
|
303
397
|
socketListenerList.forEach(([_, method, route, callback]) => {
|
|
304
398
|
socket[method](route, callback);
|
|
305
399
|
});
|
|
306
400
|
}
|
|
307
401
|
|
|
402
|
+
const remountInit = () => {
|
|
403
|
+
makeSocketCallback();
|
|
404
|
+
if (socket) {
|
|
405
|
+
++initIte;
|
|
406
|
+
clearSocket();
|
|
407
|
+
}
|
|
408
|
+
init();
|
|
409
|
+
}
|
|
410
|
+
|
|
308
411
|
if (disableAuth) {
|
|
309
412
|
init();
|
|
310
413
|
} else {
|
|
311
414
|
let lastTokenStatus;
|
|
312
415
|
|
|
313
|
-
tokenListener = listenTokenReady(
|
|
314
|
-
if (lastTokenStatus === (
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
init();
|
|
416
|
+
tokenListener = listenTokenReady(ready => {
|
|
417
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
418
|
+
if (ready) {
|
|
419
|
+
remountInit();
|
|
318
420
|
} else {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
421
|
+
makeSocketCallback();
|
|
422
|
+
++initIte;
|
|
423
|
+
clearForegroundListener();
|
|
424
|
+
clearSocket();
|
|
322
425
|
}
|
|
323
|
-
lastTokenStatus =
|
|
426
|
+
lastTokenStatus = ready || false;
|
|
324
427
|
}, projectUrl);
|
|
325
428
|
}
|
|
326
429
|
|
|
@@ -377,8 +480,14 @@ class RNMT {
|
|
|
377
480
|
destroy: () => {
|
|
378
481
|
hasCancelled = true;
|
|
379
482
|
tokenListener?.();
|
|
380
|
-
|
|
483
|
+
clearForegroundListener();
|
|
484
|
+
clearSocket();
|
|
381
485
|
socketListenerList = [];
|
|
486
|
+
if (!socketReadyCallback) {
|
|
487
|
+
makeSocketCallback();
|
|
488
|
+
}
|
|
489
|
+
socketReadyCallback[1]('socket already disconnected');
|
|
490
|
+
socketReadyCallback = undefined;
|
|
382
491
|
}
|
|
383
492
|
};
|
|
384
493
|
|
|
@@ -56,7 +56,7 @@ export const injectEmulatedAuth = async (config, emulatedURL) => {
|
|
|
56
56
|
export const parseToken = (token) => JSON.parse(decodeBinary(token.split('.')[1]));
|
|
57
57
|
|
|
58
58
|
export const triggerAuthToken = async (projectUrl, isInit) => {
|
|
59
|
-
await awaitStore();
|
|
59
|
+
if (!Scoped.IsStoreReady) await awaitStore();
|
|
60
60
|
AuthTokenListener.dispatchPersist(projectUrl, CacheStore.AuthStore[projectUrl]?.token || null, isInit);
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -97,15 +97,16 @@ export const initTokenRefresher = async ({ config, forceRefresh, justCheck }) =>
|
|
|
97
97
|
|
|
98
98
|
if (token) {
|
|
99
99
|
const rizz = () => {
|
|
100
|
-
|
|
101
|
-
if (runningProcess)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
100
|
+
let runningProcess = Scoped.TokenRefreshProcess[projectUrl];
|
|
101
|
+
if (!runningProcess) {
|
|
102
|
+
runningProcess = refreshToken(config, maxRetries, forceRefresh);
|
|
103
|
+
Scoped.TokenRefreshProcess[projectUrl] = runningProcess;
|
|
104
|
+
|
|
105
|
+
Scoped.TokenRefreshProcess[projectUrl].finally(() => {
|
|
106
|
+
delete Scoped.TokenRefreshProcess[projectUrl];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return runningProcess;
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
if (await hasTokenExpire(projectUrl) || forceRefresh) {
|
|
@@ -120,11 +121,11 @@ export const initTokenRefresher = async ({ config, forceRefresh, justCheck }) =>
|
|
|
120
121
|
clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
121
122
|
Scoped.TokenRefreshTimer[projectUrl] = setInterval(async () => {
|
|
122
123
|
const iteRef = ++lastIte;
|
|
123
|
-
if (
|
|
124
|
+
if (!(await hasTokenExpire(projectUrl)) || iteRef !== lastIte) return;
|
|
124
125
|
clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
|
|
125
126
|
notifyAuthReady();
|
|
126
127
|
rizz();
|
|
127
|
-
},
|
|
128
|
+
}, 7000);
|
|
128
129
|
}
|
|
129
130
|
}
|
|
130
131
|
} else {
|
|
@@ -149,9 +150,10 @@ const updateTokenTimestamp = async (projectUrl, token) => {
|
|
|
149
150
|
};
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
export const getEmulatedLinks = (projectUrl) =>
|
|
153
|
-
.
|
|
154
|
-
|
|
153
|
+
export const getEmulatedLinks = (projectUrl) =>
|
|
154
|
+
Object.entries(CacheStore.EmulatedAuth)
|
|
155
|
+
.filter(([_, v]) => v === projectUrl)
|
|
156
|
+
.map(v => v[0]);
|
|
155
157
|
|
|
156
158
|
const refreshToken = (builder, remainRetries = 1, isForceRefresh) =>
|
|
157
159
|
new Promise(async (resolve, reject) => {
|
|
@@ -171,15 +173,20 @@ const refreshToken = (builder, remainRetries = 1, isForceRefresh) =>
|
|
|
171
173
|
|
|
172
174
|
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
173
175
|
|
|
176
|
+
if (!CacheStore.AuthStore[projectUrl]) {
|
|
177
|
+
reject(simplifyError('token_not_mounted', 'No refresh token was mounted or has been recently removed').simpleError);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
|
|
174
181
|
CacheStore.AuthStore[projectUrl].token = f.result.token;
|
|
175
182
|
Scoped.AuthJWTToken[projectUrl] = f.result.token;
|
|
176
183
|
await updateTokenTimestamp(projectUrl, f.result.token);
|
|
177
184
|
|
|
178
185
|
resolve(f.result.token);
|
|
179
|
-
const isInit = !Scoped.InitiatedForcedToken[projectUrl]
|
|
186
|
+
const isInit = !Scoped.InitiatedForcedToken[projectUrl];
|
|
180
187
|
|
|
181
188
|
triggerAuthToken(projectUrl, isInit);
|
|
182
|
-
if (
|
|
189
|
+
if (isInit) Scoped.InitiatedForcedToken[projectUrl] = true;
|
|
183
190
|
|
|
184
191
|
getEmulatedLinks(projectUrl).forEach(v => {
|
|
185
192
|
CacheStore.AuthStore[v] = basicClone(CacheStore.AuthStore[projectUrl]);
|
|
@@ -185,13 +185,14 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
|
|
|
185
185
|
|
|
186
186
|
const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
187
187
|
|
|
188
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
189
|
+
injectFreshToken(builder, r.result);
|
|
190
|
+
|
|
188
191
|
resolve({
|
|
189
192
|
user: parseToken(r.result.token),
|
|
190
193
|
token: r.result.token,
|
|
191
194
|
refreshToken: r.result.refreshToken
|
|
192
195
|
});
|
|
193
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
194
|
-
await injectFreshToken(builder, r.result);
|
|
195
196
|
} catch (e) {
|
|
196
197
|
reject(simplifyCaughtError(e).simpleError);
|
|
197
198
|
}
|
|
@@ -218,14 +219,15 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
|
|
|
218
219
|
|
|
219
220
|
const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
220
221
|
|
|
222
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
223
|
+
injectFreshToken(builder, r.result);
|
|
224
|
+
|
|
221
225
|
resolve({
|
|
222
226
|
user: parseToken(r.result.token),
|
|
223
227
|
token: r.result.token,
|
|
224
228
|
refreshToken: r.result.refreshToken,
|
|
225
229
|
isNewUser: !!r.result.isNewUser
|
|
226
230
|
});
|
|
227
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
228
|
-
await injectFreshToken(builder, r.result);
|
|
229
231
|
} catch (e) {
|
|
230
232
|
reject(simplifyCaughtError(e).simpleError);
|
|
231
233
|
}
|
|
@@ -249,7 +251,10 @@ const clearCacheForSignout = (builder, disposeEmulated) => {
|
|
|
249
251
|
|
|
250
252
|
purgeCache(projectUrl, true);
|
|
251
253
|
if (disposeEmulated) getEmulatedLinks(projectUrl).forEach(e => purgeCache(e));
|
|
252
|
-
|
|
254
|
+
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
initTokenRefresher({ config: builder });
|
|
257
|
+
}, 600);
|
|
253
258
|
};
|
|
254
259
|
|
|
255
260
|
export const doSignOut = async (builder) => {
|
|
@@ -330,14 +335,15 @@ const doProviderSignin = (builder, token, metadata, endpointer) => new Promise(a
|
|
|
330
335
|
|
|
331
336
|
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
332
337
|
|
|
338
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
339
|
+
injectFreshToken(builder, f.result);
|
|
340
|
+
|
|
333
341
|
resolve({
|
|
334
342
|
user: parseToken(f.result.token),
|
|
335
343
|
token: f.result.token,
|
|
336
344
|
refreshToken: f.result.refreshToken,
|
|
337
345
|
isNewUser: f.result.isNewUser
|
|
338
346
|
});
|
|
339
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
340
|
-
await injectFreshToken(builder, f.result);
|
|
341
347
|
} catch (e) {
|
|
342
348
|
reject(simplifyCaughtError(e).simpleError);
|
|
343
349
|
}
|
|
@@ -2,17 +2,18 @@ import { io } from "socket.io-client";
|
|
|
2
2
|
import EngineApi from "../../helpers/engine_api";
|
|
3
3
|
import { DatabaseRecordsListener } from "../../helpers/listeners";
|
|
4
4
|
import { deserializeE2E, listenReachableServer, niceTry, serializeE2E } from "../../helpers/peripherals";
|
|
5
|
-
import { awaitStore, buildFetchInterface, buildFetchResult, getReachableServer, updateCacheStore } from "../../helpers/utils";
|
|
5
|
+
import { awaitReachableServer, awaitStore, buildFetchInterface, buildFetchResult, getReachableServer, updateCacheStore } from "../../helpers/utils";
|
|
6
6
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
7
7
|
import { addPendingWrites, generateRecordID, getCountQuery, getRecord, insertCountQuery, insertRecord, listenQueryEntry, removePendingWrite, validateWriteValue } from "./accessor";
|
|
8
8
|
import { validateCollectionName, validateFilter, validateFindConfig, validateFindObject, validateListenFindConfig } from "./validator";
|
|
9
|
-
import { awaitRefreshToken,
|
|
9
|
+
import { awaitRefreshToken, listenTokenReady } from "../auth/accessor";
|
|
10
10
|
import { DELIVERY, RETRIEVAL } from "../../helpers/values";
|
|
11
11
|
import { ObjectId } from "../../vendor/bson";
|
|
12
12
|
import { guardObject, Validator } from "guard-object";
|
|
13
13
|
import { simplifyCaughtError } from "simplify-error";
|
|
14
14
|
import { deserializeBSON, serializeToBase64 } from "./bson";
|
|
15
15
|
import { basicClone } from "../../helpers/basic_clone";
|
|
16
|
+
import { AppState } from "react-native";
|
|
16
17
|
|
|
17
18
|
export class MTCollection {
|
|
18
19
|
constructor(config) {
|
|
@@ -149,11 +150,13 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
149
150
|
validateFilter(findOne || find);
|
|
150
151
|
validateCollectionName(path);
|
|
151
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @type {import('socket.io-client').Socket}
|
|
155
|
+
*/
|
|
156
|
+
let socket;
|
|
152
157
|
let hasCancelled,
|
|
153
158
|
hasRespond,
|
|
154
159
|
cacheListener,
|
|
155
|
-
socket,
|
|
156
|
-
lastToken = Scoped.AuthJWTToken[projectUrl] || null,
|
|
157
160
|
lastInitRef = 0,
|
|
158
161
|
connectedListener,
|
|
159
162
|
lastSnapshot;
|
|
@@ -184,7 +187,23 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
184
187
|
});
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
let foregroundListener;
|
|
191
|
+
const clearForegroundListener = () => {
|
|
192
|
+
if (!foregroundListener) return;
|
|
193
|
+
foregroundListener.remove();
|
|
194
|
+
foregroundListener = undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const clearSocket = () => {
|
|
198
|
+
if (socket) {
|
|
199
|
+
socket.close();
|
|
200
|
+
socket = undefined;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
187
204
|
const init = async () => {
|
|
205
|
+
clearForegroundListener();
|
|
206
|
+
|
|
188
207
|
const processID = ++lastInitRef;
|
|
189
208
|
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
190
209
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
@@ -207,6 +226,7 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
207
226
|
const [encPlate, [privateKey]] = uglify ? await serializeE2E({ _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
208
227
|
|
|
209
228
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
229
|
+
|
|
210
230
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
211
231
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
212
232
|
extraHeaders,
|
|
@@ -217,7 +237,8 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
217
237
|
},
|
|
218
238
|
_m_internal: true,
|
|
219
239
|
_m_route: (findOne ? _listenDocument : _listenCollection)(uglify)
|
|
220
|
-
}
|
|
240
|
+
},
|
|
241
|
+
reconnection: false
|
|
221
242
|
});
|
|
222
243
|
|
|
223
244
|
socket.on('mSnapshot', async ([err, snapshot]) => {
|
|
@@ -234,18 +255,71 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
234
255
|
if (shouldCache) insertRecord(builder, config, await accessId, snapshot, episode);
|
|
235
256
|
}
|
|
236
257
|
});
|
|
237
|
-
};
|
|
238
258
|
|
|
239
|
-
|
|
259
|
+
const reconnect = (timeout) => {
|
|
260
|
+
if (processID !== lastInitRef || hasCancelled) return;
|
|
240
261
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
262
|
+
const reloadIntance = async () => {
|
|
263
|
+
if (processID !== lastInitRef && !hasCancelled) remountInit();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (AppState.currentState === 'active') {
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
awaitReachableServer(projectUrl).then(reloadIntance);
|
|
269
|
+
}, timeout);
|
|
270
|
+
} else {
|
|
271
|
+
foregroundListener = AppState.addEventListener('change', s => {
|
|
272
|
+
if (s === 'active') {
|
|
273
|
+
clearForegroundListener();
|
|
274
|
+
reloadIntance();
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
246
278
|
}
|
|
247
|
-
|
|
248
|
-
|
|
279
|
+
|
|
280
|
+
let wasHandled;
|
|
281
|
+
socket.on('connect_error', () => {
|
|
282
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
283
|
+
wasHandled = true;
|
|
284
|
+
clearSocket();
|
|
285
|
+
reconnect(3000);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
socket.on('disconnect', r => {
|
|
289
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
290
|
+
wasHandled = true;
|
|
291
|
+
clearSocket();
|
|
292
|
+
if (r === 'io client disconnect' || r === 'io server disconnect') return;
|
|
293
|
+
reconnect(0);
|
|
294
|
+
});
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const remountInit = () => {
|
|
298
|
+
if (socket) {
|
|
299
|
+
++lastInitRef;
|
|
300
|
+
clearSocket();
|
|
301
|
+
}
|
|
302
|
+
init();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let lastTokenStatus;
|
|
306
|
+
let tokenListener;
|
|
307
|
+
|
|
308
|
+
if (disableAuth) {
|
|
309
|
+
init();
|
|
310
|
+
} else {
|
|
311
|
+
tokenListener = listenTokenReady(ready => {
|
|
312
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
313
|
+
if (ready) {
|
|
314
|
+
remountInit();
|
|
315
|
+
} else {
|
|
316
|
+
++lastInitRef;
|
|
317
|
+
clearForegroundListener();
|
|
318
|
+
clearSocket();
|
|
319
|
+
}
|
|
320
|
+
lastTokenStatus = ready || false;
|
|
321
|
+
}, projectUrl);
|
|
322
|
+
}
|
|
249
323
|
|
|
250
324
|
return () => {
|
|
251
325
|
if (hasCancelled) return;
|
|
@@ -253,7 +327,8 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
253
327
|
connectedListener?.();
|
|
254
328
|
cacheListener?.();
|
|
255
329
|
tokenListener?.();
|
|
256
|
-
|
|
330
|
+
clearForegroundListener();
|
|
331
|
+
clearSocket();
|
|
257
332
|
}
|
|
258
333
|
};
|
|
259
334
|
|
|
@@ -279,18 +354,29 @@ const initOnDisconnectionTask = ({ builder, connectData, disconnectData }) => {
|
|
|
279
354
|
}
|
|
280
355
|
});
|
|
281
356
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
lastToken = Scoped.AuthJWTToken[projectUrl] || null,
|
|
357
|
+
/**
|
|
358
|
+
* @type {import('socket.io-client').Socket}
|
|
359
|
+
*/
|
|
360
|
+
let socket,
|
|
361
|
+
hasCancelled,
|
|
288
362
|
lastInitRef = 0;
|
|
289
363
|
|
|
364
|
+
let foregroundListener;
|
|
365
|
+
const clearForegroundListener = () => {
|
|
366
|
+
if (!foregroundListener) return;
|
|
367
|
+
foregroundListener.remove();
|
|
368
|
+
foregroundListener = undefined;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const clearSocket = () => {
|
|
372
|
+
if (socket) {
|
|
373
|
+
socket.close();
|
|
374
|
+
socket = undefined;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
290
378
|
const init = async () => {
|
|
291
379
|
const processID = ++lastInitRef;
|
|
292
|
-
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
293
|
-
if (hasCancelled || processID !== lastInitRef) return;
|
|
294
380
|
|
|
295
381
|
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
296
382
|
const makeObj = (d) => ({
|
|
@@ -307,39 +393,95 @@ const initOnDisconnectionTask = ({ builder, connectData, disconnectData }) => {
|
|
|
307
393
|
...dbUrl ? { dbUrl } : undefined
|
|
308
394
|
};
|
|
309
395
|
|
|
396
|
+
const uglifyData = uglify && (await serializeE2E({ _body: authObj }, mtoken, serverE2E_PublicKey))[0].toString('base64');
|
|
397
|
+
|
|
398
|
+
if (hasCancelled || processID !== lastInitRef) return;
|
|
399
|
+
|
|
310
400
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
311
401
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
312
402
|
extraHeaders,
|
|
313
403
|
auth: {
|
|
314
|
-
...uglify ? {
|
|
315
|
-
e2e: (await serializeE2E({ _body: authObj }, mtoken, serverE2E_PublicKey))[0].toString('base64')
|
|
316
|
-
} : {
|
|
404
|
+
...uglify ? { e2e: uglifyData } : {
|
|
317
405
|
...mtoken ? { mtoken } : {},
|
|
318
406
|
_body: authObj
|
|
319
407
|
},
|
|
320
408
|
_m_internal: true,
|
|
321
409
|
_m_route: _startDisconnectWriteTask(uglify)
|
|
410
|
+
},
|
|
411
|
+
reconnection: false
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
const reconnect = (timeout) => {
|
|
415
|
+
if (processID !== lastInitRef || hasCancelled) return;
|
|
416
|
+
|
|
417
|
+
const reloadIntance = async () => {
|
|
418
|
+
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
419
|
+
if (processID !== lastInitRef && !hasCancelled) remountInit();
|
|
322
420
|
}
|
|
421
|
+
|
|
422
|
+
if (AppState.currentState === 'active') {
|
|
423
|
+
setTimeout(() => {
|
|
424
|
+
awaitReachableServer(projectUrl).then(reloadIntance);
|
|
425
|
+
}, timeout);
|
|
426
|
+
} else {
|
|
427
|
+
foregroundListener = AppState.addEventListener('change', s => {
|
|
428
|
+
if (s === 'active') {
|
|
429
|
+
clearForegroundListener();
|
|
430
|
+
reloadIntance();
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
let wasHandled;
|
|
437
|
+
socket.on('connect_error', () => {
|
|
438
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
439
|
+
wasHandled = true;
|
|
440
|
+
clearSocket();
|
|
441
|
+
reconnect(3000);
|
|
323
442
|
});
|
|
324
|
-
};
|
|
325
443
|
|
|
326
|
-
|
|
444
|
+
socket.on('disconnect', r => {
|
|
445
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
446
|
+
wasHandled = true;
|
|
447
|
+
clearSocket();
|
|
448
|
+
if (r === 'io client disconnect' || r === 'io server disconnect') return;
|
|
449
|
+
reconnect(0);
|
|
450
|
+
});
|
|
451
|
+
};
|
|
327
452
|
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
socket = undefined;
|
|
333
|
-
setTimeout(init, 500);
|
|
334
|
-
} else init();
|
|
453
|
+
const remountInit = () => {
|
|
454
|
+
if (socket) {
|
|
455
|
+
++lastInitRef;
|
|
456
|
+
clearSocket();
|
|
335
457
|
}
|
|
336
|
-
|
|
337
|
-
}
|
|
458
|
+
init();
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let lastTokenStatus;
|
|
462
|
+
let tokenListener;
|
|
463
|
+
|
|
464
|
+
if (disableAuth) {
|
|
465
|
+
init();
|
|
466
|
+
} else {
|
|
467
|
+
tokenListener = listenTokenReady(ready => {
|
|
468
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
469
|
+
if (ready) {
|
|
470
|
+
remountInit();
|
|
471
|
+
} else {
|
|
472
|
+
++lastInitRef;
|
|
473
|
+
clearForegroundListener();
|
|
474
|
+
clearSocket();
|
|
475
|
+
}
|
|
476
|
+
lastTokenStatus = ready || false;
|
|
477
|
+
}, projectUrl);
|
|
478
|
+
}
|
|
338
479
|
|
|
339
480
|
return () => {
|
|
340
481
|
if (hasCancelled) return;
|
|
341
482
|
hasCancelled = true;
|
|
342
483
|
tokenListener?.();
|
|
484
|
+
clearForegroundListener();
|
|
343
485
|
if (socket) {
|
|
344
486
|
const thisSocket = socket;
|
|
345
487
|
return niceTry(() => thisSocket.timeout(5000).emitWithAck(_cancelDisconnectWriteTask(uglify))).finally(() => {
|
|
@@ -184,7 +184,9 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
184
184
|
)
|
|
185
185
|
};
|
|
186
186
|
|
|
187
|
-
if (shouldCache)
|
|
187
|
+
if (shouldCache) {
|
|
188
|
+
if (status === 200) insertFetchResources(projectUrl, reqId, resObj);
|
|
189
|
+
}
|
|
188
190
|
|
|
189
191
|
finalize(resObj);
|
|
190
192
|
} catch (e) {
|