react-native-mosquito-transport 0.0.32 → 0.0.33

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": "react-native-mosquito-transport",
3
- "version": "0.0.32",
3
+ "version": "0.0.33",
4
4
  "description": "React native javascript sdk for mosquito-transport (https://github.com/brainbehindx/mosquito-transport)",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -42,10 +42,6 @@
42
42
  "react-native": "*",
43
43
  "react-native-fs": "*",
44
44
  "react-native-sha256": "*",
45
- "react-native-sqlite-storage": "*",
46
45
  "react-native-get-random-values": "*"
47
- },
48
- "devDependencies": {
49
- "@types/react-native-sqlite-storage": "^6.0.5"
50
46
  }
51
47
  }
@@ -1,27 +1,82 @@
1
+ import { Scoped } from "./variables";
1
2
  import { Platform } from "react-native";
2
- import { Buffer } from 'buffer';
3
- import { deserialize, serialize } from 'entity-serializer';
4
- import { writeFile, mkdir, MainBundlePath, readFile, unlink, DocumentDirectoryPath } from "react-native-fs";
3
+ import { writeFile, mkdir, MainBundlePath, readFile, unlink, DocumentDirectoryPath, readdir } from "react-native-fs";
5
4
 
6
- const MAX_INLINE_BLOB = 1024;
5
+ const PARENT_FOLDER = `${Platform.OS === 'android' ? DocumentDirectoryPath.split('/').slice(0, -1).join('/') : MainBundlePath}/mosquito_base`;
7
6
 
8
- const DIR_PATH = `${Platform.OS === 'android' ? DocumentDirectoryPath.split('/').slice(0, -1).join('/').concat('/databases') : MainBundlePath}/MOSQUITO`;
9
- const resolvePath = (path = '') => `${DIR_PATH}${path.startsWith('/') ? path : '/' + path}`;
7
+ /**
8
+ * this method linearize read/write for individual access_id on the file system ensuring consistency across concurrent operations
9
+ *
10
+ * @param {any} builder
11
+ * @param {string} access_id
12
+ * @param {string} node
13
+ * @returns {(task: (system: { set: (table: string, primary_key: string, value: {}) => Promise<void>, delete: (table: string, primary_key: string) => Promise<void>, find: (table: string, primary_key: string, extractions: string[]) => Promise<{}>, list: (table: string, extractions: string[]) => Promise<[string, {}][]> }) => any) => Promise<any>}
14
+ */
15
+ export const useFS = (builder, access_id, node) => async (task) => {
16
+ const { projectUrl, dbUrl, dbName } = builder;
17
+ const nodeId = typeof builder === 'string' ? `${builder}_${access_id}` : `${projectUrl}_${dbUrl}_${dbName}_${access_id}`;
10
18
 
11
- const DIR_CREATION_PROMISE = mkdir(DIR_PATH).catch(() => null);
19
+ const thatProcess = Scoped.linearSqliteProcess[node][nodeId];
12
20
 
13
- const fsWrite = async (path, data) => {
14
- await DIR_CREATION_PROMISE;
15
- return writeFile(resolvePath(path), data instanceof Buffer ? data.toString('base64') : data, 'base64');
16
- }
21
+ const thisPromise = new Promise(async (resolve, reject) => {
22
+ try {
23
+ if (thatProcess !== undefined) await thatProcess;
24
+ } catch (_) { }
25
+ try {
26
+ resolve(await task(getSystem(builder)));
27
+ } catch (error) {
28
+ console.error('useFS err:', error, ' builder:', builder);
29
+ reject(error);
30
+ } finally {
31
+ if (Scoped.linearSqliteProcess[node][nodeId] === thisPromise)
32
+ delete Scoped.linearSqliteProcess[node][nodeId];
33
+ }
34
+ });
17
35
 
18
- const fsRead = async (path) => {
19
- await DIR_CREATION_PROMISE;
20
- return Buffer.from(await readFile(resolvePath(path), 'base64'), 'base64');
21
- }
36
+ Scoped.linearSqliteProcess[node][nodeId] = thisPromise;
37
+ return (await thisPromise);
38
+ };
39
+
40
+ export const getSystem = (builder) => {
41
+ const { projectUrl, dbUrl, dbName } = builder;
42
+
43
+ const DIR_PATH = joinPath(PARENT_FOLDER, purifyFilepath(typeof builder === 'string' ? builder : `${projectUrl}_${dbUrl}_${dbName}`));
44
+ const conjoin = (...args) => joinPath(DIR_PATH, ...args);
22
45
 
23
- const purifyFilename = (filename) => {
24
- if (!filename || typeof filename !== 'string') return 'unnamed';
46
+ return {
47
+ set: async (table, primary_key, value) => {
48
+ const path = conjoin(table, primary_key);
49
+ await mkdir(path).catch(() => null);
50
+ await Promise.all(Object.entries(value).map(([k, v]) =>
51
+ writeFile(joinPath(path, k), JSON.stringify(v), 'utf8')
52
+ ));
53
+ },
54
+ delete: (table, primary_key) => unlink(conjoin(table, primary_key)),
55
+ find: async (table, primary_key, extractions) => {
56
+ const path = conjoin(table, primary_key);
57
+
58
+ const value_map = await Promise.all(extractions.map(async node =>
59
+ [node, JSON.parse(await readFile(joinPath(path, node)), 'utf8')]
60
+ ));
61
+ return Object.fromEntries(value_map);
62
+ },
63
+ list: async (table, extractions) => {
64
+ const names = await readdir(conjoin(table));
65
+ const list_data = await Promise.all(names.map(async primary_key => {
66
+ const obj = await system.find(table, primary_key, extractions)
67
+ .catch(() => null);
68
+ if (!obj) return;
69
+ return [primary_key, obj];
70
+ }));
71
+
72
+ return list_data.filter(v => v);
73
+ }
74
+ };
75
+ };
76
+
77
+ function purifyFilepath(filename) {
78
+ if (!filename || typeof filename !== 'string')
79
+ throw `invalid filename:${filename}`;
25
80
 
26
81
  // Remove invalid characters for both iOS and Android
27
82
  return filename
@@ -29,27 +84,21 @@ const purifyFilename = (filename) => {
29
84
  .trim(); // Remove leading/trailing whitespace
30
85
  }
31
86
 
32
- export const getStoreID = (db_filename, table, primary_key) => purifyFilename(`${table}_${primary_key}_${db_filename}.blob`);
33
-
34
- export const deleteBigData = (store_id) => unlink(resolvePath(store_id));
35
-
36
- export const handleBigData = async (store_id, data) => {
37
- const bufData = serialize(data);
38
- if (bufData.byteLength <= MAX_INLINE_BLOB) {
39
- return serialize([bufData]).toString('base64');
40
- }
41
- await fsWrite(store_id, bufData);
42
- return serialize([undefined, store_id]).toString('base64');
43
- };
87
+ function joinPath(...args) {
88
+ return args.map((v, i) => {
89
+ if (i && v.startsWith('/'))
90
+ v = v.slice(1);
91
+ if (v.endsWith('/'))
92
+ v = v.slice(0, -1);
93
+ return v;
94
+ }).join('/');
95
+ }
44
96
 
45
- export const parseBigData = async (result) => {
46
- const [inline, store_id] = deserialize(Buffer.from(result, 'base64'));
47
- if (store_id) {
48
- try {
49
- return deserialize(await fsRead(store_id));
50
- } catch (error) {
51
- throw `Referenced local file is either corrupted or deleted, Error: ${error}`;
52
- }
53
- }
54
- return deserialize(inline);
97
+ export const FS_PATH = {
98
+ FILE_NAME: 'MOSQUITO_TRANSPORT',
99
+ TABLE_NAME: 'MT_MAIN',
100
+ LIMITER_RESULT: path => `${purifyFilepath(encodeURIComponent(path))}_LR`,
101
+ LIMITER_DATA: path => `${purifyFilepath(encodeURIComponent(path))}_LD`,
102
+ DB_COUNT_QUERY: path => `${purifyFilepath(encodeURIComponent(path))}_QC`,
103
+ FETCH_RESOURCES: projectUrl => `FR_${purifyFilepath(encodeURIComponent(projectUrl))}`
55
104
  };
@@ -66,7 +66,7 @@ export function sortArrayByObjectKey(arr = [], key) {
66
66
  };
67
67
 
68
68
  export async function niceHash(str = '') {
69
- const hash = await sha256(str);
69
+ const hash = Buffer.from(await sha256(str), 'hex').toString('base64');
70
70
  if (hash.length > str.length) return encodeBinary(str);
71
71
  return hash;
72
72
  };
@@ -1,11 +1,16 @@
1
- import { openDB, SQLITE_COMMANDS, SQLITE_PATH } from "./sqlite_manager";
2
1
  import { Validator } from "guard-object";
3
2
  import { incrementDatabaseSizeCore } from "../products/database/counter";
4
3
  import { incrementFetcherSizeCore } from "../products/http_callable/counter";
5
4
  import unsetLodash from 'lodash/unset';
6
- import { deleteBigData, getStoreID } from "./fs_manager";
5
+ import { FS_PATH, getSystem } from "./fs_manager";
7
6
 
8
- const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY, FETCH_RESOURCES } = SQLITE_PATH;
7
+ const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY, FETCH_RESOURCES } = FS_PATH;
8
+
9
+ const inlineFsData = (arr, access_id_node = 'access_id') =>
10
+ arr.map(([access_id, obj]) => {
11
+ obj[access_id_node] = access_id;
12
+ return obj;
13
+ });
9
14
 
10
15
  export const purgeRedundantRecords = async (data, builder) => {
11
16
  const { io, maxLocalDatabaseSize = 10485760, maxLocalFetchHttpSize = 10485760 } = builder;
@@ -145,28 +150,24 @@ export const purgeRedundantRecords = async (data, builder) => {
145
150
  instances.map(async obj => {
146
151
  const { builder, isCounter, path } = obj;
147
152
 
148
- const sqlite = await openDB(builder);
149
-
150
153
  try {
151
154
  if (isCounter) {
152
- const data = await sqlite.executeSql(`SELECT * FROM ${DB_COUNT_QUERY(path)}`).then(r =>
153
- r[0].rows.raw()
155
+ const data = inlineFsData(
156
+ await getSystem(builder).list(DB_COUNT_QUERY(path), ['size', 'touched'])
154
157
  );
155
158
  return data.map(v => [v, obj]);
156
159
  }
157
160
 
158
161
  const [instanceData, resultData] = await Promise.all([
159
- sqlite.executeSql(`SELECT access_id, touched, size FROM ${LIMITER_DATA(path)}`),
160
- sqlite.executeSql(`SELECT access_id_limiter, touched, size FROM ${LIMITER_RESULT(path)}`)
162
+ getSystem(builder).list(LIMITER_DATA(path), ['touched', 'size']).catch(() => []),
163
+ getSystem(builder).list(LIMITER_RESULT(path), ['touched', 'size']).catch(() => [])
161
164
  ]).then(r =>
162
- r.map(v => v[0].rows.raw())
165
+ r.map((v, i) => inlineFsData(v, i ? 'access_id_limiter' : 'access_id'))
163
166
  );
164
167
  return [...instanceData, ...resultData].map(v => [v, obj]);
165
168
  } catch (error) {
166
169
  console.error('redundantDbRanking err:', error);
167
170
  return [];
168
- } finally {
169
- sqlite.close();
170
171
  }
171
172
  })
172
173
  ).then(r =>
@@ -186,22 +187,15 @@ export const purgeRedundantRecords = async (data, builder) => {
186
187
 
187
188
  console.warn(`purging ${cuts} of ${redundantDbRanking.length} db entities`);
188
189
  await Promise.all(redundantDbRanking.slice(0, cuts).map(async ([v, { builder, isCounter, path }]) => {
189
- let db_filename;
190
- const sqlite = await openDB(builder, n => db_filename = n);
191
190
  try {
192
191
  const table = (isCounter ? DB_COUNT_QUERY : 'access_id_limiter' in v ? LIMITER_RESULT : LIMITER_DATA)(path);
193
192
  const id_field = 'access_id_limiter' in v ? 'access_id_limiter' : 'access_id';
194
193
  const primary_key = v[id_field];
195
-
196
- await Promise.all([
197
- sqlite.executeSql(SQLITE_COMMANDS.DELETE_ROW(table, `${id_field} = ?`), [primary_key]),
198
- deleteBigData(getStoreID(db_filename, table, primary_key)).catch(() => null)
199
- ]);
194
+ await getSystem(builder).delete(table, primary_key);
200
195
  if (!isCounter) incrementDatabaseSizeCore(data.DatabaseStats, builder, path, -v.size);
201
196
  } catch (error) {
202
197
  console.log('db redundantClearing err:', error);
203
198
  }
204
- sqlite.close();
205
199
  }));
206
200
  console.log('database purging complete');
207
201
  } catch (error) {
@@ -212,13 +206,12 @@ export const purgeRedundantRecords = async (data, builder) => {
212
206
  try {
213
207
  if (!Validator.POSITIVE_NUMBER(_fetcher_size) || !maxLocalFetchHttpSize || _fetcher_size < maxLocalFetchHttpSize) return;
214
208
 
215
- const redundantFetchRanking = await Promise.all(Object.entries(fetchers).map(async ([projectUrl]) => {
216
- const sqlite = await openDB(FETCH_RESOURCES(projectUrl));
217
- const data = await sqlite.executeSql('SELECT access_id, touched, size FROM main').then(r =>
218
- r[0].rows.raw()
219
- );
220
- return data.map(v => [v, projectUrl]);
221
- })).then(r =>
209
+ const redundantFetchRanking = await Promise.all(
210
+ Object.entries(fetchers).map(async ([projectUrl]) => {
211
+ const data = inlineFsData(await getSystem(FETCH_RESOURCES(projectUrl)).list('main', ['touched', 'size']));
212
+ return data.map(v => [v, projectUrl]);
213
+ })
214
+ ).then(r =>
222
215
  r.flat().sort(([a], [b]) =>
223
216
  a.touched - b.touched
224
217
  )
@@ -236,19 +229,12 @@ export const purgeRedundantRecords = async (data, builder) => {
236
229
 
237
230
  console.warn(`purging ${cuts} of ${redundantFetchRanking.length} fetcher entities`);
238
231
  await Promise.all(redundantFetchRanking.slice(0, cuts).map(async ([v, projectUrl]) => {
239
- let db_filename;
240
- const sqlite = await openDB(FETCH_RESOURCES(projectUrl), n => db_filename = n);
241
-
242
232
  try {
243
- await Promise.all([
244
- sqlite.executeSql(SQLITE_COMMANDS.DELETE_ROW('main', 'access_id = ?'), [v.access_id]),
245
- deleteBigData(getStoreID(db_filename, 'main', v.access_id)).catch(() => null)
246
- ]);
233
+ await getSystem(FETCH_RESOURCES(projectUrl)).delete('main', v.access_id);
247
234
  incrementFetcherSizeCore(data.DatabaseStats, projectUrl, -v.size);
248
235
  } catch (error) {
249
- console.log('fetcher redundantClearing err:', error);
236
+ console.log('fetcher redundantClearing err:', error, ' obj:', v, ' projectUrl:', projectUrl);
250
237
  }
251
- sqlite.close();
252
238
  }));
253
239
  console.log('fetcher purging complete');
254
240
  } catch (error) {
@@ -268,8 +254,4 @@ const breakDbMap = (obj, callback) =>
268
254
  });
269
255
  });
270
256
  });
271
- });
272
-
273
- export const purgeInstance = (projectUrl) => {
274
- // TODO: purge when signed-out
275
- }
257
+ });
@@ -4,66 +4,54 @@ import { serializeE2E } from "./peripherals";
4
4
  import { deserializeBSON, serializeToBase64 } from "../products/database/bson";
5
5
  import { trySendPendingWrite } from "../products/database";
6
6
  import { deserialize } from "entity-serializer";
7
- import { openDB, SQLITE_COMMANDS, SQLITE_PATH } from "./sqlite_manager";
8
7
  import { purgeRedundantRecords } from "./purger";
9
- import { getStoreID, handleBigData, parseBigData } from "./fs_manager";
8
+ import { FS_PATH, getSystem } from "./fs_manager";
10
9
 
11
- const { FILE_NAME, TABLE_NAME } = SQLITE_PATH;
10
+ const { FILE_NAME, TABLE_NAME } = FS_PATH;
12
11
 
13
12
  const CacheKeys = Object.keys(CacheStore);
14
13
 
15
- export const updateCacheStore = (timer = 300, node) => {
14
+ export const updateCacheStore = async (node) => {
16
15
  const { io, promoteCache } = Scoped.ReleaseCacheData;
17
16
 
18
- const doUpdate = async () => {
19
- const {
17
+ const {
18
+ AuthStore,
19
+ EmulatedAuth,
20
+ PendingAuthPurge,
21
+ DatabaseStore,
22
+ PendingWrites,
23
+ ...restStore
24
+ } = CacheStore;
25
+
26
+ if (io) {
27
+ const txt = JSON.stringify({
20
28
  AuthStore,
21
29
  EmulatedAuth,
22
30
  PendingAuthPurge,
23
- DatabaseStore,
24
- PendingWrites,
25
- ...restStore
26
- } = CacheStore;
27
-
28
- if (io) {
29
- const txt = JSON.stringify({
30
- AuthStore,
31
- EmulatedAuth,
32
- PendingAuthPurge,
33
- ...promoteCache ? {
34
- DatabaseStore: serializeToBase64(DatabaseStore),
35
- PendingWrites: serializeToBase64(PendingWrites)
36
- } : {},
37
- ...promoteCache ? restStore : {}
38
- });
39
-
40
- io.output(txt, node);
41
- } else {
42
- // use sqlite
43
- const exclusion = ['DatabaseStore', 'DatabaseCountResult', 'FetchedStore'];
44
- const updationKey = (node ? Array.isArray(node) ? node : [node] : CacheKeys).filter(v => !exclusion.includes(v));
45
-
46
- if (!updationKey.length) return;
47
- const sqlite = await openDB(FILE_NAME);
48
- await Promise.all(
49
- updationKey
50
- .map(v => [v, v === 'PendingWrites' ? serializeToBase64(CacheStore[v]) : CacheStore[v]])
51
- .map(async ([ref, value]) => {
52
- const blobData = await handleBigData(getStoreID(FILE_NAME, TABLE_NAME, ref), value);
53
- return sqlite.executeSql(SQLITE_COMMANDS.MERGE(TABLE_NAME, ['ref', 'value']), [ref, blobData]);
54
- })
55
- ).catch(err => {
56
- console.error('updateCacheStore err:', err);
57
- }).finally(() => {
58
- sqlite.close();
59
- });
60
- }
61
- };
62
-
63
- clearTimeout(Scoped.cacheStorageReducer);
64
- if (timer) {
65
- Scoped.cacheStorageReducer = setTimeout(doUpdate, timer);
66
- } else doUpdate();
31
+ ...promoteCache ? {
32
+ DatabaseStore: serializeToBase64(DatabaseStore),
33
+ PendingWrites: serializeToBase64(PendingWrites)
34
+ } : {},
35
+ ...promoteCache ? restStore : {}
36
+ });
37
+
38
+ io.output(txt, node);
39
+ } else {
40
+ // use sqlite
41
+ const exclusion = ['DatabaseStore', 'DatabaseCountResult', 'FetchedStore'];
42
+ const updationKey = (node ? Array.isArray(node) ? node : [node] : CacheKeys).filter(v => !exclusion.includes(v));
43
+
44
+ if (!updationKey.length) return;
45
+ await Promise.all(
46
+ updationKey
47
+ .map(v => [v, v === 'PendingWrites' ? serializeToBase64(CacheStore[v]) : CacheStore[v]])
48
+ .map(([ref, value]) =>
49
+ getSystem(FILE_NAME).set(TABLE_NAME, ref, { value })
50
+ )
51
+ ).catch(err => {
52
+ console.error('updateCacheStore err:', err);
53
+ });
54
+ }
67
55
  };
68
56
 
69
57
  export const releaseCacheStore = async (builder) => {
@@ -75,19 +63,15 @@ export const releaseCacheStore = async (builder) => {
75
63
  if (io) {
76
64
  data = JSON.parse((await io.input()) || '{}');
77
65
  } else {
78
- const sqlite = await openDB(FILE_NAME);
79
- await sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${TABLE_NAME} ( ref TEXT PRIMARY KEY, value BLOB )`).catch(() => null);
80
66
  try {
81
- const [query] = await sqlite.executeSql(`SELECT * FROM ${TABLE_NAME}`);
67
+ const query = await getSystem(FILE_NAME).list(TABLE_NAME, ['value']).catch(() => []);
82
68
  data = Object.fromEntries(
83
- await Promise.all(query.rows.raw().map(async v =>
84
- [v.ref, await parseBigData(v.value)]
85
- ))
69
+ query.map(([ref, { value }]) =>
70
+ [ref, value]
71
+ )
86
72
  );
87
73
  } catch (error) {
88
74
  console.error('initializeCache sqlite data release err:', error);
89
- } finally {
90
- sqlite.close();
91
75
  }
92
76
  }
93
77
  await purgeRedundantRecords(data, builder);
@@ -8,7 +8,6 @@ export const Scoped = {
8
8
  InitializedProject: {},
9
9
  ReleaseCacheData: undefined,
10
10
  AuthJWTToken: {},
11
- cacheStorageReducer: undefined,
12
11
  IsStoreReady: false,
13
12
  TokenRefreshTimer: {},
14
13
  LastTokenRefreshRef: {},
@@ -26,11 +25,6 @@ export const Scoped = {
26
25
  database: {},
27
26
  dbQueryCount: {},
28
27
  httpFetch: {}
29
- },
30
- initedSqliteInstances: {
31
- httpFetch: {},
32
- dbQueryCount: {},
33
- database: {}
34
28
  }
35
29
  };
36
30
 
@@ -62,6 +56,9 @@ export const CacheStore = {
62
56
  },
63
57
  AuthStore: {},
64
58
  PendingAuthPurge: {},
59
+ /**
60
+ * [the instance url]: the url been emulated
61
+ */
65
62
  EmulatedAuth: {},
66
63
  PendingWrites: {},
67
64
  FetchedStore: {}
package/src/index.d.ts CHANGED
@@ -96,11 +96,7 @@ interface ReleaseCacheOption_IO {
96
96
 
97
97
  interface ReleaseCacheOption {
98
98
  /**
99
- * This key will be used to encrypt sqlite data stored locally
100
- */
101
- sqliteKey?: string;
102
- /**
103
- * sqlite is used as the default caching mechanism
99
+ * fs is used as the default caching mechanism
104
100
  *
105
101
  * providing `io` means you want to override this behaviour and handle caching yourself in the system's memory
106
102
  */
package/src/index.js CHANGED
@@ -419,10 +419,7 @@ const discloseSocketArguments = (args = []) => {
419
419
 
420
420
  const validateReleaseCacheProp = (prop) => {
421
421
  Object.entries(prop).forEach(([k, v]) => {
422
- if (k === 'sqliteKey') {
423
- if (typeof v !== 'string' || v.trim().length <= 0)
424
- throw `Invalid value supplied to "sqliteKey", value must be a string and greater than 0 characters`;
425
- } else if (k === 'io') {
422
+ if (k === 'io') {
426
423
  Object.entries(v).forEach(([k, v]) => {
427
424
  if (k === 'input' || k === 'output') {
428
425
  if (typeof v !== 'function')
@@ -23,7 +23,7 @@ export const injectFreshToken = async (config, { token, refreshToken }) => {
23
23
  const isEmulated = projectUrl in CacheStore.EmulatedAuth;
24
24
  if (isEmulated) delete CacheStore.EmulatedAuth[projectUrl];
25
25
 
26
- updateCacheStore(0, ['AuthStore', isEmulated ? 'EmulatedAuth' : ''].filter(v => v));
26
+ updateCacheStore(['AuthStore', isEmulated ? 'EmulatedAuth' : ''].filter(v => v));
27
27
 
28
28
  triggerAuthToken(projectUrl);
29
29
  initTokenRefresher(config);
@@ -47,7 +47,7 @@ export const injectEmulatedAuth = async (config, emulatedURL) => {
47
47
  Scoped.AuthJWTToken[projectUrl] = token;
48
48
  CacheStore.EmulatedAuth[projectUrl] = emulatedURL;
49
49
 
50
- updateCacheStore(0, ['AuthStore', 'EmulatedAuth']);
50
+ updateCacheStore(['AuthStore', 'EmulatedAuth']);
51
51
  triggerAuthToken(projectUrl);
52
52
  initTokenRefresher(config);
53
53
  };
@@ -161,7 +161,7 @@ const refreshToken = (builder, processRef, remainRetries = 1, isForceRefresh) =>
161
161
  triggerAuthToken(v, isInit);
162
162
  if (isForceRefresh) Scoped.InitiatedForcedToken[v] = true;
163
163
  });
164
- updateCacheStore(0, ['AuthStore']);
164
+ updateCacheStore(['AuthStore']);
165
165
  initTokenRefresher(builder);
166
166
  } else reject(lostProcess.simpleError);
167
167
  } catch (e) {
@@ -232,13 +232,12 @@ const doCustomSignup = (builder, email, password, name, metadata) => new Promise
232
232
 
233
233
  const purgeCache = (url, isMain) => {
234
234
  if (url in Scoped.AuthJWTToken) delete Scoped.AuthJWTToken[url];
235
- Object.keys(CacheStore).forEach(e => {
236
- if (
237
- e !== 'PendingAuthPurge' &&
238
- (!['EmulatedAuth'].includes(e) || isMain)
239
- ) {
240
- if (CacheStore[e][url]) delete CacheStore[e][url];
241
- }
235
+ [
236
+ isMain ? 'EmulatedAuth' : undefined,
237
+ 'AuthStore',
238
+ 'PendingWrites'
239
+ ].forEach(e => {
240
+ if (e && CacheStore[e][url]) delete CacheStore[e][url];
242
241
  });
243
242
  TokenRefreshListener.dispatch(url);
244
243
  triggerAuthToken(url);
@@ -257,7 +256,7 @@ export const doSignOut = async (builder) => {
257
256
  const emulatedURL = CacheStore.EmulatedAuth[builder.projectUrl];
258
257
 
259
258
  clearCacheForSignout(builder, !emulatedURL);
260
- updateCacheStore(0, ['AuthStore', 'EmulatedAuth']);
259
+ updateCacheStore(['AuthStore', 'EmulatedAuth']);
261
260
  if (emulatedURL) return;
262
261
  await revokeAuthIntance(builder);
263
262
  };
@@ -304,7 +303,7 @@ export const purgePendingToken = async (nodeId) => {
304
303
  throw simplifyCaughtError(e).simpleError;
305
304
  } finally {
306
305
  delete CacheStore.PendingAuthPurge[nodeId];
307
- updateCacheStore(0);
306
+ updateCacheStore(['PendingAuthPurge']);
308
307
  }
309
308
  };
310
309
 
@@ -12,10 +12,9 @@ import { niceGuard, Validator } from "guard-object";
12
12
  import { TIMESTAMP } from "../..";
13
13
  import { docSize, incrementDatabaseSize } from "./counter";
14
14
  import { deserializeBSON, serializeToBase64 } from "./bson";
15
- import { openDB, SQLITE_COMMANDS, SQLITE_PATH, useSqliteLinearAccessId } from "../../helpers/sqlite_manager";
16
- import { getStoreID, handleBigData, parseBigData } from "../../helpers/fs_manager";
15
+ import { FS_PATH, getSystem, useFS } from "../../helpers/fs_manager";
17
16
 
18
- const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY } = SQLITE_PATH;
17
+ const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY } = FS_PATH;
19
18
 
20
19
  export const listenQueryEntry = (callback, { accessId, builder, config, processId }) => {
21
20
  const { projectUrl, dbName, dbUrl, path } = builder;
@@ -50,28 +49,14 @@ export const insertCountQuery = async (builder, access_id, value) => {
50
49
  const { io } = Scoped.ReleaseCacheData;
51
50
  if (io) {
52
51
  setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id], { value, touched: Date.now() });
52
+ updateCacheStore(['DatabaseCountResult']);
53
53
  } else {
54
- const initNode = `${projectUrl}_${dbUrl}_${dbName}_${path}`;
55
- await useSqliteLinearAccessId(builder, access_id, 'dbQueryCount')(async sqlite => {
56
- if (!Scoped.initedSqliteInstances.dbQueryCount[initNode]) {
57
- Scoped.initedSqliteInstances.dbQueryCount[initNode] = (async () => {
58
- await sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${DB_COUNT_QUERY(path)} ( access_id TEXT PRIMARY KEY, value TEXT, touched INTEGER )`).catch(() => null);
59
- await Promise.allSettled([
60
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(DB_COUNT_QUERY(path), ['access_id'])),
61
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(DB_COUNT_QUERY(path), ['touched']))
62
- ]);
63
- })();
64
- }
65
-
66
- await Scoped.initedSqliteInstances.dbQueryCount[initNode];
67
- await sqlite.executeSql(
68
- SQLITE_COMMANDS.MERGE(DB_COUNT_QUERY(path), ['access_id', 'value', 'touched']),
69
- [access_id, JSON.stringify(value), Date.now()]
70
- );
54
+ await useFS(builder, access_id, 'dbQueryCount')(async fs => {
55
+ await fs.set(DB_COUNT_QUERY(path), access_id, { value, touched: Date.now() });
71
56
  setLodash(CacheStore.DatabaseStats.counters, [projectUrl, dbUrl, dbName, path], true);
72
57
  });
58
+ updateCacheStore(['DatabaseStats']);
73
59
  }
74
- updateCacheStore(undefined, ['DatabaseCountResult'])
75
60
  }
76
61
 
77
62
  export const getCountQuery = async (builder, access_id) => {
@@ -83,14 +68,11 @@ export const getCountQuery = async (builder, access_id) => {
83
68
  if (data) data.touched = Date.now();
84
69
  return data && data.value;
85
70
  } else {
86
- const result = await useSqliteLinearAccessId(builder, access_id, 'dbQueryCount')(sqlite =>
87
- sqlite.executeSql(`SELECT * FROM ${DB_COUNT_QUERY(path)} WHERE access_id = ?`, [access_id]).then(async r => {
88
- r = JSON.parse(r[0].rows.item(0).value);
89
- await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(DB_COUNT_QUERY(path), ['touched'], 'access_id = ?'), [Date.now(), access_id]);
90
- return r;
91
- }).catch(() => undefined)
92
- );
93
- return result;
71
+ return useFS(builder, access_id, 'dbQueryCount')(async fs => {
72
+ const { value } = await fs.find(DB_COUNT_QUERY(path), access_id, ['value']);
73
+ await fs.set(DB_COUNT_QUERY(path), access_id, { touched: Date.now() });
74
+ return value;
75
+ }).catch(() => undefined);
94
76
  }
95
77
  }
96
78
 
@@ -106,35 +88,13 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
106
88
  const thisSize = docSize(value);
107
89
 
108
90
  if (!io) {
109
- await useSqliteLinearAccessId(builder, accessIdWithoutLimit, 'database')(async (sqlite, db_filename) => {
110
- const initNode = `${projectUrl}_${dbUrl}_${dbName}_${path}`;
111
-
112
- if (!Scoped.initedSqliteInstances.database[initNode]) {
113
- Scoped.initedSqliteInstances.database[initNode] = (async () => {
114
- await Promise.allSettled([
115
- sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${LIMITER_DATA(path)} ( access_id TEXT PRIMARY KEY, value BLOB, touched INTEGER, size INTEGER )`),
116
- sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${LIMITER_RESULT(path)} ( access_id_limiter TEXT PRIMARY KEY, access_id TEXT, value BLOB, touched INTEGER, size INTEGER )`)
117
- ]);
118
-
119
- await Promise.allSettled([
120
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_DATA(path), ['access_id'])),
121
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_DATA(path), ['touched'])),
122
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_RESULT(path), ['access_id_limiter'])),
123
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_RESULT(path), ['touched']))
124
- ]);
125
- })();
126
- }
127
-
128
- await Scoped.initedSqliteInstances.database[initNode];
129
-
91
+ await useFS(builder, accessIdWithoutLimit, 'database')(async fs => {
130
92
  const resultAccessId = `${accessIdWithoutLimit}-${limit}`;
131
93
 
132
94
  const [instanceData, resultData] = await Promise.all([
133
- sqlite.executeSql(`SELECT access_id, size FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [accessIdWithoutLimit]),
134
- sqlite.executeSql(`SELECT access_id_limiter, size FROM ${LIMITER_RESULT(path)} WHERE access_id_limiter = ?`, [resultAccessId])
135
- ]).then(r =>
136
- r.map(v => v[0].rows.item(0))
137
- );
95
+ fs.find(LIMITER_DATA(path), accessIdWithoutLimit, ['size']).catch(() => undefined),
96
+ fs.find(LIMITER_RESULT(path), resultAccessId, ['size']).catch(() => undefined)
97
+ ]);
138
98
  const isEpisode = episode === 1 || !!resultData;
139
99
 
140
100
  const editionSizeOffset = thisSize - (instanceData?.size || 0);
@@ -151,27 +111,24 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
151
111
  data: value,
152
112
  size: thisSize
153
113
  });
154
- const [blobData, episodeBlobData] = await Promise.all([
155
- handleBigData(getStoreID(db_filename, LIMITER_DATA(path), accessIdWithoutLimit), newData),
156
- isEpisode ?
157
- handleBigData(getStoreID(db_filename, LIMITER_RESULT(path), resultAccessId), newResultData)
158
- : Promise.resolve()
159
- ]);
160
114
 
161
115
  await Promise.all([
162
- sqlite.executeSql(
163
- SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
164
- [accessIdWithoutLimit, blobData, Date.now(), thisSize]
165
- ),
116
+ fs.set(LIMITER_DATA(path), accessIdWithoutLimit, {
117
+ value: newData,
118
+ touched: Date.now(),
119
+ size: thisSize
120
+ }),
166
121
  isEpisode ?
167
- sqlite.executeSql(
168
- SQLITE_COMMANDS.MERGE(LIMITER_RESULT(path), ['access_id_limiter', 'access_id', 'value', 'touched', 'size']),
169
- [resultAccessId, accessIdWithoutLimit, episodeBlobData, Date.now(), thisSize]
170
- ) : Promise.resolve()
122
+ fs.set(LIMITER_RESULT(path), resultAccessId, {
123
+ access_id: accessIdWithoutLimit,
124
+ value: newResultData,
125
+ touched: Date.now(),
126
+ size: thisSize
127
+ }) : Promise.resolve()
171
128
  ]);
172
129
  incrementDatabaseSize(builder, path, editionSizeOffset + resultSizeOffset);
173
130
  });
174
- updateCacheStore(undefined, ['DatabaseStore', 'DatabaseStats']);
131
+ updateCacheStore(['DatabaseStats']);
175
132
  return;
176
133
  }
177
134
 
@@ -200,7 +157,7 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
200
157
 
201
158
  setLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit], newData);
202
159
  if (isEpisode) setLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, limit], cloneDeep(newResultData));
203
- updateCacheStore(undefined, ['DatabaseStore', 'DatabaseStats']);
160
+ updateCacheStore(['DatabaseStore', 'DatabaseStats']);
204
161
  };
205
162
 
206
163
  export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
@@ -231,19 +188,19 @@ export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
231
188
  }
232
189
 
233
190
  if (!io) {
234
- const record = await useSqliteLinearAccessId(builder, accessIdWithoutLimit, 'database')(async sqlite => {
191
+ const record = await useFS(builder, accessIdWithoutLimit, 'database')(async fs => {
235
192
  const resultAccessId = `${accessIdWithoutLimit}-${limit}`;
236
193
 
237
194
  const qData = await (
238
- isEpisode ? sqlite.executeSql(`SELECT * FROM ${LIMITER_RESULT(path)} WHERE access_id_limiter = ?`, [resultAccessId]) :
239
- sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [accessIdWithoutLimit])
240
- ).then(v => v[0].rows.item(0)).catch(() => null);
241
- const thisData = qData && deserializeBSON(await parseBigData(qData.value), true);
195
+ isEpisode ? fs.find(LIMITER_RESULT(path), resultAccessId, ['value']) :
196
+ fs.find(LIMITER_DATA(path), accessIdWithoutLimit, ['value'])
197
+ ).catch(() => null);
198
+ const thisData = qData && deserializeBSON(qData.value, true);
242
199
 
243
200
  if (!thisData) return null;
244
201
 
245
202
  if (isEpisode) {
246
- await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(LIMITER_RESULT(path), ['touched'], 'access_id_limiter = ?'), [Date.now(), resultAccessId]);
203
+ await fs.set(LIMITER_RESULT(path), resultAccessId, { touched: Date.now() });
247
204
  return [thisData.data];
248
205
  }
249
206
 
@@ -253,7 +210,7 @@ export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
253
210
  latest_limiter === undefined ||
254
211
  (Validator.POSITIVE_NUMBER(limit) && latest_limiter >= limit)
255
212
  ) {
256
- await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(LIMITER_DATA(path), ['touched'], 'access_id = ?'), [Date.now(), accessIdWithoutLimit]);
213
+ await fs.set(LIMITER_DATA(path), accessIdWithoutLimit, { touched: Date.now() });
257
214
  return [transformData(data)];
258
215
  }
259
216
  });
@@ -457,46 +414,28 @@ export const addPendingWrites = async (builder, writeId, result) => {
457
414
  )
458
415
  );
459
416
  } else {
460
- const sqlite = await openDB(builder);
461
- try {
462
- const colListing = await sqlite.executeSql(`SELECT access_id FROM ${LIMITER_DATA(path)}`).then(v =>
463
- v[0].rows.raw().map(d => d.access_id)
464
- ).catch(() => []);
465
- const pathFinder = {};
466
-
467
- await Promise.all(colListing.map(async access_id =>
468
- useSqliteLinearAccessId(builder, access_id, 'database')(async (sqlite, db_filename) => {
469
- const data = await sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [access_id]).then(async v => {
470
- const d = v[0].rows.item(0);
471
- return [d.access_id, deserializeBSON(await parseBigData(d.value), true)];
472
- });
473
- await MutateDataInstance(data, path =>
474
- pathFinder[path] || (
475
- pathFinder[path] = sqlite.executeSql(`SELECT value FROM ${LIMITER_DATA(path)}`).then(v =>
476
- Promise.all(
477
- v[0].rows.raw().map(async d =>
478
- deserializeBSON(await parseBigData(d.value), true).data
479
- )
480
- ).then(r => r.flat())
481
- ).catch(() => [])
482
- )
483
- );
484
- const editedBlobData = await handleBigData(
485
- getStoreID(db_filename, LIMITER_DATA(path), access_id),
486
- serializeToBase64(data[1])
487
- );
488
-
489
- await sqlite.executeSql(
490
- SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
491
- [access_id, editedBlobData, Date.now(), data[1].size]
492
- );
493
- })
494
- ));
495
- } catch (error) {
496
- throw error;
497
- } finally {
498
- sqlite.close();
499
- }
417
+ const colListing = await getSystem(builder).list(LIMITER_DATA(path), []).catch(() => []);
418
+ const pathFinder = {};
419
+
420
+ await Promise.all(colListing.map(async ([access_id]) =>
421
+ useFS(builder, access_id, 'database')(async fs => {
422
+ const data = await fs.find(LIMITER_DATA(path), access_id, ['value'])
423
+ .then(r => deserializeBSON(r.value, true));
424
+
425
+ await MutateDataInstance([access_id, data], path =>
426
+ pathFinder[path] || (
427
+ pathFinder[path] = fs.list(LIMITER_DATA(path), ['value'])
428
+ .then(v => v.map(d => deserializeBSON(d[1].value, true).data).flat())
429
+ .catch(() => [])
430
+ )
431
+ );
432
+ await fs.set(LIMITER_DATA(path), access_id, {
433
+ touched: Date.now(),
434
+ value: serializeToBase64(data),
435
+ size: data.size
436
+ });
437
+ })
438
+ ));
500
439
  }
501
440
 
502
441
  async function MutateDataInstance([entityId, dataObj], pathGetter) {
@@ -706,7 +645,7 @@ export const addPendingWrites = async (builder, writeId, result) => {
706
645
  addedOn: Date.now()
707
646
  }));
708
647
 
709
- updateCacheStore(undefined, ['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
648
+ updateCacheStore(['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
710
649
  notifyDatabaseNodeChanges(builder, [...pathChanges]);
711
650
  };
712
651
 
@@ -724,22 +663,17 @@ export const removePendingWrite = async (builder, writeId, revert) => {
724
663
  if (io) {
725
664
  RevertMutation(getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]));
726
665
  } else {
727
- await useSqliteLinearAccessId(builder, access_id, 'database')(async (sqlite, db_filename) => {
728
- const colObj = await sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [access_id]).then(async v => {
729
- const d = v[0].rows.item(0);
730
- return deserializeBSON(await parseBigData(d.value));
731
- }).catch(() => null);
666
+ await useFS(builder, access_id, 'database')(async fs => {
667
+ const colObj = await fs.find(LIMITER_DATA(path), access_id, ['value'])
668
+ .then(v => deserializeBSON(v.value))
669
+ .catch(() => null);
732
670
  if (!colObj) return;
733
671
  RevertMutation(colObj);
734
- const revertedBlobData = await handleBigData(
735
- getStoreID(db_filename, LIMITER_DATA(path), access_id),
736
- serializeToBase64(colObj)
737
- );
738
-
739
- await sqlite.executeSql(
740
- SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
741
- [access_id, revertedBlobData, Date.now(), colObj.size]
742
- );
672
+ await fs.set(LIMITER_DATA(path), access_id, {
673
+ value: serializeToBase64(colObj),
674
+ touched: Date.now(),
675
+ size: colObj.size
676
+ });
743
677
  });
744
678
  }
745
679
 
@@ -782,7 +716,7 @@ export const removePendingWrite = async (builder, writeId, revert) => {
782
716
  }
783
717
 
784
718
  unsetLodash(CacheStore.PendingWrites, [projectUrl, writeId]);
785
- updateCacheStore(undefined, ['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
719
+ updateCacheStore(['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
786
720
  notifyDatabaseNodeChanges(builder, [...pathChanges]);
787
721
  };
788
722
 
@@ -396,7 +396,7 @@ const countCollection = async (builder, config) => {
396
396
 
397
397
  if (e?.simpleError) {
398
398
  finalize(undefined, e.simpleError);
399
- } else if (!disableCache && !Validator.NUMBER(b4Data)) {
399
+ } else if (!disableCache && Validator.NUMBER(b4Data)) {
400
400
  finalize(b4Data);
401
401
  } else if (retries > maxRetries) {
402
402
  finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
@@ -2,11 +2,10 @@ import { updateCacheStore } from "../../helpers/utils";
2
2
  import { CacheStore, Scoped } from "../../helpers/variables";
3
3
  import cloneDeep from "lodash/cloneDeep";
4
4
  import { serialize } from "entity-serializer";
5
- import { SQLITE_COMMANDS, SQLITE_PATH, useSqliteLinearAccessId } from "../../helpers/sqlite_manager";
6
5
  import { incrementFetcherSize } from "./counter";
7
- import { getStoreID, handleBigData, parseBigData } from "../../helpers/fs_manager";
6
+ import { FS_PATH, useFS } from "../../helpers/fs_manager";
8
7
 
9
- const { FETCH_RESOURCES } = SQLITE_PATH;
8
+ const { FETCH_RESOURCES } = FS_PATH;
10
9
 
11
10
  export const insertFetchResources = async (projectUrl, access_id, value) => {
12
11
  value = cloneDeep(value);
@@ -23,35 +22,16 @@ export const insertFetchResources = async (projectUrl, access_id, value) => {
23
22
  data: value,
24
23
  size: dataSize
25
24
  };
25
+ updateCacheStore(['FetchedStore', 'DatabaseStats']);
26
26
  } else {
27
- const initNode = projectUrl;
27
+ await useFS(FETCH_RESOURCES(projectUrl), access_id, 'httpFetch')(async fs => {
28
+ const b4Data = await fs.find('main', access_id, ['size']);
28
29
 
29
- await useSqliteLinearAccessId(FETCH_RESOURCES(projectUrl), access_id, 'httpFetch')(async (sqlite, db_filename) => {
30
- if (!Scoped.initedSqliteInstances.httpFetch[initNode]) {
31
- Scoped.initedSqliteInstances.httpFetch[initNode] = (async () => {
32
- await sqlite.executeSql(`CREATE TABLE IF NOT EXISTS main ( access_id TEXT PRIMARY KEY, value BLOB, touched INTEGER, size INTEGER )`).catch(() => null);
33
- await Promise.allSettled([
34
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX('main', ['access_id'])),
35
- // sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX('main', ['touched']))
36
- ]);
37
- })();
38
- }
39
-
40
- await Scoped.initedSqliteInstances.httpFetch[initNode];
41
- const b4Data = await sqlite.executeSql(`SELECT access_id, size FROM main WHERE access_id = ?`, [access_id]).then(r =>
42
- r[0].rows.item(0)
43
- );
44
- const blobData = await handleBigData(getStoreID(db_filename, 'main', access_id), value);
45
-
46
- await sqlite.executeSql(
47
- SQLITE_COMMANDS.MERGE('main', ['access_id', 'value', 'touched', 'size']),
48
- [access_id, blobData, Date.now(), dataSize]
49
- );
30
+ await fs.set('main', access_id, { value, size: dataSize, touched: Date.now() });
50
31
  incrementFetcherSize(projectUrl, dataSize - (b4Data?.size || 0));
51
32
  });
33
+ updateCacheStore(['DatabaseStats']);
52
34
  }
53
-
54
- updateCacheStore(undefined, ['FetchedStore']);
55
35
  }
56
36
 
57
37
  export const getFetchResources = async (projectUrl, access_id) => {
@@ -63,15 +43,12 @@ export const getFetchResources = async (projectUrl, access_id) => {
63
43
  return record && cloneDeep(record?.data);
64
44
  }
65
45
 
66
- const res = await useSqliteLinearAccessId(FETCH_RESOURCES(projectUrl), access_id, 'httpFetch')(async sqlite => {
67
- const query = await sqlite.executeSql('SELECT * FROM main WHERE access_id = ?', [access_id]).catch(() => null);
68
-
69
- const rawData = query && query[0].rows.item(0)?.value;
70
- if (!rawData) return null;
46
+ const res = await useFS(FETCH_RESOURCES(projectUrl), access_id, 'httpFetch')(async fs => {
47
+ const query = await fs.find('main', access_id, ['value']).catch(() => null);
48
+ if (!query) return null;
71
49
 
72
- const data = await parseBigData(rawData);
73
- await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS('main', ['touched'], 'access_id = ?'), [Date.now(), access_id]);
74
- return data;
50
+ await fs.set('main', access_id, { touched: Date.now() });
51
+ return query.value;
75
52
  });
76
53
  return res;
77
54
  }
@@ -1,144 +0,0 @@
1
- import { enablePromise, openDatabase } from 'react-native-sqlite-storage';
2
- import { Scoped, SqliteCollective } from './variables';
3
- import { niceHash } from './peripherals';
4
-
5
- enablePromise(true);
6
- let sqliteKeyHash;
7
-
8
- /**
9
- * this method implement a centralize approach for opening and closing of sqlite database to ensure consistency across multiple task opening and closing the database in diferent order
10
- *
11
- * @param {string} name
12
- * @returns {Promise<import('react-native-sqlite-storage').SQLiteDatabase>}
13
- */
14
- export const openDB = async (name, onName) => {
15
-
16
- if (name?.projectUrl) {
17
- const { projectUrl, dbUrl, dbName } = name;
18
- name = encodeURIComponent(`${projectUrl}_${dbUrl}_${dbName}`) + '.db';
19
- }
20
-
21
- const { sqliteKey } = Scoped.ReleaseCacheData;
22
-
23
- if (sqliteKey) {
24
- const thisHash = await (sqliteKeyHash || (sqliteKeyHash = niceHash(sqliteKey)));
25
- name = `${thisHash}__${name}`;
26
- }
27
- onName?.(name);
28
-
29
- if (!SqliteCollective.openedDb[name]) {
30
- SqliteCollective.openedDbProcess[name] = 0;
31
- SqliteCollective.openedDb[name] = Promise.allSettled([SqliteCollective.closeDbPromises[name] || Promise.resolve()]).then(() =>
32
- openDatabase({
33
- location: 'default',
34
- name,
35
- key: sqliteKey
36
- }).then(db => {
37
- const prevClose = db.close.bind(db);
38
-
39
- db.close = () => new Promise((resolve, reject) => {
40
- if (--SqliteCollective.openedDbProcess[name] === 0) {
41
- let willClose;
42
- const timer = setTimeout(async () => {
43
- willClose = true;
44
- delete SqliteCollective.openedDb[name];
45
- delete SqliteCollective.openedDbProcess[name];
46
- SqliteCollective.closeDbPromises[name] = prevClose().then(() => {
47
- resolve('active');
48
- }).catch(e => {
49
- reject(new Error(`${e}`));
50
- }).finally(() => {
51
- delete SqliteCollective.closeDbPromises[name];
52
- });
53
- delete SqliteCollective.openedDbReducerTimer[name];
54
- }, 7);
55
-
56
- SqliteCollective.openedDbReducerTimer[name] = () => {
57
- clearTimeout(timer);
58
- if (!willClose) resolve('passive');
59
- delete SqliteCollective.openedDbReducerTimer[name];
60
- }
61
- } else resolve('passive');
62
- });
63
- return db;
64
- })
65
- );
66
- }
67
-
68
- SqliteCollective.openedDbReducerTimer[name]?.();
69
- ++SqliteCollective.openedDbProcess[name];
70
- const thisDb = await SqliteCollective.openedDb[name];
71
- let hasClosed;
72
-
73
- const thisClose = async () => {
74
- if (hasClosed) return;
75
- hasClosed = true;
76
- return (await thisDb.close());
77
- }
78
-
79
- return new Proxy({}, {
80
- get: (_, n) => {
81
- if (n === 'close') {
82
- return thisClose;
83
- } else if (typeof thisDb[n] === 'function')
84
- return thisDb[n].bind(thisDb);
85
- return thisDb[n];
86
- },
87
- set: (_, n, v) => {
88
- thisDb[n] = v;
89
- }
90
- });
91
- };
92
-
93
- /**
94
- * this method linearize read/write on sqlite ensuring consistency across concurrent operations
95
- *
96
- * @param {any} builder
97
- * @param {string} access_id
98
- * @param {'database' | 'dbQueryCount' | 'httpFetch'} node
99
- * @returns {(task: (sqlite: import("react-native-sqlite-storage").SQLiteDatabase, db_filename: string) => Promise<{any}> )=> Promise<{any}>}
100
- */
101
- export const useSqliteLinearAccessId = (builder, access_id, node) => async (task) => {
102
- const { projectUrl, dbUrl, dbName } = builder;
103
- const nodeId = typeof builder === 'string' ? `${builder}_${access_id}` : `${projectUrl}_${dbUrl}_${dbName}_${access_id}`;
104
- let db_filename;
105
-
106
- const sqlite = await openDB(builder, n => db_filename = n);
107
-
108
- const thatProcess = Scoped.linearSqliteProcess[node][nodeId];
109
-
110
- const thisPromise = new Promise(async (resolve, reject) => {
111
- try {
112
- if (thatProcess !== undefined) await thatProcess;
113
- } catch (_) { }
114
- try {
115
- resolve(await task(sqlite, db_filename));
116
- } catch (error) {
117
- console.error('useSqliteLinearAccessId err:', error, ' builder:', builder);
118
- reject(error);
119
- } finally {
120
- if (Scoped.linearSqliteProcess[node][nodeId] === thisPromise)
121
- delete Scoped.linearSqliteProcess[node][nodeId];
122
- sqlite.close();
123
- }
124
- });
125
-
126
- Scoped.linearSqliteProcess[node][nodeId] = thisPromise;
127
- return (await thisPromise);
128
- };
129
-
130
- export const SQLITE_PATH = {
131
- FILE_NAME: 'MOSQUITO_TRANSPORT.db',
132
- TABLE_NAME: 'MT_MAIN',
133
- LIMITER_RESULT: path => `"${encodeURIComponent(path)}_LIMITER_RESULT"`,
134
- LIMITER_DATA: path => `"${encodeURIComponent(path)}_LIMITER_DATA"`,
135
- DB_COUNT_QUERY: path => `"${encodeURIComponent(path)}_DB_COUNT_QUERY"`,
136
- FETCH_RESOURCES: projectUrl => `FETCH_RESOURCES_${encodeURIComponent(projectUrl)}.db`
137
- };
138
-
139
- export const SQLITE_COMMANDS = {
140
- MERGE: (table, columns = []) => `INSERT OR REPLACE INTO ${table} (${columns.join(', ')}) VALUES (${columns.fill('?').join(', ')})`,
141
- UPDATE_COLUMNS: (table, columns = [], query = '') => `UPDATE ${table} SET ${columns.map(v => `${v} = ?`).join(', ')} WHERE ${query}`,
142
- CREATE_INDEX: (table, columns) => `CREATE INDEX idx_${columns.join('_')} ON ${table}(${columns.join(', ')})`,
143
- DELETE_ROW: (table, query) => `DELETE FROM ${table} WHERE ${query}`
144
- }