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 ADDED
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ android/
3
+ example/
4
+ ios/
package/.jshintrc ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "undef": true, // Warns on variables used before declaration
3
+ "esversion": 12,
4
+ "globals": {
5
+ "process": true,
6
+ "clearTimeout": true,
7
+ "setTimeout": true,
8
+ "console": true,
9
+ "Buffer": true,
10
+ "clearInterval": true,
11
+ "setInterval": true,
12
+ "AbortController": true,
13
+ "URLSearchParams": true,
14
+ "URL": true
15
+ }
16
+ }
package/README.md CHANGED
@@ -1 +1,75 @@
1
- # react-native-mosquito-transport
1
+ # react-native-mosquito-transport
2
+
3
+ React native javascript sdk for [mosquito-transport](https://github.com/brainbehindx/mosquito-transport).
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install react-native-mosquito-transport --save
9
+ ```
10
+
11
+ or using yarn
12
+
13
+ ```sh
14
+ yarn add react-native-mosquito-transport
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ```js
20
+ import RNMosquitoTransport from "react-native-mosquito-transport";
21
+
22
+ RNMosquitoTransport.initializeCache({
23
+ cachePassword: "****",
24
+ cacheProtocol: "sqlite",
25
+ });
26
+
27
+ const mclient = new RNMosquitoTransport({
28
+ projectUrl: "http://localhost:3444",
29
+ accessKey: "SERVER_ACCESS_KEY",
30
+ });
31
+ ```
32
+
33
+ ## Additional Documentations
34
+
35
+ - [RNMosquitoTransport Constructor](#RNMosquitoTransportConstructor)
36
+ - [dbName](#dbName)
37
+ - [dbUrl](#dbUrl)
38
+ - [projectUrl](#projectUrl)
39
+ - [disableCache](#disableCache)
40
+ - [accessKey](#accessKey)
41
+ - [maxRetries](#maxRetries)
42
+ - [enableE2E_Encryption](#enableE2E_Encryption)
43
+ - [serverE2E_PublicKey](#serverE2E_PublicKey)
44
+ - [extraHeaders](#extraHeaders)
45
+ - [castBSON](#castBSON)
46
+ - [RNMosquitoTransport Methods](#RNMosquitoTransportMethods)
47
+ - [initialCache](#initialCache)
48
+ - [getDatabase](#getDatabase)
49
+ - [collection](#collection)
50
+ - [auth](#auth)
51
+ - [storage](#storage)
52
+ - [fetchHttp](#fetchHttp)
53
+ - [listenReachableServer](#listenReachableServer)
54
+ - [getSocket](#getSocket)
55
+ - [batchWrite](#batchWrite)
56
+ - [TIMESTAMP](#TIMESTAMP)
57
+ - [AUTH_PROVIDER_ID](#AUTH_PROVIDER_ID)
58
+ - [DOCUMENT_EXTRACTION](#DOCUMENT_EXTRACTION)
59
+ - [GEO_JSON](#GEO_JSON)
60
+ - [FIND_GEO_JSON](#FIND_GEO_JSON)
61
+ - [DoNotEncrypt](#DoNotEncrypt)
62
+
63
+ ## RNMosquitoTransport Constructor
64
+
65
+ ### dbName
66
+
67
+
68
+ ### dbUrl
69
+
70
+
71
+ ### projectUrl
72
+
73
+ this is the base url of
74
+
75
+ ### disableCache
package/TODO CHANGED
@@ -3,4 +3,11 @@
3
3
  - reauthenticate
4
4
  - change `Object` in d.ts to [key: string]: any
5
5
  - add `getServerTimeOffset` method
6
- - change null to undefined in `value`
6
+ - change null to undefined in `value`
7
+ - `provide functionality to add extra header to MT instance (all outgoing request)`
8
+ - borrowToken
9
+ - `add sqlite`
10
+ - minimize extraction data
11
+ - change `collection().onDisconnect()` to `collection().socket().onDisconnect()` and `collection().socket().onConnect()`
12
+ - add `_foreign_doc` to d.ts
13
+ - tree shake dependencies
@@ -95,11 +95,18 @@ class MosquitodbUploadTask: NSObject, URLSessionDataDelegate {
95
95
 
96
96
  var request = URLRequest(url: URL(string: url)!)
97
97
  request.httpMethod = "POST"
98
+
99
+ if let extraHeaders = options["extraHeaders"] as? [[String: String]] {
100
+ for (key, value) in extraHeaders {
101
+ request.setValue(value, forHTTPHeaderField: key)
102
+ }
103
+ }
98
104
  request.setValue("application/json", forHTTPHeaderField: "Accept")
99
105
  request.setValue(authorization, forHTTPHeaderField: "Authorization")
100
106
  if options["authToken"] != nil {
101
107
  request.setValue(options["authToken"] as? String, forHTTPHeaderField: "Mosquito-Token")
102
108
  }
109
+ request.setValue(options["createHash"] as? String, forHTTPHeaderField: "hash-upload");
103
110
  request.setValue("buffer/upload", forHTTPHeaderField: "Content-Type")
104
111
  request.setValue(destination, forHTTPHeaderField: "Mosquito-Destination")
105
112
 
@@ -187,6 +194,12 @@ class MosquitodbDownloadTask: NSObject, URLSessionDownloadDelegate {
187
194
 
188
195
  var request = URLRequest(url: URL(string: url)!)
189
196
  request.httpMethod = "POST"
197
+
198
+ if let extraHeaders = options["extraHeaders"] as? [[String: String]] {
199
+ for (key, value) in extraHeaders {
200
+ request.setValue(value, forHTTPHeaderField: key)
201
+ }
202
+ }
190
203
  request.setValue(authorization, forHTTPHeaderField: "Authorization")
191
204
  if options["authToken"] != nil {
192
205
  request.setValue(options["authToken"] as? String, forHTTPHeaderField: "Mosquito-Token")
@@ -249,7 +262,7 @@ class MosquitodbDownloadTask: NSObject, URLSessionDownloadDelegate {
249
262
  "result": "{\"file\": \"\(dest)\"}"
250
263
  ]
251
264
  ])
252
- }else{
265
+ } else {
253
266
  let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
254
267
  let urlName = mainOptions["urlName"] as! String
255
268
  let destDir = documentsURL.appendingPathComponent("mosquito-transport")
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "react-native-mosquito-transport",
3
- "version": "0.0.19",
4
- "description": "React native javascript sdk for mosquito-transport (https://github.com/deflexable/mosquito-transport)",
3
+ "version": "0.0.21",
4
+ "description": "React native javascript sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
5
5
  "main": "src/index.js",
6
+ "type": "module",
6
7
  "scripts": {
7
8
  "test": "echo \"Error: no test specified\" && exit 1"
8
9
  },
9
10
  "repository": {
10
11
  "type": "git",
11
- "url": "git+https://github.com/deflexable/react-native-mosquito-transport.git"
12
+ "url": "git+https://github.com/brainbehindx/react-native-mosquito-transport.git"
12
13
  },
13
14
  "keywords": [
14
15
  "mosquito-transport",
@@ -22,23 +23,22 @@
22
23
  "author": "Anthony Onabanjo <deflexable@gmail.com> (https://github.com/deflexable)",
23
24
  "license": "MIT",
24
25
  "bugs": {
25
- "url": "https://github.com/deflexable/react-native-mosquito-transport/issues"
26
+ "url": "https://github.com/brainbehindx/react-native-mosquito-transport/issues"
26
27
  },
27
- "homepage": "https://github.com/deflexable/react-native-mosquito-transport#readme",
28
+ "homepage": "https://github.com/brainbehindx/react-native-mosquito-transport#readme",
28
29
  "dependencies": {
29
- "@types/crypto-js": "^4.2.1",
30
- "@types/lodash": "^4.14.194",
30
+ "@turf/turf": "^7.1.0",
31
+ "bson": "^6.8.0",
31
32
  "buffer": "^6.0.3",
32
33
  "crypto-js": "^4.2.0",
33
- "fast-json-stable-stringify": "^2.1.0",
34
- "guard-object": "^1.0.9",
35
- "json-buffer": "^3.0.1",
34
+ "entity-serializer": "^1.0.2",
35
+ "guard-object": "^1.1.3",
36
+ "lodash.clonedeep": "^4.5.0",
36
37
  "lodash.get": "^4.4.2",
37
- "lodash.isequal": "^4.5.0",
38
38
  "lodash.set": "^4.3.2",
39
39
  "lodash.unset": "^4.5.2",
40
40
  "react-native-get-random-values": "^1.9.0",
41
- "set-large-timeout": "^1.0.1",
41
+ "simplify-error": "^1.0.1",
42
42
  "socket.io-client": "^4.6.2",
43
43
  "subscription-listener": "^1.1.2",
44
44
  "tweetnacl": "^1.0.3"
@@ -47,4 +47,4 @@
47
47
  "react": "*",
48
48
  "react-native": "*"
49
49
  }
50
- }
50
+ }
@@ -0,0 +1,39 @@
1
+ import { encodeBinary } from './peripherals';
2
+
3
+ const EngineApiBase = (baseApi, ugly, path) => {
4
+ const url = new URL(baseApi);
5
+ if (ugly) {
6
+ url.pathname = `/e2e/${encodeBinary(path)}`;
7
+ } else url.pathname = path;
8
+ return url.href;
9
+ };
10
+
11
+ const apis = {
12
+ _readDocument: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_readDocument'),
13
+ _queryCollection: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_queryCollection'),
14
+ _documentCount: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_documentCount'),
15
+ _writeDocument: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_writeDocument'),
16
+ _writeMapDocument: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_writeMapDocument'),
17
+ _customSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_customSignin'),
18
+ _customSignup: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_customSignup'),
19
+ _googleSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_googleSignin'),
20
+ _appleSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_appleSignin'),
21
+ _facebookSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_facebookSignin'),
22
+ _twitterSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_twitterSignin'),
23
+ _githubSignin: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_githubSignin'),
24
+ _signOut: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_signOut'),
25
+ _refreshAuthToken: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_refreshAuthToken'),
26
+ _uploadFile: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_uploadFile'),
27
+ _deleteFile: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_deleteFile'),
28
+ _deleteFolder: (baseApi, ugly) => EngineApiBase(baseApi, ugly, '_deleteFolder'),
29
+ staticStorage: (baseApi) => `${baseApi}/storage`,
30
+ _areYouOk: (baseApi) => `${baseApi}/_areYouOk`,
31
+ // static path
32
+ _listenCollection: (ugly) => ugly ? encodeBinary(apis._listenCollection()) : '_listenCollection',
33
+ _listenDocument: (ugly) => ugly ? encodeBinary(apis._listenDocument()) : '_listenDocument',
34
+ _startDisconnectWriteTask: (ugly) => ugly ? encodeBinary(apis._startDisconnectWriteTask()) : '_startDisconnectWriteTask',
35
+ _cancelDisconnectWriteTask: (ugly) => ugly ? encodeBinary(apis._cancelDisconnectWriteTask()) : '_cancelDisconnectWriteTask',
36
+ _listenUserVerification: (ugly) => ugly ? encodeBinary(apis._listenUserVerification()) : '_listenUserVerification'
37
+ };
38
+
39
+ export default { ...apis };
@@ -3,129 +3,44 @@ 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
5
  import naclPkg from 'tweetnacl';
6
+ import getLodash from "lodash.get";
7
+ import { deserialize, serialize } from "entity-serializer";
6
8
 
7
9
  const { encrypt, decrypt } = aes_pkg;
8
10
  const { box, randomBytes } = naclPkg;
9
11
 
10
- export const simplifyError = (error, message) => ({
11
- simpleError: { error, message }
12
- });
13
-
14
- export const simplifyCaughtError = (e) => e?.simpleError ? e : simplifyError('unexpected_error', `${e}`);
15
-
16
- export const everyEntrie = (obj, callback) => {
17
- if (typeof obj !== 'object' || Array.isArray(obj)) return;
18
- oEntries(obj).forEach(e => {
19
- callback?.(e);
20
- });
21
- }
22
-
23
- export const flatEntries = (obj) => oEntries(obj);
24
-
25
- export const flatRawEntries = () => oEntries(obj, false);
26
-
27
- export const oEntries = (obj, includeObj = true) => {
28
- let o = [];
29
-
30
- Object.entries(obj).forEach(e => {
31
- o.push(e);
32
- if (e[1] && typeof e[1] === 'object' && !Array.isArray(e[1])) {
33
- o = [...o, ...oEntries(e[1])];
34
- }
35
- });
36
-
37
- return o.filter(v => includeObj || typeof v[1] !== 'object' || Array.isArray(v[1]));
38
- }
39
-
40
- export const IS_RAW_OBJECT = (e) => e && typeof e === 'object' && !Array.isArray(e) && !(e instanceof Date);
41
-
42
- export const IS_WHOLE_NUMBER = (v) => typeof v === 'number' && !`${v}`.includes('.');
43
-
44
- export const IS_DECIMAL_NUMBER = (v) => typeof v === 'number' && `${v}`.includes('.');
45
-
46
- export const queryEntries = (obj, lastPath = '', exceptions = [], seperator = '.') => {
47
- let o = [];
48
- const isArraySeperator = Array.isArray(lastPath);
49
-
50
- Object.entries(obj).forEach(([key, value]) => {
51
- if (IS_RAW_OBJECT(value) && !exceptions.includes(key)) {
52
- o = [
53
- ...o,
54
- ...queryEntries(
55
- value,
56
- isArraySeperator ? [...lastPath, key] : `${lastPath}${key}${seperator}`,
57
- exceptions,
58
- seperator
59
- )
60
- ];
61
- } else o.push(isArraySeperator ? [[...lastPath, key], value] : [`${lastPath}${key}`, value]);
62
- });
63
-
64
- return o;
65
- }
66
-
67
- export const objToUniqueString = (obj) => {
68
- const keys = [],
69
- values = [];
70
-
71
- if (Array.isArray(obj)) {
72
- obj.forEach(e => {
73
- if (IS_RAW_OBJECT(e)) {
74
- queryEntries(e).map(([k, v]) => {
75
- keys.push(k);
76
- values.push(v);
77
- });
78
- } else keys.push(Array.isArray(e) ? JSON.stringify(e) : `${e}`);
79
- });
80
- } else if (!IS_RAW_OBJECT(obj))
81
- return `${obj}`;
82
- else
83
- queryEntries(obj).map(([k, v]) => {
84
- keys.push(k);
85
- values.push(v);
86
- });
87
-
88
- return [
89
- ...keys.sort(),
90
- ...values.map(v => `${Array.isArray(v) ? JSON.stringify(v) : v}`).sort()
91
- ].join(',');
92
- }
93
-
94
- export const cloneInstance = (v) => {
95
- if (v && typeof v === 'object') {
96
- return Array.isArray(v) ? [...v] : { ...v };
97
- }
98
- return v;
99
- }
100
-
101
12
  export const listenReachableServer = (callback, projectUrl) => {
102
13
  let lastValue;
103
14
  return ServerReachableListener.listenTo(projectUrl, t => {
104
15
  if (typeof t === 'boolean' && t !== lastValue) callback?.(t);
105
16
  }, true);
106
- }
17
+ };
107
18
 
108
19
  export const prefixStoragePath = (path, prefix = 'file:///') => {
109
- if (!path) return path;
20
+ let cleanedPath = path.replace(/^[^/]+:\/{1,3}/, '');
110
21
 
111
- if (!path.startsWith('/') && !path.includes(':')) return prefix + path;
22
+ // Continuously remove any remaining protocol patterns until none are left
23
+ while (/^[^/]+:\/{1,3}/.test(cleanedPath)) {
24
+ cleanedPath = cleanedPath.replace(/^[^/]+:\/{1,3}/, '');
25
+ }
112
26
 
113
- return prefix + path.split('/').filter((v, i) => i && v).join('/');
114
- }
27
+ // Remove any leading slashes after protocol removal
28
+ cleanedPath = cleanedPath.replace(/^\/+/, '');
115
29
 
116
- export const getUrlExtension = (url) => {
117
- const r = url.split(/[#?]/)[0].split(".").pop().trim();
118
- return r === url ? '' : r;
119
- }
30
+ return `${prefix}${cleanedPath}`;
31
+ };
120
32
 
121
33
  export const niceTry = (promise) => new Promise(async resolve => {
122
-
123
34
  try {
124
35
  const r = await promise();
125
36
  resolve(r);
126
37
  } catch (e) { resolve(); }
127
38
  });
128
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
+
129
44
  export const shuffleArray = (n) => {
130
45
  const array = [...n];
131
46
  let currentIndex = array.length, randomIndex;
@@ -143,56 +58,87 @@ export const shuffleArray = (n) => {
143
58
  }
144
59
 
145
60
  export function sortArrayByObjectKey(arr = [], key) {
146
- return arr.slice(0).sort(function (a, b) {
61
+ return arr.sort(function (a, b) {
147
62
  const left = getLodash(a, key),
148
63
  right = getLodash(b, key);
149
64
 
150
65
  return (left > right) ? 1 : (left < right) ? -1 : 0;
151
66
  });
152
- }
67
+ };
68
+
69
+ export async function niceHash(str) {
70
+ try {
71
+ // Convert the string to a Uint8Array
72
+ const encoder = new TextEncoder();
73
+ const data = encoder.encode(str);
74
+
75
+ // Use the Web Crypto API to compute the hash
76
+ const hashBuffer = await crypto.subtle.digest('SHA-256', data);
77
+
78
+ // Convert the ArrayBuffer to a hex string for readability
79
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
80
+ const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
81
+
82
+ // Convert to base64
83
+ return Buffer.from(hashHex, 'hex').toString('base64');
84
+ } catch (_) {
85
+ return str;
86
+ }
87
+ };
88
+
89
+ export const sameInstance = (var1, var2) => {
90
+ try {
91
+ return var1.constructor === var2.constructor &&
92
+ Object.getPrototypeOf(var1) === Object.getPrototypeOf(var2)
93
+ } catch (_) {
94
+ return false;
95
+ }
96
+ };
153
97
 
154
98
  export const encryptString = (txt, password, iv) => {
155
99
  return encrypt(txt, `${password || ''}${iv || ''}`).toString();
156
- }
100
+ };
157
101
 
158
102
  export const decryptString = (txt, password, iv) => {
159
103
  return decrypt(txt, `${password || ''}${iv || ''}`).toString(Utf8Encoder);
160
- }
104
+ };
161
105
 
162
- export const serializeE2E = (data, auth_token, serverPublicKey) => {
106
+ export const serializeE2E = async (data, auth_token, serverPublicKey) => {
163
107
  const pair = box.keyPair(),
164
- nonce = randomBytes(box.nonceLength),
165
- pubBase64 = Buffer.from(pair.publicKey).toString('base64'),
166
- nonceBase64 = Buffer.from(nonce).toString('base64');
108
+ nonce = randomBytes(box.nonceLength);
167
109
 
168
110
  return [
169
- `${pubBase64}.${nonceBase64}.${Buffer.from(
170
- box(
171
- Buffer.from(JSON.stringify([
172
- data,
173
- auth_token
174
- ]), 'utf8'),
175
- nonce,
176
- Buffer.from(serverPublicKey, 'base64'),
177
- pair.secretKey
111
+ serialize([
112
+ pair.publicKey,
113
+ nonce,
114
+ Buffer.from(
115
+ box(
116
+ serialize([
117
+ data,
118
+ auth_token
119
+ ]),
120
+ nonce,
121
+ serverPublicKey,
122
+ pair.secretKey
123
+ )
178
124
  )
179
- ).toString('base64')}`,
125
+ ]),
180
126
  [pair.secretKey, pair.publicKey]
181
127
  ];
182
- }
128
+ };
183
129
 
184
- export const deserializeE2E = (data, serverPublicKey, clientPrivateKey) => {
185
- const [binaryNonce, binaryData] = data.split('.'),
130
+ export const deserializeE2E = async (data, serverPublicKey, clientPrivateKey) => {
131
+ const [binaryNonce, binaryData] = deserialize(data),
186
132
  baseArray = box.open(
187
- Buffer.from(binaryData, 'base64'),
188
- Buffer.from(binaryNonce, 'base64'),
189
- Buffer.from(serverPublicKey, 'base64'),
133
+ binaryData,
134
+ binaryNonce,
135
+ serverPublicKey,
190
136
  clientPrivateKey
191
137
  );
192
138
 
193
139
  if (!baseArray) throw 'Decrypting e2e message failed';
194
- return JSON.parse(Buffer.from(baseArray).toString('utf8'))[0];
195
- }
140
+ return deserialize(baseArray);
141
+ };
196
142
 
197
143
  export const encodeBinary = (s) => Buffer.from(s, 'utf8').toString('base64');
198
144
  export const decodeBinary = (s) => Buffer.from(s, 'base64').toString('utf8');
@@ -3,14 +3,28 @@ import { ServerReachableListener, StoreReadyListener } from "./listeners";
3
3
  import { CACHE_PROTOCOL, CACHE_STORAGE_PATH, DEFAULT_CACHE_PASSWORD, LOCAL_STORAGE_PATH } from "./values";
4
4
  import { CacheStore, Scoped } from "./variables";
5
5
  import { decryptString, encryptString, niceTry, serializeE2E } from "./peripherals";
6
+ import { deserializeBSON, serializeToBase64 } from "../products/database/bson";
7
+ import { trySendPendingWrite } from "../products/database";
8
+ import { deserialize } from "entity-serializer";
6
9
 
7
- export const updateCacheStore = () => {
8
- const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE, io } = Scoped.ReleaseCacheData;
10
+ export const updateCacheStore = (timer = 300) => {
11
+ const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE, io, promoteCache } = Scoped.ReleaseCacheData;
9
12
 
10
13
  clearTimeout(Scoped.cacheStorageReducer);
11
14
  Scoped.cacheStorageReducer = setTimeout(() => {
15
+ const { AuthStore, EmulatedAuth, PendingAuthPurge, DatabaseStore, PendingWrites, ...restStore } = CacheStore;
16
+
12
17
  const txt = encryptString(
13
- JSON.stringify({ ...CacheStore }),
18
+ JSON.stringify({
19
+ AuthStore,
20
+ EmulatedAuth,
21
+ PendingAuthPurge,
22
+ ...promoteCache ? {
23
+ DatabaseStore: serializeToBase64(DatabaseStore),
24
+ PendingWrites: serializeToBase64(PendingWrites)
25
+ } : {},
26
+ ...promoteCache ? restStore : {}
27
+ }),
14
28
  cachePassword,
15
29
  cachePassword
16
30
  );
@@ -19,12 +33,14 @@ export const updateCacheStore = () => {
19
33
  io.output(txt);
20
34
  } else if (cacheProtocol === CACHE_PROTOCOL.ASYNC_STORAGE) {
21
35
  AsyncStorage.setItem(CACHE_STORAGE_PATH, txt);
36
+ } else if (cacheProtocol === CACHE_PROTOCOL.SQLITE) {
37
+ console.error('unsupported protocol "sqlite"');
22
38
  } else {
23
39
  const fs = require('react-native-fs');
24
40
  fs.writeFile(LOCAL_STORAGE_PATH(), txt, 'utf8');
25
41
  }
26
- }, 500);
27
- }
42
+ }, timer);
43
+ };
28
44
 
29
45
  export const releaseCacheStore = async (builder) => {
30
46
  const { cachePassword = DEFAULT_CACHE_PASSWORD, cacheProtocol = CACHE_PROTOCOL.ASYNC_STORAGE, io } = builder;
@@ -41,17 +57,26 @@ export const releaseCacheStore = async (builder) => {
41
57
  }
42
58
 
43
59
  const j = JSON.parse(decryptString(txt || '', cachePassword, cachePassword) || '{}');
60
+ const projectList = new Set([]);
44
61
 
45
62
  Object.entries(j).forEach(([k, v]) => {
46
- CacheStore[k] = v;
63
+ if (['DatabaseStore', 'PendingWrites'].includes(k)) {
64
+ CacheStore[k] = deserializeBSON(v);
65
+ } else CacheStore[k] = v;
66
+ projectList.add(k);
47
67
  });
48
68
  Object.entries(CacheStore.AuthStore).forEach(([key, value]) => {
49
69
  Scoped.AuthJWTToken[key] = value?.token;
50
70
  });
71
+ projectList.forEach(projectUrl => {
72
+ if (Scoped.IS_CONNECTED[projectUrl])
73
+ trySendPendingWrite(projectUrl);
74
+ });
51
75
  Scoped.IsStoreReady = true;
52
76
  StoreReadyListener.dispatch('_', 'ready');
53
- // TODO: commit pending write
54
- }
77
+ };
78
+
79
+ export const getPrefferTime = () => Date.now() + (Scoped.serverTimeOffset || 0);
55
80
 
56
81
  export const awaitStore = () => new Promise(resolve => {
57
82
  if (Scoped.IsStoreReady) {
@@ -92,26 +117,30 @@ export const getReachableServer = (projectUrl) => new Promise(resolve => {
92
117
  }, true);
93
118
  });
94
119
 
95
- export const buildFetchInterface = ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey }) => {
120
+ export const buildFetchInterface = async ({ body, accessKey, authToken, method, uglify, serverE2E_PublicKey, extraHeaders }) => {
96
121
  if (!uglify) body = JSON.stringify({ ...body });
97
- const [plate, keyPair] = uglify ? serializeE2E(body, authToken, serverE2E_PublicKey) : [undefined, []];
122
+ const [plate, keyPair] = uglify ? await serializeE2E(body, authToken, serverE2E_PublicKey) : [undefined, []];
98
123
 
99
124
  return [{
100
125
  body: uglify ? plate : body,
101
126
  cache: 'no-cache',
102
127
  headers: {
103
- 'Content-type': uglify ? 'text/plain' : 'application/json',
128
+ 'Content-type': uglify ? 'request/buffer' : 'application/json',
104
129
  'Authorization': accessKey,
105
- ...((authToken && !uglify) ? { 'Mosquito-Token': authToken } : {})
130
+ ...(authToken && !uglify) ? { 'Mosquito-Token': authToken } : {},
131
+ ...extraHeaders
106
132
  },
107
133
  method: method || 'POST'
108
134
  }, keyPair];
109
135
  };
110
136
 
111
- export const simplifyError = (error, message) => ({
112
- simpleError: { error, message }
113
- });
114
-
115
- export const validateRequestMethod = (method) => {
116
- // TODO:
117
- }
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;
146
+ };