@wxn0brp/vql 0.5.1 → 0.6.0-alpha.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/README.md CHANGED
@@ -20,18 +20,14 @@ Here is an example of how to use the `VQLProcessor` to execute a query:
20
20
  ```typescript
21
21
  import VQLProcessor from "@wxn0brp/vql";
22
22
  import { Valthera } from "@wxn0brp/db";
23
- import { GateWarden } from "@wxn0brp/gate-warden";
24
23
 
25
24
  // Initialize database instances
26
25
  const dbInstances = {
27
26
  myDatabase: new Valthera("path/to/database"),
28
27
  };
29
28
 
30
- // Initialize Gate Warden
31
- const gw = new GateWarden("path/to/gate-warden/database");
32
-
33
29
  // Create a VQLProcessor instance
34
- const processor = new VQLProcessor(dbInstances, gw);
30
+ const processor = new VQLProcessor(dbInstances);
35
31
 
36
32
  // Define a query (VQLR)
37
33
  const query = {
@@ -53,31 +49,11 @@ s.$gt.age = 18
53
49
  f.name = 1
54
50
  f.age = 1
55
51
  `
56
- // Or (use backticks like json)
57
- const VQLB = `
58
- myDatabase users
59
- {
60
- collection: "users",
61
- search: { $gt: { age: 18 } },
62
- fields: { name: 1, age: 1 },
63
- }
64
- `
65
- // Or (use markup like yaml)
66
- const VQLM = `
67
- myDatabase users
68
- collection: users
69
- search:
70
- $gt:
71
- age: 18
72
- fields:
73
- name: 1
74
- age: 1
75
- `
76
52
 
77
53
  // Execute the query
78
54
  (async () => {
79
55
  try {
80
- const result = await processor.execute(query, { id: "user123" });
56
+ const result = await processor.execute(query);
81
57
  console.log("Query Result:", result);
82
58
  } catch (error) {
83
59
  console.error("Error executing query:", error);
@@ -89,7 +65,6 @@ fields:
89
65
 
90
66
  - [Base/Map](./docs/lang/base.md)
91
67
  - [VQLS](./docs/lang/VQLS.md)
92
- - [VQLM/B](./docs/lang/VQLM.md)
93
68
  - [VQLR](./docs/lang/VQLR.md)
94
69
 
95
70
  ## License
package/dist/config.d.ts CHANGED
@@ -3,13 +3,11 @@ export interface VQLConfigInterface {
3
3
  strictSelect: boolean;
4
4
  strictACL: boolean;
5
5
  noCheckPermissions: boolean;
6
- formatAjv: boolean;
7
6
  }
8
7
  export declare class VQLConfig implements VQLConfigInterface {
9
8
  hidePath: boolean;
10
9
  strictSelect: boolean;
11
10
  strictACL: boolean;
12
11
  noCheckPermissions: boolean;
13
- formatAjv: boolean;
14
12
  constructor(config?: Partial<VQLConfigInterface>);
15
13
  }
package/dist/config.js CHANGED
@@ -1,9 +1,8 @@
1
1
  export class VQLConfig {
2
- hidePath = true;
3
- strictSelect = true;
4
- strictACL = true;
5
- noCheckPermissions = false;
6
- formatAjv = true;
2
+ hidePath = false;
3
+ strictSelect = false;
4
+ strictACL = false;
5
+ noCheckPermissions = true;
7
6
  constructor(config) {
8
7
  if (config) {
9
8
  Object.assign(this, config);
@@ -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.gw, user, query)) {
24
+ if (!cpu.config.noCheckPermissions && !await checkRelationPermission(cpu.config, cpu.validFn, user, query)) {
25
25
  return { err: true, msg: "Permission denied", c: 403 };
26
26
  }
27
27
  const req = query.r;
@@ -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.gw, user, query)) {
8
+ if (!cpu.config.noCheckPermissions && !await checkRequestPermission(cpu.config, cpu.validFn, user, query)) {
9
9
  return { err: true, msg: "Permission denied", c: 403 };
10
10
  }
11
11
  if (operation === "find") {
@@ -1,8 +1,2 @@
1
1
  import { VQL } from "../../types/vql.js";
2
- export type VQLParserMode = "VQLS" | "VQLM" | "VQLB" | "VQLR";
3
- export declare function guessParser(query: string): {
4
- mode: VQLParserMode;
5
- query: string;
6
- };
7
- declare function parseStringQuery(query: string): VQL;
8
- export { parseStringQuery };
2
+ export declare function parseVQLS(query: string): VQL;
@@ -1,56 +1,153 @@
1
- import logger from "../../logger.js";
2
- import { parseVQLB } from "./json5.js";
3
- import { parseVQLS } from "./simple.js";
4
- import { parseVQLM } from "./yaml.js";
5
- function get3rdAnd4thWord(query) {
6
- let words = [];
7
- let word = "";
8
- let i = 0;
9
- while (i < query.length && words.length < 4) {
10
- const c = query[i++];
11
- if (" \t\n\r".includes(c)) {
12
- if (word)
13
- words.push(word);
14
- word = "";
1
+ import { convertSearchObjToSearchArray, extractMeta } from "./utils.js";
2
+ const aliases = {
3
+ s: "search",
4
+ f: "fields",
5
+ o: "options",
6
+ r: "relations",
7
+ d: "data",
8
+ e: "select",
9
+ u: "updater",
10
+ };
11
+ function parseArgs(input) {
12
+ const result = {};
13
+ const tokens = [];
14
+ let current = "";
15
+ let inQuotes = false;
16
+ let escape = false;
17
+ for (let i = 0; i < input.length; i++) {
18
+ const char = input[i];
19
+ if (escape) {
20
+ current += char;
21
+ escape = false;
22
+ }
23
+ else if (char === "\\") {
24
+ escape = true;
25
+ }
26
+ else if (char === "\"") {
27
+ inQuotes = !inQuotes;
28
+ }
29
+ else if (!inQuotes && (char === " " || char === "=")) {
30
+ if (current !== "") {
31
+ tokens.push(current);
32
+ current = "";
33
+ }
15
34
  }
16
35
  else {
17
- word += c;
36
+ current += char;
37
+ }
38
+ }
39
+ if (current !== "") {
40
+ tokens.push(current);
41
+ }
42
+ for (let i = 0; i < tokens.length; i += 2) {
43
+ const key = tokens[i];
44
+ let value = tokens[i + 1] ?? true;
45
+ if (typeof value === "string") {
46
+ const trimmed = value.trim();
47
+ if (trimmed === "") {
48
+ value = true;
49
+ }
50
+ else if (/^".*"$/.test(trimmed)) {
51
+ value = trimmed.slice(1, -1);
52
+ }
53
+ else if (trimmed.toLowerCase() === "true") {
54
+ value = true;
55
+ }
56
+ else if (trimmed.toLowerCase() === "false") {
57
+ value = false;
58
+ }
59
+ else if (!isNaN(Number(trimmed))) {
60
+ value = Number(trimmed);
61
+ }
62
+ else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
63
+ try {
64
+ value = JSON.parse(trimmed);
65
+ }
66
+ catch { }
67
+ }
18
68
  }
69
+ result[key] = value;
19
70
  }
20
- if (word && words.length < 4)
21
- words.push(word);
22
- return words[2] + (words[3] ? " " + words[3] : "");
71
+ return result;
23
72
  }
24
- export function guessParser(query) {
25
- query = query.trimStart();
26
- if (query[0] === "#") {
73
+ function buildVQL(db, op, collection, query) {
74
+ const hasRelations = "relations" in query;
75
+ if (hasRelations) {
76
+ const relations = {};
77
+ for (const key in query.relations) {
78
+ const value = query.relations[key];
79
+ relations[key] = {
80
+ path: [value.db || db, value.c || key],
81
+ ...value
82
+ };
83
+ delete relations[key].db;
84
+ delete relations[key].c;
85
+ }
86
+ if ("select" in query) {
87
+ query.select = convertSearchObjToSearchArray(query.select);
88
+ }
27
89
  return {
28
- mode: "VQL" + query[1].toUpperCase(),
29
- query: query.slice(2)
90
+ r: {
91
+ path: [db, collection],
92
+ ...query,
93
+ relations,
94
+ }
95
+ };
96
+ }
97
+ else {
98
+ if (query.fields && !query.select) {
99
+ query.select = query.fields;
100
+ delete query.fields;
101
+ }
102
+ if ("select" in query) {
103
+ query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
104
+ }
105
+ return {
106
+ db,
107
+ d: {
108
+ [op]: {
109
+ collection,
110
+ ...query,
111
+ }
112
+ }
30
113
  };
31
114
  }
32
- const _34word = get3rdAnd4thWord(query);
33
- let mode = "VQLS";
34
- if (_34word.includes("{"))
35
- mode = "VQLB";
36
- else if (_34word.includes(":"))
37
- mode = "VQLM";
38
- return {
39
- mode,
40
- query
41
- };
42
115
  }
43
- function parseStringQuery(query) {
44
- const { mode, query: queryRaw } = guessParser(query);
45
- logger.debug("Query mode: " + mode);
46
- if (mode === "VQLB") {
47
- return parseVQLB(queryRaw);
116
+ export function parseVQLS(query) {
117
+ const { db, op, collection, body } = extractMeta(query);
118
+ const parsed = parseArgs(body);
119
+ for (const keysRaw of Object.keys(parsed)) {
120
+ const keys = keysRaw.split(".");
121
+ if (keys.length === 1) {
122
+ continue;
123
+ }
124
+ let obj = parsed;
125
+ for (let i = 0; i < keys.length; i++) {
126
+ const key = keys[i];
127
+ if (i < keys.length - 1) {
128
+ if (!(key in obj)) {
129
+ obj[key] = {};
130
+ }
131
+ obj = obj[key];
132
+ }
133
+ else {
134
+ obj[key] = parsed[keysRaw];
135
+ delete parsed[keysRaw];
136
+ }
137
+ }
138
+ }
139
+ for (const key in aliases) {
140
+ if (key in parsed) {
141
+ parsed[aliases[key]] = parsed[key];
142
+ delete parsed[key];
143
+ }
48
144
  }
49
- else if (mode === "VQLM") {
50
- return parseVQLM(queryRaw);
145
+ if ((op === "find" || op === "findOne") && !("search" in parsed)) {
146
+ parsed.search = {};
51
147
  }
52
- else {
53
- return parseVQLS(queryRaw);
148
+ if ((op === "update" || op === "remove") && !("updater" in parsed) && ("data" in parsed)) {
149
+ parsed.updater = parsed.data;
150
+ delete parsed.data;
54
151
  }
152
+ return buildVQL(db, op, collection, parsed);
55
153
  }
56
- export { parseStringQuery };
@@ -15,3 +15,4 @@ export declare function extractMeta(input: string): {
15
15
  collection: string;
16
16
  body: string;
17
17
  };
18
+ export declare function convertSearchObjToSearchArray(obj: Record<string, any>, parentKeys?: string[]): string[][];
@@ -87,3 +87,17 @@ function extendedCollectionToData(collection) {
87
87
  }
88
88
  return { op, collection: collectionName };
89
89
  }
90
+ export function convertSearchObjToSearchArray(obj, parentKeys = []) {
91
+ return Object.entries(obj).reduce((acc, [key, value]) => {
92
+ const currentPath = [...parentKeys, key];
93
+ if (!value) {
94
+ return acc;
95
+ }
96
+ else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
97
+ return [...acc, ...convertSearchObjToSearchArray(value, currentPath)];
98
+ }
99
+ else {
100
+ return [...acc, currentPath];
101
+ }
102
+ }, []);
103
+ }
@@ -1,4 +1,4 @@
1
- import { parseStringQuery } from "./cpu/string/index.js";
1
+ import { parseVQLS } from "./cpu/string/index.js";
2
2
  export function FF_VQL(app, processor, options = {}) {
3
3
  const path = options.path || "/VQL";
4
4
  const getContext = options.getUser || (() => ({}));
@@ -18,7 +18,7 @@ export function FF_VQL(app, processor, options = {}) {
18
18
  if (options.dev) {
19
19
  app.get(path + "-query", (req, res) => {
20
20
  try {
21
- return res.json(parseStringQuery(req.query?.query || ""));
21
+ return res.json(parseVQLS(req.query?.query || ""));
22
22
  }
23
23
  catch (e) {
24
24
  res.status(500);
package/dist/gw.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { GateWarden } from "@wxn0brp/gate-warden";
2
+ import { ValidFn } from "./types/perm.js";
3
+ export declare function createGwValidFn(gw: GateWarden): ValidFn;
package/dist/gw.js ADDED
@@ -0,0 +1,5 @@
1
+ export function createGwValidFn(gw) {
2
+ return async (path, perm, user) => {
3
+ return gw.hasAccess(user.id, path, perm);
4
+ };
5
+ }
@@ -1,4 +1,4 @@
1
- import { GateWarden } from "@wxn0brp/gate-warden";
1
+ import { ValidFn } from "../types/perm.js";
2
2
  import { RelationQuery } from "../types/vql.js";
3
3
  import { VQLConfig } from "../config.js";
4
- export declare function checkRelationPermission(config: VQLConfig, gw: GateWarden<any>, user: any, query: RelationQuery): Promise<boolean>;
4
+ export declare function checkRelationPermission(config: VQLConfig, validFn: ValidFn, user: any, query: RelationQuery): Promise<boolean>;
@@ -1,11 +1,12 @@
1
1
  import { PermCRUD } from "../types/perm.js";
2
2
  import { extractPathsFromData, hashKey } from "./utils.js";
3
- export async function checkRelationPermission(config, gw, user, query) {
3
+ export async function checkRelationPermission(config, validFn, 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
- const result = await gw.hasAccess(user.id, entityId, PermCRUD.READ);
8
+ // const result = await gw.hasAccess(user.id, entityId, PermCRUD.READ);
9
+ const result = await validFn(entityId, PermCRUD.READ, user);
9
10
  if (result.granted) {
10
11
  return true;
11
12
  }
@@ -42,7 +43,7 @@ export async function checkRelationPermission(config, gw, user, query) {
42
43
  if (relations) {
43
44
  for (const relationKey in relations) {
44
45
  const r = relations[relationKey];
45
- if (!await checkRelationPermission(config, gw, user, { r })) {
46
+ if (!await checkRelationPermission(config, validFn, user, { r })) {
46
47
  return false;
47
48
  }
48
49
  }
@@ -1,5 +1,4 @@
1
- import { GateWarden } from "@wxn0brp/gate-warden";
2
- import { PermCRUD } from "../types/perm.js";
1
+ import { PermCRUD, ValidFn } from "../types/perm.js";
3
2
  import { VQLRequest } from "../types/vql.js";
4
3
  import { VQLConfig } from "../config.js";
5
4
  export declare function extractPaths(config: VQLConfig, query: VQLRequest): {
@@ -16,4 +15,4 @@ export declare function processFieldPath(pathObj: {
16
15
  path: string[];
17
16
  key: string;
18
17
  }): string[];
19
- export declare function checkRequestPermission(config: VQLConfig, gw: GateWarden<any>, user: any, query: VQLRequest): Promise<boolean>;
18
+ export declare function checkRequestPermission(config: VQLConfig, validFn: ValidFn, user: any, query: VQLRequest): Promise<boolean>;
@@ -79,14 +79,15 @@ export function processFieldPath(pathObj) {
79
79
  }
80
80
  return processedPath;
81
81
  }
82
- export async function checkRequestPermission(config, gw, user, query) {
82
+ export async function checkRequestPermission(config, validFn, user, query) {
83
83
  if (!query)
84
84
  return false;
85
85
  const permPaths = extractPaths(config, query);
86
86
  // Helper function to recursively check permissions
87
87
  const checkPermissionRecursively = async (entityId, requiredPerm, fallbackLevels = []) => {
88
88
  // Check if the user has access to the current entity
89
- const result = await gw.hasAccess(user.id, entityId, requiredPerm);
89
+ // const result = await gw.hasAccess(user.id, entityId, requiredPerm);
90
+ const result = await validFn(entityId, requiredPerm, user);
90
91
  if (result.granted) {
91
92
  return true;
92
93
  }
@@ -1,13 +1,13 @@
1
1
  import { Relation, ValtheraCompatible } from "@wxn0brp/db-core";
2
- import { GateWarden } from "@wxn0brp/gate-warden";
3
- import { VQLConfig } from "./config.js";
2
+ import { VQLConfig, VQLConfigInterface } from "./config.js";
4
3
  import { VQL, VQLError, VqlQueryRaw } from "./types/vql.js";
5
- export declare class VQLProcessor<GW = any> {
4
+ import { ValidFn } from "./types/perm.js";
5
+ export declare class VQLProcessor {
6
6
  dbInstances: Record<string, ValtheraCompatible>;
7
- gw: GateWarden<GW>;
8
- config: VQLConfig;
7
+ validFn: ValidFn;
9
8
  relation: Relation;
10
9
  preDefinedSheets: Map<string, VQL>;
11
- constructor(dbInstances: Record<string, ValtheraCompatible>, gw?: GateWarden<GW>, config?: VQLConfig);
12
- execute<T = any>(queryRaw: VqlQueryRaw<T>, user: any): Promise<T | VQLError>;
10
+ config: VQLConfig;
11
+ constructor(dbInstances: Record<string, ValtheraCompatible>, config?: VQLConfig | Partial<VQLConfigInterface>, validFn?: ValidFn);
12
+ execute<T = any>(queryRaw: VqlQueryRaw<T>, user?: any): Promise<T | VQLError>;
13
13
  }
package/dist/processor.js CHANGED
@@ -2,23 +2,23 @@ import { Relation } from "@wxn0brp/db-core";
2
2
  import { VQLConfig } from "./config.js";
3
3
  import { executeRelation } from "./cpu/relation.js";
4
4
  import { executeQuery } from "./cpu/request.js";
5
- import { parseStringQuery } from "./cpu/string/index.js";
6
5
  import logger from "./logger.js";
7
6
  import { executeSheetAndReplaceVars } from "./sheet/index.js";
8
7
  import { validateRaw, validateVql } from "./valid.js";
8
+ import { parseVQLS } from "./cpu/string/index.js";
9
9
  export class VQLProcessor {
10
10
  dbInstances;
11
- gw;
12
- config;
11
+ validFn;
13
12
  relation;
14
13
  preDefinedSheets = new Map();
15
- constructor(dbInstances, gw = null, config = new VQLConfig()) {
14
+ config;
15
+ constructor(dbInstances, config = new VQLConfig(), validFn = async () => ({ granted: true, via: "" })) {
16
16
  this.dbInstances = dbInstances;
17
- this.gw = gw;
18
- this.config = config;
17
+ this.validFn = validFn;
19
18
  this.relation = new Relation(dbInstances);
19
+ this.config = config instanceof VQLConfig ? config : new VQLConfig(config);
20
20
  }
21
- async execute(queryRaw, user) {
21
+ async execute(queryRaw, user = {}) {
22
22
  if (typeof queryRaw === "string" || "query" in queryRaw) {
23
23
  logger.info("Incoming string query");
24
24
  const q = typeof queryRaw === "string" ? queryRaw : queryRaw.query;
@@ -26,7 +26,7 @@ export class VQLProcessor {
26
26
  const ref = typeof queryRaw === "string" ? null : queryRaw.ref;
27
27
  logger.debug(q);
28
28
  try {
29
- queryRaw = parseStringQuery(q);
29
+ queryRaw = parseVQLS(q);
30
30
  logger.debug("transformed query: ", queryRaw);
31
31
  }
32
32
  catch (e) {
@@ -43,14 +43,14 @@ export class VQLProcessor {
43
43
  logger.info("Incoming object query");
44
44
  logger.debug("Raw query: ", queryRaw);
45
45
  }
46
- const validateRawResult = validateRaw(this.config, queryRaw);
46
+ const validateRawResult = validateRaw(queryRaw);
47
47
  if (validateRawResult !== true) {
48
48
  logger.warn("Raw validation failed:", validateRawResult);
49
49
  return validateRawResult;
50
50
  }
51
51
  const query = executeSheetAndReplaceVars(queryRaw, this.preDefinedSheets, user);
52
52
  logger.debug("Executed sheet (expanded query):", query);
53
- const validateVqlResult = validateVql(this.config, query);
53
+ const validateVqlResult = validateVql(query);
54
54
  if (validateVqlResult !== true) {
55
55
  logger.warn("VQL validation failed:", validateVqlResult);
56
56
  return validateVqlResult;
@@ -5,3 +5,8 @@ export declare enum PermCRUD {
5
5
  DELETE = 8,
6
6
  COLLECTION = 16
7
7
  }
8
+ export interface ValidFnResult {
9
+ granted: boolean;
10
+ via?: string;
11
+ }
12
+ export type ValidFn = (path: string, perm: number, user: any) => Promise<ValidFnResult>;
package/dist/valid.d.ts CHANGED
@@ -1,6 +1,3 @@
1
- import Ajv from "ajv";
2
- import { VQLConfig } from "./config.js";
3
1
  import { VQL, VQLError, VQLR } from "./types/vql.js";
4
- export declare const ajv: Ajv;
5
- export declare function validateRaw(config: VQLConfig, query: VQLR): true | VQLError;
6
- export declare function validateVql(config: VQLConfig, query: VQL): true | VQLError;
2
+ export declare function validateRaw(query: VQLR): true | VQLError;
3
+ export declare function validateVql(query: VQL): true | VQLError;
package/dist/valid.js CHANGED
@@ -1,38 +1,60 @@
1
- import Ajv from "ajv";
2
- import ajvFormat from "ajv-formats";
3
- import { readFileSync } from "fs";
4
- import { buildAjvErrorTree } from "./ajv.js";
5
- import { deepMerge } from "./merge.js";
6
- const filePath = import.meta.dirname + "/schema.json";
7
- const schema = JSON.parse(readFileSync(filePath, "utf-8"));
8
- export const ajv = new Ajv({
9
- allowUnionTypes: true,
10
- strict: false
11
- });
12
- ajvFormat(ajv);
13
- const validVQLR = ajv.compile(schema);
14
- const modSchema = schema;
15
- modSchema.anyOf = [
16
- {
17
- "$ref": "#/definitions/VQL"
18
- }
19
- ];
20
- const validVQL = ajv.compile(modSchema);
21
- export function validateRaw(config, query) {
22
- if (!validVQLR(query)) {
23
- let why = validVQLR.errors;
24
- why = config.formatAjv ? buildAjvErrorTree(why) : why;
25
- why = deepMerge(why, query);
26
- return { err: true, msg: "Invalid query raw", c: 400, why };
27
- }
1
+ function emptyErr() {
2
+ return {
3
+ err: true,
4
+ msg: "Bad query",
5
+ c: 400
6
+ };
7
+ }
8
+ function isObj(obj, one = true) {
9
+ return typeof obj === "object" && obj !== null && !Array.isArray(obj) && (!one || Object.keys(obj).length !== 0);
10
+ }
11
+ export function validateRaw(query) {
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)))
15
+ return true;
16
+ return emptyErr();
17
+ }
18
+ function validR(query) {
19
+ const { r } = query;
20
+ if (!("path" in r) || !Array.isArray(r.path) || r.path.length !== 2)
21
+ return emptyErr();
22
+ if (!isObj(r.search, false))
23
+ return emptyErr();
28
24
  return true;
29
25
  }
30
- export function validateVql(config, query) {
31
- if (!validVQL(query)) {
32
- let why = validVQL.errors;
33
- why = config.formatAjv ? buildAjvErrorTree(why) : why;
34
- why = deepMerge(why, query);
35
- return { err: true, msg: "Invalid query", c: 400, why };
26
+ function validD(query) {
27
+ const { d } = query;
28
+ const key = Object.keys(d)[0];
29
+ if (key === "getCollection")
30
+ return true;
31
+ const value = d[key];
32
+ if (typeof value.collection !== "string" || value.collection.trim() === "")
33
+ return emptyErr();
34
+ if (key === "issetCollection" || key === "ensureCollection")
35
+ return true;
36
+ if (key === "add") {
37
+ if (!isObj(value.data, false))
38
+ return emptyErr();
39
+ else
40
+ return true;
41
+ }
42
+ if ("search" in value && !isObj(value.search))
43
+ return emptyErr();
44
+ if (key === "find" || key === "findOne" || key === "remove" || key === "removeOne")
45
+ return true;
46
+ if (key === "update" || key === "updateOne" || key === "updateOneOrAdd") {
47
+ if (!isObj(value.updater, false))
48
+ return emptyErr();
49
+ else
50
+ return true;
36
51
  }
37
52
  return true;
38
53
  }
54
+ export function validateVql(query) {
55
+ if ("r" in query && isObj(query.r))
56
+ return validR(query);
57
+ if ("d" in query && isObj(query.d))
58
+ return validD(query);
59
+ return emptyErr();
60
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/vql",
3
- "version": "0.5.1",
3
+ "version": "0.6.0-alpha.0",
4
4
  "main": "dist/index.js",
5
5
  "author": "wxn0brP",
6
6
  "license": "MIT",
@@ -15,8 +15,9 @@
15
15
  "dist"
16
16
  ],
17
17
  "peerDependencies": {
18
- "@wxn0brp/db-core": ">=0.1.2",
19
- "@wxn0brp/falcon-frame": ">=0.0.20"
18
+ "@wxn0brp/db-core": ">=0.1.4",
19
+ "@wxn0brp/falcon-frame": ">=0.0.20",
20
+ "@wxn0brp/gate-warden": ">=0.4.0"
20
21
  },
21
22
  "peerDependenciesMeta": {
22
23
  "@wxn0brp/falcon-frame": {
@@ -24,26 +25,23 @@
24
25
  },
25
26
  "@wxn0brp/db-core": {
26
27
  "optional": false
28
+ },
29
+ "@wxn0brp/gate-warden": {
30
+ "optional": true
27
31
  }
28
32
  },
29
33
  "devDependencies": {
30
34
  "@types/node": "^24.1.0",
31
35
  "@wxn0brp/db": "^0.30.0",
32
36
  "@wxn0brp/falcon-frame": "0.0.20",
37
+ "@wxn0brp/gate-warden": "^0.4.0",
33
38
  "dotenv": "^17.2.0",
34
39
  "esbuild": "^0.25.8",
35
- "source-map-support": "^0.5.21",
36
40
  "tsc-alias": "^1.8.10",
37
- "typescript": "^5.7.3",
38
- "typescript-json-schema": "^0.65.1"
41
+ "typescript": "^5.7.3"
39
42
  },
40
43
  "dependencies": {
41
- "@wxn0brp/gate-warden": ">=0.4.0",
42
- "@wxn0brp/lucerna-log": "^0.1.1",
43
- "ajv": "^8.17.1",
44
- "ajv-formats": "^3.0.1",
45
- "js-yaml": "^4.1.0",
46
- "json5": "^2.2.3"
44
+ "@wxn0brp/lucerna-log": "^0.1.1"
47
45
  },
48
46
  "exports": {
49
47
  ".": {
package/dist/ajv.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import { ErrorObject } from "ajv";
2
- export declare function buildAjvErrorTree(errors: ErrorObject[]): Record<string, any>;
package/dist/ajv.js DELETED
@@ -1,80 +0,0 @@
1
- function parseInstancePath(path) {
2
- if (!path)
3
- return [];
4
- const unescaped = path.slice(1).replace(/~0/g, "~").replace(/~1/g, "/");
5
- return unescaped.split("/").filter(seg => seg !== "");
6
- }
7
- function formatSingleError(error) {
8
- const { keyword, params, message } = error;
9
- switch (keyword) {
10
- case "type":
11
- return `expected type: ${params.type}`;
12
- case "required":
13
- return `missing required property: ${params.missingProperty}`;
14
- case "additionalProperties":
15
- return `unexpected property: ${params.additionalProperty}`;
16
- case "enum":
17
- const allowedValues = params.allowedValues.join(", ");
18
- return `value not in: [${allowedValues}]`;
19
- case "minLength":
20
- return `too short (min ${params.limit} characters)`;
21
- case "maxLength":
22
- return `too long (max ${params.limit} characters)`;
23
- case "minimum":
24
- return `value < ${params.limit}`;
25
- case "maximum":
26
- return `value > ${params.limit}`;
27
- case "pattern":
28
- return `does not match pattern`;
29
- case "anyOf":
30
- return `value does not satisfy any of the variants`;
31
- case "oneOf":
32
- return `value satisfies multiple variants`;
33
- case "allOf":
34
- return `value does not satisfy all conditions`;
35
- default:
36
- return message || `validation error: ${keyword}`;
37
- }
38
- }
39
- // Recursively remove error types and leave only messages
40
- function finalizeNode(node) {
41
- if (node.__errors) {
42
- node.__errors = node.__errors.map((e) => e.message);
43
- }
44
- for (const key in node) {
45
- if (key !== "__errors" && typeof node[key] === "object") {
46
- finalizeNode(node[key]);
47
- }
48
- }
49
- }
50
- export function buildAjvErrorTree(errors) {
51
- const errorTree = {};
52
- for (const error of errors) {
53
- const segments = parseInstancePath(error.instancePath);
54
- let currentNode = errorTree;
55
- for (const segment of segments) {
56
- if (!(segment in currentNode)) {
57
- currentNode[segment] = {};
58
- }
59
- currentNode = currentNode[segment];
60
- }
61
- const formattedMessage = formatSingleError(error);
62
- const isComposite = ["anyOf", "oneOf", "allOf"].includes(error.keyword);
63
- if (!currentNode.__errors) {
64
- currentNode.__errors = [];
65
- }
66
- if (isComposite) {
67
- // Replace all previous errors with only this general one
68
- currentNode.__errors = [{ keyword: error.keyword, message: formattedMessage }];
69
- }
70
- else {
71
- // Check if there is already a general error
72
- const hasComposite = currentNode.__errors.some((e) => ["anyOf", "oneOf", "allOf"].includes(e.keyword));
73
- if (!hasComposite) {
74
- currentNode.__errors.push({ keyword: error.keyword, message: formattedMessage });
75
- }
76
- }
77
- }
78
- finalizeNode(errorTree);
79
- return errorTree;
80
- }
@@ -1,2 +0,0 @@
1
- import { VQL } from "../../types/vql.js";
2
- export declare function parseVQLB(query: string): VQL;
@@ -1,8 +0,0 @@
1
- import JSON5 from "json5";
2
- import { extractMeta } from "./utils.js";
3
- import { processVQL_MB } from "./middle.js";
4
- export function parseVQLB(query) {
5
- const { db, op, collection, body } = extractMeta(query);
6
- const parsed = JSON5.parse(body);
7
- return processVQL_MB(db, op, collection, parsed);
8
- }
@@ -1,3 +0,0 @@
1
- import { VQL } from "../../types/vql.js";
2
- export declare function processVQL_MB(db: string, op: string, collection: string, parsed: Record<string, any>): VQL;
3
- export declare function convertSearchObjToSearchArray(obj: Record<string, any>, parentKeys?: string[]): string[][];
@@ -1,41 +0,0 @@
1
- export function processVQL_MB(db, op, collection, parsed) {
2
- return "relations" in parsed ?
3
- processRelation_MB(db, collection, parsed) :
4
- processQuery_MB(db, op, collection, parsed);
5
- }
6
- export function convertSearchObjToSearchArray(obj, parentKeys = []) {
7
- return Object.entries(obj).reduce((acc, [key, value]) => {
8
- const currentPath = [...parentKeys, key];
9
- if (!value) {
10
- return acc;
11
- }
12
- else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
13
- return [...acc, ...convertSearchObjToSearchArray(value, currentPath)];
14
- }
15
- else {
16
- return [...acc, currentPath];
17
- }
18
- }, []);
19
- }
20
- function processRelation_MB(db, collection, parsed) {
21
- if ("select" in parsed && typeof parsed.select === "object" && !Array.isArray(parsed.select)) {
22
- parsed.select = convertSearchObjToSearchArray(parsed.select);
23
- }
24
- return {
25
- r: {
26
- path: [parsed.db || db, parsed.c || collection],
27
- ...parsed
28
- }
29
- };
30
- }
31
- function processQuery_MB(db, op, collection, parsed) {
32
- return {
33
- db,
34
- d: {
35
- [op]: {
36
- collection,
37
- ...parsed
38
- }
39
- }
40
- };
41
- }
@@ -1,2 +0,0 @@
1
- import { VQL } from "../../types/vql.js";
2
- export declare function parseVQLS(query: string): VQL;
@@ -1,155 +0,0 @@
1
- import { extractMeta } from "./utils.js";
2
- import JSON5 from "json5";
3
- import { convertSearchObjToSearchArray } from "./middle.js";
4
- const aliases = {
5
- s: "search",
6
- f: "fields",
7
- o: "options",
8
- r: "relations",
9
- d: "data",
10
- e: "select",
11
- u: "updater",
12
- };
13
- function parseArgs(input) {
14
- const result = {};
15
- const tokens = [];
16
- let current = "";
17
- let inQuotes = false;
18
- let escape = false;
19
- for (let i = 0; i < input.length; i++) {
20
- const char = input[i];
21
- if (escape) {
22
- current += char;
23
- escape = false;
24
- }
25
- else if (char === "\\") {
26
- escape = true;
27
- }
28
- else if (char === "\"") {
29
- inQuotes = !inQuotes;
30
- }
31
- else if (!inQuotes && (char === " " || char === "=")) {
32
- if (current !== "") {
33
- tokens.push(current);
34
- current = "";
35
- }
36
- }
37
- else {
38
- current += char;
39
- }
40
- }
41
- if (current !== "") {
42
- tokens.push(current);
43
- }
44
- for (let i = 0; i < tokens.length; i += 2) {
45
- const key = tokens[i];
46
- let value = tokens[i + 1] ?? true;
47
- if (typeof value === "string") {
48
- const trimmed = value.trim();
49
- if (trimmed === "") {
50
- value = true;
51
- }
52
- else if (/^".*"$/.test(trimmed)) {
53
- value = trimmed.slice(1, -1);
54
- }
55
- else if (trimmed.toLowerCase() === "true") {
56
- value = true;
57
- }
58
- else if (trimmed.toLowerCase() === "false") {
59
- value = false;
60
- }
61
- else if (!isNaN(Number(trimmed))) {
62
- value = Number(trimmed);
63
- }
64
- else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
65
- try {
66
- value = JSON5.parse(trimmed);
67
- }
68
- catch { }
69
- }
70
- }
71
- result[key] = value;
72
- }
73
- return result;
74
- }
75
- function buildVQL(db, op, collection, query) {
76
- const hasRelations = "relations" in query;
77
- if (hasRelations) {
78
- const relations = {};
79
- for (const key in query.relations) {
80
- const value = query.relations[key];
81
- relations[key] = {
82
- path: [value.db || db, value.c || key],
83
- ...value
84
- };
85
- delete relations[key].db;
86
- delete relations[key].c;
87
- }
88
- if ("select" in query) {
89
- query.select = convertSearchObjToSearchArray(query.select);
90
- }
91
- return {
92
- r: {
93
- path: [db, collection],
94
- ...query,
95
- relations,
96
- }
97
- };
98
- }
99
- else {
100
- if (query.fields && !query.select) {
101
- query.select = query.fields;
102
- delete query.fields;
103
- }
104
- if ("select" in query) {
105
- query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
106
- }
107
- return {
108
- db,
109
- d: {
110
- [op]: {
111
- collection,
112
- ...query,
113
- }
114
- }
115
- };
116
- }
117
- }
118
- export function parseVQLS(query) {
119
- const { db, op, collection, body } = extractMeta(query);
120
- const parsed = parseArgs(body);
121
- for (const keysRaw of Object.keys(parsed)) {
122
- const keys = keysRaw.split(".");
123
- if (keys.length === 1) {
124
- continue;
125
- }
126
- let obj = parsed;
127
- for (let i = 0; i < keys.length; i++) {
128
- const key = keys[i];
129
- if (i < keys.length - 1) {
130
- if (!(key in obj)) {
131
- obj[key] = {};
132
- }
133
- obj = obj[key];
134
- }
135
- else {
136
- obj[key] = parsed[keysRaw];
137
- delete parsed[keysRaw];
138
- }
139
- }
140
- }
141
- for (const key in aliases) {
142
- if (key in parsed) {
143
- parsed[aliases[key]] = parsed[key];
144
- delete parsed[key];
145
- }
146
- }
147
- if ((op === "find" || op === "findOne") && !("search" in parsed)) {
148
- parsed.search = {};
149
- }
150
- if ((op === "update" || op === "remove") && !("updater" in parsed) && ("data" in parsed)) {
151
- parsed.updater = parsed.data;
152
- delete parsed.data;
153
- }
154
- return buildVQL(db, op, collection, parsed);
155
- }
@@ -1,2 +0,0 @@
1
- import { VQL } from "../../types/vql.js";
2
- export declare function parseVQLM(query: string): VQL;
@@ -1,19 +0,0 @@
1
- import { processVQL_MB } from "./middle.js";
2
- import { extractMeta } from "./utils.js";
3
- import yaml from "js-yaml";
4
- export function parseVQLM(query) {
5
- const { db, op, collection } = extractMeta(query);
6
- let lineEndIndex = 0;
7
- let foundNonWhitespace = false;
8
- for (let i = 0; i < query.length; i++) {
9
- if (!foundNonWhitespace && query[i] !== " " && query[i] !== "\t" && query[i] !== "\r" && query[i] !== "\n")
10
- foundNonWhitespace = true;
11
- if (foundNonWhitespace && query[i] === "\n") {
12
- lineEndIndex = i;
13
- break;
14
- }
15
- }
16
- const body = query.slice(lineEndIndex + 1);
17
- const parsed = yaml.load(body);
18
- return processVQL_MB(db, op, collection, parsed);
19
- }
package/dist/schema.json DELETED
@@ -1 +0,0 @@
1
- {"anyOf":[{"$ref":"#/definitions/VQLRefRequired"},{"allOf":[{"$ref":"#/definitions/VQLRequest"},{"$ref":"#/definitions/VQLRef"}]},{"allOf":[{"$ref":"#/definitions/RelationQuery"},{"$ref":"#/definitions/VQLRef"}]},{"allOf":[{"$ref":"#/definitions/DeepPartial<VQLRequest&VQLRef>"},{"$ref":"#/definitions/VQLRef"},{"$ref":"#/definitions/Required<Pick<VQLRef,\"ref\">>"}]},{"allOf":[{"$ref":"#/definitions/DeepPartial<RelationQuery&VQLRef>"},{"$ref":"#/definitions/VQLRef"},{"$ref":"#/definitions/Required<Pick<VQLRef,\"ref\">>"}]}],"definitions":{"VQLRefRequired":{"allOf":[{"$ref":"#/definitions/VQLRef"},{"$ref":"#/definitions/Required<Pick<VQLRef,\"ref\">>"}]},"VQLRef":{"type":"object","properties":{"ref":{"type":"string"},"var":{"type":"object","additionalProperties":{}}}},"Required<Pick<VQLRef,\"ref\">>":{"type":"object","properties":{"ref":{"type":"string"}},"required":["ref"]},"VQLRequest":{"type":"object","properties":{"db":{"type":"string"},"d":{"$ref":"#/definitions/VQLQueryData"}},"required":["d","db"]},"VQLQueryData":{"anyOf":[{"type":"object","properties":{"find":{"$ref":"#/definitions/VQLFind"}},"required":["find"]},{"type":"object","properties":{"findOne":{"$ref":"#/definitions/VQLFindOne"}},"required":["findOne"]},{"type":"object","properties":{"f":{"$ref":"#/definitions/VQLFindOne"}},"required":["f"]},{"type":"object","properties":{"add":{"$ref":"#/definitions/VQLAdd"}},"required":["add"]},{"type":"object","properties":{"update":{"$ref":"#/definitions/VQLUpdate"}},"required":["update"]},{"type":"object","properties":{"updateOne":{"$ref":"#/definitions/VQLUpdateOne"}},"required":["updateOne"]},{"type":"object","properties":{"remove":{"$ref":"#/definitions/VQLRemove"}},"required":["remove"]},{"type":"object","properties":{"removeOne":{"$ref":"#/definitions/VQLRemoveOne"}},"required":["removeOne"]},{"type":"object","properties":{"updateOneOrAdd":{"$ref":"#/definitions/VQLUpdateOneOrAdd"}},"required":["updateOneOrAdd"]},{"type":"object","properties":{"removeCollection":{"$ref":"#/definitions/VQLCollectionOperation"}},"required":["removeCollection"]},{"type":"object","properties":{"ensureCollection":{"$ref":"#/definitions/VQLCollectionOperation"}},"required":["ensureCollection"]},{"type":"object","properties":{"issetCollection":{"$ref":"#/definitions/VQLCollectionOperation"}},"required":["issetCollection"]},{"type":"object","properties":{"getCollections":{"type":"object","properties":{}}},"required":["getCollections"]}]},"VQLFind":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"},"limit":{"type":"number"},"fields":{"$ref":"#/definitions/VQLFields"},"select":{"$ref":"#/definitions/VQLFields"},"relations":{"$ref":"#/definitions/VQLRelations"},"options":{"$ref":"#/definitions/DbFindOpts"},"searchOpts":{"$ref":"#/definitions/FindOpts"}},"required":["collection"]},"Search<any>":{"anyOf":[{"$ref":"#/definitions/SearchOptions","description":"SearchOptions can be either a function or an object with predefined operators."},{"type":"object"}]},"SearchOptions":{"description":"SearchOptions can be either a function or an object with predefined operators.","allOf":[{"description":"Logical Operators","type":"object","properties":{"$and":{"description":"Recursively applies multiple conditions, all of which must evaluate to true.\nCan include other operators such as $gt, $exists, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/SearchOptions"}},"$or":{"description":"Recursively applies multiple conditions, at least one of which must evaluate to true.\nCan include other operators such as $lt, $type, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/SearchOptions"}},"$not":{"$ref":"#/definitions/SearchOptions","description":"Negates a single condition.\nCan include any other operator as its value."}}},{"description":"Comparison Operators","type":"object","properties":{"$gt":{"$ref":"#/definitions/Record<string,number>"},"$lt":{"$ref":"#/definitions/Record<string,number>"},"$gte":{"$ref":"#/definitions/Record<string,number>"},"$lte":{"$ref":"#/definitions/Record<string,number>"},"$in":{"$ref":"#/definitions/Record<string,any[]>"},"$nin":{"$ref":"#/definitions/Record<string,any[]>"},"$between":{"$ref":"#/definitions/Record<string,[number,number]>"}}},{"description":"Type and Existence Operators","type":"object","properties":{"$exists":{"$ref":"#/definitions/Record<string,boolean>"},"$type":{"$ref":"#/definitions/Record<string,string>"}}},{"description":"Array Operators","type":"object","properties":{"$arrinc":{"$ref":"#/definitions/Record<string,any[]>"},"$arrincall":{"$ref":"#/definitions/Record<string,any[]>"},"$size":{"$ref":"#/definitions/Record<string,number>"}}},{"description":"String Operators","type":"object","properties":{"$regex":{"$ref":"#/definitions/Record<string,RegExp>"},"$startsWith":{"$ref":"#/definitions/Record<string,string>"},"$endsWith":{"$ref":"#/definitions/Record<string,string>"}}},{"description":"Other Operators","type":"object","properties":{"$subset":{"$ref":"#/definitions/Record<string,any>"}}},{"$ref":"#/definitions/Arg"}]},"Record<string,number>":{"type":"object"},"Record<string,any[]>":{"type":"object"},"Record<string,[number,number]>":{"type":"object"},"Record<string,boolean>":{"type":"object"},"Record<string,string>":{"type":"object"},"Record<string,RegExp>":{"type":"object"},"Record<string,any>":{"type":"object"},"Arg":{"type":"object","additionalProperties":{},"properties":{"_id":{"type":"string"}}},"VQLFields":{"anyOf":[{"type":"array","items":{"type":"string"}},{"$ref":"#/definitions/Record<string,number|boolean>"}]},"Record<string,number|boolean>":{"type":"object"},"VQLRelations":{"type":"object"},"DbFindOpts":{"type":"object","properties":{"reverse":{"type":"boolean"},"max":{"type":"number"},"offset":{"type":"number"},"sortBy":{"type":"string"},"sortAsc":{"type":"boolean"}}},"FindOpts":{"type":"object","properties":{"select":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"type":"string"}},"transform":{"$ref":"#/definitions/Function"}}},"Function":{"type":"object","properties":{"prototype":{},"length":{"type":"number"},"arguments":{},"caller":{"$ref":"#/definitions/Function"},"name":{"type":"string"}},"required":["arguments","caller","length","name","prototype"]},"VQLFindOne":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"},"fields":{"$ref":"#/definitions/VQLFields"},"select":{"$ref":"#/definitions/VQLFields"},"relations":{"$ref":"#/definitions/VQLRelations"},"searchOpts":{"$ref":"#/definitions/FindOpts"}},"required":["collection","search"]},"VQLAdd":{"type":"object","properties":{"collection":{"type":"string"},"data":{"$ref":"#/definitions/Arg"},"id_gen":{"type":"boolean"}},"required":["collection","data"]},"VQLUpdate":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"},"updater":{"$ref":"#/definitions/UpdaterArg"}},"required":["collection","search","updater"]},"UpdaterArg":{"allOf":[{"description":"Arrays","type":"object","properties":{"$push":{},"$pushset":{"description":"Pushes items into an array and removes duplicates"},"$pull":{},"$pullall":{}}},{"description":"Objects","type":"object","properties":{"$merge":{}}},{"description":"Values","type":"object","properties":{"$set":{},"$inc":{},"$dec":{},"$unset":{},"$rename":{}}},{"type":"object","additionalProperties":{}}]},"VQLUpdateOne":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"},"updater":{"$ref":"#/definitions/UpdaterArg"}},"required":["collection","search","updater"]},"VQLRemove":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"}},"required":["collection","search"]},"VQLRemoveOne":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"}},"required":["collection","search"]},"VQLUpdateOneOrAdd":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/Search<any>"},"updater":{"$ref":"#/definitions/UpdaterArg"},"add_arg":{"$ref":"#/definitions/Arg"},"id_gen":{"type":"boolean"}},"required":["collection","search","updater"]},"VQLCollectionOperation":{"type":"object","properties":{"collection":{"type":"string"}},"required":["collection"]},"RelationQuery":{"type":"object","properties":{"r":{"type":"object","properties":{"path":{"type":"array","items":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2},"search":{"$ref":"#/definitions/Search<any>"},"relations":{"$ref":"#/definitions/RelationTypes.Relation"},"many":{"type":"boolean"},"options":{"$ref":"#/definitions/DbFindOpts"},"select":{"type":"array","items":{"type":"array","items":{"type":"string"}}}},"required":["path","relations","search"]}},"required":["r"]},"RelationTypes.Relation":{"type":"object","additionalProperties":{"$ref":"#/definitions/RelationTypes.RelationConfig"}},"RelationTypes.RelationConfig":{"type":"object","properties":{"path":{"type":"array","items":[{"type":"string"},{"type":"string"}],"minItems":2,"maxItems":2},"pk":{"type":"string"},"fk":{"type":"string"},"as":{"type":"string"},"select":{"type":"array","items":{"type":"string"}},"findOpts":{"$ref":"#/definitions/DbFindOpts"},"type":{"enum":["1","11","1n","nm"],"type":"string"},"relations":{"$ref":"#/definitions/RelationTypes.Relation"},"through":{"type":"object","properties":{"table":{"type":"string"},"db":{"type":"string"},"pk":{"type":"string"},"fk":{"type":"string"}},"required":["fk","pk","table"]}},"required":["path"]},"DeepPartial<VQLRequest&VQLRef>":{"type":"object","properties":{"db":{"type":"string"},"d":{"$ref":"#/definitions/DeepPartial<VQLQueryData>"},"ref":{"type":"string"},"var":{"$ref":"#/definitions/DeepPartial<{[k:string]:any;}>"}}},"DeepPartial<VQLQueryData>":{"anyOf":[{"$ref":"#/definitions/DeepPartial<{find:VQLFind;}>"},{"$ref":"#/definitions/DeepPartial<{findOne:VQLFindOne;}>"},{"$ref":"#/definitions/DeepPartial<{f:VQLFindOne;}>"},{"$ref":"#/definitions/DeepPartial<{add:VQLAdd;}>"},{"$ref":"#/definitions/DeepPartial<{update:VQLUpdate;}>"},{"$ref":"#/definitions/DeepPartial<{updateOne:VQLUpdateOne;}>"},{"$ref":"#/definitions/DeepPartial<{remove:VQLRemove;}>"},{"$ref":"#/definitions/DeepPartial<{removeOne:VQLRemoveOne;}>"},{"$ref":"#/definitions/DeepPartial<{updateOneOrAdd:VQLUpdateOneOrAdd;}>"},{"$ref":"#/definitions/DeepPartial<{removeCollection:VQLCollectionOperation;}>"},{"$ref":"#/definitions/DeepPartial<{ensureCollection:VQLCollectionOperation;}>"},{"$ref":"#/definitions/DeepPartial<{issetCollection:VQLCollectionOperation;}>"},{"$ref":"#/definitions/DeepPartial<{getCollections:{};}>"}]},"DeepPartial<{find:VQLFind;}>":{"type":"object","properties":{"find":{"$ref":"#/definitions/DeepPartial<VQLFind>"}}},"DeepPartial<VQLFind>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"limit":{"type":"number"},"fields":{"$ref":"#/definitions/DeepPartial<VQLFields>"},"select":{"$ref":"#/definitions/DeepPartial<VQLFields>"},"relations":{"$ref":"#/definitions/DeepPartial<VQLRelations>"},"options":{"$ref":"#/definitions/DeepPartial<DbFindOpts>"},"searchOpts":{"$ref":"#/definitions/DeepPartial<FindOpts>"}}},"DeepPartial<Search<any>>":{"anyOf":[{"$ref":"#/definitions/DeepPartial<SearchOptions>"},{"$ref":"#/definitions/DeepPartial<SearchFunc<any>>"}]},"DeepPartial<SearchOptions>":{"type":"object","properties":{"$and":{"description":"Recursively applies multiple conditions, all of which must evaluate to true.\nCan include other operators such as $gt, $exists, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1"}},"$or":{"description":"Recursively applies multiple conditions, at least one of which must evaluate to true.\nCan include other operators such as $lt, $type, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1"}},"$not":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1","description":"Negates a single condition.\nCan include any other operator as its value."},"$gt":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$lt":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$gte":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$lte":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$in":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$nin":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$between":{"$ref":"#/definitions/DeepPartial<Record<string,[number,number]>>"},"$exists":{"$ref":"#/definitions/DeepPartial<Record<string,boolean>>"},"$type":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$arrinc":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$arrincall":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$size":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$regex":{"$ref":"#/definitions/DeepPartial<Record<string,RegExp>>"},"$startsWith":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$endsWith":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$subset":{"$ref":"#/definitions/DeepPartial<Record<string,any>>"},"_id":{"type":"string"}}},"DeepPartial<SearchOptions>_1":{"type":"object","properties":{"$and":{"description":"Recursively applies multiple conditions, all of which must evaluate to true.\nCan include other operators such as $gt, $exists, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1"}},"$or":{"description":"Recursively applies multiple conditions, at least one of which must evaluate to true.\nCan include other operators such as $lt, $type, or nested $and/$or conditions.","type":"array","items":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1"}},"$not":{"$ref":"#/definitions/DeepPartial<SearchOptions>_1","description":"Negates a single condition.\nCan include any other operator as its value."},"$gt":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$lt":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$gte":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$lte":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$in":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$nin":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$between":{"$ref":"#/definitions/DeepPartial<Record<string,[number,number]>>"},"$exists":{"$ref":"#/definitions/DeepPartial<Record<string,boolean>>"},"$type":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$arrinc":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$arrincall":{"$ref":"#/definitions/DeepPartial<Record<string,any[]>>"},"$size":{"$ref":"#/definitions/DeepPartial<Record<string,number>>"},"$regex":{"$ref":"#/definitions/DeepPartial<Record<string,RegExp>>"},"$startsWith":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$endsWith":{"$ref":"#/definitions/DeepPartial<Record<string,string>>"},"$subset":{"$ref":"#/definitions/DeepPartial<Record<string,any>>"},"_id":{"type":"string"}}},"DeepPartial<Record<string,number>>":{"type":"object"},"DeepPartial<Record<string,any[]>>":{"type":"object"},"DeepPartial<Record<string,[number,number]>>":{"type":"object"},"DeepPartial<Record<string,boolean>>":{"type":"object"},"DeepPartial<Record<string,string>>":{"type":"object"},"DeepPartial<Record<string,RegExp>>":{"type":"object"},"DeepPartial<Record<string,any>>":{"type":"object"},"DeepPartial<SearchFunc<any>>":{"type":"object"},"DeepPartial<VQLFields>":{"anyOf":[{"type":"array","items":{"type":"string"}},{"$ref":"#/definitions/DeepPartial<Record<string,number|boolean>>"}]},"DeepPartial<Record<string,number|boolean>>":{"type":"object"},"DeepPartial<VQLRelations>":{"type":"object"},"DeepPartial<DbFindOpts>":{"type":"object","properties":{"reverse":{"type":"boolean"},"max":{"type":"number"},"offset":{"type":"number"},"sortBy":{"type":"string"},"sortAsc":{"type":"boolean"}}},"DeepPartial<FindOpts>":{"type":"object","properties":{"select":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"type":"string"}},"transform":{"$ref":"#/definitions/DeepPartial<Function>"}}},"DeepPartial<Function>":{"type":"object","properties":{"apply":{"$ref":"#/definitions/DeepPartial<(this:Function,thisArg:any,argArray?:any)=>any>"},"call":{"$ref":"#/definitions/DeepPartial<(this:Function,thisArg:any,...argArray:any[])=>any>"},"bind":{"$ref":"#/definitions/DeepPartial<(this:Function,thisArg:any,...argArray:any[])=>any>_1"},"toString":{"$ref":"#/definitions/DeepPartial<()=>string>"},"prototype":{},"length":{"type":"number"},"arguments":{},"caller":{"$ref":"#/definitions/DeepPartial<Function>"},"name":{"type":"string"},"__@hasInstance@73":{"$ref":"#/definitions/DeepPartial<(value:any)=>boolean>"}}},"DeepPartial<(this:Function,thisArg:any,argArray?:any)=>any>":{"type":"object"},"DeepPartial<(this:Function,thisArg:any,...argArray:any[])=>any>":{"type":"object"},"DeepPartial<(this:Function,thisArg:any,...argArray:any[])=>any>_1":{"type":"object"},"DeepPartial<()=>string>":{"type":"object"},"DeepPartial<(value:any)=>boolean>":{"type":"object"},"DeepPartial<{findOne:VQLFindOne;}>":{"type":"object","properties":{"findOne":{"$ref":"#/definitions/DeepPartial<VQLFindOne>"}}},"DeepPartial<VQLFindOne>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"fields":{"$ref":"#/definitions/DeepPartial<VQLFields>"},"select":{"$ref":"#/definitions/DeepPartial<VQLFields>"},"relations":{"$ref":"#/definitions/DeepPartial<VQLRelations>"},"searchOpts":{"$ref":"#/definitions/DeepPartial<FindOpts>"}}},"DeepPartial<{f:VQLFindOne;}>":{"type":"object","properties":{"f":{"$ref":"#/definitions/DeepPartial<VQLFindOne>"}}},"DeepPartial<{add:VQLAdd;}>":{"type":"object","properties":{"add":{"$ref":"#/definitions/DeepPartial<VQLAdd>"}}},"DeepPartial<VQLAdd>":{"type":"object","properties":{"collection":{"type":"string"},"data":{"$ref":"#/definitions/DeepPartial<Arg>"},"id_gen":{"type":"boolean"}}},"DeepPartial<Arg>":{"type":"object","properties":{"_id":{"type":"string"}}},"DeepPartial<{update:VQLUpdate;}>":{"type":"object","properties":{"update":{"$ref":"#/definitions/DeepPartial<VQLUpdate>"}}},"DeepPartial<VQLUpdate>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"updater":{"$ref":"#/definitions/DeepPartial<UpdaterArg>"}}},"DeepPartial<UpdaterArg>":{"type":"object","properties":{"$push":{},"$pushset":{"description":"Pushes items into an array and removes duplicates"},"$pull":{},"$pullall":{},"$merge":{},"$set":{},"$inc":{},"$dec":{},"$unset":{},"$rename":{}}},"DeepPartial<{updateOne:VQLUpdateOne;}>":{"type":"object","properties":{"updateOne":{"$ref":"#/definitions/DeepPartial<VQLUpdateOne>"}}},"DeepPartial<VQLUpdateOne>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"updater":{"$ref":"#/definitions/DeepPartial<UpdaterArg>"}}},"DeepPartial<{remove:VQLRemove;}>":{"type":"object","properties":{"remove":{"$ref":"#/definitions/DeepPartial<VQLRemove>"}}},"DeepPartial<VQLRemove>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"}}},"DeepPartial<{removeOne:VQLRemoveOne;}>":{"type":"object","properties":{"removeOne":{"$ref":"#/definitions/DeepPartial<VQLRemoveOne>"}}},"DeepPartial<VQLRemoveOne>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"}}},"DeepPartial<{updateOneOrAdd:VQLUpdateOneOrAdd;}>":{"type":"object","properties":{"updateOneOrAdd":{"$ref":"#/definitions/DeepPartial<VQLUpdateOneOrAdd>"}}},"DeepPartial<VQLUpdateOneOrAdd>":{"type":"object","properties":{"collection":{"type":"string"},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"updater":{"$ref":"#/definitions/DeepPartial<UpdaterArg>"},"add_arg":{"$ref":"#/definitions/DeepPartial<Arg>"},"id_gen":{"type":"boolean"}}},"DeepPartial<{removeCollection:VQLCollectionOperation;}>":{"type":"object","properties":{"removeCollection":{"$ref":"#/definitions/DeepPartial<VQLCollectionOperation>"}}},"DeepPartial<VQLCollectionOperation>":{"type":"object","properties":{"collection":{"type":"string"}}},"DeepPartial<{ensureCollection:VQLCollectionOperation;}>":{"type":"object","properties":{"ensureCollection":{"$ref":"#/definitions/DeepPartial<VQLCollectionOperation>"}}},"DeepPartial<{issetCollection:VQLCollectionOperation;}>":{"type":"object","properties":{"issetCollection":{"$ref":"#/definitions/DeepPartial<VQLCollectionOperation>"}}},"DeepPartial<{getCollections:{};}>":{"type":"object","properties":{"getCollections":{"$ref":"#/definitions/DeepPartial<{}>"}}},"DeepPartial<{}>":{"type":"object"},"DeepPartial<{[k:string]:any;}>":{"type":"object"},"DeepPartial<RelationQuery&VQLRef>":{"type":"object","properties":{"r":{"$ref":"#/definitions/DeepPartial<{path:RelationTypes.Path;search:Search<any>;relations:RelationTypes.Relation;many?:boolean;options?:DbFindOpts;select?:RelationTypes.FieldPath[];}>"},"ref":{"type":"string"},"var":{"$ref":"#/definitions/DeepPartial<{[k:string]:any;}>"}}},"DeepPartial<{path:RelationTypes.Path;search:Search<any>;relations:RelationTypes.Relation;many?:boolean;options?:DbFindOpts;select?:RelationTypes.FieldPath[];}>":{"type":"object","properties":{"path":{"type":"array","items":[{"type":"string"},{"type":"string"}],"minItems":0,"maxItems":2},"search":{"$ref":"#/definitions/DeepPartial<Search<any>>"},"relations":{"$ref":"#/definitions/DeepPartial<RelationTypes.Relation>"},"many":{"type":"boolean"},"options":{"$ref":"#/definitions/DeepPartial<DbFindOpts>"},"select":{"type":"array","items":{"type":"array","items":{"type":"string"}}}}},"DeepPartial<RelationTypes.Relation>":{"type":"object"}},"$schema":"http://json-schema.org/draft-07/schema#"}