react-native-mosquito-transport 0.0.14 → 0.0.16

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,4 @@
1
+ - fix local cache query on sqlite and fs
2
+ - fix and add all mongodb query and update operator
3
+ - reauthenticate
4
+ - change `Object` in d.ts to [key: string]: any
@@ -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.16",
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": {
@@ -31,12 +31,17 @@
31
31
  "@types/lodash": "^4.14.194",
32
32
  "buffer": "^6.0.3",
33
33
  "crypto-js": "^4.2.0",
34
+ "fast-json-stable-stringify": "^2.1.0",
35
+ "guard-object": "^1.0.6",
34
36
  "json-buffer": "^3.0.1",
35
- "lodash": "^4.17.21",
37
+ "lodash.get": "^4.4.2",
38
+ "lodash.isequal": "^4.5.0",
39
+ "lodash.set": "^4.3.2",
40
+ "lodash.unset": "^4.5.2",
36
41
  "react-native-get-random-values": "^1.9.0",
37
42
  "set-large-timeout": "^1.0.1",
38
43
  "socket.io-client": "^4.6.2",
39
- "subscription-listener": "^1.0.9",
44
+ "subscription-listener": "^1.1.2",
40
45
  "tweetnacl": "^1.0.3"
41
46
  },
42
47
  "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
 
@@ -69,4 +70,13 @@ export const READ_OPS_LIST = Object.values(READ_OPS);
69
70
 
70
71
  export const Regexs = {
71
72
  LINK: () => /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig
72
- }
73
+ };
74
+
75
+ export const AUTH_PROVIDER_ID = {
76
+ GOOGLE: 'google.com',
77
+ FACEBOOK: 'facebook.com',
78
+ PASSWORD: 'password',
79
+ TWITTER: 'x.com',
80
+ GITHUB: 'github.com',
81
+ APPLE: 'apple.com'
82
+ };
package/src/index.d.ts CHANGED
@@ -25,9 +25,63 @@ interface mtimestamp { $timestamp: 'now' }
25
25
  export const TIMESTAMP: mtimestamp;
26
26
  export function DOCUMENT_EXTRACTION(path: string): { $dynamicValue: number };
27
27
 
28
+ type longitude = number;
29
+ type latitude = number;
30
+
31
+ export function GEO_JSON(latitude: latitude, longitude: longitude): {
32
+ type: "Point",
33
+ coordinates: [longitude, latitude],
34
+ };
35
+
36
+ export function FIND_GEO_JSON(coordinates: [latitude, longitude], offSetMeters: number, centerMeters?: number): {
37
+ $near: {
38
+ $geometry: {
39
+ type: "Point",
40
+ coordinates: [longitude, latitude]
41
+ },
42
+ $minDistance: number | 0,
43
+ $maxDistance: number
44
+ }
45
+ };
46
+
47
+ export const AUTH_PROVIDER_ID: auth_provider_id;
48
+
49
+ interface auth_provider_id {
50
+ GOOGLE: 'google.com';
51
+ FACEBOOK: 'facebook.com';
52
+ PASSWORD: 'password';
53
+ TWITTER: 'x.com';
54
+ GITHUB: 'github.com';
55
+ APPLE: 'apple.com';
56
+ }
57
+
58
+ type auth_provider_id_values = auth_provider_id['GOOGLE'] |
59
+ auth_provider_id['FACEBOOK'] |
60
+ auth_provider_id['PASSWORD'] |
61
+ auth_provider_id['GITHUB'] |
62
+ auth_provider_id['TWITTER'] |
63
+ auth_provider_id['APPLE'];
64
+
65
+ interface ReleaseCacheOption_IO {
66
+ /**
67
+ * This password will be used to encrypt data stored locally
68
+ */
69
+ cachePassword?: string;
70
+ io: {
71
+ /**
72
+ * feeds mosquito-transport data
73
+ */
74
+ input: () => string;
75
+ /**
76
+ * emits mosquito-transport internal data
77
+ */
78
+ output: (data: string) => void;
79
+ }
80
+ }
81
+
28
82
  interface ReleaseCacheOption {
29
83
  /**
30
- * This password will be used to securely store your data locally
84
+ * This password will be used to encrypt data stored locally
31
85
  */
32
86
  cachePassword?: string;
33
87
  /**
@@ -62,7 +116,7 @@ interface BatchWriteConfig extends WriteConfig {
62
116
 
63
117
  export default class RNMT {
64
118
  constructor(config: RNMTConfig);
65
- static releaseCache(option?: ReleaseCacheOption): void;
119
+ static releaseCache(option?: ReleaseCacheOption | ReleaseCacheOption_IO): void;
66
120
  getDatabase(dbName?: string, dbUrl?: string): GetDatabase;
67
121
  collection(path: string): RNMTCollection;
68
122
  auth(): RNMTAuth;
@@ -274,6 +328,7 @@ interface DocumentFind {
274
328
  $or?: any[];
275
329
  $text?: {
276
330
  $search: string;
331
+ $field: string;
277
332
  $language?: string;
278
333
  $caseSensitive?: boolean;
279
334
  $diacriticSensitive?: boolean;
@@ -303,8 +358,11 @@ interface RNMTAuth {
303
358
  listenVerifiedStatus: (callback?: (verified?: boolean) => void, onError?: (error?: ErrorResponse) => void) => () => void;
304
359
  listenAuthToken: (callback: (token: string) => void) => () => void;
305
360
  getAuthToken: () => Promise<string>;
306
- listenAuth: (callback: (auth: AuthData) => void) => () => void;
307
- getAuth: () => Promise<AuthData>;
361
+ getRefreshToken: () => Promise<string>;
362
+ getRefreshTokenData: () => Promise<RefreshTokenData>;
363
+ parseToken: () => string;
364
+ listenAuth: (callback: (auth: TokenEventData) => void) => () => void;
365
+ getAuth: () => Promise<TokenEventData>;
308
366
  signOut: () => Promise<void>;
309
367
  forceRefreshToken: () => Promise<string>;
310
368
  }
@@ -322,17 +380,38 @@ interface SignupResult extends SigninResult {
322
380
  interface AuthData {
323
381
  email?: string;
324
382
  metadata: Object;
325
- signupMethod: 'google' | 'apple' | 'custom' | 'github' | 'twitter' | 'facebook' | string;
326
- currentAuthMethod: 'google' | 'apple' | 'custom' | 'github' | 'twitter' | 'facebook' | string;
383
+ signupMethod: auth_provider_id_values;
384
+ currentAuthMethod: auth_provider_id_values;
327
385
  joinedOn: number;
328
- encryptionKey: string;
329
386
  uid: string;
330
387
  claims: Object;
331
388
  emailVerified: boolean;
389
+ tokenID: string;
390
+ disabled: boolean;
391
+ entityOf: string;
332
392
  profile: {
333
393
  photo: string;
334
394
  name: string;
335
- }
395
+ },
396
+ exp: number;
397
+ aud: string;
398
+ iss: string;
399
+ sub: string;
400
+ }
401
+
402
+ interface RefreshTokenData {
403
+ uid: string;
404
+ tokenID: string;
405
+ isRefreshToken: true;
406
+ }
407
+
408
+ interface TokenEventData extends AuthData {
409
+ tokenManager: TokenManager | null;
410
+ }
411
+
412
+ interface TokenManager {
413
+ refreshToken: string;
414
+ accessToken: string;
336
415
  }
337
416
 
338
417
  interface RNMTStorage {
package/src/index.js CHANGED
@@ -6,12 +6,12 @@ import { MTAuth } from "./products/auth";
6
6
  import { MTCollection, batchWrite } from "./products/database";
7
7
  import { MTStorage } from "./products/storage";
8
8
  import { ServerReachableListener, TokenRefreshListener } from "./helpers/listeners";
9
- import { initTokenRefresher, listenTokenReady, triggerAuthToken } from "./products/auth/accessor";
9
+ import { initTokenRefresher, listenToken, listenTokenReady, parseToken, triggerAuthToken } from "./products/auth/accessor";
10
10
  import { TIMESTAMP, DOCUMENT_EXTRACTION, FIND_GEO_JSON, GEO_JSON } from "./products/database/types";
11
11
  import { mfetch } from "./products/http_callable";
12
12
  import { io } from "socket.io-client";
13
13
  import { validateCollectionPath } from "./products/database/validator";
14
- import { CACHE_PROTOCOL, Regexs } from "./helpers/values";
14
+ import { AUTH_PROVIDER_ID, CACHE_PROTOCOL, Regexs } from "./helpers/values";
15
15
  import { trySendPendingWrite } from "./products/database/accessor";
16
16
  import EngineApi from './helpers/EngineApi';
17
17
  import { parse, stringify } from 'json-buffer';
@@ -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,16 +48,41 @@ 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
- });
51
+ const disconnectionGlich = {};
52
+ let socket, lastUid, lastSocketProcess = 0, hasInited;
52
53
 
53
- socket.on('connect', () => {
54
- ServerReachableListener.dispatch(projectUrl, true);
55
- });
56
- socket.on('disconnect', () => {
57
- ServerReachableListener.dispatch(projectUrl, false);
58
- });
54
+ listenToken((token, thisInited) => {
55
+ const user = token && parseToken(token);
56
+
57
+ const thisUid = (user?.uid || null);
58
+ if (lastUid === thisUid && (hasInited || !thisInited)) return;
59
+ if (!hasInited) hasInited = thisInited;
60
+ lastUid = thisUid;
61
+
62
+ if (lastSocketProcess) disconnectionGlich[lastSocketProcess] = true;
63
+ socket.close();
64
+
65
+ const thisProcess = ++lastSocketProcess;
66
+ socket = io(`${this.config.wsPrefix}://${projectUrl.split('://')[1]}`, {
67
+ auth: {
68
+ _m_internal: true,
69
+ _from_base: true,
70
+ atoken: token
71
+ }
72
+ });
73
+
74
+ socket.on('_signal_signout', () => {
75
+ this.auth().signOut();
76
+ });
77
+
78
+ socket.on('connect', () => {
79
+ ServerReachableListener.dispatch(projectUrl, true);
80
+ });
81
+ socket.on('disconnect', () => {
82
+ if (!disconnectionGlich[thisProcess])
83
+ ServerReachableListener.dispatch(projectUrl, false);
84
+ });
85
+ }, projectUrl);
59
86
 
60
87
  listenReachableServer(c => {
61
88
  Scoped.IS_CONNECTED[projectUrl] = c;
@@ -69,7 +96,7 @@ class RNMT {
69
96
  }
70
97
 
71
98
  static releaseCache(prop) {
72
- if (Scoped.ReleaseCacheData) throw `calling ${this.name} multiple times is prohibited`;
99
+ if (Scoped.ReleaseCacheData) throw `calling ${this.name}() multiple times is prohibited`;
73
100
  validateReleaseCacheProp({ ...prop });
74
101
  Scoped.ReleaseCacheData = { ...prop };
75
102
  releaseCacheStore({ ...prop });
@@ -95,7 +122,7 @@ class RNMT {
95
122
 
96
123
  getSocket = (configOpts) => {
97
124
  const { disableAuth, authHandshake } = configOpts || {},
98
- { projectUrl, uglify, accessKey, serverE2E_PublicKey } = this.config;
125
+ { projectUrl, uglify, accessKey, serverE2E_PublicKey, wsPrefix } = this.config;
99
126
 
100
127
  const restrictedRoute = [
101
128
  _listenCollection,
@@ -194,7 +221,7 @@ class RNMT {
194
221
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
195
222
  const [reqBuilder, [privateKey]] = uglify ? serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
196
223
 
197
- socket = io(`ws://${projectUrl.split('://')[1]}`, {
224
+ socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
198
225
  auth: uglify ? {
199
226
  ugly: true,
200
227
  e2e: reqBuilder
@@ -299,8 +326,17 @@ const validateReleaseCacheProp = (prop) => {
299
326
  throw `Invalid value supplied to cachePassword, value must be a string and greater than 0 characters`;
300
327
  } else if (k === 'cacheProtocol') {
301
328
  if (!cacheList.includes(`${v}`)) throw `unknown value supplied to ${k}, expected any of ${cacheList}`;
329
+ } else if (k === 'io') {
330
+ Object.entries(v).forEach(([k, v]) => {
331
+ if (k === 'input' || k === 'output') {
332
+ if (typeof v !== 'function')
333
+ throw `Invalid value supplied to "io.${k}", expected a function but got "${v}"`;
334
+ } else throw `Unexpected property named "io.${k}"`;
335
+ });
302
336
  } else throw `Unexpected property named ${k}`;
303
337
  });
338
+
339
+ if (!prop?.io && !prop?.cacheProtocol) throw 'You need to provide either "io" or "cacheProtocol"';
304
340
  }
305
341
 
306
342
  const validator = {
@@ -363,7 +399,8 @@ export {
363
399
  TIMESTAMP,
364
400
  DOCUMENT_EXTRACTION,
365
401
  FIND_GEO_JSON,
366
- GEO_JSON
402
+ GEO_JSON,
403
+ AUTH_PROVIDER_ID
367
404
  };
368
405
 
369
406
  export default RNMT;
@@ -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,6 +118,9 @@ export class MTAuth {
111
118
  }, this.builder.projectUrl);
112
119
  });
113
120
 
121
+ /**
122
+ * @type {import('../../index').RNMTAuth['listenAuth']}
123
+ */
114
124
  listenAuth = (callback) => {
115
125
  let lastTrig;
116
126
 
@@ -2,10 +2,10 @@ import { IS_RAW_OBJECT, objToUniqueString, queryEntries, shuffleArray, sortArray
2
2
  import { awaitStore, updateCacheStore } from "../../helpers/utils";
3
3
  import { CacheStore } from "../../helpers/variables";
4
4
  import { confirmFilterDoc } from "./validator";
5
- import getLodash from 'lodash/get';
6
- import setLodash from 'lodash/set';
7
- import unsetLodash from 'lodash/unset';
8
- import isEqual from 'lodash/isEqual';
5
+ import getLodash from 'lodash.get';
6
+ import setLodash from 'lodash.set';
7
+ import unsetLodash from 'lodash.unset';
8
+ import isEqual from 'lodash.isequal';
9
9
  import { DEFAULT_DB_NAME, DEFAULT_DB_URL, DELIVERY, RETRIEVAL, WRITE_OPS, WRITE_OPS_LIST } from "../../helpers/values";
10
10
  import { DatabaseRecordsListener } from "../../helpers/listeners";
11
11
 
@@ -8,7 +8,7 @@ import { addPendingWrites, generateRecordID, getRecord, insertRecord, listenQuer
8
8
  import { validateCollectionPath, validateFilter, validateReadConfig, validateWriteValue } from "./validator";
9
9
  import { awaitRefreshToken, listenToken } from "../auth/accessor";
10
10
  import { DEFAULT_DB_NAME, DEFAULT_DB_URL, DELIVERY, RETRIEVAL } from "../../helpers/values";
11
- import setLodash from 'lodash/set';
11
+ import setLodash from 'lodash.set';
12
12
 
13
13
  export class MTCollection {
14
14
  constructor(config) {
@@ -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
@@ -1,7 +1,7 @@
1
1
  import { IS_DECIMAL_NUMBER, IS_RAW_OBJECT, IS_WHOLE_NUMBER, queryEntries } from "../../helpers/peripherals";
2
2
  import { READ_OPS, READ_OPS_LIST, RETRIEVAL } from "../../helpers/values";
3
- import getLodash from 'lodash/get';
4
- import isEqual from 'lodash/isEqual';
3
+ import getLodash from 'lodash.get';
4
+ import isEqual from 'lodash.isequal';
5
5
 
6
6
  const dirn = ['desc', 'asc', 'ascending', 'descending'];
7
7
 
@@ -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();