@wxn0brp/vql 0.8.3 → 0.9.0
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/cpu/request.js +7 -0
- package/dist/cpu/string/args.d.ts +1 -0
- package/dist/cpu/string/args.js +102 -0
- package/dist/cpu/string/build.d.ts +2 -0
- package/dist/cpu/string/build.js +44 -0
- package/dist/cpu/string/index.js +6 -148
- package/dist/cpu/string/utils.js +5 -3
- package/dist/helpers/apiAbstract.d.ts +18 -22
- package/dist/helpers/apiAbstract.js +8 -5
- package/dist/helpers/lowAdapter.d.ts +6 -0
- package/dist/helpers/lowAdapter.js +6 -0
- package/dist/helpers/sheet.js +1 -0
- package/dist/helpers/valid.js +9 -0
- package/dist/permissions/request.js +7 -0
- package/dist/types/vql.d.ts +8 -0
- package/dist/vql.d.ts +106 -43
- package/package.json +11 -10
package/dist/cpu/request.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { checkRequestPermission } from "../permissions/index.js";
|
|
2
2
|
import { parseSelect } from "./utils.js";
|
|
3
|
+
import { LowAdapter } from "../helpers/lowAdapter.js";
|
|
3
4
|
export async function executeQuery(cpu, query, user) {
|
|
4
5
|
if (!query.db || !cpu.dbInstances[query.db])
|
|
5
6
|
return { err: true, msg: `Invalid query - db "${query.db || "undefined"}" not found`, c: 400 };
|
|
6
7
|
const db = cpu.dbInstances[query.db];
|
|
8
|
+
if (db instanceof LowAdapter)
|
|
9
|
+
return await db.resolver(query, user);
|
|
7
10
|
const operation = Object.keys(query.d)[0];
|
|
8
11
|
if (!cpu.config.noCheckPermissions && !await checkRequestPermission(cpu.config, cpu.permValidFn, user, query)) {
|
|
9
12
|
return { err: true, msg: "Permission denied", c: 403 };
|
|
@@ -51,6 +54,10 @@ export async function executeQuery(cpu, query, user) {
|
|
|
51
54
|
opts.id_gen = params.id_gen;
|
|
52
55
|
return db.updateOneOrAdd(params.collection, params.search, params.updater, opts);
|
|
53
56
|
}
|
|
57
|
+
else if (operation === "toggleOne") {
|
|
58
|
+
const params = query.d[operation];
|
|
59
|
+
return db.toggleOne(params.collection, params.search, params.data);
|
|
60
|
+
}
|
|
54
61
|
else if (operation === "removeCollection") {
|
|
55
62
|
const params = query.d[operation];
|
|
56
63
|
return db.removeCollection(params.collection);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseArgs(input: string): Record<string, any>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export function parseArgs(input) {
|
|
2
|
+
const result = {};
|
|
3
|
+
const tokens = [];
|
|
4
|
+
let current = "";
|
|
5
|
+
let inQuotes = false;
|
|
6
|
+
let escape = false;
|
|
7
|
+
let objTree = 0;
|
|
8
|
+
for (let i = 0; i < input.length; i++) {
|
|
9
|
+
const char = input[i];
|
|
10
|
+
if (escape) {
|
|
11
|
+
current += char;
|
|
12
|
+
escape = false;
|
|
13
|
+
}
|
|
14
|
+
else if (char === "\\") {
|
|
15
|
+
escape = true;
|
|
16
|
+
}
|
|
17
|
+
else if (!inQuotes && (char === "{" || char === "[")) {
|
|
18
|
+
objTree++;
|
|
19
|
+
current += char;
|
|
20
|
+
}
|
|
21
|
+
else if (!inQuotes && (char === "}" || char === "]")) {
|
|
22
|
+
objTree--;
|
|
23
|
+
current += char;
|
|
24
|
+
if (objTree === 0) {
|
|
25
|
+
tokens.push(current);
|
|
26
|
+
current = "";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (!objTree && (char === "'" || char === '"' || char === "`")) {
|
|
30
|
+
if (inQuotes === char) {
|
|
31
|
+
inQuotes = false;
|
|
32
|
+
tokens.push(`"` + current + `"`);
|
|
33
|
+
current = "";
|
|
34
|
+
}
|
|
35
|
+
else if (typeof inQuotes === "boolean") {
|
|
36
|
+
inQuotes = char;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
current += char;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (!inQuotes && (char === " " || char === "=" || char === "<" || char === ">")) {
|
|
43
|
+
if (current !== "") {
|
|
44
|
+
if (char === "<" || char === ">") {
|
|
45
|
+
let type = char === ">" ? "gt" : "lt";
|
|
46
|
+
if (i < input.length - 1 && input[i + 1] === "=") {
|
|
47
|
+
type += "e";
|
|
48
|
+
i++;
|
|
49
|
+
}
|
|
50
|
+
const split = current.split(".");
|
|
51
|
+
if (split.length > 1) {
|
|
52
|
+
const original = split.shift();
|
|
53
|
+
const operation = "$" + type;
|
|
54
|
+
split.unshift(operation);
|
|
55
|
+
split.unshift(original);
|
|
56
|
+
current = split.join(".");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
current = "$" + type + "." + current;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
tokens.push(current);
|
|
63
|
+
current = "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
current += char;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (current !== "")
|
|
71
|
+
tokens.push(current);
|
|
72
|
+
for (let i = 0; i < tokens.length; i += 2) {
|
|
73
|
+
const key = tokens[i];
|
|
74
|
+
let value = tokens[i + 1] ?? true;
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
const trimmed = value.trim();
|
|
77
|
+
if (trimmed === "") {
|
|
78
|
+
value = true;
|
|
79
|
+
}
|
|
80
|
+
else if (/^".*"$/.test(trimmed)) {
|
|
81
|
+
value = trimmed.slice(1, -1);
|
|
82
|
+
}
|
|
83
|
+
else if (trimmed.toLowerCase() === "true") {
|
|
84
|
+
value = true;
|
|
85
|
+
}
|
|
86
|
+
else if (trimmed.toLowerCase() === "false") {
|
|
87
|
+
value = false;
|
|
88
|
+
}
|
|
89
|
+
else if (!isNaN(Number(trimmed))) {
|
|
90
|
+
value = Number(trimmed);
|
|
91
|
+
}
|
|
92
|
+
else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
93
|
+
try {
|
|
94
|
+
value = JSON.parse(trimmed);
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
result[key] = value;
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { convertSearchObjToSearchArray } from "./utils.js";
|
|
2
|
+
export function buildVQL(db, op, collection, query) {
|
|
3
|
+
return "relations" in query ?
|
|
4
|
+
buildRelation(db, collection, query) :
|
|
5
|
+
buildQuery(db, op, collection, query);
|
|
6
|
+
}
|
|
7
|
+
function buildRelation(db, collection, query) {
|
|
8
|
+
const relations = {};
|
|
9
|
+
for (const key in query.relations) {
|
|
10
|
+
const value = query.relations[key];
|
|
11
|
+
relations[key] = {
|
|
12
|
+
path: [value.db || db, value.c || key],
|
|
13
|
+
...value
|
|
14
|
+
};
|
|
15
|
+
delete relations[key].db;
|
|
16
|
+
delete relations[key].c;
|
|
17
|
+
}
|
|
18
|
+
if ("select" in query)
|
|
19
|
+
query.select = convertSearchObjToSearchArray(query.select);
|
|
20
|
+
return {
|
|
21
|
+
r: {
|
|
22
|
+
path: [db, collection],
|
|
23
|
+
...query,
|
|
24
|
+
relations,
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function buildQuery(db, op, collection, query) {
|
|
29
|
+
if (query.fields && !query.select) {
|
|
30
|
+
query.select = query.fields;
|
|
31
|
+
delete query.fields;
|
|
32
|
+
}
|
|
33
|
+
if ("select" in query)
|
|
34
|
+
query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
|
|
35
|
+
return {
|
|
36
|
+
db,
|
|
37
|
+
d: {
|
|
38
|
+
[op]: {
|
|
39
|
+
collection,
|
|
40
|
+
...query,
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
package/dist/cpu/string/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseArgs } from "./args.js";
|
|
2
|
+
import { buildVQL } from "./build.js";
|
|
3
|
+
import { extractMeta } from "./utils.js";
|
|
2
4
|
const aliases = {
|
|
3
5
|
s: "search",
|
|
4
6
|
f: "fields",
|
|
@@ -8,152 +10,6 @@ const aliases = {
|
|
|
8
10
|
e: "select",
|
|
9
11
|
u: "updater",
|
|
10
12
|
};
|
|
11
|
-
function parseArgs(input) {
|
|
12
|
-
const result = {};
|
|
13
|
-
const tokens = [];
|
|
14
|
-
let current = "";
|
|
15
|
-
let inQuotes = false;
|
|
16
|
-
let escape = false;
|
|
17
|
-
let objTree = 0;
|
|
18
|
-
for (let i = 0; i < input.length; i++) {
|
|
19
|
-
const char = input[i];
|
|
20
|
-
if (escape) {
|
|
21
|
-
current += char;
|
|
22
|
-
escape = false;
|
|
23
|
-
}
|
|
24
|
-
else if (char === "\\") {
|
|
25
|
-
escape = true;
|
|
26
|
-
}
|
|
27
|
-
else if (!inQuotes && (char === "{" || char === "[")) {
|
|
28
|
-
objTree++;
|
|
29
|
-
current += char;
|
|
30
|
-
}
|
|
31
|
-
else if (!inQuotes && (char === "}" || char === "]")) {
|
|
32
|
-
objTree--;
|
|
33
|
-
current += char;
|
|
34
|
-
if (objTree === 0) {
|
|
35
|
-
tokens.push(current);
|
|
36
|
-
current = "";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
else if (!objTree && (char === "'" || char === '"' || char === "`")) {
|
|
40
|
-
if (inQuotes === char) {
|
|
41
|
-
inQuotes = false;
|
|
42
|
-
tokens.push(`"` + current + `"`);
|
|
43
|
-
current = "";
|
|
44
|
-
}
|
|
45
|
-
else if (typeof inQuotes === "boolean") {
|
|
46
|
-
inQuotes = char;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
current += char;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else if (!inQuotes && (char === " " || char === "=" || char === "<" || char === ">")) {
|
|
53
|
-
if (current !== "") {
|
|
54
|
-
if (char === "<" || char === ">") {
|
|
55
|
-
let type = char === ">" ? "gt" : "lt";
|
|
56
|
-
if (i < input.length - 1 && input[i + 1] === "=") {
|
|
57
|
-
type += "e";
|
|
58
|
-
i++;
|
|
59
|
-
}
|
|
60
|
-
const split = current.split(".");
|
|
61
|
-
if (split.length > 1) {
|
|
62
|
-
const original = split.shift();
|
|
63
|
-
const operation = "$" + type;
|
|
64
|
-
split.unshift(operation);
|
|
65
|
-
split.unshift(original);
|
|
66
|
-
current = split.join(".");
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
current = "$" + type + "." + current;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
tokens.push(current);
|
|
73
|
-
current = "";
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
current += char;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (current !== "") {
|
|
81
|
-
tokens.push(current);
|
|
82
|
-
}
|
|
83
|
-
for (let i = 0; i < tokens.length; i += 2) {
|
|
84
|
-
const key = tokens[i];
|
|
85
|
-
let value = tokens[i + 1] ?? true;
|
|
86
|
-
if (typeof value === "string") {
|
|
87
|
-
const trimmed = value.trim();
|
|
88
|
-
if (trimmed === "") {
|
|
89
|
-
value = true;
|
|
90
|
-
}
|
|
91
|
-
else if (/^".*"$/.test(trimmed)) {
|
|
92
|
-
value = trimmed.slice(1, -1);
|
|
93
|
-
}
|
|
94
|
-
else if (trimmed.toLowerCase() === "true") {
|
|
95
|
-
value = true;
|
|
96
|
-
}
|
|
97
|
-
else if (trimmed.toLowerCase() === "false") {
|
|
98
|
-
value = false;
|
|
99
|
-
}
|
|
100
|
-
else if (!isNaN(Number(trimmed))) {
|
|
101
|
-
value = Number(trimmed);
|
|
102
|
-
}
|
|
103
|
-
else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
|
|
104
|
-
try {
|
|
105
|
-
value = JSON.parse(trimmed);
|
|
106
|
-
}
|
|
107
|
-
catch { }
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
result[key] = value;
|
|
111
|
-
}
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
function buildVQL(db, op, collection, query) {
|
|
115
|
-
const hasRelations = "relations" in query;
|
|
116
|
-
if (hasRelations) {
|
|
117
|
-
const relations = {};
|
|
118
|
-
for (const key in query.relations) {
|
|
119
|
-
const value = query.relations[key];
|
|
120
|
-
relations[key] = {
|
|
121
|
-
path: [value.db || db, value.c || key],
|
|
122
|
-
...value
|
|
123
|
-
};
|
|
124
|
-
delete relations[key].db;
|
|
125
|
-
delete relations[key].c;
|
|
126
|
-
}
|
|
127
|
-
if ("select" in query) {
|
|
128
|
-
query.select = convertSearchObjToSearchArray(query.select);
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
r: {
|
|
132
|
-
path: [db, collection],
|
|
133
|
-
...query,
|
|
134
|
-
relations,
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
if (query.fields && !query.select) {
|
|
140
|
-
query.select = query.fields;
|
|
141
|
-
delete query.fields;
|
|
142
|
-
}
|
|
143
|
-
if ("select" in query) {
|
|
144
|
-
query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
|
|
145
|
-
}
|
|
146
|
-
return {
|
|
147
|
-
db,
|
|
148
|
-
d: {
|
|
149
|
-
[op]: {
|
|
150
|
-
collection,
|
|
151
|
-
...query,
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
13
|
export function parseVQLS(query) {
|
|
158
14
|
const { db, op, collection, body } = extractMeta(query);
|
|
159
15
|
const parsed = parseArgs(body);
|
|
@@ -167,7 +23,9 @@ export function parseVQLS(query) {
|
|
|
167
23
|
const key = keys[i];
|
|
168
24
|
if (i < keys.length - 1) {
|
|
169
25
|
if (!(key in obj)) {
|
|
170
|
-
|
|
26
|
+
const nextKey = keys[i + 1];
|
|
27
|
+
const isArrayIndex = /^\d+$/.test(nextKey);
|
|
28
|
+
obj[key] = isArrayIndex ? [] : {};
|
|
171
29
|
}
|
|
172
30
|
obj = obj[key];
|
|
173
31
|
}
|
package/dist/cpu/string/utils.js
CHANGED
|
@@ -2,13 +2,15 @@ const opPrefix = {
|
|
|
2
2
|
"-": "remove",
|
|
3
3
|
"+": "add",
|
|
4
4
|
"~": "update",
|
|
5
|
+
"?": "updateOneOrAdd",
|
|
6
|
+
"^": "toggleOne"
|
|
5
7
|
};
|
|
6
8
|
const operations = [
|
|
7
9
|
"add",
|
|
8
10
|
"find", "findOne",
|
|
9
11
|
"update", "updateOne",
|
|
10
12
|
"remove", "removeOne",
|
|
11
|
-
"updateOneOrAdd",
|
|
13
|
+
"updateOneOrAdd", "toggleOne",
|
|
12
14
|
"ensureCollection", "issetCollection",
|
|
13
15
|
];
|
|
14
16
|
/**
|
|
@@ -55,7 +57,7 @@ export function extractMeta(input) {
|
|
|
55
57
|
let collection = "";
|
|
56
58
|
let body = "";
|
|
57
59
|
// Determine operation and collection based on special characters
|
|
58
|
-
if (["!", "-", "+", "~"].some(c => split[0].includes(c)) || // Check if operation is indicated by special character
|
|
60
|
+
if (["!", "-", "+", "~", "?", "^"].some(c => split[0].includes(c)) || // Check if operation is indicated by special character
|
|
59
61
|
[".", ":", "{"].some(c => split[1].includes(c)) // Check if query body is indicated by special character
|
|
60
62
|
) {
|
|
61
63
|
let temp = split.shift();
|
|
@@ -93,7 +95,7 @@ export function convertSearchObjToSearchArray(obj, parentKeys = []) {
|
|
|
93
95
|
if (!value) {
|
|
94
96
|
return acc;
|
|
95
97
|
}
|
|
96
|
-
else if (typeof value ===
|
|
98
|
+
else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
97
99
|
return [...acc, ...convertSearchObjToSearchArray(value, currentPath)];
|
|
98
100
|
}
|
|
99
101
|
else {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VQL_Query_CRUD_Keys } from "../types/vql.js";
|
|
1
2
|
import { ValtheraCompatible } from "@wxn0brp/db-core";
|
|
2
3
|
type ResolverFn<TArgs extends any[] = any[], TReturn = any> = (...args: TArgs) => Promise<TReturn>;
|
|
3
4
|
export interface ValtheraResolverMeta {
|
|
@@ -18,47 +19,42 @@ export interface ValtheraResolver {
|
|
|
18
19
|
find?: ResolverFn<[
|
|
19
20
|
collection: string,
|
|
20
21
|
search: any,
|
|
21
|
-
context?: any,
|
|
22
22
|
options?: any,
|
|
23
|
-
findOpts?: any
|
|
23
|
+
findOpts?: any,
|
|
24
|
+
context?: any
|
|
24
25
|
], any[]>;
|
|
25
26
|
findOne?: ResolverFn<[
|
|
26
27
|
collection: string,
|
|
27
28
|
search: any,
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
findOpts?: any,
|
|
30
|
+
context?: any
|
|
30
31
|
], any | null>;
|
|
31
32
|
update?: ResolverFn<[collection: string, search: any, updater: any, context?: any], boolean>;
|
|
32
33
|
updateOne?: ResolverFn<[collection: string, search: any, updater: any, context?: any], boolean>;
|
|
33
|
-
updateOneOrAdd?: ResolverFn<[
|
|
34
|
-
|
|
35
|
-
search: any,
|
|
36
|
-
updater: any,
|
|
37
|
-
add_arg?: any,
|
|
38
|
-
context?: any,
|
|
39
|
-
id_gen?: boolean
|
|
40
|
-
], boolean>;
|
|
34
|
+
updateOneOrAdd?: ResolverFn<[collection: string, search: any, updater: any, opts?: any], boolean>;
|
|
35
|
+
toggleOne?: ResolverFn<[collection: string, search: any, data?: any, context?: any], boolean>;
|
|
41
36
|
remove?: ResolverFn<[collection: string, search: any, context?: any], boolean>;
|
|
42
37
|
removeOne?: ResolverFn<[collection: string, search: any, context?: any], boolean>;
|
|
43
38
|
removeCollection?: ResolverFn<[collection: string], boolean>;
|
|
44
39
|
}
|
|
40
|
+
export type Operation = Exclude<VQL_Query_CRUD_Keys, "f">;
|
|
45
41
|
export declare function createValtheraAdapter(resolver: ValtheraResolver, extendedFind?: boolean): ValtheraCompatible;
|
|
46
|
-
export type Operation = "add" | "find" | "findOne" | "update" | "updateOne" | "updateOneOrAdd" | "remove" | "removeOne" | "removeCollection";
|
|
47
42
|
export declare class AdapterBuilder {
|
|
48
43
|
private catchCb;
|
|
49
44
|
private handlers;
|
|
50
45
|
private collections;
|
|
51
46
|
constructor(catchCb?: (e: any, op: string, args: any[]) => void);
|
|
52
47
|
register(op: Operation, collection: string, fn: Function): this;
|
|
53
|
-
add(collection: string, fn:
|
|
54
|
-
find(collection: string, fn:
|
|
55
|
-
findOne(collection: string, fn:
|
|
56
|
-
update(collection: string, fn:
|
|
57
|
-
updateOne(collection: string, fn:
|
|
58
|
-
updateOneOrAdd(collection: string, fn:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
add(collection: string, fn: ValtheraResolver["add"]): this;
|
|
49
|
+
find(collection: string, fn: ValtheraResolver["find"]): this;
|
|
50
|
+
findOne(collection: string, fn: ValtheraResolver["findOne"]): this;
|
|
51
|
+
update(collection: string, fn: ValtheraResolver["update"]): this;
|
|
52
|
+
updateOne(collection: string, fn: ValtheraResolver["updateOne"]): this;
|
|
53
|
+
updateOneOrAdd(collection: string, fn: ValtheraResolver["updateOneOrAdd"]): this;
|
|
54
|
+
toggleOne(collection: string, fn: ValtheraResolver["toggleOne"]): this;
|
|
55
|
+
remove(collection: string, fn: ValtheraResolver["remove"]): this;
|
|
56
|
+
removeOne(collection: string, fn: ValtheraResolver["removeOne"]): this;
|
|
57
|
+
removeCollection(collection: string, fn: ValtheraResolver["removeCollection"]): this;
|
|
62
58
|
getAdapter(extendedFind?: boolean): ValtheraCompatible;
|
|
63
59
|
}
|
|
64
60
|
export {};
|
|
@@ -4,7 +4,8 @@ const list = [
|
|
|
4
4
|
"ensureCollection", "issetCollection", "getCollections", "removeCollection",
|
|
5
5
|
"add",
|
|
6
6
|
"find", "findOne",
|
|
7
|
-
"update", "updateOne",
|
|
7
|
+
"update", "updateOne",
|
|
8
|
+
"updateOneOrAdd", "toggleOne",
|
|
8
9
|
"remove", "removeOne"
|
|
9
10
|
];
|
|
10
11
|
export function createValtheraAdapter(resolver, extendedFind = false) {
|
|
@@ -26,8 +27,8 @@ export function createValtheraAdapter(resolver, extendedFind = false) {
|
|
|
26
27
|
let data = await safe(resolver.find)(col, search, options, findOpts, context);
|
|
27
28
|
if (options?.reverse)
|
|
28
29
|
data.reverse();
|
|
29
|
-
if (options?.
|
|
30
|
-
data = data.slice(0, options?.
|
|
30
|
+
if (options?.limit !== -1 && data.length > options?.limit)
|
|
31
|
+
data = data.slice(0, options?.limit);
|
|
31
32
|
data = data.map(d => updateFindObject(d, findOpts || {}));
|
|
32
33
|
return data;
|
|
33
34
|
};
|
|
@@ -70,6 +71,9 @@ export class AdapterBuilder {
|
|
|
70
71
|
updateOneOrAdd(collection, fn) {
|
|
71
72
|
return this.register("updateOneOrAdd", collection, fn);
|
|
72
73
|
}
|
|
74
|
+
toggleOne(collection, fn) {
|
|
75
|
+
return this.register("toggleOne", collection, fn);
|
|
76
|
+
}
|
|
73
77
|
remove(collection, fn) {
|
|
74
78
|
return this.register("remove", collection, fn);
|
|
75
79
|
}
|
|
@@ -103,9 +107,8 @@ export class AdapterBuilder {
|
|
|
103
107
|
return true;
|
|
104
108
|
},
|
|
105
109
|
}, extendedFind);
|
|
106
|
-
for (const name of
|
|
110
|
+
for (const name of list.slice(4))
|
|
107
111
|
adapter[name] = (...args) => resolve(name, ...args);
|
|
108
|
-
}
|
|
109
112
|
return adapter;
|
|
110
113
|
}
|
|
111
114
|
}
|
package/dist/helpers/sheet.js
CHANGED
package/dist/helpers/valid.js
CHANGED
|
@@ -131,6 +131,15 @@ function validD(query) {
|
|
|
131
131
|
return emptyErr("'updateOneOrAdd' operation 'id_gen' property must be a boolean.");
|
|
132
132
|
return true;
|
|
133
133
|
}
|
|
134
|
+
if (key === "toggleOne") {
|
|
135
|
+
const op = d.toggleOne;
|
|
136
|
+
if (!isObj(op.search, false))
|
|
137
|
+
return emptyErr("'toggleOne' operation requires a 'search' object.");
|
|
138
|
+
if ("data" in op && !isObj(op.data, false))
|
|
139
|
+
return emptyErr("'toggleOne' operation 'data' property must be an object.");
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
const n = key;
|
|
134
143
|
return emptyErr(`Unknown or invalid CRUD operation: '${key}'`);
|
|
135
144
|
}
|
|
136
145
|
export function validateVql(query) {
|
|
@@ -34,12 +34,19 @@ export async function extractPaths(config, query) {
|
|
|
34
34
|
permPaths.paths.push({ filed: extractPathsFromData(qo.search), p: PermCRUD.READ });
|
|
35
35
|
permPaths.paths.push({ filed: extractPathsFromData(qo.updater), p: PermCRUD.UPDATE });
|
|
36
36
|
break;
|
|
37
|
+
case "toggleOne":
|
|
38
|
+
permPaths.paths.push({ c: PermCRUD.DELETE });
|
|
39
|
+
permPaths.paths.push({ c: PermCRUD.CREATE });
|
|
40
|
+
break;
|
|
37
41
|
case "ensureCollection":
|
|
38
42
|
case "getCollections":
|
|
39
43
|
case "issetCollection":
|
|
40
44
|
case "removeCollection":
|
|
41
45
|
permPaths.paths.push({ c: PermCRUD.COLLECTION });
|
|
42
46
|
break;
|
|
47
|
+
default:
|
|
48
|
+
const n = operation;
|
|
49
|
+
break;
|
|
43
50
|
}
|
|
44
51
|
permPaths.paths = (await Promise.all(permPaths.paths.map(async (path) => {
|
|
45
52
|
if (!path.filed)
|
package/dist/types/vql.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ export interface VQL_OP_UpdateOneOrAdd<T = any> {
|
|
|
39
39
|
add_arg?: Arg<T>;
|
|
40
40
|
id_gen?: boolean;
|
|
41
41
|
}
|
|
42
|
+
export interface VQL_OP_ToggleOne<T = any> {
|
|
43
|
+
collection: string;
|
|
44
|
+
search: Search<T>;
|
|
45
|
+
data?: Arg<T>;
|
|
46
|
+
}
|
|
42
47
|
export interface VQL_OP_CollectionOperation {
|
|
43
48
|
collection: string;
|
|
44
49
|
}
|
|
@@ -61,6 +66,8 @@ export type VQL_Query_CRUD_Data<T = any> = {
|
|
|
61
66
|
removeOne: VQL_OP_Remove<T>;
|
|
62
67
|
} | {
|
|
63
68
|
updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
|
|
69
|
+
} | {
|
|
70
|
+
toggleOne: VQL_OP_ToggleOne<T>;
|
|
64
71
|
} | {
|
|
65
72
|
removeCollection: VQL_OP_CollectionOperation;
|
|
66
73
|
} | {
|
|
@@ -70,6 +77,7 @@ export type VQL_Query_CRUD_Data<T = any> = {
|
|
|
70
77
|
} | {
|
|
71
78
|
getCollections: {};
|
|
72
79
|
};
|
|
80
|
+
export type VQL_Query_CRUD_Keys = VQL_Query_CRUD_Data extends infer U ? U extends Record<string, unknown> ? keyof U : never : never;
|
|
73
81
|
export interface VQL_Query_CRUD<T = any> {
|
|
74
82
|
db: string;
|
|
75
83
|
d: VQL_Query_CRUD_Data<T>;
|
package/dist/vql.d.ts
CHANGED
|
@@ -9,8 +9,14 @@ export interface VContext {
|
|
|
9
9
|
export type KeysMatching<T, V, C = V> = {
|
|
10
10
|
[K in keyof T]-?: T[K] extends C ? K : never;
|
|
11
11
|
}[keyof T];
|
|
12
|
-
|
|
13
|
-
export type
|
|
12
|
+
/** Helper type for nested path values with type filtering */
|
|
13
|
+
export type NestedValue<T, V, C = V> = {
|
|
14
|
+
[K in keyof T as T[K] extends C ? K : T[K] extends object ? K : never]?: T[K] extends C ? V : T[K] extends object ? NestedValue<T[K], V, C> : never;
|
|
15
|
+
};
|
|
16
|
+
/** Helper type for nested path structure */
|
|
17
|
+
export type DeepPartial<T> = {
|
|
18
|
+
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
|
|
19
|
+
};
|
|
14
20
|
/** Logical Operators */
|
|
15
21
|
export type LogicalOperators<T = any> = {
|
|
16
22
|
/**
|
|
@@ -29,66 +35,110 @@ export type LogicalOperators<T = any> = {
|
|
|
29
35
|
*/
|
|
30
36
|
$not?: SearchOptions<T>;
|
|
31
37
|
};
|
|
32
|
-
/** Comparison Operators */
|
|
38
|
+
/** Comparison Operators with nested support */
|
|
33
39
|
export type ComparisonOperators<T = any> = {
|
|
34
|
-
|
|
35
|
-
$
|
|
36
|
-
|
|
37
|
-
$
|
|
38
|
-
|
|
40
|
+
/** 5 > 4 */
|
|
41
|
+
$gt?: NestedValue<T, number, number>;
|
|
42
|
+
/** 5 < 4 */
|
|
43
|
+
$lt?: NestedValue<T, number, number>;
|
|
44
|
+
/** 5 >= 4 */
|
|
45
|
+
$gte?: NestedValue<T, number, number>;
|
|
46
|
+
/** 5 <= 4 */
|
|
47
|
+
$lte?: NestedValue<T, number, number>;
|
|
48
|
+
/** 5 between [min, max] */
|
|
49
|
+
$between?: NestedValue<T, [
|
|
39
50
|
number,
|
|
40
51
|
number
|
|
41
52
|
], number>;
|
|
42
|
-
|
|
43
|
-
$
|
|
53
|
+
/** 2 in [1, 2, 3] */
|
|
54
|
+
$in?: DeepPartial<T> & {
|
|
55
|
+
[K in keyof T]?: T[K] extends any[] ? T[K] : T[K][];
|
|
56
|
+
};
|
|
57
|
+
/** 5 not in [1, 2, 3] */
|
|
58
|
+
$nin?: DeepPartial<T> & {
|
|
59
|
+
[K in keyof T]?: T[K] extends any[] ? T[K] : T[K][];
|
|
60
|
+
};
|
|
61
|
+
/** id > 4 */
|
|
62
|
+
$idGt?: NestedValue<T, string | number, string | number>;
|
|
63
|
+
/** id < 4 */
|
|
64
|
+
$idLt?: NestedValue<T, string | number, string | number>;
|
|
65
|
+
/** id >= 4 */
|
|
66
|
+
$idGte?: NestedValue<T, string | number, string | number>;
|
|
67
|
+
/** id <= 4 */
|
|
68
|
+
$idLte?: NestedValue<T, string | number, string | number>;
|
|
44
69
|
};
|
|
45
|
-
/** Type and Existence Operators */
|
|
70
|
+
/** Type and Existence Operators with nested support */
|
|
46
71
|
export type TypeAndExistenceOperators<T = any> = {
|
|
47
|
-
|
|
48
|
-
$
|
|
72
|
+
/** "name" in { name: "John" } */
|
|
73
|
+
$exists?: NestedValue<T, boolean>;
|
|
74
|
+
/** "name" == "string" in { name: "John" } */
|
|
75
|
+
$type?: NestedValue<T, string>;
|
|
49
76
|
};
|
|
50
|
-
/** Array Operators */
|
|
77
|
+
/** Array Operators with nested support */
|
|
51
78
|
export type ArrayOperators<T = any> = {
|
|
52
|
-
|
|
53
|
-
$
|
|
54
|
-
|
|
79
|
+
/** [1, 2, 3] includes 2 */
|
|
80
|
+
$arrinc?: DeepPartial<T>;
|
|
81
|
+
/** [1, 2, 3] array includes all elements e.g. [1, 2] */
|
|
82
|
+
$arrincall?: DeepPartial<T>;
|
|
83
|
+
/** [1, 2, 3] has size 3 */
|
|
84
|
+
$size?: NestedValue<T, number>;
|
|
55
85
|
};
|
|
56
|
-
/** String Operators */
|
|
86
|
+
/** String Operators with nested support */
|
|
57
87
|
export type StringOperators<T = any> = {
|
|
58
|
-
|
|
59
|
-
$
|
|
60
|
-
|
|
88
|
+
/** "John" matches /oh/ */
|
|
89
|
+
$regex?: NestedValue<T, RegExp | string, string>;
|
|
90
|
+
/** "John" starts with "Jo" */
|
|
91
|
+
$startsWith?: NestedValue<T, string, string>;
|
|
92
|
+
/** "John" ends with "hn" */
|
|
93
|
+
$endsWith?: NestedValue<T, string, string>;
|
|
61
94
|
};
|
|
62
|
-
/** Other Operators */
|
|
95
|
+
/** Other Operators with nested support */
|
|
63
96
|
export type OtherOperators<T = any> = {
|
|
64
|
-
$
|
|
97
|
+
/** { $type: "name" } matches { $type: "name" } literally - Ignore $ operators */
|
|
98
|
+
$subset?: DeepPartial<T>;
|
|
65
99
|
};
|
|
66
100
|
/** Predefined Search Operators */
|
|
67
101
|
export type PredefinedSearchOperators<T = any> = LogicalOperators<T> & ComparisonOperators<T> & TypeAndExistenceOperators<T> & ArrayOperators<T> & StringOperators<T> & OtherOperators<T>;
|
|
68
102
|
/**
|
|
69
103
|
* SearchOptions can be either a function or an object with predefined operators.
|
|
70
104
|
*/
|
|
71
|
-
export type SearchOptions<T = any> = PredefinedSearchOperators<T> &
|
|
105
|
+
export type SearchOptions<T = any> = PredefinedSearchOperators<T> & DeepPartial<T> & Record<string, any>;
|
|
72
106
|
/** Arrays */
|
|
73
107
|
export type ArrayUpdater<T = any> = {
|
|
74
|
-
$push
|
|
75
|
-
|
|
76
|
-
$pushset
|
|
77
|
-
$
|
|
78
|
-
$
|
|
108
|
+
/** [1,2] -> $push 3 -> [1,2,3] */
|
|
109
|
+
$push?: NestedValue<T, any>;
|
|
110
|
+
/** [1,2] -> $pushset 2,3 -> [1,2,3] */
|
|
111
|
+
$pushset?: NestedValue<T, any>;
|
|
112
|
+
/** [1,2,3] -> $pull 2 -> [1,3] */
|
|
113
|
+
$pull?: NestedValue<T, any>;
|
|
114
|
+
/** [1,2,2,3] -> $pullall [2] -> [1,3] */
|
|
115
|
+
$pullall?: NestedValue<T, any>;
|
|
79
116
|
};
|
|
80
117
|
/** Objects */
|
|
81
118
|
export type ObjectUpdater<T = any> = {
|
|
82
|
-
$merge
|
|
119
|
+
/** { a: 1 } -> $merge { b: 2 } -> { a: 1, b: 2 } */
|
|
120
|
+
$merge?: NestedValue<T, any>;
|
|
121
|
+
/** { a: { x: 1 } } -> $deepMerge { a: { y: 2 } } -> { a: { x: 1, y: 2 } } */
|
|
122
|
+
$deepMerge?: NestedValue<T, any>;
|
|
83
123
|
};
|
|
84
124
|
/** Values */
|
|
85
125
|
export type ValueUpdater<T = any> = {
|
|
86
|
-
$inc
|
|
87
|
-
$
|
|
88
|
-
$
|
|
89
|
-
$
|
|
126
|
+
/** { count: 1 } -> $inc 2 -> { count: 3 } */
|
|
127
|
+
$inc?: NestedValue<T, number>;
|
|
128
|
+
/** { count: 5 } -> $dec 2 -> { count: 3 } */
|
|
129
|
+
$dec?: NestedValue<T, number>;
|
|
130
|
+
/** { name: "John" } -> $unset "name" -> {} */
|
|
131
|
+
$unset?: NestedValue<T, any>;
|
|
132
|
+
/** { oldName: "value" } -> $rename "oldName" to "newName" -> { newName: "value" } */
|
|
133
|
+
$rename?: NestedValue<T, any>;
|
|
134
|
+
/**
|
|
135
|
+
* {} -> $set { name: "John" } -> { name: "John" }
|
|
136
|
+
*
|
|
137
|
+
* Note: same as { name: value }
|
|
138
|
+
*/
|
|
139
|
+
$set?: NestedValue<T, any>;
|
|
90
140
|
};
|
|
91
|
-
export type UpdaterArg<T = any> = ArrayUpdater<T> & ObjectUpdater<T> & ValueUpdater<T> &
|
|
141
|
+
export type UpdaterArg<T = any> = ArrayUpdater<T> & ObjectUpdater<T> & ValueUpdater<T> & DeepPartial<T> & Record<string, any>;
|
|
92
142
|
export type Arg<T = any> = {
|
|
93
143
|
[K in keyof T]?: any;
|
|
94
144
|
} & Record<string, any>;
|
|
@@ -98,7 +148,7 @@ export type Search<T = any> = SearchOptions<T> | SearchFunc<T>;
|
|
|
98
148
|
export type Updater<T = any> = UpdaterArg<T> | UpdaterArg<T>[] | UpdaterFunc<T>;
|
|
99
149
|
export interface DbFindOpts<T = any> {
|
|
100
150
|
reverse?: boolean;
|
|
101
|
-
|
|
151
|
+
limit?: number;
|
|
102
152
|
offset?: number;
|
|
103
153
|
sortBy?: KeysMatching<T, any>;
|
|
104
154
|
sortAsc?: boolean;
|
|
@@ -119,31 +169,35 @@ declare class CollectionManager<D = Data> {
|
|
|
119
169
|
/**
|
|
120
170
|
* Find data in a database.
|
|
121
171
|
*/
|
|
122
|
-
find<T = Data>(search?: Search<T & D>, options?: DbFindOpts<T &
|
|
172
|
+
find<T = Data>(search?: Search<T & D>, options?: DbFindOpts<T & D>, findOpts?: FindOpts<T & D>, context?: VContext): Promise<T[]>;
|
|
123
173
|
/**
|
|
124
174
|
* Find one data entry in a database.
|
|
125
175
|
*/
|
|
126
|
-
findOne<T = Data>(search?: Search<T &
|
|
176
|
+
findOne<T = Data>(search?: Search<T & D>, findOpts?: FindOpts<T & D>, context?: VContext): Promise<T>;
|
|
127
177
|
/**
|
|
128
178
|
* Update data in a database.
|
|
129
179
|
*/
|
|
130
|
-
update<T = Data>(search: Search<T &
|
|
180
|
+
update<T = Data>(search: Search<T & D>, updater: Updater<T & D>, context?: VContext): Promise<boolean>;
|
|
131
181
|
/**
|
|
132
182
|
* Update one data entry in a database.
|
|
133
183
|
*/
|
|
134
|
-
updateOne<T = Data>(search: Search<T &
|
|
184
|
+
updateOne<T = Data>(search: Search<T & D>, updater: Updater<T & D>, context?: VContext): Promise<boolean>;
|
|
135
185
|
/**
|
|
136
186
|
* Remove data from a database.
|
|
137
187
|
*/
|
|
138
|
-
remove<T = Data>(search: Search<T &
|
|
188
|
+
remove<T = Data>(search: Search<T & D>, context?: VContext): Promise<boolean>;
|
|
139
189
|
/**
|
|
140
190
|
* Remove one data entry from a database.
|
|
141
191
|
*/
|
|
142
|
-
removeOne<T = Data>(search: Search<T &
|
|
192
|
+
removeOne<T = Data>(search: Search<T & D>, context?: VContext): Promise<boolean>;
|
|
143
193
|
/**
|
|
144
194
|
* Asynchronously updates one entry in a database or adds a new one if it doesn't exist.
|
|
145
195
|
*/
|
|
146
|
-
updateOneOrAdd<T = Data>(search: Search<T &
|
|
196
|
+
updateOneOrAdd<T = Data>(search: Search<T & D>, updater: Updater<T & D>, { add_arg, context, id_gen }: UpdateOneOrAdd<T & D>): Promise<boolean>;
|
|
197
|
+
/**
|
|
198
|
+
* Asynchronously removes one entry in a database or adds a new one if it doesn't exist. Usage e.g. for toggling a flag.
|
|
199
|
+
*/
|
|
200
|
+
toggleOne<T = Data>(search: Search<T & D>, data?: Arg<T & D>, context?: VContext): Promise<boolean>;
|
|
147
201
|
}
|
|
148
202
|
export interface ValtheraCompatible {
|
|
149
203
|
c(collection: string): CollectionManager;
|
|
@@ -159,6 +213,7 @@ export interface ValtheraCompatible {
|
|
|
159
213
|
removeOne<T = Data>(collection: string, search: Search<T>, context?: VContext): Promise<boolean>;
|
|
160
214
|
removeCollection(collection: string): Promise<boolean>;
|
|
161
215
|
updateOneOrAdd<T = Data>(collection: string, search: Search<T>, updater: Updater<T>, opts?: UpdateOneOrAdd<T>): Promise<boolean>;
|
|
216
|
+
toggleOne<T = Data>(collection: string, search: Search<T>, data?: Arg<T>, context?: VContext): Promise<boolean>;
|
|
162
217
|
}
|
|
163
218
|
export interface UpdateOneOrAdd<T> {
|
|
164
219
|
add_arg?: Arg<T>;
|
|
@@ -231,6 +286,11 @@ export interface VQL_OP_UpdateOneOrAdd<T = any> {
|
|
|
231
286
|
add_arg?: Arg<T>;
|
|
232
287
|
id_gen?: boolean;
|
|
233
288
|
}
|
|
289
|
+
export interface VQL_OP_ToggleOne<T = any> {
|
|
290
|
+
collection: string;
|
|
291
|
+
search: Search<T>;
|
|
292
|
+
data?: Arg<T>;
|
|
293
|
+
}
|
|
234
294
|
export interface VQL_OP_CollectionOperation {
|
|
235
295
|
collection: string;
|
|
236
296
|
}
|
|
@@ -253,6 +313,8 @@ export type VQL_Query_CRUD_Data<T = any> = {
|
|
|
253
313
|
removeOne: VQL_OP_Remove<T>;
|
|
254
314
|
} | {
|
|
255
315
|
updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
|
|
316
|
+
} | {
|
|
317
|
+
toggleOne: VQL_OP_ToggleOne<T>;
|
|
256
318
|
} | {
|
|
257
319
|
removeCollection: VQL_OP_CollectionOperation;
|
|
258
320
|
} | {
|
|
@@ -262,6 +324,7 @@ export type VQL_Query_CRUD_Data<T = any> = {
|
|
|
262
324
|
} | {
|
|
263
325
|
getCollections: {};
|
|
264
326
|
};
|
|
327
|
+
export type VQL_Query_CRUD_Keys = VQL_Query_CRUD_Data extends infer U ? U extends Record<string, unknown> ? keyof U : never : never;
|
|
265
328
|
export interface VQL_Query_CRUD<T = any> {
|
|
266
329
|
db: string;
|
|
267
330
|
d: VQL_Query_CRUD_Data<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wxn0brp/vql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"author": "wxn0brP",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"query-language"
|
|
28
28
|
],
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@wxn0brp/db-core": ">=0.
|
|
31
|
-
"@wxn0brp/falcon-frame": ">=0.5.
|
|
32
|
-
"@wxn0brp/gate-warden": ">=0.
|
|
30
|
+
"@wxn0brp/db-core": ">=0.3.0",
|
|
31
|
+
"@wxn0brp/falcon-frame": ">=0.5.3",
|
|
32
|
+
"@wxn0brp/gate-warden": ">=0.5.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependenciesMeta": {
|
|
35
35
|
"@wxn0brp/falcon-frame": {
|
|
@@ -43,17 +43,18 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
+
"@types/bun": "*",
|
|
46
47
|
"@types/node": "*",
|
|
47
|
-
"@wxn0brp/db": "^0.
|
|
48
|
-
"@wxn0brp/db-core": "^0.
|
|
49
|
-
"@wxn0brp/falcon-frame": "^0.5.
|
|
50
|
-
"@wxn0brp/gate-warden": "^0.
|
|
51
|
-
"esbuild": "
|
|
48
|
+
"@wxn0brp/db": "^0.41.0",
|
|
49
|
+
"@wxn0brp/db-core": "^0.3.0",
|
|
50
|
+
"@wxn0brp/falcon-frame": "^0.5.3",
|
|
51
|
+
"@wxn0brp/gate-warden": "^0.5.0",
|
|
52
|
+
"esbuild": "*",
|
|
52
53
|
"tsc-alias": "*",
|
|
53
54
|
"typescript": "*"
|
|
54
55
|
},
|
|
55
56
|
"dependencies": {
|
|
56
|
-
"@wxn0brp/lucerna-log": "^0.2.
|
|
57
|
+
"@wxn0brp/lucerna-log": "^0.2.2"
|
|
57
58
|
},
|
|
58
59
|
"exports": {
|
|
59
60
|
".": {
|