@groundbrick/db-core 0.0.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.
- package/README.md +214 -0
- package/dist/adapters/BaseSqlAdapter.d.ts +14 -0
- package/dist/adapters/BaseSqlAdapter.d.ts.map +1 -0
- package/dist/adapters/BaseSqlAdapter.js +37 -0
- package/dist/adapters/BaseSqlAdapter.js.map +1 -0
- package/dist/adapters/DatabaseAdapter.d.ts +59 -0
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -0
- package/dist/adapters/DatabaseAdapter.js +120 -0
- package/dist/adapters/DatabaseAdapter.js.map +1 -0
- package/dist/base/BaseTransaction.d.ts +65 -0
- package/dist/base/BaseTransaction.d.ts.map +1 -0
- package/dist/base/BaseTransaction.js +134 -0
- package/dist/base/BaseTransaction.js.map +1 -0
- package/dist/custom-types/MigrationRecord.d.ts +6 -0
- package/dist/custom-types/MigrationRecord.d.ts.map +1 -0
- package/dist/custom-types/MigrationRecord.js +2 -0
- package/dist/custom-types/MigrationRecord.js.map +1 -0
- package/dist/custom-types/QueryParams.d.ts +5 -0
- package/dist/custom-types/QueryParams.d.ts.map +1 -0
- package/dist/custom-types/QueryParams.js +2 -0
- package/dist/custom-types/QueryParams.js.map +1 -0
- package/dist/custom-types/Transaction.d.ts +6 -0
- package/dist/custom-types/Transaction.d.ts.map +1 -0
- package/dist/custom-types/Transaction.js +2 -0
- package/dist/custom-types/Transaction.js.map +1 -0
- package/dist/custom-types/TransactionCallback.d.ts +3 -0
- package/dist/custom-types/TransactionCallback.d.ts.map +1 -0
- package/dist/custom-types/TransactionCallback.js +2 -0
- package/dist/custom-types/TransactionCallback.js.map +1 -0
- package/dist/error-handling/ConnectionError.d.ts +8 -0
- package/dist/error-handling/ConnectionError.d.ts.map +1 -0
- package/dist/error-handling/ConnectionError.js +11 -0
- package/dist/error-handling/ConnectionError.js.map +1 -0
- package/dist/error-handling/DatabaseError.d.ts +18 -0
- package/dist/error-handling/DatabaseError.d.ts.map +1 -0
- package/dist/error-handling/DatabaseError.js +22 -0
- package/dist/error-handling/DatabaseError.js.map +1 -0
- package/dist/error-handling/MigrationError.d.ts +8 -0
- package/dist/error-handling/MigrationError.d.ts.map +1 -0
- package/dist/error-handling/MigrationError.js +11 -0
- package/dist/error-handling/MigrationError.js.map +1 -0
- package/dist/error-handling/QueryError.d.ts +8 -0
- package/dist/error-handling/QueryError.d.ts.map +1 -0
- package/dist/error-handling/QueryError.js +11 -0
- package/dist/error-handling/QueryError.js.map +1 -0
- package/dist/error-handling/TransactionError.d.ts +8 -0
- package/dist/error-handling/TransactionError.d.ts.map +1 -0
- package/dist/error-handling/TransactionError.js +11 -0
- package/dist/error-handling/TransactionError.js.map +1 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +105 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/ConfigValidator.d.ts +10 -0
- package/dist/interfaces/ConfigValidator.d.ts.map +1 -0
- package/dist/interfaces/ConfigValidator.js +2 -0
- package/dist/interfaces/ConfigValidator.js.map +1 -0
- package/dist/interfaces/DatabaseClient.d.ts +18 -0
- package/dist/interfaces/DatabaseClient.d.ts.map +1 -0
- package/dist/interfaces/DatabaseClient.js +2 -0
- package/dist/interfaces/DatabaseClient.js.map +1 -0
- package/dist/interfaces/DatabaseConfig.d.ts +22 -0
- package/dist/interfaces/DatabaseConfig.d.ts.map +1 -0
- package/dist/interfaces/DatabaseConfig.js +2 -0
- package/dist/interfaces/DatabaseConfig.js.map +1 -0
- package/dist/interfaces/DatabaseFactory.d.ts +11 -0
- package/dist/interfaces/DatabaseFactory.d.ts.map +1 -0
- package/dist/interfaces/DatabaseFactory.js +2 -0
- package/dist/interfaces/DatabaseFactory.js.map +1 -0
- package/dist/interfaces/DatabaseTransaction.d.ts +31 -0
- package/dist/interfaces/DatabaseTransaction.d.ts.map +1 -0
- package/dist/interfaces/DatabaseTransaction.js +2 -0
- package/dist/interfaces/DatabaseTransaction.js.map +1 -0
- package/dist/interfaces/HealthCheckResult.d.ts +14 -0
- package/dist/interfaces/HealthCheckResult.d.ts.map +1 -0
- package/dist/interfaces/HealthCheckResult.js +2 -0
- package/dist/interfaces/HealthCheckResult.js.map +1 -0
- package/dist/interfaces/Migration.d.ts +11 -0
- package/dist/interfaces/Migration.d.ts.map +1 -0
- package/dist/interfaces/Migration.js +2 -0
- package/dist/interfaces/Migration.js.map +1 -0
- package/dist/interfaces/MigrationManager.d.ts +31 -0
- package/dist/interfaces/MigrationManager.d.ts.map +1 -0
- package/dist/interfaces/MigrationManager.js +2 -0
- package/dist/interfaces/MigrationManager.js.map +1 -0
- package/dist/interfaces/MigrationStatus.d.ts +10 -0
- package/dist/interfaces/MigrationStatus.d.ts.map +1 -0
- package/dist/interfaces/MigrationStatus.js +2 -0
- package/dist/interfaces/MigrationStatus.js.map +1 -0
- package/dist/interfaces/QueryResult.d.ts +14 -0
- package/dist/interfaces/QueryResult.d.ts.map +1 -0
- package/dist/interfaces/QueryResult.js +2 -0
- package/dist/interfaces/QueryResult.js.map +1 -0
- package/dist/interfaces/SqlAdapter.d.ts +11 -0
- package/dist/interfaces/SqlAdapter.d.ts.map +1 -0
- package/dist/interfaces/SqlAdapter.js +2 -0
- package/dist/interfaces/SqlAdapter.js.map +1 -0
- package/dist/interfaces/index.d.ts +14 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +3 -0
- package/dist/interfaces/index.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
|
|
2
|
+
# @groundbrick/db-core
|
|
3
|
+
|
|
4
|
+
**Database abstraction core for the BitBrick microframework**
|
|
5
|
+
|
|
6
|
+
This package provides the backbone for integrating with relational databases (PostgreSQL or MySQL), offering a unified layer for connection handling, querying, transactions, and migrations.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 🔧 Key Features
|
|
11
|
+
|
|
12
|
+
- Unified interface for database clients
|
|
13
|
+
- Adapter support for PostgreSQL and MySQL (via external factories)
|
|
14
|
+
- Full support for transactions
|
|
15
|
+
- Strongly-typed migration system
|
|
16
|
+
- Configuration validation and health checks
|
|
17
|
+
- Granular error handling by operation type
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 📦 Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @groundbrick/db-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> You will also need to install a specific adapter package such as `@groundbrick/db-postgres` or `@groundbrick/db-mysql`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚀 Quick Start
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { DatabaseAdapter } from '@groundbrick/db-core';
|
|
35
|
+
|
|
36
|
+
const adapter = new DatabaseAdapter({
|
|
37
|
+
type: 'postgresql',
|
|
38
|
+
host: 'localhost',
|
|
39
|
+
user: 'admin',
|
|
40
|
+
password: 'secret',
|
|
41
|
+
database: 'mydb',
|
|
42
|
+
port: 5432
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await adapter.initialize();
|
|
46
|
+
|
|
47
|
+
const users = await adapter.query('SELECT * FROM users WHERE active = $1', [true]);
|
|
48
|
+
|
|
49
|
+
await adapter.close();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🔄 Transactions
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
await adapter.transaction(async (trx) => {
|
|
58
|
+
await trx.query('INSERT INTO logs (event, ts) VALUES ($1, now())', ['create_user']);
|
|
59
|
+
|
|
60
|
+
const result = await trx.query<{ id: number }>(
|
|
61
|
+
'INSERT INTO users (email, active) VALUES ($1, $2) RETURNING id',
|
|
62
|
+
['user@example.com', true]
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const userId = result.rows[0].id;
|
|
66
|
+
|
|
67
|
+
await trx.query('INSERT INTO profiles (user_id) VALUES ($1)', [userId]);
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- The transaction will automatically **commit** if successful
|
|
72
|
+
- Any thrown error will **rollback** the transaction
|
|
73
|
+
- Nested transactions are not natively supported
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🧪 Health Check
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const status = await adapter.healthCheck();
|
|
81
|
+
|
|
82
|
+
if (status.status !== 'ok') {
|
|
83
|
+
throw new Error(`Database unavailable: ${status.reason}`);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🔍 Pool Info (if supported by the adapter)
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const pool = adapter.getClient().getConnectionInfo?.();
|
|
93
|
+
if (pool) {
|
|
94
|
+
console.log(`Pool: total=${pool.total}, idle=${pool.idle}, waiting=${pool.waiting}`);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## ⏱ Readiness Check
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
if (!adapter.isReady()) {
|
|
104
|
+
console.warn('Adapter is not ready yet.');
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 📦 Architecture Overview
|
|
111
|
+
|
|
112
|
+
### 🔌 DatabaseAdapter
|
|
113
|
+
|
|
114
|
+
This class encapsulates adapter selection (PostgreSQL or MySQL), dynamically initializes the correct client, and exposes high-level methods:
|
|
115
|
+
|
|
116
|
+
- `.initialize()` / `.close()`
|
|
117
|
+
- `.query(sql, params)`
|
|
118
|
+
- `.transaction(cb)`
|
|
119
|
+
- `.healthCheck()`
|
|
120
|
+
- `.getClient()` (direct access to `DatabaseClient`)
|
|
121
|
+
|
|
122
|
+
> The dynamic loading logic (e.g., `PostgresFactory.getInstance`) must be implemented in the actual adapter packages.
|
|
123
|
+
|
|
124
|
+
### 🧠 `DatabaseClient` Interface
|
|
125
|
+
|
|
126
|
+
Contract that all database adapters must implement:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
interface DatabaseClient {
|
|
130
|
+
initialize(): Promise<void>;
|
|
131
|
+
close(): Promise<void>;
|
|
132
|
+
query<T>(sql: string, params?: any[]): Promise<QueryResult<T>>;
|
|
133
|
+
transaction<T>(cb: (trx: DatabaseTransaction) => Promise<T>): Promise<T>;
|
|
134
|
+
healthCheck(): Promise<HealthCheckResult>;
|
|
135
|
+
isReady(): boolean;
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 🔄 `DatabaseTransaction`
|
|
140
|
+
|
|
141
|
+
Within a transaction, the callback receives a `DatabaseTransaction` instance, allowing you to perform isolated queries.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 📁 Project Structure
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
db-core/
|
|
149
|
+
├── src/
|
|
150
|
+
│ ├── adapters/ # Generic adapter layer
|
|
151
|
+
│ ├── base/ # Reusable base logic (e.g., transactions)
|
|
152
|
+
│ ├── custom-types/ # Utility and internal types
|
|
153
|
+
│ ├── error-handling/ # Operation-specific error classes
|
|
154
|
+
│ ├── interfaces/ # Public contract definitions
|
|
155
|
+
│ └── index.ts # Entry point
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 🧬 Migrations
|
|
161
|
+
|
|
162
|
+
The core defines types for managing and recording schema migrations. You can implement your own `MigrationManager`:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
const manager: MigrationManager = getCustomMigrationManager();
|
|
166
|
+
|
|
167
|
+
const pending = await manager.getPendingMigrations();
|
|
168
|
+
|
|
169
|
+
for (const migration of pending) {
|
|
170
|
+
try {
|
|
171
|
+
await manager.applyMigration(migration);
|
|
172
|
+
console.log(`Migration ${migration.version} applied`);
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(`Failed to apply migration ${migration.version}:`, err);
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Each migration should follow the `Migration` type contract with `up`, `down`, and `version` fields.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 💣 Error Handling
|
|
185
|
+
|
|
186
|
+
All operations raise specific error classes for easier diagnosis:
|
|
187
|
+
|
|
188
|
+
- `ConnectionError`
|
|
189
|
+
- `QueryError`
|
|
190
|
+
- `MigrationError`
|
|
191
|
+
- `TransactionError`
|
|
192
|
+
- `DatabaseError` (generic base class)
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 🧩 Creating a Custom Adapter
|
|
197
|
+
|
|
198
|
+
Example: PostgreSQL Adapter Factory
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
export class PostgresFactory {
|
|
202
|
+
static getInstance(config: DatabaseConfig, logger: Logger): DatabaseClient {
|
|
203
|
+
return new PostgresClient(config, logger);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Inside `DatabaseAdapter`, you would dynamically import and use this factory depending on the type.
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 📝 License
|
|
213
|
+
|
|
214
|
+
MIT — © 200Systems
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SqlAdapter } from "../interfaces/SqlAdapter.js";
|
|
2
|
+
export declare abstract class BaseSqlAdapter implements SqlAdapter {
|
|
3
|
+
abstract adaptSql(sql: string): string;
|
|
4
|
+
abstract getDatabaseType(): 'postgresql' | 'mysql';
|
|
5
|
+
abstract supportsReturning(): boolean;
|
|
6
|
+
createInsertSql(tableName: string, columns: string[]): string;
|
|
7
|
+
createUpdateSql(tableName: string, columns: string[]): string;
|
|
8
|
+
createDeleteSql(tableName: string): string;
|
|
9
|
+
getNowFunction(): string;
|
|
10
|
+
getLimitClause(limit: number, offset?: number): string;
|
|
11
|
+
protected abstract createPlaceholder(index: number): string;
|
|
12
|
+
protected createPlaceholders(count: number): string;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=BaseSqlAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseSqlAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/BaseSqlAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,8BAAsB,cAAe,YAAW,UAAU;IACtD,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IACtC,QAAQ,CAAC,eAAe,IAAI,YAAY,GAAG,OAAO;IAClD,QAAQ,CAAC,iBAAiB,IAAI,OAAO;IAErC,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAW7D,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAe7D,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI1C,cAAc,IAAI,MAAM;IAIxB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM;IAOzD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAE3D,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CAGtD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export class BaseSqlAdapter {
|
|
2
|
+
createInsertSql(tableName, columns) {
|
|
3
|
+
const placeholders = this.createPlaceholders(columns.length);
|
|
4
|
+
let sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
|
|
5
|
+
if (this.supportsReturning()) {
|
|
6
|
+
sql += ' RETURNING *';
|
|
7
|
+
}
|
|
8
|
+
return sql;
|
|
9
|
+
}
|
|
10
|
+
createUpdateSql(tableName, columns) {
|
|
11
|
+
const setParts = columns.map((col, index) => {
|
|
12
|
+
return `${col} = ${this.createPlaceholder(index + 1)}`;
|
|
13
|
+
});
|
|
14
|
+
const idPlaceholder = this.createPlaceholder(columns.length + 1);
|
|
15
|
+
let sql = `UPDATE ${tableName} SET ${setParts.join(', ')} WHERE id = ${idPlaceholder}`;
|
|
16
|
+
if (this.supportsReturning()) {
|
|
17
|
+
sql += ' RETURNING *';
|
|
18
|
+
}
|
|
19
|
+
return sql;
|
|
20
|
+
}
|
|
21
|
+
createDeleteSql(tableName) {
|
|
22
|
+
return this.adaptSql(`DELETE FROM ${tableName} WHERE id = ?`);
|
|
23
|
+
}
|
|
24
|
+
getNowFunction() {
|
|
25
|
+
return 'NOW()';
|
|
26
|
+
}
|
|
27
|
+
getLimitClause(limit, offset = 0) {
|
|
28
|
+
if (offset > 0) {
|
|
29
|
+
return `LIMIT ${limit} OFFSET ${offset}`;
|
|
30
|
+
}
|
|
31
|
+
return `LIMIT ${limit}`;
|
|
32
|
+
}
|
|
33
|
+
createPlaceholders(count) {
|
|
34
|
+
return Array.from({ length: count }, (_, i) => this.createPlaceholder(i + 1)).join(', ');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=BaseSqlAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseSqlAdapter.js","sourceRoot":"","sources":["../../src/adapters/BaseSqlAdapter.ts"],"names":[],"mappings":"AAEA,MAAM,OAAgB,cAAc;IAKhC,eAAe,CAAC,SAAiB,EAAE,OAAiB;QAChD,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,GAAG,GAAG,eAAe,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,YAAY,GAAG,CAAC;QAEtF,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC3B,GAAG,IAAI,cAAc,CAAC;QAC1B,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;IAED,eAAe,CAAC,SAAiB,EAAE,OAAiB;QAChD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE;YACxC,OAAO,GAAG,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC3D,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACjE,IAAI,GAAG,GAAG,UAAU,SAAS,QAAQ,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,aAAa,EAAE,CAAC;QAEvF,IAAI,IAAI,CAAC,iBAAiB,EAAE,EAAE,CAAC;YAC3B,GAAG,IAAI,cAAc,CAAC;QAC1B,CAAC;QAED,OAAO,GAAG,CAAC;IACf,CAAC;IAED,eAAe,CAAC,SAAiB;QAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,eAAe,SAAS,eAAe,CAAC,CAAC;IAClE,CAAC;IAED,cAAc;QACV,OAAO,OAAO,CAAC;IACnB,CAAC;IAED,cAAc,CAAC,KAAa,EAAE,SAAiB,CAAC;QAC5C,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACb,OAAO,SAAS,KAAK,WAAW,MAAM,EAAE,CAAC;QAC7C,CAAC;QACD,OAAO,SAAS,KAAK,EAAE,CAAC;IAC5B,CAAC;IAIS,kBAAkB,CAAC,KAAa;QACtC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7F,CAAC;CACJ"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Logger } from '@groundbrick/logger';
|
|
2
|
+
import { DatabaseClient, DatabaseConfig } from '../interfaces/index.js';
|
|
3
|
+
export type DatabaseType = 'postgresql' | 'mysql';
|
|
4
|
+
export interface AdapterConfig extends DatabaseConfig {
|
|
5
|
+
type: DatabaseType;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Database Adapter that provides a unified interface for both PostgreSQL and MySQL
|
|
9
|
+
* This allows easy switching between database types without changing application code
|
|
10
|
+
*/
|
|
11
|
+
export declare class DatabaseAdapter {
|
|
12
|
+
private client;
|
|
13
|
+
private logger;
|
|
14
|
+
private dbType;
|
|
15
|
+
constructor(config: AdapterConfig, logger?: Logger);
|
|
16
|
+
private createPostgresClient;
|
|
17
|
+
private createMySQLClient;
|
|
18
|
+
/**
|
|
19
|
+
* Get the database type
|
|
20
|
+
*/
|
|
21
|
+
getDatabaseType(): DatabaseType;
|
|
22
|
+
/**
|
|
23
|
+
* Initialize the database connection
|
|
24
|
+
*/
|
|
25
|
+
initialize(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Close the database connection
|
|
28
|
+
*/
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Get the underlying database client
|
|
32
|
+
* This allows access to all database-specific methods
|
|
33
|
+
*/
|
|
34
|
+
getClient(): DatabaseClient;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a database-agnostic query
|
|
37
|
+
*/
|
|
38
|
+
query<T = any>(sql: string, params?: any[]): Promise<{
|
|
39
|
+
rows: T[];
|
|
40
|
+
rowCount: number;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Execute a transaction with unified interface
|
|
44
|
+
*/
|
|
45
|
+
transaction<T>(callback: (client: DatabaseClient) => Promise<T>): Promise<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Health check
|
|
48
|
+
*/
|
|
49
|
+
healthCheck(): Promise<{
|
|
50
|
+
status: 'ok' | 'error';
|
|
51
|
+
timestamp: Date;
|
|
52
|
+
reason?: string;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Check if adapter is ready
|
|
56
|
+
*/
|
|
57
|
+
isReady(): boolean;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=DatabaseAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseAdapter.d.ts","sourceRoot":"","sources":["../../src/adapters/DatabaseAdapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExE,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC;AAElD,MAAM,WAAW,aAAc,SAAQ,cAAc;IACjD,IAAI,EAAE,YAAY,CAAC;CACtB;AAED;;;GAGG;AACH,qBAAa,eAAe;IACxB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,MAAM;IAmBlD,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,iBAAiB;IAUzB;;OAEG;IACH,eAAe,IAAI,YAAY;IAI/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAKjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;;OAGG;IACH,SAAS,IAAI,cAAc;IAI3B;;OAEG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAQ3F;;OAEG;IACG,WAAW,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAalF;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,IAAI,GAAG,OAAO,CAAC;QAAC,SAAS,EAAE,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAa1F;;OAEG;IACH,OAAO,IAAI,OAAO;CAGrB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createLogger } from '@groundbrick/logger';
|
|
2
|
+
/**
|
|
3
|
+
* Database Adapter that provides a unified interface for both PostgreSQL and MySQL
|
|
4
|
+
* This allows easy switching between database types without changing application code
|
|
5
|
+
*/
|
|
6
|
+
export class DatabaseAdapter {
|
|
7
|
+
client;
|
|
8
|
+
logger;
|
|
9
|
+
dbType;
|
|
10
|
+
constructor(config, logger) {
|
|
11
|
+
this.logger = logger || createLogger({ context: 'database-adapter' });
|
|
12
|
+
this.dbType = config.type;
|
|
13
|
+
// Create the appropriate database client based on the type
|
|
14
|
+
switch (config.type) {
|
|
15
|
+
case 'postgresql':
|
|
16
|
+
this.client = this.createPostgresClient(config);
|
|
17
|
+
break;
|
|
18
|
+
case 'mysql':
|
|
19
|
+
this.client = this.createMySQLClient(config);
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(`Unsupported database type: ${config.type}`);
|
|
23
|
+
}
|
|
24
|
+
this.logger.info('Database adapter created', { type: config.type });
|
|
25
|
+
}
|
|
26
|
+
createPostgresClient(config) {
|
|
27
|
+
try {
|
|
28
|
+
// Use dynamic import to avoid bundling unused drivers
|
|
29
|
+
const { PostgresFactory } = require('@groundbrick/db-postgres');
|
|
30
|
+
return PostgresFactory.getInstance(config, this.logger);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw new Error(`Failed to load PostgreSQL driver: ${error instanceof Error ? error.message : 'Unknown error'}. Make sure @groundbrick/db-postgres is installed.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
createMySQLClient(config) {
|
|
37
|
+
try {
|
|
38
|
+
// Use dynamic import to avoid bundling unused drivers
|
|
39
|
+
const { MySQLFactory } = require('@groundbrick/db-mysql');
|
|
40
|
+
return MySQLFactory.getInstance(config, this.logger);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error(`Failed to load MySQL driver: ${error instanceof Error ? error.message : 'Unknown error'}. Make sure @groundbrick/db-mysql is installed.`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the database type
|
|
48
|
+
*/
|
|
49
|
+
getDatabaseType() {
|
|
50
|
+
return this.dbType;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Initialize the database connection
|
|
54
|
+
*/
|
|
55
|
+
async initialize() {
|
|
56
|
+
await this.client.initialize();
|
|
57
|
+
this.logger.info('Database adapter initialized');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Close the database connection
|
|
61
|
+
*/
|
|
62
|
+
async close() {
|
|
63
|
+
await this.client.close();
|
|
64
|
+
this.logger.info('Database adapter closed');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the underlying database client
|
|
68
|
+
* This allows access to all database-specific methods
|
|
69
|
+
*/
|
|
70
|
+
getClient() {
|
|
71
|
+
return this.client;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Execute a database-agnostic query
|
|
75
|
+
*/
|
|
76
|
+
async query(sql, params) {
|
|
77
|
+
const result = await this.client.query(sql, params);
|
|
78
|
+
return {
|
|
79
|
+
rows: result.rows,
|
|
80
|
+
rowCount: result.rowCount ?? 0
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Execute a transaction with unified interface
|
|
85
|
+
*/
|
|
86
|
+
async transaction(callback) {
|
|
87
|
+
return await this.client.transaction(async (trx) => {
|
|
88
|
+
// Create a wrapper that looks like a DatabaseClient but uses the transaction
|
|
89
|
+
const transactionClient = {
|
|
90
|
+
...this.client,
|
|
91
|
+
query: trx.query.bind(trx),
|
|
92
|
+
transaction: () => Promise.reject(new Error('Nested transactions not supported'))
|
|
93
|
+
};
|
|
94
|
+
return await callback(transactionClient);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Health check
|
|
99
|
+
*/
|
|
100
|
+
async healthCheck() {
|
|
101
|
+
try {
|
|
102
|
+
await this.client.query('SELECT 1');
|
|
103
|
+
return { status: 'ok', timestamp: new Date() };
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return {
|
|
107
|
+
status: 'error',
|
|
108
|
+
timestamp: new Date(),
|
|
109
|
+
reason: error instanceof Error ? error.message : 'Unknown error'
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if adapter is ready
|
|
115
|
+
*/
|
|
116
|
+
isReady() {
|
|
117
|
+
return !!this.client;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=DatabaseAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DatabaseAdapter.js","sourceRoot":"","sources":["../../src/adapters/DatabaseAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { DatabaseTransaction } from '../interfaces/DatabaseTransaction.js';
|
|
2
|
+
import { QueryResult } from '../interfaces/QueryResult.js';
|
|
3
|
+
import { Logger } from '@groundbrick/logger';
|
|
4
|
+
/**
|
|
5
|
+
* Base transaction class that provides common transaction logic with database type awareness
|
|
6
|
+
* This ensures consistent behavior across different database implementations
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class BaseTransaction implements DatabaseTransaction {
|
|
9
|
+
protected readonly logger: Logger;
|
|
10
|
+
protected readonly dbType: 'postgresql' | 'mysql';
|
|
11
|
+
protected completed: boolean;
|
|
12
|
+
constructor(logger: Logger, dbType: 'postgresql' | 'mysql');
|
|
13
|
+
abstract query<T = any>(text: string, params?: any[]): Promise<QueryResult<T>>;
|
|
14
|
+
/**
|
|
15
|
+
* NEW: Get the database type for this transaction
|
|
16
|
+
*/
|
|
17
|
+
getDatabaseType(): 'postgresql' | 'mysql';
|
|
18
|
+
/**
|
|
19
|
+
* NEW: Check if this database supports RETURNING clause
|
|
20
|
+
*/
|
|
21
|
+
supportsReturning(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* NEW: Adapt SQL query for database-specific syntax
|
|
24
|
+
*/
|
|
25
|
+
adaptSql(sql: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* NEW: Create database-specific INSERT SQL
|
|
28
|
+
*/
|
|
29
|
+
createInsertSql(tableName: string, columns: string[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* NEW: Create database-specific UPDATE SQL
|
|
32
|
+
*/
|
|
33
|
+
createUpdateSql(tableName: string, columns: string[]): string;
|
|
34
|
+
/**
|
|
35
|
+
* Template method for committing transaction
|
|
36
|
+
* Subclasses implement doCommit() for database-specific logic
|
|
37
|
+
*/
|
|
38
|
+
commit(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Template method for rolling back transaction
|
|
41
|
+
* Subclasses implement doRollback() for database-specific logic
|
|
42
|
+
*/
|
|
43
|
+
rollback(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Check if transaction is completed
|
|
46
|
+
*/
|
|
47
|
+
isCompleted(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Database-specific commit implementation
|
|
50
|
+
*/
|
|
51
|
+
protected abstract doCommit(): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Database-specific rollback implementation
|
|
54
|
+
*/
|
|
55
|
+
protected abstract doRollback(): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Database-specific cleanup (connection release, etc.)
|
|
58
|
+
*/
|
|
59
|
+
protected abstract doCleanup(): Promise<void>;
|
|
60
|
+
/**
|
|
61
|
+
* Ensure cleanup happens in both success and error cases
|
|
62
|
+
*/
|
|
63
|
+
protected ensureCleanup(): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=BaseTransaction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseTransaction.d.ts","sourceRoot":"","sources":["../../src/base/BaseTransaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAE7C;;;GAGG;AACH,8BAAsB,eAAgB,YAAW,mBAAmB;IAI5D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;IAJrD,SAAS,CAAC,SAAS,UAAS;gBAGL,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GAAG,OAAO;IAGrD,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAE9E;;OAEG;IACH,eAAe,IAAI,YAAY,GAAG,OAAO;IAIzC;;OAEG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAU7B;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAc7D;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM;IAgB7D;;;OAGG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB7B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB/B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAE5C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9C;;OAEG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAE7C;;OAEG;cACa,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAWjD"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { TransactionError } from '../error-handling/TransactionError.js';
|
|
2
|
+
/**
|
|
3
|
+
* Base transaction class that provides common transaction logic with database type awareness
|
|
4
|
+
* This ensures consistent behavior across different database implementations
|
|
5
|
+
*/
|
|
6
|
+
export class BaseTransaction {
|
|
7
|
+
logger;
|
|
8
|
+
dbType;
|
|
9
|
+
completed = false;
|
|
10
|
+
constructor(logger, dbType // NEW: Database type awareness
|
|
11
|
+
) {
|
|
12
|
+
this.logger = logger;
|
|
13
|
+
this.dbType = dbType;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* NEW: Get the database type for this transaction
|
|
17
|
+
*/
|
|
18
|
+
getDatabaseType() {
|
|
19
|
+
return this.dbType;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* NEW: Check if this database supports RETURNING clause
|
|
23
|
+
*/
|
|
24
|
+
supportsReturning() {
|
|
25
|
+
return this.dbType === 'postgresql';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* NEW: Adapt SQL query for database-specific syntax
|
|
29
|
+
*/
|
|
30
|
+
adaptSql(sql) {
|
|
31
|
+
if (this.dbType === 'postgresql') {
|
|
32
|
+
// Convert ? placeholders to $1, $2, etc.
|
|
33
|
+
let index = 1;
|
|
34
|
+
return sql.replace(/\?/g, () => `$${index++}`);
|
|
35
|
+
}
|
|
36
|
+
// MySQL uses ? placeholders as-is
|
|
37
|
+
return sql;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* NEW: Create database-specific INSERT SQL
|
|
41
|
+
*/
|
|
42
|
+
createInsertSql(tableName, columns) {
|
|
43
|
+
const placeholders = columns.map((_, index) => {
|
|
44
|
+
return this.dbType === 'postgresql' ? `$${index + 1}` : '?';
|
|
45
|
+
}).join(', ');
|
|
46
|
+
let sql = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`;
|
|
47
|
+
if (this.supportsReturning()) {
|
|
48
|
+
sql += ' RETURNING *';
|
|
49
|
+
}
|
|
50
|
+
return sql;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* NEW: Create database-specific UPDATE SQL
|
|
54
|
+
*/
|
|
55
|
+
createUpdateSql(tableName, columns) {
|
|
56
|
+
const setParts = columns.map((col, index) => {
|
|
57
|
+
const placeholder = this.dbType === 'postgresql' ? `$${index + 1}` : '?';
|
|
58
|
+
return `${col} = ${placeholder}`;
|
|
59
|
+
});
|
|
60
|
+
const idPlaceholder = this.dbType === 'postgresql' ? `$${columns.length + 1}` : '?';
|
|
61
|
+
let sql = `UPDATE ${tableName} SET ${setParts.join(', ')} WHERE id = ${idPlaceholder}`;
|
|
62
|
+
if (this.supportsReturning()) {
|
|
63
|
+
sql += ' RETURNING *';
|
|
64
|
+
}
|
|
65
|
+
return sql;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Template method for committing transaction
|
|
69
|
+
* Subclasses implement doCommit() for database-specific logic
|
|
70
|
+
*/
|
|
71
|
+
async commit() {
|
|
72
|
+
if (this.completed) {
|
|
73
|
+
throw new TransactionError('Transaction has already been completed');
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
this.logger.debug('Committing transaction', { dbType: this.dbType });
|
|
77
|
+
await this.doCommit();
|
|
78
|
+
this.completed = true;
|
|
79
|
+
this.logger.debug('Transaction committed successfully', { dbType: this.dbType });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.logger.error('Failed to commit transaction', error instanceof Error ? error : new Error(String(error)), {
|
|
83
|
+
error,
|
|
84
|
+
dbType: this.dbType
|
|
85
|
+
});
|
|
86
|
+
throw new TransactionError(`Failed to commit transaction: ${error.message}`, error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Template method for rolling back transaction
|
|
91
|
+
* Subclasses implement doRollback() for database-specific logic
|
|
92
|
+
*/
|
|
93
|
+
async rollback() {
|
|
94
|
+
if (this.completed) {
|
|
95
|
+
this.logger.debug('Transaction already completed, nothing to rollback', { dbType: this.dbType });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
this.logger.debug('Rolling back transaction', { dbType: this.dbType });
|
|
100
|
+
await this.doRollback();
|
|
101
|
+
this.completed = true;
|
|
102
|
+
this.logger.debug('Transaction rolled back successfully', { dbType: this.dbType });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
this.logger.error('Failed to rollback transaction', error instanceof Error ? error : new Error(String(error)), {
|
|
106
|
+
error,
|
|
107
|
+
dbType: this.dbType
|
|
108
|
+
});
|
|
109
|
+
throw new TransactionError(`Failed to rollback transaction: ${error.message}`, error);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Check if transaction is completed
|
|
114
|
+
*/
|
|
115
|
+
isCompleted() {
|
|
116
|
+
return this.completed;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Ensure cleanup happens in both success and error cases
|
|
120
|
+
*/
|
|
121
|
+
async ensureCleanup() {
|
|
122
|
+
try {
|
|
123
|
+
await this.doCleanup();
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
// Don't throw here as we don't want cleanup errors to mask the original error
|
|
127
|
+
this.logger.error('Failed to cleanup transaction resources', error instanceof Error ? error : new Error(String(error)), {
|
|
128
|
+
error,
|
|
129
|
+
dbType: this.dbType
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=BaseTransaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseTransaction.js","sourceRoot":"","sources":["../../src/base/BaseTransaction.ts"],"names":[],"mappings":"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"}
|