mosquito-transport-js 0.3.0 → 0.3.2
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 +4 -4
- package/src/helpers/e2e_worker.js +109 -0
- package/src/helpers/peripherals.js +33 -15
- package/src/helpers/utils.js +2 -2
- package/src/index.d.ts +2 -2
- package/src/index.js +30 -12
- package/src/polyfill.js +3 -0
- package/src/products/auth/accessor.js +2 -2
- package/src/products/auth/index.js +10 -10
- package/src/products/database/accessor.js +12 -26
- package/src/products/database/index.js +23 -17
- package/src/products/database/validator.js +26 -4
- package/src/products/http_callable/index.js +24 -13
- package/src/products/storage/index.js +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mosquito-transport-js",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Javascript web sdk for mosquito-transport (https://github.com/deflexable/mosquito-transport)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -32,17 +32,17 @@
|
|
|
32
32
|
"bson": "^6.8.0",
|
|
33
33
|
"buffer": "^6.0.3",
|
|
34
34
|
"crypto-js": "^4.2.0",
|
|
35
|
-
"guard-object": "^1.1.
|
|
35
|
+
"guard-object": "^1.1.3",
|
|
36
|
+
"limit-task": "1.0.0",
|
|
36
37
|
"json-buffer": "^3.0.1",
|
|
37
38
|
"lodash.clonedeep": "^4.5.0",
|
|
38
39
|
"lodash.get": "^4.4.2",
|
|
39
40
|
"lodash.set": "^4.3.2",
|
|
40
41
|
"lodash.unset": "^4.5.2",
|
|
41
|
-
"set-large-timeout": "^1.0.1",
|
|
42
42
|
"simplify-error": "^1.0.1",
|
|
43
43
|
"socket.io-client": "^4.6.2",
|
|
44
44
|
"subscription-listener": "^1.1.2",
|
|
45
|
-
"tweetnacl": "^1.0.
|
|
45
|
+
"tweetnacl-functional": "^1.0.4"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@babel/core": "^7.24.6",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import LimitTask from "limit-task";
|
|
2
|
+
import naclPkg from 'tweetnacl-functional';
|
|
3
|
+
|
|
4
|
+
function e2e_baseCode() {
|
|
5
|
+
const serializeE2E = (data, serverPublicKey) => {
|
|
6
|
+
const pair = box.keyPair(),
|
|
7
|
+
nonce = randomBytes(box.nonceLength);
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
data: box(data, nonce, serverPublicKey, pair.secretKey),
|
|
11
|
+
pair,
|
|
12
|
+
nonce
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const deserializeE2E = (data, nonce, serverPublicKey, clientPrivateKey) => {
|
|
17
|
+
const result = box.open(
|
|
18
|
+
data,
|
|
19
|
+
nonce,
|
|
20
|
+
serverPublicKey,
|
|
21
|
+
clientPrivateKey
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (!result) throw 'Decrypting e2e message failed';
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
self.onmessage = function (event) {
|
|
29
|
+
const { data } = event;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
let response;
|
|
33
|
+
if (data.type === 'encrypt') {
|
|
34
|
+
response = serializeE2E(...data.params);
|
|
35
|
+
} else if (data.type === 'decrypt') {
|
|
36
|
+
response = deserializeE2E(...data.params);
|
|
37
|
+
}
|
|
38
|
+
self.postMessage([response, data.session]);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
self.postMessage([undefined, undefined, { error }]);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const workerCode = `
|
|
46
|
+
${naclPkg.NACL.toString()}
|
|
47
|
+
const naclPkg = {};
|
|
48
|
+
|
|
49
|
+
NACL(naclPkg);
|
|
50
|
+
|
|
51
|
+
const { box, randomBytes } = naclPkg;
|
|
52
|
+
|
|
53
|
+
${e2e_baseCode.toString()}
|
|
54
|
+
e2e_baseCode();
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const spawnWorker = () => {
|
|
58
|
+
const resolution = {};
|
|
59
|
+
let ite = 0;
|
|
60
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
61
|
+
const worker = new Worker(URL.createObjectURL(blob));
|
|
62
|
+
|
|
63
|
+
// Receive the result from the worker
|
|
64
|
+
worker.onmessage = function (event) {
|
|
65
|
+
const [response, session, error] = event.data;
|
|
66
|
+
if (error) {
|
|
67
|
+
resolution[session][1](error.error);
|
|
68
|
+
} else {
|
|
69
|
+
resolution[session][0](response);
|
|
70
|
+
}
|
|
71
|
+
delete resolution[session];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const queue = LimitTask(3);
|
|
75
|
+
|
|
76
|
+
return (type, params) => queue(() =>
|
|
77
|
+
new Promise((resolve, reject) => {
|
|
78
|
+
const session = `${++ite}`;
|
|
79
|
+
|
|
80
|
+
resolution[session] = [resolve, reject];
|
|
81
|
+
worker.postMessage({ type, params, session });
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
let currentTask = -1;
|
|
87
|
+
const e2e_engines = [];
|
|
88
|
+
|
|
89
|
+
const MAX_WORKERS = 7;
|
|
90
|
+
|
|
91
|
+
const addTask = (type, params) => {
|
|
92
|
+
++currentTask;
|
|
93
|
+
if (currentTask >= e2e_engines.length) {
|
|
94
|
+
if (e2e_engines.length < MAX_WORKERS) {
|
|
95
|
+
// spawn new engine
|
|
96
|
+
e2e_engines.push(spawnWorker());
|
|
97
|
+
} else currentTask = 0;
|
|
98
|
+
}
|
|
99
|
+
return e2e_engines[currentTask](type, params);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default {
|
|
103
|
+
encrypt: (bufferData, serverPublicKey) => {
|
|
104
|
+
return addTask('encrypt', [bufferData, serverPublicKey]);
|
|
105
|
+
},
|
|
106
|
+
decrypt: (data, nonce, serverPublicKey, clientPrivateKey) => {
|
|
107
|
+
return addTask('decrypt', [data, nonce, serverPublicKey, clientPrivateKey]);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
@@ -2,8 +2,9 @@ import { Buffer } from "buffer";
|
|
|
2
2
|
import { ServerReachableListener } from "./listeners";
|
|
3
3
|
import aes_pkg from 'crypto-js/aes.js';
|
|
4
4
|
import Utf8Encoder from 'crypto-js/enc-utf8.js';
|
|
5
|
-
import naclPkg from 'tweetnacl';
|
|
5
|
+
import naclPkg from 'tweetnacl-functional';
|
|
6
6
|
import getLodash from "lodash.get";
|
|
7
|
+
import e2e_worker from "./e2e_worker";
|
|
7
8
|
|
|
8
9
|
const { encrypt, decrypt } = aes_pkg;
|
|
9
10
|
const { box, randomBytes } = naclPkg;
|
|
@@ -36,6 +37,10 @@ export const niceTry = (promise) => new Promise(async resolve => {
|
|
|
36
37
|
} catch (e) { resolve(); }
|
|
37
38
|
});
|
|
38
39
|
|
|
40
|
+
export const normalizeRoute = (route = '') => route.split('').map((v, i, a) =>
|
|
41
|
+
((!i && v === '/') || (i === a.length - 1 && v === '/') || (i && a[i - 1] === '/' && v === '/')) ? '' : v
|
|
42
|
+
).join('');
|
|
43
|
+
|
|
39
44
|
export const shuffleArray = (n) => {
|
|
40
45
|
const array = [...n];
|
|
41
46
|
let currentIndex = array.length, randomIndex;
|
|
@@ -98,7 +103,21 @@ export const decryptString = (txt, password, iv) => {
|
|
|
98
103
|
return decrypt(txt, `${password || ''}${iv || ''}`).toString(Utf8Encoder);
|
|
99
104
|
};
|
|
100
105
|
|
|
101
|
-
export const serializeE2E = (data, auth_token, serverPublicKey) => {
|
|
106
|
+
export const serializeE2E = async (data, auth_token, serverPublicKey) => {
|
|
107
|
+
const inputData = new Uint8Array(Buffer.from(JSON.stringify([data, auth_token]), 'utf8'));
|
|
108
|
+
|
|
109
|
+
if (inputData.byteLength > 10240) {
|
|
110
|
+
// dispatch to background thread
|
|
111
|
+
const { data, pair, nonce } = e2e_worker.encrypt(inputData, serverPublicKey);
|
|
112
|
+
const pubBase64 = Buffer.from(pair.publicKey).toString('base64'),
|
|
113
|
+
nonceBase64 = Buffer.from(nonce).toString('base64');
|
|
114
|
+
|
|
115
|
+
return [
|
|
116
|
+
`${pubBase64}.${nonceBase64}.${Buffer.from(data).toString('base64')}`,
|
|
117
|
+
[pair.secretKey, pair.publicKey]
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
|
|
102
121
|
const pair = box.keyPair(),
|
|
103
122
|
nonce = randomBytes(box.nonceLength),
|
|
104
123
|
pubBase64 = Buffer.from(pair.publicKey).toString('base64'),
|
|
@@ -107,12 +126,9 @@ export const serializeE2E = (data, auth_token, serverPublicKey) => {
|
|
|
107
126
|
return [
|
|
108
127
|
`${pubBase64}.${nonceBase64}.${Buffer.from(
|
|
109
128
|
box(
|
|
110
|
-
|
|
111
|
-
data,
|
|
112
|
-
auth_token
|
|
113
|
-
]), 'utf8'),
|
|
129
|
+
inputData,
|
|
114
130
|
nonce,
|
|
115
|
-
|
|
131
|
+
serverPublicKey,
|
|
116
132
|
pair.secretKey
|
|
117
133
|
)
|
|
118
134
|
).toString('base64')}`,
|
|
@@ -120,14 +136,16 @@ export const serializeE2E = (data, auth_token, serverPublicKey) => {
|
|
|
120
136
|
];
|
|
121
137
|
};
|
|
122
138
|
|
|
123
|
-
export const deserializeE2E = (data, serverPublicKey, clientPrivateKey) => {
|
|
124
|
-
const [binaryNonce, binaryData] = data.split('.'),
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
139
|
+
export const deserializeE2E = async (data = '', serverPublicKey, clientPrivateKey) => {
|
|
140
|
+
const [binaryNonce, binaryData] = data.split('.').map(v => new Uint8Array(Buffer.from(v, 'base64')));
|
|
141
|
+
let baseArray;
|
|
142
|
+
|
|
143
|
+
if (binaryData.byteLength > 10240) {
|
|
144
|
+
// dispatch to background thread
|
|
145
|
+
baseArray = e2e_worker.decrypt(binaryData, binaryNonce, serverPublicKey, clientPrivateKey);
|
|
146
|
+
} else {
|
|
147
|
+
baseArray = box.open(binaryData, binaryNonce, serverPublicKey, clientPrivateKey);
|
|
148
|
+
}
|
|
131
149
|
|
|
132
150
|
if (!baseArray) throw 'Decrypting e2e message failed';
|
|
133
151
|
return JSON.parse(Buffer.from(baseArray).toString('utf8'))[0];
|
package/src/helpers/utils.js
CHANGED
|
@@ -116,9 +116,9 @@ export const getReachableServer = (projectUrl) => new Promise(resolve => {
|
|
|
116
116
|
}, true);
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
export const buildFetchInterface = ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey }) => {
|
|
119
|
+
export const buildFetchInterface = async ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey }) => {
|
|
120
120
|
if (!uglify) body = JSON.stringify({ ...body });
|
|
121
|
-
const [plate, keyPair] = uglify ? serializeE2E(body, authToken, serverE2E_PublicKey) : [undefined, []];
|
|
121
|
+
const [plate, keyPair] = uglify ? await serializeE2E(body, authToken, serverE2E_PublicKey) : [undefined, []];
|
|
122
122
|
|
|
123
123
|
return [{
|
|
124
124
|
body: uglify ? plate : body,
|
package/src/index.d.ts
CHANGED
|
@@ -115,7 +115,7 @@ interface MTSocket {
|
|
|
115
115
|
emitWithAck: (...args: any) => Promise<any>;
|
|
116
116
|
});
|
|
117
117
|
emit: (...args: any) => void;
|
|
118
|
-
emitWithAck: () => Promise<any>;
|
|
118
|
+
emitWithAck: (...args: any) => Promise<any>;
|
|
119
119
|
on: (route: string, callback?: () => any) => void;
|
|
120
120
|
once: (route: string, callback?: () => any) => void;
|
|
121
121
|
destroy: () => void;
|
|
@@ -380,7 +380,7 @@ interface MTAuth {
|
|
|
380
380
|
getAuthToken: () => Promise<string>;
|
|
381
381
|
getRefreshToken: () => Promise<string>;
|
|
382
382
|
getRefreshTokenData: () => Promise<RefreshTokenData>;
|
|
383
|
-
parseToken: () =>
|
|
383
|
+
parseToken: (token: string) => AuthData | RefreshTokenData;
|
|
384
384
|
listenAuth: (callback: (auth: TokenEventData) => void) => () => void;
|
|
385
385
|
getAuth: () => Promise<TokenEventData>;
|
|
386
386
|
signOut: () => Promise<void>;
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import './polyfill';
|
|
1
2
|
import { deserializeE2E, listenReachableServer, serializeE2E } from "./helpers/peripherals";
|
|
2
3
|
import { releaseCacheStore, awaitStore } from "./helpers/utils";
|
|
3
4
|
import { CacheStore, Scoped } from "./helpers/variables";
|
|
@@ -5,7 +6,7 @@ import { MTAuth } from "./products/auth";
|
|
|
5
6
|
import { MTCollection, batchWrite, trySendPendingWrite } from "./products/database";
|
|
6
7
|
import { MTStorage } from "./products/storage";
|
|
7
8
|
import { ServerReachableListener, TokenRefreshListener } from "./helpers/listeners";
|
|
8
|
-
import { initTokenRefresher, listenToken, listenTokenReady, triggerAuthToken } from "./products/auth/accessor";
|
|
9
|
+
import { initTokenRefresher, listenToken, listenTokenReady, parseToken, triggerAuthToken } from "./products/auth/accessor";
|
|
9
10
|
import { TIMESTAMP, DOCUMENT_EXTRACTION, FIND_GEO_JSON, GEO_JSON } from "./products/database/types";
|
|
10
11
|
import { mfetch } from "./products/http_callable";
|
|
11
12
|
import { io } from "socket.io-client";
|
|
@@ -15,6 +16,7 @@ import { parse, stringify } from 'json-buffer';
|
|
|
15
16
|
import { Validator } from 'guard-object';
|
|
16
17
|
import sendMessage from "./helpers/broadcaster";
|
|
17
18
|
import cloneDeep from "lodash.clonedeep";
|
|
19
|
+
import { Buffer } from "buffer";
|
|
18
20
|
|
|
19
21
|
const {
|
|
20
22
|
_listenCollection,
|
|
@@ -24,11 +26,22 @@ const {
|
|
|
24
26
|
_listenUserVerification
|
|
25
27
|
} = EngineApi;
|
|
26
28
|
|
|
29
|
+
// https://socket.io/docs/v3/emit-cheatsheet/#reserved-events
|
|
30
|
+
const reservedEventName = [
|
|
31
|
+
'connect',
|
|
32
|
+
'connect_error',
|
|
33
|
+
'disconnect',
|
|
34
|
+
'disconnecting',
|
|
35
|
+
'newListener',
|
|
36
|
+
'removeListener'
|
|
37
|
+
];
|
|
38
|
+
|
|
27
39
|
export class MosquitoTransport {
|
|
28
40
|
constructor(config) {
|
|
29
41
|
validateMTConfig(config, this);
|
|
30
42
|
this.config = {
|
|
31
43
|
...config,
|
|
44
|
+
serverE2E_PublicKey: config.serverE2E_PublicKey && new Uint8Array(Buffer.from(config.serverE2E_PublicKey, 'base64')),
|
|
32
45
|
castBSON: config.castBSON === undefined || config.castBSON,
|
|
33
46
|
maxRetries: config.maxRetries || 3,
|
|
34
47
|
uglify: config.enableE2E_Encryption
|
|
@@ -166,20 +179,25 @@ export class MosquitoTransport {
|
|
|
166
179
|
tokenListener,
|
|
167
180
|
clientPrivateKey;
|
|
168
181
|
|
|
169
|
-
const listenerCallback = (callback) => function () {
|
|
182
|
+
const listenerCallback = (route, callback) => async function () {
|
|
183
|
+
if (reservedEventName.includes(route)) {
|
|
184
|
+
callback?.(...[...arguments]);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
170
188
|
const [args, emitable] = [...arguments];
|
|
171
189
|
let res;
|
|
172
190
|
|
|
173
191
|
if (uglify) {
|
|
174
|
-
res = parse(deserializeE2E(args, serverE2E_PublicKey, clientPrivateKey));
|
|
192
|
+
res = parse(await deserializeE2E(args, serverE2E_PublicKey, clientPrivateKey));
|
|
175
193
|
} else res = args;
|
|
176
194
|
|
|
177
|
-
callback?.(...res || [], ...typeof emitable === 'function' ? [function () {
|
|
195
|
+
callback?.(...res || [], ...typeof emitable === 'function' ? [async function () {
|
|
178
196
|
const args = [...arguments];
|
|
179
197
|
let res;
|
|
180
198
|
|
|
181
199
|
if (uglify) {
|
|
182
|
-
res = serializeE2E(stringify(args), undefined, serverE2E_PublicKey)[0];
|
|
200
|
+
res = (await serializeE2E(stringify(args), undefined, serverE2E_PublicKey))[0];
|
|
183
201
|
} else res = args;
|
|
184
202
|
|
|
185
203
|
emitable(res);
|
|
@@ -213,26 +231,26 @@ export class MosquitoTransport {
|
|
|
213
231
|
const hasEmitable = typeof lastEmit === 'function';
|
|
214
232
|
const mit = hasEmitable ? emittion.slice(0, -1) : emittion;
|
|
215
233
|
|
|
216
|
-
const [reqBuilder, [privateKey]] = uglify ? serializeE2E(stringify(mit), undefined, serverE2E_PublicKey) : [undefined, []];
|
|
234
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(stringify(mit), undefined, serverE2E_PublicKey) : [undefined, []];
|
|
217
235
|
|
|
218
236
|
if (hasEmitable && promise)
|
|
219
237
|
throw 'emitWithAck cannot have function in it argument';
|
|
220
238
|
|
|
221
239
|
const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
|
|
222
240
|
uglify ? reqBuilder : mit,
|
|
223
|
-
...hasEmitable ? [function () {
|
|
241
|
+
...hasEmitable ? [async function () {
|
|
224
242
|
const [args] = [...arguments];
|
|
225
243
|
let res;
|
|
226
244
|
|
|
227
245
|
if (uglify) {
|
|
228
|
-
res = parse(deserializeE2E(args, serverE2E_PublicKey, privateKey));
|
|
246
|
+
res = parse(await deserializeE2E(args, serverE2E_PublicKey, privateKey));
|
|
229
247
|
} else res = args;
|
|
230
248
|
|
|
231
249
|
lastEmit(...res || []);
|
|
232
250
|
}] : []
|
|
233
251
|
);
|
|
234
252
|
|
|
235
|
-
resolve((promise && result) ? uglify ? parse(deserializeE2E(result, serverE2E_PublicKey, privateKey))[0] : result[0] : undefined);
|
|
253
|
+
resolve((promise && result) ? uglify ? parse(await deserializeE2E(result, serverE2E_PublicKey, privateKey))[0] : result[0] : undefined);
|
|
236
254
|
} catch (e) {
|
|
237
255
|
reject(e);
|
|
238
256
|
}
|
|
@@ -241,7 +259,7 @@ export class MosquitoTransport {
|
|
|
241
259
|
const init = async () => {
|
|
242
260
|
if (hasCancelled) return;
|
|
243
261
|
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
244
|
-
const [reqBuilder, [privateKey]] = uglify ? serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
262
|
+
const [reqBuilder, [privateKey]] = uglify ? await serializeE2E({ accessKey, a_extras: authHandshake }, mtoken, serverE2E_PublicKey) : [null, []];
|
|
245
263
|
|
|
246
264
|
socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
|
|
247
265
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
@@ -307,7 +325,7 @@ export class MosquitoTransport {
|
|
|
307
325
|
if (restrictedRoute.includes(route))
|
|
308
326
|
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
309
327
|
const ref = ++socketListenerIte,
|
|
310
|
-
listener = listenerCallback(callback);
|
|
328
|
+
listener = listenerCallback(route, callback);
|
|
311
329
|
|
|
312
330
|
socketListenerList.push([ref, 'on', route, listener]);
|
|
313
331
|
if (socket) socket.on(route, listener);
|
|
@@ -321,7 +339,7 @@ export class MosquitoTransport {
|
|
|
321
339
|
if (restrictedRoute.includes(route))
|
|
322
340
|
throw `${route} is a restricted socket path, avoid using any of ${restrictedRoute}`;
|
|
323
341
|
const ref = ++socketListenerIte,
|
|
324
|
-
listener = listenerCallback(callback);
|
|
342
|
+
listener = listenerCallback(route, callback);
|
|
325
343
|
|
|
326
344
|
socketListenerList.push([ref, 'once', route, listener]);
|
|
327
345
|
if (socket) socket.once(route, listener);
|
package/src/polyfill.js
ADDED
|
@@ -81,7 +81,7 @@ const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7
|
|
|
81
81
|
try {
|
|
82
82
|
const { token, refreshToken: r_token } = CacheStore.AuthStore[projectUrl];
|
|
83
83
|
|
|
84
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
84
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
85
85
|
body: { token, r_token },
|
|
86
86
|
accessKey,
|
|
87
87
|
uglify,
|
|
@@ -96,7 +96,7 @@ const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7
|
|
|
96
96
|
}
|
|
97
97
|
if (r.simpleError) throw r;
|
|
98
98
|
|
|
99
|
-
const f = uglify ? deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
99
|
+
const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
100
100
|
|
|
101
101
|
if (CacheStore.AuthStore[projectUrl]) {
|
|
102
102
|
CacheStore.AuthStore[projectUrl].token = f.result.token;
|
|
@@ -61,7 +61,7 @@ export class MTAuth {
|
|
|
61
61
|
}
|
|
62
62
|
if (processID !== lastInitRef) return;
|
|
63
63
|
const mtoken = Scoped.AuthJWTToken[projectUrl],
|
|
64
|
-
[reqBuilder, [privateKey]] = uglify ? serializeE2E({ mtoken }, undefined, serverE2E_PublicKey) : [null, []];
|
|
64
|
+
[reqBuilder, [privateKey]] = uglify ? await serializeE2E({ mtoken }, undefined, serverE2E_PublicKey) : [null, []];
|
|
65
65
|
|
|
66
66
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
67
67
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
@@ -74,11 +74,11 @@ export class MTAuth {
|
|
|
74
74
|
|
|
75
75
|
socket.emit(_listenUserVerification(uglify));
|
|
76
76
|
|
|
77
|
-
socket.on("onVerificationChanged", ([err, verified]) => {
|
|
77
|
+
socket.on("onVerificationChanged", async ([err, verified]) => {
|
|
78
78
|
if (err) {
|
|
79
79
|
onError?.(simplifyCaughtError(err).simpleError);
|
|
80
80
|
} else {
|
|
81
|
-
callback?.(uglify ? deserializeE2E(verified, serverE2E_PublicKey, privateKey) : verified);
|
|
81
|
+
callback?.(uglify ? await deserializeE2E(verified, serverE2E_PublicKey, privateKey) : verified);
|
|
82
82
|
}
|
|
83
83
|
});
|
|
84
84
|
|
|
@@ -171,7 +171,7 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
|
|
|
171
171
|
|
|
172
172
|
try {
|
|
173
173
|
await awaitStore();
|
|
174
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
174
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
175
175
|
body: { data: `${encodeBinary(email)}.${encodeBinary(password)}` },
|
|
176
176
|
accessKey,
|
|
177
177
|
serverE2E_PublicKey,
|
|
@@ -181,7 +181,7 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
|
|
|
181
181
|
const f = await (await fetch(_customSignin(projectUrl, uglify), reqBuilder)).json();
|
|
182
182
|
if (f.simpleError) throw f;
|
|
183
183
|
|
|
184
|
-
const r = uglify ? deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
|
|
184
|
+
const r = uglify ? await deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
|
|
185
185
|
|
|
186
186
|
resolve({
|
|
187
187
|
user: parseToken(r.result.token),
|
|
@@ -199,7 +199,7 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
|
|
|
199
199
|
|
|
200
200
|
try {
|
|
201
201
|
await awaitStore();
|
|
202
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
202
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
203
203
|
body: {
|
|
204
204
|
data: `${encodeBinary(email)}.${encodeBinary(password)}.${(encodeBinary((name || '').trim()))}`,
|
|
205
205
|
metadata,
|
|
@@ -212,7 +212,7 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
|
|
|
212
212
|
const f = await (await fetch(_customSignup(projectUrl, uglify), reqBuilder)).json();
|
|
213
213
|
if (f.simpleError) throw f;
|
|
214
214
|
|
|
215
|
-
const r = uglify ? deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
|
|
215
|
+
const r = uglify ? await deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
|
|
216
216
|
|
|
217
217
|
resolve({
|
|
218
218
|
user: parseToken(r.result.token),
|
|
@@ -249,7 +249,7 @@ export const doSignOut = async (builder) => {
|
|
|
249
249
|
try {
|
|
250
250
|
await awaitReachableServer(projectUrl);
|
|
251
251
|
|
|
252
|
-
const [reqBuilder] = buildFetchInterface({
|
|
252
|
+
const [reqBuilder] = await buildFetchInterface({
|
|
253
253
|
body: { token, r_token },
|
|
254
254
|
accessKey,
|
|
255
255
|
uglify,
|
|
@@ -269,7 +269,7 @@ const doGoogleSignin = (builder, token) => new Promise(async (resolve, reject) =
|
|
|
269
269
|
|
|
270
270
|
try {
|
|
271
271
|
await awaitStore();
|
|
272
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
272
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
273
273
|
body: { token },
|
|
274
274
|
accessKey,
|
|
275
275
|
uglify,
|
|
@@ -279,7 +279,7 @@ const doGoogleSignin = (builder, token) => new Promise(async (resolve, reject) =
|
|
|
279
279
|
const r = await (await fetch(_googleSignin(projectUrl, uglify), reqBuilder)).json();
|
|
280
280
|
if (r.simpleError) throw r;
|
|
281
281
|
|
|
282
|
-
const f = uglify ? deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
282
|
+
const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
283
283
|
|
|
284
284
|
resolve({
|
|
285
285
|
user: parseToken(f.result.token),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { niceHash, shuffleArray, sortArrayByObjectKey } from "../../helpers/peripherals";
|
|
2
2
|
import { awaitStore, updateCacheStore } from "../../helpers/utils";
|
|
3
3
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
4
|
-
import { CompareBson, confirmFilterDoc, defaultBSON, downcastBSON, validateCollectionName, validateFilter } from "./validator";
|
|
4
|
+
import { assignExtractionFind, CompareBson, confirmFilterDoc, defaultBSON, downcastBSON, validateCollectionName, validateFilter } from "./validator";
|
|
5
5
|
import getLodash from 'lodash.get';
|
|
6
6
|
import setLodash from 'lodash.set';
|
|
7
7
|
import unsetLodash from 'lodash.unset';
|
|
@@ -48,7 +48,7 @@ export const insertRecord = async (builder, config, accessId, value) => {
|
|
|
48
48
|
|
|
49
49
|
await awaitStore();
|
|
50
50
|
const { projectUrl, dbUrl, dbName, path, command } = builder;
|
|
51
|
-
const entityId = generateRecordID({}, config);
|
|
51
|
+
const entityId = await generateRecordID({}, config);
|
|
52
52
|
const colData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'data', entityId], { config, command, listing: [] });
|
|
53
53
|
const trackedData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'record', accessId]);
|
|
54
54
|
|
|
@@ -106,7 +106,7 @@ export const getRecord = async (builder, config, accessId) => {
|
|
|
106
106
|
await awaitStore();
|
|
107
107
|
const { projectUrl, dbUrl, dbName, path, command } = builder;
|
|
108
108
|
const { find, findOne, sort, direction, limit, random } = command;
|
|
109
|
-
const entityId = generateRecordID({}, config);
|
|
109
|
+
const entityId = await generateRecordID({}, config);
|
|
110
110
|
const colData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'data', entityId]);
|
|
111
111
|
const colRecord = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'record', accessId]);
|
|
112
112
|
|
|
@@ -193,16 +193,18 @@ const recurseNonAtomicWrite = (obj, i, type) => {
|
|
|
193
193
|
if (k === '_id') throw `avoid providing "_id" for ${type}() operation as _id only reference a single document`;
|
|
194
194
|
if (k === '_foreign_doc') throw '"_foreign_doc" is readonly';
|
|
195
195
|
}
|
|
196
|
-
if (k.includes('$') || k.includes('.'))
|
|
197
|
-
|
|
198
|
-
|
|
196
|
+
if (k.includes('$') || k.includes('.')) {
|
|
197
|
+
if (!(k === '$timestamp' && v === 'now'))
|
|
198
|
+
throw `invalid property "${k}", ${type}() operation fields must not contain .$`;
|
|
199
|
+
}
|
|
200
|
+
if (Validator.OBJECT(v)) recurseNonAtomicWrite(v, i + 1, type);
|
|
199
201
|
});
|
|
200
202
|
};
|
|
201
203
|
|
|
202
204
|
const recurseAtomicWrite = (obj, i, type) => {
|
|
203
205
|
if (!Validator.OBJECT(obj)) throw `expected a document but got ${obj}`;
|
|
204
|
-
Object.entries(obj).forEach(([k]) => {
|
|
205
|
-
if (!(k in AtomicWriter)) throw `Unknown update operator: ${k}`;
|
|
206
|
+
Object.entries(obj).forEach(([k, v]) => {
|
|
207
|
+
if (!i && !(k in AtomicWriter)) throw `Unknown update operator: ${k}`;
|
|
206
208
|
if (i === 1) {
|
|
207
209
|
if ((k === '_id' || k.startsWith('_id.')))
|
|
208
210
|
throw `avoid providing "_id" for ${type}() operation as _id only reference a single document`;
|
|
@@ -211,7 +213,7 @@ const recurseAtomicWrite = (obj, i, type) => {
|
|
|
211
213
|
throw '"_foreign_doc" is readonly';
|
|
212
214
|
}
|
|
213
215
|
if (k.includes('.$')) throw `unsupported operation at "${k}"`;
|
|
214
|
-
if (!i) recurseAtomicWrite(
|
|
216
|
+
if (!i || Validator.OBJECT(v)) recurseAtomicWrite(v, i + 1, type);
|
|
215
217
|
});
|
|
216
218
|
};
|
|
217
219
|
|
|
@@ -299,7 +301,7 @@ export const syncCache = (builder, result) => {
|
|
|
299
301
|
result.value.map(({ scope, value, find, path }) =>
|
|
300
302
|
({ type: scope, value, find, path })
|
|
301
303
|
)
|
|
302
|
-
: { ...result, path: builder.path }
|
|
304
|
+
: [{ ...result, path: builder.path }]
|
|
303
305
|
).forEach(({ value: writeObj, find, type, path }) => {
|
|
304
306
|
WriteValidator[type]({ find, value: writeObj });
|
|
305
307
|
validateCollectionName(path);
|
|
@@ -600,22 +602,6 @@ const getFindFields = (find) => {
|
|
|
600
602
|
return result.filter((v, i, a) => a.findIndex(b => b === v) === i);
|
|
601
603
|
};
|
|
602
604
|
|
|
603
|
-
const assignExtractionFind = (data, find) => {
|
|
604
|
-
if (!find) return find;
|
|
605
|
-
|
|
606
|
-
if (niceGuard({ $dynamicValue: GuardSignal.NON_EMPTY_STRING }, find)) {
|
|
607
|
-
return getLodash(data, find.$dynamicValue) || null;
|
|
608
|
-
} else if (Validator.OBJECT(find)) {
|
|
609
|
-
return Object.fromEntries(
|
|
610
|
-
Object.entries(find).map(([k, v]) =>
|
|
611
|
-
Validator.JSON(v) ? [k, assignExtractionFind(data, v)] : [k, v]
|
|
612
|
-
)
|
|
613
|
-
);
|
|
614
|
-
} else if (Array.isArray(find)) {
|
|
615
|
-
return find.map(v => assignExtractionFind(data, v));
|
|
616
|
-
} else return find;
|
|
617
|
-
};
|
|
618
|
-
|
|
619
605
|
const deserializeWriteValue = (value) => {
|
|
620
606
|
if (!value) return value;
|
|
621
607
|
|
|
@@ -133,9 +133,9 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
133
133
|
const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify, castBSON } = builder;
|
|
134
134
|
const { find, findOne, sort, direction, limit } = command;
|
|
135
135
|
const { disableAuth } = config || {};
|
|
136
|
-
const accessId = generateRecordID(builder, config);
|
|
137
136
|
const shouldCache = !disableCache;
|
|
138
137
|
const processId = `${++Scoped.AnyProcessIte}`;
|
|
138
|
+
let accessId;
|
|
139
139
|
|
|
140
140
|
validateListenFindConfig(config);
|
|
141
141
|
validateFilter(findOne || find);
|
|
@@ -159,9 +159,13 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
159
159
|
};
|
|
160
160
|
|
|
161
161
|
if (shouldCache) {
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
|
|
162
|
+
accessId = generateRecordID(builder, config).then(hash => {
|
|
163
|
+
if (hasCancelled) return hash;
|
|
164
|
+
cacheListener = listenQueryEntry(snapshot => {
|
|
165
|
+
if (!Scoped.IS_CONNECTED[projectUrl]) dispatchSnapshot(snapshot);
|
|
166
|
+
}, { accessId: hash, builder, config, processId });
|
|
167
|
+
return hash;
|
|
168
|
+
});
|
|
165
169
|
|
|
166
170
|
awaitStore().then(() => {
|
|
167
171
|
if (hasCancelled) return;
|
|
@@ -193,7 +197,7 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
193
197
|
dbUrl
|
|
194
198
|
};
|
|
195
199
|
|
|
196
|
-
const [encPlate, [privateKey]] = uglify ? serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
200
|
+
const [encPlate, [privateKey]] = uglify ? await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
197
201
|
|
|
198
202
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
199
203
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
@@ -210,13 +214,15 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
210
214
|
socket.on('mSnapshot', async ([err, snapshot]) => {
|
|
211
215
|
hasRespond = true;
|
|
212
216
|
if (err) {
|
|
213
|
-
onError
|
|
217
|
+
if (typeof onError === 'function') {
|
|
218
|
+
onError(simplifyCaughtError(err).simpleError);
|
|
219
|
+
} else console.error('unhandled listen for:', { path, find }, ' error:', err);
|
|
214
220
|
} else {
|
|
215
|
-
if (uglify) snapshot = deserializeE2E(snapshot, serverE2E_PublicKey, privateKey);
|
|
221
|
+
if (uglify) snapshot = await deserializeE2E(snapshot, serverE2E_PublicKey, privateKey);
|
|
216
222
|
snapshot = deserializeBSON(snapshot)._;
|
|
217
223
|
dispatchSnapshot(snapshot);
|
|
218
224
|
|
|
219
|
-
if (shouldCache) insertRecord(builder, config, accessId, snapshot);
|
|
225
|
+
if (shouldCache) insertRecord(builder, config, await accessId, snapshot);
|
|
220
226
|
}
|
|
221
227
|
});
|
|
222
228
|
|
|
@@ -284,7 +290,7 @@ const initOnDisconnectionTask = (builder, value, type) => {
|
|
|
284
290
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
285
291
|
transports: ['websocket', 'polling', 'flashsocket'],
|
|
286
292
|
auth: uglify ? {
|
|
287
|
-
e2e: serializeE2E(authObj, mtoken, serverE2E_PublicKey)[0],
|
|
293
|
+
e2e: (await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey))[0],
|
|
288
294
|
_m_internal: true
|
|
289
295
|
} : {
|
|
290
296
|
...mtoken ? { mtoken } : {},
|
|
@@ -334,7 +340,7 @@ const countCollection = async (builder, config) => {
|
|
|
334
340
|
const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, uglify, path, disableCache, command = {} } = builder;
|
|
335
341
|
const { find } = command;
|
|
336
342
|
const { disableAuth } = config || {};
|
|
337
|
-
const accessId = generateRecordID({ ...builder, countDoc: true }, config);
|
|
343
|
+
const accessId = await generateRecordID({ ...builder, countDoc: true }, config);
|
|
338
344
|
|
|
339
345
|
await awaitStore();
|
|
340
346
|
if (config !== undefined)
|
|
@@ -359,7 +365,7 @@ const countCollection = async (builder, config) => {
|
|
|
359
365
|
if (!disableAuth && await getReachableServer(projectUrl))
|
|
360
366
|
await awaitRefreshToken(projectUrl);
|
|
361
367
|
|
|
362
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
368
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
363
369
|
body: {
|
|
364
370
|
commands: { path, find: serializeToBase64(find) },
|
|
365
371
|
dbName,
|
|
@@ -374,7 +380,7 @@ const countCollection = async (builder, config) => {
|
|
|
374
380
|
const r = await (await fetch(_documentCount(projectUrl, uglify), reqBuilder)).json();
|
|
375
381
|
if (r.simpleError) throw r;
|
|
376
382
|
|
|
377
|
-
const f = uglify ? deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
383
|
+
const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
378
384
|
|
|
379
385
|
if (!disableCache)
|
|
380
386
|
setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, accessId], f.result);
|
|
@@ -424,7 +430,7 @@ const findObject = async (builder, config) => {
|
|
|
424
430
|
const { find, findOne, sort, direction, limit, random } = command;
|
|
425
431
|
const { retrieval = RETRIEVAL.DEFAULT, episode = 0, disableAuth, disableMinimizer } = config || {};
|
|
426
432
|
const enableMinimizer = !disableMinimizer;
|
|
427
|
-
const accessId = generateRecordID(builder, config);
|
|
433
|
+
const accessId = await generateRecordID(builder, config);
|
|
428
434
|
const processAccessId = `${accessId}${projectUrl}${dbUrl}${dbName}${retrieval}`;
|
|
429
435
|
const getRecordData = () => getRecord(builder, config, accessId);
|
|
430
436
|
const shouldCache = (retrieval !== RETRIEVAL.DEFAULT || !disableCache) &&
|
|
@@ -491,7 +497,7 @@ const findObject = async (builder, config) => {
|
|
|
491
497
|
if (!disableAuth && await getReachableServer(projectUrl))
|
|
492
498
|
await awaitRefreshToken(projectUrl);
|
|
493
499
|
|
|
494
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
500
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
495
501
|
body: {
|
|
496
502
|
commands: {
|
|
497
503
|
config: pureConfig && serializeToBase64(pureConfig),
|
|
@@ -514,7 +520,7 @@ const findObject = async (builder, config) => {
|
|
|
514
520
|
const r = await (await fetch((findOne ? _readDocument : _queryCollection)(projectUrl, uglify), reqBuilder)).json();
|
|
515
521
|
if (r.simpleError) throw r;
|
|
516
522
|
|
|
517
|
-
const result = deserializeBSON((uglify ? deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r).result)._;
|
|
523
|
+
const result = deserializeBSON((uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r).result)._;
|
|
518
524
|
|
|
519
525
|
if (shouldCache) insertRecord(builder, config, accessId, result);
|
|
520
526
|
finalize({ liveResult: result || null });
|
|
@@ -628,7 +634,7 @@ const commitData = async (builder, value, type, config) => {
|
|
|
628
634
|
if (!disableAuth && await getReachableServer(projectUrl))
|
|
629
635
|
await awaitRefreshToken(projectUrl);
|
|
630
636
|
|
|
631
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
637
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
632
638
|
body: {
|
|
633
639
|
commands: {
|
|
634
640
|
value: value && serializeToBase64({ _: value }),
|
|
@@ -650,7 +656,7 @@ const commitData = async (builder, value, type, config) => {
|
|
|
650
656
|
const r = await (await fetch((isBatchWrite ? _writeMapDocument : _writeDocument)(projectUrl, uglify), reqBuilder)).json();
|
|
651
657
|
if (r.simpleError) throw r;
|
|
652
658
|
|
|
653
|
-
const f = uglify ? deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
659
|
+
const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
|
|
654
660
|
|
|
655
661
|
finalize({ ...f }, undefined, { removeCache: true });
|
|
656
662
|
} catch (e) {
|
|
@@ -6,10 +6,15 @@ import { Binary, BSONRegExp, BSONSymbol, Code, DBRef, Decimal128, Double, Int32,
|
|
|
6
6
|
import { bboxPolygon, booleanIntersects, booleanWithin, circle, distance, polygon } from "@turf/turf";
|
|
7
7
|
|
|
8
8
|
const DirectionList = [1, -1, 'asc', 'desc', 'ascending', 'descending'];
|
|
9
|
-
const FilterFootPrint = t =>
|
|
9
|
+
const FilterFootPrint = t => {
|
|
10
|
+
validateFilter(t);
|
|
11
|
+
return true;
|
|
12
|
+
};
|
|
10
13
|
const ReturnAndExcludeFootprint = t => t === undefined ||
|
|
11
14
|
!(Array.isArray(t) ? t : [t]).filter(v => !Validator.TRIMMED_NON_EMPTY_STRING(v)).length;
|
|
12
15
|
|
|
16
|
+
const ConfigFind = t => t && FilterFootPrint(assignExtractionFind({}, t));
|
|
17
|
+
|
|
13
18
|
const FindConfig = {
|
|
14
19
|
extraction: t => t === undefined ||
|
|
15
20
|
(Array.isArray(t) ? t : [t]).filter(m =>
|
|
@@ -18,8 +23,8 @@ const FindConfig = {
|
|
|
18
23
|
sort: (t, p) => t === undefined || (Validator.TRIMMED_NON_EMPTY_STRING(t) && p.find),
|
|
19
24
|
direction: (t, p) => t === undefined || (p.sort && p.find && DirectionList.includes(t)),
|
|
20
25
|
limit: (t, p) => t === undefined || (Validator.POSITIVE_INTEGER(t) && p.find),
|
|
21
|
-
find: (t, p) => (t === undefined && p.findOne) || (!p.findOne &&
|
|
22
|
-
findOne: (t, p) => (t === undefined && p.find) || (!p.find &&
|
|
26
|
+
find: (t, p) => (t === undefined && p.findOne) || (!p.findOne && ConfigFind(t)),
|
|
27
|
+
findOne: (t, p) => (t === undefined && p.find) || (!p.find && ConfigFind(t)),
|
|
23
28
|
returnOnly: ReturnAndExcludeFootprint,
|
|
24
29
|
excludeFields: ReturnAndExcludeFootprint
|
|
25
30
|
}).validate(m)
|
|
@@ -41,7 +46,8 @@ export const validateListenFindConfig = (config) => config === undefined ||
|
|
|
41
46
|
extraction: FindConfig.extraction,
|
|
42
47
|
returnOnly: FindConfig.returnOnly,
|
|
43
48
|
excludeFields: FindConfig.excludeFields,
|
|
44
|
-
disableAuth: FindConfig.disableAuth
|
|
49
|
+
disableAuth: FindConfig.disableAuth,
|
|
50
|
+
episode: t => [undefined, 0, 1].includes(t)
|
|
45
51
|
}).validate(config);
|
|
46
52
|
|
|
47
53
|
export const validateFindObject = command =>
|
|
@@ -55,6 +61,22 @@ export const validateFindObject = command =>
|
|
|
55
61
|
random: (t, p) => t === undefined || (!p.sort && t === true),
|
|
56
62
|
}).validate({ ...command });
|
|
57
63
|
|
|
64
|
+
export const assignExtractionFind = (data, find) => {
|
|
65
|
+
if (!find) return find;
|
|
66
|
+
|
|
67
|
+
if (niceGuard({ $dynamicValue: GuardSignal.NON_EMPTY_STRING }, find)) {
|
|
68
|
+
return getLodash(data, find.$dynamicValue) || null;
|
|
69
|
+
} else if (Validator.OBJECT(find)) {
|
|
70
|
+
return Object.fromEntries(
|
|
71
|
+
Object.entries(find).map(([k, v]) =>
|
|
72
|
+
Validator.JSON(v) ? [k, assignExtractionFind(data, v)] : [k, v]
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
} else if (Array.isArray(find)) {
|
|
76
|
+
return find.map(v => assignExtractionFind(data, v));
|
|
77
|
+
} else return find;
|
|
78
|
+
};
|
|
79
|
+
|
|
58
80
|
export const validateCollectionName = collectionName => {
|
|
59
81
|
// Check if the collection name is empty
|
|
60
82
|
if (!collectionName || typeof collectionName !== 'string')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Buffer } from "buffer";
|
|
2
|
-
import { deserializeE2E, listenReachableServer, niceHash, serializeE2E } from "../../helpers/peripherals";
|
|
2
|
+
import { deserializeE2E, listenReachableServer, niceHash, normalizeRoute, serializeE2E } from "../../helpers/peripherals";
|
|
3
3
|
import { awaitStore, getReachableServer, updateCacheStore } from "../../helpers/utils";
|
|
4
4
|
import { RETRIEVAL } from "../../helpers/values";
|
|
5
5
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
@@ -7,6 +7,7 @@ import { awaitRefreshToken } from "../auth/accessor";
|
|
|
7
7
|
import { simplifyCaughtError } from "simplify-error";
|
|
8
8
|
import { guardObject, Validator } from "guard-object";
|
|
9
9
|
import cloneDeep from "lodash.clonedeep";
|
|
10
|
+
import { stringify } from "json-buffer";
|
|
10
11
|
|
|
11
12
|
const buildFetchData = (data) => {
|
|
12
13
|
const { ok, type, status, statusText, redirected, url, headers, size, base64 } = data;
|
|
@@ -35,7 +36,7 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
35
36
|
const { projectUrl, serverE2E_PublicKey, method, maxRetries = 7, disableCache, accessKey, uglify } = config;
|
|
36
37
|
const { headers, body } = init || {};
|
|
37
38
|
|
|
38
|
-
if (
|
|
39
|
+
if (method !== undefined)
|
|
39
40
|
guardObject({
|
|
40
41
|
enableMinimizer: t => t === undefined || Validator.BOOLEAN(t),
|
|
41
42
|
rawApproach: t => t === undefined || Validator.BOOLEAN(t),
|
|
@@ -48,6 +49,8 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
48
49
|
const disableAuth = method?.disableAuth || isBaseUrl;
|
|
49
50
|
const shouldCache = (retrieval !== RETRIEVAL.DEFAULT || !disableCache) &&
|
|
50
51
|
retrieval !== RETRIEVAL.NO_CACHE_NO_AWAIT;
|
|
52
|
+
const uglified = !!(!isBaseUrl && uglify);
|
|
53
|
+
|
|
51
54
|
const rawHeader = Object.fromEntries(
|
|
52
55
|
[...new Headers(headers).entries()]
|
|
53
56
|
);
|
|
@@ -58,16 +61,25 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
58
61
|
if ('uglified' in rawHeader)
|
|
59
62
|
throw '"uglified" in header is a reserved prop';
|
|
60
63
|
|
|
61
|
-
if (
|
|
64
|
+
if (isBaseUrl && !rawApproach)
|
|
62
65
|
throw `please set { rawApproach: true } if you're trying to access different endpoint at "${input}"`;
|
|
63
66
|
|
|
64
|
-
if (body !== undefined
|
|
65
|
-
|
|
67
|
+
if (body !== undefined) {
|
|
68
|
+
if (
|
|
69
|
+
typeof body !== 'string' &&
|
|
70
|
+
!Buffer.isBuffer(body) &&
|
|
71
|
+
!Validator.JSON(body) &&
|
|
72
|
+
!(body instanceof File) &&
|
|
73
|
+
!(body instanceof Blob)
|
|
74
|
+
) throw `"body" must be any of string, buffer, object, File, Blob`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const rawBody = stringify([(body instanceof File || body instanceof Blob) ? Buffer.from(await body.arrayBuffer()) : body]);
|
|
66
78
|
|
|
67
|
-
const reqId = niceHash(
|
|
79
|
+
const reqId = await niceHash(
|
|
68
80
|
JSON.stringify([
|
|
69
81
|
rawHeader,
|
|
70
|
-
|
|
82
|
+
rawBody,
|
|
71
83
|
!!disableAuth,
|
|
72
84
|
input
|
|
73
85
|
])
|
|
@@ -131,19 +143,18 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
131
143
|
await awaitRefreshToken(projectUrl);
|
|
132
144
|
|
|
133
145
|
const mtoken = Scoped.AuthJWTToken[projectUrl];
|
|
134
|
-
const uglified = !!(!isBaseUrl && body && uglify);
|
|
135
146
|
const initType = rawHeader['content-type'];
|
|
136
147
|
|
|
137
|
-
const [reqBuilder, [privateKey]] = uglified ? serializeE2E(
|
|
148
|
+
const [reqBuilder, [privateKey]] = uglified ? await serializeE2E(rawBody, mtoken, serverE2E_PublicKey) : [null, []];
|
|
138
149
|
|
|
139
|
-
const f = await fetch(isBaseUrl ? input : `${projectUrl}/${input}`, {
|
|
150
|
+
const f = await fetch(isBaseUrl ? input : `${projectUrl}/${normalizeRoute(input)}`, {
|
|
140
151
|
...isBaseUrl ? {} : { method: 'POST' },
|
|
141
152
|
...init,
|
|
142
153
|
...uglified ? { body: reqBuilder } : {},
|
|
143
154
|
cache: 'no-cache',
|
|
144
155
|
headers: {
|
|
145
|
-
...isBaseUrl ? {} : { '
|
|
146
|
-
...
|
|
156
|
+
...isBaseUrl ? {} : { 'content-type': 'application/json' },
|
|
157
|
+
...rawHeader,
|
|
147
158
|
...uglified ? {
|
|
148
159
|
uglified,
|
|
149
160
|
'content-type': 'text/plain',
|
|
@@ -159,7 +170,7 @@ export const mfetch = async (input = '', init, config) => {
|
|
|
159
170
|
if (!isBaseUrl && simple) throw { simpleError: JSON.parse(simple) };
|
|
160
171
|
|
|
161
172
|
const base64 = uglified ?
|
|
162
|
-
Buffer.from(deserializeE2E(await f.text(), serverE2E_PublicKey, privateKey), 'base64') :
|
|
173
|
+
Buffer.from(await deserializeE2E(await f.text(), serverE2E_PublicKey, privateKey), 'base64') :
|
|
163
174
|
Buffer.from(await f.arrayBuffer()).toString('base64');
|
|
164
175
|
|
|
165
176
|
const resObj = {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import EngineApi from "../../helpers/engine_api";
|
|
2
2
|
import { encodeBinary } from "../../helpers/peripherals";
|
|
3
3
|
import { Scoped } from "../../helpers/variables";
|
|
4
|
-
import { awaitReachableServer, buildFetchInterface
|
|
4
|
+
import { awaitReachableServer, buildFetchInterface } from "../../helpers/utils";
|
|
5
5
|
import { awaitRefreshToken } from "../auth/accessor";
|
|
6
6
|
import { Buffer } from "buffer";
|
|
7
|
+
import { simplifyError } from "simplify-error";
|
|
7
8
|
|
|
8
9
|
export class MTStorage {
|
|
9
10
|
constructor(config) {
|
|
@@ -104,7 +105,7 @@ const deleteContent = async (builder, path, isFolder) => {
|
|
|
104
105
|
try {
|
|
105
106
|
const r = await (await fetch(
|
|
106
107
|
EngineApi[isFolder ? '_deleteFolder' : '_deleteFile'](projectUrl, uglify),
|
|
107
|
-
buildFetchInterface({ path }, accessKey, Scoped.AuthJWTToken[projectUrl], 'DELETE')
|
|
108
|
+
await buildFetchInterface({ path }, accessKey, Scoped.AuthJWTToken[projectUrl], 'DELETE')
|
|
108
109
|
)).json();
|
|
109
110
|
if (r.simpleError) throw r;
|
|
110
111
|
if (r.status !== 'success') throw 'operation not successful';
|