@wxn0brp/vql 0.6.0-alpha.0 → 0.6.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 };
@@ -12,21 +12,21 @@ export async function checkRelationPermission(config, validFn, user, query) {
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
  }
@@ -1,7 +1,7 @@
1
1
  import { PermCRUD, ValidFn } from "../types/perm.js";
2
2
  import { VQLRequest } from "../types/vql.js";
3
3
  import { VQLConfig } from "../config.js";
4
- export declare function extractPaths(config: VQLConfig, query: VQLRequest): {
4
+ export declare function extractPaths(config: VQLConfig, query: VQLRequest): Promise<{
5
5
  db: string;
6
6
  c: string;
7
7
  paths: {
@@ -10,7 +10,7 @@ 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;
@@ -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
  };
@@ -82,7 +82,7 @@ export function processFieldPath(pathObj) {
82
82
  export async function checkRequestPermission(config, validFn, 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
@@ -93,7 +93,7 @@ export async function checkRequestPermission(config, validFn, user, query) {
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,12 +1,11 @@
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";
3
+ import { VQLError, VqlQueryRaw } from "./types/vql.js";
4
4
  import { ValidFn } from "./types/perm.js";
5
5
  export declare class VQLProcessor {
6
6
  dbInstances: Record<string, ValtheraCompatible>;
7
7
  validFn: ValidFn;
8
8
  relation: Relation;
9
- preDefinedSheets: Map<string, VQL>;
10
9
  config: VQLConfig;
11
10
  constructor(dbInstances: Record<string, ValtheraCompatible>, config?: VQLConfig | Partial<VQLConfigInterface>, validFn?: ValidFn);
12
11
  execute<T = any>(queryRaw: VqlQueryRaw<T>, user?: any): Promise<T | VQLError>;
package/dist/processor.js CHANGED
@@ -3,14 +3,13 @@ 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
11
  validFn;
12
12
  relation;
13
- preDefinedSheets = new Map();
14
13
  config;
15
14
  constructor(dbInstances, config = new VQLConfig(), validFn = async () => ({ granted: true, via: "" })) {
16
15
  this.dbInstances = dbInstances;
@@ -18,7 +17,7 @@ export class VQLProcessor {
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;
@@ -48,7 +47,7 @@ export class VQLProcessor {
48
47
  logger.warn("Raw validation failed:", validateRawResult);
49
48
  return validateRawResult;
50
49
  }
51
- const query = executeSheetAndReplaceVars(queryRaw, this.preDefinedSheets, user);
50
+ const query = replaceVars(queryRaw, user);
52
51
  logger.debug("Executed sheet (expanded query):", query);
53
52
  const validateVqlResult = validateVql(query);
54
53
  if (validateVqlResult !== true) {
@@ -0,0 +1,2 @@
1
+ import { VQL, VQLR } from "./types/vql.js";
2
+ export declare function replaceVars(query: VQLR, user: any): VQL;
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
+ }
@@ -8,9 +8,9 @@ export interface VQLQuery<T = any> {
8
8
  f: VQLFindOne<T>;
9
9
  add: VQLAdd<T>;
10
10
  update: VQLUpdate<T>;
11
- updateOne: VQLUpdateOne<T>;
11
+ updateOne: VQLUpdate<T>;
12
12
  remove: VQLRemove<T>;
13
- removeOne: VQLRemoveOne<T>;
13
+ removeOne: VQLRemove<T>;
14
14
  updateOneOrAdd: VQLUpdateOneOrAdd<T>;
15
15
  removeCollection: VQLCollectionOperation;
16
16
  ensureCollection: VQLCollectionOperation;
@@ -28,11 +28,11 @@ export type VQLQueryData<T = any> = {
28
28
  } | {
29
29
  update: VQLUpdate<T>;
30
30
  } | {
31
- updateOne: VQLUpdateOne<T>;
31
+ updateOne: VQLUpdate<T>;
32
32
  } | {
33
33
  remove: VQLRemove<T>;
34
34
  } | {
35
- removeOne: VQLRemoveOne<T>;
35
+ removeOne: VQLRemove<T>;
36
36
  } | {
37
37
  updateOneOrAdd: VQLUpdateOneOrAdd<T>;
38
38
  } | {
@@ -76,19 +76,10 @@ export interface VQLUpdate<T = any> {
76
76
  search: Search<T>;
77
77
  updater: UpdaterArg<T>;
78
78
  }
79
- export interface VQLUpdateOne<T = any> {
80
- collection: string;
81
- search: Search<T>;
82
- updater: UpdaterArg<T>;
83
- }
84
79
  export interface VQLRemove<T = any> {
85
80
  collection: string;
86
81
  search: Search<T>;
87
82
  }
88
- export interface VQLRemoveOne<T = any> {
89
- collection: string;
90
- search: Search<T>;
91
- }
92
83
  export interface VQLUpdateOneOrAdd<T = any> {
93
84
  collection: string;
94
85
  search: Search<T>;
package/dist/valid.js CHANGED
@@ -31,17 +31,17 @@ function validD(query) {
31
31
  const value = d[key];
32
32
  if (typeof value.collection !== "string" || value.collection.trim() === "")
33
33
  return emptyErr();
34
- if (key === "issetCollection" || key === "ensureCollection")
34
+ if (key === "issetCollection" || key === "ensureCollection" || key === "removeCollection")
35
35
  return true;
36
36
  if (key === "add") {
37
- if (!isObj(value.data, false))
37
+ if (!isObj(value.data))
38
38
  return emptyErr();
39
39
  else
40
40
  return true;
41
41
  }
42
- if ("search" in value && !isObj(value.search))
42
+ if ("search" in value && !isObj(value.search, false))
43
43
  return emptyErr();
44
- if (key === "find" || key === "findOne" || key === "remove" || key === "removeOne")
44
+ if (key === "find" || key === "findOne" || key === "f" || key === "remove" || key === "removeOne")
45
45
  return true;
46
46
  if (key === "update" || key === "updateOne" || key === "updateOneOrAdd") {
47
47
  if (!isObj(value.updater, false))
package/dist/vql.d.ts CHANGED
@@ -195,9 +195,9 @@ export interface VQLQuery<T = any> {
195
195
  f: VQLFindOne<T>;
196
196
  add: VQLAdd<T>;
197
197
  update: VQLUpdate<T>;
198
- updateOne: VQLUpdateOne<T>;
198
+ updateOne: VQLUpdate<T>;
199
199
  remove: VQLRemove<T>;
200
- removeOne: VQLRemoveOne<T>;
200
+ removeOne: VQLRemove<T>;
201
201
  updateOneOrAdd: VQLUpdateOneOrAdd<T>;
202
202
  removeCollection: VQLCollectionOperation;
203
203
  ensureCollection: VQLCollectionOperation;
@@ -215,11 +215,11 @@ export type VQLQueryData<T = any> = {
215
215
  } | {
216
216
  update: VQLUpdate<T>;
217
217
  } | {
218
- updateOne: VQLUpdateOne<T>;
218
+ updateOne: VQLUpdate<T>;
219
219
  } | {
220
220
  remove: VQLRemove<T>;
221
221
  } | {
222
- removeOne: VQLRemoveOne<T>;
222
+ removeOne: VQLRemove<T>;
223
223
  } | {
224
224
  updateOneOrAdd: VQLUpdateOneOrAdd<T>;
225
225
  } | {
@@ -263,19 +263,10 @@ export interface VQLUpdate<T = any> {
263
263
  search: Search<T>;
264
264
  updater: UpdaterArg<T>;
265
265
  }
266
- export interface VQLUpdateOne<T = any> {
267
- collection: string;
268
- search: Search<T>;
269
- updater: UpdaterArg<T>;
270
- }
271
266
  export interface VQLRemove<T = any> {
272
267
  collection: string;
273
268
  search: Search<T>;
274
269
  }
275
- export interface VQLRemoveOne<T = any> {
276
- collection: string;
277
- search: Search<T>;
278
- }
279
270
  export interface VQLUpdateOneOrAdd<T = any> {
280
271
  collection: string;
281
272
  search: Search<T>;
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-alpha.1",
4
4
  "main": "dist/index.js",
5
5
  "author": "wxn0brP",
6
6
  "license": "MIT",
@@ -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
- }