mosquito-transport-js 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mosquito-transport-js",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
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,9 +32,9 @@
32
32
  "bson": "^6.8.0",
33
33
  "buffer": "^6.0.3",
34
34
  "crypto-js": "^4.2.0",
35
+ "entity-serializer": "^1.0.1",
35
36
  "guard-object": "^1.1.3",
36
37
  "limit-task": "1.0.0",
37
- "json-buffer": "^3.0.1",
38
38
  "lodash.clonedeep": "^4.5.0",
39
39
  "lodash.get": "^4.4.2",
40
40
  "lodash.set": "^4.3.2",
@@ -57,4 +57,4 @@
57
57
  "engines": {
58
58
  "node": ">=14"
59
59
  }
60
- }
60
+ }
@@ -5,6 +5,7 @@ import Utf8Encoder from 'crypto-js/enc-utf8.js';
5
5
  import naclPkg from 'tweetnacl-functional';
6
6
  import getLodash from "lodash.get";
7
7
  import e2e_worker from "./e2e_worker";
8
+ import { deserialize, serialize } from "entity-serializer";
8
9
 
9
10
  const { encrypt, decrypt } = aes_pkg;
10
11
  const { box, randomBytes } = naclPkg;
@@ -104,51 +105,51 @@ export const decryptString = (txt, password, iv) => {
104
105
  };
105
106
 
106
107
  export const serializeE2E = async (data, auth_token, serverPublicKey) => {
107
- const inputData = new Uint8Array(Buffer.from(JSON.stringify([data, auth_token]), 'utf8'));
108
+ const inputData = serialize([data, auth_token]);
108
109
 
109
110
  if (inputData.byteLength > 10240) {
110
111
  // 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');
112
+ const { data, pair, nonce } = await e2e_worker.encrypt(inputData, serverPublicKey);
114
113
 
115
114
  return [
116
- `${pubBase64}.${nonceBase64}.${Buffer.from(data).toString('base64')}`,
115
+ serialize([pair.publicKey, nonce, data]),
117
116
  [pair.secretKey, pair.publicKey]
118
117
  ];
119
118
  }
120
119
 
121
120
  const pair = box.keyPair(),
122
- nonce = randomBytes(box.nonceLength),
123
- pubBase64 = Buffer.from(pair.publicKey).toString('base64'),
124
- nonceBase64 = Buffer.from(nonce).toString('base64');
121
+ nonce = randomBytes(box.nonceLength);
125
122
 
126
123
  return [
127
- `${pubBase64}.${nonceBase64}.${Buffer.from(
128
- box(
129
- inputData,
130
- nonce,
131
- serverPublicKey,
132
- pair.secretKey
124
+ serialize([
125
+ pair.publicKey,
126
+ nonce,
127
+ Buffer.from(
128
+ box(
129
+ inputData,
130
+ nonce,
131
+ serverPublicKey,
132
+ pair.secretKey
133
+ )
133
134
  )
134
- ).toString('base64')}`,
135
+ ]),
135
136
  [pair.secretKey, pair.publicKey]
136
137
  ];
137
138
  };
138
139
 
139
- export const deserializeE2E = async (data = '', serverPublicKey, clientPrivateKey) => {
140
- const [binaryNonce, binaryData] = data.split('.').map(v => new Uint8Array(Buffer.from(v, 'base64')));
140
+ export const deserializeE2E = async (data, serverPublicKey, clientPrivateKey) => {
141
+ const [binaryNonce, binaryData] = deserialize(data);
141
142
  let baseArray;
142
143
 
143
144
  if (binaryData.byteLength > 10240) {
144
145
  // dispatch to background thread
145
- baseArray = e2e_worker.decrypt(binaryData, binaryNonce, serverPublicKey, clientPrivateKey);
146
+ baseArray = await e2e_worker.decrypt(binaryData, binaryNonce, serverPublicKey, clientPrivateKey);
146
147
  } else {
147
148
  baseArray = box.open(binaryData, binaryNonce, serverPublicKey, clientPrivateKey);
148
149
  }
149
150
 
150
151
  if (!baseArray) throw 'Decrypting e2e message failed';
151
- return JSON.parse(Buffer.from(baseArray).toString('utf8'))[0];
152
+ return deserialize(baseArray);
152
153
  };
153
154
 
154
155
  export const encodeBinary = (s) => Buffer.from(s, 'utf8').toString('base64');
@@ -4,6 +4,7 @@ import { CacheStore, Scoped } from "./variables";
4
4
  import { decryptString, encryptString, serializeE2E } from "./peripherals";
5
5
  import { deserializeBSON, serializeToBase64 } from "../products/database/bson";
6
6
  import { trySendPendingWrite } from "../products/database";
7
+ import { deserialize } from "entity-serializer";
7
8
 
8
9
  export const updateCacheStore = (timer = 300) => {
9
10
  try { window } catch (_) { return; }
@@ -116,7 +117,7 @@ export const getReachableServer = (projectUrl) => new Promise(resolve => {
116
117
  }, true);
117
118
  });
118
119
 
119
- export const buildFetchInterface = async ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey }) => {
120
+ export const buildFetchInterface = async ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey, extraHeaders }) => {
120
121
  if (!uglify) body = JSON.stringify({ ...body });
121
122
  const [plate, keyPair] = uglify ? await serializeE2E(body, authToken, serverE2E_PublicKey) : [undefined, []];
122
123
 
@@ -124,10 +125,22 @@ export const buildFetchInterface = async ({ body, accessKey, authToken, method,
124
125
  body: uglify ? plate : body,
125
126
  cache: 'no-cache',
126
127
  headers: {
127
- 'Content-type': uglify ? 'text/plain' : 'application/json',
128
+ 'Content-type': uglify ? 'request/buffer' : 'application/json',
128
129
  'Authorization': accessKey,
129
- ...((authToken && !uglify) ? { 'Mosquito-Token': authToken } : {})
130
+ ...(authToken && !uglify) ? { 'Mosquito-Token': authToken } : {},
131
+ ...extraHeaders
130
132
  },
131
133
  method: method || 'POST'
132
134
  }, keyPair];
135
+ };
136
+
137
+ export const buildFetchResult = async (fetchRef, ugly) => {
138
+ if (ugly) {
139
+ const [data, simpleError] = deserialize(await fetchRef.arrayBuffer());
140
+ if (simpleError) throw simpleError;
141
+ return data;
142
+ }
143
+ const json = await fetchRef.json();
144
+ if (json.simpleError) throw json;
145
+ return json;
133
146
  };
package/src/index.d.ts CHANGED
@@ -16,6 +16,12 @@ interface MTConfig {
16
16
  * this is the base64 public key for end-to-end encryption on the server
17
17
  */
18
18
  serverE2E_PublicKey?: string;
19
+ /**
20
+ * extra headers that will be appended to all outgoing request in this instance
21
+ */
22
+ extraHeaders?: {
23
+ [key: string]: string
24
+ };
19
25
  /**
20
26
  * true to deserialize BSON values to their Node.js closest equivalent types
21
27
  *
@@ -54,6 +60,13 @@ export function FIND_GEO_JSON(coordinates: [latitude, longitude], offSetMeters:
54
60
 
55
61
  export const AUTH_PROVIDER_ID: auth_provider_id;
56
62
 
63
+ /**
64
+ * useful for avoiding encrypting data and extra overhead
65
+ */
66
+ export class DoNotEncrypt {
67
+ value: any;
68
+ }
69
+
57
70
  interface auth_provider_id {
58
71
  GOOGLE: 'google.com';
59
72
  FACEBOOK: 'facebook.com';
@@ -436,14 +449,27 @@ interface TokenManager {
436
449
 
437
450
  declare type Base64String = string;
438
451
 
439
- interface ReqOptions {
440
- awaitServer?: boolean;
452
+ interface UploadOptions {
453
+ /**
454
+ * optionally create hash for this upload to save disk space
455
+ */
441
456
  createHash?: boolean;
457
+ /**
458
+ * wait for a reachable server before initiating the upload task
459
+ */
460
+ awaitServer?: boolean;
461
+ }
462
+
463
+ interface DownloadOptions {
464
+ /**
465
+ * wait for a reachable server before initiating the download task
466
+ */
467
+ awaitServer?: boolean;
442
468
  }
443
469
 
444
470
  interface MTStorage {
445
- downloadFile: (link: string, onComplete?: (error?: ErrorResponse, filepath?: string) => void, destination?: string, onProgress?: (stats: DownloadProgressStats) => void) => () => void;
446
- uploadFile: (file: Base64String | Blob | Buffer | File, destination: string, onComplete?: (error?: ErrorResponse, downloadUrl?: string) => void, onProgress?: (stats: UploadProgressStats) => void, options?: ReqOptions) => () => void;
471
+ downloadFile: (link: string, onComplete?: (error?: ErrorResponse, filepath?: string) => void, destination?: string, onProgress?: (stats: DownloadProgressStats) => void, options?: DownloadOptions) => () => void;
472
+ uploadFile: (file: Base64String | Blob | File | Buffer, destination: string, onComplete?: (error?: ErrorResponse, downloadUrl?: string) => void, onProgress?: (stats: UploadProgressStats) => void, options?: UploadOptions) => () => void;
447
473
  deleteFile: (path: string) => Promise<void>;
448
474
  deleteFolder: (folder: string) => Promise<void>;
449
475
  }
@@ -467,13 +493,13 @@ interface ErrorResponse {
467
493
  }
468
494
 
469
495
  /** @public */
470
- export declare type Sort = string | Exclude<SortDirection, {
496
+ declare type Sort = string | Exclude<SortDirection, {
471
497
  $meta: string;
472
498
  }> | string[] | {
473
499
  [key: string]: SortDirection;
474
500
  } | Map<string, SortDirection> | [string, SortDirection][] | [string, SortDirection];
475
501
 
476
502
  /** @public */
477
- export declare type SortDirection = 1 | -1 | 'asc' | 'desc' | 'ascending' | 'descending' | {
503
+ declare type SortDirection = 1 | -1 | 'asc' | 'desc' | 'ascending' | 'descending' | {
478
504
  $meta: string;
479
505
  };
package/src/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import './polyfill';
2
1
  import { deserializeE2E, listenReachableServer, serializeE2E } from "./helpers/peripherals";
3
2
  import { releaseCacheStore, awaitStore } from "./helpers/utils";
4
3
  import { CacheStore, Scoped } from "./helpers/variables";
@@ -12,7 +11,6 @@ import { mfetch } from "./products/http_callable";
12
11
  import { io } from "socket.io-client";
13
12
  import { AUTH_PROVIDER_ID, CACHE_PROTOCOL } from "./helpers/values";
14
13
  import EngineApi from './helpers/engine_api';
15
- import { parse, stringify } from 'json-buffer';
16
14
  import { Validator } from 'guard-object';
17
15
  import sendMessage from "./helpers/broadcaster";
18
16
  import cloneDeep from "lodash.clonedeep";
@@ -46,7 +44,7 @@ export class MosquitoTransport {
46
44
  maxRetries: config.maxRetries || 3,
47
45
  uglify: config.enableE2E_Encryption
48
46
  };
49
- const { projectUrl } = this.config;
47
+ const { projectUrl, extraHeaders } = this.config;
50
48
 
51
49
  this.config.secureUrl = projectUrl.startsWith('https');
52
50
  this.config.baseUrl = projectUrl.split('://')[1];
@@ -67,6 +65,7 @@ export class MosquitoTransport {
67
65
 
68
66
  const socket = io(`${this.config.wsPrefix}://${this.config.baseUrl}`, {
69
67
  transports: ['websocket', 'polling', 'flashsocket'],
68
+ extraHeaders,
70
69
  auth: {
71
70
  _m_internal: true,
72
71
  _from_base: true
@@ -154,7 +153,7 @@ export class MosquitoTransport {
154
153
 
155
154
  getSocket = (configOpts) => {
156
155
  const { disableAuth, authHandshake } = configOpts || {};
157
- const { projectUrl, uglify, accessKey, serverE2E_PublicKey, wsPrefix } = this.config;
156
+ const { projectUrl, uglify, accessKey, serverE2E_PublicKey, wsPrefix, extraHeaders } = this.config;
158
157
 
159
158
  const restrictedRoute = [
160
159
  _listenCollection,
@@ -185,22 +184,23 @@ export class MosquitoTransport {
185
184
  return;
186
185
  }
187
186
 
188
- const [args, emitable] = [...arguments];
187
+ const [[args, not_encrypted], emitable] = [...arguments];
189
188
  let res;
190
189
 
191
190
  if (uglify) {
192
- res = parse(await deserializeE2E(args, serverE2E_PublicKey, clientPrivateKey));
191
+ res = await deserializeE2E(args, serverE2E_PublicKey, clientPrivateKey);
193
192
  } else res = args;
193
+ const sortedArgs = discloseSocketArguments([res, not_encrypted]);
194
194
 
195
- callback?.(...res || [], ...typeof emitable === 'function' ? [async function () {
196
- const args = [...arguments];
195
+ callback?.(...sortedArgs, ...typeof emitable === 'function' ? [async function () {
196
+ const [args, not_encrypted] = encloseSocketArguments([...arguments]);
197
197
  let res;
198
198
 
199
199
  if (uglify) {
200
- res = (await serializeE2E(stringify(args), undefined, serverE2E_PublicKey))[0];
200
+ res = (await serializeE2E(args, undefined, serverE2E_PublicKey))[0];
201
201
  } else res = args;
202
202
 
203
- emitable(res);
203
+ emitable([res, not_encrypted]);
204
204
  }] : []);
205
205
  };
206
206
 
@@ -229,28 +229,29 @@ export class MosquitoTransport {
229
229
 
230
230
  const lastEmit = emittion.slice(-1)[0];
231
231
  const hasEmitable = typeof lastEmit === 'function';
232
- const mit = hasEmitable ? emittion.slice(0, -1) : emittion;
232
+ const [mit, not_encrypted] = encloseSocketArguments(hasEmitable ? emittion.slice(0, -1) : emittion);
233
233
 
234
- const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(stringify(mit), undefined, serverE2E_PublicKey) : [undefined, []];
234
+ const [reqBuilder, [privateKey]] = uglify ? await serializeE2E(mit, undefined, serverE2E_PublicKey) : [undefined, []];
235
235
 
236
236
  if (hasEmitable && promise)
237
237
  throw 'emitWithAck cannot have function in it argument';
238
238
 
239
239
  const result = await thisSocket[promise ? 'emitWithAck' : 'emit'](route,
240
- uglify ? reqBuilder : mit,
240
+ [uglify ? reqBuilder : mit, not_encrypted],
241
241
  ...hasEmitable ? [async function () {
242
- const [args] = [...arguments];
242
+ const [[args, not_encrypted]] = [...arguments];
243
243
  let res;
244
244
 
245
245
  if (uglify) {
246
- res = parse(await deserializeE2E(args, serverE2E_PublicKey, privateKey));
246
+ res = await deserializeE2E(args, serverE2E_PublicKey, privateKey);
247
247
  } else res = args;
248
248
 
249
- lastEmit(...res || []);
249
+ lastEmit(...discloseSocketArguments([res, not_encrypted]));
250
250
  }] : []
251
251
  );
252
-
253
- resolve((promise && result) ? uglify ? parse(await deserializeE2E(result, serverE2E_PublicKey, privateKey))[0] : result[0] : undefined);
252
+ if (promise && result) {
253
+ resolve(discloseSocketArguments([uglify ? await deserializeE2E(result[0], serverE2E_PublicKey, privateKey) : result[0], result[1]])[0]);
254
+ } else resolve();
254
255
  } catch (e) {
255
256
  reject(e);
256
257
  }
@@ -263,9 +264,10 @@ export class MosquitoTransport {
263
264
 
264
265
  socket = io(`${wsPrefix}://${projectUrl.split('://')[1]}`, {
265
266
  transports: ['websocket', 'polling', 'flashsocket'],
267
+ extraHeaders,
266
268
  auth: uglify ? {
267
269
  ugly: true,
268
- e2e: reqBuilder
270
+ e2e: reqBuilder.toString('base64')
269
271
  } : {
270
272
  ...mtoken ? { mtoken } : {},
271
273
  a_extras: authHandshake,
@@ -359,6 +361,31 @@ export class MosquitoTransport {
359
361
  }
360
362
  };
361
363
 
364
+ class DoNotEncrypt {
365
+ constructor(value) {
366
+ this.value = value;
367
+ }
368
+ };
369
+
370
+ const encloseSocketArguments = (args) => {
371
+ const [encrypted, unencrypted] = [{}, {}];
372
+
373
+ args.forEach((v, i) => {
374
+ if (v instanceof DoNotEncrypt) {
375
+ unencrypted[i] = v.value;
376
+ } else encrypted[i] = v;
377
+ });
378
+ return [encrypted, unencrypted];
379
+ }
380
+
381
+ const discloseSocketArguments = (args = []) => {
382
+ return args.map((obj, i) => Object.entries(obj).map(v => i ? [v[0], new DoNotEncrypt(v[1])] : v)).flat()
383
+ .sort((a, b) => (a[0] * 1) - (b[0] * 1)).map((v, i) => {
384
+ if (v[0] * 1 !== i) throw 'corrupted socket arguments';
385
+ return v[1];
386
+ });
387
+ }
388
+
362
389
  const validateReleaseCacheProp = (prop) => {
363
390
  const cacheList = [...Object.values(CACHE_PROTOCOL)];
364
391
 
@@ -426,6 +453,16 @@ const validator = {
426
453
  serverE2E_PublicKey: (v) => {
427
454
  if (typeof v !== 'string' || !v.trim())
428
455
  throw `Invalid value supplied to serverETE_PublicKey, value must be a non-empty string`;
456
+ },
457
+ extraHeaders: v => {
458
+ if (!Validator.OBJECT(v)) throw '"extraHeaders" must be an object';
459
+ const reservedHeaders = ['mtoken', 'mosquito-token', 'init-content-type', 'content-type', 'authorization', 'uglified'];
460
+
461
+ Object.entries(v).forEach(([k, v]) => {
462
+ if (typeof v !== 'string') throw `expected a string at extraHeaders.${k} but got "${v}"`;
463
+ if (reservedHeaders.includes(v.toLowerCase()))
464
+ throw `extraHeaders must not include any reserved props which are: ${reservedHeaders}`;
465
+ });
429
466
  }
430
467
  };
431
468
 
@@ -445,6 +482,7 @@ const validateMTConfig = (config, that) => {
445
482
  }
446
483
 
447
484
  export {
485
+ DoNotEncrypt,
448
486
  TIMESTAMP,
449
487
  DOCUMENT_EXTRACTION,
450
488
  FIND_GEO_JSON,
@@ -2,7 +2,7 @@ import { doSignOut } from ".";
2
2
  import EngineApi from "../../helpers/engine_api";
3
3
  import { AuthTokenListener, TokenRefreshListener } from "../../helpers/listeners";
4
4
  import { decodeBinary, deserializeE2E, listenReachableServer } from "../../helpers/peripherals";
5
- import { awaitStore, buildFetchInterface, getPrefferTime, updateCacheStore } from "../../helpers/utils";
5
+ import { awaitStore, buildFetchInterface, buildFetchResult, getPrefferTime, updateCacheStore } from "../../helpers/utils";
6
6
  import { CacheStore, Scoped } from "../../helpers/variables";
7
7
  import { simplifyError } from "simplify-error";
8
8
 
@@ -75,8 +75,8 @@ export const initTokenRefresher = async (config, forceRefresh) => {
75
75
  };
76
76
 
77
77
  const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7, isForceRefresh) => new Promise(async (resolve, reject) => {
78
- const { projectUrl, serverE2E_PublicKey, accessKey, uglify } = builder;
79
- const lostProcess = simplifyError('process_lost', 'The token refresh process has been lost and replace with another one');
78
+ const { projectUrl, serverE2E_PublicKey, accessKey, uglify, extraHeaders } = builder;
79
+ const lostProcess = simplifyError('process_lost', 'The token refresh process has been lost and replaced with another one');
80
80
 
81
81
  try {
82
82
  const { token, refreshToken: r_token } = CacheStore.AuthStore[projectUrl];
@@ -85,18 +85,22 @@ const refreshToken = (builder, processRef, remainRetries = 7, initialRetries = 7
85
85
  body: { token, r_token },
86
86
  accessKey,
87
87
  uglify,
88
- serverE2E_PublicKey
88
+ serverE2E_PublicKey,
89
+ extraHeaders
89
90
  });
90
91
 
91
- const r = await (await fetch(EngineApi._refreshAuthToken(projectUrl, uglify), reqBuilder)).json();
92
+ let data;
92
93
 
93
- if (processRef !== Scoped.LastTokenRefreshRef[projectUrl]) {
94
- reject(lostProcess.simpleError);
95
- return;
94
+ try {
95
+ data = await buildFetchResult(await fetch(EngineApi._refreshAuthToken(projectUrl, uglify), reqBuilder), uglify);
96
+ } finally {
97
+ if (processRef !== Scoped.LastTokenRefreshRef[projectUrl]) {
98
+ reject(lostProcess.simpleError);
99
+ return;
100
+ }
96
101
  }
97
- if (r.simpleError) throw r;
98
102
 
99
- const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
103
+ const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
100
104
 
101
105
  if (CacheStore.AuthStore[projectUrl]) {
102
106
  CacheStore.AuthStore[projectUrl].token = f.result.token;
@@ -1,7 +1,7 @@
1
1
  import { io } from "socket.io-client";
2
2
  import EngineApi from "../../helpers/engine_api";
3
3
  import { TokenRefreshListener } from "../../helpers/listeners";
4
- import { awaitReachableServer, awaitStore, buildFetchInterface, updateCacheStore } from "../../helpers/utils";
4
+ import { awaitReachableServer, awaitStore, buildFetchInterface, buildFetchResult, updateCacheStore } from "../../helpers/utils";
5
5
  import { CacheStore, Scoped } from "../../helpers/variables";
6
6
  import { awaitRefreshToken, initTokenRefresher, injectFreshToken, listenToken, parseToken, triggerAuthToken } from "./accessor";
7
7
  import { deserializeE2E, encodeBinary, serializeE2E } from "../../helpers/peripherals";
@@ -66,7 +66,7 @@ export class MTAuth {
66
66
  socket = io(`${wsPrefix}://${baseUrl}`, {
67
67
  transports: ['websocket', 'polling', 'flashsocket'],
68
68
  auth: uglify ? {
69
- e2e: reqBuilder,
69
+ e2e: reqBuilder.toString('base64'),
70
70
  _m_internal: true
71
71
  } : { mtoken, _m_internal: true },
72
72
  transports: ['websocket', 'polling', 'flashsocket']
@@ -167,7 +167,7 @@ export class MTAuth {
167
167
  };
168
168
 
169
169
  const doCustomSignin = (builder, email, password) => new Promise(async (resolve, reject) => {
170
- const { projectUrl, serverE2E_PublicKey, accessKey, uglify } = builder;
170
+ const { projectUrl, serverE2E_PublicKey, accessKey, uglify, extraHeaders } = builder;
171
171
 
172
172
  try {
173
173
  await awaitStore();
@@ -175,13 +175,13 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
175
175
  body: { data: `${encodeBinary(email)}.${encodeBinary(password)}` },
176
176
  accessKey,
177
177
  serverE2E_PublicKey,
178
- uglify
178
+ uglify,
179
+ extraHeaders
179
180
  });
180
181
 
181
- const f = await (await fetch(_customSignin(projectUrl, uglify), reqBuilder)).json();
182
- if (f.simpleError) throw f;
182
+ const data = await buildFetchResult(await fetch(_customSignin(projectUrl, uglify), reqBuilder), uglify);
183
183
 
184
- const r = uglify ? await deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
184
+ const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
185
185
 
186
186
  resolve({
187
187
  user: parseToken(r.result.token),
@@ -195,7 +195,7 @@ const doCustomSignin = (builder, email, password) => new Promise(async (resolve,
195
195
  });
196
196
 
197
197
  const doCustomSignup = (builder, email, password, name, metadata) => new Promise(async (resolve, reject) => {
198
- const { projectUrl, serverE2E_PublicKey, accessKey, uglify } = builder;
198
+ const { projectUrl, serverE2E_PublicKey, accessKey, uglify, extraHeaders } = builder;
199
199
 
200
200
  try {
201
201
  await awaitStore();
@@ -206,13 +206,13 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
206
206
  },
207
207
  accessKey,
208
208
  serverE2E_PublicKey,
209
- uglify
209
+ uglify,
210
+ extraHeaders
210
211
  });
211
212
 
212
- const f = await (await fetch(_customSignup(projectUrl, uglify), reqBuilder)).json();
213
- if (f.simpleError) throw f;
213
+ const data = await buildFetchResult(await fetch(_customSignup(projectUrl, uglify), reqBuilder), uglify);
214
214
 
215
- const r = uglify ? await deserializeE2E(f.e2e, serverE2E_PublicKey, privateKey) : f;
215
+ const r = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
216
216
 
217
217
  resolve({
218
218
  user: parseToken(r.result.token),
@@ -239,7 +239,7 @@ export const clearCacheForSignout = (builder) => {
239
239
  export const doSignOut = async (builder) => {
240
240
  await awaitStore();
241
241
 
242
- const { projectUrl, serverE2E_PublicKey, accessKey, uglify } = builder,
242
+ const { projectUrl, serverE2E_PublicKey, accessKey, uglify, extraHeaders } = builder,
243
243
  { token, refreshToken: r_token } = CacheStore.AuthStore[projectUrl];
244
244
 
245
245
  clearCacheForSignout(builder);
@@ -253,11 +253,11 @@ export const doSignOut = async (builder) => {
253
253
  body: { token, r_token },
254
254
  accessKey,
255
255
  uglify,
256
- serverE2E_PublicKey
256
+ serverE2E_PublicKey,
257
+ extraHeaders
257
258
  });
258
259
 
259
- const r = await (await fetch(_signOut(projectUrl, uglify), reqBuilder)).json();
260
- if (r.simpleError) throw r;
260
+ await buildFetchResult(await fetch(_signOut(projectUrl, uglify), reqBuilder), uglify);
261
261
  } catch (e) {
262
262
  throw simplifyCaughtError(e).simpleError;
263
263
  }
@@ -265,7 +265,7 @@ export const doSignOut = async (builder) => {
265
265
  };
266
266
 
267
267
  const doGoogleSignin = (builder, token) => new Promise(async (resolve, reject) => {
268
- const { projectUrl, serverE2E_PublicKey, accessKey, uglify } = builder;
268
+ const { projectUrl, serverE2E_PublicKey, accessKey, uglify, extraHeaders } = builder;
269
269
 
270
270
  try {
271
271
  await awaitStore();
@@ -273,13 +273,13 @@ const doGoogleSignin = (builder, token) => new Promise(async (resolve, reject) =
273
273
  body: { token },
274
274
  accessKey,
275
275
  uglify,
276
- serverE2E_PublicKey
276
+ serverE2E_PublicKey,
277
+ extraHeaders
277
278
  });
278
279
 
279
- const r = await (await fetch(_googleSignin(projectUrl, uglify), reqBuilder)).json();
280
- if (r.simpleError) throw r;
280
+ const data = await buildFetchResult(await fetch(_googleSignin(projectUrl, uglify), reqBuilder), uglify);
281
281
 
282
- const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
282
+ const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
283
283
 
284
284
  resolve({
285
285
  user: parseToken(f.result.token),
@@ -2,7 +2,7 @@ import { io } from "socket.io-client";
2
2
  import EngineApi from "../../helpers/engine_api";
3
3
  import { DatabaseRecordsListener } from "../../helpers/listeners";
4
4
  import { deserializeE2E, listenReachableServer, niceTry, serializeE2E } from "../../helpers/peripherals";
5
- import { awaitStore, buildFetchInterface, getReachableServer } from "../../helpers/utils";
5
+ import { awaitStore, buildFetchInterface, buildFetchResult, getReachableServer } from "../../helpers/utils";
6
6
  import { CacheStore, Scoped } from "../../helpers/variables";
7
7
  import { addPendingWrites, generateRecordID, getRecord, insertRecord, listenQueryEntry, removePendingWrite, validateWriteValue } from "./accessor";
8
8
  import { validateCollectionName, validateFilter, validateFindConfig, validateFindObject, validateListenFindConfig } from "./validator";
@@ -130,7 +130,7 @@ const {
130
130
  } = EngineApi;
131
131
 
132
132
  const listenDocument = (callback, onError, builder, config) => {
133
- const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify, castBSON } = builder;
133
+ const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify, extraHeaders, castBSON } = builder;
134
134
  const { find, findOne, sort, direction, limit } = command;
135
135
  const { disableAuth } = config || {};
136
136
  const shouldCache = !disableCache;
@@ -185,14 +185,14 @@ const listenDocument = (callback, onError, builder, config) => {
185
185
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
186
186
  const pureConfig = stripRequestConfig(config);
187
187
  const authObj = {
188
- commands: {
188
+ commands: stripUndefined({
189
189
  config: pureConfig && serializeToBase64(pureConfig),
190
190
  path,
191
191
  find: serializeToBase64(findOne || find),
192
192
  sort,
193
193
  direction,
194
194
  limit
195
- },
195
+ }),
196
196
  dbName,
197
197
  dbUrl
198
198
  };
@@ -201,7 +201,8 @@ const listenDocument = (callback, onError, builder, config) => {
201
201
 
202
202
  socket = io(`${wsPrefix}://${baseUrl}`, {
203
203
  transports: ['websocket', 'polling', 'flashsocket'],
204
- auth: uglify ? { e2e: encPlate, _m_internal: true } : {
204
+ extraHeaders,
205
+ auth: uglify ? { e2e: encPlate.toString('base64'), _m_internal: true } : {
205
206
  accessKey,
206
207
  _body: authObj,
207
208
  ...mtoken ? { mtoken } : {},
@@ -257,7 +258,7 @@ const listenDocument = (callback, onError, builder, config) => {
257
258
  };
258
259
 
259
260
  const initOnDisconnectionTask = (builder, value, type) => {
260
- const { projectUrl, wsPrefix, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, command, uglify } = builder;
261
+ const { projectUrl, wsPrefix, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, extraHeaders, command, uglify } = builder;
261
262
  const { find } = command || {};
262
263
  const disableAuth = false;
263
264
 
@@ -277,20 +278,21 @@ const initOnDisconnectionTask = (builder, value, type) => {
277
278
 
278
279
  const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
279
280
  const authObj = {
280
- commands: {
281
+ commands: stripUndefined({
281
282
  path,
282
283
  find: find && serializeToBase64(find),
283
284
  value: value && serializeToBase64({ _: value }),
284
285
  scope: type
285
- },
286
+ }),
286
287
  dbName,
287
288
  dbUrl
288
289
  };
289
290
 
290
291
  socket = io(`${wsPrefix}://${baseUrl}`, {
291
292
  transports: ['websocket', 'polling', 'flashsocket'],
293
+ extraHeaders,
292
294
  auth: uglify ? {
293
- e2e: (await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey))[0],
295
+ e2e: (await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey))[0].toString('base64'),
294
296
  _m_internal: true
295
297
  } : {
296
298
  ...mtoken ? { mtoken } : {},
@@ -337,7 +339,7 @@ const initOnDisconnectionTask = (builder, value, type) => {
337
339
  };
338
340
 
339
341
  const countCollection = async (builder, config) => {
340
- const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, uglify, path, disableCache, command = {} } = builder;
342
+ const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, uglify, extraHeaders, path, disableCache, command = {} } = builder;
341
343
  const { find } = command;
342
344
  const { disableAuth } = config || {};
343
345
  const accessId = await generateRecordID({ ...builder, countDoc: true }, config);
@@ -357,8 +359,8 @@ const countCollection = async (builder, config) => {
357
359
 
358
360
  const finalize = (a, b) => {
359
361
  if (Validator.NUMBER(a)) {
360
- reject(b);
361
- } else resolve(a);
362
+ resolve(a);
363
+ } else reject(b);
362
364
  };
363
365
 
364
366
  try {
@@ -374,13 +376,13 @@ const countCollection = async (builder, config) => {
374
376
  accessKey,
375
377
  ...disableAuth ? {} : { authToken: Scoped.AuthJWTToken[projectUrl] },
376
378
  serverE2E_PublicKey,
377
- uglify
379
+ uglify,
380
+ extraHeaders
378
381
  });
379
382
 
380
- const r = await (await fetch(_documentCount(projectUrl, uglify), reqBuilder)).json();
381
- if (r.simpleError) throw r;
383
+ const data = await buildFetchResult(await fetch(_documentCount(projectUrl, uglify), reqBuilder), uglify);
382
384
 
383
- const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
385
+ const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
384
386
 
385
387
  if (!disableCache)
386
388
  setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, accessId], f.result);
@@ -420,13 +422,17 @@ const stripRequestConfig = (config) => {
420
422
  return requestConfig.length ? Object.fromEntries(requestConfig) : undefined;
421
423
  };
422
424
 
425
+ const stripUndefined = o => Object.fromEntries(
426
+ Object.entries(o).filter(v => v[1] !== undefined)
427
+ );
428
+
423
429
  const transformBSON = (d, castBSON) => {
424
430
  if (castBSON) return d && deserializeBSON(serializeToBase64({ _: d }), true)._;
425
431
  return cloneDeep(d);
426
432
  };
427
433
 
428
434
  const findObject = async (builder, config) => {
429
- const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, disableCache, uglify, command, castBSON } = builder;
435
+ const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, disableCache, uglify, extraHeaders, command, castBSON } = builder;
430
436
  const { find, findOne, sort, direction, limit, random } = command;
431
437
  const { retrieval = RETRIEVAL.DEFAULT, episode = 0, disableAuth, disableMinimizer } = config || {};
432
438
  const enableMinimizer = !disableMinimizer;
@@ -499,7 +505,7 @@ const findObject = async (builder, config) => {
499
505
 
500
506
  const [reqBuilder, [privateKey]] = await buildFetchInterface({
501
507
  body: {
502
- commands: {
508
+ commands: stripUndefined({
503
509
  config: pureConfig && serializeToBase64(pureConfig),
504
510
  path,
505
511
  find: serializeToBase64(findOne || find),
@@ -507,20 +513,20 @@ const findObject = async (builder, config) => {
507
513
  direction,
508
514
  limit,
509
515
  random
510
- },
516
+ }),
511
517
  dbName,
512
518
  dbUrl
513
519
  },
514
520
  accessKey,
515
521
  authToken: disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl],
516
522
  serverE2E_PublicKey,
517
- uglify
523
+ uglify,
524
+ extraHeaders
518
525
  });
519
526
 
520
- const r = await (await fetch((findOne ? _readDocument : _queryCollection)(projectUrl, uglify), reqBuilder)).json();
521
- if (r.simpleError) throw r;
527
+ const data = await buildFetchResult(await fetch((findOne ? _readDocument : _queryCollection)(projectUrl, uglify), reqBuilder), uglify);
522
528
 
523
- const result = deserializeBSON((uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r).result)._;
529
+ const result = deserializeBSON((uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data).result)._;
524
530
 
525
531
  if (shouldCache) insertRecord(builder, config, accessId, result);
526
532
  finalize({ liveResult: result || null });
@@ -589,7 +595,7 @@ const commitData = async (builder, value, type, config) => {
589
595
  )._;
590
596
  }
591
597
 
592
- const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, find, disableCache, uglify } = builder;
598
+ const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, find, disableCache, uglify, extraHeaders } = builder;
593
599
  const { disableAuth, delivery = DELIVERY.DEFAULT, stepping } = config || {};
594
600
  const writeId = `${Date.now() + ++Scoped.PendingIte}`;
595
601
  const isBatchWrite = type === 'batchWrite';
@@ -636,29 +642,29 @@ const commitData = async (builder, value, type, config) => {
636
642
 
637
643
  const [reqBuilder, [privateKey]] = await buildFetchInterface({
638
644
  body: {
639
- commands: {
645
+ commands: stripUndefined({
640
646
  value: value && serializeToBase64({ _: value }),
641
647
  ...isBatchWrite ? { stepping } : {
642
648
  path,
643
649
  scope: type,
644
650
  find: find && serializeToBase64(find)
645
651
  }
646
- },
652
+ }),
647
653
  dbName,
648
654
  dbUrl
649
655
  },
650
656
  accessKey,
651
657
  serverE2E_PublicKey,
652
658
  authToken: disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl],
653
- uglify
659
+ uglify,
660
+ extraHeaders
654
661
  });
655
662
 
656
- const r = await (await fetch((isBatchWrite ? _writeMapDocument : _writeDocument)(projectUrl, uglify), reqBuilder)).json();
657
- if (r.simpleError) throw r;
663
+ const data = await buildFetchResult(await fetch((isBatchWrite ? _writeMapDocument : _writeDocument)(projectUrl, uglify), reqBuilder), uglify);
658
664
 
659
- const f = uglify ? await deserializeE2E(r.e2e, serverE2E_PublicKey, privateKey) : r;
665
+ const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
660
666
 
661
- finalize({ ...f }, undefined, { removeCache: true });
667
+ finalize({ ...f.statusData }, undefined, { removeCache: true });
662
668
  } catch (e) {
663
669
  if (e?.simpleError) {
664
670
  console.error(`${type} error (${path}), ${e.simpleError?.message}`);
@@ -7,7 +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
+ import { serialize } from "entity-serializer";
11
11
 
12
12
  const buildFetchData = (data) => {
13
13
  const { ok, type, status, statusText, redirected, url, headers, size, base64 } = data;
@@ -33,7 +33,7 @@ const buildFetchData = (data) => {
33
33
  }
34
34
 
35
35
  export const mfetch = async (input = '', init, config) => {
36
- const { projectUrl, serverE2E_PublicKey, method, maxRetries = 7, disableCache, accessKey, uglify } = config;
36
+ const { projectUrl, serverE2E_PublicKey, method, maxRetries = 7, disableCache, accessKey, uglify, extraHeaders } = config;
37
37
  const { headers, body } = init || {};
38
38
 
39
39
  if (method !== undefined)
@@ -74,15 +74,15 @@ export const mfetch = async (input = '', init, config) => {
74
74
  ) throw `"body" must be any of string, buffer, object, File, Blob`;
75
75
  }
76
76
 
77
- const rawBody = stringify([(body instanceof File || body instanceof Blob) ? Buffer.from(await body.arrayBuffer()) : body]);
77
+ const rawBody = (body instanceof File || body instanceof Blob) ? Buffer.from(await body.arrayBuffer()) : body;
78
78
 
79
79
  const reqId = await niceHash(
80
- JSON.stringify([
80
+ serialize([
81
81
  rawHeader,
82
82
  rawBody,
83
83
  !!disableAuth,
84
84
  input
85
- ])
85
+ ]).toString('base64')
86
86
  );
87
87
 
88
88
  let retries = 0, hasFinalize;
@@ -153,11 +153,12 @@ export const mfetch = async (input = '', init, config) => {
153
153
  ...uglified ? { body: reqBuilder } : {},
154
154
  cache: 'no-cache',
155
155
  headers: {
156
+ ...extraHeaders,
156
157
  ...isBaseUrl ? {} : { 'content-type': 'application/json' },
157
158
  ...rawHeader,
158
159
  ...uglified ? {
159
160
  uglified,
160
- 'content-type': 'text/plain',
161
+ 'content-type': 'request/buffer',
161
162
  ...initType ? { 'init-content-type': initType } : {}
162
163
  } : {},
163
164
  ...(disableAuth || !mtoken || uglified || isBaseUrl) ? {} : { mtoken },
@@ -170,7 +171,7 @@ export const mfetch = async (input = '', init, config) => {
170
171
  if (!isBaseUrl && simple) throw { simpleError: JSON.parse(simple) };
171
172
 
172
173
  const base64 = uglified ?
173
- Buffer.from(await deserializeE2E(await f.text(), serverE2E_PublicKey, privateKey), 'base64') :
174
+ Buffer.from(await deserializeE2E(await f.arrayBuffer(), serverE2E_PublicKey, privateKey)).toString('base64') :
174
175
  Buffer.from(await f.arrayBuffer()).toString('base64');
175
176
 
176
177
  const resObj = {
@@ -12,6 +12,7 @@ export class MTStorage {
12
12
  }
13
13
 
14
14
  uploadFile = (file, destination, onComplete, onProgress, reqOptions) => {
15
+ const { awaitServer, createHash } = reqOptions || {};
15
16
  let hasCancelled = false,
16
17
  hasFinished = false,
17
18
  xhr;
@@ -44,7 +45,6 @@ export class MTStorage {
44
45
 
45
46
  const { projectUrl, accessKey, uglify } = this.builder;
46
47
  xhr = new XMLHttpRequest();
47
- const { awaitServer, createHash } = reqOptions || {};
48
48
 
49
49
  if (awaitServer) await awaitReachableServer(projectUrl);
50
50
  await awaitRefreshToken(projectUrl);
package/src/polyfill.js DELETED
@@ -1,3 +0,0 @@
1
- import { Buffer } from "buffer";
2
-
3
- if (!globalThis.Buffer) globalThis.Buffer = Buffer;