@wxn0brp/db-core 0.12.12 → 0.12.13
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/dist/customFileCpu.js +9 -1
- package/dist/db/multiStorage.js +15 -24
- package/dist/db/valthera.js +27 -23
- package/dist/helpers/addId.js +12 -0
- package/dist/helpers/assignDataPush.js +7 -1
- package/dist/helpers/forge.js +15 -2
- package/dist/helpers/relation.js +20 -7
- package/dist/utils/action.js +31 -15
- package/dist/utils/hasFields.js +1 -1
- package/dist/utils/hasFieldsAdvanced.js +16 -9
- package/dist/utils/merge.js +7 -1
- package/dist/utils/opKind.d.ts +1 -0
- package/dist/utils/opKind.js +4 -1
- package/dist/utils/security.d.ts +2 -0
- package/dist/utils/security.js +11 -0
- package/dist/utils/updateFindObject.js +9 -4
- package/dist/utils/updateObject.js +29 -12
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/customFileCpu.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import { QueryValidationError } from "./errors.js";
|
|
1
2
|
import { matchObj, updateObj } from "./utils/process.js";
|
|
2
3
|
import { updateFindObject } from "./utils/updateFindObject.js";
|
|
3
4
|
export function pathRepair(path) {
|
|
4
|
-
|
|
5
|
+
const repaired = path.replace(/\/+/g, "/");
|
|
6
|
+
if (repaired.split(/[/\\]+/).includes("..")) {
|
|
7
|
+
throw new QueryValidationError("Path traversal is not allowed", {
|
|
8
|
+
op: "pathRepair",
|
|
9
|
+
path,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return repaired;
|
|
5
13
|
}
|
|
6
14
|
export class CustomFileCpu {
|
|
7
15
|
requireClone;
|
package/dist/db/multiStorage.js
CHANGED
|
@@ -33,15 +33,12 @@ export class MultiBackend extends ActionsBase {
|
|
|
33
33
|
}
|
|
34
34
|
async findOne(config) {
|
|
35
35
|
for (const backend of this.backends) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
36
|
+
const result = await backend.findOne({
|
|
37
|
+
...config,
|
|
38
|
+
});
|
|
39
|
+
if (result !== null && result !== undefined) {
|
|
40
|
+
return result;
|
|
43
41
|
}
|
|
44
|
-
catch { }
|
|
45
42
|
}
|
|
46
43
|
return null;
|
|
47
44
|
}
|
|
@@ -53,14 +50,11 @@ export class MultiBackend extends ActionsBase {
|
|
|
53
50
|
}
|
|
54
51
|
async updateOne(config) {
|
|
55
52
|
for (const backend of this.backends) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return result;
|
|
62
|
-
}
|
|
63
|
-
catch (error) { }
|
|
53
|
+
const result = await backend.updateOne({
|
|
54
|
+
...config,
|
|
55
|
+
});
|
|
56
|
+
if (result)
|
|
57
|
+
return result;
|
|
64
58
|
}
|
|
65
59
|
return null;
|
|
66
60
|
}
|
|
@@ -72,14 +66,11 @@ export class MultiBackend extends ActionsBase {
|
|
|
72
66
|
}
|
|
73
67
|
async removeOne(config) {
|
|
74
68
|
for (const backend of this.backends) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return result;
|
|
81
|
-
}
|
|
82
|
-
catch (error) { }
|
|
69
|
+
const result = await backend.removeOne({
|
|
70
|
+
...config,
|
|
71
|
+
});
|
|
72
|
+
if (result)
|
|
73
|
+
return result;
|
|
83
74
|
}
|
|
84
75
|
return null;
|
|
85
76
|
}
|
package/dist/db/valthera.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { VEE } from "@wxn0brp/event-emitter";
|
|
2
|
-
import
|
|
2
|
+
import * as errors from "../errors.js";
|
|
3
3
|
import { Collection, resolveCollection } from "../helpers/collection.js";
|
|
4
4
|
import { SmartExecutor } from "../helpers/executor.js";
|
|
5
5
|
import { genId } from "../helpers/gen.js";
|
|
6
6
|
import { rewriteOp } from "../helpers/plugin.js";
|
|
7
|
-
import
|
|
8
|
-
import { getOpKind } from "../utils/opKind.js";
|
|
7
|
+
import * as queryDefaults from "../helpers/queryDefaults.js";
|
|
8
|
+
import { getOpKind, isKnownOp } from "../utils/opKind.js";
|
|
9
9
|
import { version } from "../version.js";
|
|
10
10
|
import { Transaction } from "./transaction.js";
|
|
11
11
|
/**
|
|
@@ -52,9 +52,9 @@ export class ValtheraClass {
|
|
|
52
52
|
return;
|
|
53
53
|
const self = this;
|
|
54
54
|
const failInit = (err) => {
|
|
55
|
-
if (isValtheraError(err))
|
|
55
|
+
if (errors.isValtheraError(err))
|
|
56
56
|
throw err;
|
|
57
|
-
throw new AdapterInitError(err, {
|
|
57
|
+
throw new errors.AdapterInitError(err, {
|
|
58
58
|
adapter: self.adapter?.version,
|
|
59
59
|
});
|
|
60
60
|
};
|
|
@@ -101,9 +101,9 @@ export class ValtheraClass {
|
|
|
101
101
|
await self.adapter.close(...args);
|
|
102
102
|
}
|
|
103
103
|
catch (err) {
|
|
104
|
-
if (isValtheraError(err))
|
|
104
|
+
if (errors.isValtheraError(err))
|
|
105
105
|
throw err;
|
|
106
|
-
throw new AdapterCloseError(err, {
|
|
106
|
+
throw new errors.AdapterCloseError(err, {
|
|
107
107
|
adapter: self.adapter?.version,
|
|
108
108
|
});
|
|
109
109
|
}
|
|
@@ -114,7 +114,7 @@ export class ValtheraClass {
|
|
|
114
114
|
await this.init();
|
|
115
115
|
const collection = typeof query === "string" ? query : query?.collection;
|
|
116
116
|
if (!collection && name !== "getCollections")
|
|
117
|
-
throw new QueryValidationError(`Missing "collection" for operation "${String(name)}"`, {
|
|
117
|
+
throw new errors.QueryValidationError(`Missing "collection" for operation "${String(name)}"`, {
|
|
118
118
|
op: name,
|
|
119
119
|
});
|
|
120
120
|
if (txHandle && query && typeof query === "object")
|
|
@@ -132,13 +132,17 @@ export class ValtheraClass {
|
|
|
132
132
|
return plugins[idx++].execute(ctx);
|
|
133
133
|
if (txHandle && ctx.query && typeof ctx.query === "object")
|
|
134
134
|
ctx.query.transaction = txHandle;
|
|
135
|
+
if (!isKnownOp(ctx.op))
|
|
136
|
+
throw new errors.QueryValidationError(`Unknown operation "${ctx.op}"`, {
|
|
137
|
+
op: ctx.op,
|
|
138
|
+
});
|
|
135
139
|
try {
|
|
136
140
|
if (txHandle)
|
|
137
141
|
return await self.adapter[ctx.op](ctx.query, txHandle);
|
|
138
142
|
return await self.executor.addOp(self.adapter[ctx.op].bind(self.adapter), ctx.query, typeof ctx.query === "string" ? ctx.query : ctx.query.collection);
|
|
139
143
|
}
|
|
140
144
|
catch (err) {
|
|
141
|
-
throw wrapAdapterError(err, {
|
|
145
|
+
throw errors.wrapAdapterError(err, {
|
|
142
146
|
op: ctx.op,
|
|
143
147
|
collection: typeof ctx.query === "string" ? ctx.query : ctx.query?.collection,
|
|
144
148
|
adapter: self.adapter?.version,
|
|
@@ -189,67 +193,67 @@ export class ValtheraClass {
|
|
|
189
193
|
* Add data to a database.
|
|
190
194
|
*/
|
|
191
195
|
add(query) {
|
|
192
|
-
applyAddDefaults(query);
|
|
196
|
+
queryDefaults.applyAddDefaults(query);
|
|
193
197
|
return this.execute("add", query);
|
|
194
198
|
}
|
|
195
199
|
bulkAdd(query) {
|
|
196
|
-
applyBulkAddDefaults(query);
|
|
200
|
+
queryDefaults.applyBulkAddDefaults(query);
|
|
197
201
|
return this.execute("bulkAdd", query);
|
|
198
202
|
}
|
|
199
203
|
/**
|
|
200
204
|
* Find data in a database.
|
|
201
205
|
*/
|
|
202
206
|
find(query) {
|
|
203
|
-
applyFindDefaults(query);
|
|
207
|
+
queryDefaults.applyFindDefaults(query);
|
|
204
208
|
return this.execute("find", query);
|
|
205
209
|
}
|
|
206
210
|
/**
|
|
207
211
|
* Find one data entry in a database.
|
|
208
212
|
*/
|
|
209
213
|
findOne(query) {
|
|
210
|
-
applyFindOneDefaults(query);
|
|
214
|
+
queryDefaults.applyFindOneDefaults(query);
|
|
211
215
|
return this.execute("findOne", query);
|
|
212
216
|
}
|
|
213
217
|
/**
|
|
214
218
|
* Update data in a database.
|
|
215
219
|
*/
|
|
216
220
|
update(query) {
|
|
217
|
-
applyUpdateDefaults(query);
|
|
221
|
+
queryDefaults.applyUpdateDefaults(query);
|
|
218
222
|
return this.execute("update", query);
|
|
219
223
|
}
|
|
220
224
|
/**
|
|
221
225
|
* Update one data entry in a database.
|
|
222
226
|
*/
|
|
223
227
|
updateOne(query) {
|
|
224
|
-
applyUpdateDefaults(query);
|
|
228
|
+
queryDefaults.applyUpdateDefaults(query);
|
|
225
229
|
return this.execute("updateOne", query);
|
|
226
230
|
}
|
|
227
231
|
/**
|
|
228
232
|
* Remove data from a database.
|
|
229
233
|
*/
|
|
230
234
|
remove(query) {
|
|
231
|
-
applyRemoveDefaults(query);
|
|
235
|
+
queryDefaults.applyRemoveDefaults(query);
|
|
232
236
|
return this.execute("remove", query);
|
|
233
237
|
}
|
|
234
238
|
/**
|
|
235
239
|
* Remove one data entry from a database.
|
|
236
240
|
*/
|
|
237
241
|
removeOne(query) {
|
|
238
|
-
applyRemoveDefaults(query);
|
|
242
|
+
queryDefaults.applyRemoveDefaults(query);
|
|
239
243
|
return this.execute("removeOne", query);
|
|
240
244
|
}
|
|
241
245
|
/**
|
|
242
246
|
* Asynchronously updates one entry in a database or adds a new one if it doesn't exist.
|
|
243
247
|
*/
|
|
244
248
|
updateOneOrAdd(query) {
|
|
245
|
-
applyUpdateOneOrAddDefaults(query);
|
|
249
|
+
queryDefaults.applyUpdateOneOrAddDefaults(query);
|
|
246
250
|
return this.execute("updateOneOrAdd", query);
|
|
247
251
|
}
|
|
248
252
|
/**
|
|
249
253
|
* Asynchronously removes one entry in a database or adds a new one if it doesn't exist. Usage e.g. for toggling a flag.
|
|
250
254
|
*/
|
|
251
255
|
toggleOne(query) {
|
|
252
|
-
applyToggleOneDefaults(query);
|
|
256
|
+
queryDefaults.applyToggleOneDefaults(query);
|
|
253
257
|
return this.execute("toggleOne", query);
|
|
254
258
|
}
|
|
255
259
|
/**
|
|
@@ -288,7 +292,7 @@ export class ValtheraClass {
|
|
|
288
292
|
});
|
|
289
293
|
}
|
|
290
294
|
count(query) {
|
|
291
|
-
applyCountDefaults(query);
|
|
295
|
+
queryDefaults.applyCountDefaults(query);
|
|
292
296
|
return this.execute("count", query);
|
|
293
297
|
}
|
|
294
298
|
/**
|
|
@@ -333,7 +337,7 @@ export class ValtheraClass {
|
|
|
333
337
|
handle = await this.adapter.beginTransaction(genId(), collections);
|
|
334
338
|
}
|
|
335
339
|
catch (err) {
|
|
336
|
-
throw wrapAdapterError(err, {
|
|
340
|
+
throw errors.wrapAdapterError(err, {
|
|
337
341
|
op: "beginTransaction",
|
|
338
342
|
adapter: this.adapter?.version,
|
|
339
343
|
});
|
|
@@ -349,8 +353,8 @@ export class ValtheraClass {
|
|
|
349
353
|
await tx.rollback();
|
|
350
354
|
}
|
|
351
355
|
catch (rollbackErr) {
|
|
352
|
-
throw new TransactionError("Transaction failed and rollback failed", {
|
|
353
|
-
code: ErrorCodes.TRANSACTION_ROLLBACK_FAILED,
|
|
356
|
+
throw new errors.TransactionError("Transaction failed and rollback failed", {
|
|
357
|
+
code: errors.ErrorCodes.TRANSACTION_ROLLBACK_FAILED,
|
|
354
358
|
cause: new AggregateError([
|
|
355
359
|
err,
|
|
356
360
|
rollbackErr,
|
package/dist/helpers/addId.js
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
import { QueryValidationError } from "../errors.js";
|
|
2
|
+
import { isUnsafeKey } from "../utils/security.js";
|
|
1
3
|
import { genId } from "./gen.js";
|
|
4
|
+
function assertSafeIdKey(idKey) {
|
|
5
|
+
if (!isUnsafeKey(idKey))
|
|
6
|
+
return;
|
|
7
|
+
throw new QueryValidationError("Unsafe idKey", {
|
|
8
|
+
op: "addId",
|
|
9
|
+
idKey,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
2
12
|
export async function addId(query, actions, defaultGen = true) {
|
|
3
13
|
const collection = query.collection;
|
|
4
14
|
const data = query.data;
|
|
@@ -6,6 +16,7 @@ export async function addId(query, actions, defaultGen = true) {
|
|
|
6
16
|
const { idKey = "_id", numberId } = actions.adapterOpts;
|
|
7
17
|
if (!id_gen)
|
|
8
18
|
return data;
|
|
19
|
+
assertSafeIdKey(idKey);
|
|
9
20
|
if (data[idKey])
|
|
10
21
|
return data;
|
|
11
22
|
if (!numberId) {
|
|
@@ -45,6 +56,7 @@ export async function addIdBulk(query, actions, defaultGen = true) {
|
|
|
45
56
|
const { idKey = "_id", numberId } = actions.adapterOpts;
|
|
46
57
|
if (!id_gen)
|
|
47
58
|
return;
|
|
59
|
+
assertSafeIdKey(idKey);
|
|
48
60
|
if (!numberId) {
|
|
49
61
|
for (const data of query.datas) {
|
|
50
62
|
if (!data[idKey])
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isUnsafeKey } from "../utils/security.js";
|
|
1
2
|
export function assignDataPush(data) {
|
|
2
3
|
if (!data)
|
|
3
4
|
return {};
|
|
@@ -7,6 +8,8 @@ export function assignDataPush(data) {
|
|
|
7
8
|
return {};
|
|
8
9
|
const obj = {};
|
|
9
10
|
for (const key of Object.keys(data)) {
|
|
11
|
+
if (isUnsafeKey(key))
|
|
12
|
+
continue;
|
|
10
13
|
if (!key.startsWith("$")) {
|
|
11
14
|
obj[key] = data[key];
|
|
12
15
|
continue;
|
|
@@ -14,7 +17,10 @@ export function assignDataPush(data) {
|
|
|
14
17
|
const dk = data[key];
|
|
15
18
|
if (Array.isArray(dk))
|
|
16
19
|
continue;
|
|
17
|
-
Object.keys(dk).forEach(k =>
|
|
20
|
+
Object.keys(dk).forEach(k => {
|
|
21
|
+
if (!isUnsafeKey(k))
|
|
22
|
+
obj[k] = dk[k];
|
|
23
|
+
});
|
|
18
24
|
}
|
|
19
25
|
return obj;
|
|
20
26
|
}
|
package/dist/helpers/forge.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
import { isUnsafeKey } from "../utils/security.js";
|
|
2
|
+
function hasApiProperty(target, prop) {
|
|
3
|
+
if (isUnsafeKey(prop))
|
|
4
|
+
return false;
|
|
5
|
+
let current = target;
|
|
6
|
+
while (current && current !== Object.prototype) {
|
|
7
|
+
if (Object.hasOwn(current, prop))
|
|
8
|
+
return true;
|
|
9
|
+
current = Object.getPrototypeOf(current);
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
1
13
|
export function forgeTypedValthera(target) {
|
|
2
14
|
return new Proxy(target, {
|
|
3
15
|
get(target, prop, receiver) {
|
|
4
|
-
if (prop
|
|
16
|
+
if (typeof prop === "symbol")
|
|
17
|
+
return Reflect.get(target, prop, receiver);
|
|
18
|
+
if (hasApiProperty(target, prop))
|
|
5
19
|
return Reflect.get(target, prop, receiver);
|
|
6
|
-
}
|
|
7
20
|
return target.c(prop);
|
|
8
21
|
},
|
|
9
22
|
set(target, prop, value, receiver) {
|
package/dist/helpers/relation.js
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { QueryValidationError } from "../errors.js";
|
|
2
|
+
import { hasUnsafeSegment, isUnsafeKey } from "../utils/security.js";
|
|
2
3
|
export function pickByPath(obj, paths) {
|
|
3
4
|
const result = {};
|
|
4
5
|
for (const path of paths) {
|
|
6
|
+
if (hasUnsafeSegment(path))
|
|
7
|
+
continue;
|
|
5
8
|
let src = obj;
|
|
6
9
|
let dst = result;
|
|
7
10
|
for (let i = 0; i < path.length; i++) {
|
|
8
11
|
const k = path[i];
|
|
9
12
|
if (src == null)
|
|
10
13
|
break;
|
|
14
|
+
const isOwn = Object.hasOwn(src, k);
|
|
15
|
+
if (!isOwn && k in Object(src))
|
|
16
|
+
break;
|
|
11
17
|
if (i === path.length - 1) {
|
|
12
18
|
dst[k] = src[k];
|
|
19
|
+
break;
|
|
13
20
|
}
|
|
14
|
-
|
|
15
|
-
dst[k]
|
|
16
|
-
dst
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
if (!Object.hasOwn(dst, k) ||
|
|
22
|
+
typeof dst[k] !== "object" ||
|
|
23
|
+
dst[k] === null ||
|
|
24
|
+
Array.isArray(dst[k]))
|
|
25
|
+
dst[k] = {};
|
|
26
|
+
if (!isOwn)
|
|
27
|
+
break;
|
|
28
|
+
dst = dst[k];
|
|
29
|
+
src = src[k];
|
|
19
30
|
}
|
|
20
31
|
}
|
|
21
32
|
return result;
|
|
@@ -71,10 +82,12 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
71
82
|
if (targets.length === 0)
|
|
72
83
|
return;
|
|
73
84
|
for (const key in cfg) {
|
|
74
|
-
if (!Object.hasOwn(cfg, key))
|
|
85
|
+
if (!Object.hasOwn(cfg, key) || isUnsafeKey(key))
|
|
75
86
|
continue;
|
|
76
87
|
const rel = cfg[key];
|
|
77
88
|
const { pk = "_id", fk = "_id", type = "1", path, as = key, select, dbFindOpts = {}, through, } = rel;
|
|
89
|
+
if (isUnsafeKey(as))
|
|
90
|
+
continue;
|
|
78
91
|
const [dbKey, collection] = path;
|
|
79
92
|
const db = dbs[dbKey];
|
|
80
93
|
if (type === "1") {
|
|
@@ -150,7 +163,7 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
150
163
|
acc[id] ||= [];
|
|
151
164
|
acc[id].push(row);
|
|
152
165
|
return acc;
|
|
153
|
-
},
|
|
166
|
+
}, Object.create(null));
|
|
154
167
|
for (const item of targets)
|
|
155
168
|
item[as] = grouped[item[pk]] || [];
|
|
156
169
|
if (rel.relations)
|
package/dist/utils/action.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { compareSafe } from "./compare.js";
|
|
2
|
+
import { isUnsafeKey } from "./security.js";
|
|
3
|
+
function getOwnValue(obj, key) {
|
|
4
|
+
return Object.hasOwn(obj, key) ? obj[key] : undefined;
|
|
5
|
+
}
|
|
2
6
|
export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
3
7
|
const { dbFindOpts = {} } = query;
|
|
4
8
|
const { reverse = false, offset = 0, sortBy, sortAsc = true, min, max, avg, sum, distinct, groupBy, count, } = dbFindOpts;
|
|
@@ -69,18 +73,18 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
69
73
|
const hasAggregations = min || max || avg || sum || groupBy || count;
|
|
70
74
|
if (hasAggregations) {
|
|
71
75
|
const groups = new Map();
|
|
72
|
-
const groupKeys = Array.isArray(groupBy)
|
|
76
|
+
const groupKeys = (Array.isArray(groupBy)
|
|
73
77
|
? groupBy
|
|
74
78
|
: groupBy
|
|
75
79
|
? [
|
|
76
80
|
groupBy,
|
|
77
81
|
]
|
|
78
|
-
: [];
|
|
82
|
+
: []).filter(key => !isUnsafeKey(key));
|
|
79
83
|
if (groupKeys.length) {
|
|
80
84
|
for (const data of datas) {
|
|
81
|
-
let key = String(data
|
|
85
|
+
let key = String(getOwnValue(data, groupKeys[0]) ?? "");
|
|
82
86
|
for (let i = 1; i < groupKeys.length; i++) {
|
|
83
|
-
key += "|" + String(data
|
|
87
|
+
key += "|" + String(getOwnValue(data, groupKeys[i]) ?? "");
|
|
84
88
|
}
|
|
85
89
|
let group = groups.get(key);
|
|
86
90
|
if (!group) {
|
|
@@ -98,14 +102,18 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
98
102
|
const result = {};
|
|
99
103
|
if (groupKeys.length) {
|
|
100
104
|
const sample = groupItems[0];
|
|
101
|
-
for (const k of groupKeys)
|
|
102
|
-
|
|
105
|
+
for (const k of groupKeys) {
|
|
106
|
+
if (Object.hasOwn(sample, k))
|
|
107
|
+
result[k] = sample[k];
|
|
108
|
+
}
|
|
103
109
|
}
|
|
104
110
|
if (count) {
|
|
105
111
|
for (const [outKey, srcKey] of Object.entries(count)) {
|
|
112
|
+
if (isUnsafeKey(outKey))
|
|
113
|
+
continue;
|
|
106
114
|
let c = 0;
|
|
107
115
|
for (let i = 0; i < groupItems.length; i++) {
|
|
108
|
-
const v = groupItems[i]
|
|
116
|
+
const v = getOwnValue(groupItems[i], srcKey);
|
|
109
117
|
if (v !== undefined && v !== null)
|
|
110
118
|
c++;
|
|
111
119
|
}
|
|
@@ -118,9 +126,11 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
118
126
|
const sumEntries = sum ? Object.entries(sum) : null;
|
|
119
127
|
if (minEntries) {
|
|
120
128
|
for (const [outKey, srcField] of minEntries) {
|
|
129
|
+
if (isUnsafeKey(outKey))
|
|
130
|
+
continue;
|
|
121
131
|
let best = null;
|
|
122
132
|
for (let i = 0; i < groupItems.length; i++) {
|
|
123
|
-
const v = groupItems[i]
|
|
133
|
+
const v = getOwnValue(groupItems[i], srcField);
|
|
124
134
|
if (typeof v === "number") {
|
|
125
135
|
if (best === null || v < best)
|
|
126
136
|
best = v;
|
|
@@ -131,9 +141,11 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
131
141
|
}
|
|
132
142
|
if (maxEntries) {
|
|
133
143
|
for (const [outKey, srcField] of maxEntries) {
|
|
144
|
+
if (isUnsafeKey(outKey))
|
|
145
|
+
continue;
|
|
134
146
|
let best = null;
|
|
135
147
|
for (let i = 0; i < groupItems.length; i++) {
|
|
136
|
-
const v = groupItems[i]
|
|
148
|
+
const v = getOwnValue(groupItems[i], srcField);
|
|
137
149
|
if (typeof v === "number") {
|
|
138
150
|
if (best === null || v > best)
|
|
139
151
|
best = v;
|
|
@@ -144,10 +156,12 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
144
156
|
}
|
|
145
157
|
if (avgEntries) {
|
|
146
158
|
for (const [outKey, srcField] of avgEntries) {
|
|
159
|
+
if (isUnsafeKey(outKey))
|
|
160
|
+
continue;
|
|
147
161
|
let total = 0;
|
|
148
162
|
let c = 0;
|
|
149
163
|
for (let i = 0; i < groupItems.length; i++) {
|
|
150
|
-
const v = groupItems[i]
|
|
164
|
+
const v = getOwnValue(groupItems[i], srcField);
|
|
151
165
|
if (typeof v === "number") {
|
|
152
166
|
total += v;
|
|
153
167
|
c++;
|
|
@@ -158,10 +172,12 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
158
172
|
}
|
|
159
173
|
if (sumEntries) {
|
|
160
174
|
for (const [outKey, srcField] of sumEntries) {
|
|
175
|
+
if (isUnsafeKey(outKey))
|
|
176
|
+
continue;
|
|
161
177
|
let total = 0;
|
|
162
178
|
let c = 0;
|
|
163
179
|
for (let i = 0; i < groupItems.length; i++) {
|
|
164
|
-
const v = groupItems[i]
|
|
180
|
+
const v = getOwnValue(groupItems[i], srcField);
|
|
165
181
|
if (typeof v === "number") {
|
|
166
182
|
total += v;
|
|
167
183
|
c++;
|
|
@@ -174,11 +190,11 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
174
190
|
}
|
|
175
191
|
datas = aggregated;
|
|
176
192
|
}
|
|
177
|
-
if (distinct) {
|
|
193
|
+
if (distinct && !isUnsafeKey(distinct)) {
|
|
178
194
|
const seen = new Set();
|
|
179
195
|
const result = [];
|
|
180
196
|
for (let i = 0; i < datas.length; i++) {
|
|
181
|
-
const val = datas[i]
|
|
197
|
+
const val = getOwnValue(datas[i], distinct);
|
|
182
198
|
const key = typeof val === "object" && val !== null ? JSON.stringify(val) : val;
|
|
183
199
|
if (!seen.has(key)) {
|
|
184
200
|
seen.add(key);
|
|
@@ -195,7 +211,7 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
195
211
|
datas.sort((a, b) => {
|
|
196
212
|
for (const { field, asc = true } of sortBy) {
|
|
197
213
|
const dir = asc ? 1 : -1;
|
|
198
|
-
const cmp = compareSafe(a
|
|
214
|
+
const cmp = compareSafe(getOwnValue(a, field), getOwnValue(b, field)) * dir;
|
|
199
215
|
if (cmp !== 0)
|
|
200
216
|
return cmp;
|
|
201
217
|
}
|
|
@@ -204,7 +220,7 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
204
220
|
}
|
|
205
221
|
else {
|
|
206
222
|
const dir = sortAsc ? 1 : -1;
|
|
207
|
-
datas.sort((a, b) => compareSafe(a
|
|
223
|
+
datas.sort((a, b) => compareSafe(getOwnValue(a, sortBy), getOwnValue(b, sortBy)) * dir);
|
|
208
224
|
}
|
|
209
225
|
}
|
|
210
226
|
if (offset > 0 || limit !== -1) {
|
package/dist/utils/hasFields.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
export function hasFields(obj, fields) {
|
|
8
8
|
const keys = Object.keys(fields);
|
|
9
9
|
return keys.every(key => {
|
|
10
|
-
if (obj
|
|
10
|
+
if (Object.hasOwn(obj, key)) {
|
|
11
11
|
if (typeof fields[key] === "object" && fields[key] !== null) {
|
|
12
12
|
return hasFields(obj[key], fields[key]);
|
|
13
13
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { QueryValidationError } from "../errors.js";
|
|
2
2
|
import { hasFields } from "./hasFields.js";
|
|
3
3
|
import { compareIds } from "./id.js";
|
|
4
|
+
import { isUnsafeKey } from "./security.js";
|
|
4
5
|
/**
|
|
5
6
|
* Checks if an object meets the criteria specified in the fields with operators.
|
|
6
7
|
*/
|
|
@@ -16,6 +17,8 @@ export function hasFieldsAdvanced(obj, fields) {
|
|
|
16
17
|
const subsetFields = {};
|
|
17
18
|
let hasSubset = false;
|
|
18
19
|
for (const key in fields) {
|
|
20
|
+
if (!Object.hasOwn(fields, key) || isUnsafeKey(key))
|
|
21
|
+
continue;
|
|
19
22
|
if (key.charCodeAt(0) === 36) {
|
|
20
23
|
$fields[key.toLowerCase()] = fields[key];
|
|
21
24
|
}
|
|
@@ -31,17 +34,17 @@ export function hasFieldsAdvanced(obj, fields) {
|
|
|
31
34
|
if (Object.keys($fields).length === 0)
|
|
32
35
|
return true;
|
|
33
36
|
// Subset check
|
|
34
|
-
if ("$subset"
|
|
37
|
+
if (Object.hasOwn($fields, "$subset") && !hasFields(obj, $fields["$subset"]))
|
|
35
38
|
return false;
|
|
36
39
|
// Advanced operators
|
|
37
40
|
if (!mainCheck(obj, $fields))
|
|
38
41
|
return false;
|
|
39
42
|
if (!checkNot(obj, $fields))
|
|
40
43
|
return false;
|
|
41
|
-
if ("$and"
|
|
44
|
+
if (Object.hasOwn($fields, "$and")) {
|
|
42
45
|
return $fields["$and"].every((subFields) => hasFieldsAdvanced(obj, subFields));
|
|
43
46
|
}
|
|
44
|
-
if ("$or"
|
|
47
|
+
if (Object.hasOwn($fields, "$or")) {
|
|
45
48
|
return $fields["$or"].some((subFields) => hasFieldsAdvanced(obj, subFields));
|
|
46
49
|
}
|
|
47
50
|
return true;
|
|
@@ -57,9 +60,9 @@ function mainCheck(obj, fields) {
|
|
|
57
60
|
nin: (data, value) => !value.includes(data),
|
|
58
61
|
type: (data, value) => typeof data === value,
|
|
59
62
|
exists: (targetObj, shouldExist, key) => {
|
|
60
|
-
if (shouldExist && !(key
|
|
63
|
+
if (shouldExist && !Object.hasOwn(targetObj, key))
|
|
61
64
|
return false;
|
|
62
|
-
if (!shouldExist && key
|
|
65
|
+
if (!shouldExist && Object.hasOwn(targetObj, key))
|
|
63
66
|
return false;
|
|
64
67
|
return true;
|
|
65
68
|
},
|
|
@@ -93,7 +96,7 @@ function mainCheck(obj, fields) {
|
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
function checkNot(obj, fields) {
|
|
96
|
-
if ("$not"
|
|
99
|
+
if (Object.hasOwn(fields, "$not")) {
|
|
97
100
|
return !hasFieldsAdvanced(obj, fields["$not"]);
|
|
98
101
|
}
|
|
99
102
|
return true;
|
|
@@ -101,11 +104,13 @@ function checkNot(obj, fields) {
|
|
|
101
104
|
function _for(fields, obj, opts) {
|
|
102
105
|
for (const [field, value] of Object.entries(fields)) {
|
|
103
106
|
const fieldRaw = field.slice(1);
|
|
104
|
-
const fieldFn = opts[fieldRaw];
|
|
107
|
+
const fieldFn = Object.hasOwn(opts, fieldRaw) ? opts[fieldRaw] : undefined;
|
|
105
108
|
if (!fieldFn)
|
|
106
109
|
continue;
|
|
107
110
|
for (const [key, val] of Object.entries(value)) {
|
|
108
|
-
|
|
111
|
+
if (isUnsafeKey(key))
|
|
112
|
+
continue;
|
|
113
|
+
const targetValue = obj != null && Object.hasOwn(obj, key) ? obj[key] : undefined;
|
|
109
114
|
if (typeof val === "object" &&
|
|
110
115
|
val !== null &&
|
|
111
116
|
!Array.isArray(val) &&
|
|
@@ -131,7 +136,9 @@ function deepCheck(valueObj, targetObj, fieldFn, fieldRaw) {
|
|
|
131
136
|
if (typeof targetObj !== "object" || targetObj === null)
|
|
132
137
|
return false;
|
|
133
138
|
for (const [k, v] of Object.entries(valueObj)) {
|
|
134
|
-
|
|
139
|
+
if (isUnsafeKey(k))
|
|
140
|
+
continue;
|
|
141
|
+
const targetValue = Object.hasOwn(targetObj, k) ? targetObj[k] : undefined;
|
|
135
142
|
if (typeof v === "object" &&
|
|
136
143
|
v !== null &&
|
|
137
144
|
!Array.isArray(v) &&
|
package/dist/utils/merge.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isUnsafeKey } from "./security.js";
|
|
1
2
|
export function _deepMerge(target, ...sources) {
|
|
2
3
|
if (!sources.length)
|
|
3
4
|
return target;
|
|
@@ -6,6 +7,8 @@ export function _deepMerge(target, ...sources) {
|
|
|
6
7
|
target = {};
|
|
7
8
|
if (isObject(target) && isObject(source)) {
|
|
8
9
|
for (const key in source) {
|
|
10
|
+
if (!Object.hasOwn(source, key) || isUnsafeKey(key))
|
|
11
|
+
continue;
|
|
9
12
|
if (isObject(source[key])) {
|
|
10
13
|
if (!target[key])
|
|
11
14
|
Object.assign(target, {
|
|
@@ -30,8 +33,11 @@ function fastClone(obj) {
|
|
|
30
33
|
if (obj instanceof Date)
|
|
31
34
|
return new Date(obj.getTime());
|
|
32
35
|
const result = {};
|
|
33
|
-
for (const key in obj)
|
|
36
|
+
for (const key in obj) {
|
|
37
|
+
if (!Object.hasOwn(obj, key) || isUnsafeKey(key))
|
|
38
|
+
continue;
|
|
34
39
|
result[key] = fastClone(obj[key]);
|
|
40
|
+
}
|
|
35
41
|
return result;
|
|
36
42
|
}
|
|
37
43
|
export function deepMerge(target, ...sources) {
|
package/dist/utils/opKind.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type PluginOpKind = "read" | "write" | "delete" | "collection" | "index" | "meta" | "control";
|
|
2
2
|
export declare function getOpKind(op: string): PluginOpKind;
|
|
3
|
+
export declare function isKnownOp(op: string): boolean;
|
|
3
4
|
export declare function isMutation(kind: PluginOpKind): kind is "delete" | "write";
|
|
4
5
|
export declare function isCollectionOp(op: string): boolean;
|
|
5
6
|
export declare function isIndexOp(op: string): boolean;
|
package/dist/utils/opKind.js
CHANGED
|
@@ -22,7 +22,10 @@ const OP_KINDS = {
|
|
|
22
22
|
rollbackTransaction: "control",
|
|
23
23
|
};
|
|
24
24
|
export function getOpKind(op) {
|
|
25
|
-
return OP_KINDS[op]
|
|
25
|
+
return Object.hasOwn(OP_KINDS, op) ? OP_KINDS[op] : "write";
|
|
26
|
+
}
|
|
27
|
+
export function isKnownOp(op) {
|
|
28
|
+
return Object.hasOwn(OP_KINDS, op);
|
|
26
29
|
}
|
|
27
30
|
export function isMutation(kind) {
|
|
28
31
|
return kind === "write" || kind === "delete";
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
import { hasUnsafeSegment } from "./security.js";
|
|
1
2
|
function resolvePath(path) {
|
|
2
3
|
return Array.isArray(path) ? path : path.split(".");
|
|
3
4
|
}
|
|
4
5
|
function deleteNested(obj, path) {
|
|
5
6
|
const segs = resolvePath(path);
|
|
6
|
-
if (segs.length === 0)
|
|
7
|
+
if (segs.length === 0 || hasUnsafeSegment(segs))
|
|
7
8
|
return;
|
|
8
9
|
let cur = obj;
|
|
9
10
|
for (let i = 0; i < segs.length - 1; i++) {
|
|
10
11
|
if (cur == null || typeof cur !== "object")
|
|
11
12
|
return;
|
|
13
|
+
if (!Object.hasOwn(cur, segs[i]))
|
|
14
|
+
return;
|
|
12
15
|
cur = cur[segs[i]];
|
|
13
16
|
}
|
|
14
17
|
if (cur != null && typeof cur === "object") {
|
|
@@ -39,12 +42,14 @@ export function updateFindObject(obj, findOpts) {
|
|
|
39
42
|
const result = {};
|
|
40
43
|
for (const field of select) {
|
|
41
44
|
const segs = typeof field === "string" ? field.split(".") : field;
|
|
42
|
-
if (!segs.length)
|
|
45
|
+
if (!segs.length || hasUnsafeSegment(segs))
|
|
43
46
|
continue;
|
|
44
47
|
let src = obj;
|
|
45
48
|
let found = true;
|
|
46
49
|
for (let i = 0; i < segs.length; i++) {
|
|
47
|
-
if (src == null ||
|
|
50
|
+
if (src == null ||
|
|
51
|
+
typeof src !== "object" ||
|
|
52
|
+
!Object.hasOwn(src, segs[i])) {
|
|
48
53
|
found = false;
|
|
49
54
|
break;
|
|
50
55
|
}
|
|
@@ -54,7 +59,7 @@ export function updateFindObject(obj, findOpts) {
|
|
|
54
59
|
let dst = result;
|
|
55
60
|
for (let i = 0; i < segs.length - 1; i++) {
|
|
56
61
|
const s = segs[i];
|
|
57
|
-
if (!(s
|
|
62
|
+
if (!Object.hasOwn(dst, s) ||
|
|
58
63
|
typeof dst[s] !== "object" ||
|
|
59
64
|
Array.isArray(dst[s])) {
|
|
60
65
|
dst[s] = {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { QueryValidationError } from "../errors.js";
|
|
2
2
|
import { _deepMerge } from "./merge.js";
|
|
3
|
+
import { isUnsafeKey } from "./security.js";
|
|
3
4
|
/**
|
|
4
5
|
* Updates an object with new values.
|
|
5
6
|
* @param obj - The object to update.
|
|
@@ -17,6 +18,8 @@ export function updateObjectAdvanced(obj, field) {
|
|
|
17
18
|
let hasDollar = false;
|
|
18
19
|
let hasSubset = false;
|
|
19
20
|
for (const key in field) {
|
|
21
|
+
if (!Object.hasOwn(field, key) || isUnsafeKey(key))
|
|
22
|
+
continue;
|
|
20
23
|
if (key.charCodeAt(0) === 36) {
|
|
21
24
|
$fields[key.toLowerCase()] = field[key];
|
|
22
25
|
hasDollar = true;
|
|
@@ -34,7 +37,7 @@ export function updateObjectAdvanced(obj, field) {
|
|
|
34
37
|
updateObject(obj, subsetFields);
|
|
35
38
|
}
|
|
36
39
|
function deepMerge(obj, fields) {
|
|
37
|
-
if (!("$deepmerge"
|
|
40
|
+
if (!Object.hasOwn(fields, "$deepmerge"))
|
|
38
41
|
return obj;
|
|
39
42
|
if (Object.keys(obj).length === 0)
|
|
40
43
|
return fields.$deepmerge;
|
|
@@ -86,10 +89,16 @@ function mainUpdate(obj, fields) {
|
|
|
86
89
|
];
|
|
87
90
|
}
|
|
88
91
|
if (typeof item === "object" &&
|
|
92
|
+
item !== null &&
|
|
89
93
|
typeof updater === "object" &&
|
|
94
|
+
updater !== null &&
|
|
90
95
|
!Array.isArray(item) &&
|
|
91
96
|
!Array.isArray(updater)) {
|
|
92
|
-
Object.
|
|
97
|
+
for (const key of Object.keys(updater)) {
|
|
98
|
+
if (isUnsafeKey(key))
|
|
99
|
+
continue;
|
|
100
|
+
item[key] = updater[key];
|
|
101
|
+
}
|
|
93
102
|
return item;
|
|
94
103
|
}
|
|
95
104
|
return updater;
|
|
@@ -98,7 +107,7 @@ function mainUpdate(obj, fields) {
|
|
|
98
107
|
if (typeof item === "number" && typeof updater === "number") {
|
|
99
108
|
return item + updater;
|
|
100
109
|
}
|
|
101
|
-
if (!(key
|
|
110
|
+
if (!Object.hasOwn(deepObj, key))
|
|
102
111
|
return updater;
|
|
103
112
|
throw new QueryValidationError(`Cannot increment non-numeric value at key: ${key}`, {
|
|
104
113
|
op: "update",
|
|
@@ -109,7 +118,7 @@ function mainUpdate(obj, fields) {
|
|
|
109
118
|
if (typeof item === "number" && typeof updater === "number") {
|
|
110
119
|
return item - updater;
|
|
111
120
|
}
|
|
112
|
-
if (!(key
|
|
121
|
+
if (!Object.hasOwn(deepObj, key))
|
|
113
122
|
return -updater;
|
|
114
123
|
throw new QueryValidationError(`Cannot decrement non-numeric value at key: ${key}`, {
|
|
115
124
|
op: "update",
|
|
@@ -117,14 +126,16 @@ function mainUpdate(obj, fields) {
|
|
|
117
126
|
});
|
|
118
127
|
},
|
|
119
128
|
rename: (_, newKey, oldKey, deepObj) => {
|
|
120
|
-
if (oldKey
|
|
129
|
+
if (isUnsafeKey(oldKey) || isUnsafeKey(newKey))
|
|
130
|
+
return;
|
|
131
|
+
if (Object.hasOwn(deepObj, oldKey)) {
|
|
121
132
|
deepObj[newKey] = deepObj[oldKey];
|
|
122
133
|
delete deepObj[oldKey];
|
|
123
134
|
}
|
|
124
135
|
},
|
|
125
136
|
set: (_, value) => value,
|
|
126
137
|
unset: (_, __, key, deepObj) => {
|
|
127
|
-
if (key
|
|
138
|
+
if (Object.hasOwn(deepObj, key))
|
|
128
139
|
delete deepObj[key];
|
|
129
140
|
},
|
|
130
141
|
});
|
|
@@ -135,20 +146,24 @@ function mainUpdate(obj, fields) {
|
|
|
135
146
|
* @param newVal - An object containing new values to update in the target object.
|
|
136
147
|
*/
|
|
137
148
|
function updateObject(obj, newVal) {
|
|
138
|
-
for (const key in newVal)
|
|
139
|
-
if (Object.hasOwn(newVal, key))
|
|
140
|
-
|
|
149
|
+
for (const key in newVal) {
|
|
150
|
+
if (!Object.hasOwn(newVal, key) || isUnsafeKey(key))
|
|
151
|
+
continue;
|
|
152
|
+
obj[key] = newVal[key];
|
|
153
|
+
}
|
|
141
154
|
return obj;
|
|
142
155
|
}
|
|
143
156
|
function _for(fields, obj, opts) {
|
|
144
157
|
for (const [field, value] of Object.entries(fields)) {
|
|
145
158
|
const fieldRaw = field.slice(1);
|
|
146
|
-
const fieldFn = opts[fieldRaw];
|
|
159
|
+
const fieldFn = Object.hasOwn(opts, fieldRaw) ? opts[fieldRaw] : undefined;
|
|
147
160
|
if (!fieldFn)
|
|
148
161
|
continue;
|
|
149
162
|
for (const [key, val] of Object.entries(value)) {
|
|
163
|
+
if (isUnsafeKey(key))
|
|
164
|
+
continue;
|
|
150
165
|
if (typeof val === "object" && val !== null && !Array.isArray(val)) {
|
|
151
|
-
if (!obj[key])
|
|
166
|
+
if (!Object.hasOwn(obj, key) || !obj[key])
|
|
152
167
|
obj[key] = {};
|
|
153
168
|
deepUpdateCheck(val, obj[key], fieldFn);
|
|
154
169
|
}
|
|
@@ -162,8 +177,10 @@ function _for(fields, obj, opts) {
|
|
|
162
177
|
}
|
|
163
178
|
function deepUpdateCheck(valueObj, targetObj, fieldFn) {
|
|
164
179
|
for (const [k, v] of Object.entries(valueObj)) {
|
|
180
|
+
if (isUnsafeKey(k))
|
|
181
|
+
continue;
|
|
165
182
|
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
166
|
-
if (!targetObj[k])
|
|
183
|
+
if (!Object.hasOwn(targetObj, k) || !targetObj[k])
|
|
167
184
|
targetObj[k] = {};
|
|
168
185
|
deepUpdateCheck(v, targetObj[k], fieldFn);
|
|
169
186
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.12.
|
|
1
|
+
export declare const version = "0.12.13";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.12.
|
|
1
|
+
export const version = "0.12.13";
|