@wxn0brp/db-core 0.12.0 → 0.12.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,9 +5,10 @@ export type WriteFile = (file: string, data: any[]) => Promise<void>;
5
5
  export type ReadFile = (file: string) => Promise<any[]>;
6
6
  export declare function pathRepair(path: string): string;
7
7
  export declare class CustomFileCpu implements FileCpu {
8
+ requireClone: boolean;
8
9
  _readFile: ReadFile;
9
10
  _writeFile: WriteFile;
10
- constructor(readFile: ReadFile, writeFile: WriteFile);
11
+ constructor(readFile: ReadFile, writeFile: WriteFile, requireClone?: boolean);
11
12
  add(file: string, config: VQueryT.Add): Promise<void>;
12
13
  find(file: string, config: VQueryT.Find): Promise<Data[]>;
13
14
  findOne(file: string, config: VQueryT.FindOne): Promise<DataInternal | false>;
@@ -1,11 +1,14 @@
1
- import { matchObj, findObj, updateObj } from "./utils/process.js";
1
+ import { matchObj, updateObj } from "./utils/process.js";
2
+ import { updateFindObject } from "./utils/updateFindObject.js";
2
3
  export function pathRepair(path) {
3
4
  return path.replace(/\/+/g, "/");
4
5
  }
5
6
  export class CustomFileCpu {
7
+ requireClone;
6
8
  _readFile;
7
9
  _writeFile;
8
- constructor(readFile, writeFile) {
10
+ constructor(readFile, writeFile, requireClone = false) {
11
+ this.requireClone = requireClone;
9
12
  this._readFile = readFile;
10
13
  this._writeFile = writeFile;
11
14
  }
@@ -18,15 +21,27 @@ export class CustomFileCpu {
18
21
  async find(file, config) {
19
22
  file = pathRepair(file);
20
23
  const entries = await this._readFile(file);
21
- return entries.map(entry => findObj(config, entry)).filter(Boolean);
24
+ const findOpts = config.findOpts || {};
25
+ const result = [];
26
+ for (let i = 0; i < entries.length; i++) {
27
+ if (matchObj(config, entries[i])) {
28
+ const obj = this.requireClone
29
+ ? structuredClone(entries[i])
30
+ : entries[i];
31
+ result.push(updateFindObject(obj, findOpts));
32
+ }
33
+ }
34
+ return result;
22
35
  }
23
36
  async findOne(file, config) {
24
37
  file = pathRepair(file);
25
38
  const entries = await this._readFile(file);
26
39
  for (const entry of entries) {
27
- const result = findObj(config, entry);
28
- if (result)
29
- return result;
40
+ const isMatch = matchObj(config, entry);
41
+ if (!isMatch)
42
+ continue;
43
+ const obj = this.requireClone ? structuredClone(entry) : entry;
44
+ return updateFindObject(obj, config.findOpts || {});
30
45
  }
31
46
  return false;
32
47
  }
package/dist/db/memory.js CHANGED
@@ -7,16 +7,16 @@ export class MemoryAction extends CustomActionsBase {
7
7
  smartExecutor = true;
8
8
  constructor() {
9
9
  super();
10
- this.fileCpu = new CustomFileCpu(this._readMemory.bind(this), this._writeMemory.bind(this));
10
+ this.fileCpu = new CustomFileCpu(this._readMemory.bind(this), this._writeMemory.bind(this), true);
11
11
  this.memory = new Map();
12
12
  }
13
13
  _readMemory(key) {
14
14
  if (!this.memory.has(key))
15
15
  return [];
16
- return structuredClone(this.memory.get(key));
16
+ return this.memory.get(key);
17
17
  }
18
18
  _writeMemory(key, data) {
19
- this.memory.set(key, structuredClone(data));
19
+ this.memory.set(key, data);
20
20
  }
21
21
  async getCollections() {
22
22
  const collections = Array.from(this.memory.keys());
@@ -2,6 +2,8 @@ import { Id } from "../types/Id.js";
2
2
  export declare function getIdData(): {
3
3
  usedIds: Map<any, any>;
4
4
  lastId: Id;
5
+ lastTimeStr: string;
6
+ lastCounter: number;
5
7
  recentIdsTimestamps: number[];
6
8
  startIndex: number;
7
9
  lastGeneratedMs: number;
@@ -17,6 +19,8 @@ export type IdData = ReturnType<typeof getIdData>;
17
19
  export declare function genId(parts?: number[], idData?: {
18
20
  usedIds: Map<any, any>;
19
21
  lastId: Id;
22
+ lastTimeStr: string;
23
+ lastCounter: number;
20
24
  recentIdsTimestamps: number[];
21
25
  startIndex: number;
22
26
  lastGeneratedMs: number;
@@ -2,6 +2,8 @@ export function getIdData() {
2
2
  return {
3
3
  usedIds: new Map(),
4
4
  lastId: undefined,
5
+ lastTimeStr: "",
6
+ lastCounter: 0,
5
7
  recentIdsTimestamps: [],
6
8
  startIndex: 0,
7
9
  lastGeneratedMs: 0,
@@ -35,13 +37,8 @@ export function genId(parts = null, idData = defaultIdData) {
35
37
  function getUniqueRandom(opts, idData) {
36
38
  while (true) {
37
39
  let minValue = 0;
38
- if (idData.lastId) {
39
- const parts = idData.lastId.split("-");
40
- const lastTime = parts.shift();
41
- if (lastTime === opts.time) {
42
- const int36 = parts.join("");
43
- minValue = parseInt(int36, 36) + 1;
44
- }
40
+ if (idData.lastTimeStr === opts.time) {
41
+ minValue = idData.lastCounter + 1;
45
42
  }
46
43
  const partsLengthSum = opts.partsSchema.reduce((acc, num) => acc + num, 0);
47
44
  const partsData = generateBase36InRange(minValue, partsLengthSum, idData);
@@ -54,6 +51,8 @@ function getUniqueRandom(opts, idData) {
54
51
  idData.usedIds.set(id, true);
55
52
  setTimeout(() => idData.usedIds.delete(id), 1000);
56
53
  idData.lastId = id;
54
+ idData.lastTimeStr = opts.time;
55
+ idData.lastCounter = idData.lastRandomValue;
57
56
  return id;
58
57
  }
59
58
  opts.s++;
@@ -9,11 +9,16 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
9
9
  let datas = [];
10
10
  let skippedEntries = 0;
11
11
  if (Array.isArray(fileCpuOrData)) {
12
- datas = [
13
- ...fileCpuOrData,
14
- ];
15
- if (reverse && !sortBy)
16
- datas.reverse();
12
+ const src = fileCpuOrData;
13
+ const len = src.length;
14
+ if (reverse && !sortBy) {
15
+ datas = new Array(len);
16
+ for (let i = 0; i < len; i++)
17
+ datas[i] = src[len - 1 - i];
18
+ }
19
+ else {
20
+ datas = src.slice();
21
+ }
17
22
  if (!needsAllData) {
18
23
  if (offset > 0 || limit !== -1) {
19
24
  const start = offset;
@@ -33,7 +38,8 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
33
38
  if (reverse && !sortBy)
34
39
  entries.reverse();
35
40
  if (needsAllData) {
36
- datas.push(...entries);
41
+ for (let i = 0; i < entries.length; i++)
42
+ datas.push(entries[i]);
37
43
  continue;
38
44
  }
39
45
  if (offset > skippedEntries) {
@@ -50,7 +56,8 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
50
56
  if (entries.length > remaining)
51
57
  entries = entries.slice(0, remaining);
52
58
  }
53
- datas.push(...entries);
59
+ for (let i = 0; i < entries.length; i++)
60
+ datas.push(entries[i]);
54
61
  if (limit !== -1 && datas.length >= limit)
55
62
  return datas;
56
63
  }
@@ -71,18 +78,20 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
71
78
  : [];
72
79
  if (groupKeys.length) {
73
80
  for (const data of datas) {
74
- const key = groupKeys
75
- .map(k => String(data[k] ?? ""))
76
- .join("|");
77
- if (!groups.has(key))
78
- groups.set(key, []);
79
- groups.get(key).push(data);
81
+ let key = String(data[groupKeys[0]] ?? "");
82
+ for (let i = 1; i < groupKeys.length; i++) {
83
+ key += "|" + String(data[groupKeys[i]] ?? "");
84
+ }
85
+ let group = groups.get(key);
86
+ if (!group) {
87
+ group = [];
88
+ groups.set(key, group);
89
+ }
90
+ group.push(data);
80
91
  }
81
92
  }
82
93
  else {
83
- groups.set("all", [
84
- ...datas,
85
- ]);
94
+ groups.set("all", datas.slice());
86
95
  }
87
96
  const aggregated = [];
88
97
  for (const [, groupItems] of groups) {
@@ -94,34 +103,72 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
94
103
  }
95
104
  if (count) {
96
105
  for (const [outKey, srcKey] of Object.entries(count)) {
97
- result[outKey] = groupItems.filter(d => d[srcKey] !== undefined && d[srcKey] !== null).length;
106
+ let c = 0;
107
+ for (let i = 0; i < groupItems.length; i++) {
108
+ const v = groupItems[i][srcKey];
109
+ if (v !== undefined && v !== null)
110
+ c++;
111
+ }
112
+ result[outKey] = c;
98
113
  }
99
114
  }
100
- for (const [outKey, srcField] of Object.entries(min ?? {})) {
101
- const nums = groupItems
102
- .map(d => d[srcField])
103
- .filter((v) => typeof v === "number");
104
- result[outKey] = nums.length ? Math.min(...nums) : null;
115
+ const minEntries = min ? Object.entries(min) : null;
116
+ const maxEntries = max ? Object.entries(max) : null;
117
+ const avgEntries = avg ? Object.entries(avg) : null;
118
+ const sumEntries = sum ? Object.entries(sum) : null;
119
+ if (minEntries) {
120
+ for (const [outKey, srcField] of minEntries) {
121
+ let best = null;
122
+ for (let i = 0; i < groupItems.length; i++) {
123
+ const v = groupItems[i][srcField];
124
+ if (typeof v === "number") {
125
+ if (best === null || v < best)
126
+ best = v;
127
+ }
128
+ }
129
+ result[outKey] = best;
130
+ }
105
131
  }
106
- for (const [outKey, srcField] of Object.entries(max ?? {})) {
107
- const nums = groupItems
108
- .map(d => d[srcField])
109
- .filter((v) => typeof v === "number");
110
- result[outKey] = nums.length ? Math.max(...nums) : null;
132
+ if (maxEntries) {
133
+ for (const [outKey, srcField] of maxEntries) {
134
+ let best = null;
135
+ for (let i = 0; i < groupItems.length; i++) {
136
+ const v = groupItems[i][srcField];
137
+ if (typeof v === "number") {
138
+ if (best === null || v > best)
139
+ best = v;
140
+ }
141
+ }
142
+ result[outKey] = best;
143
+ }
111
144
  }
112
- for (const [outKey, srcField] of Object.entries(avg ?? {})) {
113
- const nums = groupItems
114
- .map(d => d[srcField])
115
- .filter((v) => typeof v === "number");
116
- result[outKey] = nums.length
117
- ? nums.reduce((a, b) => a + b, 0) / nums.length
118
- : null;
145
+ if (avgEntries) {
146
+ for (const [outKey, srcField] of avgEntries) {
147
+ let total = 0;
148
+ let c = 0;
149
+ for (let i = 0; i < groupItems.length; i++) {
150
+ const v = groupItems[i][srcField];
151
+ if (typeof v === "number") {
152
+ total += v;
153
+ c++;
154
+ }
155
+ }
156
+ result[outKey] = c > 0 ? total / c : null;
157
+ }
119
158
  }
120
- for (const [outKey, srcField] of Object.entries(sum ?? {})) {
121
- const nums = groupItems
122
- .map(d => d[srcField])
123
- .filter((v) => typeof v === "number");
124
- result[outKey] = nums.length ? nums.reduce((a, b) => a + b, 0) : null;
159
+ if (sumEntries) {
160
+ for (const [outKey, srcField] of sumEntries) {
161
+ let total = 0;
162
+ let c = 0;
163
+ for (let i = 0; i < groupItems.length; i++) {
164
+ const v = groupItems[i][srcField];
165
+ if (typeof v === "number") {
166
+ total += v;
167
+ c++;
168
+ }
169
+ }
170
+ result[outKey] = c > 0 ? total : null;
171
+ }
125
172
  }
126
173
  aggregated.push(result);
127
174
  }
@@ -129,14 +176,16 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
129
176
  }
130
177
  if (distinct) {
131
178
  const seen = new Set();
132
- datas = datas.filter(item => {
133
- const val = item[distinct];
134
- const key = JSON.stringify(val);
135
- if (seen.has(key))
136
- return false;
137
- seen.add(key);
138
- return true;
139
- });
179
+ const result = [];
180
+ for (let i = 0; i < datas.length; i++) {
181
+ const val = datas[i][distinct];
182
+ const key = typeof val === "object" && val !== null ? JSON.stringify(val) : val;
183
+ if (!seen.has(key)) {
184
+ seen.add(key);
185
+ result.push(datas[i]);
186
+ }
187
+ }
188
+ datas = result;
140
189
  }
141
190
  if (sortBy) {
142
191
  if (sortBy === "random()") {
@@ -10,7 +10,7 @@ export function compareSafe(a, b) {
10
10
  const typeA = typeof a;
11
11
  const typeB = typeof b;
12
12
  if (typeA === "string" && typeB === "string") {
13
- return Math.sign(a.localeCompare(b));
13
+ return a < b ? -1 : a > b ? 1 : 0;
14
14
  }
15
15
  if ((typeA === "number" || a instanceof Date) &&
16
16
  (typeB === "number" || b instanceof Date)) {
@@ -11,14 +11,18 @@ export function hasFieldsAdvanced(obj, fields) {
11
11
  return true;
12
12
  const $fields = {};
13
13
  const subsetFields = {};
14
- Object.keys(fields).forEach(key => {
15
- if (key.startsWith("$"))
14
+ let hasSubset = false;
15
+ for (const key in fields) {
16
+ if (key.charCodeAt(0) === 36) {
16
17
  $fields[key.toLowerCase()] = fields[key];
17
- else
18
+ }
19
+ else {
18
20
  subsetFields[key] = fields[key];
19
- });
21
+ hasSubset = true;
22
+ }
23
+ }
20
24
  // Shallow fields first
21
- if (Object.keys(subsetFields).length && !hasFields(obj, subsetFields))
25
+ if (hasSubset && !hasFields(obj, subsetFields))
22
26
  return false;
23
27
  // Direct fields only
24
28
  if (Object.keys($fields).length === 0)
@@ -22,8 +22,20 @@ export function _deepMerge(target, ...sources) {
22
22
  }
23
23
  return _deepMerge(target, ...sources);
24
24
  }
25
+ function fastClone(obj) {
26
+ if (obj === null || typeof obj !== "object")
27
+ return obj;
28
+ if (Array.isArray(obj))
29
+ return obj.map(fastClone);
30
+ if (obj instanceof Date)
31
+ return new Date(obj.getTime());
32
+ const result = {};
33
+ for (const key in obj)
34
+ result[key] = fastClone(obj[key]);
35
+ return result;
36
+ }
25
37
  export function deepMerge(target, ...sources) {
26
- return _deepMerge(structuredClone(target), ...sources.map(s => structuredClone(s)));
38
+ return _deepMerge(fastClone(target), ...sources.map(fastClone));
27
39
  }
28
40
  function isObject(item) {
29
41
  return item && typeof item === "object" && !Array.isArray(item);
@@ -1,40 +1,6 @@
1
1
  function resolvePath(path) {
2
2
  return Array.isArray(path) ? path : path.split(".");
3
3
  }
4
- function pathExists(obj, path) {
5
- const segs = resolvePath(path);
6
- if (segs.length === 0)
7
- return false;
8
- let cur = obj;
9
- for (const s of segs) {
10
- if (cur == null || typeof cur !== "object" || !(s in cur))
11
- return false;
12
- cur = cur[s];
13
- }
14
- return true;
15
- }
16
- function getValueAt(obj, path) {
17
- const segs = resolvePath(path);
18
- let cur = obj;
19
- for (const s of segs) {
20
- if (cur == null || typeof cur !== "object")
21
- return undefined;
22
- cur = cur[s];
23
- }
24
- return cur;
25
- }
26
- function setNested(obj, path, value) {
27
- const segs = resolvePath(path);
28
- let cur = obj;
29
- for (let i = 0; i < segs.length - 1; i++) {
30
- const s = segs[i];
31
- if (!(s in cur) || typeof cur[s] !== "object" || Array.isArray(cur[s])) {
32
- cur[s] = {};
33
- }
34
- cur = cur[s];
35
- }
36
- cur[segs[segs.length - 1]] = value;
37
- }
38
4
  function deleteNested(obj, path) {
39
5
  const segs = resolvePath(path);
40
6
  if (segs.length === 0)
@@ -72,9 +38,30 @@ export function updateFindObject(obj, findOpts) {
72
38
  if (Array.isArray(select)) {
73
39
  const result = {};
74
40
  for (const field of select) {
75
- if ((typeof field === "string" || Array.isArray(field)) &&
76
- pathExists(obj, field)) {
77
- setNested(result, field, getValueAt(obj, field));
41
+ const segs = typeof field === "string" ? field.split(".") : field;
42
+ if (!segs.length)
43
+ continue;
44
+ let src = obj;
45
+ let found = true;
46
+ for (let i = 0; i < segs.length; i++) {
47
+ if (src == null || typeof src !== "object" || !(segs[i] in src)) {
48
+ found = false;
49
+ break;
50
+ }
51
+ src = src[segs[i]];
52
+ }
53
+ if (found) {
54
+ let dst = result;
55
+ for (let i = 0; i < segs.length - 1; i++) {
56
+ const s = segs[i];
57
+ if (!(s in dst) ||
58
+ typeof dst[s] !== "object" ||
59
+ Array.isArray(dst[s])) {
60
+ dst[s] = {};
61
+ }
62
+ dst = dst[s];
63
+ }
64
+ dst[segs[segs.length - 1]] = src;
78
65
  }
79
66
  }
80
67
  obj = result;
@@ -11,15 +11,24 @@ export function updateObjectAdvanced(obj, field) {
11
11
  return;
12
12
  const $fields = {};
13
13
  const subsetFields = {};
14
- Object.keys(field).forEach(key => {
15
- if (key.startsWith("$"))
14
+ let hasDollar = false;
15
+ let hasSubset = false;
16
+ for (const key in field) {
17
+ if (key.charCodeAt(0) === 36) {
16
18
  $fields[key.toLowerCase()] = field[key];
17
- else
19
+ hasDollar = true;
20
+ }
21
+ else {
18
22
  subsetFields[key] = field[key];
19
- });
20
- mainUpdate(obj, $fields);
21
- const mergedObj = deepMerge(obj, $fields);
22
- updateObject(mergedObj, subsetFields);
23
+ hasSubset = true;
24
+ }
25
+ }
26
+ if (hasDollar) {
27
+ mainUpdate(obj, $fields);
28
+ deepMerge(obj, $fields);
29
+ }
30
+ if (hasSubset)
31
+ updateObject(obj, subsetFields);
23
32
  }
24
33
  function deepMerge(obj, fields) {
25
34
  if (!("$deepmerge" in fields))
@@ -68,7 +77,10 @@ function mainUpdate(obj, fields) {
68
77
  },
69
78
  merge: (item, updater) => {
70
79
  if (Array.isArray(item) && Array.isArray(updater)) {
71
- return [...item, ...updater];
80
+ return [
81
+ ...item,
82
+ ...updater,
83
+ ];
72
84
  }
73
85
  if (typeof item === "object" &&
74
86
  typeof updater === "object" &&
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.12.0";
1
+ export const version = "0.12.2-alpha.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/db-core",
3
- "version": "0.12.0",
3
+ "version": "0.12.2-alpha.0",
4
4
  "types": "dist/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "author": "wxn0brP",