@wxn0brp/vql 0.6.0-alpha.1 → 0.6.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/relation.d.ts +2 -2
- package/dist/cpu/relation.js +1 -1
- package/dist/cpu/request.d.ts +2 -2
- package/dist/cpu/request.js +1 -1
- package/dist/cpu/string/index.d.ts +2 -2
- package/dist/gw.d.ts +2 -2
- package/dist/permissions/relation.d.ts +3 -3
- package/dist/permissions/relation.js +3 -3
- package/dist/permissions/request.d.ts +4 -4
- package/dist/permissions/request.js +2 -2
- package/dist/processor.d.ts +5 -5
- package/dist/processor.js +4 -7
- package/dist/sheet.d.ts +2 -2
- package/dist/types/perm.d.ts +1 -1
- package/dist/types/vql.d.ts +51 -75
- package/dist/valid.d.ts +3 -3
- package/dist/valid.js +107 -25
- package/dist/vql.d.ts +51 -74
- package/package.json +1 -2
package/dist/cpu/relation.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { VQLProcessor } from "../processor.js";
|
|
2
|
-
import {
|
|
3
|
-
export declare function executeRelation(cpu: VQLProcessor, query:
|
|
2
|
+
import { VQL_Query_Relation } from "../types/vql.js";
|
|
3
|
+
export declare function executeRelation(cpu: VQLProcessor, query: VQL_Query_Relation, user: any): Promise<any>;
|
package/dist/cpu/relation.js
CHANGED
|
@@ -21,7 +21,7 @@ export async function executeRelation(cpu, query, user) {
|
|
|
21
21
|
const checkDb = checkDBsExist(cpu, query.r);
|
|
22
22
|
if (checkDb.err)
|
|
23
23
|
return checkDb;
|
|
24
|
-
if (!cpu.config.noCheckPermissions && !await checkRelationPermission(cpu.config, cpu.
|
|
24
|
+
if (!cpu.config.noCheckPermissions && !await checkRelationPermission(cpu.config, cpu.permValidFn, user, query)) {
|
|
25
25
|
return { err: true, msg: "Permission denied", c: 403 };
|
|
26
26
|
}
|
|
27
27
|
const req = query.r;
|
package/dist/cpu/request.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { VQLProcessor } from "../processor.js";
|
|
2
|
-
import {
|
|
3
|
-
export declare function executeQuery(cpu: VQLProcessor, query:
|
|
2
|
+
import { VQL_Query_CRUD } from "../types/vql.js";
|
|
3
|
+
export declare function executeQuery(cpu: VQLProcessor, query: VQL_Query_CRUD, user: any): Promise<any>;
|
package/dist/cpu/request.js
CHANGED
|
@@ -5,7 +5,7 @@ export async function executeQuery(cpu, query, user) {
|
|
|
5
5
|
return { err: true, msg: `Invalid query - db "${query.db || "undefined"}" not found`, c: 400 };
|
|
6
6
|
const db = cpu.dbInstances[query.db];
|
|
7
7
|
const operation = Object.keys(query.d)[0];
|
|
8
|
-
if (!cpu.config.noCheckPermissions && !await checkRequestPermission(cpu.config, cpu.
|
|
8
|
+
if (!cpu.config.noCheckPermissions && !await checkRequestPermission(cpu.config, cpu.permValidFn, user, query)) {
|
|
9
9
|
return { err: true, msg: "Permission denied", c: 403 };
|
|
10
10
|
}
|
|
11
11
|
if (operation === "find") {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function parseVQLS(query: string):
|
|
1
|
+
import { VQL_Query } from "../../types/vql.js";
|
|
2
|
+
export declare function parseVQLS(query: string): VQL_Query;
|
package/dist/gw.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { GateWarden } from "@wxn0brp/gate-warden";
|
|
2
|
-
import {
|
|
3
|
-
export declare function createGwValidFn(gw: GateWarden):
|
|
2
|
+
import { PermValidFn } from "./types/perm.js";
|
|
3
|
+
export declare function createGwValidFn(gw: GateWarden): PermValidFn;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { PermValidFn } from "../types/perm.js";
|
|
2
|
+
import { VQL_Query_Relation } from "../types/vql.js";
|
|
3
3
|
import { VQLConfig } from "../config.js";
|
|
4
|
-
export declare function checkRelationPermission(config: VQLConfig,
|
|
4
|
+
export declare function checkRelationPermission(config: VQLConfig, permValidFn: PermValidFn, user: any, query: VQL_Query_Relation): Promise<boolean>;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { PermCRUD } from "../types/perm.js";
|
|
2
2
|
import { extractPathsFromData, hashKey } from "./utils.js";
|
|
3
|
-
export async function checkRelationPermission(config,
|
|
3
|
+
export async function checkRelationPermission(config, permValidFn, user, query) {
|
|
4
4
|
const { path, search, relations, select } = query.r;
|
|
5
5
|
// Helper function to recursively check permissions with fallback mechanism
|
|
6
6
|
const checkPermissionRecursively = async (entityId, fallbackLevels = []) => {
|
|
7
7
|
// Check if the user has access to the current entity
|
|
8
8
|
// const result = await gw.hasAccess(user.id, entityId, PermCRUD.READ);
|
|
9
|
-
const result = await
|
|
9
|
+
const result = await permValidFn(entityId, PermCRUD.READ, user);
|
|
10
10
|
if (result.granted) {
|
|
11
11
|
return true;
|
|
12
12
|
}
|
|
@@ -43,7 +43,7 @@ export async function checkRelationPermission(config, validFn, user, query) {
|
|
|
43
43
|
if (relations) {
|
|
44
44
|
for (const relationKey in relations) {
|
|
45
45
|
const r = relations[relationKey];
|
|
46
|
-
if (!await checkRelationPermission(config,
|
|
46
|
+
if (!await checkRelationPermission(config, permValidFn, user, { r })) {
|
|
47
47
|
return false;
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { PermCRUD, ValidFn } from "../types/perm.js";
|
|
2
|
-
import { VQLRequest } from "../types/vql.js";
|
|
3
1
|
import { VQLConfig } from "../config.js";
|
|
4
|
-
|
|
2
|
+
import { PermCRUD, PermValidFn } from "../types/perm.js";
|
|
3
|
+
import { VQL_Query_CRUD } from "../types/vql.js";
|
|
4
|
+
export declare function extractPaths(config: VQLConfig, query: VQL_Query_CRUD): Promise<{
|
|
5
5
|
db: string;
|
|
6
6
|
c: string;
|
|
7
7
|
paths: {
|
|
@@ -15,4 +15,4 @@ export declare function processFieldPath(pathObj: {
|
|
|
15
15
|
path: string[];
|
|
16
16
|
key: string;
|
|
17
17
|
}): string[];
|
|
18
|
-
export declare function checkRequestPermission(config: VQLConfig,
|
|
18
|
+
export declare function checkRequestPermission(config: VQLConfig, permValidFn: PermValidFn, user: any, query: VQL_Query_CRUD): Promise<boolean>;
|
|
@@ -79,7 +79,7 @@ export function processFieldPath(pathObj) {
|
|
|
79
79
|
}
|
|
80
80
|
return processedPath;
|
|
81
81
|
}
|
|
82
|
-
export async function checkRequestPermission(config,
|
|
82
|
+
export async function checkRequestPermission(config, permValidFn, user, query) {
|
|
83
83
|
if (!query)
|
|
84
84
|
return false;
|
|
85
85
|
const permPaths = await extractPaths(config, query);
|
|
@@ -87,7 +87,7 @@ export async function checkRequestPermission(config, validFn, user, query) {
|
|
|
87
87
|
const checkPermissionRecursively = async (entityId, requiredPerm, fallbackLevels = []) => {
|
|
88
88
|
// Check if the user has access to the current entity
|
|
89
89
|
// const result = await gw.hasAccess(user.id, entityId, requiredPerm);
|
|
90
|
-
const result = await
|
|
90
|
+
const result = await permValidFn(entityId, requiredPerm, user);
|
|
91
91
|
if (result.granted) {
|
|
92
92
|
return true;
|
|
93
93
|
}
|
package/dist/processor.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Relation, ValtheraCompatible } from "@wxn0brp/db-core";
|
|
2
2
|
import { VQLConfig, VQLConfigInterface } from "./config.js";
|
|
3
|
-
import { VQLError,
|
|
4
|
-
import {
|
|
3
|
+
import { VQLError, VQLUQ } from "./types/vql.js";
|
|
4
|
+
import { PermValidFn } from "./types/perm.js";
|
|
5
5
|
export declare class VQLProcessor {
|
|
6
6
|
dbInstances: Record<string, ValtheraCompatible>;
|
|
7
|
-
|
|
7
|
+
permValidFn: PermValidFn;
|
|
8
8
|
relation: Relation;
|
|
9
9
|
config: VQLConfig;
|
|
10
|
-
constructor(dbInstances: Record<string, ValtheraCompatible>, config?: VQLConfig | Partial<VQLConfigInterface>,
|
|
11
|
-
execute<T = any>(queryRaw:
|
|
10
|
+
constructor(dbInstances: Record<string, ValtheraCompatible>, config?: VQLConfig | Partial<VQLConfigInterface>, permValidFn?: PermValidFn);
|
|
11
|
+
execute<T = any>(queryRaw: VQLUQ<T>, user?: any): Promise<T | VQLError>;
|
|
12
12
|
}
|
package/dist/processor.js
CHANGED
|
@@ -8,12 +8,12 @@ import { validateRaw, validateVql } from "./valid.js";
|
|
|
8
8
|
import { parseVQLS } from "./cpu/string/index.js";
|
|
9
9
|
export class VQLProcessor {
|
|
10
10
|
dbInstances;
|
|
11
|
-
|
|
11
|
+
permValidFn;
|
|
12
12
|
relation;
|
|
13
13
|
config;
|
|
14
|
-
constructor(dbInstances, config = new VQLConfig(),
|
|
14
|
+
constructor(dbInstances, config = new VQLConfig(), permValidFn = async () => ({ granted: true, via: "" })) {
|
|
15
15
|
this.dbInstances = dbInstances;
|
|
16
|
-
this.
|
|
16
|
+
this.permValidFn = permValidFn;
|
|
17
17
|
this.relation = new Relation(dbInstances);
|
|
18
18
|
this.config = config instanceof VQLConfig ? config : new VQLConfig(config);
|
|
19
19
|
}
|
|
@@ -22,7 +22,6 @@ export class VQLProcessor {
|
|
|
22
22
|
logger.info("Incoming string query");
|
|
23
23
|
const q = typeof queryRaw === "string" ? queryRaw : queryRaw.query;
|
|
24
24
|
const vars = typeof queryRaw === "string" ? null : queryRaw.var;
|
|
25
|
-
const ref = typeof queryRaw === "string" ? null : queryRaw.ref;
|
|
26
25
|
logger.debug(q);
|
|
27
26
|
try {
|
|
28
27
|
queryRaw = parseVQLS(q);
|
|
@@ -30,12 +29,10 @@ export class VQLProcessor {
|
|
|
30
29
|
}
|
|
31
30
|
catch (e) {
|
|
32
31
|
logger.error("Error parsing string query: ", { error: e, msg: e.message });
|
|
33
|
-
return { err: true,
|
|
32
|
+
return { err: true, c: 400, msg: `String query parsing error: ${e.message}` };
|
|
34
33
|
}
|
|
35
34
|
if (vars)
|
|
36
35
|
queryRaw = { ...queryRaw, var: vars };
|
|
37
|
-
if (ref)
|
|
38
|
-
queryRaw = { ...queryRaw, ref };
|
|
39
36
|
logger.debug("Final string query: ", queryRaw);
|
|
40
37
|
}
|
|
41
38
|
else {
|
package/dist/sheet.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function replaceVars(query:
|
|
1
|
+
import { VQL_Query } from "./types/vql.js";
|
|
2
|
+
export declare function replaceVars(query: VQL_Query, user: any): VQL_Query;
|
package/dist/types/perm.d.ts
CHANGED
package/dist/types/vql.d.ts
CHANGED
|
@@ -2,97 +2,79 @@ import { RelationTypes } from "@wxn0brp/db";
|
|
|
2
2
|
import { Search, Arg } from "@wxn0brp/db-core/types/arg";
|
|
3
3
|
import { DbFindOpts, FindOpts } from "@wxn0brp/db-core/types/options";
|
|
4
4
|
import { UpdaterArg } from "@wxn0brp/db-core/types/updater";
|
|
5
|
-
export interface
|
|
6
|
-
find: VQLFind<T>;
|
|
7
|
-
findOne: VQLFindOne<T>;
|
|
8
|
-
f: VQLFindOne<T>;
|
|
9
|
-
add: VQLAdd<T>;
|
|
10
|
-
update: VQLUpdate<T>;
|
|
11
|
-
updateOne: VQLUpdate<T>;
|
|
12
|
-
remove: VQLRemove<T>;
|
|
13
|
-
removeOne: VQLRemove<T>;
|
|
14
|
-
updateOneOrAdd: VQLUpdateOneOrAdd<T>;
|
|
15
|
-
removeCollection: VQLCollectionOperation;
|
|
16
|
-
ensureCollection: VQLCollectionOperation;
|
|
17
|
-
issetCollection: VQLCollectionOperation;
|
|
18
|
-
getCollections: {};
|
|
19
|
-
}
|
|
20
|
-
export type VQLQueryData<T = any> = {
|
|
21
|
-
find: VQLFind<T>;
|
|
22
|
-
} | {
|
|
23
|
-
findOne: VQLFindOne<T>;
|
|
24
|
-
} | {
|
|
25
|
-
f: VQLFindOne<T>;
|
|
26
|
-
} | {
|
|
27
|
-
add: VQLAdd<T>;
|
|
28
|
-
} | {
|
|
29
|
-
update: VQLUpdate<T>;
|
|
30
|
-
} | {
|
|
31
|
-
updateOne: VQLUpdate<T>;
|
|
32
|
-
} | {
|
|
33
|
-
remove: VQLRemove<T>;
|
|
34
|
-
} | {
|
|
35
|
-
removeOne: VQLRemove<T>;
|
|
36
|
-
} | {
|
|
37
|
-
updateOneOrAdd: VQLUpdateOneOrAdd<T>;
|
|
38
|
-
} | {
|
|
39
|
-
removeCollection: VQLCollectionOperation;
|
|
40
|
-
} | {
|
|
41
|
-
ensureCollection: VQLCollectionOperation;
|
|
42
|
-
} | {
|
|
43
|
-
issetCollection: VQLCollectionOperation;
|
|
44
|
-
} | {
|
|
45
|
-
getCollections: {};
|
|
46
|
-
};
|
|
47
|
-
export interface VQLRequest<T = any> {
|
|
48
|
-
db: string;
|
|
49
|
-
d: VQLQueryData<T>;
|
|
50
|
-
}
|
|
51
|
-
export interface VQLFind<T = any> {
|
|
5
|
+
export interface VQL_OP_Find<T = any> {
|
|
52
6
|
collection: string;
|
|
53
7
|
search?: Search<T>;
|
|
54
8
|
limit?: number;
|
|
55
|
-
fields?:
|
|
56
|
-
select?:
|
|
57
|
-
relations?: VQLRelations;
|
|
9
|
+
fields?: VQL_Fields;
|
|
10
|
+
select?: VQL_Fields;
|
|
58
11
|
options?: DbFindOpts<T>;
|
|
59
12
|
searchOpts?: FindOpts<T>;
|
|
60
13
|
}
|
|
61
|
-
export interface
|
|
14
|
+
export interface VQL_OP_FindOne<T = any> {
|
|
62
15
|
collection: string;
|
|
63
16
|
search: Search<T>;
|
|
64
|
-
fields?:
|
|
65
|
-
select?:
|
|
66
|
-
relations?: VQLRelations;
|
|
17
|
+
fields?: VQL_Fields;
|
|
18
|
+
select?: VQL_Fields;
|
|
67
19
|
searchOpts?: FindOpts<T>;
|
|
68
20
|
}
|
|
69
|
-
export interface
|
|
21
|
+
export interface VQL_OP_Add<T = any> {
|
|
70
22
|
collection: string;
|
|
71
23
|
data: Arg<T>;
|
|
72
24
|
id_gen?: boolean;
|
|
73
25
|
}
|
|
74
|
-
export interface
|
|
26
|
+
export interface VQL_OP_Update<T = any> {
|
|
75
27
|
collection: string;
|
|
76
28
|
search: Search<T>;
|
|
77
29
|
updater: UpdaterArg<T>;
|
|
78
30
|
}
|
|
79
|
-
export interface
|
|
31
|
+
export interface VQL_OP_Remove<T = any> {
|
|
80
32
|
collection: string;
|
|
81
33
|
search: Search<T>;
|
|
82
34
|
}
|
|
83
|
-
export interface
|
|
35
|
+
export interface VQL_OP_UpdateOneOrAdd<T = any> {
|
|
84
36
|
collection: string;
|
|
85
37
|
search: Search<T>;
|
|
86
38
|
updater: UpdaterArg<T>;
|
|
87
39
|
add_arg?: Arg<T>;
|
|
88
40
|
id_gen?: boolean;
|
|
89
41
|
}
|
|
90
|
-
export interface
|
|
42
|
+
export interface VQL_OP_CollectionOperation {
|
|
91
43
|
collection: string;
|
|
92
44
|
}
|
|
93
|
-
export type
|
|
94
|
-
export type
|
|
95
|
-
|
|
45
|
+
export type VQL_Fields = Record<string, boolean | number> | string[];
|
|
46
|
+
export type VQL_Query_CRUD_Data<T = any> = {
|
|
47
|
+
find: VQL_OP_Find<T>;
|
|
48
|
+
} | {
|
|
49
|
+
findOne: VQL_OP_FindOne<T>;
|
|
50
|
+
} | {
|
|
51
|
+
f: VQL_OP_FindOne<T>;
|
|
52
|
+
} | {
|
|
53
|
+
add: VQL_OP_Add<T>;
|
|
54
|
+
} | {
|
|
55
|
+
update: VQL_OP_Update<T>;
|
|
56
|
+
} | {
|
|
57
|
+
updateOne: VQL_OP_Update<T>;
|
|
58
|
+
} | {
|
|
59
|
+
remove: VQL_OP_Remove<T>;
|
|
60
|
+
} | {
|
|
61
|
+
removeOne: VQL_OP_Remove<T>;
|
|
62
|
+
} | {
|
|
63
|
+
updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
|
|
64
|
+
} | {
|
|
65
|
+
removeCollection: VQL_OP_CollectionOperation;
|
|
66
|
+
} | {
|
|
67
|
+
ensureCollection: VQL_OP_CollectionOperation;
|
|
68
|
+
} | {
|
|
69
|
+
issetCollection: VQL_OP_CollectionOperation;
|
|
70
|
+
} | {
|
|
71
|
+
getCollections: {};
|
|
72
|
+
};
|
|
73
|
+
export interface VQL_Query_CRUD<T = any> {
|
|
74
|
+
db: string;
|
|
75
|
+
d: VQL_Query_CRUD_Data<T>;
|
|
76
|
+
}
|
|
77
|
+
export interface VQL_Query_Relation {
|
|
96
78
|
r: {
|
|
97
79
|
path: RelationTypes.Path;
|
|
98
80
|
search: Search;
|
|
@@ -102,25 +84,19 @@ export interface RelationQuery {
|
|
|
102
84
|
select?: RelationTypes.FieldPath[];
|
|
103
85
|
};
|
|
104
86
|
}
|
|
105
|
-
export interface
|
|
106
|
-
ref?: string;
|
|
87
|
+
export interface VQL_Var {
|
|
107
88
|
var?: {
|
|
108
89
|
[k: string]: any;
|
|
109
90
|
};
|
|
110
91
|
}
|
|
111
|
-
|
|
112
|
-
type
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
92
|
+
/** VQL Query */
|
|
93
|
+
export type VQL_Query<T = any> = (VQL_Query_CRUD<T> | VQL_Query_Relation) & VQL_Var;
|
|
94
|
+
/** VQL Universal Query */
|
|
95
|
+
export type VQLUQ<T = any> = VQL_Query<T> | string | {
|
|
96
|
+
query: string;
|
|
97
|
+
} & VQL_Var;
|
|
117
98
|
export interface VQLError {
|
|
118
99
|
err: true;
|
|
119
100
|
msg: string;
|
|
120
101
|
c: number;
|
|
121
|
-
why?: string;
|
|
122
102
|
}
|
|
123
|
-
export type VqlQueryRaw<T = any> = VQLR<T> | string | {
|
|
124
|
-
query: string;
|
|
125
|
-
} & VQLRef;
|
|
126
|
-
export {};
|
package/dist/valid.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function validateRaw(query:
|
|
3
|
-
export declare function validateVql(query:
|
|
1
|
+
import { VQL_Query, VQLError } from "./types/vql.js";
|
|
2
|
+
export declare function validateRaw(query: VQL_Query): true | VQLError;
|
|
3
|
+
export declare function validateVql(query: VQL_Query): true | VQLError;
|
package/dist/valid.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
function emptyErr() {
|
|
1
|
+
function emptyErr(msg = "Bad query") {
|
|
2
2
|
return {
|
|
3
3
|
err: true,
|
|
4
|
-
msg
|
|
4
|
+
msg,
|
|
5
5
|
c: 400
|
|
6
6
|
};
|
|
7
7
|
}
|
|
@@ -10,51 +10,133 @@ function isObj(obj, one = true) {
|
|
|
10
10
|
}
|
|
11
11
|
export function validateRaw(query) {
|
|
12
12
|
if (("r" in query && isObj(query.r)) ||
|
|
13
|
-
("db" in query && typeof query.db === "string" && isObj(query.d))
|
|
14
|
-
("ref" in query && isObj(query.ref)))
|
|
13
|
+
("db" in query && typeof query.db === "string" && isObj(query.d)))
|
|
15
14
|
return true;
|
|
16
|
-
return emptyErr();
|
|
15
|
+
return emptyErr("Invalid VQL query structure. Must contain 'r' or 'db' and 'd'.");
|
|
16
|
+
}
|
|
17
|
+
function validateRelations(relations) {
|
|
18
|
+
if (!isObj(relations, false))
|
|
19
|
+
return emptyErr("The 'relations' property must be an object.");
|
|
20
|
+
for (const key in relations) {
|
|
21
|
+
const relation = relations[key];
|
|
22
|
+
if (!isObj(relation))
|
|
23
|
+
return emptyErr(`Relation '${key}' must be an object.`);
|
|
24
|
+
if (!("path" in relation) || !Array.isArray(relation.path) || relation.path.length !== 2)
|
|
25
|
+
return emptyErr(`Relation '${key}' must have a 'path' property with an array of two strings.`);
|
|
26
|
+
if ("search" in relation && !isObj(relation.search, false))
|
|
27
|
+
return emptyErr(`Relation '${key}' has an invalid 'search' property; it must be an object.`);
|
|
28
|
+
if ("many" in relation && typeof relation.many !== "boolean")
|
|
29
|
+
return emptyErr(`Relation '${key}' has an invalid 'many' property; it must be a boolean.`);
|
|
30
|
+
if ("options" in relation && !isObj(relation.options, false))
|
|
31
|
+
return emptyErr(`Relation '${key}' has an invalid 'options' property; it must be an object.`);
|
|
32
|
+
if ("select" in relation && !Array.isArray(relation.select))
|
|
33
|
+
return emptyErr(`Relation '${key}' has an invalid 'select' property; it must be an array.`);
|
|
34
|
+
if ("relations" in relation) {
|
|
35
|
+
const nestedResult = validateRelations(relation.relations);
|
|
36
|
+
if (nestedResult !== true)
|
|
37
|
+
return nestedResult;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
17
41
|
}
|
|
18
42
|
function validR(query) {
|
|
19
43
|
const { r } = query;
|
|
20
44
|
if (!("path" in r) || !Array.isArray(r.path) || r.path.length !== 2)
|
|
21
|
-
return emptyErr();
|
|
45
|
+
return emptyErr("Relation query 'r' must have a 'path' property with an array of two strings.");
|
|
22
46
|
if (!isObj(r.search, false))
|
|
23
|
-
return emptyErr();
|
|
47
|
+
return emptyErr("Relation query 'r.search' must be an object.");
|
|
48
|
+
if (!isObj(r.relations, false))
|
|
49
|
+
return emptyErr("Relation query 'r' must have a 'relations' object.");
|
|
50
|
+
const relationsValidation = validateRelations(r.relations);
|
|
51
|
+
if (relationsValidation !== true)
|
|
52
|
+
return relationsValidation;
|
|
53
|
+
if ("many" in r && typeof r.many !== "boolean")
|
|
54
|
+
return emptyErr("Relation query 'r.many' must be a boolean.");
|
|
55
|
+
if ("options" in r && !isObj(r.options, false))
|
|
56
|
+
return emptyErr("Relation query 'r.options' must be an object.");
|
|
57
|
+
if ("select" in r && !Array.isArray(r.select))
|
|
58
|
+
return emptyErr("Relation query 'r.select' must be an array.");
|
|
24
59
|
return true;
|
|
25
60
|
}
|
|
26
61
|
function validD(query) {
|
|
27
62
|
const { d } = query;
|
|
28
63
|
const key = Object.keys(d)[0];
|
|
29
|
-
|
|
64
|
+
const value = d[key]; // Cast for common property
|
|
65
|
+
if (key === "getCollections")
|
|
30
66
|
return true;
|
|
31
|
-
const value = d[key];
|
|
32
67
|
if (typeof value.collection !== "string" || value.collection.trim() === "")
|
|
33
|
-
return emptyErr();
|
|
34
|
-
if (key === "issetCollection" || key === "ensureCollection" || key === "removeCollection")
|
|
68
|
+
return emptyErr(`CRUD operation '${key}' must specify a non-empty 'collection' string.`);
|
|
69
|
+
if (key === "issetCollection" || key === "ensureCollection" || key === "removeCollection") {
|
|
35
70
|
return true;
|
|
71
|
+
}
|
|
36
72
|
if (key === "add") {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
73
|
+
const op = d.add;
|
|
74
|
+
if (!isObj(op.data))
|
|
75
|
+
return emptyErr("'add' operation requires a 'data' object.");
|
|
76
|
+
if ("id_gen" in op && typeof op.id_gen !== "boolean")
|
|
77
|
+
return emptyErr("'add' operation 'id_gen' property must be a boolean.");
|
|
78
|
+
return true;
|
|
41
79
|
}
|
|
42
|
-
if ("
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
if (key === "find") {
|
|
81
|
+
const op = d.find;
|
|
82
|
+
if ("search" in op && !isObj(op.search, false))
|
|
83
|
+
return emptyErr("'find' operation 'search' property must be an object.");
|
|
84
|
+
if ("limit" in op && typeof op.limit !== "number")
|
|
85
|
+
return emptyErr("'find' operation 'limit' property must be a number.");
|
|
86
|
+
if ("fields" in op && !isObj(op.fields, false) && !Array.isArray(op.fields))
|
|
87
|
+
return emptyErr("'find' operation 'fields' property must be an object or an array.");
|
|
88
|
+
if ("select" in op && !isObj(op.select, false) && !Array.isArray(op.select))
|
|
89
|
+
return emptyErr("'find' operation 'select' property must be an object or an array.");
|
|
90
|
+
if ("options" in op && !isObj(op.options, false))
|
|
91
|
+
return emptyErr("'find' operation 'options' property must be an object.");
|
|
92
|
+
if ("searchOpts" in op && !isObj(op.searchOpts, false))
|
|
93
|
+
return emptyErr("'find' operation 'searchOpts' property must be an object.");
|
|
45
94
|
return true;
|
|
46
|
-
if (key === "update" || key === "updateOne" || key === "updateOneOrAdd") {
|
|
47
|
-
if (!isObj(value.updater, false))
|
|
48
|
-
return emptyErr();
|
|
49
|
-
else
|
|
50
|
-
return true;
|
|
51
95
|
}
|
|
52
|
-
|
|
96
|
+
if (key === "findOne" || key === "f") {
|
|
97
|
+
const op = d.findOne || d.f;
|
|
98
|
+
if (!isObj(op.search, false))
|
|
99
|
+
return emptyErr(`'${key}' operation requires a 'search' object.`);
|
|
100
|
+
if ("fields" in op && !isObj(op.fields, false) && !Array.isArray(op.fields))
|
|
101
|
+
return emptyErr(`'${key}' operation 'fields' property must be an object or an array.`);
|
|
102
|
+
if ("select" in op && !isObj(op.select, false) && !Array.isArray(op.select))
|
|
103
|
+
return emptyErr(`'${key}' operation 'select' property must be an object or an array.`);
|
|
104
|
+
if ("searchOpts" in op && !isObj(op.searchOpts, false))
|
|
105
|
+
return emptyErr(`'${key}' operation 'searchOpts' property must be an object.`);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (key === "remove" || key === "removeOne") {
|
|
109
|
+
const op = d.remove || d.removeOne;
|
|
110
|
+
if (!isObj(op.search, false))
|
|
111
|
+
return emptyErr(`'${key}' operation requires a 'search' object.`);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (key === "update" || key === "updateOne") {
|
|
115
|
+
const op = d.update || d.updateOne;
|
|
116
|
+
if (!isObj(op.search, false))
|
|
117
|
+
return emptyErr(`'${key}' operation requires a 'search' object.`);
|
|
118
|
+
if (!isObj(op.updater, false))
|
|
119
|
+
return emptyErr(`'${key}' operation requires an 'updater' object.`);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
if (key === "updateOneOrAdd") {
|
|
123
|
+
const op = d.updateOneOrAdd;
|
|
124
|
+
if (!isObj(op.search, false))
|
|
125
|
+
return emptyErr("'updateOneOrAdd' operation requires a 'search' object.");
|
|
126
|
+
if (!isObj(op.updater, false))
|
|
127
|
+
return emptyErr("'updateOneOrAdd' operation requires an 'updater' object.");
|
|
128
|
+
if ("add_arg" in op && !isObj(op.add_arg, false))
|
|
129
|
+
return emptyErr("'updateOneOrAdd' operation 'add_arg' property must be an object.");
|
|
130
|
+
if ("id_gen" in op && typeof op.id_gen !== "boolean")
|
|
131
|
+
return emptyErr("'updateOneOrAdd' operation 'id_gen' property must be a boolean.");
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return emptyErr(`Unknown or invalid CRUD operation: '${key}'`);
|
|
53
135
|
}
|
|
54
136
|
export function validateVql(query) {
|
|
55
137
|
if ("r" in query && isObj(query.r))
|
|
56
138
|
return validR(query);
|
|
57
139
|
if ("d" in query && isObj(query.d))
|
|
58
140
|
return validD(query);
|
|
59
|
-
return emptyErr();
|
|
141
|
+
return emptyErr("Query must contain a valid 'r' (relation) or 'd' (database) property.");
|
|
60
142
|
}
|
package/dist/vql.d.ts
CHANGED
|
@@ -189,97 +189,79 @@ declare namespace RelationTypes {
|
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
export interface
|
|
193
|
-
find: VQLFind<T>;
|
|
194
|
-
findOne: VQLFindOne<T>;
|
|
195
|
-
f: VQLFindOne<T>;
|
|
196
|
-
add: VQLAdd<T>;
|
|
197
|
-
update: VQLUpdate<T>;
|
|
198
|
-
updateOne: VQLUpdate<T>;
|
|
199
|
-
remove: VQLRemove<T>;
|
|
200
|
-
removeOne: VQLRemove<T>;
|
|
201
|
-
updateOneOrAdd: VQLUpdateOneOrAdd<T>;
|
|
202
|
-
removeCollection: VQLCollectionOperation;
|
|
203
|
-
ensureCollection: VQLCollectionOperation;
|
|
204
|
-
issetCollection: VQLCollectionOperation;
|
|
205
|
-
getCollections: {};
|
|
206
|
-
}
|
|
207
|
-
export type VQLQueryData<T = any> = {
|
|
208
|
-
find: VQLFind<T>;
|
|
209
|
-
} | {
|
|
210
|
-
findOne: VQLFindOne<T>;
|
|
211
|
-
} | {
|
|
212
|
-
f: VQLFindOne<T>;
|
|
213
|
-
} | {
|
|
214
|
-
add: VQLAdd<T>;
|
|
215
|
-
} | {
|
|
216
|
-
update: VQLUpdate<T>;
|
|
217
|
-
} | {
|
|
218
|
-
updateOne: VQLUpdate<T>;
|
|
219
|
-
} | {
|
|
220
|
-
remove: VQLRemove<T>;
|
|
221
|
-
} | {
|
|
222
|
-
removeOne: VQLRemove<T>;
|
|
223
|
-
} | {
|
|
224
|
-
updateOneOrAdd: VQLUpdateOneOrAdd<T>;
|
|
225
|
-
} | {
|
|
226
|
-
removeCollection: VQLCollectionOperation;
|
|
227
|
-
} | {
|
|
228
|
-
ensureCollection: VQLCollectionOperation;
|
|
229
|
-
} | {
|
|
230
|
-
issetCollection: VQLCollectionOperation;
|
|
231
|
-
} | {
|
|
232
|
-
getCollections: {};
|
|
233
|
-
};
|
|
234
|
-
export interface VQLRequest<T = any> {
|
|
235
|
-
db: string;
|
|
236
|
-
d: VQLQueryData<T>;
|
|
237
|
-
}
|
|
238
|
-
export interface VQLFind<T = any> {
|
|
192
|
+
export interface VQL_OP_Find<T = any> {
|
|
239
193
|
collection: string;
|
|
240
194
|
search?: Search<T>;
|
|
241
195
|
limit?: number;
|
|
242
|
-
fields?:
|
|
243
|
-
select?:
|
|
244
|
-
relations?: VQLRelations;
|
|
196
|
+
fields?: VQL_Fields;
|
|
197
|
+
select?: VQL_Fields;
|
|
245
198
|
options?: DbFindOpts<T>;
|
|
246
199
|
searchOpts?: FindOpts<T>;
|
|
247
200
|
}
|
|
248
|
-
export interface
|
|
201
|
+
export interface VQL_OP_FindOne<T = any> {
|
|
249
202
|
collection: string;
|
|
250
203
|
search: Search<T>;
|
|
251
|
-
fields?:
|
|
252
|
-
select?:
|
|
253
|
-
relations?: VQLRelations;
|
|
204
|
+
fields?: VQL_Fields;
|
|
205
|
+
select?: VQL_Fields;
|
|
254
206
|
searchOpts?: FindOpts<T>;
|
|
255
207
|
}
|
|
256
|
-
export interface
|
|
208
|
+
export interface VQL_OP_Add<T = any> {
|
|
257
209
|
collection: string;
|
|
258
210
|
data: Arg<T>;
|
|
259
211
|
id_gen?: boolean;
|
|
260
212
|
}
|
|
261
|
-
export interface
|
|
213
|
+
export interface VQL_OP_Update<T = any> {
|
|
262
214
|
collection: string;
|
|
263
215
|
search: Search<T>;
|
|
264
216
|
updater: UpdaterArg<T>;
|
|
265
217
|
}
|
|
266
|
-
export interface
|
|
218
|
+
export interface VQL_OP_Remove<T = any> {
|
|
267
219
|
collection: string;
|
|
268
220
|
search: Search<T>;
|
|
269
221
|
}
|
|
270
|
-
export interface
|
|
222
|
+
export interface VQL_OP_UpdateOneOrAdd<T = any> {
|
|
271
223
|
collection: string;
|
|
272
224
|
search: Search<T>;
|
|
273
225
|
updater: UpdaterArg<T>;
|
|
274
226
|
add_arg?: Arg<T>;
|
|
275
227
|
id_gen?: boolean;
|
|
276
228
|
}
|
|
277
|
-
export interface
|
|
229
|
+
export interface VQL_OP_CollectionOperation {
|
|
278
230
|
collection: string;
|
|
279
231
|
}
|
|
280
|
-
export type
|
|
281
|
-
export type
|
|
282
|
-
|
|
232
|
+
export type VQL_Fields = Record<string, boolean | number> | string[];
|
|
233
|
+
export type VQL_Query_CRUD_Data<T = any> = {
|
|
234
|
+
find: VQL_OP_Find<T>;
|
|
235
|
+
} | {
|
|
236
|
+
findOne: VQL_OP_FindOne<T>;
|
|
237
|
+
} | {
|
|
238
|
+
f: VQL_OP_FindOne<T>;
|
|
239
|
+
} | {
|
|
240
|
+
add: VQL_OP_Add<T>;
|
|
241
|
+
} | {
|
|
242
|
+
update: VQL_OP_Update<T>;
|
|
243
|
+
} | {
|
|
244
|
+
updateOne: VQL_OP_Update<T>;
|
|
245
|
+
} | {
|
|
246
|
+
remove: VQL_OP_Remove<T>;
|
|
247
|
+
} | {
|
|
248
|
+
removeOne: VQL_OP_Remove<T>;
|
|
249
|
+
} | {
|
|
250
|
+
updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
|
|
251
|
+
} | {
|
|
252
|
+
removeCollection: VQL_OP_CollectionOperation;
|
|
253
|
+
} | {
|
|
254
|
+
ensureCollection: VQL_OP_CollectionOperation;
|
|
255
|
+
} | {
|
|
256
|
+
issetCollection: VQL_OP_CollectionOperation;
|
|
257
|
+
} | {
|
|
258
|
+
getCollections: {};
|
|
259
|
+
};
|
|
260
|
+
export interface VQL_Query_CRUD<T = any> {
|
|
261
|
+
db: string;
|
|
262
|
+
d: VQL_Query_CRUD_Data<T>;
|
|
263
|
+
}
|
|
264
|
+
export interface VQL_Query_Relation {
|
|
283
265
|
r: {
|
|
284
266
|
path: RelationTypes.Path;
|
|
285
267
|
search: Search;
|
|
@@ -289,26 +271,21 @@ export interface RelationQuery {
|
|
|
289
271
|
select?: RelationTypes.FieldPath[];
|
|
290
272
|
};
|
|
291
273
|
}
|
|
292
|
-
export interface
|
|
293
|
-
ref?: string;
|
|
274
|
+
export interface VQL_Var {
|
|
294
275
|
var?: {
|
|
295
276
|
[k: string]: any;
|
|
296
277
|
};
|
|
297
278
|
}
|
|
298
|
-
|
|
299
|
-
export type
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
279
|
+
/** VQL Query */
|
|
280
|
+
export type VQL_Query<T = any> = (VQL_Query_CRUD<T> | VQL_Query_Relation) & VQL_Var;
|
|
281
|
+
/** VQL Universal Query */
|
|
282
|
+
export type VQLUQ<T = any> = VQL_Query<T> | string | {
|
|
283
|
+
query: string;
|
|
284
|
+
} & VQL_Var;
|
|
304
285
|
export interface VQLError {
|
|
305
286
|
err: true;
|
|
306
287
|
msg: string;
|
|
307
288
|
c: number;
|
|
308
|
-
why?: string;
|
|
309
289
|
}
|
|
310
|
-
export type VqlQueryRaw<T = any> = VQLR<T> | string | {
|
|
311
|
-
query: string;
|
|
312
|
-
} & VQLRef;
|
|
313
290
|
|
|
314
291
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wxn0brp/vql",
|
|
3
|
-
"version": "0.6.0
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"author": "wxn0brP",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
"@wxn0brp/db": "^0.30.0",
|
|
36
36
|
"@wxn0brp/falcon-frame": "0.0.20",
|
|
37
37
|
"@wxn0brp/gate-warden": "^0.4.0",
|
|
38
|
-
"dotenv": "^17.2.0",
|
|
39
38
|
"esbuild": "^0.25.8",
|
|
40
39
|
"tsc-alias": "^1.8.10",
|
|
41
40
|
"typescript": "^5.7.3"
|