react-native-mosquito-transport 0.0.19 → 0.0.21
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/.jshintignore +4 -0
- package/.jshintrc +16 -0
- package/README.md +75 -1
- package/TODO +8 -1
- package/ios/Mosquitodb.swift +14 -1
- package/package.json +13 -13
- package/src/helpers/engine_api.js +39 -0
- package/src/helpers/peripherals.js +73 -127
- package/src/helpers/utils.js +48 -19
- package/src/helpers/values.js +8 -47
- package/src/helpers/variables.js +14 -6
- package/src/index.d.ts +103 -43
- package/src/index.js +171 -96
- package/src/products/auth/accessor.js +97 -36
- package/src/products/auth/index.js +151 -82
- package/src/products/database/accessor.js +720 -223
- package/src/products/database/bson.js +16 -0
- package/src/products/database/counter.js +16 -0
- package/src/products/database/index.js +303 -190
- package/src/products/database/types.js +1 -1
- package/src/products/database/validator.js +517 -254
- package/src/products/http_callable/index.js +111 -106
- package/src/products/storage/index.js +97 -88
- package/src/helpers/EngineApi.js +0 -33
package/src/index.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import 'react-native-get-random-values';
|
|
2
|
-
import {
|
|
3
|
-
import { releaseCacheStore } from "./helpers/utils";
|
|
4
|
-
import { Scoped } from "./helpers/variables";
|
|
5
|
-
import {
|
|
6
|
-
import { MTCollection, batchWrite } from "./products/database";
|
|
2
|
+
import { deserializeE2E, listenReachableServer, serializeE2E } from "./helpers/peripherals";
|
|
3
|
+
import { awaitStore, releaseCacheStore } from "./helpers/utils";
|
|
4
|
+
import { CacheStore, Scoped } from "./helpers/variables";
|
|
5
|
+
import { MTCollection, batchWrite, trySendPendingWrite } from "./products/database";
|
|
7
6
|
import { MTStorage } from "./products/storage";
|
|
8
7
|
import { ServerReachableListener, TokenRefreshListener } from "./helpers/listeners";
|
|
9
8
|
import { initTokenRefresher, listenToken, listenTokenReady, triggerAuthToken } from "./products/auth/accessor";
|
|
10
9
|
import { TIMESTAMP, DOCUMENT_EXTRACTION, FIND_GEO_JSON, GEO_JSON } from "./products/database/types";
|
|
11
10
|
import { mfetch } from "./products/http_callable";
|
|
12
11
|
import { io } from "socket.io-client";
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
12
|
+
import { AUTH_PROVIDER_ID, CACHE_PROTOCOL } from "./helpers/values";
|
|
13
|
+
import EngineApi from './helpers/engine_api';
|
|
14
|
+
import { Validator } from 'guard-object';
|
|
15
|
+
import cloneDeep from 'lodash.clonedeep';
|
|
16
|
+
import { Buffer } from 'buffer';
|
|
17
|
+
import MTAuth, { purgePendingToken } from './products/auth';
|
|
18
18
|
|
|
19
19
|
const {
|
|
20
20
|
_listenCollection,
|
|
@@ -24,33 +24,48 @@ const {
|
|
|
24
24
|
_listenUserVerification
|
|
25
25
|
} = EngineApi;
|
|
26
26
|
|
|
27
|
+
// https://socket.io/docs/v3/emit-cheatsheet/#reserved-events
|
|
28
|
+
const reservedEventName = [
|
|
29
|
+
'connect',
|
|
30
|
+
'connect_error',
|
|
31
|
+
'disconnect',
|
|
32
|
+
'disconnecting',
|
|
33
|
+
'newListener',
|
|
34
|
+
'removeListener'
|
|
35
|
+
];
|
|
36
|
+
|
|
27
37
|
class RNMT {
|
|
28
38
|
constructor(config) {
|
|
29
39
|
validateMTConfig(config, this);
|
|
30
40
|
this.config = {
|
|
31
41
|
...config,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
42
|
+
serverE2E_PublicKey: config.serverE2E_PublicKey && new Uint8Array(Buffer.from(config.serverE2E_PublicKey, 'base64')),
|
|
43
|
+
castBSON: config.castBSON === undefined || config.castBSON,
|
|
44
|
+
maxRetries: config.maxRetries || 3,
|
|
45
|
+
uglify: config.enableE2E_Encryption
|
|
35
46
|
};
|
|
36
|
-
const { projectUrl } = this.config;
|
|
47
|
+
const { projectUrl, extraHeaders } = this.config;
|
|
37
48
|
|
|
38
49
|
this.config.secureUrl = projectUrl.startsWith('https');
|
|
39
50
|
this.config.baseUrl = projectUrl.split('://')[1];
|
|
40
51
|
this.config.wsPrefix = this.config.secureUrl ? 'wss' : 'ws';
|
|
41
52
|
|
|
42
53
|
if (!Scoped.ReleaseCacheData)
|
|
43
|
-
throw `
|
|
54
|
+
throw `initializeCache must be called before creating any ${this.constructor.name} instance`;
|
|
44
55
|
|
|
45
56
|
if (!Scoped.InitializedProject[projectUrl]) {
|
|
46
|
-
Scoped.InitializedProject[projectUrl] =
|
|
57
|
+
Scoped.InitializedProject[projectUrl] = cloneDeep(this.config);
|
|
47
58
|
Scoped.LastTokenRefreshRef[projectUrl] = 0;
|
|
48
59
|
triggerAuthToken(projectUrl);
|
|
49
60
|
initTokenRefresher({ ...this.config }, true);
|
|
50
61
|
|
|
51
|
-
let isConnected,
|
|
62
|
+
let isConnected,
|
|
63
|
+
lastSentToken,
|
|
64
|
+
queuedToken;
|
|
52
65
|
|
|
53
|
-
const socket = io(`${this.config.wsPrefix}://${
|
|
66
|
+
const socket = io(`${this.config.wsPrefix}://${this.config.baseUrl}`, {
|
|
67
|
+
transports: ['websocket', 'polling', 'flashsocket'],
|
|
68
|
+
extraHeaders,
|
|
54
69
|
auth: {
|
|
55
70
|
_m_internal: true,
|
|
56
71
|
_from_base: true
|
|
@@ -63,12 +78,17 @@ class RNMT {
|
|
|
63
78
|
|
|
64
79
|
socket.on('connect', () => {
|
|
65
80
|
isConnected = true;
|
|
81
|
+
Scoped.IS_CONNECTED[projectUrl] = true;
|
|
66
82
|
if (queuedToken) updateMountedToken(queuedToken.token);
|
|
67
83
|
ServerReachableListener.dispatch(projectUrl, true);
|
|
84
|
+
awaitStore().then(() => {
|
|
85
|
+
trySendPendingWrite(projectUrl);
|
|
86
|
+
});
|
|
68
87
|
});
|
|
69
88
|
|
|
70
89
|
socket.on('disconnect', () => {
|
|
71
90
|
isConnected = false;
|
|
91
|
+
Scoped.IS_CONNECTED[projectUrl] = false;
|
|
72
92
|
ServerReachableListener.dispatch(projectUrl, false);
|
|
73
93
|
});
|
|
74
94
|
|
|
@@ -78,7 +98,7 @@ class RNMT {
|
|
|
78
98
|
lastSentToken = token;
|
|
79
99
|
}
|
|
80
100
|
queuedToken = undefined;
|
|
81
|
-
}
|
|
101
|
+
};
|
|
82
102
|
|
|
83
103
|
listenToken(token => {
|
|
84
104
|
if (isConnected) {
|
|
@@ -86,36 +106,35 @@ class RNMT {
|
|
|
86
106
|
} else queuedToken = { token };
|
|
87
107
|
}, projectUrl);
|
|
88
108
|
|
|
89
|
-
listenReachableServer(c => {
|
|
90
|
-
Scoped.IS_CONNECTED[projectUrl] = c;
|
|
91
|
-
if (c) trySendPendingWrite();
|
|
92
|
-
}, projectUrl);
|
|
93
|
-
|
|
94
109
|
TokenRefreshListener.listenTo(projectUrl, v => {
|
|
95
110
|
Scoped.IS_TOKEN_READY[projectUrl] = v;
|
|
96
111
|
});
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
114
|
|
|
100
|
-
static
|
|
115
|
+
static initializeCache(prop) {
|
|
101
116
|
if (Scoped.ReleaseCacheData) throw `calling ${this.name}() multiple times is prohibited`;
|
|
102
117
|
validateReleaseCacheProp({ ...prop });
|
|
103
118
|
Scoped.ReleaseCacheData = { ...prop };
|
|
104
119
|
releaseCacheStore({ ...prop });
|
|
120
|
+
// purge residue tokens
|
|
121
|
+
awaitStore().then(() => {
|
|
122
|
+
Object.keys(CacheStore.PendingAuthPurge).forEach(k => {
|
|
123
|
+
purgePendingToken(k);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
105
126
|
}
|
|
106
127
|
|
|
107
128
|
getDatabase = (dbName, dbUrl) => ({
|
|
108
129
|
collection: (path) => new MTCollection({
|
|
109
130
|
...this.config,
|
|
110
131
|
path,
|
|
111
|
-
...
|
|
112
|
-
...
|
|
132
|
+
...dbName ? { dbName } : {},
|
|
133
|
+
...dbUrl ? { dbUrl } : {}
|
|
113
134
|
})
|
|
114
135
|
});
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
return new MTCollection({ ...this.config, path });
|
|
118
|
-
}
|
|
136
|
+
|
|
137
|
+
collection = (path) => new MTCollection({ ...this.config, path });
|
|
119
138
|
batchWrite = (map, configx) => batchWrite({ ...this.config }, map, configx);
|
|
120
139
|
auth = () => new MTAuth({ ...this.config });
|
|
121
140
|
storage = () => new MTStorage({ ...this.config });
|
|
@@ -123,8 +142,8 @@ class RNMT {
|
|
|
123
142
|
listenReachableServer = (callback) => listenReachableServer(callback, this.config.projectUrl);
|
|
124
143
|
|
|
125
144
|
getSocket = (configOpts) => {
|
|
126
|
-
const { disableAuth, authHandshake } = configOpts || {}
|
|
127
|
-
|
|
145
|
+
const { disableAuth, authHandshake } = configOpts || {};
|
|
146
|
+
const { projectUrl, uglify, accessKey, serverE2E_PublicKey, wsPrefix, extraHeaders } = this.config;
|
|
128
147
|
|
|
129
148
|
const restrictedRoute = [
|
|
130
149
|
_listenCollection,
|
|
@@ -132,12 +151,14 @@ class RNMT {
|
|
|
132
151
|
_startDisconnectWriteTask,
|
|
133
152
|
_cancelDisconnectWriteTask,
|
|
134
153
|
_listenUserVerification
|
|
135
|
-
];
|
|
154
|
+
].map(v => [v(), v(true)]).flat();
|
|
136
155
|
|
|
137
|
-
|
|
138
|
-
|
|
156
|
+
const makeSocketCallback = () =>
|
|
157
|
+
new Promise(resolve => {
|
|
139
158
|
socketReadyCallback = resolve;
|
|
140
|
-
})
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let socketReadyCallback,
|
|
141
162
|
socketReadyPromise = makeSocketCallback(),
|
|
142
163
|
socketListenerList = [],
|
|
143
164
|
socketListenerIte = 0;
|
|
@@ -147,25 +168,31 @@ class RNMT {
|
|
|
147
168
|
tokenListener,
|
|
148
169
|
clientPrivateKey;
|
|
149
170
|
|
|
150
|
-
const listenerCallback = (callback) => function () {
|
|
151
|
-
|
|
171
|
+
const listenerCallback = (route, callback) => async function () {
|
|
172
|
+
if (reservedEventName.includes(route)) {
|
|
173
|
+
callback?.(...[...arguments]);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const [[args, not_encrypted], emitable] = [...arguments];
|
|
152
178
|
let res;
|
|
153
179
|
|
|
154
180
|
if (uglify) {
|
|
155
|
-
res =
|
|
181
|
+
res = await deserializeE2E(args, serverE2E_PublicKey, clientPrivateKey);
|
|
156
182
|
} else res = args;
|
|
183
|
+
const sortedArgs = discloseSocketArguments([res, not_encrypted]);
|
|
157
184
|
|
|
158
|
-
callback?.(...
|
|
159
|
-
const args = [...arguments];
|
|
185
|
+
callback?.(...sortedArgs, ...typeof emitable === 'function' ? [async function () {
|
|
186
|
+
const [args, not_encrypted] = encloseSocketArguments([...arguments]);
|
|
160
187
|
let res;
|
|
161
188
|
|
|
162
189
|
if (uglify) {
|
|
163
|
-
res = serializeE2E(
|
|
190
|
+
res = (await serializeE2E(args, undefined, serverE2E_PublicKey))[0];
|
|
164
191
|
} else res = args;
|
|
165
192
|
|
|
166
|
-
|
|
193
|
+
emitable([res, not_encrypted]);
|
|
167
194
|
}] : []);
|
|
168
|
-
}
|
|
195
|
+
};
|
|
169
196
|
|
|
170
197
|
const emit = ({ timeout, promise, emittion: emittionx }) => new Promise(async (resolve, reject) => {
|
|
171
198
|
const [route, ...emittion] = emittionx;
|
|
@@ -178,41 +205,43 @@ class RNMT {
|
|
|
178
205
|
|
|
179
206
|
let hasResolved, stime = Date.now();
|
|
180
207
|
|
|
181
|
-
const timer =
|
|
208
|
+
const timer = timeout ? setTimeout(() => {
|
|
182
209
|
hasResolved = true;
|
|
183
210
|
reject(new Error('emittion timeout'));
|
|
184
|
-
}, timeout);
|
|
211
|
+
}, timeout) : undefined;
|
|
185
212
|
|
|
186
213
|
await socketReadyPromise;
|
|
187
214
|
if (hasResolved) return;
|
|
188
215
|
clearTimeout(timer);
|
|
189
216
|
|
|
190
217
|
try {
|
|
191
|
-
const
|
|
218
|
+
const thisSocket = timeout ? socket.timeout(Math.max(timeout - (Date.now() - stime), 0)) : socket;
|
|
192
219
|
|
|
193
|
-
const lastEmit = emittion.slice(-1)[0]
|
|
194
|
-
|
|
220
|
+
const lastEmit = emittion.slice(-1)[0];
|
|
221
|
+
const hasEmitable = typeof lastEmit === 'function';
|
|
222
|
+
const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
|
|
195
223
|
|
|
196
|
-
const [reqBuilder, [privateKey]] = uglify ? serializeE2E(
|
|
224
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(mit, undefined, serverE2E_PublicKey) : [undefined, []];
|
|
197
225
|
|
|
198
|
-
if (
|
|
199
|
-
throw 'emitWithAck cannot have function in it
|
|
226
|
+
if (hasEmitable && promise)
|
|
227
|
+
throw 'emitWithAck cannot have function in it argument';
|
|
200
228
|
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
...
|
|
204
|
-
const args = [...arguments]
|
|
229
|
+
const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
|
|
230
|
+
[uglify ? reqBuilder : mit, not_encrypted],
|
|
231
|
+
...hasEmitable ? [async function () {
|
|
232
|
+
const [[args, not_encrypted]] = [...arguments];
|
|
205
233
|
let res;
|
|
206
234
|
|
|
207
235
|
if (uglify) {
|
|
208
|
-
res =
|
|
236
|
+
res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
|
|
209
237
|
} else res = args;
|
|
210
238
|
|
|
211
|
-
lastEmit(...res
|
|
239
|
+
lastEmit(...discloseSocketArguments([res, not_encrypted]));
|
|
212
240
|
}] : []
|
|
213
241
|
);
|
|
214
|
-
|
|
215
|
-
|
|
242
|
+
if (promise && result) {
|
|
243
|
+
resolve(discloseSocketArguments([uglify ? await deserializeE2E(result[0], serverE2E_PublicKey, privateKey) : result[0], result[1]])[0]);
|
|
244
|
+
} else resolve();
|
|
216
245
|
} catch (e) {
|
|
217
246
|
reject(e);
|
|
218
247
|
}
|
|
@@ -221,12 +250,14 @@ class RNMT {
|
|
|
221
250
|
const init = async () => {
|
|
222
251
|
if (hasCancelled) return;
|
|
223
252
|
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
224
|
-
const [reqBuilder, [privateKey]] = uglify ? serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
253
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
225
254
|
|
|
226
255
|
socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
|
|
256
|
+
transports: ['websocket', 'polling', 'flashsocket'],
|
|
257
|
+
extraHeaders,
|
|
227
258
|
auth: uglify ? {
|
|
228
259
|
ugly: true,
|
|
229
|
-
e2e: reqBuilder
|
|
260
|
+
e2e: reqBuilder.toString('base64')
|
|
230
261
|
} : {
|
|
231
262
|
...mtoken ? { mtoken } : {},
|
|
232
263
|
a_extras: authHandshake,
|
|
@@ -261,15 +292,20 @@ class RNMT {
|
|
|
261
292
|
}
|
|
262
293
|
|
|
263
294
|
return {
|
|
264
|
-
timeout: (timeout) =>
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
295
|
+
timeout: (timeout) => {
|
|
296
|
+
if (timeout !== undefined && !Validator.POSITIVE_INTEGER(timeout))
|
|
297
|
+
throw `expected a positive integer for timeout but got ${timeout}`;
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
emitWithAck: function () {
|
|
301
|
+
return emit({
|
|
302
|
+
timeout,
|
|
303
|
+
promise: true,
|
|
304
|
+
emittion: [...arguments]
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
},
|
|
273
309
|
emit: function () { emit({ emittion: [...arguments] }) },
|
|
274
310
|
emitWithAck: function () {
|
|
275
311
|
return emit({
|
|
@@ -281,7 +317,7 @@ class RNMT {
|
|
|
281
317
|
if (restrictedRoute.includes(route))
|
|
282
318
|
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
283
319
|
const ref = ++socketListenerIte,
|
|
284
|
-
listener = listenerCallback(callback);
|
|
320
|
+
listener = listenerCallback(route, callback);
|
|
285
321
|
|
|
286
322
|
socketListenerList.push([ref, 'on', route, listener]);
|
|
287
323
|
if (socket) socket.on(route, listener);
|
|
@@ -295,7 +331,7 @@ class RNMT {
|
|
|
295
331
|
if (restrictedRoute.includes(route))
|
|
296
332
|
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
297
333
|
const ref = ++socketListenerIte,
|
|
298
|
-
listener = listenerCallback(callback);
|
|
334
|
+
listener = listenerCallback(route, callback);
|
|
299
335
|
|
|
300
336
|
socketListenerList.push([ref, 'once', route, listener]);
|
|
301
337
|
if (socket) socket.once(route, listener);
|
|
@@ -313,10 +349,31 @@ class RNMT {
|
|
|
313
349
|
}
|
|
314
350
|
}
|
|
315
351
|
}
|
|
352
|
+
};
|
|
316
353
|
|
|
317
|
-
|
|
318
|
-
|
|
354
|
+
class DoNotEncrypt {
|
|
355
|
+
constructor(value) {
|
|
356
|
+
this.value = value;
|
|
319
357
|
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const encloseSocketArguments = (args) => {
|
|
361
|
+
const [encrypted, unencrypted] = [{}, {}];
|
|
362
|
+
|
|
363
|
+
args.forEach((v, i) => {
|
|
364
|
+
if (v instanceof DoNotEncrypt) {
|
|
365
|
+
unencrypted[i] = v.value;
|
|
366
|
+
} else encrypted[i] = v;
|
|
367
|
+
});
|
|
368
|
+
return [encrypted, unencrypted];
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const discloseSocketArguments = (args = []) => {
|
|
372
|
+
return args.map((obj, i) => Object.entries(obj).map(v => i ? [v[0], new DoNotEncrypt(v[1])] : v)).flat()
|
|
373
|
+
.sort((a, b) => (a[0] * 1) - (b[0] * 1)).map((v, i) => {
|
|
374
|
+
if (v[0] * 1 !== i) throw 'corrupted socket arguments';
|
|
375
|
+
return v[1];
|
|
376
|
+
});
|
|
320
377
|
}
|
|
321
378
|
|
|
322
379
|
const validateReleaseCacheProp = (prop) => {
|
|
@@ -335,6 +392,11 @@ const validateReleaseCacheProp = (prop) => {
|
|
|
335
392
|
throw `Invalid value supplied to "io.${k}", expected a function but got "${v}"`;
|
|
336
393
|
} else throw `Unexpected property named "io.${k}"`;
|
|
337
394
|
});
|
|
395
|
+
} else if (k === 'promoteCache') {
|
|
396
|
+
if (typeof v !== 'boolean') throw 'promoteCache should be a boolean';
|
|
397
|
+
} else if (k === 'heapMemory') {
|
|
398
|
+
if (typeof v !== 'number' || v <= 0)
|
|
399
|
+
throw `Invalid value supplied to heapMemory, value must be an integer greater than zero`;
|
|
338
400
|
} else throw `Unexpected property named ${k}`;
|
|
339
401
|
});
|
|
340
402
|
|
|
@@ -344,19 +406,15 @@ const validateReleaseCacheProp = (prop) => {
|
|
|
344
406
|
const validator = {
|
|
345
407
|
dbName: (v) => {
|
|
346
408
|
if (typeof v !== 'string' || !v.trim())
|
|
347
|
-
throw `Invalid value supplied to dbName, value must be
|
|
409
|
+
throw `Invalid value supplied to dbName, value must be a non-empty string`;
|
|
348
410
|
},
|
|
349
411
|
dbUrl: (v) => {
|
|
350
412
|
if (typeof v !== 'string' || !v.trim())
|
|
351
|
-
throw `Invalid value supplied to dbUrl, value must be
|
|
352
|
-
},
|
|
353
|
-
heapMemory: (v) => {
|
|
354
|
-
if (typeof v !== 'number' || v <= 0)
|
|
355
|
-
throw `Invalid value supplied to heapMemory, value must be number and greater than zero`;
|
|
413
|
+
throw `Invalid value supplied to dbUrl, value must be a non-empty string`;
|
|
356
414
|
},
|
|
357
415
|
projectUrl: (v) => {
|
|
358
|
-
if (typeof v !== 'string' || !
|
|
359
|
-
throw `
|
|
416
|
+
if (typeof v !== 'string' || (!Validator.HTTPS(v) && !Validator.HTTP(v)))
|
|
417
|
+
throw `Expected "projectUrl" to be valid https or http link but got "${v}"`;
|
|
360
418
|
},
|
|
361
419
|
disableCache: (v) => {
|
|
362
420
|
if (typeof v !== 'boolean')
|
|
@@ -364,40 +422,57 @@ const validator = {
|
|
|
364
422
|
},
|
|
365
423
|
accessKey: (v) => {
|
|
366
424
|
if (typeof v !== 'string' || !v.trim())
|
|
367
|
-
throw `Invalid value supplied to accessKey, value must be a string
|
|
425
|
+
throw `Invalid value supplied to accessKey, value must be a non-empty string`;
|
|
368
426
|
},
|
|
369
427
|
maxRetries: (v) => {
|
|
370
|
-
if (
|
|
371
|
-
throw `Invalid value supplied to maxRetries, value must be
|
|
428
|
+
if (v <= 0 || !Validator.POSITIVE_INTEGER(v))
|
|
429
|
+
throw `Invalid value supplied to maxRetries, value must be positive integer greater than zero`;
|
|
372
430
|
},
|
|
373
431
|
enableE2E_Encryption: (v) => {
|
|
374
432
|
if (typeof v !== 'boolean')
|
|
375
433
|
throw `Invalid value supplied to enableE2E_Encryption, value must be a boolean`;
|
|
376
434
|
},
|
|
435
|
+
castBSON: v => {
|
|
436
|
+
if (typeof v !== 'boolean')
|
|
437
|
+
throw `Invalid value supplied to castBSON, value must be a boolean`;
|
|
438
|
+
},
|
|
439
|
+
borrowToken: v => {
|
|
440
|
+
if (typeof v !== 'string' || (!Validator.HTTPS(v) && !Validator.HTTP(v)))
|
|
441
|
+
throw `Expected "borrowToken" to be valid https or http link but got "${v}"`;
|
|
442
|
+
},
|
|
377
443
|
serverE2E_PublicKey: (v) => {
|
|
378
444
|
if (typeof v !== 'string' || !v.trim())
|
|
379
|
-
throw `Invalid value supplied to serverETE_PublicKey, value must be
|
|
445
|
+
throw `Invalid value supplied to serverETE_PublicKey, value must be a non-empty string`;
|
|
446
|
+
},
|
|
447
|
+
extraHeaders: v => {
|
|
448
|
+
if (!Validator.OBJECT(v)) throw '"extraHeaders" must be an object';
|
|
449
|
+
const reservedHeaders = ['mtoken', 'mosquito-token', 'init-content-type', 'content-type', 'authorization', 'uglified'];
|
|
450
|
+
|
|
451
|
+
Object.entries(v).forEach(([k, v]) => {
|
|
452
|
+
if (typeof v !== 'string') throw `expected a string at extraHeaders.${k} but got "${v}"`;
|
|
453
|
+
if (reservedHeaders.includes(v.toLowerCase()))
|
|
454
|
+
throw `extraHeaders must not include any reserved props which are: ${reservedHeaders}`;
|
|
455
|
+
});
|
|
380
456
|
}
|
|
381
457
|
};
|
|
382
458
|
|
|
383
459
|
const validateMTConfig = (config, that) => {
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
for (let i = 0; i < h.length; i++) {
|
|
388
|
-
const k = h[i];
|
|
460
|
+
if (!Validator.OBJECT(config))
|
|
461
|
+
throw `${that.constructor.name} config is not an object`;
|
|
389
462
|
|
|
463
|
+
for (const [k, v] of Object.entries(config)) {
|
|
390
464
|
if (!validator[k]) throw `Unexpected property named ${k}`;
|
|
391
|
-
validator[k](
|
|
465
|
+
validator[k](v);
|
|
392
466
|
}
|
|
393
467
|
|
|
394
468
|
if (config.enableE2E_Encryption && !config.serverE2E_PublicKey)
|
|
395
469
|
throw '"serverE2E_PublicKey" is missing, enabling end-to-end encryption requires a public encryption key from the server';
|
|
396
|
-
if (!config
|
|
397
|
-
if (!config
|
|
470
|
+
if (!config.projectUrl) throw `projectUrl is a required property in ${that.constructor.name}() constructor`;
|
|
471
|
+
if (!config.accessKey) throw `accessKey is a required property in ${that.constructor.name}() constructor`;
|
|
398
472
|
}
|
|
399
473
|
|
|
400
474
|
export {
|
|
475
|
+
DoNotEncrypt,
|
|
401
476
|
TIMESTAMP,
|
|
402
477
|
DOCUMENT_EXTRACTION,
|
|
403
478
|
FIND_GEO_JSON,
|