@wxn0brp/db 0.4.0 → 0.4.2

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/action.d.ts CHANGED
@@ -4,6 +4,7 @@ import { Context } from "./types/types.js";
4
4
  import { SearchOptions } from "./types/searchOpts.js";
5
5
  import Data from "./types/data.js";
6
6
  import FileCpu from "./types/fileCpu.js";
7
+ import { Transaction } from "./types/transactions.js";
7
8
  /**
8
9
  * A class representing database actions on files.
9
10
  * @class
@@ -64,5 +65,9 @@ declare class dbActionC {
64
65
  * Removes a database collection from the file system.
65
66
  */
66
67
  removeCollection(collection: string): Promise<void>;
68
+ /**
69
+ * Apply a series of transactions to a database collection.
70
+ */
71
+ transaction(collection: string, transactions: Transaction[]): Promise<void>;
67
72
  }
68
73
  export default dbActionC;
package/dist/action.js CHANGED
@@ -156,6 +156,23 @@ class dbActionC {
156
156
  async removeCollection(collection) {
157
157
  await promises.rm(this.folder + "/" + collection, { recursive: true, force: true });
158
158
  }
159
+ /**
160
+ * Apply a series of transactions to a database collection.
161
+ */
162
+ async transaction(collection, transactions) {
163
+ await this.checkCollection(collection);
164
+ const files = await getSortedFiles(this._getCollectionPath(collection));
165
+ if (files.length == 0) {
166
+ await promises.writeFile(this._getCollectionPath(collection) + "1.db", "");
167
+ files.push("1.db");
168
+ }
169
+ for (const file of files) {
170
+ await this.fileCpu.transactions(this._getCollectionPath(collection) + file, transactions);
171
+ }
172
+ console.log("Transactions applied successfully.");
173
+ console.log("Files:", files);
174
+ console.log("Transactions:", transactions);
175
+ }
159
176
  }
160
177
  /**
161
178
  * Get the last file in the specified directory.
@@ -196,9 +213,9 @@ async function operationUpdater(cpath, worker, one, ...args) {
196
213
  let update = false;
197
214
  for (const file of files) {
198
215
  const updated = await worker(cpath + file, one, ...args);
216
+ update = update || updated;
199
217
  if (one && updated)
200
218
  break;
201
- update = update || updated;
202
219
  }
203
220
  return update;
204
221
  }
@@ -4,6 +4,7 @@ import { Arg, Search, Updater } from "../types/arg.js";
4
4
  import { DbFindOpts, FindOpts } from "../types/options.js";
5
5
  import { Context } from "../types/types.js";
6
6
  import Data from "../types/data.js";
7
+ import { Transaction } from "../types/transactions.js";
7
8
  /**
8
9
  * Represents a database management class for performing CRUD operations.
9
10
  * Uses a remote database.
@@ -68,5 +69,9 @@ declare class DataBaseRemote {
68
69
  * Removes a database collection from the file system.
69
70
  */
70
71
  removeCollection(name: string): Promise<boolean>;
72
+ /**
73
+ * Execute a transaction.
74
+ */
75
+ transaction(collection: string, transaction: Transaction[]): Promise<boolean>;
71
76
  }
72
77
  export default DataBaseRemote;
@@ -110,5 +110,11 @@ class DataBaseRemote {
110
110
  async removeCollection(name) {
111
111
  return await this._request("removeCollection", [name]);
112
112
  }
113
+ /**
114
+ * Execute a transaction.
115
+ */
116
+ async transaction(collection, transaction) {
117
+ return await this._request("transaction", [collection, transaction]);
118
+ }
113
119
  }
114
120
  export default DataBaseRemote;
@@ -6,6 +6,8 @@ import { Arg, Search, Updater } from "./types/arg.js";
6
6
  import Data from "./types/data.js";
7
7
  import { Context } from "./types/types.js";
8
8
  import FileCpu from "./types/fileCpu.js";
9
+ import { Transaction } from "./types/transactions.js";
10
+ import EventEmitter from "events";
9
11
  /**
10
12
  * Represents a database management class for performing CRUD operations.
11
13
  * @class
@@ -13,7 +15,9 @@ import FileCpu from "./types/fileCpu.js";
13
15
  declare class DataBase {
14
16
  dbAction: dbActionC;
15
17
  executor: executorC;
18
+ emiter: EventEmitter;
16
19
  constructor(folder: string, options?: DbOpts, fileCpu?: FileCpu);
20
+ private execute;
17
21
  /**
18
22
  * Create a new instance of a CollectionManager class.
19
23
  */
@@ -25,7 +29,7 @@ declare class DataBase {
25
29
  /**
26
30
  * Check and create the specified collection if it doesn't exist.
27
31
  */
28
- checkCollection(collection: string): Promise<void>;
32
+ checkCollection(collection: string): Promise<boolean>;
29
33
  /**
30
34
  * Check if a collection exists.
31
35
  */
@@ -65,6 +69,10 @@ declare class DataBase {
65
69
  /**
66
70
  * Removes a database collection from the file system.
67
71
  */
68
- removeCollection(collection: string): Promise<void>;
72
+ removeCollection(collection: string): Promise<boolean>;
73
+ /**
74
+ * Execute a transaction.
75
+ */
76
+ transaction(collection: string, transaction: Transaction[]): Promise<boolean>;
69
77
  }
70
78
  export default DataBase;
package/dist/database.js CHANGED
@@ -2,6 +2,7 @@ import dbActionC from "./action.js";
2
2
  import executorC from "./executor.js";
3
3
  import CollectionManager from "./CollectionManager.js";
4
4
  import vFileCpu from "./file/index.js";
5
+ import EventEmitter from "events";
5
6
  /**
6
7
  * Represents a database management class for performing CRUD operations.
7
8
  * @class
@@ -9,11 +10,21 @@ import vFileCpu from "./file/index.js";
9
10
  class DataBase {
10
11
  dbAction;
11
12
  executor;
13
+ emiter;
12
14
  constructor(folder, options = {}, fileCpu) {
13
15
  if (!fileCpu)
14
16
  fileCpu = vFileCpu;
15
17
  this.dbAction = options.dbAction || new dbActionC(folder, options, fileCpu);
16
18
  this.executor = options.executor || new executorC();
19
+ this.emiter = new EventEmitter();
20
+ }
21
+ async execute(name, ...args) {
22
+ const result = await this.executor.addOp(this.dbAction[name].bind(this.dbAction), ...args);
23
+ if (this.emiter.listeners(name).length !== 0)
24
+ this.emiter.emit(name, args, result);
25
+ if (this.emiter.listeners("*").length !== 0)
26
+ this.emiter.emit("*", name, args, result);
27
+ return result;
17
28
  }
18
29
  /**
19
30
  * Create a new instance of a CollectionManager class.
@@ -25,61 +36,61 @@ class DataBase {
25
36
  * Get the names of all available databases.
26
37
  */
27
38
  async getCollections() {
28
- return await this.dbAction.getCollections();
39
+ return await this.execute("getCollections");
29
40
  }
30
41
  /**
31
42
  * Check and create the specified collection if it doesn't exist.
32
43
  */
33
44
  async checkCollection(collection) {
34
- await this.dbAction.checkCollection(collection);
45
+ return await this.execute("checkCollection", collection);
35
46
  }
36
47
  /**
37
48
  * Check if a collection exists.
38
49
  */
39
50
  async issetCollection(collection) {
40
- return await this.dbAction.issetCollection(collection);
51
+ return await this.execute("issetCollection", collection);
41
52
  }
42
53
  /**
43
54
  * Add data to a database.
44
55
  */
45
56
  async add(collection, data, id_gen = true) {
46
- return await this.executor.addOp(this.dbAction.add.bind(this.dbAction), collection, data, id_gen);
57
+ return await this.execute("add", collection, data, id_gen);
47
58
  }
48
59
  /**
49
60
  * Find data in a database.
50
61
  */
51
62
  async find(collection, search, context = {}, options = {}, findOpts = {}) {
52
- return await this.executor.addOp(this.dbAction.find.bind(this.dbAction), collection, search, context, options, findOpts);
63
+ return await this.execute("find", collection, search, context, options, findOpts);
53
64
  }
54
65
  /**
55
66
  * Find one data entry in a database.
56
67
  */
57
68
  async findOne(collection, search, context = {}, findOpts = {}) {
58
- return await this.executor.addOp(this.dbAction.findOne.bind(this.dbAction), collection, search, context, findOpts);
69
+ return await this.execute("findOne", collection, search, context, findOpts);
59
70
  }
60
71
  /**
61
72
  * Update data in a database.
62
73
  */
63
74
  async update(collection, search, updater, context = {}) {
64
- return await this.executor.addOp(this.dbAction.update.bind(this.dbAction), collection, search, updater, context);
75
+ return await this.execute("update", collection, search, updater, context);
65
76
  }
66
77
  /**
67
78
  * Update one data entry in a database.
68
79
  */
69
80
  async updateOne(collection, search, updater, context = {}) {
70
- return await this.executor.addOp(this.dbAction.updateOne.bind(this.dbAction), collection, search, updater, context);
81
+ return await this.execute("updateOne", collection, search, updater, context);
71
82
  }
72
83
  /**
73
84
  * Remove data from a database.
74
85
  */
75
86
  async remove(collection, search, context = {}) {
76
- return await this.executor.addOp(this.dbAction.remove.bind(this.dbAction), collection, search, context);
87
+ return await this.execute("remove", collection, search, context);
77
88
  }
78
89
  /**
79
90
  * Remove one data entry from a database.
80
91
  */
81
92
  async removeOne(collection, search, context = {}) {
82
- return await this.executor.addOp(this.dbAction.removeOne.bind(this.dbAction), collection, search, context);
93
+ return await this.execute("removeOne", collection, search, context);
83
94
  }
84
95
  /**
85
96
  * Asynchronously updates one entry in a database or adds a new one if it doesn't exist.
@@ -88,12 +99,24 @@ class DataBase {
88
99
  const res = await this.updateOne(collection, search, updater, context);
89
100
  if (!res) {
90
101
  const assignData = [];
91
- if (typeof search === "object" && !Array.isArray(search))
92
- assignData.push(search);
93
- if (typeof updater === "object" && !Array.isArray(updater))
94
- assignData.push(updater);
95
- if (typeof add_arg === "object" && !Array.isArray(add_arg))
96
- assignData.push(add_arg);
102
+ function assignDataPush(data) {
103
+ if (typeof data !== "object" || Array.isArray(data))
104
+ return;
105
+ const obj = {};
106
+ for (const key of Object.keys(data)) {
107
+ if (key.startsWith("$")) {
108
+ Object.keys(data[key]).forEach((k) => {
109
+ obj[k] = data[key][k];
110
+ });
111
+ }
112
+ else
113
+ obj[key] = data[key];
114
+ }
115
+ assignData.push(obj);
116
+ }
117
+ assignDataPush(search);
118
+ assignDataPush(updater);
119
+ assignDataPush(add_arg);
97
120
  await this.add(collection, Object.assign({}, ...assignData), id_gen);
98
121
  }
99
122
  return res;
@@ -102,7 +125,13 @@ class DataBase {
102
125
  * Removes a database collection from the file system.
103
126
  */
104
127
  async removeCollection(collection) {
105
- await this.dbAction.removeCollection(collection);
128
+ return await this.execute("removeCollection", collection);
129
+ }
130
+ /**
131
+ * Execute a transaction.
132
+ */
133
+ async transaction(collection, transaction) {
134
+ return await this.execute("transaction", collection, transaction);
106
135
  }
107
136
  }
108
137
  export default DataBase;
@@ -3,6 +3,7 @@ import { Search, Updater } from "../types/arg.js";
3
3
  import Data from "../types/data.js";
4
4
  import FileCpu from "../types/fileCpu.js";
5
5
  import { FindOpts } from "../types/options.js";
6
+ import { Transaction } from "../types/transactions.js";
6
7
  export type WriteFile = (file: string, data: any[]) => Promise<void>;
7
8
  export type ReadFile = (file: string) => Promise<any[]>;
8
9
  declare class CustomFileCpu implements FileCpu {
@@ -14,5 +15,6 @@ declare class CustomFileCpu implements FileCpu {
14
15
  findOne(file: string, arg: Search, context?: Context, findOpts?: FindOpts): Promise<any | false>;
15
16
  remove(file: string, one: boolean, arg: Search, context?: Context): Promise<boolean>;
16
17
  update(file: string, one: boolean, arg: Search, updater: Updater, context?: Context): Promise<boolean>;
18
+ transactions(file: string, transactions: Transaction[]): Promise<void>;
17
19
  }
18
20
  export default CustomFileCpu;
@@ -64,5 +64,8 @@ class CustomFileCpu {
64
64
  await this._writeFile(file, entries);
65
65
  return true;
66
66
  }
67
+ transactions(file, transactions) {
68
+ throw new Error("Method not implemented.");
69
+ }
67
70
  }
68
71
  export default CustomFileCpu;
@@ -3,6 +3,7 @@ import remove from "./remove.js";
3
3
  import { find, findOne } from "./find.js";
4
4
  import { appendFileSync } from "fs";
5
5
  import { stringify } from "../format.js";
6
+ import transactions from "./transactions.js";
6
7
  const vFileCpu = {
7
8
  add: async (file, data) => {
8
9
  const dataString = stringify(data);
@@ -11,6 +12,7 @@ const vFileCpu = {
11
12
  find,
12
13
  findOne,
13
14
  update,
14
- remove
15
+ remove,
16
+ transactions
15
17
  };
16
18
  export default vFileCpu;
@@ -0,0 +1,3 @@
1
+ import { Transaction } from "../types/transactions.js";
2
+ declare function processTransactions(file: string, transactions: Transaction[]): Promise<void>;
3
+ export default processTransactions;
@@ -0,0 +1,119 @@
1
+ import { existsSync, promises } from "fs";
2
+ import { parse, stringify } from "../format.js";
3
+ import genId from "../gen.js";
4
+ import hasFieldsAdvanced from "../utils/hasFieldsAdvanced.js";
5
+ import updateObjectAdvanced from "../utils/updateObject.js";
6
+ import { createRL, pathRepair } from "./utils.js";
7
+ async function processTransactions(file, transactions) {
8
+ file = pathRepair(file);
9
+ const tempFile = file + ".tmp";
10
+ const processedTransactions = transactions.map(t => ({
11
+ ...t,
12
+ applied: false
13
+ }));
14
+ if (existsSync(file)) {
15
+ await promises.copyFile(file, tempFile);
16
+ }
17
+ else {
18
+ await promises.writeFile(file, "");
19
+ await promises.writeFile(tempFile, "");
20
+ }
21
+ await promises.writeFile(file, "");
22
+ const rl = createRL(tempFile);
23
+ for await (const line of rl) {
24
+ const originalLine = line.trim();
25
+ if (!originalLine)
26
+ continue;
27
+ let data = parse(originalLine);
28
+ let shouldRemove = false;
29
+ let modified = false;
30
+ for (const transaction of processedTransactions) {
31
+ if (shouldRemove)
32
+ break;
33
+ let matches = false;
34
+ if (typeof transaction.search === 'function') {
35
+ matches = transaction.search(data, transaction.context || {}) || false;
36
+ }
37
+ else if (typeof transaction.search === 'object') {
38
+ matches = hasFieldsAdvanced(data, transaction.search);
39
+ }
40
+ if (!matches)
41
+ continue;
42
+ switch (transaction.type) {
43
+ case 'update':
44
+ if (transaction.updater) {
45
+ data = applyUpdater(data, transaction.updater, transaction.context || {});
46
+ modified = true;
47
+ }
48
+ break;
49
+ case 'updateOne':
50
+ if (!transaction.applied && transaction.updater) {
51
+ data = applyUpdater(data, transaction.updater, transaction.context || {});
52
+ modified = true;
53
+ transaction.applied = true;
54
+ }
55
+ break;
56
+ case 'updateOneOrAdd':
57
+ if (!transaction.applied && transaction.updater) {
58
+ data = applyUpdater(data, transaction.updater, transaction.context || {});
59
+ modified = true;
60
+ transaction.applied = true;
61
+ }
62
+ break;
63
+ case 'remove':
64
+ shouldRemove = true;
65
+ modified = true;
66
+ break;
67
+ case 'removeOne':
68
+ if (!transaction.applied) {
69
+ shouldRemove = true;
70
+ modified = true;
71
+ transaction.applied = true;
72
+ }
73
+ break;
74
+ }
75
+ }
76
+ if (!shouldRemove) {
77
+ const outputLine = modified ? await stringify(data) : originalLine;
78
+ await promises.appendFile(file, outputLine + "\n");
79
+ }
80
+ }
81
+ for (const transaction of processedTransactions) {
82
+ if (transaction.type === 'updateOneOrAdd' && !transaction.applied) {
83
+ const assignData = [];
84
+ if (typeof transaction.search === 'object' && !Array.isArray(transaction.search)) {
85
+ assignData.push(transaction.search);
86
+ }
87
+ if (transaction.updater && typeof transaction.updater === 'object' && !Array.isArray(transaction.updater)) {
88
+ const newData = {};
89
+ Object.keys(transaction.updater).filter(key => !key.startsWith('$')).forEach(key => newData[key] = transaction.updater[key]);
90
+ assignData.push(newData);
91
+ }
92
+ if (transaction.addArg && typeof transaction.addArg === 'object' && !Array.isArray(transaction.addArg)) {
93
+ assignData.push(transaction.addArg);
94
+ }
95
+ let newData = Object.assign({}, ...assignData);
96
+ if (transaction.updater && typeof transaction.updater === 'object' && !Array.isArray(transaction.updater)) {
97
+ newData = applyUpdater(newData, transaction.updater, transaction.context || {});
98
+ }
99
+ await add(file, newData, transaction.idGen !== false);
100
+ }
101
+ }
102
+ await promises.unlink(tempFile);
103
+ }
104
+ function applyUpdater(data, updater, context = {}) {
105
+ if (typeof updater === 'function') {
106
+ const result = updater(data, context);
107
+ return data === null ? result : (result || data);
108
+ }
109
+ if (typeof updater === 'object' && !Array.isArray(updater)) {
110
+ return updateObjectAdvanced(data || {}, updater);
111
+ }
112
+ return data;
113
+ }
114
+ async function add(file, data, idGen) {
115
+ if (idGen && !data._id)
116
+ data._id = genId();
117
+ await promises.appendFile(file, await stringify(data) + "\n");
118
+ }
119
+ export default processTransactions;
package/dist/memory.js CHANGED
@@ -114,6 +114,13 @@ class MemoryAction {
114
114
  async removeCollection(collection) {
115
115
  this.memory.delete(collection);
116
116
  }
117
+ /**
118
+ * Executes a list of transactions on the specified database collection.
119
+ * @throws Error - Method not supported in memory.
120
+ */
121
+ transaction(collection, transactions) {
122
+ throw new Error("Method not supported in memory.");
123
+ }
117
124
  }
118
125
  export default class ValtheraMemory extends DataBase {
119
126
  constructor(...args) {
@@ -1,6 +1,7 @@
1
1
  import { Search, Updater } from "./arg.js";
2
2
  import Data from "./data.js";
3
3
  import { FindOpts } from "./options.js";
4
+ import { Transaction } from "./transactions.js";
4
5
  import { Context } from "./types.js";
5
6
  interface FileCpu {
6
7
  /**
@@ -47,5 +48,12 @@ interface FileCpu {
47
48
  * @returns A promise resolving to `true` if at least one entry was updated, otherwise `false`.
48
49
  */
49
50
  update(file: string, one: boolean, arg: Search, updater: Updater, context?: Context): Promise<boolean>;
51
+ /**
52
+ * Executes a list of transactions on the specified database collection.
53
+ * @param file The path to the file.
54
+ * @param transactions An array of transactions to execute.
55
+ * @returns A promise resolved when all transactions have been executed.
56
+ */
57
+ transactions(file: string, transactions: Transaction[]): Promise<void>;
50
58
  }
51
59
  export default FileCpu;
@@ -0,0 +1,10 @@
1
+ import { Arg, Search, Updater } from "./arg.js";
2
+ import { Context } from "./types.js";
3
+ export interface Transaction {
4
+ type: 'update' | 'updateOne' | 'updateOneOrAdd' | 'remove' | 'removeOne';
5
+ search: Search;
6
+ updater?: Updater;
7
+ addArg?: Arg;
8
+ idGen?: boolean;
9
+ context?: Context;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/db",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "description": "A simple file-based database management system with support for CRUD operations, custom queries, and graph structures.",