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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mosquito-transport",
3
- "version": "0.0.51",
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.1",
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('');
@@ -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 awaitReachableServer = (projectUrl) => new Promise(resolve => {
157
- if (Scoped.IS_CONNECTED[projectUrl]) {
158
- resolve();
159
- return;
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
- const l = ServerReachableListener.listenToPersist(projectUrl, t => {
162
- if (t) {
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
- l();
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) => new Promise(resolve => {
170
- if (typeof Scoped.IS_CONNECTED[projectUrl] === 'boolean') {
171
- resolve(Scoped.IS_CONNECTED[projectUrl]);
172
- return;
173
- }
174
- const l = ServerReachableListener.listenToPersist(projectUrl, t => {
175
- if (typeof t === 'boolean') {
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 });
@@ -4,7 +4,7 @@ export const Scoped = {
4
4
  PendingIte: 0,
5
5
  AnyProcessIte: 0,
6
6
  IS_CONNECTED: {},
7
- IS_TOKEN_READY: {},
7
+ AreYouOkPromise: {},
8
8
  InitializedProject: {},
9
9
  ReleaseCacheData: undefined,
10
10
  AuthJWTToken: {},
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import 'react-native-get-random-values';
2
- import { deserializeE2E, listenReachableServer, serializeE2E } from "./helpers/peripherals";
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, TokenRefreshListener } from "./helpers/listeners";
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, isVirtualMachineFocused = true;
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
- isConnected = true;
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
- fetch(_areYouOk(projectUrl), { credentials: 'omit' }).then(async r => {
97
- if ((await r.json()).status === 'yes') {
98
- if (ref === connectionIte) onConnect();
99
- } else throw null;
100
- }).catch(() => {
101
- if (ref === connectionIte) onDisconnect();
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', (s) => {
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 = makeSocketCallback(),
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 }) => new Promise(async (resolve, reject) => {
229
- const [route, ...emittion] = emittionx;
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
- if (typeof route !== 'string')
232
- throw `expected ${promise ? 'emitWithAck' : 'emit'} first argument to be a string type`;
253
+ let hasResolved, stime = Date.now();
233
254
 
234
- if (restrictedRoute.includes(route))
235
- throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
255
+ const timer = timeout ? setTimeout(() => {
256
+ hasResolved = true;
257
+ reject(new Error('emittion timeout'));
258
+ }, timeout) : undefined;
236
259
 
237
- let hasResolved, stime = Date.now();
260
+ await socketReadyPromise;
261
+ if (hasResolved) return;
262
+ clearTimeout(timer);
238
263
 
239
- const timer = timeout ? setTimeout(() => {
240
- hasResolved = true;
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
- await socketReadyPromise;
245
- if (hasResolved) return;
246
- clearTimeout(timer);
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
- try {
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
- const lastEmit = emittion.slice(-1)[0];
252
- const hasEmitable = typeof lastEmit === 'function';
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
- const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(mit, undefined, serverE2E_PublicKey) : [undefined, []];
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
- if (hasEmitable && promise)
258
- throw 'emitWithAck cannot have function in it argument';
282
+ if (uglify) {
283
+ res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
284
+ } else res = args;
259
285
 
260
- const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
261
- [uglify ? reqBuilder : mit, not_encrypted],
262
- ...hasEmitable ? [async function () {
263
- const [[args, not_encrypted]] = [...arguments];
264
- let res;
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
- if (uglify) {
267
- res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
268
- } else res = args;
297
+ let initIte = 0;
298
+ let foregroundListener;
299
+ const clearForegroundListener = () => {
300
+ if (!foregroundListener) return;
301
+ foregroundListener.remove();
302
+ foregroundListener = undefined;
303
+ }
269
304
 
270
- lastEmit(...discloseSocketArguments([res, not_encrypted]));
271
- }] : []
272
- );
273
- if (promise && result) {
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
- socketReadyCallback();
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(status => {
314
- if (lastTokenStatus === (status || false)) return;
315
-
316
- if (status) {
317
- init();
404
+ tokenListener = listenTokenReady(ready => {
405
+ if (lastTokenStatus === (ready || false)) return;
406
+ if (ready) {
407
+ remountInit();
318
408
  } else {
319
- socket?.close?.();
320
- socket = undefined;
321
- socketReadyPromise = makeSocketCallback();
409
+ makeSocketCallback();
410
+ ++initIte;
411
+ clearForegroundListener();
412
+ clearSocket();
322
413
  }
323
- lastTokenStatus = status || false;
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
- if (socket) socket.close();
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, listenReachableServer } from "../../helpers/peripherals";
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
- const runningProcess = Scoped.TokenRefreshProcess[projectUrl];
101
- if (runningProcess) return runningProcess;
102
-
103
- Scoped.TokenRefreshProcess[projectUrl] =
104
- refreshToken(config, maxRetries, forceRefresh);
105
-
106
- Scoped.TokenRefreshProcess[projectUrl].finally(() => {
107
- delete Scoped.TokenRefreshProcess[projectUrl];
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 (iteRef !== lastIte || !(await hasTokenExpire(projectUrl))) return;
124
+ if (!(await hasTokenExpire(projectUrl)) || iteRef !== lastIte) return;
124
125
  clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
125
126
  notifyAuthReady();
126
127
  rizz();
127
- }, 9000);
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) => Object.entries(CacheStore.EmulatedAuth)
153
- .filter(([_, v]) => v === projectUrl)
154
- .map(v => v[0]);
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] && isForceRefresh;
186
+ const isInit = !Scoped.InitiatedForcedToken[projectUrl];
180
187
 
181
188
  triggerAuthToken(projectUrl, isInit);
182
- if (isForceRefresh) Scoped.InitiatedForcedToken[projectUrl] = true;
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
- const l = listenReachableServer(c => {
205
- if (c) {
206
- l();
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
- initTokenRefresher({ config: builder });
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
- let isConnected;
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, listenReachableServer, niceTry, serializeE2E } from "../../helpers/peripherals";
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, listenToken } from "../auth/accessor";
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
- connectedListener = listenReachableServer(async connected => {
180
- connectedListener();
181
+ getReachableServer(projectUrl).then(connected => {
181
182
  if (!connected && !hasRespond && !hasCancelled && shouldCache)
182
183
  DatabaseRecordsListener.dispatch('d', processId);
183
- }, projectUrl);
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
- init();
257
+ const reconnect = (timeout) => {
258
+ if (processID !== lastInitRef || hasCancelled) return;
240
259
 
241
- const tokenListener = listenToken(t => {
242
- if ((t || null) !== lastToken) {
243
- socket?.close?.();
244
- socket = undefined;
245
- init();
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
- return () => {
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
- if (socket) socket.close();
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
- let hasCancelled,
283
- /**
284
- * @type {import('socket.io-client').Socket}
285
- */
286
- socket,
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
- init();
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 tokenListener = disableAuth ? undefined : listenToken(async t => {
329
- if ((t || null) !== lastToken) {
330
- if (socket) {
331
- socket.close();
332
- socket = undefined;
333
- setTimeout(init, 500);
334
- } else init();
454
+ const remountInit = () => {
455
+ if (socket) {
456
+ ++lastInitRef;
457
+ clearSocket();
335
458
  }
336
- lastToken = t;
337
- }, projectUrl);
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
- return () => {
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
- return niceTry(() => thisSocket.timeout(5000).emitWithAck(_cancelDisconnectWriteTask(uglify))).finally(() => {
346
- thisSocket.close();
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
- const onlineListener = listenReachableServer(connected => {
414
- if (connected) {
415
- onlineListener();
416
- readValue().then(
417
- e => { finalize(e); },
418
- e => { finalize(undefined, e); }
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
- const onlineListener = listenReachableServer(connected => {
592
- if (connected) {
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
- }, projectUrl);
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
- const onlineListener = listenReachableServer(connected => {
741
- if (connected) {
742
- onlineListener();
743
- sendValue().then(
744
- e => { finalize(e.a, undefined, e.c); },
745
- e => { finalize(undefined, e.b, e.c); }
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, listenReachableServer, niceHash, normalizeRoute, serializeE2E } from "../../helpers/peripherals";
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) insertFetchResources(projectUrl, reqId, resObj);
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
- const listener = listenReachableServer(async online => {
218
- if (online) {
219
- listener();
220
- callFetch().then(
221
- e => finalize(e),
222
- e => finalize(undefined, e)
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
  });