react-native-mosquito-transport 0.0.18 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jshintignore +4 -0
- package/.jshintrc +16 -0
- package/README.md +75 -1
- package/TODO +10 -1
- package/example/ios/MosquitodbExample.xcodeproj/project.pbxproj +6 -5
- package/example/ios/MosquitodbExample.xcworkspace/contents.xcworkspacedata +10 -0
- package/example/ios/MosquitodbExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/example/ios/MosquitodbExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +8 -0
- package/example/ios/MosquitodbExample.xcworkspace/xcuserdata/anthony.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/example/ios/MosquitodbExample.xcworkspace/xcuserdata/anthony.xcuserdatad/WorkspaceSettings.xcsettings +14 -0
- package/ios/Mosquitodb.swift +14 -1
- package/package.json +15 -14
- package/src/helpers/engine_api.js +39 -0
- package/src/helpers/peripherals.js +73 -127
- package/src/helpers/utils.js +48 -19
- package/src/helpers/values.js +8 -47
- package/src/helpers/variables.js +14 -6
- package/src/index.d.ts +103 -43
- package/src/index.js +198 -121
- package/src/products/auth/accessor.js +97 -36
- package/src/products/auth/index.js +151 -82
- package/src/products/database/accessor.js +720 -223
- package/src/products/database/bson.js +16 -0
- package/src/products/database/counter.js +16 -0
- package/src/products/database/index.js +303 -190
- package/src/products/database/types.js +1 -1
- package/src/products/database/validator.js +517 -254
- package/src/products/http_callable/index.js +111 -106
- package/src/products/storage/index.js +97 -88
- package/src/helpers/EngineApi.js +0 -33
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { io } from "socket.io-client";
|
|
2
|
-
import EngineApi from "../../helpers/
|
|
2
|
+
import EngineApi from "../../helpers/engine_api";
|
|
3
3
|
import { DatabaseRecordsListener } from "../../helpers/listeners";
|
|
4
|
-
import {
|
|
5
|
-
import { awaitStore, buildFetchInterface, getReachableServer } from "../../helpers/utils";
|
|
4
|
+
import { deserializeE2E, listenReachableServer, niceTry, serializeE2E } from "../../helpers/peripherals";
|
|
5
|
+
import { awaitStore, buildFetchInterface, buildFetchResult, getReachableServer } from "../../helpers/utils";
|
|
6
6
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
7
|
-
import { addPendingWrites, generateRecordID, getRecord, insertRecord, listenQueryEntry, removePendingWrite } from "./accessor";
|
|
8
|
-
import {
|
|
7
|
+
import { addPendingWrites, generateRecordID, getRecord, insertRecord, listenQueryEntry, removePendingWrite, validateWriteValue } from "./accessor";
|
|
8
|
+
import { validateCollectionName, validateFilter, validateFindConfig, validateFindObject, validateListenFindConfig } from "./validator";
|
|
9
9
|
import { awaitRefreshToken, listenToken } from "../auth/accessor";
|
|
10
|
-
import {
|
|
10
|
+
import { DELIVERY, RETRIEVAL } from "../../helpers/values";
|
|
11
11
|
import setLodash from 'lodash.set';
|
|
12
|
+
import { ObjectId } from "bson";
|
|
13
|
+
import { guardObject, Validator } from "guard-object";
|
|
14
|
+
import { simplifyCaughtError } from "simplify-error";
|
|
15
|
+
import cloneDeep from "lodash.clonedeep";
|
|
16
|
+
import { deserializeBSON, serializeToBase64 } from "./bson";
|
|
12
17
|
|
|
13
18
|
export class MTCollection {
|
|
14
19
|
constructor(config) {
|
|
15
20
|
this.builder = { ...config };
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
find = (find) => ({
|
|
23
|
+
find = (find = {}) => ({
|
|
19
24
|
get: (config) => findObject({ ...this.builder, command: { find } }, config),
|
|
20
25
|
listen: (callback, error, config) => listenDocument(callback, error, { ...this.builder, command: { find } }, config),
|
|
21
26
|
count: (config) => countCollection({ ...this.builder, command: { find } }, config),
|
|
@@ -51,11 +56,11 @@ export class MTCollection {
|
|
|
51
56
|
|
|
52
57
|
limit = (limit) => this.find().limit(limit);
|
|
53
58
|
|
|
54
|
-
count = (config) =>
|
|
59
|
+
count = (config) => this.find().count(config);
|
|
55
60
|
|
|
56
|
-
get = (config) =>
|
|
61
|
+
get = (config) => this.find().get(config);
|
|
57
62
|
|
|
58
|
-
listen = (callback, error, config) =>
|
|
63
|
+
listen = (callback, error, config) => this.find().listen(callback, error, config);
|
|
59
64
|
|
|
60
65
|
findOne = (findOne = {}) => ({
|
|
61
66
|
listen: (callback, error, config) => listenDocument(callback, error, { ...this.builder, command: { findOne } }, config),
|
|
@@ -65,36 +70,50 @@ export class MTCollection {
|
|
|
65
70
|
onDisconnect = () => ({
|
|
66
71
|
setOne: (value) => initOnDisconnectionTask({ ...this.builder }, value, 'setOne'),
|
|
67
72
|
setMany: (value) => initOnDisconnectionTask({ ...this.builder }, value, 'setMany'),
|
|
68
|
-
updateOne: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'updateOne'),
|
|
69
|
-
updateMany: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'updateMany'),
|
|
70
|
-
mergeOne: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'mergeOne'),
|
|
71
|
-
mergeMany: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'mergeMany'),
|
|
72
|
-
deleteOne: (find) => initOnDisconnectionTask({ ...this.builder, command: { find } },
|
|
73
|
-
deleteMany: (find) => initOnDisconnectionTask({ ...this.builder, command: { find } },
|
|
74
|
-
replaceOne: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'replaceOne'),
|
|
75
|
-
putOne: (find, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'putOne')
|
|
76
|
-
})
|
|
73
|
+
updateOne: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'updateOne'),
|
|
74
|
+
updateMany: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'updateMany'),
|
|
75
|
+
mergeOne: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'mergeOne'),
|
|
76
|
+
mergeMany: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'mergeMany'),
|
|
77
|
+
deleteOne: (find = {}) => initOnDisconnectionTask({ ...this.builder, command: { find } }, undefined, 'deleteOne'),
|
|
78
|
+
deleteMany: (find = {}) => initOnDisconnectionTask({ ...this.builder, command: { find } }, undefined, 'deleteMany'),
|
|
79
|
+
replaceOne: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'replaceOne'),
|
|
80
|
+
putOne: (find = {}, value) => initOnDisconnectionTask({ ...this.builder, command: { find } }, value, 'putOne')
|
|
81
|
+
});
|
|
77
82
|
|
|
78
83
|
setOne = (value, config) => commitData(this.builder, value, 'setOne', config);
|
|
79
84
|
|
|
80
85
|
setMany = (value, config) => commitData(this.builder, value, 'setMany', config);
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
addOne = (value, config) => commitData(
|
|
88
|
+
this.builder,
|
|
89
|
+
Validator.OBJECT(value) ? { ...value, _id: new ObjectId() } : value,
|
|
90
|
+
'setOne',
|
|
91
|
+
config
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
addMany = (value, config) => commitData(
|
|
95
|
+
this.builder,
|
|
96
|
+
value.map(v => Validator.OBJECT(v) ? ({ ...v, _id: new ObjectId() }) : v),
|
|
97
|
+
'setMany',
|
|
98
|
+
config
|
|
99
|
+
);
|
|
83
100
|
|
|
84
|
-
|
|
101
|
+
updateOne = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'updateOne', config);
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
updateMany = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'updateMany', config);
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
mergeOne = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'mergeOne', config);
|
|
89
106
|
|
|
90
|
-
|
|
107
|
+
mergeMany = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'mergeMany', config);
|
|
91
108
|
|
|
92
|
-
|
|
109
|
+
replaceOne = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'replaceOne', config);
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
putOne = (find = {}, value, config) => commitData({ ...this.builder, find }, value, 'putOne', config);
|
|
95
112
|
|
|
96
|
-
|
|
97
|
-
|
|
113
|
+
deleteOne = (find = {}, config) => commitData({ ...this.builder, find }, undefined, 'deleteOne', config);
|
|
114
|
+
|
|
115
|
+
deleteMany = (find = {}, config) => commitData({ ...this.builder, find }, undefined, 'deleteMany', config);
|
|
116
|
+
};
|
|
98
117
|
|
|
99
118
|
export const batchWrite = (builder, map, config) => commitData({ ...builder }, map, 'batchWrite', config);
|
|
100
119
|
|
|
@@ -111,16 +130,16 @@ const {
|
|
|
111
130
|
} = EngineApi;
|
|
112
131
|
|
|
113
132
|
const listenDocument = (callback, onError, builder, config) => {
|
|
114
|
-
const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify } = builder
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
133
|
+
const { projectUrl, wsPrefix, serverE2E_PublicKey, baseUrl, dbUrl, dbName, accessKey, path, disableCache, command, uglify, extraHeaders, castBSON } = builder;
|
|
134
|
+
const { find, findOne, sort, direction, limit } = command;
|
|
135
|
+
const { disableAuth } = config || {};
|
|
136
|
+
const shouldCache = !disableCache;
|
|
137
|
+
const processId = `${++Scoped.AnyProcessIte}`;
|
|
138
|
+
let accessId;
|
|
139
|
+
|
|
140
|
+
validateListenFindConfig(config);
|
|
122
141
|
validateFilter(findOne || find);
|
|
123
|
-
|
|
142
|
+
validateCollectionName(path);
|
|
124
143
|
|
|
125
144
|
let hasCancelled,
|
|
126
145
|
hasRespond,
|
|
@@ -129,17 +148,33 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
129
148
|
wasDisconnected,
|
|
130
149
|
lastToken = Scoped.AuthJWTToken[projectUrl] || null,
|
|
131
150
|
lastInitRef = 0,
|
|
132
|
-
connectedListener
|
|
151
|
+
connectedListener,
|
|
152
|
+
lastSnapshot;
|
|
153
|
+
|
|
154
|
+
const dispatchSnapshot = s => {
|
|
155
|
+
const thisSnapshotId = serializeToBase64({ _: s });
|
|
156
|
+
if (thisSnapshotId === lastSnapshot) return;
|
|
157
|
+
lastSnapshot = thisSnapshotId;
|
|
158
|
+
callback?.(cloneDeep(transformBSON(s, castBSON)));
|
|
159
|
+
};
|
|
133
160
|
|
|
134
161
|
if (shouldCache) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
162
|
+
accessId = generateRecordID(builder, config).then(hash => {
|
|
163
|
+
if (hasCancelled) return hash;
|
|
164
|
+
cacheListener = listenQueryEntry(snapshot => {
|
|
165
|
+
if (!Scoped.IS_CONNECTED[projectUrl]) dispatchSnapshot(snapshot);
|
|
166
|
+
}, { accessId: hash, builder, config, processId });
|
|
167
|
+
return hash;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
awaitStore().then(() => {
|
|
171
|
+
if (hasCancelled) return;
|
|
172
|
+
connectedListener = listenReachableServer(async connected => {
|
|
173
|
+
connectedListener();
|
|
174
|
+
if (!connected && !hasRespond && !hasCancelled && shouldCache)
|
|
175
|
+
DatabaseRecordsListener.dispatch('d', processId);
|
|
176
|
+
}, projectUrl);
|
|
177
|
+
});
|
|
143
178
|
}
|
|
144
179
|
|
|
145
180
|
const init = async () => {
|
|
@@ -147,24 +182,27 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
147
182
|
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
148
183
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
149
184
|
|
|
150
|
-
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl]
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
186
|
+
const pureConfig = stripRequestConfig(config);
|
|
187
|
+
const authObj = {
|
|
188
|
+
commands: stripUndefined({
|
|
189
|
+
config: pureConfig && serializeToBase64(pureConfig),
|
|
190
|
+
path,
|
|
191
|
+
find: serializeToBase64(findOne || find),
|
|
192
|
+
sort,
|
|
193
|
+
direction,
|
|
194
|
+
limit
|
|
195
|
+
}),
|
|
196
|
+
dbName,
|
|
197
|
+
dbUrl
|
|
198
|
+
};
|
|
163
199
|
|
|
164
|
-
const [encPlate, [privateKey]] = uglify ? serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
200
|
+
const [encPlate, [privateKey]] = uglify ? await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey) : ['', []];
|
|
165
201
|
|
|
166
202
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
167
|
-
|
|
203
|
+
transports: ['websocket', 'polling', 'flashsocket'],
|
|
204
|
+
extraHeaders,
|
|
205
|
+
auth: uglify ? { e2e: encPlate.toString('base64'), _m_internal: true } : {
|
|
168
206
|
accessKey,
|
|
169
207
|
_body: authObj,
|
|
170
208
|
...mtoken ? { mtoken } : {},
|
|
@@ -176,13 +214,15 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
176
214
|
socket.on('mSnapshot', async ([err, snapshot]) => {
|
|
177
215
|
hasRespond = true;
|
|
178
216
|
if (err) {
|
|
179
|
-
onError
|
|
217
|
+
if (typeof onError === 'function') {
|
|
218
|
+
onError(simplifyCaughtError(err).simpleError);
|
|
219
|
+
} else console.error('unhandled listen for:', { path, find }, ' error:', err);
|
|
180
220
|
} else {
|
|
181
|
-
if (uglify) snapshot = deserializeE2E(snapshot, serverE2E_PublicKey, privateKey);
|
|
182
|
-
|
|
221
|
+
if (uglify) snapshot = await deserializeE2E(snapshot, serverE2E_PublicKey, privateKey);
|
|
222
|
+
snapshot = deserializeBSON(snapshot)._;
|
|
223
|
+
dispatchSnapshot(snapshot);
|
|
183
224
|
|
|
184
|
-
if (shouldCache)
|
|
185
|
-
insertRecord(builder, accessId, { sort, direction, limit, find, findOne, config }, snapshot);
|
|
225
|
+
if (shouldCache) insertRecord(builder, config, await accessId, snapshot);
|
|
186
226
|
}
|
|
187
227
|
});
|
|
188
228
|
|
|
@@ -214,14 +254,16 @@ const listenDocument = (callback, onError, builder, config) => {
|
|
|
214
254
|
tokenListener?.();
|
|
215
255
|
if (socket) socket.close();
|
|
216
256
|
}
|
|
217
|
-
}
|
|
257
|
+
};
|
|
218
258
|
|
|
219
259
|
const initOnDisconnectionTask = (builder, value, type) => {
|
|
220
|
-
const { projectUrl, wsPrefix, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, command, uglify } = builder
|
|
221
|
-
|
|
222
|
-
|
|
260
|
+
const { projectUrl, wsPrefix, baseUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, path, extraHeaders, command, uglify } = builder;
|
|
261
|
+
const { find } = command || {};
|
|
262
|
+
const disableAuth = false;
|
|
263
|
+
|
|
264
|
+
validateCollectionName(path);
|
|
265
|
+
validateWriteValue({ type, find, value });
|
|
223
266
|
|
|
224
|
-
validateCollectionPath(path);
|
|
225
267
|
let hasCancelled,
|
|
226
268
|
socket,
|
|
227
269
|
wasDisconnected,
|
|
@@ -233,16 +275,23 @@ const initOnDisconnectionTask = (builder, value, type) => {
|
|
|
233
275
|
if (!disableAuth) await awaitRefreshToken(projectUrl);
|
|
234
276
|
if (hasCancelled || processID !== lastInitRef) return;
|
|
235
277
|
|
|
236
|
-
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl]
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
278
|
+
const mtoken = disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl];
|
|
279
|
+
const authObj = {
|
|
280
|
+
commands: stripUndefined({
|
|
281
|
+
path,
|
|
282
|
+
find: find && serializeToBase64(find),
|
|
283
|
+
value: value && serializeToBase64({ _: value }),
|
|
284
|
+
scope: type
|
|
285
|
+
}),
|
|
286
|
+
dbName,
|
|
287
|
+
dbUrl
|
|
288
|
+
};
|
|
242
289
|
|
|
243
290
|
socket = io(`${wsPrefix}://${baseUrl}`, {
|
|
291
|
+
transports: ['websocket', 'polling', 'flashsocket'],
|
|
292
|
+
extraHeaders,
|
|
244
293
|
auth: uglify ? {
|
|
245
|
-
e2e: serializeE2E(authObj, mtoken, serverE2E_PublicKey)[0],
|
|
294
|
+
e2e: (await serializeE2E({ accessKey, _body: authObj }, mtoken, serverE2E_PublicKey))[0].toString('base64'),
|
|
246
295
|
_m_internal: true
|
|
247
296
|
} : {
|
|
248
297
|
...mtoken ? { mtoken } : {},
|
|
@@ -267,7 +316,7 @@ const initOnDisconnectionTask = (builder, value, type) => {
|
|
|
267
316
|
const tokenListener = listenToken(async t => {
|
|
268
317
|
if ((t || null) !== lastToken) {
|
|
269
318
|
if (socket) {
|
|
270
|
-
await niceTry(() => socket.timeout(
|
|
319
|
+
await niceTry(() => socket.timeout(7000).emitWithAck(_cancelDisconnectWriteTask(uglify)));
|
|
271
320
|
socket.close();
|
|
272
321
|
}
|
|
273
322
|
wasDisconnected = undefined;
|
|
@@ -280,31 +329,26 @@ const initOnDisconnectionTask = (builder, value, type) => {
|
|
|
280
329
|
if (hasCancelled) return;
|
|
281
330
|
tokenListener();
|
|
282
331
|
if (socket)
|
|
283
|
-
(
|
|
284
|
-
await niceTry(() => socket.timeout(10000).emitWithAck(_cancelDisconnectWriteTask(uglify)));
|
|
332
|
+
niceTry(() => socket.timeout(7000).emitWithAck(_cancelDisconnectWriteTask(uglify))).then(() => {
|
|
285
333
|
socket.close();
|
|
286
|
-
})
|
|
334
|
+
});
|
|
287
335
|
hasCancelled = true;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
336
|
+
};
|
|
337
|
+
};
|
|
290
338
|
|
|
291
339
|
const countCollection = async (builder, config) => {
|
|
292
|
-
const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, uglify, path, disableCache, command = {} } = builder
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
340
|
+
const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, uglify, extraHeaders, path, disableCache, command = {} } = builder;
|
|
341
|
+
const { find } = command;
|
|
342
|
+
const { disableAuth } = config || {};
|
|
343
|
+
const accessId = await generateRecordID({ ...builder, countDoc: true }, config);
|
|
296
344
|
|
|
297
345
|
await awaitStore();
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
'disableMinimizer'
|
|
305
|
-
]);
|
|
306
|
-
validateFilter(find || {});
|
|
307
|
-
validateCollectionPath(path);
|
|
346
|
+
if (config !== undefined)
|
|
347
|
+
guardObject({
|
|
348
|
+
disableAuth: t => t === undefined || Validator.BOOLEAN(t)
|
|
349
|
+
}).validate(config);
|
|
350
|
+
validateFilter(find);
|
|
351
|
+
validateCollectionName(path);
|
|
308
352
|
|
|
309
353
|
let retries = 0;
|
|
310
354
|
|
|
@@ -312,41 +356,42 @@ const countCollection = async (builder, config) => {
|
|
|
312
356
|
++retries;
|
|
313
357
|
|
|
314
358
|
const finalize = (a, b) => {
|
|
315
|
-
if (
|
|
316
|
-
|
|
317
|
-
} else
|
|
318
|
-
}
|
|
359
|
+
if (Validator.NUMBER(a)) {
|
|
360
|
+
resolve(a);
|
|
361
|
+
} else reject(b);
|
|
362
|
+
};
|
|
319
363
|
|
|
320
364
|
try {
|
|
321
|
-
if (!disableAuth && await getReachableServer(projectUrl))
|
|
365
|
+
if (!disableAuth && await getReachableServer(projectUrl))
|
|
366
|
+
await awaitRefreshToken(projectUrl);
|
|
322
367
|
|
|
323
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
368
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
324
369
|
body: {
|
|
325
|
-
commands: { path, find },
|
|
370
|
+
commands: { path, find: serializeToBase64(find) },
|
|
326
371
|
dbName,
|
|
327
372
|
dbUrl
|
|
328
373
|
},
|
|
329
374
|
accessKey,
|
|
330
375
|
...disableAuth ? {} : { authToken: Scoped.AuthJWTToken[projectUrl] },
|
|
331
376
|
serverE2E_PublicKey,
|
|
332
|
-
uglify
|
|
377
|
+
uglify,
|
|
378
|
+
extraHeaders
|
|
333
379
|
});
|
|
334
380
|
|
|
335
|
-
const
|
|
336
|
-
if (r.simpleError) throw r;
|
|
381
|
+
const data = await buildFetchResult(await fetch(_documentCount(projectUrl, uglify), reqBuilder), uglify);
|
|
337
382
|
|
|
338
|
-
const f = uglify ? deserializeE2E(
|
|
383
|
+
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
339
384
|
|
|
340
385
|
if (!disableCache)
|
|
341
|
-
setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl
|
|
386
|
+
setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, accessId], f.result);
|
|
342
387
|
|
|
343
388
|
finalize(f.result);
|
|
344
389
|
} catch (e) {
|
|
345
|
-
const b4Data = setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl
|
|
390
|
+
const b4Data = setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, accessId]);
|
|
346
391
|
|
|
347
392
|
if (e?.simpleError) {
|
|
348
393
|
finalize(undefined, e.simpleError);
|
|
349
|
-
} else if (!disableCache && !
|
|
394
|
+
} else if (!disableCache && !Validator.NUMBER(b4Data)) {
|
|
350
395
|
finalize(b4Data);
|
|
351
396
|
} else if (retries > maxRetries) {
|
|
352
397
|
finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
|
|
@@ -364,9 +409,8 @@ const countCollection = async (builder, config) => {
|
|
|
364
409
|
}
|
|
365
410
|
});
|
|
366
411
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
412
|
+
return await readValue();
|
|
413
|
+
};
|
|
370
414
|
|
|
371
415
|
const stripRequestConfig = (config) => {
|
|
372
416
|
const known_fields = ['extraction', 'returnOnly', 'excludeFields'];
|
|
@@ -374,28 +418,33 @@ const stripRequestConfig = (config) => {
|
|
|
374
418
|
known_fields.includes(k) ? [k, v] : null
|
|
375
419
|
).filter(v => v);
|
|
376
420
|
return requestConfig.length ? Object.fromEntries(requestConfig) : undefined;
|
|
377
|
-
}
|
|
421
|
+
};
|
|
378
422
|
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
{ retrieval = RETRIEVAL.DEFAULT, episode = 0, disableAuth, disableMinimizer } = config || {},
|
|
383
|
-
enableMinimizer = !disableMinimizer,
|
|
384
|
-
accessId = generateRecordID(builder, config),
|
|
385
|
-
processAccessId = `${accessId}${projectUrl}${dbUrl}${dbName}${retrieval}`,
|
|
386
|
-
getRecordData = () => getRecord(builder, accessId),
|
|
387
|
-
shouldCache = (retrieval === RETRIEVAL.DEFAULT ? !disableCache : true) &&
|
|
388
|
-
retrieval !== RETRIEVAL.NO_CACHE_NO_AWAIT;
|
|
423
|
+
const stripUndefined = o => Object.fromEntries(
|
|
424
|
+
Object.entries(o).filter(v => v[1] !== undefined)
|
|
425
|
+
);
|
|
389
426
|
|
|
390
|
-
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
validateFilter(findOne || find);
|
|
427
|
+
const transformBSON = (d, castBSON) => {
|
|
428
|
+
if (castBSON) return d && deserializeBSON(serializeToBase64({ _: d }), true)._;
|
|
429
|
+
return cloneDeep(d);
|
|
430
|
+
};
|
|
395
431
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
}
|
|
432
|
+
const findObject = async (builder, config) => {
|
|
433
|
+
const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, disableCache, uglify, extraHeaders, command, castBSON } = builder;
|
|
434
|
+
const { find, findOne, sort, direction, limit, random } = command;
|
|
435
|
+
const { retrieval = RETRIEVAL.DEFAULT, episode = 0, disableAuth, disableMinimizer } = config || {};
|
|
436
|
+
const enableMinimizer = !disableMinimizer;
|
|
437
|
+
const accessId = await generateRecordID(builder, config);
|
|
438
|
+
const processAccessId = `${accessId}${projectUrl}${dbUrl}${dbName}${retrieval}`;
|
|
439
|
+
const getRecordData = () => getRecord(builder, config, accessId);
|
|
440
|
+
const shouldCache = (retrieval !== RETRIEVAL.DEFAULT || !disableCache) &&
|
|
441
|
+
![RETRIEVAL.NO_CACHE_NO_AWAIT, RETRIEVAL.NO_CACHE_AWAIT].includes(retrieval);
|
|
442
|
+
|
|
443
|
+
const pureConfig = stripRequestConfig(config);
|
|
444
|
+
validateFindObject(command);
|
|
445
|
+
validateFindConfig(config);
|
|
446
|
+
validateCollectionName(path);
|
|
447
|
+
await awaitStore();
|
|
399
448
|
|
|
400
449
|
let retries = 0, hasFinalize;
|
|
401
450
|
|
|
@@ -406,18 +455,18 @@ const findObject = async (builder, config) => {
|
|
|
406
455
|
const finalize = (a, b) => {
|
|
407
456
|
const res = (instantProcess && a) ?
|
|
408
457
|
(a.liveResult || a.liveResult === null) ?
|
|
409
|
-
(a.liveResult || undefined) :
|
|
410
|
-
a.episode[episode] : a;
|
|
458
|
+
transformBSON(a.liveResult || undefined, castBSON) :
|
|
459
|
+
transformBSON(a.episode[episode], castBSON) : a;
|
|
411
460
|
|
|
412
461
|
if (a) {
|
|
413
|
-
resolve(instantProcess ? res : a);
|
|
414
|
-
} else reject(b);
|
|
462
|
+
resolve(instantProcess ? cloneDeep(res) : a);
|
|
463
|
+
} else reject(instantProcess ? cloneDeep(b) : b);
|
|
415
464
|
if (hasFinalize || !instantProcess) return;
|
|
416
465
|
hasFinalize = true;
|
|
417
466
|
|
|
418
467
|
if (enableMinimizer) {
|
|
419
468
|
(Scoped.PendingDbReadCollective.pendingResolution[processAccessId] || []).forEach(e => {
|
|
420
|
-
e(a ? { result:
|
|
469
|
+
e(a ? { result: res } : undefined, b);
|
|
421
470
|
});
|
|
422
471
|
if (Scoped.PendingDbReadCollective.pendingResolution[processAccessId])
|
|
423
472
|
delete Scoped.PendingDbReadCollective.pendingResolution[processAccessId];
|
|
@@ -435,8 +484,8 @@ const findObject = async (builder, config) => {
|
|
|
435
484
|
Scoped.PendingDbReadCollective.pendingResolution[processAccessId] = [];
|
|
436
485
|
|
|
437
486
|
Scoped.PendingDbReadCollective.pendingResolution[processAccessId].push((a, b) => {
|
|
438
|
-
if (a) resolve(a.result);
|
|
439
|
-
else reject(b);
|
|
487
|
+
if (a) resolve(cloneDeep(a.result));
|
|
488
|
+
else reject(cloneDeep(b));
|
|
440
489
|
});
|
|
441
490
|
return;
|
|
442
491
|
}
|
|
@@ -448,51 +497,56 @@ const findObject = async (builder, config) => {
|
|
|
448
497
|
if (retrieval !== RETRIEVAL.STICKY_RELOAD) return;
|
|
449
498
|
}
|
|
450
499
|
}
|
|
500
|
+
|
|
451
501
|
if (!disableAuth && await getReachableServer(projectUrl))
|
|
452
502
|
await awaitRefreshToken(projectUrl);
|
|
453
503
|
|
|
454
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
504
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
455
505
|
body: {
|
|
456
|
-
commands: {
|
|
457
|
-
config:
|
|
506
|
+
commands: stripUndefined({
|
|
507
|
+
config: pureConfig && serializeToBase64(pureConfig),
|
|
458
508
|
path,
|
|
459
|
-
find: findOne || find,
|
|
509
|
+
find: serializeToBase64(findOne || find),
|
|
460
510
|
sort,
|
|
461
511
|
direction,
|
|
462
512
|
limit,
|
|
463
513
|
random
|
|
464
|
-
},
|
|
514
|
+
}),
|
|
465
515
|
dbName,
|
|
466
516
|
dbUrl
|
|
467
517
|
},
|
|
468
518
|
accessKey,
|
|
469
519
|
authToken: disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl],
|
|
470
520
|
serverE2E_PublicKey,
|
|
471
|
-
uglify
|
|
521
|
+
uglify,
|
|
522
|
+
extraHeaders
|
|
472
523
|
});
|
|
473
524
|
|
|
474
|
-
const
|
|
475
|
-
if (r.simpleError) throw r;
|
|
525
|
+
const data = await buildFetchResult(await fetch((findOne ? _readDocument : _queryCollection)(projectUrl, uglify), reqBuilder), uglify);
|
|
476
526
|
|
|
477
|
-
const
|
|
527
|
+
const result = deserializeBSON((uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data).result)._;
|
|
478
528
|
|
|
479
|
-
if (shouldCache) insertRecord(builder,
|
|
480
|
-
finalize({ liveResult:
|
|
529
|
+
if (shouldCache) insertRecord(builder, config, accessId, result);
|
|
530
|
+
finalize({ liveResult: result || null });
|
|
481
531
|
} catch (e) {
|
|
532
|
+
let thisRecord;
|
|
533
|
+
const getThisRecord = async () => thisRecord ? thisRecord.value :
|
|
534
|
+
(thisRecord = { value: await getRecordData() }).value;
|
|
535
|
+
|
|
482
536
|
if (e?.simpleError) {
|
|
483
537
|
finalize(undefined, e?.simpleError);
|
|
484
538
|
} else if (
|
|
485
|
-
(retrieval === RETRIEVAL.CACHE_NO_AWAIT && !(await
|
|
539
|
+
(retrieval === RETRIEVAL.CACHE_NO_AWAIT && !(await getThisRecord())) ||
|
|
486
540
|
retrieval === RETRIEVAL.STICKY_NO_AWAIT ||
|
|
487
541
|
retrieval === RETRIEVAL.NO_CACHE_NO_AWAIT
|
|
488
542
|
) {
|
|
489
543
|
finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
490
544
|
} else if (
|
|
491
545
|
shouldCache &&
|
|
492
|
-
|
|
493
|
-
await
|
|
546
|
+
[RETRIEVAL.DEFAULT, RETRIEVAL.CACHE_NO_AWAIT].includes(retrieval) &&
|
|
547
|
+
await getThisRecord()
|
|
494
548
|
) {
|
|
495
|
-
finalize({ episode: await
|
|
549
|
+
finalize({ episode: await getThisRecord() });
|
|
496
550
|
} else if (retries > maxRetries) {
|
|
497
551
|
finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
|
|
498
552
|
} else {
|
|
@@ -509,26 +563,50 @@ const findObject = async (builder, config) => {
|
|
|
509
563
|
}
|
|
510
564
|
});
|
|
511
565
|
|
|
512
|
-
|
|
513
|
-
return g;
|
|
566
|
+
return await readValue();
|
|
514
567
|
};
|
|
515
568
|
|
|
569
|
+
const transformNullRecursively = obj => Object.fromEntries(
|
|
570
|
+
Object.entries(obj).map(([k, v]) =>
|
|
571
|
+
[k, [undefined, Infinity, NaN].includes(v) ? null : Validator.OBJECT(v) ? transformNullRecursively(v) : v]
|
|
572
|
+
)
|
|
573
|
+
);
|
|
574
|
+
|
|
516
575
|
const commitData = async (builder, value, type, config) => {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
576
|
+
// transform undefined
|
|
577
|
+
if (Validator.OBJECT(value)) {
|
|
578
|
+
value = value && deserializeBSON(serializeToBase64({ _: transformNullRecursively(value) }))._;
|
|
579
|
+
} else if (type === 'batchWrite' && Array.isArray(value)) {
|
|
580
|
+
value = deserializeBSON(
|
|
581
|
+
serializeToBase64({
|
|
582
|
+
_: value.map(v => {
|
|
583
|
+
if (Validator.OBJECT(v?.value)) {
|
|
584
|
+
v.value = transformNullRecursively(v.value);
|
|
585
|
+
} else if (Array.isArray(v?.value)) {
|
|
586
|
+
v.value = v.value.map(e =>
|
|
587
|
+
Validator.OBJECT(e) ? transformNullRecursively(e) : e
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
return v;
|
|
591
|
+
})
|
|
592
|
+
})
|
|
593
|
+
)._;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const { projectUrl, serverE2E_PublicKey, dbUrl, dbName, accessKey, maxRetries = 7, path, find, disableCache, uglify, extraHeaders } = builder;
|
|
597
|
+
const { disableAuth, delivery = DELIVERY.DEFAULT, stepping } = config || {};
|
|
598
|
+
const writeId = `${Date.now() + ++Scoped.PendingIte}`;
|
|
599
|
+
const isBatchWrite = type === 'batchWrite';
|
|
600
|
+
const shouldCache = (delivery !== DELIVERY.DEFAULT || !disableCache) &&
|
|
601
|
+
delivery !== DELIVERY.NO_CACHE &&
|
|
602
|
+
delivery !== DELIVERY.NO_AWAIT_NO_CACHE &&
|
|
603
|
+
delivery !== DELIVERY.AWAIT_NO_CACHE;
|
|
525
604
|
|
|
526
605
|
await awaitStore();
|
|
527
606
|
if (shouldCache) {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
await addPendingWrites(builder, writeId, { value, type, find });
|
|
607
|
+
await addPendingWrites(builder, writeId, { value, type, find, config });
|
|
608
|
+
Scoped.OutgoingWrites[writeId] = true;
|
|
609
|
+
await Scoped.dispatchingWritesPromise;
|
|
532
610
|
}
|
|
533
611
|
|
|
534
612
|
let retries = 0, hasFinalize;
|
|
@@ -549,55 +627,65 @@ const commitData = async (builder, value, type, config) => {
|
|
|
549
627
|
} else reject(b);
|
|
550
628
|
if (hasFinalize || !instantProcess) return;
|
|
551
629
|
hasFinalize = true;
|
|
552
|
-
if (
|
|
630
|
+
if (shouldCache) {
|
|
631
|
+
if (removeCache) removePendingWrite(builder, writeId, revertCache);
|
|
632
|
+
if (Scoped.OutgoingWrites[writeId])
|
|
633
|
+
delete Scoped.OutgoingWrites[writeId];
|
|
634
|
+
}
|
|
553
635
|
};
|
|
554
636
|
|
|
555
637
|
try {
|
|
556
638
|
if (!disableAuth && await getReachableServer(projectUrl))
|
|
557
639
|
await awaitRefreshToken(projectUrl);
|
|
558
640
|
|
|
559
|
-
const [reqBuilder, [privateKey]] = buildFetchInterface({
|
|
641
|
+
const [reqBuilder, [privateKey]] = await buildFetchInterface({
|
|
560
642
|
body: {
|
|
561
|
-
commands: {
|
|
562
|
-
value,
|
|
643
|
+
commands: stripUndefined({
|
|
644
|
+
value: value && serializeToBase64({ _: value }),
|
|
563
645
|
...isBatchWrite ? { stepping } : {
|
|
564
646
|
path,
|
|
565
647
|
scope: type,
|
|
566
|
-
find
|
|
648
|
+
find: find && serializeToBase64(find)
|
|
567
649
|
}
|
|
568
|
-
},
|
|
650
|
+
}),
|
|
569
651
|
dbName,
|
|
570
652
|
dbUrl
|
|
571
653
|
},
|
|
572
654
|
accessKey,
|
|
573
655
|
serverE2E_PublicKey,
|
|
574
656
|
authToken: disableAuth ? undefined : Scoped.AuthJWTToken[projectUrl],
|
|
575
|
-
uglify
|
|
657
|
+
uglify,
|
|
658
|
+
extraHeaders
|
|
576
659
|
});
|
|
577
660
|
|
|
578
|
-
const
|
|
579
|
-
if (r.simpleError) throw r;
|
|
661
|
+
const data = await buildFetchResult(await fetch((isBatchWrite ? _writeMapDocument : _writeDocument)(projectUrl, uglify), reqBuilder), uglify);
|
|
580
662
|
|
|
581
|
-
const f = uglify ? deserializeE2E(
|
|
663
|
+
const f = uglify ? await deserializeE2E(data, serverE2E_PublicKey, privateKey) : data;
|
|
582
664
|
|
|
583
|
-
finalize({
|
|
665
|
+
finalize({ ...f.statusData }, undefined, { removeCache: true });
|
|
584
666
|
} catch (e) {
|
|
585
667
|
if (e?.simpleError) {
|
|
586
668
|
console.error(`${type} error (${path}), ${e.simpleError?.message}`);
|
|
587
669
|
finalize(undefined, e?.simpleError, { removeCache: true, revertCache: true });
|
|
588
670
|
} else if (
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
671
|
+
[
|
|
672
|
+
DELIVERY.NO_AWAIT,
|
|
673
|
+
DELIVERY.CACHE_NO_AWAIT,
|
|
674
|
+
DELIVERY.NO_AWAIT_NO_CACHE,
|
|
675
|
+
DELIVERY.NO_CACHE
|
|
676
|
+
].includes(delivery)
|
|
593
677
|
) {
|
|
594
678
|
finalize(
|
|
595
679
|
undefined,
|
|
596
680
|
simplifyCaughtError(e).simpleError,
|
|
597
|
-
await getReachableServer(projectUrl) ? { removeCache: true } :
|
|
681
|
+
await getReachableServer(projectUrl) ? { removeCache: true } : undefined
|
|
682
|
+
);
|
|
683
|
+
} else if (retries >= maxRetries) {
|
|
684
|
+
finalize(
|
|
685
|
+
undefined,
|
|
686
|
+
{ error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` },
|
|
687
|
+
{ removeCache: true, revertCache: true }
|
|
598
688
|
);
|
|
599
|
-
} else if (retries > maxRetries) {
|
|
600
|
-
finalize(undefined, { error: 'retry_limit_exceeded', message: `retry exceed limit(${maxRetries})` });
|
|
601
689
|
} else {
|
|
602
690
|
if (delivery === DELIVERY.AWAIT_NO_CACHE) {
|
|
603
691
|
const onlineListener = listenReachableServer(connected => {
|
|
@@ -609,11 +697,36 @@ const commitData = async (builder, value, type, config) => {
|
|
|
609
697
|
);
|
|
610
698
|
}
|
|
611
699
|
}, projectUrl);
|
|
612
|
-
} else if (shouldCache) finalize({ status: '
|
|
700
|
+
} else if (shouldCache) finalize({ status: 'queued' });
|
|
613
701
|
else finalize(undefined, simplifyCaughtError(e).simpleError);
|
|
614
702
|
}
|
|
615
703
|
}
|
|
616
704
|
});
|
|
617
705
|
|
|
618
706
|
return await sendValue();
|
|
619
|
-
}
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
export const trySendPendingWrite = (projectUrl) => {
|
|
710
|
+
if (Scoped.dispatchingWritesPromise) return;
|
|
711
|
+
|
|
712
|
+
Scoped.dispatchingWritesPromise = new Promise(async resolve => {
|
|
713
|
+
const sortedWrite = Object.entries(CacheStore.PendingWrites[projectUrl] || {})
|
|
714
|
+
.filter(([k]) => !Scoped.OutgoingWrites[k])
|
|
715
|
+
.sort((a, b) => a[1].addedOn - b[1].addedOn);
|
|
716
|
+
|
|
717
|
+
for (const [writeId, { snapshot, builder, attempts = 1 }] of sortedWrite) {
|
|
718
|
+
try {
|
|
719
|
+
await commitData(builder, snapshot.value, snapshot.type, { ...snapshot.config, delivery: DELIVERY.NO_AWAIT_NO_CACHE });
|
|
720
|
+
delete CacheStore.PendingWrites[projectUrl][writeId];
|
|
721
|
+
} catch (_) {
|
|
722
|
+
const { maxRetries } = builder;
|
|
723
|
+
if (!maxRetries || attempts >= maxRetries) {
|
|
724
|
+
delete CacheStore.PendingWrites[projectUrl][writeId];
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
resolve();
|
|
729
|
+
Scoped.dispatchingWritesPromise = undefined;
|
|
730
|
+
if (sortedWrite.length && await getReachableServer(projectUrl)) trySendPendingWrite(projectUrl);
|
|
731
|
+
});
|
|
732
|
+
};
|