@bernierllc/database-adapter-core 1.0.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.
- package/README.md +467 -0
- package/dist/base/BaseDatabaseAdapter.d.ts +34 -0
- package/dist/base/BaseDatabaseAdapter.d.ts.map +1 -0
- package/dist/base/BaseDatabaseAdapter.js +52 -0
- package/dist/base/BaseMigrationManager.d.ts +41 -0
- package/dist/base/BaseMigrationManager.d.ts.map +1 -0
- package/dist/base/BaseMigrationManager.js +113 -0
- package/dist/base/BaseQueryBuilder.d.ts +68 -0
- package/dist/base/BaseQueryBuilder.d.ts.map +1 -0
- package/dist/base/BaseQueryBuilder.js +103 -0
- package/dist/base/BaseTransaction.d.ts +13 -0
- package/dist/base/BaseTransaction.d.ts.map +1 -0
- package/dist/base/BaseTransaction.js +17 -0
- package/dist/base/index.d.ts +5 -0
- package/dist/base/index.d.ts.map +1 -0
- package/dist/base/index.js +27 -0
- package/dist/errors/ConnectionError.d.ts +17 -0
- package/dist/errors/ConnectionError.d.ts.map +1 -0
- package/dist/errors/ConnectionError.js +27 -0
- package/dist/errors/DatabaseError.d.ts +16 -0
- package/dist/errors/DatabaseError.d.ts.map +1 -0
- package/dist/errors/DatabaseError.js +27 -0
- package/dist/errors/QueryError.d.ts +17 -0
- package/dist/errors/QueryError.d.ts.map +1 -0
- package/dist/errors/QueryError.js +27 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +26 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/interfaces/IConnectionPool.d.ts +41 -0
- package/dist/interfaces/IConnectionPool.d.ts.map +1 -0
- package/dist/interfaces/IConnectionPool.js +9 -0
- package/dist/interfaces/IDatabaseAdapter.d.ts +45 -0
- package/dist/interfaces/IDatabaseAdapter.d.ts.map +1 -0
- package/dist/interfaces/IDatabaseAdapter.js +9 -0
- package/dist/interfaces/IDatabaseConfig.d.ts +40 -0
- package/dist/interfaces/IDatabaseConfig.d.ts.map +1 -0
- package/dist/interfaces/IDatabaseConfig.js +9 -0
- package/dist/interfaces/IDatabaseHealth.d.ts +16 -0
- package/dist/interfaces/IDatabaseHealth.d.ts.map +1 -0
- package/dist/interfaces/IDatabaseHealth.js +9 -0
- package/dist/interfaces/IMigration.d.ts +54 -0
- package/dist/interfaces/IMigration.d.ts.map +1 -0
- package/dist/interfaces/IMigration.js +9 -0
- package/dist/interfaces/IQueryBuilder.d.ts +106 -0
- package/dist/interfaces/IQueryBuilder.d.ts.map +1 -0
- package/dist/interfaces/IQueryBuilder.js +9 -0
- package/dist/interfaces/IQueryResult.d.ts +28 -0
- package/dist/interfaces/IQueryResult.d.ts.map +1 -0
- package/dist/interfaces/IQueryResult.js +9 -0
- package/dist/interfaces/IRLSContext.d.ts +15 -0
- package/dist/interfaces/IRLSContext.d.ts.map +1 -0
- package/dist/interfaces/IRLSContext.js +9 -0
- package/dist/interfaces/ITransaction.d.ts +32 -0
- package/dist/interfaces/ITransaction.d.ts.map +1 -0
- package/dist/interfaces/ITransaction.js +9 -0
- package/dist/interfaces/index.d.ts +10 -0
- package/dist/interfaces/index.d.ts.map +1 -0
- package/dist/interfaces/index.js +32 -0
- package/dist/rls/RLSContextManager.d.ts +45 -0
- package/dist/rls/RLSContextManager.d.ts.map +1 -0
- package/dist/rls/RLSContextManager.js +88 -0
- package/dist/rls/index.d.ts +2 -0
- package/dist/rls/index.d.ts.map +1 -0
- package/dist/rls/index.js +24 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
# @bernierllc/database-adapter-core
|
|
2
|
+
|
|
3
|
+
Abstract Database Interface for MECE Architecture - provides database-agnostic interfaces and base implementations for all database operations across BernierLLC tools.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Abstract Database Interfaces** - Clean, database-agnostic interfaces for all database operations
|
|
8
|
+
- **Base Implementations** - Common functionality through abstract base classes
|
|
9
|
+
- **Row Level Security (RLS)** - Built-in support for multi-tenant security and user-based access control
|
|
10
|
+
- **Query Builder Foundation** - Type-safe SQL generation without string concatenation
|
|
11
|
+
- **Migration System** - Schema migration tracking and execution
|
|
12
|
+
- **Transaction Support** - ACID transaction interfaces
|
|
13
|
+
- **Connection Pooling** - Interface for connection pool management
|
|
14
|
+
- **Error Handling** - Structured error classes with sensitive data sanitization
|
|
15
|
+
- **Type-Safe** - Full TypeScript support with strict mode
|
|
16
|
+
- **Zero Runtime Overhead** - Pure interfaces and abstract classes
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @bernierllc/database-adapter-core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Creating a Database Adapter
|
|
27
|
+
|
|
28
|
+
Extend `BaseDatabaseAdapter` to create a concrete database implementation:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import {
|
|
32
|
+
BaseDatabaseAdapter,
|
|
33
|
+
IDatabaseConfig,
|
|
34
|
+
IQueryResult,
|
|
35
|
+
ITransaction,
|
|
36
|
+
IDatabaseHealth,
|
|
37
|
+
ConnectionError
|
|
38
|
+
} from '@bernierllc/database-adapter-core';
|
|
39
|
+
|
|
40
|
+
class PostgreSQLAdapter extends BaseDatabaseAdapter {
|
|
41
|
+
private pool: pg.Pool;
|
|
42
|
+
|
|
43
|
+
async connect(): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
this.pool = new pg.Pool({
|
|
46
|
+
host: this.config.host,
|
|
47
|
+
port: this.config.port,
|
|
48
|
+
database: this.config.database,
|
|
49
|
+
user: this.config.username,
|
|
50
|
+
password: this.config.password
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await this.pool.connect();
|
|
54
|
+
this.connected = true;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new ConnectionError(
|
|
57
|
+
'Failed to connect to PostgreSQL',
|
|
58
|
+
'CONNECTION_FAILED',
|
|
59
|
+
error as Error,
|
|
60
|
+
this.config.host,
|
|
61
|
+
this.config.port
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async disconnect(): Promise<void> {
|
|
67
|
+
await this.pool.end();
|
|
68
|
+
this.connected = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async query<T>(sql: string, params?: any[]): Promise<IQueryResult<T>> {
|
|
72
|
+
this.ensureConnected();
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const result = await this.pool.query(sql, params);
|
|
76
|
+
return {
|
|
77
|
+
rows: result.rows as T[],
|
|
78
|
+
rowCount: result.rowCount || 0,
|
|
79
|
+
fields: result.fields.map(f => ({
|
|
80
|
+
name: f.name,
|
|
81
|
+
dataTypeID: f.dataTypeID,
|
|
82
|
+
dataTypeName: f.dataTypeName,
|
|
83
|
+
nullable: true
|
|
84
|
+
})),
|
|
85
|
+
command: result.command
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw this.sanitizeError(error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async beginTransaction(): Promise<ITransaction> {
|
|
93
|
+
this.ensureConnected();
|
|
94
|
+
// Implementation details...
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async healthCheck(): Promise<IDatabaseHealth> {
|
|
98
|
+
const start = Date.now();
|
|
99
|
+
await this.query('SELECT 1');
|
|
100
|
+
const latencyMs = Date.now() - start;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
connected: this.connected,
|
|
104
|
+
latencyMs,
|
|
105
|
+
version: '15.0',
|
|
106
|
+
activeConnections: this.pool.totalCount,
|
|
107
|
+
maxConnections: this.pool.options.max
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Using Row Level Security (RLS)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { RLSContextManager, IRLSContext } from '@bernierllc/database-adapter-core';
|
|
117
|
+
|
|
118
|
+
const rlsManager = new RLSContextManager();
|
|
119
|
+
|
|
120
|
+
// Set RLS context for multi-tenant security
|
|
121
|
+
const context: IRLSContext = {
|
|
122
|
+
userId: 'user-123',
|
|
123
|
+
userRole: 'admin',
|
|
124
|
+
tenantId: 'tenant-456',
|
|
125
|
+
claims: { department: 'engineering' }
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Execute operations with RLS context
|
|
129
|
+
await rlsManager.withContext(context, async () => {
|
|
130
|
+
// All database operations here will have RLS context applied
|
|
131
|
+
const users = await adapter.query('SELECT * FROM users');
|
|
132
|
+
// PostgreSQL RLS policies will automatically filter based on context
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Generate PostgreSQL SET statements for RLS
|
|
136
|
+
rlsManager.setContext(context);
|
|
137
|
+
const statements = rlsManager.generateRLSStatements();
|
|
138
|
+
// [
|
|
139
|
+
// 'SET LOCAL "app.user_id" = \'user-123\';',
|
|
140
|
+
// 'SET LOCAL "app.user_role" = \'admin\';',
|
|
141
|
+
// 'SET LOCAL "app.tenant_id" = \'tenant-456\';',
|
|
142
|
+
// 'SET LOCAL "app.claims" = \'{"department":"engineering"}\';'
|
|
143
|
+
// ]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Building Type-Safe Queries
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import { BaseQueryBuilder } from '@bernierllc/database-adapter-core';
|
|
150
|
+
|
|
151
|
+
class PostgreSQLQueryBuilder extends BaseQueryBuilder<User> {
|
|
152
|
+
insert(data: Partial<User>): this {
|
|
153
|
+
// Implementation for INSERT
|
|
154
|
+
return this;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
update(data: Partial<User>): this {
|
|
158
|
+
// Implementation for UPDATE
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
delete(): this {
|
|
163
|
+
// Implementation for DELETE
|
|
164
|
+
return this;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
build(): { sql: string; params: any[] } {
|
|
168
|
+
// Build PostgreSQL-specific SQL
|
|
169
|
+
let sql = `SELECT ${this._select.join(', ')} FROM ${this._from}`;
|
|
170
|
+
if (this._where.length > 0) {
|
|
171
|
+
sql += ` WHERE ${this._where.join(' AND ')}`;
|
|
172
|
+
}
|
|
173
|
+
return { sql, params: this._params };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Usage
|
|
178
|
+
const query = new PostgreSQLQueryBuilder()
|
|
179
|
+
.select('id', 'name', 'email')
|
|
180
|
+
.from('users')
|
|
181
|
+
.where('age > $1', [18])
|
|
182
|
+
.where('status = $2', ['active'])
|
|
183
|
+
.orderBy('name', 'ASC')
|
|
184
|
+
.limit(10)
|
|
185
|
+
.offset(20);
|
|
186
|
+
|
|
187
|
+
const result = await query.execute(adapter);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Managing Migrations
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import {
|
|
194
|
+
BaseMigrationManager,
|
|
195
|
+
IMigration,
|
|
196
|
+
IDatabaseAdapter
|
|
197
|
+
} from '@bernierllc/database-adapter-core';
|
|
198
|
+
|
|
199
|
+
const migrations: IMigration[] = [
|
|
200
|
+
{
|
|
201
|
+
id: '001',
|
|
202
|
+
name: 'create_users_table',
|
|
203
|
+
async up(adapter: IDatabaseAdapter) {
|
|
204
|
+
await adapter.query(`
|
|
205
|
+
CREATE TABLE users (
|
|
206
|
+
id SERIAL PRIMARY KEY,
|
|
207
|
+
name VARCHAR(255) NOT NULL,
|
|
208
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
209
|
+
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
210
|
+
)
|
|
211
|
+
`);
|
|
212
|
+
},
|
|
213
|
+
async down(adapter: IDatabaseAdapter) {
|
|
214
|
+
await adapter.query('DROP TABLE users');
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: '002',
|
|
219
|
+
name: 'add_users_status',
|
|
220
|
+
async up(adapter: IDatabaseAdapter) {
|
|
221
|
+
await adapter.query(`
|
|
222
|
+
ALTER TABLE users ADD COLUMN status VARCHAR(50) DEFAULT 'active'
|
|
223
|
+
`);
|
|
224
|
+
},
|
|
225
|
+
async down(adapter: IDatabaseAdapter) {
|
|
226
|
+
await adapter.query('ALTER TABLE users DROP COLUMN status');
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
const migrationManager = new BaseMigrationManager(adapter, migrations);
|
|
232
|
+
|
|
233
|
+
// Run pending migrations
|
|
234
|
+
await migrationManager.up();
|
|
235
|
+
|
|
236
|
+
// Get migration status
|
|
237
|
+
const status = await migrationManager.status();
|
|
238
|
+
console.log(status);
|
|
239
|
+
// [
|
|
240
|
+
// { id: '001', name: 'create_users_table', status: 'applied', appliedAt: Date },
|
|
241
|
+
// { id: '002', name: 'add_users_status', status: 'applied', appliedAt: Date }
|
|
242
|
+
// ]
|
|
243
|
+
|
|
244
|
+
// Rollback last migration
|
|
245
|
+
await migrationManager.down();
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Error Handling
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import {
|
|
252
|
+
DatabaseError,
|
|
253
|
+
ConnectionError,
|
|
254
|
+
QueryError
|
|
255
|
+
} from '@bernierllc/database-adapter-core';
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await adapter.connect();
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error instanceof ConnectionError) {
|
|
261
|
+
console.error(`Connection failed: ${error.host}:${error.port}`);
|
|
262
|
+
console.error(`Error code: ${error.code}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
await adapter.query('SELECT * FROM users WHERE id = $1', [userId]);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error instanceof QueryError) {
|
|
270
|
+
console.error(`Query failed: ${error.query}`);
|
|
271
|
+
console.error(`Parameters: ${error.params}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## API Reference
|
|
277
|
+
|
|
278
|
+
### Interfaces
|
|
279
|
+
|
|
280
|
+
#### `IDatabaseAdapter`
|
|
281
|
+
Main adapter interface that all concrete adapters must implement.
|
|
282
|
+
|
|
283
|
+
**Methods:**
|
|
284
|
+
- `connect(): Promise<void>` - Connect to database
|
|
285
|
+
- `disconnect(): Promise<void>` - Disconnect from database
|
|
286
|
+
- `isConnected(): boolean` - Check connection status
|
|
287
|
+
- `query<T>(sql: string, params?: any[]): Promise<IQueryResult<T>>` - Execute query
|
|
288
|
+
- `beginTransaction(): Promise<ITransaction>` - Begin transaction
|
|
289
|
+
- `healthCheck(): Promise<IDatabaseHealth>` - Check database health
|
|
290
|
+
|
|
291
|
+
#### `ITransaction`
|
|
292
|
+
Transaction interface for ACID operations.
|
|
293
|
+
|
|
294
|
+
**Methods:**
|
|
295
|
+
- `query<T>(sql: string, params?: any[]): Promise<IQueryResult<T>>` - Execute query in transaction
|
|
296
|
+
- `commit(): Promise<void>` - Commit transaction
|
|
297
|
+
- `rollback(): Promise<void>` - Rollback transaction
|
|
298
|
+
- `isActive(): boolean` - Check if transaction is active
|
|
299
|
+
|
|
300
|
+
#### `IQueryResult<T>`
|
|
301
|
+
Query result with metadata.
|
|
302
|
+
|
|
303
|
+
**Properties:**
|
|
304
|
+
- `rows: T[]` - Result rows
|
|
305
|
+
- `rowCount: number` - Number of rows returned
|
|
306
|
+
- `fields: IFieldInfo[]` - Field metadata
|
|
307
|
+
- `command: string` - SQL command (SELECT, INSERT, UPDATE, DELETE)
|
|
308
|
+
|
|
309
|
+
#### `IDatabaseConfig`
|
|
310
|
+
Database connection configuration.
|
|
311
|
+
|
|
312
|
+
**Properties:**
|
|
313
|
+
- `type: 'postgresql' | 'mysql' | 'sqlite' | 'mssql'` - Database type
|
|
314
|
+
- `host?: string` - Host address
|
|
315
|
+
- `port?: number` - Port number
|
|
316
|
+
- `database: string` - Database name
|
|
317
|
+
- `username?: string` - Username
|
|
318
|
+
- `password?: string` - Password
|
|
319
|
+
- `ssl?: boolean | object` - SSL configuration
|
|
320
|
+
- `pooling?: object` - Connection pooling settings
|
|
321
|
+
|
|
322
|
+
#### `IRLSContext`
|
|
323
|
+
Row Level Security context.
|
|
324
|
+
|
|
325
|
+
**Properties:**
|
|
326
|
+
- `userId: string` - User ID
|
|
327
|
+
- `userRole: string` - User role
|
|
328
|
+
- `tenantId?: string` - Tenant ID (optional)
|
|
329
|
+
- `claims?: Record<string, any>` - Additional claims (optional)
|
|
330
|
+
|
|
331
|
+
### Base Classes
|
|
332
|
+
|
|
333
|
+
#### `BaseDatabaseAdapter`
|
|
334
|
+
Abstract base class providing common adapter functionality.
|
|
335
|
+
|
|
336
|
+
**Methods:**
|
|
337
|
+
- `isConnected(): boolean` - Returns connection status
|
|
338
|
+
- `ensureConnected(): void` - Throws error if not connected
|
|
339
|
+
- `sanitizeError(error: unknown): Error` - Removes sensitive data from errors
|
|
340
|
+
|
|
341
|
+
#### `BaseQueryBuilder<T>`
|
|
342
|
+
Abstract query builder with fluent API.
|
|
343
|
+
|
|
344
|
+
**Methods:**
|
|
345
|
+
- `select(...columns: string[]): this`
|
|
346
|
+
- `from(table: string): this`
|
|
347
|
+
- `where(condition: string, params?: readonly unknown[]): this`
|
|
348
|
+
- `whereIn(column: string, values: readonly unknown[]): this`
|
|
349
|
+
- `join(table: string, condition: string): this`
|
|
350
|
+
- `leftJoin(table: string, condition: string): this`
|
|
351
|
+
- `orderBy(column: string, direction?: 'ASC' | 'DESC'): this`
|
|
352
|
+
- `limit(count: number): this`
|
|
353
|
+
- `offset(count: number): this`
|
|
354
|
+
- `execute(adapter: IDatabaseAdapter): Promise<IQueryResult<T>>`
|
|
355
|
+
|
|
356
|
+
#### `BaseMigrationManager`
|
|
357
|
+
Migration manager for schema changes.
|
|
358
|
+
|
|
359
|
+
**Methods:**
|
|
360
|
+
- `up(): Promise<void>` - Run pending migrations
|
|
361
|
+
- `down(): Promise<void>` - Rollback last migration
|
|
362
|
+
- `status(): Promise<IMigrationStatus[]>` - Get migration status
|
|
363
|
+
|
|
364
|
+
### Utilities
|
|
365
|
+
|
|
366
|
+
#### `RLSContextManager`
|
|
367
|
+
Row Level Security context manager.
|
|
368
|
+
|
|
369
|
+
**Methods:**
|
|
370
|
+
- `setContext(context: IRLSContext): void` - Set RLS context
|
|
371
|
+
- `getContext(): IRLSContext | null` - Get current context
|
|
372
|
+
- `clearContext(): void` - Clear context
|
|
373
|
+
- `withContext<T>(context: IRLSContext, fn: () => Promise<T>): Promise<T>` - Execute with context
|
|
374
|
+
- `generateRLSStatements(): string[]` - Generate PostgreSQL SET statements
|
|
375
|
+
|
|
376
|
+
### Error Classes
|
|
377
|
+
|
|
378
|
+
- `DatabaseError` - Base database error
|
|
379
|
+
- `ConnectionError` - Connection-specific errors
|
|
380
|
+
- `QueryError` - Query-specific errors
|
|
381
|
+
|
|
382
|
+
## Integration Status
|
|
383
|
+
|
|
384
|
+
### Logger Integration
|
|
385
|
+
**Status**: Optional - Can be integrated for error logging and query tracing
|
|
386
|
+
|
|
387
|
+
Adapters can optionally use `@bernierllc/logger` for:
|
|
388
|
+
- Connection lifecycle logging
|
|
389
|
+
- Query execution tracing
|
|
390
|
+
- Error logging with context
|
|
391
|
+
|
|
392
|
+
### Docs-Suite Integration
|
|
393
|
+
**Status**: Ready
|
|
394
|
+
|
|
395
|
+
This package includes complete JSDoc/TypeDoc comments and can be integrated with documentation generators:
|
|
396
|
+
- All public APIs documented
|
|
397
|
+
- Interface documentation complete
|
|
398
|
+
- Examples included
|
|
399
|
+
|
|
400
|
+
### NeverHub Integration
|
|
401
|
+
**Status**: Not Applicable
|
|
402
|
+
|
|
403
|
+
This is a pure interface package with no runtime behavior requiring service discovery or event communication.
|
|
404
|
+
|
|
405
|
+
## Dependencies
|
|
406
|
+
|
|
407
|
+
- `@bernierllc/logger` - Structured logging (optional usage)
|
|
408
|
+
- `@bernierllc/crypto-utils` - For secure token generation in adapters
|
|
409
|
+
|
|
410
|
+
## Testing
|
|
411
|
+
|
|
412
|
+
This package has comprehensive test coverage:
|
|
413
|
+
- **90.27%** statement coverage
|
|
414
|
+
- **90.47%** branch coverage
|
|
415
|
+
- **100%** function coverage
|
|
416
|
+
- **90%** line coverage
|
|
417
|
+
- **65 passing tests**
|
|
418
|
+
|
|
419
|
+
Run tests:
|
|
420
|
+
```bash
|
|
421
|
+
npm test # Watch mode
|
|
422
|
+
npm run test:run # Single run
|
|
423
|
+
npm run test:coverage # With coverage report
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Building
|
|
427
|
+
|
|
428
|
+
```bash
|
|
429
|
+
npm run build # Compile TypeScript
|
|
430
|
+
npm run lint # Run ESLint
|
|
431
|
+
npm run clean # Remove build artifacts
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Related Packages
|
|
435
|
+
|
|
436
|
+
### Depends On
|
|
437
|
+
- [@bernierllc/logger](../logger) - Structured logging
|
|
438
|
+
- [@bernierllc/crypto-utils](../crypto-utils) - Cryptographic utilities
|
|
439
|
+
|
|
440
|
+
### Used By
|
|
441
|
+
- `@bernierllc/database-adapter-postgresql` - PostgreSQL implementation (coming soon)
|
|
442
|
+
- `@bernierllc/content-storage-adapter` - Content CRUD operations (coming soon)
|
|
443
|
+
- All packages requiring database access
|
|
444
|
+
|
|
445
|
+
### Part Of
|
|
446
|
+
- **content-management-suite** - Database foundation layer
|
|
447
|
+
|
|
448
|
+
## Design Philosophy
|
|
449
|
+
|
|
450
|
+
This package follows MECE (Mutually Exclusive, Collectively Exhaustive) architecture principles:
|
|
451
|
+
|
|
452
|
+
- **Abstract Interfaces** - No concrete database implementations
|
|
453
|
+
- **Single Responsibility** - Pure interface definitions and base utilities
|
|
454
|
+
- **Zero Coupling** - Implementations are separate packages
|
|
455
|
+
- **Adapter Pattern** - Clean abstraction layer for all database operations
|
|
456
|
+
|
|
457
|
+
By keeping interfaces separate from implementations, we enable:
|
|
458
|
+
- **Pluggable Backends** - Switch databases without changing application code
|
|
459
|
+
- **Testability** - Mock adapters easily for testing
|
|
460
|
+
- **Flexibility** - Support multiple databases simultaneously
|
|
461
|
+
- **Maintainability** - Changes to one adapter don't affect others
|
|
462
|
+
|
|
463
|
+
## License
|
|
464
|
+
|
|
465
|
+
Copyright (c) 2025 Bernier LLC. All rights reserved.
|
|
466
|
+
|
|
467
|
+
This file is licensed to the client under a limited-use license. The client may use and modify this code *only within the scope of the project it was delivered for*. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IDatabaseAdapter, IDatabaseConfig, IDatabaseHealth, IQueryResult, ITransaction } from '../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for database adapters
|
|
4
|
+
* Provides common functionality for all implementations
|
|
5
|
+
*/
|
|
6
|
+
export declare abstract class BaseDatabaseAdapter implements IDatabaseAdapter {
|
|
7
|
+
protected config: IDatabaseConfig;
|
|
8
|
+
protected connected: boolean;
|
|
9
|
+
constructor(config: IDatabaseConfig);
|
|
10
|
+
/**
|
|
11
|
+
* Abstract methods - MUST be implemented by concrete adapters
|
|
12
|
+
*/
|
|
13
|
+
abstract connect(): Promise<void>;
|
|
14
|
+
abstract disconnect(): Promise<void>;
|
|
15
|
+
abstract query<T = any>(sql: string, params?: any[]): Promise<IQueryResult<T>>;
|
|
16
|
+
abstract beginTransaction(): Promise<ITransaction>;
|
|
17
|
+
abstract healthCheck(): Promise<IDatabaseHealth>;
|
|
18
|
+
/**
|
|
19
|
+
* Check if database is connected
|
|
20
|
+
*/
|
|
21
|
+
isConnected(): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Ensure database is connected before operations
|
|
24
|
+
* @throws ConnectionError if not connected
|
|
25
|
+
*/
|
|
26
|
+
protected ensureConnected(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Sanitize error messages to remove sensitive data
|
|
29
|
+
* @param error - Original error
|
|
30
|
+
* @returns Sanitized error
|
|
31
|
+
*/
|
|
32
|
+
protected sanitizeError(error: unknown): Error;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=BaseDatabaseAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseDatabaseAdapter.d.ts","sourceRoot":"","sources":["../../src/base/BaseDatabaseAdapter.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,YAAY,EACb,MAAM,eAAe,CAAC;AAGvB;;;GAGG;AACH,8BAAsB,mBAAoB,YAAW,gBAAgB;IACnE,SAAS,CAAC,MAAM,EAAE,eAAe,CAAC;IAClC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAS;gBAEzB,MAAM,EAAE,eAAe;IAInC;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IACpC,QAAQ,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,gBAAgB,IAAI,OAAO,CAAC,YAAY,CAAC;IAClD,QAAQ,CAAC,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IAEhD;;OAEG;IACI,WAAW,IAAI,OAAO;IAI7B;;;OAGG;IACH,SAAS,CAAC,eAAe,IAAI,IAAI;IASjC;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK;CAa/C"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.BaseDatabaseAdapter = void 0;
|
|
11
|
+
const errors_1 = require("../errors");
|
|
12
|
+
/**
|
|
13
|
+
* Abstract base class for database adapters
|
|
14
|
+
* Provides common functionality for all implementations
|
|
15
|
+
*/
|
|
16
|
+
class BaseDatabaseAdapter {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.connected = false;
|
|
19
|
+
this.config = config;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if database is connected
|
|
23
|
+
*/
|
|
24
|
+
isConnected() {
|
|
25
|
+
return this.connected;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Ensure database is connected before operations
|
|
29
|
+
* @throws ConnectionError if not connected
|
|
30
|
+
*/
|
|
31
|
+
ensureConnected() {
|
|
32
|
+
if (!this.connected) {
|
|
33
|
+
throw new errors_1.ConnectionError('Database not connected. Call connect() first.', 'NOT_CONNECTED');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sanitize error messages to remove sensitive data
|
|
38
|
+
* @param error - Original error
|
|
39
|
+
* @returns Sanitized error
|
|
40
|
+
*/
|
|
41
|
+
sanitizeError(error) {
|
|
42
|
+
const message = error instanceof Error ? error.message : 'Database error';
|
|
43
|
+
// Remove sensitive data patterns from error messages
|
|
44
|
+
const sanitized = message
|
|
45
|
+
.replace(/password[^\s]*/gi, 'password=***')
|
|
46
|
+
.replace(/secret[^\s]*/gi, 'secret=***')
|
|
47
|
+
.replace(/token[^\s]*/gi, 'token=***')
|
|
48
|
+
.replace(/key[^\s]*/gi, 'key=***');
|
|
49
|
+
return new Error(sanitized);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.BaseDatabaseAdapter = BaseDatabaseAdapter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { IDatabaseAdapter, IMigration, IMigrationManager, IMigrationStatus } from '../interfaces';
|
|
2
|
+
/**
|
|
3
|
+
* Base migration manager implementation
|
|
4
|
+
* Tracks and executes schema migrations
|
|
5
|
+
*/
|
|
6
|
+
export declare class BaseMigrationManager implements IMigrationManager {
|
|
7
|
+
private adapter;
|
|
8
|
+
private migrations;
|
|
9
|
+
constructor(adapter: IDatabaseAdapter, migrations: IMigration[]);
|
|
10
|
+
/**
|
|
11
|
+
* Run all pending migrations
|
|
12
|
+
* @throws Error if any migration fails
|
|
13
|
+
*/
|
|
14
|
+
up(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Rollback last applied migration
|
|
17
|
+
* @throws Error if no migrations to rollback or rollback fails
|
|
18
|
+
*/
|
|
19
|
+
down(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Get status of all migrations
|
|
22
|
+
*/
|
|
23
|
+
status(): Promise<IMigrationStatus[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Ensure migrations table exists
|
|
26
|
+
*/
|
|
27
|
+
private ensureMigrationsTable;
|
|
28
|
+
/**
|
|
29
|
+
* Get list of applied migration IDs in order
|
|
30
|
+
*/
|
|
31
|
+
private getAppliedMigrations;
|
|
32
|
+
/**
|
|
33
|
+
* Record a migration as applied
|
|
34
|
+
*/
|
|
35
|
+
private recordMigration;
|
|
36
|
+
/**
|
|
37
|
+
* Remove a migration record
|
|
38
|
+
*/
|
|
39
|
+
private removeMigration;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=BaseMigrationManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseMigrationManager.d.ts","sourceRoot":"","sources":["../../src/base/BaseMigrationManager.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,gBAAgB,EAChB,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IAE1D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,UAAU;gBADV,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,UAAU,EAAE;IAGlC;;;OAGG;IACU,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBhC;;;OAGG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBlC;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAYlD;;OAEG;YACW,qBAAqB;IAUnC;;OAEG;YACW,oBAAoB;IAOlC;;OAEG;YACW,eAAe;IAO7B;;OAEG;YACW,eAAe;CAG9B"}
|