@wxn0brp/vql 0.6.0-alpha.0 → 0.6.0-beta.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.
@@ -1,3 +1,3 @@
1
1
  import { VQLProcessor } from "../processor.js";
2
- import { RelationQuery } from "../types/vql.js";
3
- export declare function executeRelation(cpu: VQLProcessor, query: RelationQuery, user: any): Promise<any>;
2
+ import { VQL_Query_Relation } from "../types/vql.js";
3
+ export declare function executeRelation(cpu: VQLProcessor, query: VQL_Query_Relation, user: any): Promise<any>;
@@ -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.validFn, user, query)) {
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;
@@ -1,3 +1,3 @@
1
1
  import { VQLProcessor } from "../processor.js";
2
- import { VQLRequest } from "../types/vql.js";
3
- export declare function executeQuery(cpu: VQLProcessor, query: VQLRequest, user: any): Promise<any>;
2
+ import { VQL_Query_CRUD } from "../types/vql.js";
3
+ export declare function executeQuery(cpu: VQLProcessor, query: VQL_Query_CRUD, user: any): Promise<any>;
@@ -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.validFn, user, query)) {
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 { VQL } from "../../types/vql.js";
2
- export declare function parseVQLS(query: string): VQL;
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 { ValidFn } from "./types/perm.js";
3
- export declare function createGwValidFn(gw: GateWarden): ValidFn;
2
+ import { PermValidFn } from "./types/perm.js";
3
+ export declare function createGwValidFn(gw: GateWarden): PermValidFn;
package/dist/index.d.ts CHANGED
@@ -4,6 +4,6 @@ import { createValtheraAdapter } from "./apiAbstract.js";
4
4
  import logger from "./logger.js";
5
5
  import { LogLevel } from "@wxn0brp/lucerna-log";
6
6
  import { FF_VQL } from "./falconFrame.js";
7
+ import { createGwValidFn } from "./gw.js";
7
8
  export default VQLProcessor;
8
- export { createValtheraAdapter, VQLConfig, logger as VQLLogger, LogLevel as VQLLogLevel, FF_VQL };
9
- export * as VQLSheet from "./sheet/load.js";
9
+ export { VQLProcessor, createValtheraAdapter, VQLConfig, logger as VQLLogger, LogLevel as VQLLogLevel, FF_VQL, createGwValidFn };
package/dist/index.js CHANGED
@@ -4,6 +4,6 @@ import { createValtheraAdapter } from "./apiAbstract.js";
4
4
  import logger from "./logger.js";
5
5
  import { LogLevel } from "@wxn0brp/lucerna-log";
6
6
  import { FF_VQL } from "./falconFrame.js";
7
+ import { createGwValidFn } from "./gw.js";
7
8
  export default VQLProcessor;
8
- export { createValtheraAdapter, VQLConfig, logger as VQLLogger, LogLevel as VQLLogLevel, FF_VQL };
9
- export * as VQLSheet from "./sheet/load.js";
9
+ export { VQLProcessor, createValtheraAdapter, VQLConfig, logger as VQLLogger, LogLevel as VQLLogLevel, FF_VQL, createGwValidFn };
@@ -1,4 +1,4 @@
1
- import { ValidFn } from "../types/perm.js";
2
- import { RelationQuery } from "../types/vql.js";
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, validFn: ValidFn, user: any, query: RelationQuery): Promise<boolean>;
4
+ export declare function checkRelationPermission(config: VQLConfig, permValidFn: PermValidFn, user: any, query: VQL_Query_Relation): Promise<boolean>;
@@ -1,32 +1,32 @@
1
1
  import { PermCRUD } from "../types/perm.js";
2
2
  import { extractPathsFromData, hashKey } from "./utils.js";
3
- export async function checkRelationPermission(config, validFn, user, query) {
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 validFn(entityId, PermCRUD.READ, user);
9
+ const result = await permValidFn(entityId, PermCRUD.READ, user);
10
10
  if (result.granted) {
11
11
  return true;
12
12
  }
13
13
  // If the result is "entity-404", check the next fallback level
14
14
  if (!config.strictACL && result.via === "entity-404" && fallbackLevels.length > 0) {
15
- const nextFallbackEntityId = hashKey(config, fallbackLevels.slice(0, -1));
15
+ const nextFallbackEntityId = await hashKey(config, fallbackLevels.slice(0, -1));
16
16
  return checkPermissionRecursively(nextFallbackEntityId, fallbackLevels.slice(0, -2));
17
17
  }
18
18
  // If no fallback levels are left or the result is not "entity-404", deny access
19
19
  return false;
20
20
  };
21
21
  // Check permission for the relation field in the parent collection
22
- if (!await checkPermissionRecursively(hashKey(config, path), path)) {
22
+ if (!await checkPermissionRecursively(await hashKey(config, path), path)) {
23
23
  return false;
24
24
  }
25
25
  // Check permissions for search fields
26
26
  const searchPaths = extractPathsFromData(search || {});
27
27
  for (const searchPath of searchPaths) {
28
28
  const key = [...path, ...searchPath.path, searchPath.key];
29
- if (!await checkPermissionRecursively(hashKey(config, key), key)) {
29
+ if (!await checkPermissionRecursively(await hashKey(config, key), key)) {
30
30
  return false;
31
31
  }
32
32
  }
@@ -34,7 +34,7 @@ export async function checkRelationPermission(config, validFn, user, query) {
34
34
  if (select) {
35
35
  for (const fieldPath of select) {
36
36
  const key = [...path, fieldPath];
37
- if (!await checkPermissionRecursively(hashKey(config, key), key)) {
37
+ if (!await checkPermissionRecursively(await hashKey(config, key), key)) {
38
38
  return false;
39
39
  }
40
40
  }
@@ -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, validFn, user, { r })) {
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
- export declare function extractPaths(config: VQLConfig, query: VQLRequest): {
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: {
@@ -10,9 +10,9 @@ export declare function extractPaths(config: VQLConfig, query: VQLRequest): {
10
10
  c?: PermCRUD;
11
11
  path?: string[];
12
12
  }[];
13
- };
13
+ }>;
14
14
  export declare function processFieldPath(pathObj: {
15
15
  path: string[];
16
16
  key: string;
17
17
  }): string[];
18
- export declare function checkRequestPermission(config: VQLConfig, validFn: ValidFn, user: any, query: VQLRequest): Promise<boolean>;
18
+ export declare function checkRequestPermission(config: VQLConfig, permValidFn: PermValidFn, user: any, query: VQL_Query_CRUD): Promise<boolean>;
@@ -1,10 +1,10 @@
1
1
  import { PermCRUD } from "../types/perm.js";
2
2
  import { extractPathsFromData, hashKey } from "./utils.js";
3
- export function extractPaths(config, query) {
3
+ export async function extractPaths(config, query) {
4
4
  const operation = Object.keys(query.d)[0];
5
5
  const collection = query.d[operation].collection;
6
6
  const permPaths = {
7
- db: hashKey(config, query.db),
7
+ db: await hashKey(config, query.db),
8
8
  c: collection,
9
9
  paths: []
10
10
  };
@@ -79,21 +79,21 @@ export function processFieldPath(pathObj) {
79
79
  }
80
80
  return processedPath;
81
81
  }
82
- export async function checkRequestPermission(config, validFn, user, query) {
82
+ export async function checkRequestPermission(config, permValidFn, user, query) {
83
83
  if (!query)
84
84
  return false;
85
- const permPaths = extractPaths(config, query);
85
+ const permPaths = await 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
89
  // const result = await gw.hasAccess(user.id, entityId, requiredPerm);
90
- const result = await validFn(entityId, requiredPerm, user);
90
+ const result = await permValidFn(entityId, requiredPerm, user);
91
91
  if (result.granted) {
92
92
  return true;
93
93
  }
94
94
  // If the result is "entity-404", check the next fallback level
95
95
  if (!config.strictACL && result.via === "entity-404" && fallbackLevels.length > 0) {
96
- const nextFallbackEntityId = hashKey(config, fallbackLevels.slice(0, -1));
96
+ const nextFallbackEntityId = await hashKey(config, fallbackLevels.slice(0, -1));
97
97
  return checkPermissionRecursively(nextFallbackEntityId, requiredPerm, fallbackLevels.slice(0, -2));
98
98
  }
99
99
  // If no fallback levels are left or the result is not "entity-404", deny access
@@ -107,7 +107,7 @@ export async function checkRequestPermission(config, validFn, user, query) {
107
107
  let fallbackLevels = [];
108
108
  if ("c" in path) {
109
109
  // Collection-level permission: hash the combination of db and collection
110
- entityId = hashKey(config, [query.db, permPaths.c]);
110
+ entityId = await hashKey(config, [query.db, permPaths.c]);
111
111
  requiredPerm = path.c;
112
112
  // Fallback to database level if needed
113
113
  fallbackLevels = [query.db];
@@ -1,5 +1,6 @@
1
1
  import { VQLConfig } from "../config.js";
2
- export declare function hashKey(config: VQLConfig, path: any): string;
2
+ export declare function getHash(json: string): Promise<string>;
3
+ export declare function hashKey(config: VQLConfig, path: any): Promise<string>;
3
4
  export declare function extractPathsFromData(data: any, stack?: string[]): {
4
5
  path: string[];
5
6
  key: string;
@@ -1,8 +1,20 @@
1
- import crypto from "crypto";
2
- export function hashKey(config, path) {
1
+ let cryptoRef = null;
2
+ export async function getHash(json) {
3
+ if (typeof window !== "undefined" && window.crypto?.subtle) {
4
+ const buffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(json));
5
+ return [...new Uint8Array(buffer)].map(b => b.toString(16).padStart(2, "0")).join("");
6
+ }
7
+ else {
8
+ if (!cryptoRef) {
9
+ cryptoRef = await import("crypto");
10
+ }
11
+ return cryptoRef.createHash("sha256").update(json).digest("hex");
12
+ }
13
+ }
14
+ export async function hashKey(config, path) {
3
15
  const json = JSON.stringify(path);
4
16
  if (config.hidePath)
5
- return crypto.createHash("sha256").update(json).digest("hex");
17
+ return await getHash(json);
6
18
  else
7
19
  return json;
8
20
  }
@@ -1,13 +1,12 @@
1
1
  import { Relation, ValtheraCompatible } from "@wxn0brp/db-core";
2
2
  import { VQLConfig, VQLConfigInterface } from "./config.js";
3
- import { VQL, VQLError, VqlQueryRaw } from "./types/vql.js";
4
- import { ValidFn } from "./types/perm.js";
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
- validFn: ValidFn;
7
+ permValidFn: PermValidFn;
8
8
  relation: Relation;
9
- preDefinedSheets: Map<string, VQL>;
10
9
  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>;
10
+ constructor(dbInstances: Record<string, ValtheraCompatible>, config?: VQLConfig | Partial<VQLConfigInterface>, permValidFn?: PermValidFn);
11
+ execute<T = any>(queryRaw: VQLUQ<T>, user?: any): Promise<T | VQLError>;
13
12
  }
package/dist/processor.js CHANGED
@@ -3,27 +3,25 @@ import { VQLConfig } from "./config.js";
3
3
  import { executeRelation } from "./cpu/relation.js";
4
4
  import { executeQuery } from "./cpu/request.js";
5
5
  import logger from "./logger.js";
6
- import { executeSheetAndReplaceVars } from "./sheet/index.js";
6
+ import { replaceVars } from "./sheet.js";
7
7
  import { validateRaw, validateVql } from "./valid.js";
8
8
  import { parseVQLS } from "./cpu/string/index.js";
9
9
  export class VQLProcessor {
10
10
  dbInstances;
11
- validFn;
11
+ permValidFn;
12
12
  relation;
13
- preDefinedSheets = new Map();
14
13
  config;
15
- constructor(dbInstances, config = new VQLConfig(), validFn = async () => ({ granted: true, via: "" })) {
14
+ constructor(dbInstances, config = new VQLConfig(), permValidFn = async () => ({ granted: true, via: "" })) {
16
15
  this.dbInstances = dbInstances;
17
- this.validFn = validFn;
16
+ this.permValidFn = permValidFn;
18
17
  this.relation = new Relation(dbInstances);
19
18
  this.config = config instanceof VQLConfig ? config : new VQLConfig(config);
20
19
  }
21
- async execute(queryRaw, user = {}) {
20
+ async execute(queryRaw, user = { _id: "null-null-null" }) {
22
21
  if (typeof queryRaw === "string" || "query" in queryRaw) {
23
22
  logger.info("Incoming string query");
24
23
  const q = typeof queryRaw === "string" ? queryRaw : queryRaw.query;
25
24
  const vars = typeof queryRaw === "string" ? null : queryRaw.var;
26
- const ref = typeof queryRaw === "string" ? null : queryRaw.ref;
27
25
  logger.debug(q);
28
26
  try {
29
27
  queryRaw = parseVQLS(q);
@@ -31,12 +29,10 @@ export class VQLProcessor {
31
29
  }
32
30
  catch (e) {
33
31
  logger.error("Error parsing string query: ", { error: e, msg: e.message });
34
- return { err: true, msg: "Invalid query", c: 400, why: `String query parsing error: ${e.message}` };
32
+ return { err: true, c: 400, msg: `String query parsing error: ${e.message}` };
35
33
  }
36
34
  if (vars)
37
35
  queryRaw = { ...queryRaw, var: vars };
38
- if (ref)
39
- queryRaw = { ...queryRaw, ref };
40
36
  logger.debug("Final string query: ", queryRaw);
41
37
  }
42
38
  else {
@@ -48,7 +44,7 @@ export class VQLProcessor {
48
44
  logger.warn("Raw validation failed:", validateRawResult);
49
45
  return validateRawResult;
50
46
  }
51
- const query = executeSheetAndReplaceVars(queryRaw, this.preDefinedSheets, user);
47
+ const query = replaceVars(queryRaw, user);
52
48
  logger.debug("Executed sheet (expanded query):", query);
53
49
  const validateVqlResult = validateVql(query);
54
50
  if (validateVqlResult !== true) {
@@ -0,0 +1,2 @@
1
+ import { VQL_Query } from "./types/vql.js";
2
+ export declare function replaceVars(query: VQL_Query, user: any): VQL_Query;
package/dist/sheet.js ADDED
@@ -0,0 +1,34 @@
1
+ function replaceVariables(obj, variables) {
2
+ if (typeof obj === "object" && !Array.isArray(obj) && obj !== null && "__" in obj) {
3
+ const varKey = obj.__;
4
+ return variables[varKey] ?? obj;
5
+ }
6
+ if (typeof obj === "string") {
7
+ if (obj.startsWith("$"))
8
+ return variables[obj.slice(1)] ?? obj;
9
+ return obj;
10
+ }
11
+ if (Array.isArray(obj))
12
+ return obj.map((item) => replaceVariables(item, variables));
13
+ if (typeof obj === "object" && obj !== null) {
14
+ const newObj = {};
15
+ for (const key in obj) {
16
+ newObj[key] = replaceVariables(obj[key], variables);
17
+ }
18
+ return newObj;
19
+ }
20
+ return obj;
21
+ }
22
+ export function replaceVars(query, user) {
23
+ query.var = {
24
+ _me: user?.id || user?._id || user,
25
+ _now: Date.now(),
26
+ _nowShort: Math.floor(Date.now() / 1000),
27
+ __now: Date.now().toString(),
28
+ __nowShort: Math.floor(Date.now() / 1000).toString(),
29
+ ...(query.var || {})
30
+ };
31
+ query = replaceVariables(query, query.var);
32
+ delete query.var;
33
+ return query;
34
+ }
@@ -9,4 +9,4 @@ export interface ValidFnResult {
9
9
  granted: boolean;
10
10
  via?: string;
11
11
  }
12
- export type ValidFn = (path: string, perm: number, user: any) => Promise<ValidFnResult>;
12
+ export type PermValidFn = (path: string, perm: number, user: any) => Promise<ValidFnResult>;
@@ -2,106 +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 VQLQuery<T = any> {
6
- find: VQLFind<T>;
7
- findOne: VQLFindOne<T>;
8
- f: VQLFindOne<T>;
9
- add: VQLAdd<T>;
10
- update: VQLUpdate<T>;
11
- updateOne: VQLUpdateOne<T>;
12
- remove: VQLRemove<T>;
13
- removeOne: VQLRemoveOne<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: VQLUpdateOne<T>;
32
- } | {
33
- remove: VQLRemove<T>;
34
- } | {
35
- removeOne: VQLRemoveOne<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?: VQLFields;
56
- select?: VQLFields;
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 VQLFindOne<T = any> {
14
+ export interface VQL_OP_FindOne<T = any> {
62
15
  collection: string;
63
16
  search: Search<T>;
64
- fields?: VQLFields;
65
- select?: VQLFields;
66
- relations?: VQLRelations;
17
+ fields?: VQL_Fields;
18
+ select?: VQL_Fields;
67
19
  searchOpts?: FindOpts<T>;
68
20
  }
69
- export interface VQLAdd<T = any> {
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 VQLUpdate<T = any> {
75
- collection: string;
76
- search: Search<T>;
77
- updater: UpdaterArg<T>;
78
- }
79
- export interface VQLUpdateOne<T = any> {
26
+ export interface VQL_OP_Update<T = any> {
80
27
  collection: string;
81
28
  search: Search<T>;
82
29
  updater: UpdaterArg<T>;
83
30
  }
84
- export interface VQLRemove<T = any> {
31
+ export interface VQL_OP_Remove<T = any> {
85
32
  collection: string;
86
33
  search: Search<T>;
87
34
  }
88
- export interface VQLRemoveOne<T = any> {
89
- collection: string;
90
- search: Search<T>;
91
- }
92
- export interface VQLUpdateOneOrAdd<T = any> {
35
+ export interface VQL_OP_UpdateOneOrAdd<T = any> {
93
36
  collection: string;
94
37
  search: Search<T>;
95
38
  updater: UpdaterArg<T>;
96
39
  add_arg?: Arg<T>;
97
40
  id_gen?: boolean;
98
41
  }
99
- export interface VQLCollectionOperation {
42
+ export interface VQL_OP_CollectionOperation {
100
43
  collection: string;
101
44
  }
102
- export type VQLFields = Record<string, boolean | number> | string[];
103
- export type VQLRelations = Record<string, VQLFind | VQLFindOne>;
104
- export interface RelationQuery {
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 {
105
78
  r: {
106
79
  path: RelationTypes.Path;
107
80
  search: Search;
@@ -111,25 +84,19 @@ export interface RelationQuery {
111
84
  select?: RelationTypes.FieldPath[];
112
85
  };
113
86
  }
114
- export interface VQLRef {
115
- ref?: string;
87
+ export interface VQL_Var {
116
88
  var?: {
117
89
  [k: string]: any;
118
90
  };
119
91
  }
120
- type VQLRefRequired = VQLRef & Required<Pick<VQLRef, "ref">>;
121
- type DeepPartial<T> = {
122
- [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
123
- };
124
- export type VQL<T = any> = (VQLRequest<T> | RelationQuery) & VQLRef;
125
- export type VQLR<T = any> = VQL<T> | (DeepPartial<VQL<T>> & VQLRefRequired) | VQLRefRequired;
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;
126
98
  export interface VQLError {
127
99
  err: true;
128
100
  msg: string;
129
101
  c: number;
130
- why?: string;
131
102
  }
132
- export type VqlQueryRaw<T = any> = VQLR<T> | string | {
133
- query: string;
134
- } & VQLRef;
135
- export {};
package/dist/valid.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { VQL, VQLError, VQLR } from "./types/vql.js";
2
- export declare function validateRaw(query: VQLR): true | VQLError;
3
- export declare function validateVql(query: VQL): true | VQLError;
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: "Bad query",
4
+ msg,
5
5
  c: 400
6
6
  };
7
7
  }
@@ -10,51 +10,136 @@ 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
- if (key === "getCollection")
64
+ const value = d[key]; // Cast for common property
65
+ if (key === "getCollections") {
66
+ if (Object.keys(value).length > 0)
67
+ return emptyErr("'getCollections' should be an empty object.");
30
68
  return true;
31
- const value = d[key];
69
+ }
32
70
  if (typeof value.collection !== "string" || value.collection.trim() === "")
33
- return emptyErr();
34
- if (key === "issetCollection" || key === "ensureCollection")
71
+ return emptyErr(`CRUD operation '${key}' must specify a non-empty 'collection' string.`);
72
+ if (key === "issetCollection" || key === "ensureCollection" || key === "removeCollection") {
35
73
  return true;
74
+ }
36
75
  if (key === "add") {
37
- if (!isObj(value.data, false))
38
- return emptyErr();
39
- else
40
- return true;
76
+ const op = d.add;
77
+ if (!isObj(op.data))
78
+ return emptyErr("'add' operation requires a 'data' object.");
79
+ if ("id_gen" in op && typeof op.id_gen !== "boolean")
80
+ return emptyErr("'add' operation 'id_gen' property must be a boolean.");
81
+ return true;
41
82
  }
42
- if ("search" in value && !isObj(value.search))
43
- return emptyErr();
44
- if (key === "find" || key === "findOne" || key === "remove" || key === "removeOne")
83
+ if (key === "find") {
84
+ const op = d.find;
85
+ if ("search" in op && !isObj(op.search, false))
86
+ return emptyErr("'find' operation 'search' property must be an object.");
87
+ if ("limit" in op && typeof op.limit !== "number")
88
+ return emptyErr("'find' operation 'limit' property must be a number.");
89
+ if ("fields" in op && !isObj(op.fields, false) && !Array.isArray(op.fields))
90
+ return emptyErr("'find' operation 'fields' property must be an object or an array.");
91
+ if ("select" in op && !isObj(op.select, false) && !Array.isArray(op.select))
92
+ return emptyErr("'find' operation 'select' property must be an object or an array.");
93
+ if ("options" in op && !isObj(op.options, false))
94
+ return emptyErr("'find' operation 'options' property must be an object.");
95
+ if ("searchOpts" in op && !isObj(op.searchOpts, false))
96
+ return emptyErr("'find' operation 'searchOpts' property must be an object.");
45
97
  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
98
  }
52
- return true;
99
+ if (key === "findOne" || key === "f") {
100
+ const op = d.findOne || d.f;
101
+ if (!isObj(op.search, false))
102
+ return emptyErr(`'${key}' operation requires a 'search' object.`);
103
+ if ("fields" in op && !isObj(op.fields, false) && !Array.isArray(op.fields))
104
+ return emptyErr(`'${key}' operation 'fields' property must be an object or an array.`);
105
+ if ("select" in op && !isObj(op.select, false) && !Array.isArray(op.select))
106
+ return emptyErr(`'${key}' operation 'select' property must be an object or an array.`);
107
+ if ("searchOpts" in op && !isObj(op.searchOpts, false))
108
+ return emptyErr(`'${key}' operation 'searchOpts' property must be an object.`);
109
+ return true;
110
+ }
111
+ if (key === "remove" || key === "removeOne") {
112
+ const op = d.remove || d.removeOne;
113
+ if (!isObj(op.search, false))
114
+ return emptyErr(`'${key}' operation requires a 'search' object.`);
115
+ return true;
116
+ }
117
+ if (key === "update" || key === "updateOne") {
118
+ const op = d.update || d.updateOne;
119
+ if (!isObj(op.search, false))
120
+ return emptyErr(`'${key}' operation requires a 'search' object.`);
121
+ if (!isObj(op.updater, false))
122
+ return emptyErr(`'${key}' operation requires an 'updater' object.`);
123
+ return true;
124
+ }
125
+ if (key === "updateOneOrAdd") {
126
+ const op = d.updateOneOrAdd;
127
+ if (!isObj(op.search, false))
128
+ return emptyErr("'updateOneOrAdd' operation requires a 'search' object.");
129
+ if (!isObj(op.updater, false))
130
+ return emptyErr("'updateOneOrAdd' operation requires an 'updater' object.");
131
+ if ("add_arg" in op && !isObj(op.add_arg, false))
132
+ return emptyErr("'updateOneOrAdd' operation 'add_arg' property must be an object.");
133
+ if ("id_gen" in op && typeof op.id_gen !== "boolean")
134
+ return emptyErr("'updateOneOrAdd' operation 'id_gen' property must be a boolean.");
135
+ return true;
136
+ }
137
+ return emptyErr(`Unknown or invalid CRUD operation: '${key}'`);
53
138
  }
54
139
  export function validateVql(query) {
55
140
  if ("r" in query && isObj(query.r))
56
141
  return validR(query);
57
142
  if ("d" in query && isObj(query.d))
58
143
  return validD(query);
59
- return emptyErr();
144
+ return emptyErr("Query must contain a valid 'r' (relation) or 'd' (database) property.");
60
145
  }
package/dist/vql.d.ts CHANGED
@@ -189,106 +189,79 @@ declare namespace RelationTypes {
189
189
  };
190
190
  }
191
191
  }
192
- export interface VQLQuery<T = any> {
193
- find: VQLFind<T>;
194
- findOne: VQLFindOne<T>;
195
- f: VQLFindOne<T>;
196
- add: VQLAdd<T>;
197
- update: VQLUpdate<T>;
198
- updateOne: VQLUpdateOne<T>;
199
- remove: VQLRemove<T>;
200
- removeOne: VQLRemoveOne<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: VQLUpdateOne<T>;
219
- } | {
220
- remove: VQLRemove<T>;
221
- } | {
222
- removeOne: VQLRemoveOne<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?: VQLFields;
243
- select?: VQLFields;
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 VQLFindOne<T = any> {
201
+ export interface VQL_OP_FindOne<T = any> {
249
202
  collection: string;
250
203
  search: Search<T>;
251
- fields?: VQLFields;
252
- select?: VQLFields;
253
- relations?: VQLRelations;
204
+ fields?: VQL_Fields;
205
+ select?: VQL_Fields;
254
206
  searchOpts?: FindOpts<T>;
255
207
  }
256
- export interface VQLAdd<T = any> {
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 VQLUpdate<T = any> {
262
- collection: string;
263
- search: Search<T>;
264
- updater: UpdaterArg<T>;
265
- }
266
- export interface VQLUpdateOne<T = any> {
213
+ export interface VQL_OP_Update<T = any> {
267
214
  collection: string;
268
215
  search: Search<T>;
269
216
  updater: UpdaterArg<T>;
270
217
  }
271
- export interface VQLRemove<T = any> {
218
+ export interface VQL_OP_Remove<T = any> {
272
219
  collection: string;
273
220
  search: Search<T>;
274
221
  }
275
- export interface VQLRemoveOne<T = any> {
276
- collection: string;
277
- search: Search<T>;
278
- }
279
- export interface VQLUpdateOneOrAdd<T = any> {
222
+ export interface VQL_OP_UpdateOneOrAdd<T = any> {
280
223
  collection: string;
281
224
  search: Search<T>;
282
225
  updater: UpdaterArg<T>;
283
226
  add_arg?: Arg<T>;
284
227
  id_gen?: boolean;
285
228
  }
286
- export interface VQLCollectionOperation {
229
+ export interface VQL_OP_CollectionOperation {
287
230
  collection: string;
288
231
  }
289
- export type VQLFields = Record<string, boolean | number> | string[];
290
- export type VQLRelations = Record<string, VQLFind | VQLFindOne>;
291
- export interface RelationQuery {
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 {
292
265
  r: {
293
266
  path: RelationTypes.Path;
294
267
  search: Search;
@@ -298,26 +271,21 @@ export interface RelationQuery {
298
271
  select?: RelationTypes.FieldPath[];
299
272
  };
300
273
  }
301
- export interface VQLRef {
302
- ref?: string;
274
+ export interface VQL_Var {
303
275
  var?: {
304
276
  [k: string]: any;
305
277
  };
306
278
  }
307
- export type VQLRefRequired = VQLRef & Required<Pick<VQLRef, "ref">>;
308
- export type DeepPartial<T> = {
309
- [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
310
- };
311
- export type VQL<T = any> = (VQLRequest<T> | RelationQuery) & VQLRef;
312
- export type VQLR<T = any> = VQL<T> | (DeepPartial<VQL<T>> & VQLRefRequired) | VQLRefRequired;
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;
313
285
  export interface VQLError {
314
286
  err: true;
315
287
  msg: string;
316
288
  c: number;
317
- why?: string;
318
289
  }
319
- export type VqlQueryRaw<T = any> = VQLR<T> | string | {
320
- query: string;
321
- } & VQLRef;
322
290
 
323
291
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/vql",
3
- "version": "0.6.0-alpha.0",
3
+ "version": "0.6.0-beta.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"
@@ -1,2 +0,0 @@
1
- import { VQL, VQLR } from "../types/vql.js";
2
- export declare function executeSheetAndReplaceVars(query: VQLR, preDefinedSheets: Map<string, VQL>, user: any): VQL;
@@ -1,70 +0,0 @@
1
- import { deepMerge } from "../merge.js";
2
- function trimPath(path) {
3
- return path && path.length > 2 ? path.slice(0, 2) : path;
4
- }
5
- function relationFix(relations) {
6
- for (const key in relations) {
7
- const value = relations[key];
8
- value.path = trimPath(value.path);
9
- if (value.relations) {
10
- relationFix(value.relations);
11
- }
12
- }
13
- }
14
- function pathFix(vql) {
15
- if ("d" in vql) {
16
- const key = Object.keys(vql.d)[0];
17
- const value = vql.d[key];
18
- value.path = trimPath(value.path);
19
- }
20
- else if ("r" in vql) {
21
- const value = vql.r;
22
- value.path = trimPath(value.path);
23
- if (value.relations) {
24
- relationFix(value.relations);
25
- }
26
- }
27
- }
28
- function replaceVariables(obj, variables) {
29
- if (typeof obj === "object" && !Array.isArray(obj) && obj !== null && "__" in obj) {
30
- const varKey = obj.__;
31
- return variables[varKey] ?? obj;
32
- }
33
- if (typeof obj === "string") {
34
- if (obj.startsWith("$"))
35
- return variables[obj.slice(1)] ?? obj;
36
- return obj;
37
- }
38
- if (Array.isArray(obj))
39
- return obj.map((item) => replaceVariables(item, variables));
40
- if (typeof obj === "object" && obj !== null) {
41
- const newObj = {};
42
- for (const key in obj) {
43
- newObj[key] = replaceVariables(obj[key], variables);
44
- }
45
- return newObj;
46
- }
47
- return obj;
48
- }
49
- export function executeSheetAndReplaceVars(query, preDefinedSheets, user) {
50
- if ("ref" in query) {
51
- if (preDefinedSheets.has(query.ref)) {
52
- const ref = preDefinedSheets.get(query.ref);
53
- const merge = deepMerge(query, ref);
54
- pathFix(merge);
55
- query = merge;
56
- }
57
- delete query.ref;
58
- }
59
- query.var = {
60
- _me: user?.id || user?._id || user,
61
- _now: Date.now(),
62
- _nowShort: Math.floor(Date.now() / 1000),
63
- __now: Date.now().toString(),
64
- __nowShort: Math.floor(Date.now() / 1000).toString(),
65
- ...(query.var || {})
66
- };
67
- query = replaceVariables(query, query.var);
68
- delete query.var;
69
- return query;
70
- }
@@ -1,3 +0,0 @@
1
- import { VQL } from "../types/vql.js";
2
- export declare function loadSheetsFromDir(dir: string): Map<string, VQL>;
3
- export declare function loadSheetFromFile(file: string): Map<string, VQL>;
@@ -1,22 +0,0 @@
1
- import { existsSync, readdirSync, readFileSync } from "fs";
2
- function loadSheet(map, file) {
3
- if (!existsSync(file)) {
4
- throw new Error(`Sheet ${file} not found`);
5
- }
6
- const sheet = JSON.parse(readFileSync(file, "utf-8"));
7
- for (const key in sheet) {
8
- map.set(key, sheet[key]);
9
- }
10
- }
11
- export function loadSheetsFromDir(dir) {
12
- const map = new Map();
13
- for (const file of readdirSync(dir)) {
14
- loadSheet(map, `${dir}/${file}`);
15
- }
16
- return map;
17
- }
18
- export function loadSheetFromFile(file) {
19
- const map = new Map();
20
- loadSheet(map, file);
21
- return map;
22
- }