react-native-mosquito-transport 0.0.51 → 0.0.53
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/peripherals.js +0 -16
- package/src/helpers/utils.js +64 -17
- package/src/helpers/variables.js +1 -1
- package/src/index.js +183 -85
- package/src/products/auth/accessor.js +29 -25
- package/src/products/auth/index.js +15 -15
- package/src/products/database/index.js +212 -71
- package/src/products/http_callable/index.js +11 -12
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.53",
|
|
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
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Buffer } from "buffer";
|
|
2
|
-
import { ServerReachableListener } from "./listeners";
|
|
3
2
|
import naclPkg from 'tweetnacl';
|
|
4
3
|
import { deserialize, serialize } from "entity-serializer";
|
|
5
4
|
import { sha256 } from 'react-native-sha256';
|
|
@@ -8,14 +7,6 @@ import { grab } from "poke-object";
|
|
|
8
7
|
|
|
9
8
|
const { box, randomBytes } = naclPkg;
|
|
10
9
|
|
|
11
|
-
export const listenReachableServer = (callback, projectUrl) => {
|
|
12
|
-
let lastValue;
|
|
13
|
-
return ServerReachableListener.listenToPersist(projectUrl, t => {
|
|
14
|
-
if (typeof t === 'boolean' && t !== lastValue) callback?.(t);
|
|
15
|
-
lastValue = t;
|
|
16
|
-
});
|
|
17
|
-
};
|
|
18
|
-
|
|
19
10
|
export const prefixStoragePath = (path, prefix = 'file:///') => {
|
|
20
11
|
let cleanedPath = path.replace(/^[^/]+:\/{1,3}/, '');
|
|
21
12
|
|
|
@@ -30,13 +21,6 @@ export const prefixStoragePath = (path, prefix = 'file:///') => {
|
|
|
30
21
|
return `${prefix}${cleanedPath}`;
|
|
31
22
|
};
|
|
32
23
|
|
|
33
|
-
export const niceTry = (promise) => new Promise(async resolve => {
|
|
34
|
-
try {
|
|
35
|
-
const r = await promise();
|
|
36
|
-
resolve(r);
|
|
37
|
-
} catch (e) { resolve(); }
|
|
38
|
-
});
|
|
39
|
-
|
|
40
24
|
export const normalizeRoute = (route = '') => route.split('').map((v, i, a) =>
|
|
41
25
|
((!i && v === '/') || (i === a.length - 1 && v === '/') || (i && a[i - 1] === '/' && v === '/')) ? '' : v
|
|
42
26
|
).join('');
|
package/src/helpers/utils.js
CHANGED
|
@@ -7,6 +7,8 @@ import { breakDbMap, purgeRedundantRecords } from "./purger";
|
|
|
7
7
|
import { FS_PATH, getSystem } from "./fs_manager";
|
|
8
8
|
import { Buffer } from "buffer";
|
|
9
9
|
import { basicClone } from "./basic_clone";
|
|
10
|
+
import engine_api from "./engine_api";
|
|
11
|
+
import { AppState } from "react-native";
|
|
10
12
|
|
|
11
13
|
const { FILE_NAME, TABLE_NAME } = FS_PATH;
|
|
12
14
|
|
|
@@ -153,31 +155,76 @@ export const awaitStore = () => new Promise(resolve => {
|
|
|
153
155
|
});
|
|
154
156
|
});
|
|
155
157
|
|
|
156
|
-
export const
|
|
157
|
-
if (Scoped.
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
export const checkAreYouOk = (projectUrl) => {
|
|
159
|
+
if (!Scoped.AreYouOkPromise[projectUrl]) {
|
|
160
|
+
const signal = new AbortController();
|
|
161
|
+
const timer = setTimeout(() => {
|
|
162
|
+
signal.abort();
|
|
163
|
+
}, 9000);
|
|
164
|
+
const promise = fetch(engine_api._areYouOk(projectUrl), { credentials: 'omit', signal })
|
|
165
|
+
.then(async r => (await r.json()).status === 'yes')
|
|
166
|
+
.catch(() => false)
|
|
167
|
+
.then(async connected => {
|
|
168
|
+
Scoped.IS_CONNECTED[projectUrl] = connected;
|
|
169
|
+
ServerReachableListener.dispatchPersist(projectUrl, connected);
|
|
170
|
+
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
delete Scoped.AreYouOkPromise[projectUrl];
|
|
173
|
+
return connected;
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
Scoped.AreYouOkPromise[projectUrl] = promise;
|
|
160
177
|
}
|
|
161
|
-
|
|
162
|
-
|
|
178
|
+
|
|
179
|
+
return Scoped.AreYouOkPromise[projectUrl];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const listenReachableServer = (callback, projectUrl) => {
|
|
183
|
+
let lastValue;
|
|
184
|
+
return ServerReachableListener.listenToPersist(projectUrl, t => {
|
|
185
|
+
if (typeof t === 'boolean' && t !== lastValue) callback?.(t);
|
|
186
|
+
lastValue = t;
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export const awaitReachableServer = (projectUrl) =>
|
|
191
|
+
new Promise(async resolve => {
|
|
192
|
+
if (AppState.currentState !== 'active') {
|
|
193
|
+
if (await checkAreYouOk(projectUrl)) {
|
|
194
|
+
resolve();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (Scoped.IS_CONNECTED[projectUrl]) {
|
|
163
200
|
resolve();
|
|
164
|
-
|
|
201
|
+
return;
|
|
165
202
|
}
|
|
203
|
+
|
|
204
|
+
const l = listenReachableServer(t => {
|
|
205
|
+
if (!t) return;
|
|
206
|
+
resolve();
|
|
207
|
+
l();
|
|
208
|
+
}, projectUrl);
|
|
166
209
|
});
|
|
167
|
-
});
|
|
168
210
|
|
|
169
|
-
export const getReachableServer = (projectUrl) =>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
211
|
+
export const getReachableServer = (projectUrl) =>
|
|
212
|
+
new Promise(async resolve => {
|
|
213
|
+
if (AppState.currentState !== 'active') {
|
|
214
|
+
resolve(await checkAreYouOk(projectUrl));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (typeof Scoped.IS_CONNECTED[projectUrl] === 'boolean') {
|
|
219
|
+
resolve(Scoped.IS_CONNECTED[projectUrl]);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const l = listenReachableServer(t => {
|
|
176
224
|
resolve(t);
|
|
177
225
|
l();
|
|
178
|
-
}
|
|
226
|
+
}, projectUrl);
|
|
179
227
|
});
|
|
180
|
-
});
|
|
181
228
|
|
|
182
229
|
export const buildFetchInterface = async ({ body, authToken, method, uglify, serverE2E_PublicKey, extraHeaders }) => {
|
|
183
230
|
if (!uglify) body = JSON.stringify({ ...body });
|
package/src/helpers/variables.js
CHANGED
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import 'react-native-get-random-values';
|
|
2
|
-
import { deserializeE2E,
|
|
3
|
-
import { awaitStore, releaseCacheStore } from "./helpers/utils";
|
|
2
|
+
import { deserializeE2E, serializeE2E } from "./helpers/peripherals";
|
|
3
|
+
import { awaitReachableServer, awaitStore, checkAreYouOk, listenReachableServer, 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";
|
|
@@ -23,8 +23,7 @@ const {
|
|
|
23
23
|
_listenDocument,
|
|
24
24
|
_startDisconnectWriteTask,
|
|
25
25
|
_cancelDisconnectWriteTask,
|
|
26
|
-
_listenUserVerification
|
|
27
|
-
_areYouOk
|
|
26
|
+
_listenUserVerification
|
|
28
27
|
} = EngineApi;
|
|
29
28
|
|
|
30
29
|
// https://socket.io/docs/v3/emit-cheatsheet/#reserved-events
|
|
@@ -63,7 +62,7 @@ class RNMT {
|
|
|
63
62
|
triggerAuthToken(projectUrl);
|
|
64
63
|
initTokenRefresher({ config: this.config, forceRefresh: true });
|
|
65
64
|
|
|
66
|
-
let isConnected, recentToken
|
|
65
|
+
let isConnected, recentToken;
|
|
67
66
|
|
|
68
67
|
const socket = io(`${this.config.wsPrefix}://${this.config.baseUrl}`, {
|
|
69
68
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
@@ -73,34 +72,40 @@ class RNMT {
|
|
|
73
72
|
_from_base: true
|
|
74
73
|
}
|
|
75
74
|
});
|
|
75
|
+
|
|
76
76
|
let connectionIte = 0;
|
|
77
|
+
let chainedPromise;
|
|
78
|
+
const setConnected = c => {
|
|
79
|
+
isConnected = c;
|
|
80
|
+
Scoped.IS_CONNECTED[projectUrl] = isConnected;
|
|
81
|
+
ServerReachableListener.dispatchPersist(projectUrl, isConnected);
|
|
82
|
+
}
|
|
83
|
+
|
|
77
84
|
const onConnect = () => {
|
|
78
85
|
++connectionIte;
|
|
79
|
-
|
|
80
|
-
Scoped.IS_CONNECTED[projectUrl] = true;
|
|
86
|
+
setConnected(true);
|
|
81
87
|
if (recentToken) updateMountedToken();
|
|
82
|
-
ServerReachableListener.dispatchPersist(projectUrl, true);
|
|
83
88
|
awaitStore().then(() => {
|
|
84
89
|
if (isConnected) trySendPendingWrite(projectUrl);
|
|
85
90
|
});
|
|
86
91
|
};
|
|
87
|
-
const onDisconnect = () => {
|
|
88
|
-
++connectionIte;
|
|
89
|
-
isConnected = isVirtualMachineFocused ? false : null;
|
|
90
|
-
Scoped.IS_CONNECTED[projectUrl] = isConnected;
|
|
91
|
-
ServerReachableListener.dispatchPersist(projectUrl, isConnected);
|
|
92
|
-
}
|
|
93
92
|
|
|
94
93
|
const manualCheckConnection = () => {
|
|
94
|
+
if (chainedPromise) return;
|
|
95
95
|
const ref = ++connectionIte;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
|
|
97
|
+
checkAreYouOk(projectUrl).then(ok => {
|
|
98
|
+
chainedPromise = undefined;
|
|
99
|
+
if (ref !== connectionIte) return;
|
|
100
|
+
if (ok) {
|
|
101
|
+
onConnect();
|
|
102
|
+
} else {
|
|
103
|
+
++connectionIte;
|
|
104
|
+
isConnected = false;
|
|
105
|
+
}
|
|
102
106
|
});
|
|
103
107
|
}
|
|
108
|
+
|
|
104
109
|
manualCheckConnection();
|
|
105
110
|
|
|
106
111
|
socket.on('_signal_signout', () => {
|
|
@@ -112,8 +117,7 @@ class RNMT {
|
|
|
112
117
|
manualCheckConnection();
|
|
113
118
|
});
|
|
114
119
|
|
|
115
|
-
AppState.addEventListener('change',
|
|
116
|
-
isVirtualMachineFocused = s === 'active';
|
|
120
|
+
AppState.addEventListener('change', s => {
|
|
117
121
|
manualCheckConnection();
|
|
118
122
|
});
|
|
119
123
|
|
|
@@ -125,10 +129,6 @@ class RNMT {
|
|
|
125
129
|
recentToken = token;
|
|
126
130
|
if (isConnected) updateMountedToken();
|
|
127
131
|
}, projectUrl);
|
|
128
|
-
|
|
129
|
-
TokenRefreshListener.listenTo(projectUrl, v => {
|
|
130
|
-
Scoped.IS_TOKEN_READY[projectUrl] = v;
|
|
131
|
-
});
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -181,16 +181,31 @@ class RNMT {
|
|
|
181
181
|
_listenUserVerification
|
|
182
182
|
].map(v => [v(), v(true)]).flat();
|
|
183
183
|
|
|
184
|
-
const makeSocketCallback = () =>
|
|
185
|
-
new Promise(resolve => {
|
|
186
|
-
socketReadyCallback = resolve;
|
|
187
|
-
});
|
|
188
|
-
|
|
189
184
|
let socketReadyCallback,
|
|
190
|
-
socketReadyPromise
|
|
185
|
+
socketReadyPromise,
|
|
191
186
|
socketListenerList = [],
|
|
192
187
|
socketListenerIte = 0;
|
|
193
188
|
|
|
189
|
+
const makeSocketCallback = () => {
|
|
190
|
+
const prevCallback = socketReadyCallback;
|
|
191
|
+
|
|
192
|
+
socketReadyPromise = new Promise((resolve, reject) => {
|
|
193
|
+
|
|
194
|
+
socketReadyCallback = [
|
|
195
|
+
() => {
|
|
196
|
+
prevCallback?.[0]?.();
|
|
197
|
+
resolve();
|
|
198
|
+
},
|
|
199
|
+
() => {
|
|
200
|
+
prevCallback?.[1]?.();
|
|
201
|
+
reject();
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
makeSocketCallback();
|
|
208
|
+
|
|
194
209
|
/**
|
|
195
210
|
* @type {import('socket.io-client').Socket}
|
|
196
211
|
*/
|
|
@@ -225,67 +240,88 @@ class RNMT {
|
|
|
225
240
|
}] : []);
|
|
226
241
|
};
|
|
227
242
|
|
|
228
|
-
const emit = ({ timeout, promise, emittion: emittionx }) =>
|
|
229
|
-
|
|
243
|
+
const emit = ({ timeout, promise, emittion: emittionx }) =>
|
|
244
|
+
new Promise(async (resolve, reject) => {
|
|
245
|
+
const [route, ...emittion] = emittionx;
|
|
246
|
+
|
|
247
|
+
if (typeof route !== 'string')
|
|
248
|
+
throw `expected ${promise ? 'emitWithAck' : 'emit'} first argument to be a string type`;
|
|
249
|
+
|
|
250
|
+
if (restrictedRoute.includes(route))
|
|
251
|
+
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
230
252
|
|
|
231
|
-
|
|
232
|
-
throw `expected ${promise ? 'emitWithAck' : 'emit'} first argument to be a string type`;
|
|
253
|
+
let hasResolved, stime = Date.now();
|
|
233
254
|
|
|
234
|
-
|
|
235
|
-
|
|
255
|
+
const timer = timeout ? setTimeout(() => {
|
|
256
|
+
hasResolved = true;
|
|
257
|
+
reject(new Error('emittion timeout'));
|
|
258
|
+
}, timeout) : undefined;
|
|
236
259
|
|
|
237
|
-
|
|
260
|
+
await socketReadyPromise;
|
|
261
|
+
if (hasResolved) return;
|
|
262
|
+
clearTimeout(timer);
|
|
238
263
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
reject(new Error('emittion timeout'));
|
|
242
|
-
}, timeout) : undefined;
|
|
264
|
+
try {
|
|
265
|
+
const thisSocket = timeout ? socket.timeout(Math.max(timeout - (Date.now() - stime), 0)) : socket;
|
|
243
266
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
267
|
+
const lastEmit = emittion.slice(-1)[0];
|
|
268
|
+
const hasEmitable = typeof lastEmit === 'function';
|
|
269
|
+
const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
|
|
247
270
|
|
|
248
|
-
|
|
249
|
-
const thisSocket = timeout ? socket.timeout(Math.max(timeout - (Date.now() - stime), 0)) : socket;
|
|
271
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(mit, undefined, serverE2E_PublicKey) : [undefined, []];
|
|
250
272
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
|
|
273
|
+
if (hasEmitable && promise)
|
|
274
|
+
throw 'emitWithAck cannot have function in it argument';
|
|
254
275
|
|
|
255
|
-
|
|
276
|
+
const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
|
|
277
|
+
[uglify ? reqBuilder : mit, not_encrypted],
|
|
278
|
+
...hasEmitable ? [async function () {
|
|
279
|
+
const [[args, not_encrypted]] = [...arguments];
|
|
280
|
+
let res;
|
|
256
281
|
|
|
257
|
-
|
|
258
|
-
|
|
282
|
+
if (uglify) {
|
|
283
|
+
res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
|
|
284
|
+
} else res = args;
|
|
259
285
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
286
|
+
lastEmit(...discloseSocketArguments([res, not_encrypted]));
|
|
287
|
+
}] : []
|
|
288
|
+
);
|
|
289
|
+
if (promise && result) {
|
|
290
|
+
resolve(discloseSocketArguments([uglify ? await deserializeE2E(result[0], serverE2E_PublicKey, privateKey) : result[0], result[1]])[0]);
|
|
291
|
+
} else resolve();
|
|
292
|
+
} catch (e) {
|
|
293
|
+
reject(e);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
265
296
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
297
|
+
let initIte = 0;
|
|
298
|
+
let foregroundListener;
|
|
299
|
+
const clearForegroundListener = () => {
|
|
300
|
+
if (!foregroundListener) return;
|
|
301
|
+
foregroundListener.remove();
|
|
302
|
+
foregroundListener = undefined;
|
|
303
|
+
}
|
|
269
304
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
resolve(discloseSocketArguments([uglify ? await deserializeE2E(result[0], serverE2E_PublicKey, privateKey) : result[0], result[1]])[0]);
|
|
275
|
-
} else resolve();
|
|
276
|
-
} catch (e) {
|
|
277
|
-
reject(e);
|
|
305
|
+
const clearSocket = () => {
|
|
306
|
+
if (socket) {
|
|
307
|
+
socket.close();
|
|
308
|
+
socket = undefined;
|
|
278
309
|
}
|
|
279
|
-
}
|
|
310
|
+
}
|
|
280
311
|
|
|
281
312
|
const init = async () => {
|
|
313
|
+
clearForegroundListener();
|
|
282
314
|
if (hasCancelled) return;
|
|
315
|
+
const instance_id = ++initIte;
|
|
316
|
+
|
|
283
317
|
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
284
318
|
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
285
319
|
|
|
286
320
|
const getWsPrefix = url => url.startsWith('https') ? 'wss' : 'ws';
|
|
287
321
|
const wsUrl = overidenUrl ? `${getWsPrefix(overidenUrl)}://${overidenUrl.split('://')[1]}` : `${wsPrefix}://${projectUrl.split('://')[1]}`;
|
|
288
322
|
|
|
323
|
+
if (instance_id !== initIte) return;
|
|
324
|
+
|
|
289
325
|
socket = io(wsUrl, {
|
|
290
326
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
291
327
|
extraHeaders,
|
|
@@ -295,32 +331,87 @@ class RNMT {
|
|
|
295
331
|
} : {
|
|
296
332
|
...mtoken ? { mtoken } : {},
|
|
297
333
|
a_extras: authHandshake
|
|
334
|
+
},
|
|
335
|
+
reconnection: false
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const reconnect = (timeout) => {
|
|
339
|
+
if (initIte !== instance_id || hasCancelled) return;
|
|
340
|
+
|
|
341
|
+
makeSocketCallback();
|
|
342
|
+
const reloadIntance = async () => {
|
|
343
|
+
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
344
|
+
if (initIte === instance_id && !hasCancelled) remountInit();
|
|
298
345
|
}
|
|
346
|
+
|
|
347
|
+
if (AppState.currentState === 'active') {
|
|
348
|
+
setTimeout(() => {
|
|
349
|
+
awaitReachableServer(projectUrl).then(reloadIntance);
|
|
350
|
+
}, timeout);
|
|
351
|
+
} else {
|
|
352
|
+
foregroundListener = AppState.addEventListener('change', s => {
|
|
353
|
+
if (s === 'active') {
|
|
354
|
+
clearForegroundListener();
|
|
355
|
+
reloadIntance();
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let wasHandled;
|
|
362
|
+
socket.on('connect', () => {
|
|
363
|
+
if (initIte !== instance_id) return;
|
|
364
|
+
socketReadyCallback[0]();
|
|
365
|
+
socketReadyCallback = undefined;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
socket.on('connect_error', () => {
|
|
369
|
+
if (initIte !== instance_id || wasHandled) return;
|
|
370
|
+
wasHandled = true;
|
|
371
|
+
clearSocket();
|
|
372
|
+
reconnect(3000);
|
|
299
373
|
});
|
|
300
|
-
clientPrivateKey = privateKey;
|
|
301
374
|
|
|
302
|
-
|
|
375
|
+
socket.on('disconnect', r => {
|
|
376
|
+
if (initIte !== instance_id || wasHandled) return;
|
|
377
|
+
wasHandled = true;
|
|
378
|
+
clearSocket();
|
|
379
|
+
if (r === 'io client disconnect' || r === 'io server disconnect') {
|
|
380
|
+
resultant.destroy();
|
|
381
|
+
} else reconnect(0);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
clientPrivateKey = privateKey;
|
|
303
385
|
socketListenerList.forEach(([_, method, route, callback]) => {
|
|
304
386
|
socket[method](route, callback);
|
|
305
387
|
});
|
|
306
388
|
}
|
|
307
389
|
|
|
390
|
+
const remountInit = () => {
|
|
391
|
+
makeSocketCallback();
|
|
392
|
+
if (socket) {
|
|
393
|
+
++initIte;
|
|
394
|
+
clearSocket();
|
|
395
|
+
}
|
|
396
|
+
init();
|
|
397
|
+
}
|
|
398
|
+
|
|
308
399
|
if (disableAuth) {
|
|
309
400
|
init();
|
|
310
401
|
} else {
|
|
311
402
|
let lastTokenStatus;
|
|
312
403
|
|
|
313
|
-
tokenListener = listenTokenReady(
|
|
314
|
-
if (lastTokenStatus === (
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
init();
|
|
404
|
+
tokenListener = listenTokenReady(ready => {
|
|
405
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
406
|
+
if (ready) {
|
|
407
|
+
remountInit();
|
|
318
408
|
} else {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
409
|
+
makeSocketCallback();
|
|
410
|
+
++initIte;
|
|
411
|
+
clearForegroundListener();
|
|
412
|
+
clearSocket();
|
|
322
413
|
}
|
|
323
|
-
lastTokenStatus =
|
|
414
|
+
lastTokenStatus = ready || false;
|
|
324
415
|
}, projectUrl);
|
|
325
416
|
}
|
|
326
417
|
|
|
@@ -375,10 +466,17 @@ class RNMT {
|
|
|
375
466
|
}
|
|
376
467
|
},
|
|
377
468
|
destroy: () => {
|
|
469
|
+
if (hasCancelled) return;
|
|
378
470
|
hasCancelled = true;
|
|
379
471
|
tokenListener?.();
|
|
380
|
-
|
|
472
|
+
clearForegroundListener();
|
|
473
|
+
clearSocket();
|
|
381
474
|
socketListenerList = [];
|
|
475
|
+
if (!socketReadyCallback) {
|
|
476
|
+
makeSocketCallback();
|
|
477
|
+
}
|
|
478
|
+
socketReadyCallback[1]('socket already disconnected');
|
|
479
|
+
socketReadyCallback = undefined;
|
|
382
480
|
}
|
|
383
481
|
};
|
|
384
482
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { doSignOut, revokeAuthIntance } from "./index.js";
|
|
2
2
|
import EngineApi from "../../helpers/engine_api";
|
|
3
3
|
import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
|
|
4
|
-
import { decodeBinary, deserializeE2E
|
|
5
|
-
import { awaitStore, buildFetchInterface, buildFetchResult, updateCacheStore } from "../../helpers/utils";
|
|
4
|
+
import { decodeBinary, deserializeE2E } from "../../helpers/peripherals";
|
|
5
|
+
import { awaitReachableServer, awaitStore, buildFetchInterface, buildFetchResult, updateCacheStore } from "../../helpers/utils";
|
|
6
6
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
7
7
|
import { simplifyError } from "simplify-error";
|
|
8
8
|
import { Validator } from "guard-object";
|
|
@@ -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]);
|
|
@@ -201,12 +208,9 @@ const refreshToken = (builder, remainRetries = 1, isForceRefresh) =>
|
|
|
201
208
|
);
|
|
202
209
|
console.error(`refreshToken retry limit exceeded err:`, e);
|
|
203
210
|
} else {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
refreshToken(builder, remainRetries - 1, isForceRefresh).then(resolve, reject);
|
|
208
|
-
}
|
|
209
|
-
}, projectUrl);
|
|
211
|
+
awaitReachableServer(projectUrl).then(() => {
|
|
212
|
+
refreshToken(builder, remainRetries - 1, isForceRefresh).then(resolve, reject);
|
|
213
|
+
});
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
});
|
|
@@ -14,8 +14,7 @@ const {
|
|
|
14
14
|
_customSignin,
|
|
15
15
|
_customSignup,
|
|
16
16
|
_googleSignin,
|
|
17
|
-
_appleSignin
|
|
18
|
-
_areYouOk
|
|
17
|
+
_appleSignin
|
|
19
18
|
} = EngineApi;
|
|
20
19
|
|
|
21
20
|
export default class MTAuth {
|
|
@@ -185,13 +184,14 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
|
|
|
185
184
|
|
|
186
185
|
const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
187
186
|
|
|
187
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
188
|
+
injectFreshToken(builder, r.result);
|
|
189
|
+
|
|
188
190
|
resolve({
|
|
189
191
|
user: parseToken(r.result.token),
|
|
190
192
|
token: r.result.token,
|
|
191
193
|
refreshToken: r.result.refreshToken
|
|
192
194
|
});
|
|
193
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
194
|
-
await injectFreshToken(builder, r.result);
|
|
195
195
|
} catch (e) {
|
|
196
196
|
reject(simplifyCaughtError(e).simpleError);
|
|
197
197
|
}
|
|
@@ -218,14 +218,15 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
|
|
|
218
218
|
|
|
219
219
|
const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
220
220
|
|
|
221
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
222
|
+
injectFreshToken(builder, r.result);
|
|
223
|
+
|
|
221
224
|
resolve({
|
|
222
225
|
user: parseToken(r.result.token),
|
|
223
226
|
token: r.result.token,
|
|
224
227
|
refreshToken: r.result.refreshToken,
|
|
225
228
|
isNewUser: !!r.result.isNewUser
|
|
226
229
|
});
|
|
227
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
228
|
-
await injectFreshToken(builder, r.result);
|
|
229
230
|
} catch (e) {
|
|
230
231
|
reject(simplifyCaughtError(e).simpleError);
|
|
231
232
|
}
|
|
@@ -249,7 +250,10 @@ const clearCacheForSignout = (builder, disposeEmulated) => {
|
|
|
249
250
|
|
|
250
251
|
purgeCache(projectUrl, true);
|
|
251
252
|
if (disposeEmulated) getEmulatedLinks(projectUrl).forEach(e => purgeCache(e));
|
|
252
|
-
|
|
253
|
+
|
|
254
|
+
setTimeout(() => {
|
|
255
|
+
initTokenRefresher({ config: builder });
|
|
256
|
+
}, 600);
|
|
253
257
|
};
|
|
254
258
|
|
|
255
259
|
export const doSignOut = async (builder) => {
|
|
@@ -286,12 +290,7 @@ export const purgePendingToken = async (nodeId) => {
|
|
|
286
290
|
|
|
287
291
|
if (!token) return;
|
|
288
292
|
try {
|
|
289
|
-
|
|
290
|
-
try {
|
|
291
|
-
isConnected = (await (await fetch(_areYouOk(projectUrl), { credentials: 'omit' })).json()).status === 'yes';
|
|
292
|
-
} catch (_) { }
|
|
293
|
-
|
|
294
|
-
if (!isConnected) await awaitReachableServer(projectUrl);
|
|
293
|
+
await awaitReachableServer(projectUrl);
|
|
295
294
|
|
|
296
295
|
const [reqBuilder] = await buildFetchInterface({
|
|
297
296
|
body: { token, r_token },
|
|
@@ -330,14 +329,15 @@ const doProviderSignin = (builder, token, metadata, endpointer) => new Promise(a
|
|
|
330
329
|
|
|
331
330
|
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
332
331
|
|
|
332
|
+
revokeAuthIntance(builder, thisAuthStore);
|
|
333
|
+
injectFreshToken(builder, f.result);
|
|
334
|
+
|
|
333
335
|
resolve({
|
|
334
336
|
user: parseToken(f.result.token),
|
|
335
337
|
token: f.result.token,
|
|
336
338
|
refreshToken: f.result.refreshToken,
|
|
337
339
|
isNewUser: f.result.isNewUser
|
|
338
340
|
});
|
|
339
|
-
revokeAuthIntance(builder, thisAuthStore);
|
|
340
|
-
await injectFreshToken(builder, f.result);
|
|
341
341
|
} catch (e) {
|
|
342
342
|
reject(simplifyCaughtError(e).simpleError);
|
|
343
343
|
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { io } from "socket.io-client";
|
|
2
2
|
import EngineApi from "../../helpers/engine_api";
|
|
3
3
|
import { DatabaseRecordsListener } from "../../helpers/listeners";
|
|
4
|
-
import { deserializeE2E,
|
|
5
|
-
import { awaitStore, buildFetchInterface, buildFetchResult, getReachableServer, updateCacheStore } from "../../helpers/utils";
|
|
4
|
+
import { deserializeE2E, serializeE2E } from "../../helpers/peripherals";
|
|
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,13 +150,14 @@ 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
|
-
connectedListener,
|
|
159
161
|
lastSnapshot;
|
|
160
162
|
|
|
161
163
|
const dispatchSnapshot = s => {
|
|
@@ -176,15 +178,30 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
176
178
|
|
|
177
179
|
awaitStore().then(() => {
|
|
178
180
|
if (hasCancelled) return;
|
|
179
|
-
|
|
180
|
-
connectedListener();
|
|
181
|
+
getReachableServer(projectUrl).then(connected => {
|
|
181
182
|
if (!connected && !hasRespond && !hasCancelled && shouldCache)
|
|
182
183
|
DatabaseRecordsListener.dispatch('d', processId);
|
|
183
|
-
}
|
|
184
|
+
});
|
|
184
185
|
});
|
|
185
186
|
}
|
|
186
187
|
|
|
188
|
+
let foregroundListener;
|
|
189
|
+
const clearForegroundListener = () => {
|
|
190
|
+
if (!foregroundListener) return;
|
|
191
|
+
foregroundListener.remove();
|
|
192
|
+
foregroundListener = undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const clearSocket = () => {
|
|
196
|
+
if (socket) {
|
|
197
|
+
socket.close();
|
|
198
|
+
socket = undefined;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
187
202
|
const init = async () => {
|
|
203
|
+
clearForegroundListener();
|
|
204
|
+
|
|
188
205
|
const processID = ++lastInitRef;
|
|
189
206
|
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
190
207
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
@@ -207,6 +224,7 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
207
224
|
const [encPlate, [privateKey]] = uglify ? await serializeE2E({ _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
208
225
|
|
|
209
226
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
227
|
+
|
|
210
228
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
211
229
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
212
230
|
extraHeaders,
|
|
@@ -217,7 +235,8 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
217
235
|
},
|
|
218
236
|
_m_internal: true,
|
|
219
237
|
_m_route: (findOne ? _listenDocument : _listenCollection)(uglify)
|
|
220
|
-
}
|
|
238
|
+
},
|
|
239
|
+
reconnection: false
|
|
221
240
|
});
|
|
222
241
|
|
|
223
242
|
socket.on('mSnapshot', async ([err, snapshot]) => {
|
|
@@ -234,27 +253,83 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
234
253
|
if (shouldCache) insertRecord(builder, config, await accessId, snapshot, episode);
|
|
235
254
|
}
|
|
236
255
|
});
|
|
237
|
-
};
|
|
238
256
|
|
|
239
|
-
|
|
257
|
+
const reconnect = (timeout) => {
|
|
258
|
+
if (processID !== lastInitRef || hasCancelled) return;
|
|
240
259
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
const reloadIntance = async () => {
|
|
261
|
+
if (processID === lastInitRef && !hasCancelled) remountInit();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (AppState.currentState === 'active') {
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
awaitReachableServer(projectUrl).then(reloadIntance);
|
|
267
|
+
}, timeout);
|
|
268
|
+
} else {
|
|
269
|
+
foregroundListener = AppState.addEventListener('change', s => {
|
|
270
|
+
if (s === 'active') {
|
|
271
|
+
clearForegroundListener();
|
|
272
|
+
reloadIntance();
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
246
276
|
}
|
|
247
|
-
lastToken = t || null;
|
|
248
|
-
}, projectUrl);
|
|
249
277
|
|
|
250
|
-
|
|
278
|
+
let wasHandled;
|
|
279
|
+
socket.on('connect_error', () => {
|
|
280
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
281
|
+
wasHandled = true;
|
|
282
|
+
clearSocket();
|
|
283
|
+
reconnect(3000);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
socket.on('disconnect', r => {
|
|
287
|
+
if (processID !== lastInitRef || wasHandled) return;
|
|
288
|
+
wasHandled = true;
|
|
289
|
+
clearSocket();
|
|
290
|
+
if (r === 'io client disconnect' || r === 'io server disconnect') {
|
|
291
|
+
canceller();
|
|
292
|
+
} else reconnect(0);
|
|
293
|
+
});
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const remountInit = () => {
|
|
297
|
+
if (socket) {
|
|
298
|
+
++lastInitRef;
|
|
299
|
+
clearSocket();
|
|
300
|
+
}
|
|
301
|
+
init();
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
let lastTokenStatus;
|
|
305
|
+
let tokenListener;
|
|
306
|
+
|
|
307
|
+
if (disableAuth) {
|
|
308
|
+
init();
|
|
309
|
+
} else {
|
|
310
|
+
tokenListener = listenTokenReady(ready => {
|
|
311
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
312
|
+
if (ready) {
|
|
313
|
+
remountInit();
|
|
314
|
+
} else {
|
|
315
|
+
++lastInitRef;
|
|
316
|
+
clearForegroundListener();
|
|
317
|
+
clearSocket();
|
|
318
|
+
}
|
|
319
|
+
lastTokenStatus = ready || false;
|
|
320
|
+
}, projectUrl);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const canceller = () => {
|
|
251
324
|
if (hasCancelled) return;
|
|
252
325
|
hasCancelled = true;
|
|
253
|
-
connectedListener?.();
|
|
254
326
|
cacheListener?.();
|
|
255
327
|
tokenListener?.();
|
|
256
|
-
|
|
328
|
+
clearForegroundListener();
|
|
329
|
+
clearSocket();
|
|
257
330
|
}
|
|
331
|
+
|
|
332
|
+
return canceller;
|
|
258
333
|
};
|
|
259
334
|
|
|
260
335
|
const initOnDisconnectionTask = ({ builder, connectData, disconnectData }) => {
|
|
@@ -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,46 +393,109 @@ 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') {
|
|
449
|
+
canceller();
|
|
450
|
+
} else reconnect(0);
|
|
451
|
+
});
|
|
452
|
+
};
|
|
327
453
|
|
|
328
|
-
const
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
socket = undefined;
|
|
333
|
-
setTimeout(init, 500);
|
|
334
|
-
} else init();
|
|
454
|
+
const remountInit = () => {
|
|
455
|
+
if (socket) {
|
|
456
|
+
++lastInitRef;
|
|
457
|
+
clearSocket();
|
|
335
458
|
}
|
|
336
|
-
|
|
337
|
-
}
|
|
459
|
+
init();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
let lastTokenStatus;
|
|
463
|
+
let tokenListener;
|
|
464
|
+
|
|
465
|
+
if (disableAuth) {
|
|
466
|
+
init();
|
|
467
|
+
} else {
|
|
468
|
+
tokenListener = listenTokenReady(ready => {
|
|
469
|
+
if (lastTokenStatus === (ready || false)) return;
|
|
470
|
+
if (ready) {
|
|
471
|
+
remountInit();
|
|
472
|
+
} else {
|
|
473
|
+
++lastInitRef;
|
|
474
|
+
clearForegroundListener();
|
|
475
|
+
clearSocket();
|
|
476
|
+
}
|
|
477
|
+
lastTokenStatus = ready || false;
|
|
478
|
+
}, projectUrl);
|
|
479
|
+
}
|
|
338
480
|
|
|
339
|
-
|
|
481
|
+
const canceller = () => {
|
|
340
482
|
if (hasCancelled) return;
|
|
341
483
|
hasCancelled = true;
|
|
342
484
|
tokenListener?.();
|
|
485
|
+
clearForegroundListener();
|
|
343
486
|
if (socket) {
|
|
344
487
|
const thisSocket = socket;
|
|
345
|
-
|
|
346
|
-
thisSocket.
|
|
347
|
-
|
|
488
|
+
try {
|
|
489
|
+
thisSocket.timeout(5000).emitWithAck(_cancelDisconnectWriteTask(uglify)).finally(() => {
|
|
490
|
+
thisSocket.close();
|
|
491
|
+
});
|
|
492
|
+
} catch (error) {
|
|
493
|
+
console.warn('socket closure error:', error);
|
|
494
|
+
}
|
|
348
495
|
}
|
|
349
496
|
};
|
|
497
|
+
|
|
498
|
+
return canceller;
|
|
350
499
|
};
|
|
351
500
|
|
|
352
501
|
const countCollection = async (builder, config) => {
|
|
@@ -410,15 +559,12 @@ const countCollection = async (builder, config) => {
|
|
|
410
559
|
} else if (retries > maxRetries) {
|
|
411
560
|
finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
|
|
412
561
|
} else {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
}, projectUrl);
|
|
562
|
+
awaitReachableServer(projectUrl).then(() => {
|
|
563
|
+
readValue().then(
|
|
564
|
+
e => { finalize(e); },
|
|
565
|
+
e => { finalize(undefined, e); }
|
|
566
|
+
);
|
|
567
|
+
});
|
|
422
568
|
}
|
|
423
569
|
}
|
|
424
570
|
});
|
|
@@ -588,20 +734,18 @@ const findObject = async (builder, initConfig) => {
|
|
|
588
734
|
} else if (retries > maxRetries) {
|
|
589
735
|
finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
|
|
590
736
|
} else {
|
|
591
|
-
|
|
592
|
-
if (
|
|
737
|
+
awaitReachableServer(projectUrl).then(() => {
|
|
738
|
+
if (intruder) {
|
|
593
739
|
intruder.resolve = undefined;
|
|
594
740
|
intruder.reject = undefined;
|
|
595
|
-
onlineListener();
|
|
596
741
|
readValue().then(
|
|
597
742
|
e => { finalize(e); },
|
|
598
743
|
e => { finalize(undefined, e); }
|
|
599
744
|
);
|
|
600
745
|
}
|
|
601
|
-
}
|
|
746
|
+
});
|
|
602
747
|
|
|
603
748
|
const cleanseIntruder = () => {
|
|
604
|
-
onlineListener?.();
|
|
605
749
|
intruder = undefined;
|
|
606
750
|
}
|
|
607
751
|
|
|
@@ -737,15 +881,12 @@ const commitData = async (builder, value, type, config) => {
|
|
|
737
881
|
);
|
|
738
882
|
} else {
|
|
739
883
|
if (delivery === DELIVERY.NO_CACHE_AWAIT) {
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
}, projectUrl);
|
|
884
|
+
awaitReachableServer(projectUrl).then(() => {
|
|
885
|
+
sendValue().then(
|
|
886
|
+
e => { finalize(e.a, undefined, e.c); },
|
|
887
|
+
e => { finalize(undefined, e.b, e.c); }
|
|
888
|
+
);
|
|
889
|
+
});
|
|
749
890
|
} else if (shouldCache) finalize({ status: 'queued' });
|
|
750
891
|
else finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
751
892
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Buffer } from "buffer";
|
|
2
|
-
import { deserializeE2E,
|
|
3
|
-
import { awaitStore, getReachableServer } from "../../helpers/utils";
|
|
2
|
+
import { deserializeE2E, niceHash, normalizeRoute, serializeE2E } from "../../helpers/peripherals";
|
|
3
|
+
import { awaitReachableServer, awaitStore, getReachableServer } from "../../helpers/utils";
|
|
4
4
|
import { RETRIEVAL } from "../../helpers/values";
|
|
5
5
|
import { Scoped } from "../../helpers/variables";
|
|
6
6
|
import { awaitRefreshToken, parseToken } from "../auth/accessor";
|
|
@@ -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) {
|
|
@@ -214,15 +216,12 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
214
216
|
} else if (retries > maxRetries) {
|
|
215
217
|
finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
216
218
|
} else {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
}, projectUrl);
|
|
219
|
+
awaitReachableServer(projectUrl).then(() => {
|
|
220
|
+
callFetch().then(
|
|
221
|
+
e => finalize(e),
|
|
222
|
+
e => finalize(undefined, e)
|
|
223
|
+
);
|
|
224
|
+
});
|
|
226
225
|
}
|
|
227
226
|
}
|
|
228
227
|
});
|