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 +1 -1
- package/src/helpers/fs_manager.js +4 -2
- package/src/helpers/purger.js +12 -6
- package/src/helpers/utils.js +70 -20
- package/src/helpers/values.js +0 -1
- package/src/index.d.ts +5 -13
- package/src/index.js +4 -2
- package/src/products/database/accessor.js +136 -119
- package/src/products/database/bson.js +43 -1
- package/src/products/database/index.js +7 -5
- package/src/products/database/types.js +11 -15
- package/src/products/http_callable/accessor.js +1 -0
- package/src/products/http_callable/index.js +7 -7
package/package.json
CHANGED
|
@@ -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),
|
|
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,
|
|
61
|
+
[node, deserialize(Buffer.from(await FileSystem.readFile(joinPath(path, node), 'base64'), 'base64'))]
|
|
60
62
|
));
|
|
61
63
|
return Object.fromEntries(value_map);
|
|
62
64
|
},
|
package/src/helpers/purger.js
CHANGED
|
@@ -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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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]) => {
|
package/src/helpers/utils.js
CHANGED
|
@@ -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 {
|
|
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:
|
|
32
|
-
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' ?
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
73
|
-
|
|
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('
|
|
127
|
+
console.error('releaseCacheStore data err:', e);
|
|
79
128
|
}
|
|
80
129
|
|
|
81
130
|
Object.entries(data).forEach(([k, v]) => {
|
|
82
|
-
|
|
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);
|
package/src/helpers/values.js
CHANGED
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' | '
|
|
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
|
|
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 {
|
|
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 =
|
|
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 &&
|
|
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 &&
|
|
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 =>
|
|
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 =>
|
|
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:
|
|
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
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
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
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
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
|
-
|
|
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
|
-
|
|
718
|
+
unpoke(CacheStore.PendingWrites, [projectUrl, writeId]);
|
|
719
|
+
updateCacheStore(['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
|
|
695
720
|
notifyDatabaseNodeChanges(builder, [...pathChanges]);
|
|
696
721
|
};
|
|
697
722
|
|
|
698
|
-
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
await
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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
|
-
|
|
727
|
-
|
|
746
|
+
function RevertMutation(colObj) {
|
|
747
|
+
const colList = colObj?.data;
|
|
728
748
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
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
|
-
|
|
765
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,
|
|
14
|
+
const { ok, type, status, statusText, redirected, url, headers, size, buffer } = data;
|
|
15
15
|
|
|
16
|
-
const response = new Response(
|
|
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
|
-
|
|
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
|
|
169
|
-
Buffer.from(await deserializeE2E(await f.arrayBuffer(), serverE2E_PublicKey, privateKey))
|
|
170
|
-
Buffer.from(await f.arrayBuffer())
|
|
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
|
-
|
|
173
|
+
buffer,
|
|
174
174
|
type,
|
|
175
175
|
status,
|
|
176
176
|
statusText,
|