@wxn0brp/db-core 0.12.4-alpha.0 → 0.12.4-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.
@@ -0,0 +1,30 @@
1
+ import { Collection } from "../helpers/collection.js";
2
+ import { Data } from "../types/data.js";
3
+ import { VQueryT } from "../types/query.js";
4
+ import { TransactionHandle } from "../types/transaction.js";
5
+ import { ValtheraClass } from "./valthera.js";
6
+ /**
7
+ * Transaction context. Operations are executed directly through the parent
8
+ * database with the transaction handle passed to the executor.
9
+ */
10
+ export declare class Transaction {
11
+ readonly handle: TransactionHandle;
12
+ _parent: ValtheraClass;
13
+ constructor(handle: TransactionHandle, _parent: ValtheraClass);
14
+ c<T = Data>(name: string): Collection<T>;
15
+ add<T = Data>(q: VQueryT.Add<T>): Promise<T>;
16
+ find<T = Data>(q: VQueryT.Find<T, true>): Promise<T[]>;
17
+ findOne<T = Data>(q: VQueryT.FindOne<T>): Promise<T>;
18
+ update<T = Data>(q: VQueryT.Update<T>): Promise<T[]>;
19
+ updateOne<T = Data>(q: VQueryT.Update<T>): Promise<T>;
20
+ remove<T = Data>(q: VQueryT.Remove<T>): Promise<T[]>;
21
+ removeOne<T = Data>(q: VQueryT.Remove<T>): Promise<T>;
22
+ updateOneOrAdd<T = Data>(q: VQueryT.UpdateOneOrAdd<T>): Promise<VQueryT.UpdateOneOrAddResult<T>>;
23
+ toggleOne<T = Data>(q: VQueryT.ToggleOne<T>): Promise<VQueryT.ToggleOneResult<T>>;
24
+ ensureCollection(collection: string): Promise<boolean>;
25
+ issetCollection(collection: string): Promise<boolean>;
26
+ getCollections(): Promise<string[]>;
27
+ removeCollection(collection: string): Promise<boolean>;
28
+ commit(): Promise<void>;
29
+ rollback(): Promise<void>;
30
+ }
@@ -0,0 +1,71 @@
1
+ import { Collection } from "../helpers/collection.js";
2
+ import { applyAddDefaults, applyFindDefaults, applyFindOneDefaults, applyRemoveDefaults, applyToggleOneDefaults, applyUpdateDefaults, applyUpdateOneOrAddDefaults, } from "../helpers/queryDefaults.js";
3
+ /**
4
+ * Transaction context. Operations are executed directly through the parent
5
+ * database with the transaction handle passed to the executor.
6
+ */
7
+ export class Transaction {
8
+ handle;
9
+ _parent;
10
+ constructor(handle, _parent) {
11
+ this.handle = handle;
12
+ this._parent = _parent;
13
+ }
14
+ c(name) {
15
+ return new Collection(this, name);
16
+ }
17
+ add(q) {
18
+ applyAddDefaults(q);
19
+ return this._parent.execute("add", q, this.handle);
20
+ }
21
+ find(q) {
22
+ applyFindDefaults(q);
23
+ return this._parent.execute("find", q, this.handle);
24
+ }
25
+ findOne(q) {
26
+ applyFindOneDefaults(q);
27
+ return this._parent.execute("findOne", q, this.handle);
28
+ }
29
+ update(q) {
30
+ applyUpdateDefaults(q);
31
+ return this._parent.execute("update", q, this.handle);
32
+ }
33
+ updateOne(q) {
34
+ applyUpdateDefaults(q);
35
+ return this._parent.execute("updateOne", q, this.handle);
36
+ }
37
+ remove(q) {
38
+ applyRemoveDefaults(q);
39
+ return this._parent.execute("remove", q, this.handle);
40
+ }
41
+ removeOne(q) {
42
+ applyRemoveDefaults(q);
43
+ return this._parent.execute("removeOne", q, this.handle);
44
+ }
45
+ updateOneOrAdd(q) {
46
+ applyUpdateOneOrAddDefaults(q);
47
+ return this._parent.execute("updateOneOrAdd", q, this.handle);
48
+ }
49
+ toggleOne(q) {
50
+ applyToggleOneDefaults(q);
51
+ return this._parent.execute("toggleOne", q, this.handle);
52
+ }
53
+ ensureCollection(collection) {
54
+ return this._parent.adapter.ensureCollection(collection);
55
+ }
56
+ issetCollection(collection) {
57
+ return this._parent.adapter.issetCollection(collection);
58
+ }
59
+ getCollections() {
60
+ return this._parent.adapter.getCollections();
61
+ }
62
+ removeCollection(collection) {
63
+ return this._parent.adapter.removeCollection(collection);
64
+ }
65
+ async commit() {
66
+ await this._parent.adapter.commitTransaction(this.handle);
67
+ }
68
+ async rollback() {
69
+ await this._parent.adapter.rollbackTransaction(this.handle);
70
+ }
71
+ }
@@ -8,6 +8,7 @@ import { ValtheraPlugin } from "../types/plugin.js";
8
8
  import { VQuery, VQueryT } from "../types/query.js";
9
9
  import { TransactionHandle } from "../types/transaction.js";
10
10
  import { ValtheraCompatible } from "../types/valthera.js";
11
+ import { Transaction } from "./transaction.js";
11
12
  /**
12
13
  * Represents a database management class for performing CRUD operations.
13
14
  * @class
@@ -17,19 +18,18 @@ export declare class ValtheraClass implements ValtheraCompatible {
17
18
  adapter: ActionsBase;
18
19
  executor: ExecutorInterface;
19
20
  emitter: VEE<{
20
- [K in keyof ValtheraCompatible]: (query: VQuery, result: Awaited<ReturnType<ValtheraCompatible[K]>>) => void;
21
+ [K in keyof Omit<ValtheraCompatible, "c">]: (query: VQuery, result: Awaited<ReturnType<ValtheraCompatible[K]>>) => void;
21
22
  } & {
22
- "*": (name: keyof ValtheraCompatible, query: VQuery, result: any) => void;
23
+ "*": (name: keyof Omit<ValtheraCompatible, "c">, query: VQuery, result: any) => void;
23
24
  }>;
24
25
  version: string;
25
26
  _plugins: ValtheraPlugin[];
26
27
  _collections: Map<string, Collection<any>>;
27
- _activeTx: TransactionHandle | null;
28
28
  plugin(p: ValtheraPlugin): () => void;
29
29
  constructor(options: DbOpts);
30
30
  init(...args: any[]): Promise<any>;
31
31
  close(...args: any[]): Promise<any>;
32
- execute<T>(name: keyof ValtheraCompatible, query: VQuery<any> | string): Promise<T>;
32
+ execute<T>(name: keyof ValtheraCompatible, query: VQuery<any> | string, txHandle?: TransactionHandle): Promise<T>;
33
33
  /**
34
34
  * Create a new instance of a Collection class.
35
35
  */
@@ -91,12 +91,14 @@ export declare class ValtheraClass implements ValtheraCompatible {
91
91
  *
92
92
  * Executes operations within a transaction. Automatically rolls back on error.
93
93
  *
94
+ * Receives a transaction-scoped object (`tx`) whose operations are executed
95
+ * within the transaction context.
96
+ *
94
97
  * This feature is highly experimental and may change or be removed at any time.
95
98
  *
96
- * - Requires `executorAware: false` in ValtheraClass options.
97
- * - Transaction support depends on the executor implementation.
98
- * - Nested transactions are not supported.
99
- * - Transactions lock the whole database for the duration of the transaction.
99
+ * - Transaction support depends on the adapter implementation.
100
+ * - Collections must be declared upfront to ensure proper locking.
101
+ * - Operations invoked via the original `db` reference are NOT part of the transaction.
100
102
  */
101
- transaction<T>(fn: (handle: TransactionHandle) => Promise<T>): Promise<T>;
103
+ transaction<T>(collections: string[], fn: (tx: Transaction) => Promise<T>): Promise<T>;
102
104
  }
@@ -2,7 +2,9 @@ import { VEE } from "@wxn0brp/event-emitter";
2
2
  import { Collection } from "../helpers/collection.js";
3
3
  import { SmartExecutor } from "../helpers/executor.js";
4
4
  import { genId } from "../helpers/gen.js";
5
+ import { applyAddDefaults, applyFindDefaults, applyFindOneDefaults, applyRemoveDefaults, applyToggleOneDefaults, applyUpdateDefaults, applyUpdateOneOrAddDefaults, } from "../helpers/queryDefaults.js";
5
6
  import { version } from "../version.js";
7
+ import { Transaction } from "./transaction.js";
6
8
  /**
7
9
  * Represents a database management class for performing CRUD operations.
8
10
  * @class
@@ -15,7 +17,6 @@ export class ValtheraClass {
15
17
  version = version;
16
18
  _plugins = [];
17
19
  _collections = new Map();
18
- _activeTx = null;
19
20
  plugin(p) {
20
21
  p.init?.(this);
21
22
  this._plugins.push(p);
@@ -74,12 +75,10 @@ export class ValtheraClass {
74
75
  self.adapter._inited = false;
75
76
  });
76
77
  }
77
- async execute(name, query) {
78
+ async execute(name, query, txHandle) {
78
79
  await this.init();
79
- const isTransaction = this._activeTx && typeof query !== "string";
80
- if (isTransaction) {
81
- query.transaction = this._activeTx;
82
- }
80
+ if (txHandle && typeof query === "object")
81
+ query.transaction = txHandle;
83
82
  const plugins = this._plugins;
84
83
  const self = this;
85
84
  let idx = 0;
@@ -89,8 +88,8 @@ export class ValtheraClass {
89
88
  next: async () => {
90
89
  if (idx < plugins.length)
91
90
  return plugins[idx++].execute(ctx);
92
- if (isTransaction)
93
- return self.adapter[name](query);
91
+ if (txHandle)
92
+ return self.adapter[ctx.op](query, txHandle);
94
93
  return self.executor.addOp(self.adapter[ctx.op].bind(self.adapter), ctx.query, typeof ctx.query === "string" ? ctx.query : ctx.query.collection);
95
94
  },
96
95
  };
@@ -130,79 +129,63 @@ export class ValtheraClass {
130
129
  * Add data to a database.
131
130
  */
132
131
  add(query) {
133
- query.control ||= {};
134
- query.id_gen ??= true;
132
+ applyAddDefaults(query);
135
133
  return this.execute("add", query);
136
134
  }
137
135
  /**
138
136
  * Find data in a database.
139
137
  */
140
138
  find(query) {
141
- query.search ||= {};
142
- query.dbFindOpts ||= {};
143
- query.findOpts ||= {};
144
- query.context ||= {};
145
- query.control ||= {};
139
+ applyFindDefaults(query);
146
140
  return this.execute("find", query);
147
141
  }
148
142
  /**
149
143
  * Find one data entry in a database.
150
144
  */
151
145
  findOne(query) {
152
- query.findOpts ||= {};
153
- query.context ||= {};
154
- query.control ||= {};
146
+ applyFindOneDefaults(query);
155
147
  return this.execute("findOne", query);
156
148
  }
157
149
  /**
158
150
  * Update data in a database.
159
151
  */
160
152
  update(query) {
161
- query.context ||= {};
162
- query.control ||= {};
153
+ applyUpdateDefaults(query);
163
154
  return this.execute("update", query);
164
155
  }
165
156
  /**
166
157
  * Update one data entry in a database.
167
158
  */
168
159
  updateOne(query) {
169
- query.context ||= {};
170
- query.control ||= {};
160
+ applyUpdateDefaults(query);
171
161
  return this.execute("updateOne", query);
172
162
  }
173
163
  /**
174
164
  * Remove data from a database.
175
165
  */
176
166
  remove(query) {
177
- query.context ||= {};
178
- query.control ||= {};
167
+ applyRemoveDefaults(query);
179
168
  return this.execute("remove", query);
180
169
  }
181
170
  /**
182
171
  * Remove one data entry from a database.
183
172
  */
184
173
  removeOne(query) {
185
- query.context ||= {};
186
- query.control ||= {};
174
+ applyRemoveDefaults(query);
187
175
  return this.execute("removeOne", query);
188
176
  }
189
177
  /**
190
178
  * Asynchronously updates one entry in a database or adds a new one if it doesn't exist.
191
179
  */
192
180
  updateOneOrAdd(query) {
193
- query.context ||= {};
194
- query.add_arg ||= {};
195
- query.control ||= {};
196
- query.id_gen ??= true;
181
+ applyUpdateOneOrAddDefaults(query);
197
182
  return this.execute("updateOneOrAdd", query);
198
183
  }
199
184
  /**
200
185
  * Asynchronously removes one entry in a database or adds a new one if it doesn't exist. Usage e.g. for toggling a flag.
201
186
  */
202
187
  toggleOne(query) {
203
- query.data ||= {};
204
- query.context ||= {};
205
- query.control ||= {};
188
+ applyToggleOneDefaults(query);
206
189
  return this.execute("toggleOne", query);
207
190
  }
208
191
  /**
@@ -217,47 +200,49 @@ export class ValtheraClass {
217
200
  *
218
201
  * Executes operations within a transaction. Automatically rolls back on error.
219
202
  *
203
+ * Receives a transaction-scoped object (`tx`) whose operations are executed
204
+ * within the transaction context.
205
+ *
220
206
  * This feature is highly experimental and may change or be removed at any time.
221
207
  *
222
- * - Requires `executorAware: false` in ValtheraClass options.
223
- * - Transaction support depends on the executor implementation.
224
- * - Nested transactions are not supported.
225
- * - Transactions lock the whole database for the duration of the transaction.
208
+ * - Transaction support depends on the adapter implementation.
209
+ * - Collections must be declared upfront to ensure proper locking.
210
+ * - Operations invoked via the original `db` reference are NOT part of the transaction.
226
211
  */
227
- async transaction(fn) {
212
+ async transaction(collections, fn) {
228
213
  await this.init();
229
- if ("aware" in this.executor && this.executor.aware) {
230
- throw new Error("Transactions are not supported when using a smart executor. " +
231
- "Please use options.executorAware = false when creating the Valthera instance.");
214
+ const releases = [];
215
+ const promises = collections.map(collection => {
216
+ let started;
217
+ const startedPromise = new Promise(r => (started = r));
218
+ this.executor.addOp(async () => {
219
+ started();
220
+ await new Promise(resolve => releases.push(resolve));
221
+ }, undefined, collection);
222
+ return startedPromise;
223
+ });
224
+ await Promise.all(promises);
225
+ const handle = await this.adapter.beginTransaction(genId());
226
+ const tx = new Transaction(handle, this);
227
+ try {
228
+ const result = await fn(tx);
229
+ await tx.commit();
230
+ return result;
232
231
  }
233
- if (this._activeTx)
234
- throw new Error("Nested transactions are not supported");
235
- const self = this;
236
- return this.executor.addOp(async () => {
237
- if (self._activeTx)
238
- throw new Error("Nested transactions are not supported");
239
- const handle = await self.adapter.beginTransaction(genId());
240
- self._activeTx = handle;
232
+ catch (err) {
241
233
  try {
242
- const result = await fn(handle);
243
- await self.adapter.commitTransaction(handle);
244
- return result;
234
+ await tx.rollback();
245
235
  }
246
- catch (err) {
247
- try {
248
- await self.adapter.rollbackTransaction(handle);
249
- }
250
- catch (rollbackErr) {
251
- throw new AggregateError([
252
- err,
253
- rollbackErr,
254
- ], "Transaction failed and rollback failed");
255
- }
256
- throw err;
236
+ catch (rollbackErr) {
237
+ throw new AggregateError([
238
+ err,
239
+ rollbackErr,
240
+ ], "Transaction failed and rollback failed");
257
241
  }
258
- finally {
259
- self._activeTx = null;
260
- }
261
- });
242
+ throw err;
243
+ }
244
+ finally {
245
+ releases.forEach(release => release());
246
+ }
262
247
  }
263
248
  }
@@ -11,11 +11,13 @@ export async function addId(query, actions, defaultGen = true) {
11
11
  data[idKey] = genId();
12
12
  return;
13
13
  }
14
+ const txOpts = query.transaction ? { transaction: query.transaction } : {};
14
15
  const find = (await actions.findOne({
15
16
  collection: "__vdb_id",
16
17
  search: {
17
18
  c: collection,
18
19
  },
20
+ ...txOpts,
19
21
  }));
20
22
  data[idKey] = find?.i ? find.i + 1 : 1;
21
23
  await actions.updateOneOrAdd({
@@ -29,5 +31,6 @@ export async function addId(query, actions, defaultGen = true) {
29
31
  },
30
32
  },
31
33
  id_gen: false,
34
+ ...txOpts,
32
35
  });
33
36
  }
@@ -0,0 +1,8 @@
1
+ import { VQueryT } from "../types/query.js";
2
+ export declare function applyAddDefaults<T>(q: VQueryT.Add<T>): void;
3
+ export declare function applyFindDefaults<T>(q: VQueryT.Find<T>): void;
4
+ export declare function applyFindOneDefaults<T>(q: VQueryT.FindOne<T>): void;
5
+ export declare function applyUpdateDefaults<T>(q: VQueryT.Update<T>): void;
6
+ export declare function applyRemoveDefaults<T>(q: VQueryT.Remove<T>): void;
7
+ export declare function applyUpdateOneOrAddDefaults<T>(q: VQueryT.UpdateOneOrAdd<T>): void;
8
+ export declare function applyToggleOneDefaults<T>(q: VQueryT.ToggleOne<T>): void;
@@ -0,0 +1,35 @@
1
+ export function applyAddDefaults(q) {
2
+ q.control ||= {};
3
+ q.id_gen ??= true;
4
+ }
5
+ export function applyFindDefaults(q) {
6
+ q.search ||= {};
7
+ q.dbFindOpts ||= {};
8
+ q.findOpts ||= {};
9
+ q.context ||= {};
10
+ q.control ||= {};
11
+ }
12
+ export function applyFindOneDefaults(q) {
13
+ q.findOpts ||= {};
14
+ q.context ||= {};
15
+ q.control ||= {};
16
+ }
17
+ export function applyUpdateDefaults(q) {
18
+ q.context ||= {};
19
+ q.control ||= {};
20
+ }
21
+ export function applyRemoveDefaults(q) {
22
+ q.context ||= {};
23
+ q.control ||= {};
24
+ }
25
+ export function applyUpdateOneOrAddDefaults(q) {
26
+ q.context ||= {};
27
+ q.add_arg ||= {};
28
+ q.control ||= {};
29
+ q.id_gen ??= true;
30
+ }
31
+ export function applyToggleOneDefaults(q) {
32
+ q.data ||= {};
33
+ q.context ||= {};
34
+ q.control ||= {};
35
+ }
@@ -1,10 +1,10 @@
1
1
  import { ActionsBase } from "../base/actions.js";
2
- import { Executor } from "../helpers/executor.js";
2
+ import { ExecutorInterface } from "../helpers/executor.js";
3
3
  import { KeysMatching } from "./utils.js";
4
4
  export type AdapterValue = ActionsBase | (() => Promise<ActionsBase>);
5
5
  export interface DbOpts {
6
6
  adapter: AdapterValue;
7
- executor?: Executor;
7
+ executor?: ExecutorInterface;
8
8
  executorAware?: boolean;
9
9
  adapterOpts?: AdapterOpts;
10
10
  }
@@ -32,6 +32,7 @@ export declare namespace VQueryT {
32
32
  type QueryBase<T = Data> = {
33
33
  collection: string;
34
34
  control?: VQuery_Control;
35
+ transaction?: TransactionHandle;
35
36
  };
36
37
  type Add<T = Data> = QueryBase<T> & {
37
38
  data: Arg<T>;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.12.4-alpha.0";
1
+ export declare const version = "0.12.4-alpha.1";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.12.4-alpha.0";
1
+ export const version = "0.12.4-alpha.1";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/db-core",
3
- "version": "0.12.4-alpha.0",
3
+ "version": "0.12.4-alpha.1",
4
4
  "types": "dist/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "author": "wxn0brP",