@wxn0brp/db-core 0.2.5 → 0.2.7

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
@@ -14,7 +14,11 @@ npm install @wxn0brp/db-core
14
14
 
15
15
  ## Documentation
16
16
 
17
- See the [documentation](docs/README.md) for more information about the API and usage.
17
+ See [typedoc](https://wxn0brp.github.io/ValtheraDB-core/).
18
+
19
+ ## Contributing
20
+
21
+ Contributions are welcome! Please submit pull requests or open issues on GitHub.
18
22
 
19
23
  ## License
20
24
 
@@ -33,6 +33,10 @@ export type ComparisonOperators<T = any> = {
33
33
  $between?: PartialOfType<T, [number, number], number>;
34
34
  $in?: Partial<Record<keyof T, T[keyof T][]>>;
35
35
  $nin?: Partial<Record<keyof T, T[keyof T][]>>;
36
+ $idGt?: PartialOfType<T, string | number>;
37
+ $idLt?: PartialOfType<T, string | number>;
38
+ $idGte?: PartialOfType<T, string | number>;
39
+ $idLte?: PartialOfType<T, string | number>;
36
40
  };
37
41
  /** Type and Existence Operators */
38
42
  export type TypeAndExistenceOperators<T = any> = {
@@ -13,7 +13,8 @@ export type ArrayUpdater<T = any> = {
13
13
  };
14
14
  /** Objects */
15
15
  export type ObjectUpdater<T = any> = {
16
- $merge?: PartialOfType<T, any[]>;
16
+ $merge?: PartialOfType<T, any>;
17
+ $deepMerge?: PartialOfType<T, any>;
17
18
  };
18
19
  /** Values */
19
20
  export type ValueUpdater<T = any> = {
@@ -21,5 +22,7 @@ export type ValueUpdater<T = any> = {
21
22
  $dec?: PartialOfType<T, number>;
22
23
  $unset?: PartialOfType<T, any>;
23
24
  $rename?: PartialOfType<T, any>;
25
+ /** same as { name: value } */
26
+ $set?: PartialOfType<T, any>;
24
27
  };
25
28
  export type UpdaterArg<T = any> = ArrayUpdater<T> & ObjectUpdater<T> & ValueUpdater<T> & Arg<T>;
@@ -1,4 +1,4 @@
1
- import { compareSafe } from "./sort.js";
1
+ import { compareSafe } from "./compare.js";
2
2
  export async function findUtil(query, fileCpu, files) {
3
3
  const { search, context = {}, dbFindOpts = {}, findOpts = {} } = query;
4
4
  const { reverse = false, max = -1, offset = 0, sortBy, sortAsc = true } = dbFindOpts;
@@ -1,4 +1,5 @@
1
1
  import hasFields from "./hasFields.js";
2
+ import { compareIds } from "./id.js";
2
3
  /**
3
4
  * Checks if an object meets the criteria specified in the fields with operators.
4
5
  */
@@ -6,161 +7,66 @@ export default function hasFieldsAdvanced(obj, fields) {
6
7
  if (typeof fields !== "object" || fields === null) {
7
8
  throw new Error("Fields must be an object");
8
9
  }
10
+ if (Object.keys(fields).length === 0)
11
+ return true;
9
12
  if ("$and" in fields) {
10
- return fields["$and"].every(subFields => hasFieldsAdvanced(obj, subFields));
13
+ return fields["$and"].every((subFields) => hasFieldsAdvanced(obj, subFields));
11
14
  }
12
15
  if ("$or" in fields) {
13
- return fields["$or"].some(subFields => hasFieldsAdvanced(obj, subFields));
16
+ return fields["$or"].some((subFields) => hasFieldsAdvanced(obj, subFields));
14
17
  }
15
18
  // Check various conditions
16
19
  if (!checkConditions(obj, fields))
17
20
  return false;
18
- const fieldsSubset = removeAdvancedOperators({ ...fields });
21
+ const fieldsSubset = { ...fields };
22
+ Object.keys(fieldsSubset).filter(key => key.startsWith("$")).forEach(key => delete fieldsSubset[key]);
23
+ if (!Object.keys(fieldsSubset).length)
24
+ return true;
19
25
  return hasFields(obj, fieldsSubset);
20
26
  }
21
- function removeAdvancedOperators(fields) {
22
- const advancedOperators = [
23
- "and", "or",
24
- "gt", "lt", "gte", "lte", "in", "nin",
25
- "exists",
26
- "type",
27
- "regex",
28
- "arrinc", "arrincall", "size",
29
- "startsWith", "endsWith",
30
- "between",
31
- "not",
32
- "subset"
33
- ].map(operator => "$" + operator);
34
- advancedOperators.forEach(operator => delete fields[operator]);
35
- return fields;
36
- }
37
27
  function checkConditions(obj, fields) {
38
- return (checkComparison(obj, fields) &&
39
- checkExistence(obj, fields) &&
40
- checkType(obj, fields) &&
41
- checkRegex(obj, fields) &&
42
- checkArrayConditions(obj, fields) &&
43
- checkStringConditions(obj, fields) &&
44
- checkBetween(obj, fields) &&
28
+ return (mainCheck(obj, fields) &&
45
29
  checkNot(obj, fields) &&
46
30
  checkSubset(obj, fields));
47
31
  }
48
- function checkComparison(obj, fields) {
49
- const comparisonOperators = ["$gt", "$lt", "$gte", "$lte", "$in", "$nin"];
50
- for (const operator of comparisonOperators) {
51
- if (operator in fields) {
52
- for (const entries of Object.entries(fields[operator])) {
53
- const [key, value] = entries;
54
- switch (operator) {
55
- case "$gt":
56
- if (!(obj[key] > value))
57
- return false;
58
- break;
59
- case "$lt":
60
- if (!(obj[key] < value))
61
- return false;
62
- break;
63
- case "$gte":
64
- if (!(obj[key] >= value))
65
- return false;
66
- break;
67
- case "$lte":
68
- if (!(obj[key] <= value))
69
- return false;
70
- break;
71
- case "$in":
72
- if (!value.includes(obj[key]))
73
- return false;
74
- break;
75
- case "$nin":
76
- if (value.includes(obj[key]))
77
- return false;
78
- break;
79
- }
80
- }
81
- }
82
- }
83
- return true;
84
- }
85
- function checkExistence(obj, fields) {
86
- if ("$exists" in fields) {
87
- for (const [key, shouldExist] of Object.entries(fields["$exists"])) {
32
+ function mainCheck(obj, fields) {
33
+ return _for(fields, obj, {
34
+ gt: (data, value) => data > value,
35
+ lt: (data, value) => data < value,
36
+ gte: (data, value) => data >= value,
37
+ lte: (data, value) => data <= value,
38
+ in: (data, value) => value.includes(data),
39
+ nin: (data, value) => !value.includes(data),
40
+ type: (data, value) => typeof data === value,
41
+ exists: (_, shouldExist, key) => {
88
42
  if (shouldExist && !(key in obj))
89
43
  return false;
90
44
  if (!shouldExist && (key in obj))
91
45
  return false;
92
- }
93
- }
94
- return true;
95
- }
96
- function checkType(obj, fields) {
97
- if ("$type" in fields) {
98
- for (const [key, type] of Object.entries(fields["$type"])) {
99
- if (typeof obj[key] !== type)
100
- return false;
101
- }
102
- }
103
- return true;
104
- }
105
- function checkRegex(obj, fields) {
106
- if ("$regex" in fields) {
107
- for (const [key, regexData] of Object.entries(fields["$regex"])) {
46
+ return true;
47
+ },
48
+ regex: (data, regexData) => {
108
49
  const regex = typeof regexData === "string" ? new RegExp(regexData) : regexData;
109
- if (!regex.test(obj[key]))
110
- return false;
111
- }
112
- }
113
- return true;
114
- }
115
- function checkArrayConditions(obj, fields) {
116
- if ("$arrinc" in fields) {
117
- for (const [key, values] of Object.entries(fields["$arrinc"])) {
118
- if (!Array.isArray(obj[key]) || !values.some(val => obj[key].includes(val)))
119
- return false;
120
- }
121
- }
122
- if ("$arrincall" in fields) {
123
- for (const [key, values] of Object.entries(fields["$arrincall"])) {
124
- if (!Array.isArray(obj[key]) || !values.every(val => obj[key].includes(val)))
125
- return false;
126
- }
127
- }
128
- if ("$size" in fields) {
129
- for (const [key, size] of Object.entries(fields["$size"])) {
130
- if (Array.isArray(obj[key]) || typeof obj[key] === "string") {
131
- if (obj[key].length !== size)
132
- return false;
50
+ return regex.test(data);
51
+ },
52
+ size: (data, size) => {
53
+ if (Array.isArray(data) || typeof data === "string") {
54
+ return data.length === size;
133
55
  }
134
56
  else {
135
57
  return false;
136
58
  }
137
- }
138
- }
139
- return true;
140
- }
141
- function checkStringConditions(obj, fields) {
142
- if ("$startsWith" in fields) {
143
- for (const [key, value] of Object.entries(fields["$startsWith"])) {
144
- if (typeof obj[key] !== "string" || !obj[key].startsWith(value))
145
- return false;
146
- }
147
- }
148
- if ("$endsWith" in fields) {
149
- for (const [key, value] of Object.entries(fields["$endsWith"])) {
150
- if (typeof obj[key] !== "string" || !obj[key].endsWith(value))
151
- return false;
152
- }
153
- }
154
- return true;
155
- }
156
- function checkBetween(obj, fields) {
157
- if ("$between" in fields) {
158
- for (const [key, [min, max]] of Object.entries(fields["$between"])) {
159
- if (typeof obj[key] !== "number" || obj[key] < min || obj[key] > max)
160
- return false;
161
- }
162
- }
163
- return true;
59
+ },
60
+ startsWith: (data, value) => typeof data === "string" && data.startsWith(value),
61
+ endsWith: (data, value) => typeof data === "string" && data.endsWith(value),
62
+ between: (data, [min, max]) => typeof data === "number" && data >= min && data <= max,
63
+ arrinc: (data, values) => Array.isArray(data) && values.some((val) => data.includes(val)),
64
+ arrincall: (data, values) => Array.isArray(data) && values.every((val) => data.includes(val)),
65
+ idGt: (data, value) => compareIds(data, value) > 0,
66
+ idLt: (data, value) => compareIds(data, value) < 0,
67
+ idGte: (data, value) => compareIds(data, value) >= 0,
68
+ idLte: (data, value) => compareIds(data, value) <= 0,
69
+ });
164
70
  }
165
71
  function checkNot(obj, fields) {
166
72
  if ("$not" in fields) {
@@ -175,3 +81,15 @@ function checkSubset(obj, fields) {
175
81
  }
176
82
  return true;
177
83
  }
84
+ function _for(fields, obj, opts) {
85
+ for (const [fieldRaw, fieldFn] of Object.entries(opts)) {
86
+ const field = "$" + fieldRaw;
87
+ if (field in fields) {
88
+ for (const [key, value] of Object.entries(fields[field])) {
89
+ if (!fieldFn(obj?.[key], value, key))
90
+ return false;
91
+ }
92
+ }
93
+ }
94
+ return true;
95
+ }
@@ -2,3 +2,4 @@ export declare function convertIdToUnix(id: string): number;
2
2
  export declare function sortByIds<T extends {
3
3
  _id: string;
4
4
  }>(objects: T[]): T[];
5
+ export declare function compareIds(a: string | number, b: string | number): number;
package/dist/utils/id.js CHANGED
@@ -2,7 +2,14 @@ export function convertIdToUnix(id) {
2
2
  return parseInt(id.split("-")[0], 36);
3
3
  }
4
4
  export function sortByIds(objects) {
5
- return objects.slice().sort((a, b) => {
6
- return convertIdToUnix(a._id) - convertIdToUnix(b._id) || a._id.localeCompare(b._id);
7
- });
5
+ return objects.slice().sort((a, b) => compareIds(a._id, b._id));
6
+ }
7
+ export function compareIds(a, b) {
8
+ if (typeof a === "string" && typeof b === "string")
9
+ return convertIdToUnix(a) - convertIdToUnix(b) || a.localeCompare(b);
10
+ if (typeof a === "number" && typeof b === "number")
11
+ return a - b;
12
+ const timeA = typeof a === "string" ? convertIdToUnix(a) : a;
13
+ const timeB = typeof b === "string" ? convertIdToUnix(b) : b;
14
+ return timeA - timeB;
8
15
  }
@@ -0,0 +1 @@
1
+ export declare function deepMerge(target: any, source: any): any;
@@ -0,0 +1,22 @@
1
+ export function deepMerge(target, source) {
2
+ const output = { ...target };
3
+ if (isObject(target) && isObject(source)) {
4
+ for (const key in source) {
5
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
6
+ if (isObject(source[key])) {
7
+ if (!target[key]) {
8
+ output[key] = {};
9
+ }
10
+ output[key] = deepMerge(target[key], source[key]);
11
+ }
12
+ else {
13
+ output[key] = source[key];
14
+ }
15
+ }
16
+ }
17
+ }
18
+ return output;
19
+ }
20
+ function isObject(item) {
21
+ return item !== null && typeof item === "object" && !Array.isArray(item);
22
+ }
@@ -2,6 +2,6 @@ import { UpdaterArg } from "../types/updater.js";
2
2
  /**
3
3
  * Updates an object with new values.
4
4
  * @param obj - The object to update.
5
- * @param fields - An object containing new values to update in the target object.
5
+ * @param field - An object containing new values to update in the target object.
6
6
  */
7
- export default function updateObjectAdvanced(obj: Object, fields: UpdaterArg | UpdaterArg[]): Object;
7
+ export default function updateObjectAdvanced(obj: Object, field: UpdaterArg): Object;
@@ -1,117 +1,86 @@
1
+ import { deepMerge } from "./merge.js";
1
2
  /**
2
3
  * Updates an object with new values.
3
4
  * @param obj - The object to update.
4
- * @param fields - An object containing new values to update in the target object.
5
+ * @param field - An object containing new values to update in the target object.
5
6
  */
6
- export default function updateObjectAdvanced(obj, fields) {
7
- if (typeof fields !== "object" || fields === null) {
7
+ export default function updateObjectAdvanced(obj, field) {
8
+ if (typeof field !== "object" || field === null) {
8
9
  throw new Error("Fields must be an object or object array");
9
10
  }
10
- const fieldsArray = Array.isArray(fields) ? fields : [fields];
11
- for (const field of fieldsArray) {
12
- if (typeof field !== "object" || field === null) {
13
- throw new Error("Fields must be an object or object array");
14
- }
15
- updateAdvanced(obj, field);
16
- const fieldsSubset = removeAdvancedOperators({ ...field });
17
- updateObject(obj, fieldsSubset);
11
+ if (typeof field !== "object" || field === null) {
12
+ throw new Error("Fields must be an object or object array");
18
13
  }
14
+ updateAdvanced(obj, field);
15
+ const fieldsSubset = { ...field };
16
+ Object.keys(fieldsSubset).filter(key => key.startsWith("$")).forEach(key => delete fieldsSubset[key]);
17
+ updateObject(obj, fieldsSubset);
19
18
  return obj;
20
19
  }
21
20
  function updateAdvanced(obj, fields) {
22
- updateArray(obj, fields);
23
- updateNested(obj, fields);
24
- updateIncrement(obj, fields);
21
+ mainUpdate(obj, fields);
25
22
  updateUnset(obj, fields);
26
- updateRename(obj, fields);
27
- }
28
- function removeAdvancedOperators(fields) {
29
- const advancedOperators = [
30
- "push", "pushset", "pull", "pullall",
31
- "inc", "dec",
32
- "unset",
33
- "merge",
34
- "rename"
35
- ].map(operator => "$" + operator);
36
- advancedOperators.forEach(operator => delete fields[operator]);
37
- return fields;
38
- }
39
- function updateArray(obj, fields) {
40
- if ("$push" in fields) {
41
- for (const [key, value] of Object.entries(fields["$push"])) {
42
- if (Array.isArray(obj[key])) {
43
- obj[key].push(value);
44
- }
45
- else {
46
- obj[key] = [value];
47
- }
48
- }
49
- }
50
- if ("$pushset" in fields) {
51
- for (const [key, value] of Object.entries(fields["$pushset"])) {
52
- if (Array.isArray(obj[key])) {
53
- obj[key].push(value);
54
- }
55
- else {
56
- obj[key] = [value];
57
- }
58
- obj[key] = [...new Set(obj[key])];
59
- }
60
- }
61
- if ("$pull" in fields) {
62
- for (const [key, value] of Object.entries(fields["$pull"])) {
63
- if (Array.isArray(obj[key])) {
64
- obj[key] = obj[key].filter(item => item !== value);
65
- }
66
- }
67
- }
68
- if ("$pullall" in fields) {
69
- for (const [key, values] of Object.entries(fields["$pullall"])) {
70
- if (Array.isArray(obj[key])) {
71
- obj[key] = obj[key].filter(item => !values.includes(item));
72
- }
73
- }
74
- }
75
23
  }
76
- function updateNested(obj, fields) {
77
- if ("$merge" in fields) {
78
- for (const [key, value] of Object.entries(fields["$merge"])) {
79
- if (typeof obj[key] === "object" && typeof value === "object") {
80
- obj[key] = { ...obj[key], ...value };
24
+ function mainUpdate(obj, fields) {
25
+ _for(fields, obj, {
26
+ push: (item, updater) => {
27
+ if (Array.isArray(item)) {
28
+ item.push(updater);
29
+ return item;
81
30
  }
82
- else {
83
- obj[key] = value;
84
- }
85
- }
86
- }
87
- }
88
- function updateIncrement(obj, fields) {
89
- if ("$inc" in fields) {
90
- for (const [key, value] of Object.entries(fields["$inc"])) {
91
- if (typeof obj[key] === "number" && typeof value === "number") {
92
- obj[key] += value;
31
+ return [updater];
32
+ },
33
+ pushSet: (item, updater) => {
34
+ if (Array.isArray(item)) {
35
+ item.push(updater);
36
+ return [...new Set(item)];
93
37
  }
94
- else if (!(key in obj)) {
95
- obj[key] = value;
38
+ return [updater];
39
+ },
40
+ pull: (item, updater) => {
41
+ if (Array.isArray(item))
42
+ return item.filter(i => i !== updater);
43
+ },
44
+ pullall: (item, updater) => {
45
+ if (Array.isArray(item))
46
+ return item.filter(i => !updater.includes(i));
47
+ },
48
+ merge: (item, updater) => {
49
+ if (typeof item === "object" && typeof updater === "object") {
50
+ return { ...item, ...updater };
96
51
  }
97
- else {
98
- throw new Error(`Cannot increment non-numeric value at key: ${key}`);
52
+ return updater;
53
+ },
54
+ deepMerge: (item, updater) => {
55
+ if (typeof item === "object" && typeof updater === "object") {
56
+ return deepMerge(item, updater);
99
57
  }
100
- }
101
- }
102
- if ("$dec" in fields) {
103
- for (const [key, value] of Object.entries(fields["$dec"])) {
104
- if (typeof obj[key] === "number" && typeof value === "number") {
105
- obj[key] -= value;
58
+ return updater;
59
+ },
60
+ inc: (item, updater, key) => {
61
+ if (typeof item === "number" && typeof updater === "number") {
62
+ return item + updater;
106
63
  }
107
- else if (!(key in obj)) {
108
- obj[key] = value;
64
+ if (!(key in obj))
65
+ return updater;
66
+ throw new Error(`Cannot increment non-numeric value at key: ${key}`);
67
+ },
68
+ dec: (item, updater, key) => {
69
+ if (typeof item === "number" && typeof updater === "number") {
70
+ return item - updater;
109
71
  }
110
- else {
111
- throw new Error(`Cannot decrement non-numeric value at key: ${key}`);
72
+ if (!(key in obj))
73
+ return -updater;
74
+ throw new Error(`Cannot decrement non-numeric value at key: ${key}`);
75
+ },
76
+ rename: (_, newKey, oldKey) => {
77
+ if (oldKey in obj) {
78
+ obj[newKey] = obj[oldKey];
79
+ delete obj[oldKey];
112
80
  }
113
- }
114
- }
81
+ },
82
+ set: (_, value) => value
83
+ });
115
84
  }
116
85
  function updateUnset(obj, fields) {
117
86
  if ("$unset" in fields) {
@@ -120,16 +89,6 @@ function updateUnset(obj, fields) {
120
89
  }
121
90
  }
122
91
  }
123
- function updateRename(obj, fields) {
124
- if ("$rename" in fields) {
125
- for (const [oldKey, newKey] of Object.entries(fields["$rename"])) {
126
- if (oldKey in obj) {
127
- obj[newKey] = obj[oldKey];
128
- delete obj[oldKey];
129
- }
130
- }
131
- }
132
- }
133
92
  /**
134
93
  * Updates an object with new values.
135
94
  * @param obj - The object to update.
@@ -143,3 +102,15 @@ function updateObject(obj, newVal) {
143
102
  }
144
103
  return obj;
145
104
  }
105
+ function _for(fields, obj, opts) {
106
+ for (const [fieldRaw, fieldFn] of Object.entries(opts)) {
107
+ const field = "$" + fieldRaw;
108
+ if (field in fields) {
109
+ for (const [key, value] of Object.entries(fields[field])) {
110
+ const res = fieldFn(obj?.[key], value, key);
111
+ if (res !== undefined)
112
+ obj[key] = res;
113
+ }
114
+ }
115
+ }
116
+ }
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.2.5";
1
+ export const version = "0.2.7";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/db-core",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "types": "dist/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "author": "wxn0brP",
@@ -13,12 +13,13 @@
13
13
  },
14
14
  "homepage": "https://github.com/wxn0brP/ValtheraDB",
15
15
  "dependencies": {
16
- "@wxn0brp/event-emitter": "^0.0.2"
16
+ "@wxn0brp/event-emitter": "^0.0.3"
17
17
  },
18
18
  "devDependencies": {
19
- "@types/node": "^24.2.0",
20
- "tsc-alias": "^1.8.10",
21
- "typescript": "^5.9.2"
19
+ "@types/bun": "*",
20
+ "@types/node": "*",
21
+ "tsc-alias": "*",
22
+ "typescript": "*"
22
23
  },
23
24
  "files": [
24
25
  "dist"
File without changes
File without changes