mosquito-transport-js 0.6.2 → 0.6.4

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": "mosquito-transport-js",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Javascript web sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -179,18 +179,16 @@ export const awaitStore = () => new Promise(resolve => {
179
179
 
180
180
  export const checkAreYouOk = (projectUrl) => {
181
181
  if (!Scoped.AreYouOkPromise[projectUrl]) {
182
- const signal = new AbortController();
183
- const timer = setTimeout(() => {
184
- signal.abort();
185
- }, 9000);
186
- const promise = fetch(engine_api._areYouOk(projectUrl), { credentials: 'omit', signal })
182
+ Scoped.IS_CONNECTED[projectUrl] = undefined;
183
+ ServerReachableListener.dispatchPersist(projectUrl, undefined);
184
+
185
+ const promise = fetch(engine_api._areYouOk(projectUrl), { credentials: 'omit' })
187
186
  .then(async r => (await r.json()).status === 'yes')
188
187
  .catch(() => false)
189
188
  .then(async connected => {
190
189
  Scoped.IS_CONNECTED[projectUrl] = connected;
191
190
  ServerReachableListener.dispatchPersist(projectUrl, connected);
192
191
 
193
- clearTimeout(timer);
194
192
  delete Scoped.AreYouOkPromise[projectUrl];
195
193
  return connected;
196
194
  });
@@ -209,25 +207,31 @@ export const listenReachableServer = (callback, projectUrl) => {
209
207
  });
210
208
  };
211
209
 
212
- export const awaitReachableServer = (projectUrl) =>
213
- new Promise(async resolve => {
214
- if (!isScreenFocused()) {
215
- if (await checkAreYouOk(projectUrl)) {
210
+ export const awaitReachableServer = (projectUrl, pauseForRetry) =>
211
+ new Promise(resolve => {
212
+ const check = async () => {
213
+ if (!isScreenFocused()) {
214
+ if (await checkAreYouOk(projectUrl)) {
215
+ resolve();
216
+ return;
217
+ }
218
+ }
219
+
220
+ if (Scoped.IS_CONNECTED[projectUrl]) {
216
221
  resolve();
217
222
  return;
218
223
  }
219
- }
220
224
 
221
- if (Scoped.IS_CONNECTED[projectUrl]) {
222
- resolve();
223
- return;
225
+ const l = listenReachableServer(t => {
226
+ if (!t) return;
227
+ resolve();
228
+ l();
229
+ }, projectUrl);
224
230
  }
225
231
 
226
- const l = listenReachableServer(t => {
227
- if (!t) return;
228
- resolve();
229
- l();
230
- }, projectUrl);
232
+ if (pauseForRetry) {
233
+ setTimeout(check, Number.isInteger(pauseForRetry) ? pauseForRetry : 300);
234
+ } else check();
231
235
  });
232
236
 
233
237
  export const getReachableServer = (projectUrl) =>
package/src/index.d.ts CHANGED
@@ -302,6 +302,14 @@ export interface FetchHttpConfig {
302
302
  disableAuth?: boolean;
303
303
  enableMinimizer?: boolean;
304
304
  rawApproach?: boolean;
305
+ /**
306
+ * Wait for the server to come online if the response does not include a header with status 200.
307
+ *
308
+ * This ensures that the fetchHttp method only resolves when a response with status 200 is received; otherwise, it rejects if the server is online but continues returning a non-200 status
309
+ *
310
+ * @default false
311
+ */
312
+ enforce200?: boolean;
305
313
  }
306
314
 
307
315
  type Delievery = 'default' | 'cache-no-await' | 'no-cache-no-await' | 'no-cache-await';
package/src/index.js CHANGED
@@ -73,7 +73,6 @@ export class MosquitoTransport {
73
73
  });
74
74
 
75
75
  let connectionIte = 0;
76
- let chainedPromise;
77
76
  const setConnected = c => {
78
77
  isConnected = c;
79
78
  Scoped.IS_CONNECTED[projectUrl] = isConnected;
@@ -90,11 +89,9 @@ export class MosquitoTransport {
90
89
  };
91
90
 
92
91
  const manualCheckConnection = () => {
93
- if (chainedPromise) return;
94
92
  const ref = ++connectionIte;
95
93
 
96
94
  checkAreYouOk(projectUrl).then(ok => {
97
- chainedPromise = undefined;
98
95
  if (ref !== connectionIte) return;
99
96
  if (ok) {
100
97
  onConnect();
@@ -363,9 +360,7 @@ export class MosquitoTransport {
363
360
  }
364
361
 
365
362
  if (isScreenFocused()) {
366
- setTimeout(() => {
367
- awaitReachableServer(projectUrl).then(reloadIntance);
368
- }, timeout);
363
+ awaitReachableServer(projectUrl, timeout).then(reloadIntance);
369
364
  } else {
370
365
  foregroundListener = listenScreenVisible(visible => {
371
366
  if (visible) {
@@ -396,7 +391,7 @@ export class MosquitoTransport {
396
391
  clearSocket();
397
392
  if (r === 'io client disconnect' || r === 'io server disconnect') {
398
393
  resultant.destroy();
399
- } else reconnect(0);
394
+ } else reconnect(true);
400
395
  });
401
396
 
402
397
  clientPrivateKey = privateKey;
@@ -2,7 +2,7 @@ import { doSignOut, revokeAuthIntance } from "./index.js";
2
2
  import EngineApi from "../../helpers/engine_api";
3
3
  import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
4
4
  import { decodeBinary, deserializeE2E } from "../../helpers/peripherals";
5
- import { awaitReachableServer, awaitStore, buildFetchInterface, buildFetchResult, 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 { simplifyError } from "simplify-error";
8
8
  import { Validator } from "guard-object";
@@ -75,6 +75,19 @@ export const awaitRefreshToken = (projectUrl) =>
75
75
  }
76
76
  });
77
77
 
78
+ export const ensureActiveToken = async (projectUrl) => {
79
+ if (await getReachableServer(projectUrl)) {
80
+ await awaitRefreshToken(projectUrl);
81
+ } else {
82
+ const emulatedURL = CacheStore.EmulatedAuth[projectUrl];
83
+ const { token } = CacheStore.AuthStore[emulatedURL || projectUrl] || {};
84
+
85
+ if (token && await hasTokenExpire(emulatedURL || projectUrl)) {
86
+ throw 'unable to refreshed expired token because of unreachable internet connection';
87
+ }
88
+ }
89
+ }
90
+
78
91
  export const listenTokenReady = (callback, projectUrl) => TokenRefreshListener.listenToPersist(projectUrl, callback);
79
92
 
80
93
  export const initTokenRefresher = async ({ config, forceRefresh, justCheck }) => {
@@ -217,7 +230,7 @@ const refreshToken = (builder, remainRetries = 1, isForceRefresh) =>
217
230
  );
218
231
  console.error(`refreshToken retry limit exceeded err:`, e);
219
232
  } else {
220
- awaitReachableServer(projectUrl).then(() => {
233
+ awaitReachableServer(projectUrl, true).then(() => {
221
234
  refreshToken(builder, remainRetries - 1, isForceRefresh).then(resolve, reject);
222
235
  });
223
236
  }
@@ -54,11 +54,13 @@ export default class MTAuth {
54
54
  const processID = ++lastInitRef;
55
55
  await awaitRefreshToken(projectUrl);
56
56
 
57
+ if (processID !== lastInitRef || hasCancelled) return;
58
+
57
59
  if (!Scoped.AuthJWTToken[projectUrl]) {
58
60
  onError?.(simplifyError('user_login_required', 'You must be signed-in to use this method').simpleError);
59
61
  return;
60
62
  }
61
- if (processID !== lastInitRef || hasCancelled) return;
63
+
62
64
  const mtoken = Scoped.AuthJWTToken[projectUrl],
63
65
  [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ mtoken }, undefined, serverE2E_PublicKey) : [null, []];
64
66
 
@@ -251,6 +253,7 @@ const clearCacheForSignout = (builder, disposeEmulated) => {
251
253
  purgeCache(projectUrl, true);
252
254
  if (disposeEmulated) getEmulatedLinks(projectUrl).forEach(e => purgeCache(e));
253
255
 
256
+ clearInterval(Scoped.TokenRefreshTimer[projectUrl]);
254
257
  setTimeout(() => {
255
258
  initTokenRefresher({ config: builder });
256
259
  }, 600);
@@ -6,7 +6,7 @@ import { awaitReachableServer, awaitStore, buildFetchInterface, buildFetchResult
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, listenTokenReady } from "../auth/accessor";
9
+ import { awaitRefreshToken, ensureActiveToken, listenTokenReady } from "../auth/accessor";
10
10
  import { DELIVERY, RETRIEVAL } from "../../helpers/values";
11
11
  import { ObjectId } from "bson";
12
12
  import { guardObject, Validator } from "guard-object";
@@ -261,9 +261,7 @@ const listenDocument = (callback, onError, builder, config) => {
261
261
  }
262
262
 
263
263
  if (isScreenFocused()) {
264
- setTimeout(() => {
265
- awaitReachableServer(projectUrl).then(reloadIntance);
266
- }, timeout);
264
+ awaitReachableServer(projectUrl, timeout).then(reloadIntance);
267
265
  } else {
268
266
  foregroundListener = listenScreenVisible(visible => {
269
267
  if (visible) {
@@ -288,7 +286,7 @@ const listenDocument = (callback, onError, builder, config) => {
288
286
  clearSocket();
289
287
  if (r === 'io client disconnect' || r === 'io server disconnect') {
290
288
  canceller();
291
- } else reconnect(0);
289
+ } else reconnect(true);
292
290
  });
293
291
  };
294
292
 
@@ -419,9 +417,7 @@ const initOnDisconnectionTask = ({ builder, connectData, disconnectData }) => {
419
417
  }
420
418
 
421
419
  if (isScreenFocused()) {
422
- setTimeout(() => {
423
- awaitReachableServer(projectUrl).then(reloadIntance);
424
- }, timeout);
420
+ awaitReachableServer(projectUrl, timeout).then(reloadIntance);
425
421
  } else {
426
422
  foregroundListener = listenScreenVisible(visible => {
427
423
  if (visible) {
@@ -446,7 +442,7 @@ const initOnDisconnectionTask = ({ builder, connectData, disconnectData }) => {
446
442
  clearSocket();
447
443
  if (r === 'io client disconnect' || r === 'io server disconnect') {
448
444
  canceller();
449
- } else reconnect(0);
445
+ } else reconnect(true);
450
446
  });
451
447
  };
452
448
 
@@ -525,8 +521,7 @@ const countCollection = async (builder, config) => {
525
521
  };
526
522
 
527
523
  try {
528
- if (!disableAuth && await getReachableServer(projectUrl))
529
- await awaitRefreshToken(projectUrl);
524
+ if (!disableAuth) await ensureActiveToken(projectUrl);
530
525
 
531
526
  const [reqBuilder, [privateKey]] = await buildFetchInterface({
532
527
  body: {
@@ -558,7 +553,7 @@ const countCollection = async (builder, config) => {
558
553
  } else if (retries > maxRetries) {
559
554
  finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
560
555
  } else {
561
- awaitReachableServer(projectUrl).then(() => {
556
+ awaitReachableServer(projectUrl, true).then(() => {
562
557
  readValue().then(
563
558
  e => { finalize(e); },
564
559
  e => { finalize(undefined, e); }
@@ -675,8 +670,7 @@ const findObject = async (builder, initConfig) => {
675
670
  }
676
671
  }
677
672
 
678
- if (!disableAuth && await getReachableServer(projectUrl))
679
- await awaitRefreshToken(projectUrl);
673
+ if (!disableAuth) await ensureActiveToken(projectUrl);
680
674
 
681
675
  const [reqBuilder, [privateKey]] = await buildFetchInterface({
682
676
  body: {
@@ -733,7 +727,7 @@ const findObject = async (builder, initConfig) => {
733
727
  } else if (retries > maxRetries) {
734
728
  finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
735
729
  } else {
736
- awaitReachableServer(projectUrl).then(() => {
730
+ awaitReachableServer(projectUrl, true).then(() => {
737
731
  if (intruder) {
738
732
  intruder.resolve = undefined;
739
733
  intruder.reject = undefined;
@@ -838,8 +832,7 @@ const commitData = async (builder, value, type, config) => {
838
832
  };
839
833
 
840
834
  try {
841
- if (!disableAuth && await getReachableServer(projectUrl))
842
- await awaitRefreshToken(projectUrl);
835
+ if (!disableAuth) await ensureActiveToken(projectUrl);
843
836
 
844
837
  const [reqBuilder, [privateKey]] = await buildFetchInterface({
845
838
  body: {
@@ -880,7 +873,7 @@ const commitData = async (builder, value, type, config) => {
880
873
  );
881
874
  } else {
882
875
  if (delivery === DELIVERY.NO_CACHE_AWAIT) {
883
- awaitReachableServer(projectUrl).then(() => {
876
+ awaitReachableServer(projectUrl, true).then(() => {
884
877
  sendValue().then(
885
878
  e => { finalize(e.a, undefined, e.c); },
886
879
  e => { finalize(undefined, e.b, e.c); }
@@ -1,9 +1,9 @@
1
1
  import { Buffer } from "buffer";
2
2
  import { deserializeE2E, niceHash, normalizeRoute, serializeE2E } from "../../helpers/peripherals";
3
- import { awaitReachableServer, awaitStore, getReachableServer } from "../../helpers/utils";
3
+ import { awaitReachableServer, awaitStore } from "../../helpers/utils";
4
4
  import { RETRIEVAL } from "../../helpers/values";
5
5
  import { Scoped } from "../../helpers/variables";
6
- import { awaitRefreshToken, parseToken } from "../auth/accessor";
6
+ import { ensureActiveToken, parseToken } from "../auth/accessor";
7
7
  import { simplifyCaughtError } from "simplify-error";
8
8
  import { guardObject, Validator } from "guard-object";
9
9
  import { serialize } from "entity-serializer";
@@ -42,10 +42,11 @@ export const mfetch = async (input = '', init, config) => {
42
42
  enableMinimizer: t => t === undefined || Validator.BOOLEAN(t),
43
43
  rawApproach: t => t === undefined || Validator.BOOLEAN(t),
44
44
  disableAuth: t => t === undefined || Validator.BOOLEAN(t),
45
- retrieval: t => t === undefined || Object.values(RETRIEVAL).includes(t)
45
+ retrieval: t => t === undefined || Object.values(RETRIEVAL).includes(t),
46
+ enforce200: t => t === undefined || Validator.BOOLEAN(t)
46
47
  }).validate(method);
47
48
 
48
- const { retrieval = RETRIEVAL.DEFAULT, enableMinimizer, rawApproach } = method || {};
49
+ const { retrieval = RETRIEVAL.DEFAULT, enableMinimizer, rawApproach, enforce200 } = method || {};
49
50
  const isLink = Validator.LINK(input);
50
51
  const isBaseUrl = isLink || rawApproach;
51
52
  const disableAuth = method?.disableAuth === undefined ? isBaseUrl : method?.disableAuth;
@@ -138,8 +139,7 @@ export const mfetch = async (input = '', init, config) => {
138
139
  }
139
140
  }
140
141
 
141
- if (!disableAuth && await getReachableServer(projectUrl))
142
- await awaitRefreshToken(projectUrl);
142
+ if (!disableAuth) await ensureActiveToken(projectUrl);
143
143
 
144
144
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
145
145
  const initType = rawHeader['content-type'];
@@ -168,6 +168,8 @@ export const mfetch = async (input = '', init, config) => {
168
168
  const { ok, type, status, statusText, redirected, url, headers, size } = f;
169
169
  const simple = headers.get('simple_error');
170
170
 
171
+ if (enforce200 && status !== 200)
172
+ throw `expected response status to be 200 but got ${status}`;
171
173
  if (!isLink && simple) throw { simpleError: JSON.parse(simple) };
172
174
 
173
175
  const buffer = uglified ?
@@ -220,7 +222,7 @@ export const mfetch = async (input = '', init, config) => {
220
222
  } else if (retries > maxRetries) {
221
223
  finalize(undefined, simplifyCaughtError(e).simpleError);
222
224
  } else {
223
- awaitReachableServer(projectUrl).then(() => {
225
+ awaitReachableServer(projectUrl, true).then(() => {
224
226
  callFetch().then(
225
227
  e => finalize(e),
226
228
  e => finalize(undefined, e)