react-native-mosquito-transport 0.0.14 → 0.0.15

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/TODO ADDED
@@ -0,0 +1,3 @@
1
+ - fix local cache query on sqlite and fs
2
+ - fix and add all mongodb query and update operator
3
+ - reauthenticate
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <Workspace
3
+ version = "1.0">
4
+ </Workspace>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mosquito-transport",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "React native javascript sdk for mosquito-transport (https://github.com/deflexable/mosquito-transport)",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -36,7 +36,7 @@
36
36
  "react-native-get-random-values": "^1.9.0",
37
37
  "set-large-timeout": "^1.0.1",
38
38
  "socket.io-client": "^4.6.2",
39
- "subscription-listener": "^1.0.9",
39
+ "subscription-listener": "^1.1.2",
40
40
  "tweetnacl": "^1.0.3"
41
41
  },
42
42
  "engines": {
@@ -14,7 +14,6 @@ const apis = {
14
14
  _twitterSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._twitterSignin(baseApi)) : '_twitterSignin'}`,
15
15
  _githubSignin: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._githubSignin(baseApi)) : '_githubSignin'}`,
16
16
  _signOut: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._signOut(baseApi)) : '_signOut'}`,
17
- _invalidateToken: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._invalidateToken(baseApi)) : '_invalidateToken'}`,
18
17
  _refreshAuthToken: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._refreshAuthToken(baseApi)) : '_refreshAuthToken'}`,
19
18
  _downloadFile: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._downloadFile(baseApi)) : '_downloadFile'}`,
20
19
  _uploadFile: (baseApi, ugly) => `${baseApi}/${ugly ? encodeBinary(apis._uploadFile(baseApi)) : '_uploadFile'}`,
@@ -5,7 +5,7 @@ import { CacheStore, Scoped } from "./variables";
5
5
  import { decryptString, encryptString, niceTry, serializeE2E } from "./peripherals";
6
6
 
7
7
  export const updateCacheStore = () => {
8
- const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE } = Scoped.ReleaseCacheData;
8
+ const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE, io } = Scoped.ReleaseCacheData;
9
9
 
10
10
  clearTimeout(Scoped.cacheStorageReducer);
11
11
  Scoped.cacheStorageReducer = setTimeout(() => {
@@ -15,7 +15,9 @@ export const updateCacheStore = () => {
15
15
  cachePassword
16
16
  );
17
17
 
18
- if (cacheProtocol === CACHE_PROTOCOL.ASYNC_STORAGE) {
18
+ if (io) {
19
+ io.output(txt);
20
+ } else if (cacheProtocol === CACHE_PROTOCOL.ASYNC_STORAGE) {
19
21
  AsyncStorage.setItem(CACHE_STORAGE_PATH, txt);
20
22
  } else {
21
23
  const fs = require('react-native-fs');
@@ -25,11 +27,13 @@ export const updateCacheStore = () => {
25
27
  }
26
28
 
27
29
  export const releaseCacheStore = async (builder) => {
28
- const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE } = builder;
30
+ const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE, io } = builder;
29
31
 
30
32
  let txt;
31
33
 
32
- if (cacheProtocol === CACHE_PROTOCOL.ASYNC_STORAGE) {
34
+ if (io) {
35
+ txt = await io.input();
36
+ } else if (cacheProtocol === CACHE_PROTOCOL.ASYNC_STORAGE) {
33
37
  txt = await niceTry(() => AsyncStorage.getItem(CACHE_STORAGE_PATH));
34
38
  } else {
35
39
  const fs = require('react-native-fs');
@@ -37,7 +41,7 @@ export const releaseCacheStore = async (builder) => {
37
41
  }
38
42
 
39
43
  const j = JSON.parse(decryptString(txt || '', cachePassword, cachePassword) || '{}');
40
-
44
+
41
45
  Object.entries(j).forEach(([k, v]) => {
42
46
  CacheStore[k] = v;
43
47
  });
@@ -13,7 +13,8 @@ export const CACHE_STORAGE_PATH = encodeBinary('MOSQUITO_TRANSPORT_FREEZER'),
13
13
 
14
14
  export const CACHE_PROTOCOL = {
15
15
  ASYNC_STORAGE: 'async-storage',
16
- REACT_NATIVE_FS: 'reat-native-fs'
16
+ REACT_NATIVE_FS: 'reat-native-fs',
17
+ SQLITE: 'sqlite' // TODO:
17
18
  };
18
19
 
19
20
  export const RETRIEVAL = {
@@ -43,8 +44,8 @@ export const WRITE_OPS = {
43
44
  $MAX: '$max',
44
45
  $MIN: '$min',
45
46
  $MUL: '$mul',
46
- $RENAME: '$rename'
47
- // $SET_ON_INSERT: '$setOnInsert'
47
+ $RENAME: '$rename',
48
+ $SET_ON_INSERT: '$setOnInsert'
48
49
  };
49
50
  export const WRITE_OPS_LIST = Object.values(WRITE_OPS);
50
51
 
package/src/index.d.ts CHANGED
@@ -25,9 +25,26 @@ interface mtimestamp { $timestamp: 'now' }
25
25
  export const TIMESTAMP: mtimestamp;
26
26
  export function DOCUMENT_EXTRACTION(path: string): { $dynamicValue: number };
27
27
 
28
+ interface ReleaseCacheOption_IO {
29
+ /**
30
+ * This password will be used to encrypt data stored locally
31
+ */
32
+ cachePassword?: string;
33
+ io: {
34
+ /**
35
+ * feeds mosquito-transport data
36
+ */
37
+ input: () => string;
38
+ /**
39
+ * emits mosquito-transport internal data
40
+ */
41
+ output: (data: string) => void;
42
+ }
43
+ }
44
+
28
45
  interface ReleaseCacheOption {
29
46
  /**
30
- * This password will be used to securely store your data locally
47
+ * This password will be used to encrypt data stored locally
31
48
  */
32
49
  cachePassword?: string;
33
50
  /**
@@ -62,7 +79,7 @@ interface BatchWriteConfig extends WriteConfig {
62
79
 
63
80
  export default class RNMT {
64
81
  constructor(config: RNMTConfig);
65
- static releaseCache(option?: ReleaseCacheOption): void;
82
+ static releaseCache(option?: ReleaseCacheOption | ReleaseCacheOption_IO): void;
66
83
  getDatabase(dbName?: string, dbUrl?: string): GetDatabase;
67
84
  collection(path: string): RNMTCollection;
68
85
  auth(): RNMTAuth;
@@ -274,6 +291,7 @@ interface DocumentFind {
274
291
  $or?: any[];
275
292
  $text?: {
276
293
  $search: string;
294
+ $field: string;
277
295
  $language?: string;
278
296
  $caseSensitive?: boolean;
279
297
  $diacriticSensitive?: boolean;
@@ -303,8 +321,11 @@ interface RNMTAuth {
303
321
  listenVerifiedStatus: (callback?: (verified?: boolean) => void, onError?: (error?: ErrorResponse) => void) => () => void;
304
322
  listenAuthToken: (callback: (token: string) => void) => () => void;
305
323
  getAuthToken: () => Promise<string>;
306
- listenAuth: (callback: (auth: AuthData) => void) => () => void;
307
- getAuth: () => Promise<AuthData>;
324
+ getRefreshToken: () => Promise<string>;
325
+ getRefreshTokenData: () => Promise<RefreshTokenData>;
326
+ parseToken: () => string;
327
+ listenAuth: (callback: (auth: TokenEventData) => void) => () => void;
328
+ getAuth: () => Promise<TokenEventData>;
308
329
  signOut: () => Promise<void>;
309
330
  forceRefreshToken: () => Promise<string>;
310
331
  }
@@ -325,14 +346,35 @@ interface AuthData {
325
346
  signupMethod: 'google' | 'apple' | 'custom' | 'github' | 'twitter' | 'facebook' | string;
326
347
  currentAuthMethod: 'google' | 'apple' | 'custom' | 'github' | 'twitter' | 'facebook' | string;
327
348
  joinedOn: number;
328
- encryptionKey: string;
329
349
  uid: string;
330
350
  claims: Object;
331
351
  emailVerified: boolean;
352
+ tokenID: string;
353
+ disabled: boolean;
354
+ entityOf: string;
332
355
  profile: {
333
356
  photo: string;
334
357
  name: string;
335
- }
358
+ },
359
+ exp: number;
360
+ aud: string;
361
+ iss: string;
362
+ sub: string;
363
+ }
364
+
365
+ interface RefreshTokenData {
366
+ uid: string;
367
+ tokenID: string;
368
+ isRefreshToken: true;
369
+ }
370
+
371
+ interface TokenEventData extends AuthData {
372
+ tokenManager: TokenManager | null;
373
+ }
374
+
375
+ interface TokenManager {
376
+ refreshToken: string;
377
+ accessToken: string;
336
378
  }
337
379
 
338
380
  interface RNMTStorage {
package/src/index.js CHANGED
@@ -35,7 +35,9 @@ class RNMT {
35
35
  };
36
36
  const { projectUrl } = this.config;
37
37
 
38
+ this.config.secureUrl = projectUrl.startsWith('https');
38
39
  this.config.baseUrl = projectUrl.split('://')[1];
40
+ this.config.wsPrefix = this.config.secureUrl ? 'wss' : 'ws';
39
41
 
40
42
  if (!Scoped.ReleaseCacheData)
41
43
  throw `releaseCache must be called before creating any ${this.constructor.name} instance`;
@@ -46,8 +48,12 @@ class RNMT {
46
48
  triggerAuthToken(projectUrl);
47
49
  initTokenRefresher({ ...this.config }, true);
48
50
 
49
- const socket = io(`ws://${projectUrl.split('://')[1]}`, {
50
- auth: { _m_internal: true }
51
+ const socket = io(`${this.config.wsPrefix}://${projectUrl.split('://')[1]}`, {
52
+ auth: { _m_internal: true, _from_base: true }
53
+ });
54
+
55
+ socket.on('_signal_signout', () => {
56
+ this.auth().signOut();
51
57
  });
52
58
 
53
59
  socket.on('connect', () => {
@@ -69,7 +75,7 @@ class RNMT {
69
75
  }
70
76
 
71
77
  static releaseCache(prop) {
72
- if (Scoped.ReleaseCacheData) throw `calling ${this.name} multiple times is prohibited`;
78
+ if (Scoped.ReleaseCacheData) throw `calling ${this.name}() multiple times is prohibited`;
73
79
  validateReleaseCacheProp({ ...prop });
74
80
  Scoped.ReleaseCacheData = { ...prop };
75
81
  releaseCacheStore({ ...prop });
@@ -95,7 +101,7 @@ class RNMT {
95
101
 
96
102
  getSocket = (configOpts) => {
97
103
  const { disableAuth, authHandshake } = configOpts || {},
98
- { projectUrl, uglify, accessKey, serverE2E_PublicKey } = this.config;
104
+ { projectUrl, uglify, accessKey, serverE2E_PublicKey, wsPrefix } = this.config;
99
105
 
100
106
  const restrictedRoute = [
101
107
  _listenCollection,
@@ -194,7 +200,7 @@ class RNMT {
194
200
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
195
201
  const [reqBuilder, [privateKey]] = uglify ? serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
196
202
 
197
- socket = io(`ws://${projectUrl.split('://')[1]}`, {
203
+ socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
198
204
  auth: uglify ? {
199
205
  ugly: true,
200
206
  e2e: reqBuilder
@@ -299,8 +305,17 @@ const validateReleaseCacheProp = (prop) => {
299
305
  throw `Invalid value supplied to cachePassword, value must be a string and greater than 0 characters`;
300
306
  } else if (k === 'cacheProtocol') {
301
307
  if (!cacheList.includes(`${v}`)) throw `unknown value supplied to ${k}, expected any of ${cacheList}`;
308
+ } else if (k === 'io') {
309
+ Object.entries(v).forEach(([k, v]) => {
310
+ if (k === 'input' || k === 'output') {
311
+ if (typeof v !== 'function')
312
+ throw `Invalid value supplied to "io.${k}", expected a function but got "${v}"`;
313
+ } else throw `Unexpected property named "io.${k}"`;
314
+ });
302
315
  } else throw `Unexpected property named ${k}`;
303
316
  });
317
+
318
+ if (!prop?.io && !prop?.cacheProtocol) throw 'You need to provide either "io" or "cacheProtocol"';
304
319
  }
305
320
 
306
321
  const validator = {
@@ -2,8 +2,8 @@ import setLargeTimeout from "set-large-timeout";
2
2
  import { doSignOut } from ".";
3
3
  import EngineApi from "../../helpers/EngineApi";
4
4
  import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
5
- import { decodeBinary, deserializeE2E, listenReachableServer, simplifyCaughtError } from "../../helpers/peripherals";
6
- import { awaitReachableServer, awaitStore, buildFetchInterface, simplifyError, updateCacheStore } from "../../helpers/utils";
5
+ import { decodeBinary, deserializeE2E, listenReachableServer } from "../../helpers/peripherals";
6
+ import { awaitStore, buildFetchInterface, simplifyError, updateCacheStore } from "../../helpers/utils";
7
7
  import { CacheStore, Scoped } from "../../helpers/variables";
8
8
 
9
9
  export const listenToken = (callback, projectUrl) =>
@@ -103,7 +103,6 @@ const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7
103
103
  if (isForceRefresh) Scoped.InitiatedForcedToken[projectUrl] = true;
104
104
  updateCacheStore();
105
105
  initTokenRefresher(builder);
106
- invalidateToken(builder, token);
107
106
  } else reject(lostProcess.simpleError);
108
107
  } catch (e) {
109
108
  if (e.simpleError) {
@@ -129,23 +128,4 @@ const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7
129
128
  }, projectUrl);
130
129
  }
131
130
  }
132
- });
133
-
134
- export const invalidateToken = async (builder, token) => {
135
- try {
136
- const { projectUrl, accessKey, uglify, serverE2E_PublicKey } = builder;
137
- await awaitReachableServer(projectUrl);
138
-
139
- const [reqBuilder] = buildFetchInterface({
140
- body: { token },
141
- accessKey,
142
- uglify,
143
- serverE2E_PublicKey
144
- });
145
-
146
- const r = await (await fetch(EngineApi._invalidateToken(projectUrl, uglify), reqBuilder)).json();
147
- if (r.simpleError) throw r;
148
- } catch (e) {
149
- throw simplifyCaughtError(e).simpleError;
150
- }
151
- }
131
+ });
@@ -42,7 +42,7 @@ export class MTAuth {
42
42
  }
43
43
 
44
44
  listenVerifiedStatus(callback, onError) {
45
- const { projectUrl, serverE2E_PublicKey, uglify, baseUrl } = this.builder;
45
+ const { projectUrl, serverE2E_PublicKey, uglify, baseUrl, wsPrefix } = this.builder;
46
46
 
47
47
  let socket, wasDisconnected, lastToken = Scoped.AuthJWTToken[projectUrl] || null, lastInitRef = 0;
48
48
 
@@ -58,7 +58,7 @@ export class MTAuth {
58
58
  const mtoken = Scoped.AuthJWTToken[projectUrl],
59
59
  [reqBuilder, [privateKey]] = uglify ? serializeE2E({ mtoken }, undefined, serverE2E_PublicKey) : [null, []];
60
60
 
61
- socket = io(`ws://${baseUrl}`, {
61
+ socket = io(`${wsPrefix}://${baseUrl}`, {
62
62
  auth: uglify ? {
63
63
  e2e: reqBuilder,
64
64
  _m_internal: true
@@ -104,6 +104,13 @@ export class MTAuth {
104
104
 
105
105
  listenAuthToken = (callback) => listenToken(callback, this.builder.projectUrl);
106
106
 
107
+ getRefreshToken = async () => {
108
+ await awaitStore();
109
+ return CacheStore.AuthStore[this.builder.projectUrl]?.refreshToken;
110
+ }
111
+
112
+ parseToken = (token) => parseToken(token);
113
+
107
114
  getAuthToken = () => new Promise(resolve => {
108
115
  const l = listenToken(t => {
109
116
  l();
@@ -111,7 +111,7 @@ const {
111
111
  } = EngineApi;
112
112
 
113
113
  const listenDocument = (callback, onError, builder, config) => {
114
- const { projectUrl, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify } = builder,
114
+ const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify } = builder,
115
115
  { find, findOne, sort, direction, limit } = command,
116
116
  { disableAuth } = config || {},
117
117
  accessId = generateRecordID(builder, config),
@@ -163,7 +163,7 @@ const listenDocument = (callback, onError, builder, config) => {
163
163
 
164
164
  const [encPlate, [privateKey]] = uglify ? serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
165
165
 
166
- socket = io(`ws://${baseUrl}`, {
166
+ socket = io(`${wsPrefix}://${baseUrl}`, {
167
167
  auth: uglify ? { e2e: encPlate, _m_internal: true } : {
168
168
  accessKey,
169
169
  _body: authObj,
@@ -217,7 +217,7 @@ const listenDocument = (callback, onError, builder, config) => {
217
217
  }
218
218
 
219
219
  const initOnDisconnectionTask = (builder, value, type) => {
220
- const { projectUrl, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, command, uglify } = builder,
220
+ const { projectUrl, wsPrefix, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, command, uglify } = builder,
221
221
  { find } = command || {},
222
222
  disableAuth = false;
223
223
 
@@ -240,7 +240,7 @@ const initOnDisconnectionTask = (builder, value, type) => {
240
240
  dbUrl
241
241
  };
242
242
 
243
- socket = io(`ws://${baseUrl}`, {
243
+ socket = io(`${wsPrefix}://${baseUrl}`, {
244
244
  auth: uglify ? {
245
245
  e2e: serializeE2E(authObj, mtoken, serverE2E_PublicKey)[0],
246
246
  _m_internal: true
@@ -202,12 +202,19 @@ const evaluateFilter = (data = {}, filter = {}) => {
202
202
  } else logics.push(false);
203
203
  } else if ($ === $TEXT) {
204
204
  if (commandSplit.slice(-1)[0].$ === '$search') {
205
- const { $caseSensitive, $localFields = [], $search } = dataObj.$text;
205
+ const { $caseSensitive, $search, $field } = filter.$text;
206
206
 
207
207
  if (typeof value !== 'string' || typeof $search !== 'string')
208
208
  throw `$search must have a string value`;
209
+ if (!$field) throw '"$field" is required inside "$text" operator when "disableCache=false"';
210
+ const fieldArr = Array.isArray($field) ? $field : [$field];
209
211
 
210
- const searchTxt = $localFields.map(v => getLodash(dataObj, v || '')).map(v =>
212
+ fieldArr.forEach(v => {
213
+ if (typeof v !== 'string' || !v.trim())
214
+ throw `invalid item inside "$field", expected a non-empty string but got "${v}"`;
215
+ });
216
+
217
+ const searchTxt = fieldArr.map(v => getLodash(dataObj, v || '')).map(v =>
211
218
  `${typeof v === 'string' ? v :
212
219
  Array.isArray(v) ? v.map(v => typeof v === 'string' ? v : '').join(' ').trim() : ''}`.trim()
213
220
  ).join(' ').trim();