@wxn0brp/vql 0.8.3 → 0.8.4

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.
@@ -51,6 +51,10 @@ export async function executeQuery(cpu, query, user) {
51
51
  opts.id_gen = params.id_gen;
52
52
  return db.updateOneOrAdd(params.collection, params.search, params.updater, opts);
53
53
  }
54
+ else if (operation === "toggleOne") {
55
+ const params = query.d[operation];
56
+ return db.toggleOne(params.collection, params.search, params.data);
57
+ }
54
58
  else if (operation === "removeCollection") {
55
59
  const params = query.d[operation];
56
60
  return db.removeCollection(params.collection);
@@ -0,0 +1 @@
1
+ export declare function parseArgs(input: string): Record<string, any>;
@@ -0,0 +1,102 @@
1
+ export function parseArgs(input) {
2
+ const result = {};
3
+ const tokens = [];
4
+ let current = "";
5
+ let inQuotes = false;
6
+ let escape = false;
7
+ let objTree = 0;
8
+ for (let i = 0; i < input.length; i++) {
9
+ const char = input[i];
10
+ if (escape) {
11
+ current += char;
12
+ escape = false;
13
+ }
14
+ else if (char === "\\") {
15
+ escape = true;
16
+ }
17
+ else if (!inQuotes && (char === "{" || char === "[")) {
18
+ objTree++;
19
+ current += char;
20
+ }
21
+ else if (!inQuotes && (char === "}" || char === "]")) {
22
+ objTree--;
23
+ current += char;
24
+ if (objTree === 0) {
25
+ tokens.push(current);
26
+ current = "";
27
+ }
28
+ }
29
+ else if (!objTree && (char === "'" || char === '"' || char === "`")) {
30
+ if (inQuotes === char) {
31
+ inQuotes = false;
32
+ tokens.push(`"` + current + `"`);
33
+ current = "";
34
+ }
35
+ else if (typeof inQuotes === "boolean") {
36
+ inQuotes = char;
37
+ }
38
+ else {
39
+ current += char;
40
+ }
41
+ }
42
+ else if (!inQuotes && (char === " " || char === "=" || char === "<" || char === ">")) {
43
+ if (current !== "") {
44
+ if (char === "<" || char === ">") {
45
+ let type = char === ">" ? "gt" : "lt";
46
+ if (i < input.length - 1 && input[i + 1] === "=") {
47
+ type += "e";
48
+ i++;
49
+ }
50
+ const split = current.split(".");
51
+ if (split.length > 1) {
52
+ const original = split.shift();
53
+ const operation = "$" + type;
54
+ split.unshift(operation);
55
+ split.unshift(original);
56
+ current = split.join(".");
57
+ }
58
+ else {
59
+ current = "$" + type + "." + current;
60
+ }
61
+ }
62
+ tokens.push(current);
63
+ current = "";
64
+ }
65
+ }
66
+ else {
67
+ current += char;
68
+ }
69
+ }
70
+ if (current !== "")
71
+ tokens.push(current);
72
+ for (let i = 0; i < tokens.length; i += 2) {
73
+ const key = tokens[i];
74
+ let value = tokens[i + 1] ?? true;
75
+ if (typeof value === "string") {
76
+ const trimmed = value.trim();
77
+ if (trimmed === "") {
78
+ value = true;
79
+ }
80
+ else if (/^".*"$/.test(trimmed)) {
81
+ value = trimmed.slice(1, -1);
82
+ }
83
+ else if (trimmed.toLowerCase() === "true") {
84
+ value = true;
85
+ }
86
+ else if (trimmed.toLowerCase() === "false") {
87
+ value = false;
88
+ }
89
+ else if (!isNaN(Number(trimmed))) {
90
+ value = Number(trimmed);
91
+ }
92
+ else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
93
+ try {
94
+ value = JSON.parse(trimmed);
95
+ }
96
+ catch { }
97
+ }
98
+ }
99
+ result[key] = value;
100
+ }
101
+ return result;
102
+ }
@@ -0,0 +1,2 @@
1
+ import { VQL_Query } from "../../types/vql.js";
2
+ export declare function buildVQL(db: string, op: string, collection: string, query: Record<string, any>): VQL_Query;
@@ -0,0 +1,44 @@
1
+ import { convertSearchObjToSearchArray } from "./utils.js";
2
+ export function buildVQL(db, op, collection, query) {
3
+ return "relations" in query ?
4
+ buildRelation(db, collection, query) :
5
+ buildQuery(db, op, collection, query);
6
+ }
7
+ function buildRelation(db, collection, query) {
8
+ const relations = {};
9
+ for (const key in query.relations) {
10
+ const value = query.relations[key];
11
+ relations[key] = {
12
+ path: [value.db || db, value.c || key],
13
+ ...value
14
+ };
15
+ delete relations[key].db;
16
+ delete relations[key].c;
17
+ }
18
+ if ("select" in query)
19
+ query.select = convertSearchObjToSearchArray(query.select);
20
+ return {
21
+ r: {
22
+ path: [db, collection],
23
+ ...query,
24
+ relations,
25
+ }
26
+ };
27
+ }
28
+ function buildQuery(db, op, collection, query) {
29
+ if (query.fields && !query.select) {
30
+ query.select = query.fields;
31
+ delete query.fields;
32
+ }
33
+ if ("select" in query)
34
+ query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
35
+ return {
36
+ db,
37
+ d: {
38
+ [op]: {
39
+ collection,
40
+ ...query,
41
+ }
42
+ }
43
+ };
44
+ }
@@ -1,4 +1,6 @@
1
- import { convertSearchObjToSearchArray, extractMeta } from "./utils.js";
1
+ import { parseArgs } from "./args.js";
2
+ import { buildVQL } from "./build.js";
3
+ import { extractMeta } from "./utils.js";
2
4
  const aliases = {
3
5
  s: "search",
4
6
  f: "fields",
@@ -8,152 +10,6 @@ const aliases = {
8
10
  e: "select",
9
11
  u: "updater",
10
12
  };
11
- function parseArgs(input) {
12
- const result = {};
13
- const tokens = [];
14
- let current = "";
15
- let inQuotes = false;
16
- let escape = false;
17
- let objTree = 0;
18
- for (let i = 0; i < input.length; i++) {
19
- const char = input[i];
20
- if (escape) {
21
- current += char;
22
- escape = false;
23
- }
24
- else if (char === "\\") {
25
- escape = true;
26
- }
27
- else if (!inQuotes && (char === "{" || char === "[")) {
28
- objTree++;
29
- current += char;
30
- }
31
- else if (!inQuotes && (char === "}" || char === "]")) {
32
- objTree--;
33
- current += char;
34
- if (objTree === 0) {
35
- tokens.push(current);
36
- current = "";
37
- }
38
- }
39
- else if (!objTree && (char === "'" || char === '"' || char === "`")) {
40
- if (inQuotes === char) {
41
- inQuotes = false;
42
- tokens.push(`"` + current + `"`);
43
- current = "";
44
- }
45
- else if (typeof inQuotes === "boolean") {
46
- inQuotes = char;
47
- }
48
- else {
49
- current += char;
50
- }
51
- }
52
- else if (!inQuotes && (char === " " || char === "=" || char === "<" || char === ">")) {
53
- if (current !== "") {
54
- if (char === "<" || char === ">") {
55
- let type = char === ">" ? "gt" : "lt";
56
- if (i < input.length - 1 && input[i + 1] === "=") {
57
- type += "e";
58
- i++;
59
- }
60
- const split = current.split(".");
61
- if (split.length > 1) {
62
- const original = split.shift();
63
- const operation = "$" + type;
64
- split.unshift(operation);
65
- split.unshift(original);
66
- current = split.join(".");
67
- }
68
- else {
69
- current = "$" + type + "." + current;
70
- }
71
- }
72
- tokens.push(current);
73
- current = "";
74
- }
75
- }
76
- else {
77
- current += char;
78
- }
79
- }
80
- if (current !== "") {
81
- tokens.push(current);
82
- }
83
- for (let i = 0; i < tokens.length; i += 2) {
84
- const key = tokens[i];
85
- let value = tokens[i + 1] ?? true;
86
- if (typeof value === "string") {
87
- const trimmed = value.trim();
88
- if (trimmed === "") {
89
- value = true;
90
- }
91
- else if (/^".*"$/.test(trimmed)) {
92
- value = trimmed.slice(1, -1);
93
- }
94
- else if (trimmed.toLowerCase() === "true") {
95
- value = true;
96
- }
97
- else if (trimmed.toLowerCase() === "false") {
98
- value = false;
99
- }
100
- else if (!isNaN(Number(trimmed))) {
101
- value = Number(trimmed);
102
- }
103
- else if ((trimmed.startsWith("{") && trimmed.endsWith("}")) || (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
104
- try {
105
- value = JSON.parse(trimmed);
106
- }
107
- catch { }
108
- }
109
- }
110
- result[key] = value;
111
- }
112
- return result;
113
- }
114
- function buildVQL(db, op, collection, query) {
115
- const hasRelations = "relations" in query;
116
- if (hasRelations) {
117
- const relations = {};
118
- for (const key in query.relations) {
119
- const value = query.relations[key];
120
- relations[key] = {
121
- path: [value.db || db, value.c || key],
122
- ...value
123
- };
124
- delete relations[key].db;
125
- delete relations[key].c;
126
- }
127
- if ("select" in query) {
128
- query.select = convertSearchObjToSearchArray(query.select);
129
- }
130
- return {
131
- r: {
132
- path: [db, collection],
133
- ...query,
134
- relations,
135
- }
136
- };
137
- }
138
- else {
139
- if (query.fields && !query.select) {
140
- query.select = query.fields;
141
- delete query.fields;
142
- }
143
- if ("select" in query) {
144
- query.select = [...new Set(convertSearchObjToSearchArray(query.select).map(k => k[0]).flat())];
145
- }
146
- return {
147
- db,
148
- d: {
149
- [op]: {
150
- collection,
151
- ...query,
152
- }
153
- }
154
- };
155
- }
156
- }
157
13
  export function parseVQLS(query) {
158
14
  const { db, op, collection, body } = extractMeta(query);
159
15
  const parsed = parseArgs(body);
@@ -167,7 +23,9 @@ export function parseVQLS(query) {
167
23
  const key = keys[i];
168
24
  if (i < keys.length - 1) {
169
25
  if (!(key in obj)) {
170
- obj[key] = {};
26
+ const nextKey = keys[i + 1];
27
+ const isArrayIndex = /^\d+$/.test(nextKey);
28
+ obj[key] = isArrayIndex ? [] : {};
171
29
  }
172
30
  obj = obj[key];
173
31
  }
@@ -2,13 +2,15 @@ const opPrefix = {
2
2
  "-": "remove",
3
3
  "+": "add",
4
4
  "~": "update",
5
+ "?": "updateOneOrAdd",
6
+ "^": "toggleOne"
5
7
  };
6
8
  const operations = [
7
9
  "add",
8
10
  "find", "findOne",
9
11
  "update", "updateOne",
10
12
  "remove", "removeOne",
11
- "updateOneOrAdd",
13
+ "updateOneOrAdd", "toggleOne",
12
14
  "ensureCollection", "issetCollection",
13
15
  ];
14
16
  /**
@@ -55,7 +57,7 @@ export function extractMeta(input) {
55
57
  let collection = "";
56
58
  let body = "";
57
59
  // Determine operation and collection based on special characters
58
- if (["!", "-", "+", "~"].some(c => split[0].includes(c)) || // Check if operation is indicated by special character
60
+ if (["!", "-", "+", "~", "?", "^"].some(c => split[0].includes(c)) || // Check if operation is indicated by special character
59
61
  [".", ":", "{"].some(c => split[1].includes(c)) // Check if query body is indicated by special character
60
62
  ) {
61
63
  let temp = split.shift();
@@ -93,7 +95,7 @@ export function convertSearchObjToSearchArray(obj, parentKeys = []) {
93
95
  if (!value) {
94
96
  return acc;
95
97
  }
96
- else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
98
+ else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
97
99
  return [...acc, ...convertSearchObjToSearchArray(value, currentPath)];
98
100
  }
99
101
  else {
@@ -131,6 +131,15 @@ function validD(query) {
131
131
  return emptyErr("'updateOneOrAdd' operation 'id_gen' property must be a boolean.");
132
132
  return true;
133
133
  }
134
+ if (key === "toggleOne") {
135
+ const op = d.toggleOne;
136
+ if (!isObj(op.search, false))
137
+ return emptyErr("'toggleOne' operation requires a 'search' object.");
138
+ if ("data" in op && !isObj(op.data, false))
139
+ return emptyErr("'toggleOne' operation 'data' property must be an object.");
140
+ return true;
141
+ }
142
+ const n = key;
134
143
  return emptyErr(`Unknown or invalid CRUD operation: '${key}'`);
135
144
  }
136
145
  export function validateVql(query) {
@@ -34,12 +34,19 @@ export async function extractPaths(config, query) {
34
34
  permPaths.paths.push({ filed: extractPathsFromData(qo.search), p: PermCRUD.READ });
35
35
  permPaths.paths.push({ filed: extractPathsFromData(qo.updater), p: PermCRUD.UPDATE });
36
36
  break;
37
+ case "toggleOne":
38
+ permPaths.paths.push({ c: PermCRUD.DELETE });
39
+ permPaths.paths.push({ c: PermCRUD.CREATE });
40
+ break;
37
41
  case "ensureCollection":
38
42
  case "getCollections":
39
43
  case "issetCollection":
40
44
  case "removeCollection":
41
45
  permPaths.paths.push({ c: PermCRUD.COLLECTION });
42
46
  break;
47
+ default:
48
+ const n = operation;
49
+ break;
43
50
  }
44
51
  permPaths.paths = (await Promise.all(permPaths.paths.map(async (path) => {
45
52
  if (!path.filed)
@@ -39,6 +39,11 @@ export interface VQL_OP_UpdateOneOrAdd<T = any> {
39
39
  add_arg?: Arg<T>;
40
40
  id_gen?: boolean;
41
41
  }
42
+ export interface VQL_OP_ToggleOne<T = any> {
43
+ collection: string;
44
+ search: Search<T>;
45
+ data?: Arg<T>;
46
+ }
42
47
  export interface VQL_OP_CollectionOperation {
43
48
  collection: string;
44
49
  }
@@ -61,6 +66,8 @@ export type VQL_Query_CRUD_Data<T = any> = {
61
66
  removeOne: VQL_OP_Remove<T>;
62
67
  } | {
63
68
  updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
69
+ } | {
70
+ toggleOne: VQL_OP_ToggleOne<T>;
64
71
  } | {
65
72
  removeCollection: VQL_OP_CollectionOperation;
66
73
  } | {
@@ -70,6 +77,7 @@ export type VQL_Query_CRUD_Data<T = any> = {
70
77
  } | {
71
78
  getCollections: {};
72
79
  };
80
+ export type VQL_Query_CRUD_Keys = VQL_Query_CRUD_Data extends infer U ? U extends Record<string, unknown> ? keyof U : never : never;
73
81
  export interface VQL_Query_CRUD<T = any> {
74
82
  db: string;
75
83
  d: VQL_Query_CRUD_Data<T>;
package/dist/vql.d.ts CHANGED
@@ -41,6 +41,10 @@ export type ComparisonOperators<T = any> = {
41
41
  ], number>;
42
42
  $in?: Partial<Record<keyof T, T[keyof T][]>>;
43
43
  $nin?: Partial<Record<keyof T, T[keyof T][]>>;
44
+ $idGt?: PartialOfType<T, string | number>;
45
+ $idLt?: PartialOfType<T, string | number>;
46
+ $idGte?: PartialOfType<T, string | number>;
47
+ $idLte?: PartialOfType<T, string | number>;
44
48
  };
45
49
  /** Type and Existence Operators */
46
50
  export type TypeAndExistenceOperators<T = any> = {
@@ -79,7 +83,8 @@ export type ArrayUpdater<T = any> = {
79
83
  };
80
84
  /** Objects */
81
85
  export type ObjectUpdater<T = any> = {
82
- $merge?: PartialOfType<T, any[]>;
86
+ $merge?: PartialOfType<T, any>;
87
+ $deepMerge?: PartialOfType<T, any>;
83
88
  };
84
89
  /** Values */
85
90
  export type ValueUpdater<T = any> = {
@@ -87,6 +92,8 @@ export type ValueUpdater<T = any> = {
87
92
  $dec?: PartialOfType<T, number>;
88
93
  $unset?: PartialOfType<T, any>;
89
94
  $rename?: PartialOfType<T, any>;
95
+ /** same as { name: value } */
96
+ $set?: PartialOfType<T, any>;
90
97
  };
91
98
  export type UpdaterArg<T = any> = ArrayUpdater<T> & ObjectUpdater<T> & ValueUpdater<T> & Arg<T>;
92
99
  export type Arg<T = any> = {
@@ -159,6 +166,7 @@ export interface ValtheraCompatible {
159
166
  removeOne<T = Data>(collection: string, search: Search<T>, context?: VContext): Promise<boolean>;
160
167
  removeCollection(collection: string): Promise<boolean>;
161
168
  updateOneOrAdd<T = Data>(collection: string, search: Search<T>, updater: Updater<T>, opts?: UpdateOneOrAdd<T>): Promise<boolean>;
169
+ toggleOne<T = Data>(collection: string, search: Search<T>, data?: Arg<T>, context?: VContext): Promise<boolean>;
162
170
  }
163
171
  export interface UpdateOneOrAdd<T> {
164
172
  add_arg?: Arg<T>;
@@ -231,6 +239,11 @@ export interface VQL_OP_UpdateOneOrAdd<T = any> {
231
239
  add_arg?: Arg<T>;
232
240
  id_gen?: boolean;
233
241
  }
242
+ export interface VQL_OP_ToggleOne<T = any> {
243
+ collection: string;
244
+ search: Search<T>;
245
+ data?: Arg<T>;
246
+ }
234
247
  export interface VQL_OP_CollectionOperation {
235
248
  collection: string;
236
249
  }
@@ -253,6 +266,8 @@ export type VQL_Query_CRUD_Data<T = any> = {
253
266
  removeOne: VQL_OP_Remove<T>;
254
267
  } | {
255
268
  updateOneOrAdd: VQL_OP_UpdateOneOrAdd<T>;
269
+ } | {
270
+ toggleOne: VQL_OP_ToggleOne<T>;
256
271
  } | {
257
272
  removeCollection: VQL_OP_CollectionOperation;
258
273
  } | {
@@ -262,6 +277,7 @@ export type VQL_Query_CRUD_Data<T = any> = {
262
277
  } | {
263
278
  getCollections: {};
264
279
  };
280
+ export type VQL_Query_CRUD_Keys = VQL_Query_CRUD_Data extends infer U ? U extends Record<string, unknown> ? keyof U : never : never;
265
281
  export interface VQL_Query_CRUD<T = any> {
266
282
  db: string;
267
283
  d: VQL_Query_CRUD_Data<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/vql",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "main": "dist/index.js",
5
5
  "author": "wxn0brP",
6
6
  "license": "MIT",
@@ -27,9 +27,9 @@
27
27
  "query-language"
28
28
  ],
29
29
  "peerDependencies": {
30
- "@wxn0brp/db-core": ">=0.2.5",
31
- "@wxn0brp/falcon-frame": ">=0.5.1",
32
- "@wxn0brp/gate-warden": ">=0.4.1"
30
+ "@wxn0brp/db-core": ">=0.2.11",
31
+ "@wxn0brp/falcon-frame": ">=0.5.3",
32
+ "@wxn0brp/gate-warden": ">=0.5.0"
33
33
  },
34
34
  "peerDependenciesMeta": {
35
35
  "@wxn0brp/falcon-frame": {
@@ -43,17 +43,18 @@
43
43
  }
44
44
  },
45
45
  "devDependencies": {
46
+ "@types/bun": "*",
46
47
  "@types/node": "*",
47
- "@wxn0brp/db": "^0.40.1",
48
- "@wxn0brp/db-core": "^0.2.5",
49
- "@wxn0brp/falcon-frame": "^0.5.1",
50
- "@wxn0brp/gate-warden": "^0.4.1",
51
- "esbuild": "^0.25.11",
48
+ "@wxn0brp/db": "^0.40.3",
49
+ "@wxn0brp/db-core": "^0.2.11",
50
+ "@wxn0brp/falcon-frame": "^0.5.3",
51
+ "@wxn0brp/gate-warden": "^0.5.0",
52
+ "esbuild": "*",
52
53
  "tsc-alias": "*",
53
54
  "typescript": "*"
54
55
  },
55
56
  "dependencies": {
56
- "@wxn0brp/lucerna-log": "^0.2.1"
57
+ "@wxn0brp/lucerna-log": "^0.2.2"
57
58
  },
58
59
  "exports": {
59
60
  ".": {