@bernierllc/database-adapter-postgresql 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 +456 -0
- package/dist/PostgreSQLAdapter.d.ts +65 -0
- package/dist/PostgreSQLAdapter.js +252 -0
- package/dist/PostgreSQLTransaction.d.ts +46 -0
- package/dist/PostgreSQLTransaction.js +199 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +29 -0
- package/dist/migrations/001_initial_schema.d.ts +2 -0
- package/dist/migrations/001_initial_schema.js +91 -0
- package/dist/realtime/RealtimeManager.d.ts +76 -0
- package/dist/realtime/RealtimeManager.js +243 -0
- package/dist/rls/ClerkIntegration.d.ts +41 -0
- package/dist/rls/ClerkIntegration.js +161 -0
- package/dist/rls/RLSPolicyGenerator.d.ts +38 -0
- package/dist/rls/RLSPolicyGenerator.js +212 -0
- package/dist/search/FullTextSearchManager.d.ts +61 -0
- package/dist/search/FullTextSearchManager.js +265 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# @bernierllc/database-adapter-postgresql
|
|
2
|
+
|
|
3
|
+
PostgreSQL/Supabase database adapter with Row Level Security, real-time subscriptions, and full-text search.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bernierllc/database-adapter-postgresql
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Connection Pooling** - Efficient connection management with pg Pool
|
|
14
|
+
- **Row Level Security** - Multi-tenant security with Clerk JWT integration
|
|
15
|
+
- **Real-time Subscriptions** - Supabase real-time for collaborative editing
|
|
16
|
+
- **Full-Text Search** - pg_trgm fuzzy matching and ts_vector search
|
|
17
|
+
- **Transaction Support** - ACID transactions with savepoints
|
|
18
|
+
- **Automatic Retries** - Connection retry with exponential backoff
|
|
19
|
+
- **Type Safety** - Strict TypeScript with comprehensive type definitions
|
|
20
|
+
- **Logging Integration** - Structured logging with @bernierllc/logger
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
### Basic Connection
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { PostgreSQLAdapter } from '@bernierllc/database-adapter-postgresql';
|
|
28
|
+
|
|
29
|
+
const adapter = new PostgreSQLAdapter({
|
|
30
|
+
type: 'postgresql',
|
|
31
|
+
host: 'localhost',
|
|
32
|
+
port: 5432,
|
|
33
|
+
database: 'myapp',
|
|
34
|
+
username: 'postgres',
|
|
35
|
+
password: 'password',
|
|
36
|
+
pooling: {
|
|
37
|
+
min: 2,
|
|
38
|
+
max: 10
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await adapter.connect();
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### With Supabase
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const adapter = new PostgreSQLAdapter({
|
|
49
|
+
type: 'postgresql',
|
|
50
|
+
host: 'db.project.supabase.co',
|
|
51
|
+
port: 5432,
|
|
52
|
+
database: 'postgres',
|
|
53
|
+
username: 'postgres',
|
|
54
|
+
password: 'password',
|
|
55
|
+
ssl: true,
|
|
56
|
+
supabaseUrl: 'https://project.supabase.co',
|
|
57
|
+
supabaseAnonKey: 'your-anon-key'
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Query Execution
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Simple query
|
|
65
|
+
const result = await adapter.query('SELECT * FROM users WHERE id = $1', ['user-id']);
|
|
66
|
+
|
|
67
|
+
// Typed query
|
|
68
|
+
interface User {
|
|
69
|
+
id: string;
|
|
70
|
+
email: string;
|
|
71
|
+
role: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const users = await adapter.query<User>('SELECT * FROM users');
|
|
75
|
+
console.log(users.rows[0].email);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Transactions
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const txn = await adapter.beginTransaction();
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await txn.query('INSERT INTO users (email) VALUES ($1)', ['user@example.com']);
|
|
85
|
+
await txn.query('INSERT INTO profiles (user_id) VALUES ($1)', ['user-id']);
|
|
86
|
+
|
|
87
|
+
await txn.commit();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
await txn.rollback();
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Savepoints
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
const txn = await adapter.beginTransaction();
|
|
98
|
+
|
|
99
|
+
await txn.query('INSERT INTO content (title) VALUES ($1)', ['Draft 1']);
|
|
100
|
+
await txn.savepoint('draft1');
|
|
101
|
+
|
|
102
|
+
await txn.query('INSERT INTO content (title) VALUES ($1)', ['Draft 2']);
|
|
103
|
+
|
|
104
|
+
// Rollback to savepoint
|
|
105
|
+
await txn.rollbackToSavepoint('draft1');
|
|
106
|
+
|
|
107
|
+
await txn.commit(); // Only 'Draft 1' is committed
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Row Level Security
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { RLSPolicyGenerator } from '@bernierllc/database-adapter-postgresql';
|
|
114
|
+
|
|
115
|
+
// Generate user-scoped policy
|
|
116
|
+
const policy = RLSPolicyGenerator.generateUserPolicy('content');
|
|
117
|
+
await adapter.query(policy);
|
|
118
|
+
|
|
119
|
+
// Set RLS context for queries
|
|
120
|
+
adapter.setRLSContext('user_123', 'user', 'tenant_456');
|
|
121
|
+
|
|
122
|
+
// All subsequent queries respect RLS policies
|
|
123
|
+
const content = await adapter.query('SELECT * FROM content');
|
|
124
|
+
// Only returns content for user_123 in tenant_456
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Clerk Integration
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { ClerkIntegration } from '@bernierllc/database-adapter-postgresql';
|
|
131
|
+
|
|
132
|
+
// Set up Clerk authentication functions
|
|
133
|
+
const setup = ClerkIntegration.generateCompleteSetup();
|
|
134
|
+
await adapter.query(setup);
|
|
135
|
+
|
|
136
|
+
// RLS policies automatically use Clerk JWT
|
|
137
|
+
const policy = RLSPolicyGenerator.generateUserPolicy('content');
|
|
138
|
+
await adapter.query(policy);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Real-time Subscriptions
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
import { RealtimeManager } from '@bernierllc/database-adapter-postgresql';
|
|
145
|
+
|
|
146
|
+
const realtimeManager = new RealtimeManager(adapter);
|
|
147
|
+
|
|
148
|
+
// Subscribe to content changes
|
|
149
|
+
const unsubscribe = realtimeManager.subscribeToContent('content-id', {
|
|
150
|
+
onInsert: (payload) => {
|
|
151
|
+
console.log('New version created:', payload.new);
|
|
152
|
+
},
|
|
153
|
+
onUpdate: (payload) => {
|
|
154
|
+
console.log('Content updated:', payload.new);
|
|
155
|
+
},
|
|
156
|
+
onDelete: (payload) => {
|
|
157
|
+
console.log('Content deleted:', payload.old);
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Broadcast presence for collaborative editing
|
|
162
|
+
await realtimeManager.broadcastPresence('content-id', 'user-id', {
|
|
163
|
+
line: 10,
|
|
164
|
+
column: 5
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Get current presence state
|
|
168
|
+
const presence = realtimeManager.getPresence('content-id');
|
|
169
|
+
|
|
170
|
+
// Cleanup
|
|
171
|
+
unsubscribe();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Full-Text Search
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { FullTextSearchManager } from '@bernierllc/database-adapter-postgresql';
|
|
178
|
+
|
|
179
|
+
const searchManager = new FullTextSearchManager(adapter);
|
|
180
|
+
|
|
181
|
+
// Create indexes first
|
|
182
|
+
await searchManager.createIndexes();
|
|
183
|
+
|
|
184
|
+
// Fuzzy search with typo tolerance
|
|
185
|
+
const results = await searchManager.searchContent('databse', {
|
|
186
|
+
minSimilarity: 0.3,
|
|
187
|
+
limit: 10
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
results.forEach(result => {
|
|
191
|
+
console.log(result.title, 'Relevance:', result.totalSimilarity);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Combined search (pg_trgm + ts_vector)
|
|
195
|
+
const combined = await searchManager.searchCombined('PostgreSQL performance', {
|
|
196
|
+
minSimilarity: 0.2,
|
|
197
|
+
limit: 20
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Search specific fields
|
|
201
|
+
const titleResults = await searchManager.searchContent('guide', {
|
|
202
|
+
fields: ['title'],
|
|
203
|
+
minSimilarity: 0.4
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Migrations
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { migration_001_initial_schema } from '@bernierllc/database-adapter-postgresql';
|
|
211
|
+
|
|
212
|
+
// Run migration
|
|
213
|
+
await migration_001_initial_schema.up(adapter);
|
|
214
|
+
|
|
215
|
+
// Rollback
|
|
216
|
+
await migration_001_initial_schema.down(adapter);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Health Checks
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const health = await adapter.healthCheck();
|
|
223
|
+
|
|
224
|
+
console.log('Connected:', health.connected);
|
|
225
|
+
console.log('Latency:', health.latencyMs, 'ms');
|
|
226
|
+
console.log('Version:', health.version);
|
|
227
|
+
console.log('Active connections:', health.activeConnections);
|
|
228
|
+
console.log('Max connections:', health.maxConnections);
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Pool Statistics
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const stats = adapter.getPoolStats();
|
|
235
|
+
|
|
236
|
+
console.log('Total connections:', stats.total);
|
|
237
|
+
console.log('Idle connections:', stats.idle);
|
|
238
|
+
console.log('Waiting requests:', stats.waiting);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## API Reference
|
|
242
|
+
|
|
243
|
+
### PostgreSQLAdapter
|
|
244
|
+
|
|
245
|
+
Main adapter class extending `BaseDatabaseAdapter` from `@bernierllc/database-adapter-core`.
|
|
246
|
+
|
|
247
|
+
#### Methods
|
|
248
|
+
|
|
249
|
+
- `connect(): Promise<void>` - Connect to PostgreSQL with retry
|
|
250
|
+
- `disconnect(): Promise<void>` - Close connection pool
|
|
251
|
+
- `query<T>(sql: string, params?: any[]): Promise<IQueryResult<T>>` - Execute query
|
|
252
|
+
- `beginTransaction(): Promise<ITransaction>` - Start transaction
|
|
253
|
+
- `healthCheck(): Promise<IDatabaseHealth>` - Check connection health
|
|
254
|
+
- `setRLSContext(userId: string, userRole: string, tenantId?: string): void` - Set RLS context
|
|
255
|
+
- `clearRLSContext(): void` - Clear RLS context
|
|
256
|
+
- `getSupabaseClient(): SupabaseClient | null` - Get Supabase client
|
|
257
|
+
- `getPoolStats(): object | null` - Get connection pool statistics
|
|
258
|
+
|
|
259
|
+
### PostgreSQLTransaction
|
|
260
|
+
|
|
261
|
+
Transaction implementation with savepoint support.
|
|
262
|
+
|
|
263
|
+
#### Methods
|
|
264
|
+
|
|
265
|
+
- `query<T>(sql: string, params?: any[]): Promise<IQueryResult<T>>` - Execute query in transaction
|
|
266
|
+
- `commit(): Promise<void>` - Commit transaction
|
|
267
|
+
- `rollback(): Promise<void>` - Rollback transaction
|
|
268
|
+
- `savepoint(name: string): Promise<void>` - Create savepoint
|
|
269
|
+
- `rollbackToSavepoint(name: string): Promise<void>` - Rollback to savepoint
|
|
270
|
+
- `releaseSavepoint(name: string): Promise<void>` - Release savepoint
|
|
271
|
+
- `isActive(): boolean` - Check if transaction is active
|
|
272
|
+
|
|
273
|
+
### RLSPolicyGenerator
|
|
274
|
+
|
|
275
|
+
Static utility for generating RLS policies.
|
|
276
|
+
|
|
277
|
+
#### Methods
|
|
278
|
+
|
|
279
|
+
- `generateUserPolicy(table: string): string` - User-scoped access
|
|
280
|
+
- `generateAdminPolicy(table: string): string` - Admin bypass
|
|
281
|
+
- `generateTenantPolicy(table: string): string` - Multi-tenant isolation
|
|
282
|
+
- `generatePublicReadPolicy(table: string, condition?: string): string` - Public read access
|
|
283
|
+
- `generateSharedResourcePolicy(table: string, sharingTable?: string): string` - Shared resources
|
|
284
|
+
- `generateRoleBasedPolicy(table: string, roles: string[]): string` - Role-based access
|
|
285
|
+
- `generateTimeBasedPolicy(table: string): string` - Time-based access
|
|
286
|
+
- `disableRLS(table: string): string` - Disable RLS (use with caution)
|
|
287
|
+
|
|
288
|
+
### ClerkIntegration
|
|
289
|
+
|
|
290
|
+
Static utility for Clerk JWT integration.
|
|
291
|
+
|
|
292
|
+
#### Methods
|
|
293
|
+
|
|
294
|
+
- `generateCompleteSetup(): string` - Generate all functions and schema
|
|
295
|
+
- `generateAuthSchema(): string` - Create auth schema
|
|
296
|
+
- `generateClerkUserIdFunction(): string` - Extract Clerk user ID
|
|
297
|
+
- `generateUserIdFunction(): string` - Get internal user ID
|
|
298
|
+
- `generateIsAdminFunction(): string` - Check admin status
|
|
299
|
+
- `generateUserRoleFunction(): string` - Get user role
|
|
300
|
+
- `generateHasRoleFunction(): string` - Check specific role
|
|
301
|
+
- `generateUserTenantIdFunction(): string` - Get user tenant ID
|
|
302
|
+
|
|
303
|
+
### RealtimeManager
|
|
304
|
+
|
|
305
|
+
Manages Supabase real-time subscriptions.
|
|
306
|
+
|
|
307
|
+
#### Methods
|
|
308
|
+
|
|
309
|
+
- `subscribeToContent(contentId: string, callbacks: RealtimeCallbacks): () => void` - Subscribe to content
|
|
310
|
+
- `subscribeToTable(table: string, filter: string | null, callbacks: RealtimeCallbacks): () => void` - Subscribe to table
|
|
311
|
+
- `broadcastPresence(contentId: string, userId: string, cursorPosition?: object): Promise<void>` - Broadcast presence
|
|
312
|
+
- `getPresence(contentId: string): object | null` - Get presence state
|
|
313
|
+
- `unsubscribe(contentId: string): Promise<void>` - Unsubscribe from channel
|
|
314
|
+
- `unsubscribeAll(): Promise<void>` - Unsubscribe from all channels
|
|
315
|
+
|
|
316
|
+
### FullTextSearchManager
|
|
317
|
+
|
|
318
|
+
PostgreSQL full-text search with pg_trgm.
|
|
319
|
+
|
|
320
|
+
#### Methods
|
|
321
|
+
|
|
322
|
+
- `searchContent(query: string, options?: SearchOptions): Promise<SearchResult[]>` - Fuzzy search
|
|
323
|
+
- `searchWithTsVector(query: string, options?: SearchOptions): Promise<any[]>` - ts_vector search
|
|
324
|
+
- `searchCombined(query: string, options?: SearchOptions): Promise<SearchResult[]>` - Combined search
|
|
325
|
+
- `createIndexes(): Promise<void>` - Create search indexes
|
|
326
|
+
- `dropIndexes(): Promise<void>` - Drop search indexes
|
|
327
|
+
- `getSearchStats(): Promise<object>` - Get index statistics
|
|
328
|
+
|
|
329
|
+
## Configuration
|
|
330
|
+
|
|
331
|
+
### IDatabaseConfig
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
interface IDatabaseConfig {
|
|
335
|
+
type: 'postgresql';
|
|
336
|
+
host: string;
|
|
337
|
+
port?: number; // Default: 5432
|
|
338
|
+
database: string;
|
|
339
|
+
username: string;
|
|
340
|
+
password: string;
|
|
341
|
+
ssl?: boolean | object;
|
|
342
|
+
pooling?: {
|
|
343
|
+
min?: number; // Default: 2
|
|
344
|
+
max?: number; // Default: 10
|
|
345
|
+
idleTimeoutMs?: number; // Default: 30000
|
|
346
|
+
};
|
|
347
|
+
connectionTimeoutMs?: number; // Default: 5000
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### ISupabaseConfig
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
interface ISupabaseConfig {
|
|
355
|
+
supabaseUrl?: string;
|
|
356
|
+
supabaseAnonKey?: string;
|
|
357
|
+
supabaseServiceKey?: string;
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
## Integration Status
|
|
362
|
+
|
|
363
|
+
- **Logger**: ✅ Integrated - Uses @bernierllc/logger for structured logging
|
|
364
|
+
- **Docs-Suite**: ✅ Ready - TypeDoc API documentation, complete README
|
|
365
|
+
- **NeverHub**: 🔔 Optional - Health metrics and pool statistics publishing
|
|
366
|
+
|
|
367
|
+
### NeverHub Integration
|
|
368
|
+
|
|
369
|
+
This package provides **optional** NeverHub integration for enhanced observability:
|
|
370
|
+
|
|
371
|
+
**What NeverHub Provides:**
|
|
372
|
+
- Real-time connection pool monitoring
|
|
373
|
+
- Query performance metrics aggregation
|
|
374
|
+
- Database health status broadcasting
|
|
375
|
+
- Automatic service discovery for database adapters
|
|
376
|
+
|
|
377
|
+
**Integration Approach:**
|
|
378
|
+
- **Graceful Degradation**: All core functionality works without NeverHub
|
|
379
|
+
- **Auto-Detection**: Package detects NeverHub availability at runtime
|
|
380
|
+
- **Opt-In Publishing**: Metrics are published only when NeverHub is present
|
|
381
|
+
|
|
382
|
+
**Usage Example:**
|
|
383
|
+
```typescript
|
|
384
|
+
import { PostgreSQLAdapter } from '@bernierllc/database-adapter-postgresql';
|
|
385
|
+
import { NeverHubAdapter } from '@bernierllc/neverhub-adapter';
|
|
386
|
+
|
|
387
|
+
const adapter = new PostgreSQLAdapter({ /* config */ });
|
|
388
|
+
await adapter.connect();
|
|
389
|
+
|
|
390
|
+
// Optional: Publish metrics to NeverHub if available
|
|
391
|
+
if (await NeverHubAdapter.detect()) {
|
|
392
|
+
const neverhub = new NeverHubAdapter();
|
|
393
|
+
await neverhub.register({
|
|
394
|
+
type: 'database-adapter',
|
|
395
|
+
name: '@bernierllc/database-adapter-postgresql',
|
|
396
|
+
capabilities: [
|
|
397
|
+
{ type: 'storage', name: 'postgresql', version: '1.0.0' }
|
|
398
|
+
]
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// Publish pool statistics
|
|
402
|
+
setInterval(async () => {
|
|
403
|
+
const stats = adapter.getPoolStats();
|
|
404
|
+
await neverhub.publishEvent({
|
|
405
|
+
type: 'database.pool.stats',
|
|
406
|
+
data: stats
|
|
407
|
+
});
|
|
408
|
+
}, 30000); // Every 30 seconds
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Metrics Published:**
|
|
413
|
+
- `database.pool.stats` - Connection pool statistics (total, idle, waiting)
|
|
414
|
+
- `database.health` - Health check results (connected, latency, version)
|
|
415
|
+
- `database.query.performance` - Query execution metrics (optional, requires instrumentation)
|
|
416
|
+
|
|
417
|
+
**Why Optional:**
|
|
418
|
+
- Core packages should minimize dependencies
|
|
419
|
+
- Database operations must work in all environments
|
|
420
|
+
- NeverHub primarily benefits distributed systems with multiple services
|
|
421
|
+
- Single-instance applications don't need service discovery
|
|
422
|
+
|
|
423
|
+
## Performance
|
|
424
|
+
|
|
425
|
+
- **Query Latency**: <5ms (p95) for indexed queries
|
|
426
|
+
- **Connection Pool Reuse**: >90%
|
|
427
|
+
- **Real-time Latency**: <100ms for subscriptions
|
|
428
|
+
- **Search**: Fuzzy matching with typo tolerance via pg_trgm
|
|
429
|
+
|
|
430
|
+
## Security
|
|
431
|
+
|
|
432
|
+
- **SQL Injection Protection**: Parameterized queries only
|
|
433
|
+
- **RLS Enforcement**: Row-level security for all user data
|
|
434
|
+
- **Clerk JWT Verification**: Automatic user authentication
|
|
435
|
+
- **Tenant Isolation**: Multi-tenant data separation
|
|
436
|
+
|
|
437
|
+
## Testing
|
|
438
|
+
|
|
439
|
+
This package uses testcontainers for real PostgreSQL testing (no mocks).
|
|
440
|
+
|
|
441
|
+
```bash
|
|
442
|
+
npm test # Run tests in watch mode
|
|
443
|
+
npm run test:run # Run tests once
|
|
444
|
+
npm run test:coverage # Run with coverage report
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## See Also
|
|
448
|
+
|
|
449
|
+
- [@bernierllc/database-adapter-core](../database-adapter-core) - Abstract base classes
|
|
450
|
+
- [@bernierllc/logger](../logger) - Structured logging
|
|
451
|
+
- [@bernierllc/retry-policy](../retry-policy) - Retry logic
|
|
452
|
+
- [@bernierllc/crypto-utils](../crypto-utils) - Cryptographic utilities
|
|
453
|
+
|
|
454
|
+
## License
|
|
455
|
+
|
|
456
|
+
Copyright (c) 2025 Bernier LLC. All rights reserved.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
import { BaseDatabaseAdapter, IDatabaseConfig, IDatabaseHealth, IQueryResult, ITransaction } from '@bernierllc/database-adapter-core';
|
|
3
|
+
/**
|
|
4
|
+
* Supabase-specific configuration
|
|
5
|
+
*/
|
|
6
|
+
export interface ISupabaseConfig {
|
|
7
|
+
supabaseUrl?: string;
|
|
8
|
+
supabaseAnonKey?: string;
|
|
9
|
+
supabaseServiceKey?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* PostgreSQL database adapter with Supabase support
|
|
13
|
+
* Implements connection pooling, RLS, and real-time features
|
|
14
|
+
*/
|
|
15
|
+
export declare class PostgreSQLAdapter extends BaseDatabaseAdapter {
|
|
16
|
+
private pool;
|
|
17
|
+
private supabase;
|
|
18
|
+
private rlsManager;
|
|
19
|
+
private logger;
|
|
20
|
+
constructor(config: IDatabaseConfig & ISupabaseConfig);
|
|
21
|
+
/**
|
|
22
|
+
* Connect to PostgreSQL with connection pooling
|
|
23
|
+
*/
|
|
24
|
+
connect(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Disconnect and close pool
|
|
27
|
+
*/
|
|
28
|
+
disconnect(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Execute query with RLS context
|
|
31
|
+
*/
|
|
32
|
+
query<T = unknown>(sql: string, params?: unknown[]): Promise<IQueryResult<T>>;
|
|
33
|
+
/**
|
|
34
|
+
* Begin transaction with RLS context
|
|
35
|
+
*/
|
|
36
|
+
beginTransaction(): Promise<ITransaction>;
|
|
37
|
+
/**
|
|
38
|
+
* Health check with connection pool stats
|
|
39
|
+
*/
|
|
40
|
+
healthCheck(): Promise<IDatabaseHealth>;
|
|
41
|
+
/**
|
|
42
|
+
* Get Supabase client for real-time features
|
|
43
|
+
*/
|
|
44
|
+
getSupabaseClient(): SupabaseClient | null;
|
|
45
|
+
/**
|
|
46
|
+
* Set RLS context for current operation
|
|
47
|
+
*/
|
|
48
|
+
setRLSContext(userId: string, userRole: string, tenantId?: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Clear RLS context
|
|
51
|
+
*/
|
|
52
|
+
clearRLSContext(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Get current connection pool statistics
|
|
55
|
+
*/
|
|
56
|
+
getPoolStats(): {
|
|
57
|
+
total: number;
|
|
58
|
+
idle: number;
|
|
59
|
+
waiting: number;
|
|
60
|
+
} | null;
|
|
61
|
+
/**
|
|
62
|
+
* Map PostgreSQL data type OID to name
|
|
63
|
+
*/
|
|
64
|
+
private getDataTypeName;
|
|
65
|
+
}
|