@wxn0brp/db-core 0.12.9 → 0.12.10-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.
@@ -1,4 +1,4 @@
1
- import { ActionsBaseInterface } from "../types/action.js";
1
+ import { ActionsBaseInterface, AdapterCapabilities } from "../types/action.js";
2
2
  import { DataInternal } from "../types/data.js";
3
3
  import { Id } from "../types/Id.js";
4
4
  import { AdapterOpts } from "../types/options.js";
@@ -8,6 +8,7 @@ export declare abstract class ActionsBase implements ActionsBaseInterface {
8
8
  _inited: boolean;
9
9
  adapterOpts: AdapterOpts;
10
10
  smartExecutor: boolean;
11
+ capabilities: AdapterCapabilities;
11
12
  version: string;
12
13
  init(...args: any[]): Promise<void>;
13
14
  close(...args: any[]): Promise<void>;
@@ -1,3 +1,4 @@
1
+ import { UnsupportedOperationError } from "../errors.js";
1
2
  import { setDataForToggleOne, setDataForUpdateOneOrAdd, } from "../helpers/assignDataPush.js";
2
3
  import { version } from "../version.js";
3
4
  export class ActionsBase {
@@ -7,17 +8,22 @@ export class ActionsBase {
7
8
  idKey: "_id",
8
9
  };
9
10
  smartExecutor = false;
11
+ capabilities = {
12
+ transactions: false,
13
+ indexes: false,
14
+ persistent: true,
15
+ };
10
16
  version = "ActionsBase-core-" + version;
11
17
  async init(...args) { }
12
18
  async close(...args) { }
13
19
  async beginTransaction(id) {
14
- throw new Error("Transactions are not supported for ActionsBase");
20
+ throw new UnsupportedOperationError("beginTransaction");
15
21
  }
16
22
  async commitTransaction(handle) {
17
- throw new Error("Transactions are not supported for ActionsBase");
23
+ throw new UnsupportedOperationError("commitTransaction");
18
24
  }
19
25
  async rollbackTransaction(handle) {
20
- throw new Error("Transactions are not supported for ActionsBase");
26
+ throw new UnsupportedOperationError("rollbackTransaction");
21
27
  }
22
28
  async createIndex(config) {
23
29
  // No-op: adapter does not use indexes internally
@@ -6,6 +6,11 @@ import { ValtheraClass } from "./valthera.js";
6
6
  export declare class MemoryAction extends CustomActionsBase {
7
7
  memory: Map<string, any[]>;
8
8
  smartExecutor: boolean;
9
+ capabilities: {
10
+ transactions: boolean;
11
+ indexes: boolean;
12
+ persistent: boolean;
13
+ };
9
14
  constructor();
10
15
  _readMemory(key: string): any[];
11
16
  _writeMemory(key: string, data: any[]): void;
package/dist/db/memory.js CHANGED
@@ -6,6 +6,11 @@ import { ValtheraClass } from "./valthera.js";
6
6
  export class MemoryAction extends CustomActionsBase {
7
7
  memory;
8
8
  smartExecutor = true;
9
+ capabilities = {
10
+ transactions: false,
11
+ indexes: false,
12
+ persistent: false,
13
+ };
9
14
  constructor() {
10
15
  super();
11
16
  this.fileCpu = new CustomFileCpu(this._readMemory.bind(this), this._writeMemory.bind(this), true);
@@ -1,4 +1,5 @@
1
1
  import { ActionsBase } from "../base/actions.js";
2
+ import { UnsupportedOperationError } from "../errors.js";
2
3
  export class MultiBackend extends ActionsBase {
3
4
  backends;
4
5
  primaryBackend;
@@ -107,12 +108,18 @@ export class MultiBackend extends ActionsBase {
107
108
  return Array.from(uniqueCollections);
108
109
  }
109
110
  async beginTransaction() {
110
- throw new Error("Transactions are not supported for MultiBackend");
111
+ throw new UnsupportedOperationError("beginTransaction", {
112
+ adapter: "MultiBackend",
113
+ });
111
114
  }
112
115
  async commitTransaction() {
113
- throw new Error("Transactions are not supported for MultiBackend");
116
+ throw new UnsupportedOperationError("commitTransaction", {
117
+ adapter: "MultiBackend",
118
+ });
114
119
  }
115
120
  async rollbackTransaction() {
116
- throw new Error("Transactions are not supported for MultiBackend");
121
+ throw new UnsupportedOperationError("rollbackTransaction", {
122
+ adapter: "MultiBackend",
123
+ });
117
124
  }
118
125
  }
@@ -1,4 +1,5 @@
1
1
  import { ActionsBase } from "../base/actions.js";
2
+ import { UnsupportedOperationError } from "../errors.js";
2
3
  export class RoutedStorage extends ActionsBase {
3
4
  rules;
4
5
  defaultBackend;
@@ -110,12 +111,18 @@ export class RoutedStorage extends ActionsBase {
110
111
  return Array.from(merged);
111
112
  }
112
113
  async beginTransaction() {
113
- throw new Error("Transactions are not supported for RoutedStorage");
114
+ throw new UnsupportedOperationError("beginTransaction", {
115
+ adapter: "RoutedStorage",
116
+ });
114
117
  }
115
118
  async commitTransaction() {
116
- throw new Error("Transactions are not supported for RoutedStorage");
119
+ throw new UnsupportedOperationError("commitTransaction", {
120
+ adapter: "RoutedStorage",
121
+ });
117
122
  }
118
123
  async rollbackTransaction() {
119
- throw new Error("Transactions are not supported for RoutedStorage");
124
+ throw new UnsupportedOperationError("rollbackTransaction", {
125
+ adapter: "RoutedStorage",
126
+ });
120
127
  }
121
128
  }
@@ -13,7 +13,7 @@ export declare class Transaction {
13
13
  constructor(handle: TransactionHandle, _parent: ValtheraClass);
14
14
  c<T = Data>(name: string): Collection<T>;
15
15
  add<T = Data>(q: VQueryT.Add<T>): Promise<T>;
16
- bulkAdd<T = Data>(q: VQueryT.BulkAdd<T>): Promise<import("../types/data.js").DataInternal[]>;
16
+ bulkAdd<T = Data>(q: VQueryT.BulkAdd<T>): Promise<T[]>;
17
17
  find<T = Data>(q: VQueryT.Find<T, true>): Promise<T[]>;
18
18
  findOne<T = Data>(q: VQueryT.FindOne<T>): Promise<T | null>;
19
19
  update<T = Data>(q: VQueryT.Update<T>): Promise<T[]>;
@@ -1,3 +1,4 @@
1
+ import { wrapAdapterError } from "../errors.js";
1
2
  import { Collection } from "../helpers/collection.js";
2
3
  import { applyAddDefaults, applyBulkAddDefaults, applyCountDefaults, applyFindDefaults, applyFindOneDefaults, applyRemoveDefaults, applyToggleOneDefaults, applyUpdateDefaults, applyUpdateOneOrAddDefaults, } from "../helpers/queryDefaults.js";
3
4
  /**
@@ -20,7 +21,7 @@ export class Transaction {
20
21
  }
21
22
  bulkAdd(q) {
22
23
  applyBulkAddDefaults(q);
23
- return this._parent.adapter.bulkAdd(q);
24
+ return this._parent.execute("bulkAdd", q, this.handle);
24
25
  }
25
26
  find(q) {
26
27
  applyFindDefaults(q);
@@ -56,24 +57,40 @@ export class Transaction {
56
57
  }
57
58
  count(q) {
58
59
  applyCountDefaults(q);
59
- return this._parent.adapter.count(q);
60
+ return this._parent.execute("count", q, this.handle);
60
61
  }
61
62
  ensureCollection(collection) {
62
- return this._parent.adapter.ensureCollection(collection);
63
+ return this._parent.execute("ensureCollection", collection, this.handle);
63
64
  }
64
65
  issetCollection(collection) {
65
- return this._parent.adapter.issetCollection(collection);
66
+ return this._parent.execute("issetCollection", collection, this.handle);
66
67
  }
67
68
  getCollections() {
68
- return this._parent.adapter.getCollections();
69
+ return this._parent.execute("getCollections", {}, this.handle);
69
70
  }
70
71
  removeCollection(collection) {
71
- return this._parent.adapter.removeCollection(collection);
72
+ return this._parent.execute("removeCollection", collection, this.handle);
72
73
  }
73
74
  async commit() {
74
- await this._parent.adapter.commitTransaction(this.handle);
75
+ try {
76
+ await this._parent.adapter.commitTransaction(this.handle);
77
+ }
78
+ catch (err) {
79
+ throw wrapAdapterError(err, {
80
+ op: "commitTransaction",
81
+ adapter: this._parent.adapter?.version,
82
+ });
83
+ }
75
84
  }
76
85
  async rollback() {
77
- await this._parent.adapter.rollbackTransaction(this.handle);
86
+ try {
87
+ await this._parent.adapter.rollbackTransaction(this.handle);
88
+ }
89
+ catch (err) {
90
+ throw wrapAdapterError(err, {
91
+ op: "rollbackTransaction",
92
+ adapter: this._parent.adapter?.version,
93
+ });
94
+ }
78
95
  }
79
96
  }
@@ -2,6 +2,7 @@ import { VEE } from "@wxn0brp/event-emitter";
2
2
  import { ActionsBase } from "../base/actions.js";
3
3
  import { Collection } from "../helpers/collection.js";
4
4
  import { ExecutorInterface } from "../helpers/executor.js";
5
+ import { AdapterCapabilities } from "../types/action.js";
5
6
  import { Data } from "../types/data.js";
6
7
  import { IndexOpts } from "../types/idx.js";
7
8
  import { DbOpts } from "../types/options.js";
@@ -97,6 +98,10 @@ export declare class ValtheraClass implements ValtheraCompatible {
97
98
  */
98
99
  removeIndex(collection: string, fields: string[]): Promise<void>;
99
100
  count(query: VQueryT.Count): Promise<number>;
101
+ /**
102
+ * Describe what the underlying adapter supports.
103
+ */
104
+ capabilities(): Promise<AdapterCapabilities>;
100
105
  /**
101
106
  * @experimental
102
107
  *
@@ -1,8 +1,9 @@
1
1
  import { VEE } from "@wxn0brp/event-emitter";
2
+ import { AdapterCloseError, AdapterInitError, ErrorCodes, isValtheraError, QueryValidationError, TransactionError, wrapAdapterError, } from "../errors.js";
2
3
  import { Collection } from "../helpers/collection.js";
3
4
  import { SmartExecutor } from "../helpers/executor.js";
4
5
  import { genId } from "../helpers/gen.js";
5
- import { applyAddDefaults, applyCountDefaults, applyBulkAddDefaults, applyFindDefaults, applyFindOneDefaults, applyRemoveDefaults, applyToggleOneDefaults, applyUpdateDefaults, applyUpdateOneOrAddDefaults, } from "../helpers/queryDefaults.js";
6
+ import { applyAddDefaults, applyBulkAddDefaults, applyCountDefaults, applyFindDefaults, applyFindOneDefaults, applyRemoveDefaults, applyToggleOneDefaults, applyUpdateDefaults, applyUpdateOneOrAddDefaults, } from "../helpers/queryDefaults.js";
6
7
  import { version } from "../version.js";
7
8
  import { Transaction } from "./transaction.js";
8
9
  /**
@@ -48,11 +49,24 @@ export class ValtheraClass {
48
49
  if (this.adapter?._inited)
49
50
  return;
50
51
  const self = this;
52
+ const failInit = (err) => {
53
+ if (isValtheraError(err))
54
+ throw err;
55
+ throw new AdapterInitError(err, {
56
+ adapter: self.adapter?.version,
57
+ });
58
+ };
51
59
  return await this.executor.addOp(async () => {
52
60
  if (self.adapter?._inited)
53
61
  return;
54
- if (typeof self.options.adapter === "function")
55
- self.adapter = await self.options.adapter();
62
+ if (typeof self.options.adapter === "function") {
63
+ try {
64
+ self.adapter = await self.options.adapter();
65
+ }
66
+ catch (err) {
67
+ failInit(err);
68
+ }
69
+ }
56
70
  // if the executor is not set, and the action wants a smart executor
57
71
  if (!self.options.executor &&
58
72
  self.options.executorAware &&
@@ -65,7 +79,12 @@ export class ValtheraClass {
65
79
  ...self.options.adapterOpts,
66
80
  };
67
81
  }
68
- await self.adapter.init(...args);
82
+ try {
83
+ await self.adapter.init(...args);
84
+ }
85
+ catch (err) {
86
+ failInit(err);
87
+ }
69
88
  self.adapter._inited = true;
70
89
  });
71
90
  }
@@ -76,15 +95,28 @@ export class ValtheraClass {
76
95
  return await this.executor.addOp(async () => {
77
96
  if (!self.adapter?._inited)
78
97
  return;
79
- await self.adapter.close(...args);
98
+ try {
99
+ await self.adapter.close(...args);
100
+ }
101
+ catch (err) {
102
+ if (isValtheraError(err))
103
+ throw err;
104
+ throw new AdapterCloseError(err, {
105
+ adapter: self.adapter?.version,
106
+ });
107
+ }
80
108
  self.adapter._inited = false;
81
109
  });
82
110
  }
83
111
  async execute(name, query, txHandle) {
84
112
  await this.init();
85
- if (txHandle && typeof query === "object")
113
+ const collection = typeof query === "string" ? query : query?.collection;
114
+ if (!collection && name !== "getCollections")
115
+ throw new QueryValidationError(`Missing "collection" for operation "${String(name)}"`, {
116
+ op: name,
117
+ });
118
+ if (txHandle && query && typeof query === "object")
86
119
  query.transaction = txHandle;
87
- const collection = typeof query === "string" ? query : query.collection;
88
120
  const plugins = this._plugins.filter(p => !p.collections || p.collections.includes(collection));
89
121
  const self = this;
90
122
  let idx = 0;
@@ -94,9 +126,18 @@ export class ValtheraClass {
94
126
  next: async () => {
95
127
  if (idx < plugins.length)
96
128
  return plugins[idx++].execute(ctx);
97
- if (txHandle)
98
- return self.adapter[ctx.op](query, txHandle);
99
- return self.executor.addOp(self.adapter[ctx.op].bind(self.adapter), ctx.query, typeof ctx.query === "string" ? ctx.query : ctx.query.collection);
129
+ try {
130
+ if (txHandle)
131
+ return await self.adapter[ctx.op](query, txHandle);
132
+ return await self.executor.addOp(self.adapter[ctx.op].bind(self.adapter), ctx.query, typeof ctx.query === "string" ? ctx.query : ctx.query.collection);
133
+ }
134
+ catch (err) {
135
+ throw wrapAdapterError(err, {
136
+ op: ctx.op,
137
+ collection,
138
+ adapter: self.adapter?.version,
139
+ });
140
+ }
100
141
  },
101
142
  };
102
143
  const result = await ctx.next();
@@ -236,6 +277,15 @@ export class ValtheraClass {
236
277
  applyCountDefaults(query);
237
278
  return this.execute("count", query);
238
279
  }
280
+ /**
281
+ * Describe what the underlying adapter supports.
282
+ */
283
+ async capabilities() {
284
+ await this.init();
285
+ return {
286
+ ...this.adapter.capabilities,
287
+ };
288
+ }
239
289
  /**
240
290
  * @experimental
241
291
  *
@@ -264,7 +314,16 @@ export class ValtheraClass {
264
314
  });
265
315
  await Promise.all(promises);
266
316
  try {
267
- const handle = await this.adapter.beginTransaction(genId());
317
+ let handle;
318
+ try {
319
+ handle = await this.adapter.beginTransaction(genId());
320
+ }
321
+ catch (err) {
322
+ throw wrapAdapterError(err, {
323
+ op: "beginTransaction",
324
+ adapter: this.adapter?.version,
325
+ });
326
+ }
268
327
  const tx = new Transaction(handle, this);
269
328
  try {
270
329
  const result = await fn(tx);
@@ -276,10 +335,16 @@ export class ValtheraClass {
276
335
  await tx.rollback();
277
336
  }
278
337
  catch (rollbackErr) {
279
- throw new AggregateError([
280
- err,
281
- rollbackErr,
282
- ], "Transaction failed and rollback failed");
338
+ throw new TransactionError("Transaction failed and rollback failed", {
339
+ code: ErrorCodes.TRANSACTION_ROLLBACK_FAILED,
340
+ cause: new AggregateError([
341
+ err,
342
+ rollbackErr,
343
+ ], "Transaction failed and rollback failed"),
344
+ context: {
345
+ collections,
346
+ },
347
+ });
283
348
  }
284
349
  throw err;
285
350
  }
@@ -0,0 +1,71 @@
1
+ export declare const ErrorCodes: {
2
+ readonly UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION";
3
+ readonly ADAPTER_ERROR: "ADAPTER_ERROR";
4
+ readonly ADAPTER_INIT: "ADAPTER_INIT";
5
+ readonly ADAPTER_CLOSE: "ADAPTER_CLOSE";
6
+ readonly QUERY_INVALID: "QUERY_INVALID";
7
+ readonly TRANSACTION_FAILED: "TRANSACTION_FAILED";
8
+ readonly TRANSACTION_ROLLBACK_FAILED: "TRANSACTION_ROLLBACK_FAILED";
9
+ readonly REMOTE_ERROR: "REMOTE_ERROR";
10
+ readonly REMOTE_UNKNOWN: "REMOTE_UNKNOWN";
11
+ };
12
+ export type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
13
+ export interface ValtheraErrorOpts {
14
+ code: string;
15
+ cause?: unknown;
16
+ suggestion?: string;
17
+ context?: Record<string, unknown>;
18
+ }
19
+ export interface ValtheraErrorWire {
20
+ code?: string;
21
+ message?: string;
22
+ suggestion?: string;
23
+ context?: Record<string, unknown>;
24
+ }
25
+ export declare class ValtheraError extends Error {
26
+ code: string;
27
+ suggestion?: string;
28
+ context?: Record<string, unknown>;
29
+ constructor(message: string, opts: ValtheraErrorOpts);
30
+ toJSON(): {
31
+ name: string;
32
+ code: string;
33
+ message: string;
34
+ suggestion?: string | undefined;
35
+ context?: Record<string, unknown> | undefined;
36
+ };
37
+ }
38
+ export declare class UnsupportedOperationError extends ValtheraError {
39
+ constructor(operation: string, context?: Record<string, unknown>);
40
+ }
41
+ export declare class QueryValidationError extends ValtheraError {
42
+ constructor(reason: string, context?: Record<string, unknown>);
43
+ }
44
+ export declare class AdapterError extends ValtheraError {
45
+ constructor(cause: unknown, context?: Record<string, unknown>);
46
+ }
47
+ export declare class AdapterInitError extends ValtheraError {
48
+ constructor(cause: unknown, context?: Record<string, unknown>);
49
+ }
50
+ export declare class AdapterCloseError extends ValtheraError {
51
+ constructor(cause: unknown, context?: Record<string, unknown>);
52
+ }
53
+ export declare class TransactionError extends ValtheraError {
54
+ constructor(message: string, opts?: {
55
+ cause?: unknown;
56
+ context?: Record<string, unknown>;
57
+ code?: string;
58
+ });
59
+ }
60
+ export declare class RemoteError extends ValtheraError {
61
+ constructor(cause: unknown, context?: Record<string, unknown>);
62
+ }
63
+ export declare function isValtheraError(e: unknown): e is ValtheraError;
64
+ /**
65
+ * Adapter errors: keep ValtheraError codes, else wrap into AdapterError.
66
+ */
67
+ export declare function wrapAdapterError(cause: unknown, context?: Record<string, unknown>): ValtheraError;
68
+ /**
69
+ * Rebuild a ValtheraError from a server response; match by `code`, not class.
70
+ */
71
+ export declare function errorFromWire(payload: ValtheraErrorWire): ValtheraError;
package/dist/errors.js ADDED
@@ -0,0 +1,156 @@
1
+ export const ErrorCodes = {
2
+ UNSUPPORTED_OPERATION: "UNSUPPORTED_OPERATION",
3
+ ADAPTER_ERROR: "ADAPTER_ERROR",
4
+ ADAPTER_INIT: "ADAPTER_INIT",
5
+ ADAPTER_CLOSE: "ADAPTER_CLOSE",
6
+ QUERY_INVALID: "QUERY_INVALID",
7
+ TRANSACTION_FAILED: "TRANSACTION_FAILED",
8
+ TRANSACTION_ROLLBACK_FAILED: "TRANSACTION_ROLLBACK_FAILED",
9
+ REMOTE_ERROR: "REMOTE_ERROR",
10
+ REMOTE_UNKNOWN: "REMOTE_UNKNOWN",
11
+ };
12
+ export class ValtheraError extends Error {
13
+ code;
14
+ suggestion;
15
+ context;
16
+ constructor(message, opts) {
17
+ super(message, {
18
+ cause: opts.cause,
19
+ });
20
+ this.name = "ValtheraError";
21
+ this.code = opts.code;
22
+ this.suggestion = opts.suggestion;
23
+ this.context = opts.context;
24
+ }
25
+ toJSON() {
26
+ return {
27
+ name: this.name,
28
+ code: this.code,
29
+ message: this.message,
30
+ ...(this.suggestion
31
+ ? {
32
+ suggestion: this.suggestion,
33
+ }
34
+ : {}),
35
+ ...(this.context
36
+ ? {
37
+ context: this.context,
38
+ }
39
+ : {}),
40
+ };
41
+ }
42
+ }
43
+ export class UnsupportedOperationError extends ValtheraError {
44
+ constructor(operation, context = {}) {
45
+ super(`Operation "${operation}" is not supported`, {
46
+ code: ErrorCodes.UNSUPPORTED_OPERATION,
47
+ context: {
48
+ op: operation,
49
+ ...context,
50
+ },
51
+ suggestion: "Check the adapter capabilities before calling this operation",
52
+ });
53
+ this.name = "UnsupportedOperationError";
54
+ }
55
+ }
56
+ export class QueryValidationError extends ValtheraError {
57
+ constructor(reason, context = {}) {
58
+ super(reason, {
59
+ code: ErrorCodes.QUERY_INVALID,
60
+ context,
61
+ suggestion: "Check the query shape and the collection name",
62
+ });
63
+ this.name = "QueryValidationError";
64
+ }
65
+ }
66
+ export class AdapterError extends ValtheraError {
67
+ constructor(cause, context = {}) {
68
+ super(errorMessage(cause), {
69
+ code: ErrorCodes.ADAPTER_ERROR,
70
+ cause,
71
+ context,
72
+ });
73
+ this.name = "AdapterError";
74
+ }
75
+ }
76
+ export class AdapterInitError extends ValtheraError {
77
+ constructor(cause, context = {}) {
78
+ super(`Adapter init failed: ${errorMessage(cause)}`, {
79
+ code: ErrorCodes.ADAPTER_INIT,
80
+ cause,
81
+ context,
82
+ });
83
+ this.name = "AdapterInitError";
84
+ }
85
+ }
86
+ export class AdapterCloseError extends ValtheraError {
87
+ constructor(cause, context = {}) {
88
+ super(`Adapter close failed: ${errorMessage(cause)}`, {
89
+ code: ErrorCodes.ADAPTER_CLOSE,
90
+ cause,
91
+ context,
92
+ });
93
+ this.name = "AdapterCloseError";
94
+ }
95
+ }
96
+ export class TransactionError extends ValtheraError {
97
+ constructor(message, opts = {}) {
98
+ super(message, {
99
+ code: opts.code ?? ErrorCodes.TRANSACTION_FAILED,
100
+ cause: opts.cause,
101
+ context: opts.context,
102
+ });
103
+ this.name = "TransactionError";
104
+ }
105
+ }
106
+ export class RemoteError extends ValtheraError {
107
+ constructor(cause, context = {}) {
108
+ super(errorMessage(cause), {
109
+ code: ErrorCodes.REMOTE_ERROR,
110
+ cause,
111
+ context,
112
+ });
113
+ this.name = "RemoteError";
114
+ }
115
+ }
116
+ function errorMessage(cause) {
117
+ return cause instanceof Error ? cause.message : String(cause);
118
+ }
119
+ export function isValtheraError(e) {
120
+ return e instanceof ValtheraError;
121
+ }
122
+ /**
123
+ * Copy a ValtheraError with merged context; keeps `instanceof` and `cause`
124
+ */
125
+ function cloneWithContext(err, context) {
126
+ const clone = Object.create(Object.getPrototypeOf(err));
127
+ Object.assign(clone, err);
128
+ clone.message = err.message;
129
+ clone.stack = err.stack;
130
+ clone.cause = err.cause;
131
+ clone.context = {
132
+ ...context,
133
+ ...err.context,
134
+ };
135
+ return clone;
136
+ }
137
+ /**
138
+ * Adapter errors: keep ValtheraError codes, else wrap into AdapterError.
139
+ */
140
+ export function wrapAdapterError(cause, context = {}) {
141
+ if (cause instanceof ValtheraError)
142
+ return Object.keys(context).length
143
+ ? cloneWithContext(cause, context)
144
+ : cause;
145
+ return new AdapterError(cause, context);
146
+ }
147
+ /**
148
+ * Rebuild a ValtheraError from a server response; match by `code`, not class.
149
+ */
150
+ export function errorFromWire(payload) {
151
+ return new ValtheraError(payload.message ?? "Unknown remote error", {
152
+ code: payload.code ?? ErrorCodes.REMOTE_UNKNOWN,
153
+ suggestion: payload.suggestion,
154
+ context: payload.context,
155
+ });
156
+ }
@@ -1,3 +1,4 @@
1
+ import { QueryValidationError } from "../errors.js";
1
2
  export function pickByPath(obj, paths) {
2
3
  const result = {};
3
4
  for (const path of paths) {
@@ -160,7 +161,10 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
160
161
  }
161
162
  else if (type === "nm") {
162
163
  if (!through || !through.table || !through.pk || !through.fk)
163
- throw new Error(`Relation type "nm" requires a defined "through" in "${key}"`);
164
+ throw new QueryValidationError(`Relation type "nm" requires a defined "through" in "${key}"`, {
165
+ op: "relation",
166
+ key,
167
+ });
164
168
  const allSourceIds = targets.map(i => i[pk]);
165
169
  const pivotDb = dbs[through.db || dbKey];
166
170
  const allPivots = await pivotDb.find({
@@ -248,7 +252,10 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
248
252
  }
249
253
  }
250
254
  else {
251
- throw new Error(`Unknown relation type: ${type}`);
255
+ throw new QueryValidationError(`Unknown relation type: ${type}`, {
256
+ op: "relation",
257
+ type,
258
+ });
252
259
  }
253
260
  }
254
261
  }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { CustomFileCpu } from "./customFileCpu.js";
2
2
  export { createMemoryValthera, ValtheraMemory } from "./db/memory.js";
3
3
  export { ValtheraClass } from "./db/valthera.js";
4
+ export * from "./errors.js";
4
5
  export { forgeTypedValthera } from "./helpers/forge.js";
5
6
  export { genId } from "./helpers/gen.js";
6
7
  export { Relation } from "./helpers/relation.js";
7
8
  export { convertIdToUnix, sortByIds } from "./utils/id.js";
9
+ export type { AdapterCapabilities } from "./types/action.js";
8
10
  export type { ValtheraTypes } from "./types/export.js";
9
11
  export type { Id } from "./types/Id.js";
10
12
  export type { RelationTypes } from "./types/relation.js";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export { CustomFileCpu } from "./customFileCpu.js";
2
2
  export { createMemoryValthera, ValtheraMemory } from "./db/memory.js";
3
3
  export { ValtheraClass } from "./db/valthera.js";
4
+ export * from "./errors.js";
4
5
  export { forgeTypedValthera } from "./helpers/forge.js";
5
6
  export { genId } from "./helpers/gen.js";
6
7
  export { Relation } from "./helpers/relation.js";
@@ -3,10 +3,16 @@ import { Id } from "./Id.js";
3
3
  import { AdapterOpts } from "./options.js";
4
4
  import { VQueryT } from "./query.js";
5
5
  import { TransactionHandle } from "./transaction.js";
6
+ export interface AdapterCapabilities {
7
+ transactions: boolean;
8
+ indexes: boolean;
9
+ persistent: boolean;
10
+ }
6
11
  export interface ActionsBaseInterface {
7
12
  _inited: boolean;
8
13
  adapterOpts: AdapterOpts;
9
14
  smartExecutor: boolean;
15
+ capabilities: AdapterCapabilities;
10
16
  init(...args: any[]): Promise<void>;
11
17
  close(...args: any[]): Promise<void>;
12
18
  getCollections(): Promise<string[]>;
@@ -6,6 +6,7 @@ export declare namespace ValtheraTypes {
6
6
  type FindOpts = import("./options.js").FindOpts;
7
7
  type DbOpts = import("./options.js").DbOpts;
8
8
  type AdapterOpts = import("./options.js").AdapterOpts;
9
+ type AdapterCapabilities = import("./action.js").AdapterCapabilities;
9
10
  type Data = import("./data.js").Data;
10
11
  type SearchOptions = import("./searchOpts.js").SearchOptions;
11
12
  type Id = import("./Id.js").Id;
@@ -1,4 +1,5 @@
1
1
  import { Collection } from "../helpers/collection.js";
2
+ import { AdapterCapabilities } from "./action.js";
2
3
  import { Data } from "./data.js";
3
4
  import { IndexOpts } from "./idx.js";
4
5
  import { VQueryT } from "./query.js";
@@ -21,4 +22,5 @@ export interface ValtheraCompatible {
21
22
  removeIndex(collection: string, fields: string[]): Promise<void>;
22
23
  count(query: VQueryT.Count): Promise<number>;
23
24
  bulkAdd<T = Data>(query: VQueryT.BulkAdd<T>): Promise<T[]>;
25
+ capabilities(): Promise<AdapterCapabilities>;
24
26
  }
@@ -1,3 +1,4 @@
1
+ import { QueryValidationError } from "../errors.js";
1
2
  import { hasFields } from "./hasFields.js";
2
3
  import { compareIds } from "./id.js";
3
4
  /**
@@ -5,7 +6,9 @@ import { compareIds } from "./id.js";
5
6
  */
6
7
  export function hasFieldsAdvanced(obj, fields) {
7
8
  if (typeof fields !== "object" || fields === null) {
8
- throw new Error("Fields must be an object");
9
+ throw new QueryValidationError("Fields must be an object", {
10
+ op: "search",
11
+ });
9
12
  }
10
13
  if (Object.keys(fields).length === 0)
11
14
  return true;
@@ -1,3 +1,4 @@
1
+ import { QueryValidationError } from "../errors.js";
1
2
  import { _deepMerge } from "./merge.js";
2
3
  /**
3
4
  * Updates an object with new values.
@@ -6,7 +7,9 @@ import { _deepMerge } from "./merge.js";
6
7
  */
7
8
  export function updateObjectAdvanced(obj, field) {
8
9
  if (typeof field !== "object" || Array.isArray(field) || field === null)
9
- throw new Error("Fields must be an object");
10
+ throw new QueryValidationError("Fields must be an object", {
11
+ op: "update",
12
+ });
10
13
  if (Object.keys(field).length === 0)
11
14
  return;
12
15
  const $fields = {};
@@ -97,7 +100,10 @@ function mainUpdate(obj, fields) {
97
100
  }
98
101
  if (!(key in deepObj))
99
102
  return updater;
100
- throw new Error(`Cannot increment non-numeric value at key: ${key}`);
103
+ throw new QueryValidationError(`Cannot increment non-numeric value at key: ${key}`, {
104
+ op: "update",
105
+ key,
106
+ });
101
107
  },
102
108
  dec: (item, updater, key, deepObj) => {
103
109
  if (typeof item === "number" && typeof updater === "number") {
@@ -105,7 +111,10 @@ function mainUpdate(obj, fields) {
105
111
  }
106
112
  if (!(key in deepObj))
107
113
  return -updater;
108
- throw new Error(`Cannot decrement non-numeric value at key: ${key}`);
114
+ throw new QueryValidationError(`Cannot decrement non-numeric value at key: ${key}`, {
115
+ op: "update",
116
+ key,
117
+ });
109
118
  },
110
119
  rename: (_, newKey, oldKey, deepObj) => {
111
120
  if (oldKey in deepObj) {
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "0.12.9";
1
+ export declare const version = "0.12.10-alpha.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const version = "0.12.9";
1
+ export const version = "0.12.10-alpha.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/db-core",
3
- "version": "0.12.9",
3
+ "version": "0.12.10-alpha.0",
4
4
  "types": "dist/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "author": "wxn0brP",