react-native-mosquito-transport 0.0.35 → 0.0.37

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.35",
3
+ "version": "0.0.37",
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",
@@ -1,6 +1,8 @@
1
+ import { deserialize, serialize } from "entity-serializer";
1
2
  import { Scoped } from "./variables";
2
3
  import { Platform } from "react-native";
3
4
  import { Dirs, FileSystem } from "react-native-file-access";
5
+ import { Buffer } from "buffer";
4
6
 
5
7
  const PARENT_FOLDER = `${Platform.OS === 'android' ? Dirs.DocumentDir.split('/').slice(0, -1).join('/') : Dirs.MainBundleDir}/mosquito_base`;
6
8
 
@@ -48,7 +50,7 @@ export const getSystem = (builder) => {
48
50
  const path = conjoin(table, primary_key);
49
51
  await FileSystem.mkdir(path).catch(() => null);
50
52
  await Promise.all(Object.entries(value).map(([k, v]) =>
51
- FileSystem.writeFile(joinPath(path, k), JSON.stringify(v), 'utf8')
53
+ FileSystem.writeFile(joinPath(path, k), serialize(v).toString('base64'), 'base64')
52
54
  ));
53
55
  },
54
56
  delete: (table, primary_key) => FileSystem.unlink(conjoin(table, primary_key)),
@@ -56,7 +58,7 @@ export const getSystem = (builder) => {
56
58
  const path = conjoin(table, primary_key);
57
59
 
58
60
  const value_map = await Promise.all(extractions.map(async node =>
59
- [node, JSON.parse(await FileSystem.readFile(joinPath(path, node), 'utf8'))]
61
+ [node, deserialize(Buffer.from(await FileSystem.readFile(joinPath(path, node), 'base64'), 'base64'))]
60
62
  ));
61
63
  return Object.fromEntries(value_map);
62
64
  },
@@ -12,7 +12,7 @@ const inlineFsData = (arr, access_id_node = 'access_id') =>
12
12
  return obj;
13
13
  });
14
14
 
15
- export const purgeRedundantRecords = async (data, builder) => {
15
+ export const purgeRedundantRecords = async (data, builder, willPurge) => {
16
16
  const { io, maxLocalDatabaseSize = 10485760, maxLocalFetchHttpSize = 10485760 } = builder;
17
17
 
18
18
  /**
@@ -20,9 +20,15 @@ export const purgeRedundantRecords = async (data, builder) => {
20
20
  */
21
21
  const { _db_size, _fetcher_size, counters, database, fetchers } = data.DatabaseStats || {};
22
22
 
23
+ const shouldPurgeDb = Validator.POSITIVE_NUMBER(_db_size) && maxLocalDatabaseSize && _db_size >= maxLocalDatabaseSize;
24
+ const shouldPurgeFetcher = Validator.POSITIVE_NUMBER(_fetcher_size) && maxLocalFetchHttpSize && _fetcher_size >= maxLocalFetchHttpSize;
25
+
26
+ if (shouldPurgeDb) willPurge(['DatabaseStore', 'DatabaseCountResult', 'DatabaseStats']);
27
+ if (shouldPurgeFetcher) willPurge(['FetchedStore', 'DatabaseStats']);
28
+
23
29
  if (io) {
24
30
  const purgeDatabase = () => {
25
- if (!Validator.POSITIVE_NUMBER(_db_size) || !maxLocalDatabaseSize || _db_size < maxLocalDatabaseSize) return;
31
+ if (!shouldPurgeDb) return;
26
32
  const DbListing = [];
27
33
 
28
34
  breakDbMap(data.DatabaseStore, (projectUrl, dbUrl, dbName, path, value) => {
@@ -98,7 +104,7 @@ export const purgeRedundantRecords = async (data, builder) => {
98
104
  });
99
105
  }
100
106
  const purgeFetcher = () => {
101
- if (!Validator.POSITIVE_NUMBER(_fetcher_size) || !maxLocalFetchHttpSize || _fetcher_size < maxLocalFetchHttpSize) return;
107
+ if (!shouldPurgeFetcher) return;
102
108
  const redundantFetchRanking = Object.entries(data.FetchedStore).map(([projectUrl, access_id_Obj]) =>
103
109
  Object.entries(access_id_Obj).map(([access_id, data]) => ({
104
110
  access_id,
@@ -133,7 +139,7 @@ export const purgeRedundantRecords = async (data, builder) => {
133
139
  await Promise.allSettled([
134
140
  (async () => {
135
141
  try {
136
- if (!Validator.POSITIVE_NUMBER(_db_size) || !maxLocalDatabaseSize || _db_size < maxLocalDatabaseSize) return;
142
+ if (!shouldPurgeDb) return;
137
143
  const instances = [];
138
144
 
139
145
  [database, counters].forEach((map, i) => {
@@ -204,7 +210,7 @@ export const purgeRedundantRecords = async (data, builder) => {
204
210
  })(),
205
211
  (async () => {
206
212
  try {
207
- if (!Validator.POSITIVE_NUMBER(_fetcher_size) || !maxLocalFetchHttpSize || _fetcher_size < maxLocalFetchHttpSize) return;
213
+ if (!shouldPurgeFetcher) return;
208
214
 
209
215
  const redundantFetchRanking = await Promise.all(
210
216
  Object.entries(fetchers).map(async ([projectUrl]) => {
@@ -245,7 +251,7 @@ export const purgeRedundantRecords = async (data, builder) => {
245
251
  }
246
252
  }
247
253
 
248
- const breakDbMap = (obj, callback) =>
254
+ export const breakDbMap = (obj = {}, callback) =>
249
255
  Object.entries(obj).forEach(([projectUrl, dbUrlObj]) => {
250
256
  Object.entries(dbUrlObj).forEach(([dbUrl, dbNameObj]) => {
251
257
  Object.entries(dbNameObj).forEach(([dbName, pathObj]) => {
@@ -1,16 +1,46 @@
1
1
  import { ServerReachableListener, StoreReadyListener } from "./listeners";
2
2
  import { CacheStore, Scoped } from "./variables";
3
3
  import { serializeE2E } from "./peripherals";
4
- import { deserializeBSON, serializeToBase64 } from "../products/database/bson";
4
+ import { DatastoreParser } from "../products/database/bson";
5
5
  import { deserialize } from "entity-serializer";
6
- import { purgeRedundantRecords } from "./purger";
6
+ import { breakDbMap, purgeRedundantRecords } from "./purger";
7
7
  import { FS_PATH, getSystem } from "./fs_manager";
8
+ import cloneDeep from "lodash/cloneDeep";
9
+ import { Buffer } from "buffer";
8
10
 
9
11
  const { FILE_NAME, TABLE_NAME } = FS_PATH;
10
12
 
11
13
  const CacheKeys = Object.keys(CacheStore);
12
14
 
15
+ const prefillDatastore = (obj, caller) => {
16
+ obj = cloneDeep(obj);
17
+ breakDbMap(obj, (_projectUrl, _dbUrl, _dbName, _path, value) => {
18
+ Object.entries(value.instance).forEach(([access_id, obj]) => {
19
+ value.instance[access_id] = caller(obj);
20
+ });
21
+ Object.entries(value.episode).forEach(([_access_id, limitObj]) => {
22
+ Object.entries(limitObj).forEach(([limit, obj]) => {
23
+ limitObj[limit] = caller(obj);
24
+ });
25
+ });
26
+ });
27
+ return obj;
28
+ };
29
+
30
+ const prefillFetcher = (store, encode) => {
31
+ store = cloneDeep(store);
32
+ Object.values(store).forEach(accessIdObj => {
33
+ Object.values(accessIdObj).forEach(value => {
34
+ value.data.buffer = encode ?
35
+ Buffer.from(value.data.buffer).toString('base64')
36
+ : Buffer.from(value.data.buffer, 'base64');
37
+ });
38
+ });
39
+ return store;
40
+ }
41
+
13
42
  export const updateCacheStore = async (node) => {
43
+ node = node && node.filter((v, i, a) => a.indexOf(v) === i);
14
44
  const { io, promoteCache } = Scoped.ReleaseCacheData;
15
45
 
16
46
  const {
@@ -19,17 +49,29 @@ export const updateCacheStore = async (node) => {
19
49
  PendingAuthPurge,
20
50
  DatabaseStore,
21
51
  PendingWrites,
52
+ FetchedStore,
22
53
  ...restStore
23
54
  } = CacheStore;
24
55
 
56
+ const minimizePendingWrite = () => {
57
+ const obj = cloneDeep(PendingWrites);
58
+ Object.values(obj).forEach(e => {
59
+ Object.values(e).forEach(b => {
60
+ if ('editions' in b) delete b.editions;
61
+ });
62
+ });
63
+ return obj;
64
+ }
65
+
25
66
  if (io) {
26
67
  const txt = JSON.stringify({
27
68
  AuthStore,
28
69
  EmulatedAuth,
29
70
  PendingAuthPurge,
30
71
  ...promoteCache ? {
31
- DatabaseStore: serializeToBase64(DatabaseStore),
32
- PendingWrites: serializeToBase64(PendingWrites)
72
+ DatabaseStore: prefillDatastore(DatabaseStore, DatastoreParser.encode),
73
+ PendingWrites: minimizePendingWrite(),
74
+ FetchedStore: prefillFetcher(FetchedStore, true)
33
75
  } : {},
34
76
  ...promoteCache ? restStore : {}
35
77
  });
@@ -43,7 +85,7 @@ export const updateCacheStore = async (node) => {
43
85
  if (!updationKey.length) return;
44
86
  await Promise.all(
45
87
  updationKey
46
- .map(v => [v, v === 'PendingWrites' ? serializeToBase64(CacheStore[v]) : CacheStore[v]])
88
+ .map(v => [v, v === 'PendingWrites' ? minimizePendingWrite() : CacheStore[v]])
47
89
  .map(([ref, value]) =>
48
90
  getSystem(FILE_NAME).set(TABLE_NAME, ref, { value })
49
91
  )
@@ -57,37 +99,45 @@ export const releaseCacheStore = async (builder) => {
57
99
  const { io } = builder;
58
100
 
59
101
  let data = {};
102
+ const tobePurged = [];
60
103
 
61
104
  try {
62
105
  if (io) {
63
106
  data = JSON.parse((await io.input()) || '{}');
64
- } else {
65
- try {
66
- const query = await getSystem(FILE_NAME).list(TABLE_NAME, ['value']).catch(() => []);
67
- data = Object.fromEntries(
68
- query.map(([ref, { value }]) =>
69
- [ref, value]
70
- )
107
+
108
+ if (data.DatabaseStore)
109
+ data.DatabaseStore = prefillDatastore(
110
+ data.DatabaseStore,
111
+ r => DatastoreParser.decode(r, false)
71
112
  );
72
- } catch (error) {
73
- console.error('initializeCache data release err:', error);
74
- }
113
+ if (data.FetchedStore)
114
+ data.FetchedStore = prefillFetcher(data.FetchedStore, false);
115
+ } else {
116
+ const query = await getSystem(FILE_NAME).list(TABLE_NAME, ['value']).catch(() => []);
117
+ data = Object.fromEntries(
118
+ query.map(([ref, { value }]) =>
119
+ [ref, value]
120
+ )
121
+ );
75
122
  }
76
- await purgeRedundantRecords(data, builder);
123
+ await purgeRedundantRecords(data, builder, purgeNodes => {
124
+ tobePurged.push(...purgeNodes);
125
+ });
77
126
  } catch (e) {
78
- console.error('initializeCache data err:', e);
127
+ console.error('releaseCacheStore data err:', e);
79
128
  }
80
129
 
81
130
  Object.entries(data).forEach(([k, v]) => {
82
- if (['DatabaseStore', 'PendingWrites'].includes(k)) {
83
- CacheStore[k] = deserializeBSON(v);
84
- } else CacheStore[k] = v;
131
+ CacheStore[k] = v;
85
132
  });
86
133
  Object.entries(CacheStore.AuthStore).forEach(([key, value]) => {
87
134
  Scoped.AuthJWTToken[key] = value?.token;
88
135
  });
89
136
  Scoped.IsStoreReady = true;
90
137
  StoreReadyListener.dispatch('_', 'ready');
138
+ setTimeout(() => {
139
+ if (tobePurged.length) updateCacheStore(tobePurged);
140
+ }, 0);
91
141
  };
92
142
 
93
143
  export const getPrefferTime = () => Date.now() + (Scoped.serverTimeOffset || 0);
@@ -12,7 +12,6 @@ export const RETRIEVAL = {
12
12
 
13
13
  export const DELIVERY = {
14
14
  DEFAULT: 'default',
15
- CACHE_AWAIT: 'cache-await', // TODO: remove
16
15
  CACHE_NO_AWAIT: 'cache-no-await',
17
16
  NO_CACHE_NO_AWAIT: 'no-cache-no-await',
18
17
  NO_CACHE_AWAIT: 'no-cache-await'
package/src/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { BSON } from "./vendor/bson";
2
+
1
3
  interface RNMTConfig {
2
4
  dbName?: string;
3
5
  dbUrl?: string;
@@ -75,6 +77,8 @@ export class DoNotEncrypt {
75
77
  value: any;
76
78
  }
77
79
 
80
+ export { BSON }
81
+
78
82
  interface auth_provider_id {
79
83
  GOOGLE: 'google';
80
84
  FACEBOOK: 'facebook';
@@ -289,7 +293,7 @@ interface FetchHttpConfig {
289
293
  rawApproach?: boolean;
290
294
  }
291
295
 
292
- type Delievery = 'default' | 'no-cache' | 'no-await' | 'no-await-no-cache' | 'await-no-cache' | 'cache-no-await';
296
+ type Delievery = 'default' | 'cache-no-await' | 'no-cache-no-await' | 'no-cache-await';
293
297
 
294
298
  interface WriteConfig {
295
299
  /**
@@ -297,21 +301,9 @@ interface WriteConfig {
297
301
  */
298
302
  disableAuth?: boolean;
299
303
 
300
- DEFAULT: 'default',
301
- CACHE_AWAIT: 'cache-await',
302
- CACHE_NO_AWAIT: 'cache-no-await',
303
- NO_CACHE_NO_AWAIT: 'no-cache-no-await',
304
- NO_CACHE_AWAIT: 'no-cache-await'
305
-
306
304
  /**
307
305
  * This property determines how the write will be dispatch to the remote server
308
306
  * TODO:
309
- *
310
- * `default`:
311
- * `cache-await`:
312
- * `cache-no-await`:
313
- * `no-cache-no-await`:
314
- * `no-cache-await`:
315
307
  */
316
308
  delivery?: Delievery;
317
309
  }
package/src/index.js CHANGED
@@ -15,6 +15,7 @@ import { Validator } from 'guard-object';
15
15
  import cloneDeep from 'lodash/cloneDeep';
16
16
  import { Buffer } from 'buffer';
17
17
  import MTAuth, { purgePendingToken } from './products/auth';
18
+ import { BSON } from "./vendor/bson";
18
19
 
19
20
  const {
20
21
  _listenCollection,
@@ -426,7 +427,7 @@ const validateReleaseCacheProp = (prop) => {
426
427
  throw `Invalid value supplied to "io.${k}", expected a function but got "${v}"`;
427
428
  } else throw `Unexpected property named "io.${k}"`;
428
429
  });
429
- if (!v?.input && !v?.output) throw '"input" and "output" are required when "io" is provided';
430
+ if (!v?.input || !v?.output) throw '"input" and "output" are required when "io" is provided';
430
431
  } else if (k === 'promoteCache') {
431
432
  if (typeof v !== 'boolean') throw 'promoteCache should be a boolean';
432
433
  } else if (['maxLocalDatabaseSize', 'maxLocalFetchHttpSize'].includes(k)) {
@@ -507,7 +508,8 @@ export {
507
508
  DOCUMENT_EXTRACTION,
508
509
  FIND_GEO_JSON,
509
510
  GEO_JSON,
510
- AUTH_PROVIDER_ID
511
+ AUTH_PROVIDER_ID,
512
+ BSON
511
513
  };
512
514
 
513
515
  export default RNMT;
@@ -6,9 +6,9 @@ import { DatabaseRecordsListener } from "../../helpers/listeners";
6
6
  import cloneDeep from "lodash/cloneDeep";
7
7
  import { BSONRegExp, ObjectId, Timestamp } from "../../vendor/bson";
8
8
  import { niceGuard, Validator } from "guard-object";
9
- import { TIMESTAMP } from "../..";
9
+ import { TIMESTAMP } from "./types";
10
10
  import { docSize, incrementDatabaseSize } from "./counter";
11
- import { deserializeBSON, serializeToBase64 } from "./bson";
11
+ import { DatastoreParser, serializeToBase64 } from "./bson";
12
12
  import { FS_PATH, getSystem, useFS } from "../../helpers/fs_manager";
13
13
  import { grab, poke, unpoke } from "poke-object";
14
14
 
@@ -100,14 +100,13 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
100
100
  const editionSizeOffset = thisSize - (instanceData?.size || 0);
101
101
  const resultSizeOffset = isEpisode ? thisSize - (resultData?.size || 0) : 0;
102
102
 
103
- const newData = serializeToBase64({
103
+ const newData = DatastoreParser.encode({
104
104
  command,
105
105
  config,
106
106
  latest_limiter: limit,
107
- size: thisSize,
108
107
  data: value ? Array.isArray(value) ? value : [value] : []
109
108
  });
110
- const newResultData = isEpisode && serializeToBase64({
109
+ const newResultData = isEpisode && DatastoreParser.encode({
111
110
  data: value,
112
111
  size: thisSize
113
112
  });
@@ -195,7 +194,7 @@ export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
195
194
  isEpisode ? fs.find(LIMITER_RESULT(path), resultAccessId, ['value']) :
196
195
  fs.find(LIMITER_DATA(path), accessIdWithoutLimit, ['value'])
197
196
  ).catch(() => null);
198
- const thisData = qData && deserializeBSON(qData.value, true);
197
+ const thisData = qData && DatastoreParser.decode(qData.value);
199
198
 
200
199
  if (!thisData) return null;
201
200
 
@@ -283,7 +282,7 @@ const arrangeCommands = (c, removeLimit) => {
283
282
  if (c.sort) c.direction = [-1, 'desc', 'descending'].includes(c.direction) ? 'desc' : 'asc';
284
283
  if (c.find) c.find = sortFind(c.find);
285
284
  if (c.findOne) c.findOne = sortFind(c.findOne);
286
- if (removeLimit && 'limit' in c) delete c.limit;
285
+ if (removeLimit && ('limit' in c)) delete c.limit;
287
286
  return sortObject(c);
288
287
  };
289
288
 
@@ -380,12 +379,72 @@ export const addPendingWrites = async (builder, writeId, result) => {
380
379
  result = result && cloneDeep(result);
381
380
  await awaitStore();
382
381
 
382
+ const { projectUrl } = builder;
383
+ const pendingSnapshot = cloneDeep(result);
384
+ const { editions, linearWrite, pathChanges } = await syncCache(builder, result);
385
+
386
+ const isStaticWrite = !linearWrite.some(({ value, type }) => {
387
+ if (
388
+ [
389
+ 'updateOne',
390
+ 'updateMany',
391
+ 'mergeOne',
392
+ 'mergeMany'
393
+ ].includes(type)
394
+ ) {
395
+ const operators = Object.keys(value);
396
+ return ['$inc', '$min', '$max', '$mul', '$pop', '$pull', '$push', '$rename'].includes(operators);
397
+ }
398
+ });
399
+ const pureBuilder = {};
400
+
401
+ ['path', 'dbUrl', 'dbName', 'find', 'extraHeaders', 'maxRetries'].forEach(v => {
402
+ if (builder[v] !== undefined) pureBuilder[v] = builder[v];
403
+ });
404
+ pureBuilder.find = serializeToBase64({ _: pureBuilder.find });
405
+ pendingSnapshot.value = serializeToBase64({ _: pendingSnapshot.value });
406
+
407
+ let wasShifted;
408
+
409
+ if (isStaticWrite) {
410
+ // find previously matching pending write
411
+ const entries = Object.entries(CacheStore.PendingWrites[projectUrl] || {});
412
+
413
+ for (const [writeId, obj] of entries) {
414
+ if (!Scoped.OutgoingWrites[writeId]) {
415
+ if (
416
+ niceGuard(
417
+ { builder: obj.builder, snapshot: obj.snapshot },
418
+ { builder: pureBuilder, snapshot: pendingSnapshot }
419
+ )
420
+ ) {
421
+ // shift it to the back
422
+ obj.addedOn = Date.now();
423
+ wasShifted = true;
424
+ break;
425
+ }
426
+ }
427
+ }
428
+ }
429
+
430
+ if (!wasShifted)
431
+ poke(CacheStore.PendingWrites, [projectUrl, writeId], cloneDeep({
432
+ builder: pureBuilder,
433
+ snapshot: pendingSnapshot,
434
+ editions,
435
+ addedOn: Date.now()
436
+ }));
437
+
438
+ updateCacheStore(['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
439
+ notifyDatabaseNodeChanges(builder, [...pathChanges]);
440
+ };
441
+
442
+ const syncCache = async (builder, result) => {
383
443
  const { io } = Scoped.ReleaseCacheData;
384
444
  const { projectUrl, dbUrl, dbName } = builder;
385
- const editions = [];
386
445
  const duplicateSets = {};
446
+ const editions = [];
387
447
  const pathChanges = new Set([]);
388
- const pendingSnapshot = cloneDeep(result);
389
448
 
390
449
  const linearWrite =
391
450
  result.type === 'batchWrite' ?
@@ -394,6 +453,8 @@ export const addPendingWrites = async (builder, writeId, result) => {
394
453
  )
395
454
  : [{ ...result, find: builder.find, path: builder.path }];
396
455
 
456
+ const copiedWrite = cloneDeep(linearWrite);
457
+
397
458
  await Promise.all(linearWrite.map(async ({ value: writeObj, find, type, path }) => {
398
459
  WriteValidator[type]({ find, value: writeObj });
399
460
  validateCollectionName(path);
@@ -421,18 +482,18 @@ export const addPendingWrites = async (builder, writeId, result) => {
421
482
  await Promise.all(colListing.map(async ([access_id]) =>
422
483
  useFS(builder, access_id, 'database')(async fs => {
423
484
  const data = await fs.find(LIMITER_DATA(path), access_id, ['value'])
424
- .then(r => deserializeBSON(r.value, true));
485
+ .then(r => DatastoreParser.decode(r.value));
425
486
 
426
487
  await MutateDataInstance([access_id, data], path =>
427
488
  pathFinder[path] || (
428
489
  pathFinder[path] = fs.list(LIMITER_DATA(path), ['value'])
429
- .then(v => v.map(d => deserializeBSON(d[1].value, true).data).flat())
490
+ .then(v => v.map(d => DatastoreParser.decode(d[1].value).data).flat())
430
491
  .catch(() => [])
431
492
  )
432
493
  );
433
494
  await fs.set(LIMITER_DATA(path), access_id, {
434
495
  touched: Date.now(),
435
- value: serializeToBase64(data),
496
+ value: DatastoreParser.encode(data),
436
497
  size: data.size
437
498
  });
438
499
  })
@@ -639,132 +700,88 @@ export const addPendingWrites = async (builder, writeId, result) => {
639
700
  };
640
701
  }));
641
702
 
642
- const isStaticWrite = !linearWrite.some(({ value, type }) => {
643
- if (
644
- [
645
- 'updateOne',
646
- 'updateMany',
647
- 'mergeOne',
648
- 'mergeMany'
649
- ].includes(type)
650
- ) {
651
- const operators = Object.keys(value);
652
- return ['$inc', '$min', '$max', '$mul', '$pop', '$pull', '$push', '$rename'].includes(operators);
653
- }
654
- });
655
- const pureBuilder = {};
656
-
657
- ['path', 'dbUrl', 'dbName', 'find', 'extraHeaders'].forEach(v => {
658
- if (builder[v] !== undefined) pureBuilder[v] = builder[v];
659
- });
660
- pureBuilder.find = serializeToBase64({ _: pureBuilder.find });
661
- pendingSnapshot.value = serializeToBase64({ _: pendingSnapshot.value });
662
-
663
- let wasShifted;
664
-
665
- if (isStaticWrite) {
666
- // find previously matching pending write
667
- const entries = Object.entries(CacheStore.PendingWrites[projectUrl] || {});
703
+ return {
704
+ editions,
705
+ pathChanges: [...pathChanges],
706
+ linearWrite: copiedWrite
707
+ };
708
+ }
668
709
 
669
- for (const [writeId, obj] of entries) {
670
- if (!Scoped.OutgoingWrites[writeId]) {
671
- if (
672
- niceGuard(
673
- { builder: obj.builder, snapshot: obj.snapshot },
674
- { builder: pureBuilder, snapshot: pendingSnapshot }
675
- )
676
- ) {
677
- // shift it to the front
678
- obj.addedOn = Date.now();
679
- wasShifted = true;
680
- break;
681
- }
682
- }
683
- }
684
- }
710
+ export const removePendingWrite = async (builder, writeId, revert) => {
711
+ await awaitStore();
712
+ const { projectUrl } = builder;
713
+ const pendingData = grab(CacheStore.PendingWrites, [projectUrl, writeId]);
714
+ if (!pendingData) return;
685
715
 
686
- if (!wasShifted)
687
- poke(CacheStore.PendingWrites, [projectUrl, writeId], cloneDeep({
688
- builder: pureBuilder,
689
- snapshot: pendingSnapshot,
690
- editions,
691
- addedOn: Date.now()
692
- }));
716
+ const pathChanges = revert ? revertChanges(builder, pendingData.editions) : [];
693
717
 
694
- updateCacheStore(['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
718
+ unpoke(CacheStore.PendingWrites, [projectUrl, writeId]);
719
+ updateCacheStore(['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
695
720
  notifyDatabaseNodeChanges(builder, [...pathChanges]);
696
721
  };
697
722
 
698
- export const removePendingWrite = async (builder, writeId, revert) => {
699
- await awaitStore();
700
- const { projectUrl, dbUrl, dbName } = builder;
701
- const pendingData = grab(CacheStore.PendingWrites, [projectUrl, writeId]);
723
+ const revertChanges = async (builder, pendingData) => {
702
724
  const { io } = Scoped.ReleaseCacheData;
703
-
704
- if (!pendingData) return;
725
+ const { projectUrl, dbUrl, dbName } = builder;
705
726
  const pathChanges = new Set([]);
706
727
 
707
- if (revert) {
708
- await Promise.all(pendingData.editions.map(async ([access_id, [b4Doc, afDoc], path]) => {
709
- if (io) {
710
- RevertMutation(grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]));
711
- } else {
712
- await useFS(builder, access_id, 'database')(async fs => {
713
- const colObj = await fs.find(LIMITER_DATA(path), access_id, ['value'])
714
- .then(v => deserializeBSON(v.value, true))
715
- .catch(() => null);
716
- if (!colObj) return;
717
- RevertMutation(colObj);
718
- await fs.set(LIMITER_DATA(path), access_id, {
719
- value: serializeToBase64(colObj),
720
- touched: Date.now(),
721
- size: colObj.size
722
- });
728
+ await Promise.all(pendingData.map(async ([access_id, [b4Doc, afDoc], path]) => {
729
+ if (io) {
730
+ RevertMutation(grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]));
731
+ } else {
732
+ await useFS(builder, access_id, 'database')(async fs => {
733
+ const colObj = await fs.find(LIMITER_DATA(path), access_id, ['value'])
734
+ .then(v => DatastoreParser.decode(v.value))
735
+ .catch(() => null);
736
+ if (!colObj) return;
737
+ RevertMutation(colObj);
738
+ await fs.set(LIMITER_DATA(path), access_id, {
739
+ value: DatastoreParser.encode(colObj),
740
+ touched: Date.now(),
741
+ size: colObj.size
723
742
  });
724
- }
743
+ });
744
+ }
725
745
 
726
- function RevertMutation(colObj) {
727
- const colList = colObj?.data;
746
+ function RevertMutation(colObj) {
747
+ const colList = colObj?.data;
728
748
 
729
- const updateSize = (b4, af) => {
730
- const offset = docSize(af) - docSize(b4);
731
- colObj.size += offset;
732
- incrementDatabaseSize(builder, path, offset);
733
- }
749
+ const updateSize = (b4, af) => {
750
+ const offset = docSize(af) - docSize(b4);
751
+ colObj.size += offset;
752
+ incrementDatabaseSize(builder, path, offset);
753
+ }
734
754
 
735
- if (colList) {
736
- if (afDoc) {
737
- const editedIndex = colList.findIndex(e => CompareBson.equal(e._id, afDoc._id));
738
- if (editedIndex !== -1) {
739
- if (
740
- serializeToBase64(afDoc) === serializeToBase64(colList[editedIndex])
741
- ) {
742
- if (b4Doc) {
743
- colList[editedIndex] = b4Doc;
744
- updateSize(afDoc, b4Doc);
745
- } else {
746
- colList.splice(editedIndex, 1);
747
- updateSize(afDoc, undefined);
748
- }
755
+ if (colList) {
756
+ if (afDoc) {
757
+ const editedIndex = colList.findIndex(e => CompareBson.equal(e._id, afDoc._id));
758
+ if (editedIndex !== -1) {
759
+ if (
760
+ serializeToBase64(afDoc) === serializeToBase64(colList[editedIndex])
761
+ ) {
762
+ if (b4Doc) {
763
+ colList[editedIndex] = b4Doc;
764
+ updateSize(afDoc, b4Doc);
765
+ } else {
766
+ colList.splice(editedIndex, 1);
767
+ updateSize(afDoc, undefined);
749
768
  }
750
769
  }
751
- } else if (
752
- b4Doc &&
753
- colList.findIndex(e => CompareBson.equal(e._id, b4Doc._id)) === -1
754
- ) {
755
- colList.push(b4Doc);
756
- updateSize(undefined, b4Doc);
757
770
  }
771
+ } else if (
772
+ b4Doc &&
773
+ colList.findIndex(e => CompareBson.equal(e._id, b4Doc._id)) === -1
774
+ ) {
775
+ colList.push(b4Doc);
776
+ updateSize(undefined, b4Doc);
758
777
  }
759
- pathChanges.add(path);
760
778
  }
761
- }));
762
- }
779
+ pathChanges.add(path);
780
+ }
781
+ }));
763
782
 
764
- unpoke(CacheStore.PendingWrites, [projectUrl, writeId]);
765
- updateCacheStore(['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
766
- notifyDatabaseNodeChanges(builder, [...pathChanges]);
767
- };
783
+ return [...pendingData];
784
+ }
768
785
 
769
786
  const notifyDatabaseNodeChanges = (builder, changedCollections = []) => {
770
787
  const { projectUrl, dbName, dbUrl } = builder;
@@ -1,3 +1,4 @@
1
+ import cloneDeep from "lodash/cloneDeep";
1
2
  import { deserialize, serialize } from "../../vendor/bson";
2
3
  import { Buffer } from "buffer";
3
4
 
@@ -13,4 +14,45 @@ export const deserializeBSON = (data, cast) => {
13
14
  });
14
15
  };
15
16
 
16
- export const serializeToBase64 = doc => Buffer.from(serialize(doc)).toString('base64');
17
+ export const serializeToBase64 = doc => Buffer.from(serialize(doc)).toString('base64');
18
+
19
+ export const DatastoreParser = {
20
+ encode: (obj) => {
21
+ obj = cloneDeep(obj);
22
+ const { command, config } = obj;
23
+
24
+ const serializeQuery = (e) =>
25
+ ['find', 'findOne'].forEach(n => {
26
+ if (e?.[n]) e[n] = serializeToBase64({ _: e[n] });
27
+ });
28
+
29
+ if (command) serializeQuery(command);
30
+ if (config) {
31
+ if (config.extraction)
32
+ (Array.isArray(config.extraction) ? config.extraction : [config.extraction]).forEach(e => {
33
+ serializeQuery(e);
34
+ });
35
+ }
36
+ if (obj.data) obj.data = serializeToBase64({ _: obj.data });
37
+ return obj;
38
+ },
39
+ decode: (obj, cast = true) => {
40
+ obj = cloneDeep(obj);
41
+ const { command, config } = obj;
42
+
43
+ const serializeQuery = (e) =>
44
+ ['find', 'findOne'].forEach(n => {
45
+ if (e?.[n]) e[n] = deserializeBSON(e[n], cast)._;
46
+ });
47
+
48
+ if (command) serializeQuery(command);
49
+ if (config) {
50
+ if (config.extraction)
51
+ (Array.isArray(config.extraction) ? config.extraction : [config.extraction]).forEach(e => {
52
+ serializeQuery(e);
53
+ });
54
+ }
55
+ if (obj.data) obj.data = deserializeBSON(obj.data, cast)._;
56
+ return obj;
57
+ }
58
+ };
@@ -395,6 +395,7 @@ const countCollection = async (builder, config) => {
395
395
  const b4Data = await getCountQuery(builder, accessId).catch(() => null);
396
396
 
397
397
  if (e?.simpleError) {
398
+ e.simpleError.ack = true;
398
399
  finalize(undefined, e.simpleError);
399
400
  } else if (!disableCache && Validator.NUMBER(b4Data)) {
400
401
  finalize(b4Data);
@@ -552,6 +553,7 @@ const findObject = async (builder, config) => {
552
553
  (thisRecord = [await getRecordData()])[0];
553
554
 
554
555
  if (e?.simpleError) {
556
+ e.simpleError.ack = true;
555
557
  finalize(undefined, e?.simpleError);
556
558
  } else if (
557
559
  (retrieval === RETRIEVAL.CACHE_NO_AWAIT && !(await getThisRecord())) ||
@@ -689,7 +691,8 @@ const commitData = async (builder, value, type, config) => {
689
691
  } catch (e) {
690
692
  if (e?.simpleError) {
691
693
  console.error(`${type} error (${path}), ${e.simpleError?.message}`);
692
- finalize(undefined, e?.simpleError, { removeCache: true, revertCache: true });
694
+ e.simpleError.ack = true;
695
+ finalize(undefined, e.simpleError, { removeCache: true, revertCache: true });
693
696
  } else if (delivery === DELIVERY.NO_CACHE_NO_AWAIT) {
694
697
  finalize(undefined, simplifyCaughtError(e).simpleError);
695
698
  } else if (retries > maxRetries) {
@@ -738,9 +741,9 @@ export const trySendPendingWrite = (projectUrl) => {
738
741
 
739
742
  delete CacheStore.PendingWrites[projectUrl][writeId];
740
743
  ++resolveCounts;
741
- } catch (_) {
744
+ } catch (err) {
742
745
  const { maxRetries } = builder;
743
- if (!maxRetries || attempts >= maxRetries) {
746
+ if (err?.ack || !maxRetries || attempts >= maxRetries) {
744
747
  delete CacheStore.PendingWrites[projectUrl][writeId];
745
748
  ++resolveCounts;
746
749
  } else if (CacheStore.PendingWrites[projectUrl]?.[writeId]) {
@@ -749,8 +752,7 @@ export const trySendPendingWrite = (projectUrl) => {
749
752
  }
750
753
  }
751
754
  resolve();
752
- if (projectUrl in Scoped.dispatchingWritesPromise)
753
- delete Scoped.dispatchingWritesPromise[projectUrl];
755
+ Scoped.dispatchingWritesPromise[projectUrl] = undefined;
754
756
  updateCacheStore(['PendingWrites']);
755
757
 
756
758
  if (
@@ -1,4 +1,4 @@
1
- import { guardObject, GuardSignal, niceGuard } from "guard-object";
1
+ import { niceGuard } from "guard-object";
2
2
 
3
3
  export const TIMESTAMP = { $timestamp: "now" };
4
4
  export const TIMESTAMP_OFFSET = (v) => ({ $timestamp_offset: v });
@@ -12,17 +12,13 @@ export const GEO_JSON = (lat, lng) => ({
12
12
  coordinates: [lng, lat],
13
13
  });
14
14
 
15
- export const FIND_GEO_JSON = (location, offSetMeters, centerMeters) => {
16
- guardObject(GuardSignal.COORDINATE.LAT_LNG_INT).validate(location);
17
-
18
- return {
19
- $nearSphere: {
20
- $geometry: {
21
- type: "Point",
22
- coordinates: location.slice(0).reverse()
23
- },
24
- $minDistance: centerMeters || 0,
25
- $maxDistance: offSetMeters
26
- }
27
- };
28
- };
15
+ export const FIND_GEO_JSON = (location, offSetMeters, centerMeters) => ({
16
+ $nearSphere: {
17
+ $geometry: {
18
+ type: "Point",
19
+ coordinates: location.slice(0).reverse()
20
+ },
21
+ $minDistance: centerMeters || 0,
22
+ $maxDistance: offSetMeters
23
+ }
24
+ });
@@ -40,6 +40,7 @@ export const getFetchResources = async (projectUrl, access_id) => {
40
40
  if (io) {
41
41
  const record = CacheStore.FetchedStore[projectUrl]?.[access_id];
42
42
  if (record) record.touched = Date.now();
43
+ updateCacheStore(['FetchedStore']);
43
44
  return record && cloneDeep(record?.data);
44
45
  }
45
46
 
@@ -11,9 +11,9 @@ import { serialize } from "entity-serializer";
11
11
  import { getFetchResources, insertFetchResources } from "./accessor";
12
12
 
13
13
  const buildFetchData = (data, extras) => {
14
- const { ok, type, status, statusText, redirected, url, headers, size, base64 } = data;
14
+ const { ok, type, status, statusText, redirected, url, headers, size, buffer } = data;
15
15
 
16
- const response = new Response(Buffer.from(base64, 'base64'), {
16
+ const response = new Response(buffer, {
17
17
  headers: new Headers(headers),
18
18
  status,
19
19
  statusText,
@@ -146,7 +146,7 @@ export const mfetch = async (input = '', init, config) => {
146
146
  credentials: 'omit',
147
147
  ...init,
148
148
  ...uglified ? { body: reqBuilder } : encodeBody ? { body: serialize(body) } : {},
149
- // cache: 'no-cache',
149
+ cache: 'no-cache',
150
150
  headers: {
151
151
  ...extraHeaders,
152
152
  ...rawHeader,
@@ -165,12 +165,12 @@ export const mfetch = async (input = '', init, config) => {
165
165
 
166
166
  if (!isLink && simple) throw { simpleError: JSON.parse(simple) };
167
167
 
168
- const base64 = uglified ?
169
- Buffer.from(await deserializeE2E(await f.arrayBuffer(), serverE2E_PublicKey, privateKey)).toString('base64') :
170
- Buffer.from(await f.arrayBuffer()).toString('base64');
168
+ const buffer = uglified ?
169
+ Buffer.from(await deserializeE2E(await f.arrayBuffer(), serverE2E_PublicKey, privateKey)) :
170
+ Buffer.from(await f.arrayBuffer());
171
171
 
172
172
  const resObj = {
173
- base64,
173
+ buffer,
174
174
  type,
175
175
  status,
176
176
  statusText,