react-native-mosquito-transport 0.0.33 → 0.0.35
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 +6 -6
- package/src/helpers/fs_manager.js +13 -13
- package/src/helpers/peripherals.js +5 -5
- package/src/helpers/purger.js +5 -5
- package/src/helpers/utils.js +2 -7
- package/src/helpers/values.js +1 -1
- package/src/helpers/variables.js +2 -9
- package/src/index.js +1 -1
- package/src/products/auth/index.js +1 -1
- package/src/products/database/accessor.js +103 -57
- package/src/products/database/bson.js +1 -1
- package/src/products/database/counter.js +5 -6
- package/src/products/database/index.js +22 -11
- package/src/products/database/types.js +16 -11
- package/src/products/database/validator.js +5 -5
- package/src/products/http_callable/accessor.js +1 -1
- package/src/products/http_callable/counter.js +3 -4
- package/src/products/http_callable/index.js +1 -1
- package/src/vendor/bson.d.ts +1723 -0
- package/src/vendor/bson.js +4650 -0
- package/src/vendor/encoder.js +37 -0
- package/src/vendor/utf8-validator.js +60 -0
- package/src/vendor/utf8-validator.test.js +35 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mosquito-transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
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",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"homepage": "https://github.com/brainbehindx/react-native-mosquito-transport#readme",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@turf/turf": "^7.2.0",
|
|
31
|
-
"bson": "^6.8.0",
|
|
32
31
|
"buffer": "^6.0.3",
|
|
33
32
|
"entity-serializer": "^1.0.3",
|
|
34
33
|
"guard-object": "^1.1.4",
|
|
35
34
|
"lodash": "^4.17.21",
|
|
35
|
+
"poke-object": "^1.0.1",
|
|
36
36
|
"simplify-error": "^1.0.1",
|
|
37
37
|
"socket.io-client": "^4.8.1",
|
|
38
38
|
"subscription-listener": "^1.1.2",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"react-native": "*",
|
|
43
|
-
"react-native-
|
|
44
|
-
"react-native-
|
|
45
|
-
"react-native-
|
|
43
|
+
"react-native-file-access": "*",
|
|
44
|
+
"react-native-get-random-values": "*",
|
|
45
|
+
"react-native-sha256": "*"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Scoped } from "./variables";
|
|
2
2
|
import { Platform } from "react-native";
|
|
3
|
-
import {
|
|
3
|
+
import { Dirs, FileSystem } from "react-native-file-access";
|
|
4
4
|
|
|
5
|
-
const PARENT_FOLDER = `${Platform.OS === 'android' ?
|
|
5
|
+
const PARENT_FOLDER = `${Platform.OS === 'android' ? Dirs.DocumentDir.split('/').slice(0, -1).join('/') : Dirs.MainBundleDir}/mosquito_base`;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* this method linearize read/write for individual access_id on the file system ensuring consistency across concurrent operations
|
|
@@ -16,7 +16,7 @@ export const useFS = (builder, access_id, node) => async (task) => {
|
|
|
16
16
|
const { projectUrl, dbUrl, dbName } = builder;
|
|
17
17
|
const nodeId = typeof builder === 'string' ? `${builder}_${access_id}` : `${projectUrl}_${dbUrl}_${dbName}_${access_id}`;
|
|
18
18
|
|
|
19
|
-
const thatProcess = Scoped.
|
|
19
|
+
const thatProcess = Scoped.linearFsProcess[node][nodeId];
|
|
20
20
|
|
|
21
21
|
const thisPromise = new Promise(async (resolve, reject) => {
|
|
22
22
|
try {
|
|
@@ -28,12 +28,12 @@ export const useFS = (builder, access_id, node) => async (task) => {
|
|
|
28
28
|
console.error('useFS err:', error, ' builder:', builder);
|
|
29
29
|
reject(error);
|
|
30
30
|
} finally {
|
|
31
|
-
if (Scoped.
|
|
32
|
-
delete Scoped.
|
|
31
|
+
if (Scoped.linearFsProcess[node][nodeId] === thisPromise)
|
|
32
|
+
delete Scoped.linearFsProcess[node][nodeId];
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
Scoped.
|
|
36
|
+
Scoped.linearFsProcess[node][nodeId] = thisPromise;
|
|
37
37
|
return (await thisPromise);
|
|
38
38
|
};
|
|
39
39
|
|
|
@@ -46,24 +46,24 @@ export const getSystem = (builder) => {
|
|
|
46
46
|
return {
|
|
47
47
|
set: async (table, primary_key, value) => {
|
|
48
48
|
const path = conjoin(table, primary_key);
|
|
49
|
-
await mkdir(path).catch(() => null);
|
|
49
|
+
await FileSystem.mkdir(path).catch(() => null);
|
|
50
50
|
await Promise.all(Object.entries(value).map(([k, v]) =>
|
|
51
|
-
writeFile(joinPath(path, k), JSON.stringify(v), 'utf8')
|
|
51
|
+
FileSystem.writeFile(joinPath(path, k), JSON.stringify(v), 'utf8')
|
|
52
52
|
));
|
|
53
53
|
},
|
|
54
|
-
delete: (table, primary_key) => unlink(conjoin(table, primary_key)),
|
|
54
|
+
delete: (table, primary_key) => FileSystem.unlink(conjoin(table, primary_key)),
|
|
55
55
|
find: async (table, primary_key, extractions) => {
|
|
56
56
|
const path = conjoin(table, primary_key);
|
|
57
57
|
|
|
58
58
|
const value_map = await Promise.all(extractions.map(async node =>
|
|
59
|
-
[node, JSON.parse(await readFile(joinPath(path, node)
|
|
59
|
+
[node, JSON.parse(await FileSystem.readFile(joinPath(path, node), 'utf8'))]
|
|
60
60
|
));
|
|
61
61
|
return Object.fromEntries(value_map);
|
|
62
62
|
},
|
|
63
63
|
list: async (table, extractions) => {
|
|
64
|
-
const names = await
|
|
64
|
+
const names = await FileSystem.ls(conjoin(table));
|
|
65
65
|
const list_data = await Promise.all(names.map(async primary_key => {
|
|
66
|
-
const obj = await
|
|
66
|
+
const obj = await getSystem(builder).find(table, primary_key, extractions)
|
|
67
67
|
.catch(() => null);
|
|
68
68
|
if (!obj) return;
|
|
69
69
|
return [primary_key, obj];
|
|
@@ -74,7 +74,7 @@ export const getSystem = (builder) => {
|
|
|
74
74
|
};
|
|
75
75
|
};
|
|
76
76
|
|
|
77
|
-
function purifyFilepath(filename) {
|
|
77
|
+
export function purifyFilepath(filename) {
|
|
78
78
|
if (!filename || typeof filename !== 'string')
|
|
79
79
|
throw `invalid filename:${filename}`;
|
|
80
80
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Buffer } from "buffer";
|
|
2
2
|
import { ServerReachableListener } from "./listeners";
|
|
3
3
|
import naclPkg from 'tweetnacl';
|
|
4
|
-
import getLodash from "lodash/get";
|
|
5
4
|
import { deserialize, serialize } from "entity-serializer";
|
|
6
5
|
import { sha256 } from 'react-native-sha256';
|
|
6
|
+
import { purifyFilepath } from "./fs_manager";
|
|
7
|
+
import { grab } from "poke-object";
|
|
7
8
|
|
|
8
9
|
const { box, randomBytes } = naclPkg;
|
|
9
10
|
|
|
@@ -58,8 +59,8 @@ export const shuffleArray = (n) => {
|
|
|
58
59
|
|
|
59
60
|
export function sortArrayByObjectKey(arr = [], key) {
|
|
60
61
|
return arr.sort(function (a, b) {
|
|
61
|
-
const left =
|
|
62
|
-
right =
|
|
62
|
+
const left = grab(a, key),
|
|
63
|
+
right = grab(b, key);
|
|
63
64
|
|
|
64
65
|
return (left > right) ? 1 : (left < right) ? -1 : 0;
|
|
65
66
|
});
|
|
@@ -67,8 +68,7 @@ export function sortArrayByObjectKey(arr = [], key) {
|
|
|
67
68
|
|
|
68
69
|
export async function niceHash(str = '') {
|
|
69
70
|
const hash = Buffer.from(await sha256(str), 'hex').toString('base64');
|
|
70
|
-
|
|
71
|
-
return hash;
|
|
71
|
+
return purifyFilepath(hash);
|
|
72
72
|
};
|
|
73
73
|
|
|
74
74
|
export const sameInstance = (var1, var2) => {
|
package/src/helpers/purger.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Validator } from "guard-object";
|
|
2
2
|
import { incrementDatabaseSizeCore } from "../products/database/counter";
|
|
3
3
|
import { incrementFetcherSizeCore } from "../products/http_callable/counter";
|
|
4
|
-
import unsetLodash from 'lodash/unset';
|
|
5
4
|
import { FS_PATH, getSystem } from "./fs_manager";
|
|
5
|
+
import { unpoke } from "poke-object";
|
|
6
6
|
|
|
7
7
|
const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY, FETCH_RESOURCES } = FS_PATH;
|
|
8
8
|
|
|
@@ -86,13 +86,13 @@ export const purgeRedundantRecords = async (data, builder) => {
|
|
|
86
86
|
}) => {
|
|
87
87
|
const { projectUrl, dbUrl, dbName } = builder;
|
|
88
88
|
if (isCount) {
|
|
89
|
-
|
|
89
|
+
unpoke(data.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id]);
|
|
90
90
|
} else {
|
|
91
91
|
incrementDatabaseSizeCore(data.DatabaseStats, builder, path, -size);
|
|
92
92
|
if (isEpisode) {
|
|
93
|
-
|
|
93
|
+
unpoke(data.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', access_id, `${limit}`]);
|
|
94
94
|
} else {
|
|
95
|
-
|
|
95
|
+
unpoke(data.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]);
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
});
|
|
@@ -122,7 +122,7 @@ export const purgeRedundantRecords = async (data, builder) => {
|
|
|
122
122
|
console.warn(`purging ${cuts} of ${redundantFetchRanking.length} fetcher entities`);
|
|
123
123
|
redundantFetchRanking.slice(0, cuts).forEach(({ access_id, data: { size }, projectUrl }) => {
|
|
124
124
|
incrementFetcherSizeCore(data.DatabaseStats, projectUrl, -size);
|
|
125
|
-
|
|
125
|
+
unpoke(data.FetchedStore, [projectUrl, access_id]);
|
|
126
126
|
});
|
|
127
127
|
console.log('fetcher purging complete');
|
|
128
128
|
}
|
package/src/helpers/utils.js
CHANGED
|
@@ -2,7 +2,6 @@ import { ServerReachableListener, StoreReadyListener } from "./listeners";
|
|
|
2
2
|
import { CacheStore, Scoped } from "./variables";
|
|
3
3
|
import { serializeE2E } from "./peripherals";
|
|
4
4
|
import { deserializeBSON, serializeToBase64 } from "../products/database/bson";
|
|
5
|
-
import { trySendPendingWrite } from "../products/database";
|
|
6
5
|
import { deserialize } from "entity-serializer";
|
|
7
6
|
import { purgeRedundantRecords } from "./purger";
|
|
8
7
|
import { FS_PATH, getSystem } from "./fs_manager";
|
|
@@ -37,7 +36,7 @@ export const updateCacheStore = async (node) => {
|
|
|
37
36
|
|
|
38
37
|
io.output(txt, node);
|
|
39
38
|
} else {
|
|
40
|
-
// use
|
|
39
|
+
// use fs
|
|
41
40
|
const exclusion = ['DatabaseStore', 'DatabaseCountResult', 'FetchedStore'];
|
|
42
41
|
const updationKey = (node ? Array.isArray(node) ? node : [node] : CacheKeys).filter(v => !exclusion.includes(v));
|
|
43
42
|
|
|
@@ -71,7 +70,7 @@ export const releaseCacheStore = async (builder) => {
|
|
|
71
70
|
)
|
|
72
71
|
);
|
|
73
72
|
} catch (error) {
|
|
74
|
-
console.error('initializeCache
|
|
73
|
+
console.error('initializeCache data release err:', error);
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
await purgeRedundantRecords(data, builder);
|
|
@@ -87,10 +86,6 @@ export const releaseCacheStore = async (builder) => {
|
|
|
87
86
|
Object.entries(CacheStore.AuthStore).forEach(([key, value]) => {
|
|
88
87
|
Scoped.AuthJWTToken[key] = value?.token;
|
|
89
88
|
});
|
|
90
|
-
Object.keys(CacheStore.PendingWrites).forEach(projectUrl => {
|
|
91
|
-
if (Scoped.IS_CONNECTED[projectUrl])
|
|
92
|
-
trySendPendingWrite(projectUrl);
|
|
93
|
-
});
|
|
94
89
|
Scoped.IsStoreReady = true;
|
|
95
90
|
StoreReadyListener.dispatch('_', 'ready');
|
|
96
91
|
};
|
package/src/helpers/values.js
CHANGED
|
@@ -12,7 +12,7 @@ export const RETRIEVAL = {
|
|
|
12
12
|
|
|
13
13
|
export const DELIVERY = {
|
|
14
14
|
DEFAULT: 'default',
|
|
15
|
-
CACHE_AWAIT: 'cache-await',
|
|
15
|
+
CACHE_AWAIT: 'cache-await', // TODO: remove
|
|
16
16
|
CACHE_NO_AWAIT: 'cache-no-await',
|
|
17
17
|
NO_CACHE_NO_AWAIT: 'no-cache-no-await',
|
|
18
18
|
NO_CACHE_AWAIT: 'no-cache-await'
|
package/src/helpers/variables.js
CHANGED
|
@@ -20,21 +20,14 @@ export const Scoped = {
|
|
|
20
20
|
/**
|
|
21
21
|
* @type {Promise<any> | undefined}
|
|
22
22
|
*/
|
|
23
|
-
dispatchingWritesPromise:
|
|
24
|
-
|
|
23
|
+
dispatchingWritesPromise: {},
|
|
24
|
+
linearFsProcess: {
|
|
25
25
|
database: {},
|
|
26
26
|
dbQueryCount: {},
|
|
27
27
|
httpFetch: {}
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export const SqliteCollective = {
|
|
32
|
-
openedDb: {},
|
|
33
|
-
openedDbProcess: {},
|
|
34
|
-
closeDbPromises: {},
|
|
35
|
-
openedDbReducerTimer: {}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
31
|
export const CacheStore = {
|
|
39
32
|
DatabaseStore: {},
|
|
40
33
|
DatabaseCountResult: {},
|
package/src/index.js
CHANGED
|
@@ -82,7 +82,7 @@ class RNMT {
|
|
|
82
82
|
if (queuedToken) updateMountedToken(queuedToken.token);
|
|
83
83
|
ServerReachableListener.dispatch(projectUrl, true);
|
|
84
84
|
awaitStore().then(() => {
|
|
85
|
-
trySendPendingWrite(projectUrl);
|
|
85
|
+
if (isConnected) trySendPendingWrite(projectUrl);
|
|
86
86
|
});
|
|
87
87
|
};
|
|
88
88
|
const onDisconnect = () => {
|
|
@@ -237,7 +237,7 @@ const purgeCache = (url, isMain) => {
|
|
|
237
237
|
'AuthStore',
|
|
238
238
|
'PendingWrites'
|
|
239
239
|
].forEach(e => {
|
|
240
|
-
if (e && CacheStore[e][url]) delete CacheStore[e][url];
|
|
240
|
+
if (e && CacheStore[e]?.[url]) delete CacheStore[e][url];
|
|
241
241
|
});
|
|
242
242
|
TokenRefreshListener.dispatch(url);
|
|
243
243
|
triggerAuthToken(url);
|
|
@@ -2,17 +2,15 @@ import { niceHash, shuffleArray, sortArrayByObjectKey } from "../../helpers/peri
|
|
|
2
2
|
import { awaitStore, updateCacheStore } from "../../helpers/utils";
|
|
3
3
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
4
4
|
import { assignExtractionFind, CompareBson, confirmFilterDoc, defaultBSON, downcastBSON, validateCollectionName, validateFilter } from "./validator";
|
|
5
|
-
import getLodash from 'lodash/get';
|
|
6
|
-
import setLodash from 'lodash/set';
|
|
7
|
-
import unsetLodash from 'lodash/unset';
|
|
8
5
|
import { DatabaseRecordsListener } from "../../helpers/listeners";
|
|
9
6
|
import cloneDeep from "lodash/cloneDeep";
|
|
10
|
-
import { BSONRegExp, ObjectId, Timestamp } from "
|
|
7
|
+
import { BSONRegExp, ObjectId, Timestamp } from "../../vendor/bson";
|
|
11
8
|
import { niceGuard, Validator } from "guard-object";
|
|
12
9
|
import { TIMESTAMP } from "../..";
|
|
13
10
|
import { docSize, incrementDatabaseSize } from "./counter";
|
|
14
11
|
import { deserializeBSON, serializeToBase64 } from "./bson";
|
|
15
12
|
import { FS_PATH, getSystem, useFS } from "../../helpers/fs_manager";
|
|
13
|
+
import { grab, poke, unpoke } from "poke-object";
|
|
16
14
|
|
|
17
15
|
const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY } = FS_PATH;
|
|
18
16
|
|
|
@@ -48,12 +46,12 @@ export const insertCountQuery = async (builder, access_id, value) => {
|
|
|
48
46
|
|
|
49
47
|
const { io } = Scoped.ReleaseCacheData;
|
|
50
48
|
if (io) {
|
|
51
|
-
|
|
49
|
+
poke(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id], { value, touched: Date.now() });
|
|
52
50
|
updateCacheStore(['DatabaseCountResult']);
|
|
53
51
|
} else {
|
|
54
52
|
await useFS(builder, access_id, 'dbQueryCount')(async fs => {
|
|
55
53
|
await fs.set(DB_COUNT_QUERY(path), access_id, { value, touched: Date.now() });
|
|
56
|
-
|
|
54
|
+
poke(CacheStore.DatabaseStats.counters, [projectUrl, dbUrl, dbName, path], true);
|
|
57
55
|
});
|
|
58
56
|
updateCacheStore(['DatabaseStats']);
|
|
59
57
|
}
|
|
@@ -64,15 +62,17 @@ export const getCountQuery = async (builder, access_id) => {
|
|
|
64
62
|
const { io } = Scoped.ReleaseCacheData;
|
|
65
63
|
|
|
66
64
|
if (io) {
|
|
67
|
-
const data =
|
|
65
|
+
const data = grab(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id]);
|
|
68
66
|
if (data) data.touched = Date.now();
|
|
69
67
|
return data && data.value;
|
|
70
68
|
} else {
|
|
71
69
|
return useFS(builder, access_id, 'dbQueryCount')(async fs => {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
const data = await fs.find(DB_COUNT_QUERY(path), access_id, ['value']).catch(() => null);
|
|
71
|
+
if (data) {
|
|
72
|
+
await fs.set(DB_COUNT_QUERY(path), access_id, { touched: Date.now() });
|
|
73
|
+
return data.value;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -132,8 +132,8 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
|
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
const instanceData =
|
|
136
|
-
const resultData =
|
|
135
|
+
const instanceData = grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit]);
|
|
136
|
+
const resultData = grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, `${limit}`]);
|
|
137
137
|
const isEpisode = episode === 1 || !!resultData;
|
|
138
138
|
|
|
139
139
|
const editionSizeOffset = thisSize - (instanceData?.size || 0);
|
|
@@ -155,8 +155,8 @@ export const insertRecord = async (builder, config, accessIdWithoutLimit, value,
|
|
|
155
155
|
|
|
156
156
|
incrementDatabaseSize(builder, path, editionSizeOffset + resultSizeOffset);
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
if (isEpisode)
|
|
158
|
+
poke(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit], newData);
|
|
159
|
+
if (isEpisode) poke(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, `${limit}`], cloneDeep(newResultData));
|
|
160
160
|
updateCacheStore(['DatabaseStore', 'DatabaseStats']);
|
|
161
161
|
};
|
|
162
162
|
|
|
@@ -219,7 +219,7 @@ export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
|
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
if (isEpisode) {
|
|
222
|
-
const resultData =
|
|
222
|
+
const resultData = grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, `${limit}`]);
|
|
223
223
|
if (resultData) {
|
|
224
224
|
resultData.touched = Date.now();
|
|
225
225
|
return [cloneDeep(resultData.data)];
|
|
@@ -227,7 +227,7 @@ export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
|
|
|
227
227
|
return null;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
const instanceData =
|
|
230
|
+
const instanceData = grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit]);
|
|
231
231
|
if (!instanceData) return null;
|
|
232
232
|
const { latest_limiter, data } = instanceData;
|
|
233
233
|
|
|
@@ -387,19 +387,20 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
387
387
|
const pathChanges = new Set([]);
|
|
388
388
|
const pendingSnapshot = cloneDeep(result);
|
|
389
389
|
|
|
390
|
-
|
|
390
|
+
const linearWrite =
|
|
391
391
|
result.type === 'batchWrite' ?
|
|
392
392
|
result.value.map(({ scope, value, find, path }) =>
|
|
393
393
|
({ type: scope, value, find, path })
|
|
394
394
|
)
|
|
395
|
-
: [{ ...result, path: builder.path }]
|
|
396
|
-
|
|
395
|
+
: [{ ...result, find: builder.find, path: builder.path }];
|
|
396
|
+
|
|
397
|
+
await Promise.all(linearWrite.map(async ({ value: writeObj, find, type, path }) => {
|
|
397
398
|
WriteValidator[type]({ find, value: writeObj });
|
|
398
399
|
validateCollectionName(path);
|
|
399
400
|
pathChanges.add(path);
|
|
400
401
|
|
|
401
402
|
if (io) {
|
|
402
|
-
const colObj =
|
|
403
|
+
const colObj = grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance']);
|
|
403
404
|
|
|
404
405
|
if (colObj)
|
|
405
406
|
await Promise.all(
|
|
@@ -408,7 +409,7 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
408
409
|
e,
|
|
409
410
|
path =>
|
|
410
411
|
Object.values(
|
|
411
|
-
|
|
412
|
+
grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance'], {})
|
|
412
413
|
).map(({ data }) => data).flat()
|
|
413
414
|
)
|
|
414
415
|
)
|
|
@@ -638,12 +639,57 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
638
639
|
};
|
|
639
640
|
}));
|
|
640
641
|
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
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] || {});
|
|
668
|
+
|
|
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
|
+
}
|
|
685
|
+
|
|
686
|
+
if (!wasShifted)
|
|
687
|
+
poke(CacheStore.PendingWrites, [projectUrl, writeId], cloneDeep({
|
|
688
|
+
builder: pureBuilder,
|
|
689
|
+
snapshot: pendingSnapshot,
|
|
690
|
+
editions,
|
|
691
|
+
addedOn: Date.now()
|
|
692
|
+
}));
|
|
647
693
|
|
|
648
694
|
updateCacheStore(['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
|
|
649
695
|
notifyDatabaseNodeChanges(builder, [...pathChanges]);
|
|
@@ -652,7 +698,7 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
652
698
|
export const removePendingWrite = async (builder, writeId, revert) => {
|
|
653
699
|
await awaitStore();
|
|
654
700
|
const { projectUrl, dbUrl, dbName } = builder;
|
|
655
|
-
const pendingData =
|
|
701
|
+
const pendingData = grab(CacheStore.PendingWrites, [projectUrl, writeId]);
|
|
656
702
|
const { io } = Scoped.ReleaseCacheData;
|
|
657
703
|
|
|
658
704
|
if (!pendingData) return;
|
|
@@ -661,11 +707,11 @@ export const removePendingWrite = async (builder, writeId, revert) => {
|
|
|
661
707
|
if (revert) {
|
|
662
708
|
await Promise.all(pendingData.editions.map(async ([access_id, [b4Doc, afDoc], path]) => {
|
|
663
709
|
if (io) {
|
|
664
|
-
RevertMutation(
|
|
710
|
+
RevertMutation(grab(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]));
|
|
665
711
|
} else {
|
|
666
712
|
await useFS(builder, access_id, 'database')(async fs => {
|
|
667
713
|
const colObj = await fs.find(LIMITER_DATA(path), access_id, ['value'])
|
|
668
|
-
.then(v => deserializeBSON(v.value))
|
|
714
|
+
.then(v => deserializeBSON(v.value, true))
|
|
669
715
|
.catch(() => null);
|
|
670
716
|
if (!colObj) return;
|
|
671
717
|
RevertMutation(colObj);
|
|
@@ -715,7 +761,7 @@ export const removePendingWrite = async (builder, writeId, revert) => {
|
|
|
715
761
|
}));
|
|
716
762
|
}
|
|
717
763
|
|
|
718
|
-
|
|
764
|
+
unpoke(CacheStore.PendingWrites, [projectUrl, writeId]);
|
|
719
765
|
updateCacheStore(['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
|
|
720
766
|
notifyDatabaseNodeChanges(builder, [...pathChanges]);
|
|
721
767
|
};
|
|
@@ -766,19 +812,19 @@ const snipDocument = (data, find, config) => {
|
|
|
766
812
|
if (returnOnly) {
|
|
767
813
|
output = {};
|
|
768
814
|
(Array.isArray(returnOnly) ? returnOnly : [returnOnly]).filter(v => v).forEach(e => {
|
|
769
|
-
const thisData =
|
|
770
|
-
if (thisData)
|
|
815
|
+
const thisData = grab(data, e);
|
|
816
|
+
if (thisData) poke(output, e, thisData);
|
|
771
817
|
});
|
|
772
818
|
} else if (excludeFields) {
|
|
773
819
|
(Array.isArray(excludeFields) ? excludeFields : [excludeFields]).filter(v => v).forEach(e => {
|
|
774
|
-
if (
|
|
820
|
+
if (grab(data, e) && e !== '_id') unpoke(output, e);
|
|
775
821
|
});
|
|
776
822
|
}
|
|
777
823
|
|
|
778
824
|
getFindFields(find).forEach(field => {
|
|
779
|
-
if (!
|
|
780
|
-
const mainData =
|
|
781
|
-
if (mainData !== undefined)
|
|
825
|
+
if (!grab(output, field)) {
|
|
826
|
+
const mainData = grab(data, field);
|
|
827
|
+
if (mainData !== undefined) poke(output, field, mainData);
|
|
782
828
|
}
|
|
783
829
|
});
|
|
784
830
|
|
|
@@ -848,10 +894,10 @@ const AtomicWriter = {
|
|
|
848
894
|
!isDate &&
|
|
849
895
|
!isTimestamp
|
|
850
896
|
) throw `invalid value at $currentDate.${field}, expected any of boolean (true), { $type: "timestamp" } or { $type: "date" } but got ${value}`;
|
|
851
|
-
|
|
897
|
+
poke(object, field, isDate ? new Date() : new Timestamp({ t: Math.floor(Date.now() / 1000), i: 0 }));
|
|
852
898
|
},
|
|
853
899
|
$inc: (field, value, object) => {
|
|
854
|
-
const current =
|
|
900
|
+
const current = grab(object, field);
|
|
855
901
|
if (current === null) {
|
|
856
902
|
console.warn(`cannot use $inc operator on a null value at ${field}`);
|
|
857
903
|
return;
|
|
@@ -861,29 +907,29 @@ const AtomicWriter = {
|
|
|
861
907
|
|
|
862
908
|
if (!Validator.NUMBER(castedValue)) throw `expected a number at $inc.${field} but got ${value}`;
|
|
863
909
|
|
|
864
|
-
|
|
910
|
+
poke(object, field, Validator.NUMBER(castedCurrent) ? defaultBSON(castedCurrent + castedValue, current) : value);
|
|
865
911
|
},
|
|
866
912
|
$min: (field, value, object) => {
|
|
867
|
-
const current =
|
|
913
|
+
const current = grab(object, field);
|
|
868
914
|
if (CompareBson.lesser(value, current)) {
|
|
869
|
-
|
|
915
|
+
poke(object, field, value);
|
|
870
916
|
}
|
|
871
917
|
},
|
|
872
918
|
$max: (field, value, object) => {
|
|
873
|
-
const current =
|
|
919
|
+
const current = grab(object, field);
|
|
874
920
|
if (CompareBson.greater(value, current)) {
|
|
875
|
-
|
|
921
|
+
poke(object, field, value);
|
|
876
922
|
}
|
|
877
923
|
},
|
|
878
924
|
$mul: (field, value, object) => {
|
|
879
|
-
const current =
|
|
925
|
+
const current = grab(object, field);
|
|
880
926
|
const castedValue = downcastBSON(value);
|
|
881
927
|
const castedCurrent = downcastBSON(current);
|
|
882
928
|
|
|
883
929
|
if (!Validator.NUMBER(castedValue))
|
|
884
930
|
throw `expected a number at $mul.${field} but got ${value}`;
|
|
885
931
|
|
|
886
|
-
|
|
932
|
+
poke(object, field, Validator.NUMBER(castedCurrent) ? defaultBSON(castedCurrent * castedValue, value) : 0);
|
|
887
933
|
},
|
|
888
934
|
$rename: (field, value, object) => {
|
|
889
935
|
if (!Validator.EMPTY_STRING(value))
|
|
@@ -901,7 +947,7 @@ const AtomicWriter = {
|
|
|
901
947
|
if (!e) throw `empty node for ${field}`;
|
|
902
948
|
});
|
|
903
949
|
const [tipObj, tipSource, tipDest] = destStage.length === 1 ? [object, field, value]
|
|
904
|
-
: [
|
|
950
|
+
: [grab(object, destStage.slice(0, -1).join('.')), sourceStage.slice(-1)[0], destStage.slice(-1)[0]];
|
|
905
951
|
|
|
906
952
|
if (tipObj && tipSource in tipObj) {
|
|
907
953
|
tipObj[tipDest] = cloneDeep(tipObj[tipSource]);
|
|
@@ -909,16 +955,16 @@ const AtomicWriter = {
|
|
|
909
955
|
}
|
|
910
956
|
},
|
|
911
957
|
$set: (field, value, object) => {
|
|
912
|
-
|
|
958
|
+
poke(object, field, value === undefined ? null : value);
|
|
913
959
|
},
|
|
914
960
|
$setOnInsert: (field, value, object, isNew) => {
|
|
915
961
|
if (isNew) AtomicWriter.$set(field, value, object);
|
|
916
962
|
},
|
|
917
963
|
$unset: (field, _, object) => {
|
|
918
|
-
|
|
964
|
+
unpoke(object, field);
|
|
919
965
|
},
|
|
920
966
|
$addToSet: (field, value, object) => {
|
|
921
|
-
const current =
|
|
967
|
+
const current = grab(object, field);
|
|
922
968
|
if (Array.isArray(current)) {
|
|
923
969
|
if (
|
|
924
970
|
Validator.OBJECT(value) &&
|
|
@@ -940,7 +986,7 @@ const AtomicWriter = {
|
|
|
940
986
|
},
|
|
941
987
|
$pop: (field, value, object) => {
|
|
942
988
|
if (![1, -1].includes(value)) throw `expected 1 or -1 at "$pop.${field}" but got ${value}`;
|
|
943
|
-
const current =
|
|
989
|
+
const current = grab(object, field);
|
|
944
990
|
if (
|
|
945
991
|
Array.isArray(current) &&
|
|
946
992
|
current.length
|
|
@@ -948,7 +994,7 @@ const AtomicWriter = {
|
|
|
948
994
|
},
|
|
949
995
|
$pull: (field, value, object) => {
|
|
950
996
|
// TODO: issues
|
|
951
|
-
const current =
|
|
997
|
+
const current = grab(object, field);
|
|
952
998
|
const isQueryObject = Validator.OBJECT(value);
|
|
953
999
|
|
|
954
1000
|
if (
|
|
@@ -970,11 +1016,11 @@ const AtomicWriter = {
|
|
|
970
1016
|
} catch (_) { }
|
|
971
1017
|
return true;
|
|
972
1018
|
});
|
|
973
|
-
|
|
1019
|
+
poke(object, field, remainingCurrent);
|
|
974
1020
|
}
|
|
975
1021
|
},
|
|
976
1022
|
$push: (field, value, object) => {
|
|
977
|
-
const current =
|
|
1023
|
+
const current = grab(object, field);
|
|
978
1024
|
|
|
979
1025
|
if (Array.isArray(current)) {
|
|
980
1026
|
if (Validator.OBJECT(value)) {
|
|
@@ -1021,13 +1067,13 @@ const AtomicWriter = {
|
|
|
1021
1067
|
if (!Array.isArray(value))
|
|
1022
1068
|
throw `expected an array at $pullAll.${field}`;
|
|
1023
1069
|
|
|
1024
|
-
const current =
|
|
1070
|
+
const current = grab(object, field);
|
|
1025
1071
|
|
|
1026
1072
|
if (Array.isArray(current)) {
|
|
1027
1073
|
const remainingCurrent = current.filter(v =>
|
|
1028
1074
|
!value.some(k => CompareBson.equal(v, k))
|
|
1029
1075
|
);
|
|
1030
|
-
|
|
1076
|
+
poke(object, field, remainingCurrent);
|
|
1031
1077
|
}
|
|
1032
1078
|
}
|
|
1033
1079
|
};
|