@rineex/pg-slonik 0.0.0 → 0.2.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 +316 -101
- package/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @rineex/pg-slonik
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
>
|
|
3
|
+
> NestJS module for PostgreSQL using Slonik. Provides type-safe database
|
|
4
|
+
> connection management with dependency injection, automatic retry logic, and
|
|
5
|
+
> graceful shutdown handling.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@rineex/pg-slonik)
|
|
8
8
|
[](LICENSE)
|
|
@@ -21,17 +21,20 @@
|
|
|
21
21
|
|
|
22
22
|
## Overview
|
|
23
23
|
|
|
24
|
-
`@rineex/pg-slonik` is a
|
|
25
|
-
[Slonik](https://github.com/gajus/slonik), providing
|
|
26
|
-
|
|
24
|
+
`@rineex/pg-slonik` is a NestJS module wrapper for
|
|
25
|
+
[Slonik](https://github.com/gajus/slonik), providing seamless integration with
|
|
26
|
+
NestJS dependency injection, automatic connection retry logic, and graceful
|
|
27
|
+
shutdown handling.
|
|
27
28
|
|
|
28
29
|
### Key Features
|
|
29
30
|
|
|
31
|
+
- **NestJS Integration**: Full NestJS module support with dependency injection
|
|
32
|
+
- **Multiple Connections**: Support for multiple named database connections
|
|
30
33
|
- **Type-Safe Database Access**: Full TypeScript support with Slonik's type
|
|
31
34
|
system
|
|
32
|
-
- **
|
|
33
|
-
|
|
34
|
-
- **
|
|
35
|
+
- **Automatic Retry Logic**: Built-in connection retry with configurable
|
|
36
|
+
attempts and delays
|
|
37
|
+
- **Graceful Shutdown**: Automatic pool cleanup on application shutdown
|
|
35
38
|
- **SQL Safety**: Built-in SQL injection protection via Slonik
|
|
36
39
|
- **Transaction Support**: First-class transaction handling
|
|
37
40
|
- **Production Ready**: Used in high-performance systems at scale
|
|
@@ -51,114 +54,250 @@ yarn add @rineex/pg-slonik
|
|
|
51
54
|
- **Node.js**: 18.0 or higher
|
|
52
55
|
- **TypeScript**: 5.0 or higher (recommended: 5.9+)
|
|
53
56
|
- **PostgreSQL**: 12.0 or higher
|
|
54
|
-
- **@
|
|
57
|
+
- **@nestjs/common**: 11.0 or higher
|
|
55
58
|
|
|
56
59
|
## Quick Start
|
|
57
60
|
|
|
58
|
-
Here's a minimal example to get started:
|
|
61
|
+
Here's a minimal example to get started with NestJS:
|
|
59
62
|
|
|
60
63
|
```typescript
|
|
61
|
-
import {
|
|
62
|
-
import
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
import { Module } from '@nestjs/common';
|
|
65
|
+
import { SlonikModule } from '@rineex/pg-slonik';
|
|
66
|
+
|
|
67
|
+
@Module({
|
|
68
|
+
imports: [
|
|
69
|
+
SlonikModule.register({
|
|
70
|
+
connections: [
|
|
71
|
+
{
|
|
72
|
+
name: 'DEFAULT',
|
|
73
|
+
dsn: 'postgresql://user:password@localhost:5432/database',
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
export class AppModule {}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then inject the pool in your service:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { Injectable } from '@nestjs/common';
|
|
86
|
+
import { InjectPool } from '@rineex/pg-slonik';
|
|
87
|
+
import type { DatabasePool } from 'slonik';
|
|
71
88
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
);
|
|
89
|
+
@Injectable()
|
|
90
|
+
export class UserService {
|
|
91
|
+
constructor(@InjectPool() private readonly pool: DatabasePool) {}
|
|
76
92
|
|
|
77
|
-
|
|
78
|
-
await pool.
|
|
93
|
+
async findUser(id: string) {
|
|
94
|
+
const result = await this.pool.query(
|
|
95
|
+
this.pool.sql`SELECT * FROM users WHERE id = ${id}`,
|
|
96
|
+
);
|
|
97
|
+
return result.rows[0];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
79
100
|
```
|
|
80
101
|
|
|
81
102
|
## API Reference
|
|
82
103
|
|
|
83
|
-
### `
|
|
104
|
+
### `SlonikModule`
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
NestJS module for managing Slonik database connections.
|
|
86
107
|
|
|
87
|
-
#### `
|
|
108
|
+
#### `SlonikModule.register(options: SlonikModuleOptions): DynamicModule`
|
|
88
109
|
|
|
89
|
-
|
|
110
|
+
Registers the Slonik module with the provided configuration.
|
|
90
111
|
|
|
91
112
|
**Parameters:**
|
|
92
113
|
|
|
93
|
-
- `
|
|
94
|
-
- `
|
|
95
|
-
|
|
96
|
-
- `
|
|
97
|
-
- `
|
|
98
|
-
(
|
|
114
|
+
- `options.connections` (required): Array of connection configurations
|
|
115
|
+
- `connection.name` (required): Unique name for the connection (default:
|
|
116
|
+
'DEFAULT')
|
|
117
|
+
- `connection.dsn` (required): PostgreSQL connection string (DSN)
|
|
118
|
+
- `connection.options` (optional): Slonik `ClientConfigurationInput` options
|
|
119
|
+
- `connection.tags` (optional): Array of connection tags
|
|
120
|
+
- `options.isGlobal` (optional): Whether the module should be global (default:
|
|
121
|
+
true)
|
|
122
|
+
|
|
123
|
+
**Returns:** A configured NestJS `DynamicModule`
|
|
124
|
+
|
|
125
|
+
### `InjectPool(name?: string)`
|
|
126
|
+
|
|
127
|
+
Decorator for injecting a Slonik database pool into a class.
|
|
128
|
+
|
|
129
|
+
**Parameters:**
|
|
130
|
+
|
|
131
|
+
- `name` (optional): Name of the connection to inject (default: 'DEFAULT')
|
|
132
|
+
|
|
133
|
+
**Returns:** A NestJS `Inject` decorator
|
|
134
|
+
|
|
135
|
+
**Example:**
|
|
99
136
|
|
|
100
|
-
|
|
137
|
+
```typescript
|
|
138
|
+
@Injectable()
|
|
139
|
+
export class MyService {
|
|
140
|
+
constructor(@InjectPool('DEFAULT') private readonly pool: DatabasePool) {}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
101
143
|
|
|
102
144
|
### Type Exports
|
|
103
145
|
|
|
104
|
-
- `
|
|
105
|
-
- `
|
|
146
|
+
- `SlonikConnectionConfig`: Configuration interface for a single connection
|
|
147
|
+
- `SlonikModuleOptions`: Module configuration interface
|
|
148
|
+
- `SlonikModuleExtraOptions`: Additional module options (e.g., `isGlobal`)
|
|
106
149
|
|
|
107
150
|
## Examples
|
|
108
151
|
|
|
109
|
-
### Basic
|
|
152
|
+
### Basic Module Setup
|
|
110
153
|
|
|
111
154
|
```typescript
|
|
112
|
-
import {
|
|
155
|
+
import { Module } from '@nestjs/common';
|
|
156
|
+
import { SlonikModule } from '@rineex/pg-slonik';
|
|
157
|
+
|
|
158
|
+
@Module({
|
|
159
|
+
imports: [
|
|
160
|
+
SlonikModule.register({
|
|
161
|
+
connections: [
|
|
162
|
+
{
|
|
163
|
+
name: 'DEFAULT',
|
|
164
|
+
dsn: process.env.DATABASE_URL!,
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
}),
|
|
168
|
+
],
|
|
169
|
+
})
|
|
170
|
+
export class AppModule {}
|
|
171
|
+
```
|
|
113
172
|
|
|
114
|
-
|
|
115
|
-
connectionString: process.env.DATABASE_URL!,
|
|
116
|
-
});
|
|
173
|
+
### Multiple Connections
|
|
117
174
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
175
|
+
```typescript
|
|
176
|
+
import { Module } from '@nestjs/common';
|
|
177
|
+
import { SlonikModule } from '@rineex/pg-slonik';
|
|
178
|
+
|
|
179
|
+
@Module({
|
|
180
|
+
imports: [
|
|
181
|
+
SlonikModule.register({
|
|
182
|
+
connections: [
|
|
183
|
+
{
|
|
184
|
+
name: 'DEFAULT',
|
|
185
|
+
dsn: process.env.DATABASE_URL!,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: 'ANALYTICS',
|
|
189
|
+
dsn: process.env.ANALYTICS_DATABASE_URL!,
|
|
190
|
+
options: {
|
|
191
|
+
maximumPoolSize: 5,
|
|
192
|
+
statementTimeout: 30000,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
}),
|
|
197
|
+
],
|
|
198
|
+
})
|
|
199
|
+
export class AppModule {}
|
|
122
200
|
```
|
|
123
201
|
|
|
124
|
-
###
|
|
202
|
+
### Service with Dependency Injection
|
|
125
203
|
|
|
126
204
|
```typescript
|
|
127
|
-
import {
|
|
205
|
+
import { Injectable } from '@nestjs/common';
|
|
206
|
+
import { InjectPool } from '@rineex/pg-slonik';
|
|
207
|
+
import type { DatabasePool } from 'slonik';
|
|
128
208
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
}
|
|
209
|
+
@Injectable()
|
|
210
|
+
export class UserService {
|
|
211
|
+
constructor(@InjectPool() private readonly pool: DatabasePool) {}
|
|
132
212
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
213
|
+
async findAll() {
|
|
214
|
+
const result = await this.pool.query(
|
|
215
|
+
this.pool.sql`SELECT id, email FROM users LIMIT 10`,
|
|
216
|
+
);
|
|
217
|
+
return result.rows;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async findById(id: string) {
|
|
221
|
+
const result = await this.pool.query(
|
|
222
|
+
this.pool.sql`SELECT * FROM users WHERE id = ${id}`,
|
|
223
|
+
);
|
|
224
|
+
return result.rows[0] || null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
146
227
|
```
|
|
147
228
|
|
|
148
|
-
###
|
|
229
|
+
### Using Named Connections
|
|
149
230
|
|
|
150
231
|
```typescript
|
|
151
|
-
import {
|
|
152
|
-
import
|
|
153
|
-
import {
|
|
232
|
+
import { Injectable } from '@nestjs/common';
|
|
233
|
+
import { InjectPool } from '@rineex/pg-slonik';
|
|
234
|
+
import type { DatabasePool } from 'slonik';
|
|
235
|
+
|
|
236
|
+
@Injectable()
|
|
237
|
+
export class AnalyticsService {
|
|
238
|
+
constructor(
|
|
239
|
+
@InjectPool('ANALYTICS') private readonly analyticsPool: DatabasePool,
|
|
240
|
+
) {}
|
|
241
|
+
|
|
242
|
+
async getMetrics() {
|
|
243
|
+
const result = await this.analyticsPool.query(
|
|
244
|
+
this.analyticsPool.sql`SELECT * FROM metrics ORDER BY created_at DESC`,
|
|
245
|
+
);
|
|
246
|
+
return result.rows;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
154
250
|
|
|
155
|
-
|
|
156
|
-
constructor(private readonly pool: SlonikPool) {}
|
|
251
|
+
### Transaction Handling
|
|
157
252
|
|
|
158
|
-
|
|
253
|
+
```typescript
|
|
254
|
+
import { Injectable } from '@nestjs/common';
|
|
255
|
+
import { InjectPool } from '@rineex/pg-slonik';
|
|
256
|
+
import type { DatabasePool } from 'slonik';
|
|
257
|
+
|
|
258
|
+
@Injectable()
|
|
259
|
+
export class UserService {
|
|
260
|
+
constructor(@InjectPool() private readonly pool: DatabasePool) {}
|
|
261
|
+
|
|
262
|
+
async createUserWithProfile(userId: string, email: string, name: string) {
|
|
263
|
+
await this.pool.transaction(async transactionConnection => {
|
|
264
|
+
await transactionConnection.query(
|
|
265
|
+
transactionConnection.sql`
|
|
266
|
+
INSERT INTO users (id, email) VALUES (${userId}, ${email})
|
|
267
|
+
`,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
await transactionConnection.query(
|
|
271
|
+
transactionConnection.sql`
|
|
272
|
+
INSERT INTO user_profiles (user_id, name) VALUES (${userId}, ${name})
|
|
273
|
+
`,
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Repository Pattern
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { Injectable } from '@nestjs/common';
|
|
284
|
+
import { InjectPool } from '@rineex/pg-slonik';
|
|
285
|
+
import type { DatabasePool } from 'slonik';
|
|
286
|
+
|
|
287
|
+
interface User {
|
|
288
|
+
id: string;
|
|
289
|
+
email: string;
|
|
290
|
+
createdAt: Date;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@Injectable()
|
|
294
|
+
export class UserRepository {
|
|
295
|
+
constructor(@InjectPool() private readonly pool: DatabasePool) {}
|
|
296
|
+
|
|
297
|
+
async findById(id: string) {
|
|
159
298
|
const result = await this.pool.query(
|
|
160
299
|
this.pool.sql`
|
|
161
|
-
SELECT * FROM users WHERE id = ${id
|
|
300
|
+
SELECT * FROM users WHERE id = ${id}
|
|
162
301
|
`,
|
|
163
302
|
);
|
|
164
303
|
|
|
@@ -166,50 +305,89 @@ class UserRepository {
|
|
|
166
305
|
return null;
|
|
167
306
|
}
|
|
168
307
|
|
|
169
|
-
// Map database row to domain
|
|
170
|
-
return this.
|
|
308
|
+
// Map database row to domain object
|
|
309
|
+
return this.mapToUser(result.rows[0]);
|
|
171
310
|
}
|
|
172
311
|
|
|
173
312
|
async save(user: User) {
|
|
174
313
|
await this.pool.query(
|
|
175
314
|
this.pool.sql`
|
|
176
315
|
INSERT INTO users (id, email, created_at)
|
|
177
|
-
VALUES (${user.id
|
|
316
|
+
VALUES (${user.id}, ${user.email}, ${user.createdAt})
|
|
178
317
|
ON CONFLICT (id) DO UPDATE
|
|
179
|
-
SET email = ${user.email
|
|
318
|
+
SET email = ${user.email}
|
|
180
319
|
`,
|
|
181
320
|
);
|
|
182
321
|
}
|
|
183
322
|
|
|
184
|
-
private
|
|
185
|
-
|
|
323
|
+
private mapToUser(row: any): User {
|
|
324
|
+
return {
|
|
325
|
+
id: row.id,
|
|
326
|
+
email: row.email,
|
|
327
|
+
createdAt: row.created_at,
|
|
328
|
+
};
|
|
186
329
|
}
|
|
187
330
|
}
|
|
188
331
|
```
|
|
189
332
|
|
|
333
|
+
### Advanced Configuration
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { Module } from '@nestjs/common';
|
|
337
|
+
import { SlonikModule } from '@rineex/pg-slonik';
|
|
338
|
+
|
|
339
|
+
@Module({
|
|
340
|
+
imports: [
|
|
341
|
+
SlonikModule.register({
|
|
342
|
+
connections: [
|
|
343
|
+
{
|
|
344
|
+
name: 'DEFAULT',
|
|
345
|
+
dsn: process.env.DATABASE_URL!,
|
|
346
|
+
options: {
|
|
347
|
+
maximumPoolSize: 20,
|
|
348
|
+
idleTimeout: 5000,
|
|
349
|
+
statementTimeout: 60000,
|
|
350
|
+
connectionTimeout: 10000,
|
|
351
|
+
interceptors: [
|
|
352
|
+
// Add custom interceptors here
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
tags: ['primary', 'read-write'],
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
isGlobal: true, // Make the module global
|
|
359
|
+
}),
|
|
360
|
+
],
|
|
361
|
+
})
|
|
362
|
+
export class AppModule {}
|
|
363
|
+
```
|
|
364
|
+
|
|
190
365
|
## Best Practices
|
|
191
366
|
|
|
192
|
-
### 1. **Use
|
|
367
|
+
### 1. **Use Dependency Injection**
|
|
193
368
|
|
|
194
|
-
Always
|
|
369
|
+
Always inject the pool using the `@InjectPool()` decorator instead of creating
|
|
370
|
+
pools manually:
|
|
195
371
|
|
|
196
372
|
```typescript
|
|
197
|
-
// ❌ BAD: Creating
|
|
198
|
-
const
|
|
373
|
+
// ❌ BAD: Creating pools manually
|
|
374
|
+
const pool = await createPool(connectionString);
|
|
199
375
|
|
|
200
|
-
// ✅ GOOD: Using
|
|
201
|
-
|
|
376
|
+
// ✅ GOOD: Using dependency injection
|
|
377
|
+
@Injectable()
|
|
378
|
+
export class MyService {
|
|
379
|
+
constructor(@InjectPool() private readonly pool: DatabasePool) {}
|
|
380
|
+
}
|
|
202
381
|
```
|
|
203
382
|
|
|
204
|
-
### 2. **
|
|
383
|
+
### 2. **Automatic Pool Cleanup**
|
|
205
384
|
|
|
206
|
-
|
|
385
|
+
The module automatically closes all connection pools on application shutdown.
|
|
386
|
+
You don't need to manually handle cleanup:
|
|
207
387
|
|
|
208
388
|
```typescript
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
process.exit(0);
|
|
212
|
-
});
|
|
389
|
+
// ✅ GOOD: Automatic cleanup via OnApplicationShutdown
|
|
390
|
+
// The SlonikModule handles this automatically
|
|
213
391
|
```
|
|
214
392
|
|
|
215
393
|
### 3. **Use Tagged Template Literals**
|
|
@@ -222,7 +400,7 @@ injection:
|
|
|
222
400
|
const query = `SELECT * FROM users WHERE id = '${userId}'`;
|
|
223
401
|
|
|
224
402
|
// ✅ GOOD: Tagged template literal
|
|
225
|
-
const query = pool.sql`SELECT * FROM users WHERE id = ${userId}`;
|
|
403
|
+
const query = this.pool.sql`SELECT * FROM users WHERE id = ${userId}`;
|
|
226
404
|
```
|
|
227
405
|
|
|
228
406
|
### 4. **Handle Transactions Properly**
|
|
@@ -230,7 +408,7 @@ const query = pool.sql`SELECT * FROM users WHERE id = ${userId}`;
|
|
|
230
408
|
Use transactions for operations that must be atomic:
|
|
231
409
|
|
|
232
410
|
```typescript
|
|
233
|
-
await pool.transaction(async
|
|
411
|
+
await this.pool.transaction(async transactionConnection => {
|
|
234
412
|
// All queries in this block are part of the same transaction
|
|
235
413
|
await transactionConnection.query(/* ... */);
|
|
236
414
|
await transactionConnection.query(/* ... */);
|
|
@@ -244,18 +422,55 @@ Keep database concerns separate from domain logic:
|
|
|
244
422
|
|
|
245
423
|
```typescript
|
|
246
424
|
// ❌ BAD: Exposing database rows directly
|
|
247
|
-
async
|
|
248
|
-
const result = await pool.query(/* ... */);
|
|
425
|
+
async getUser(id: string) {
|
|
426
|
+
const result = await this.pool.query(/* ... */);
|
|
249
427
|
return result.rows[0]; // Raw database row
|
|
250
428
|
}
|
|
251
429
|
|
|
252
430
|
// ✅ GOOD: Mapping to domain objects
|
|
253
|
-
async
|
|
254
|
-
const result = await pool.query(/* ... */);
|
|
255
|
-
return this.mapToUser(result.rows[0]); // Domain
|
|
431
|
+
async getUser(id: string) {
|
|
432
|
+
const result = await this.pool.query(/* ... */);
|
|
433
|
+
return this.mapToUser(result.rows[0]); // Domain object
|
|
256
434
|
}
|
|
257
435
|
```
|
|
258
436
|
|
|
437
|
+
### 6. **Use Named Connections for Multiple Databases**
|
|
438
|
+
|
|
439
|
+
When working with multiple databases, use named connections:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
// ✅ GOOD: Named connections for different databases
|
|
443
|
+
SlonikModule.register({
|
|
444
|
+
connections: [
|
|
445
|
+
{ name: 'DEFAULT', dsn: process.env.DATABASE_URL! },
|
|
446
|
+
{ name: 'ANALYTICS', dsn: process.env.ANALYTICS_DB_URL! },
|
|
447
|
+
],
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Then inject the specific connection
|
|
451
|
+
@InjectPool('ANALYTICS') private readonly analyticsPool: DatabasePool
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### 7. **Configure Connection Options**
|
|
455
|
+
|
|
456
|
+
Use the `options` field to configure pool behavior:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
SlonikModule.register({
|
|
460
|
+
connections: [
|
|
461
|
+
{
|
|
462
|
+
name: 'DEFAULT',
|
|
463
|
+
dsn: process.env.DATABASE_URL!,
|
|
464
|
+
options: {
|
|
465
|
+
maximumPoolSize: 20,
|
|
466
|
+
statementTimeout: 60000,
|
|
467
|
+
// Add other Slonik configuration options
|
|
468
|
+
},
|
|
469
|
+
},
|
|
470
|
+
],
|
|
471
|
+
});
|
|
472
|
+
```
|
|
473
|
+
|
|
259
474
|
## Contributing
|
|
260
475
|
|
|
261
476
|
Contributions are welcome! Please follow these guidelines:
|
|
@@ -296,7 +511,7 @@ This project is licensed under the Apache License 2.0 - see the
|
|
|
296
511
|
|
|
297
512
|
- [Slonik Documentation](https://github.com/gajus/slonik)
|
|
298
513
|
- [PostgreSQL Documentation](https://www.postgresql.org/docs/)
|
|
299
|
-
- [
|
|
514
|
+
- [NestJS Documentation](https://docs.nestjs.com/)
|
|
300
515
|
|
|
301
516
|
## Support
|
|
302
517
|
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
+
import { OnApplicationShutdown, DynamicModule } from '@nestjs/common';
|
|
3
|
+
import { ClientConfigurationInput } from 'slonik';
|
|
4
|
+
|
|
5
|
+
declare function InjectPool(name?: string): PropertyDecorator & ParameterDecorator;
|
|
6
|
+
|
|
7
|
+
type SlonikConnectionName = string;
|
|
8
|
+
type SlonikConnectionTags = string;
|
|
9
|
+
interface SlonikConnectionConfig {
|
|
10
|
+
name: SlonikConnectionName;
|
|
11
|
+
options?: ClientConfigurationInput;
|
|
12
|
+
dsn: string;
|
|
13
|
+
tags?: SlonikConnectionTags[];
|
|
14
|
+
}
|
|
15
|
+
interface SlonikModuleOptions {
|
|
16
|
+
connections: SlonikConnectionConfig[];
|
|
17
|
+
}
|
|
18
|
+
interface SlonikModuleExtraOptions {
|
|
19
|
+
isGlobal?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare const SlonikCoreModule: _nestjs_common.ConfigurableModuleCls<SlonikModuleOptions, "register", "create", SlonikModuleExtraOptions>;
|
|
23
|
+
declare const MODULE_OPTIONS_TOKEN: string | symbol;
|
|
24
|
+
declare const OPTIONS_TYPE: SlonikModuleOptions & Partial<SlonikModuleExtraOptions>;
|
|
25
|
+
declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<SlonikModuleOptions, "create"> & Partial<SlonikModuleExtraOptions>;
|
|
26
|
+
|
|
27
|
+
declare class SlonikModule extends SlonikCoreModule implements OnApplicationShutdown {
|
|
28
|
+
private static pools;
|
|
29
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule;
|
|
30
|
+
onApplicationShutdown(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { ASYNC_OPTIONS_TYPE, InjectPool, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, SlonikCoreModule, SlonikModule };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
+
import { OnApplicationShutdown, DynamicModule } from '@nestjs/common';
|
|
3
|
+
import { ClientConfigurationInput } from 'slonik';
|
|
4
|
+
|
|
5
|
+
declare function InjectPool(name?: string): PropertyDecorator & ParameterDecorator;
|
|
6
|
+
|
|
7
|
+
type SlonikConnectionName = string;
|
|
8
|
+
type SlonikConnectionTags = string;
|
|
9
|
+
interface SlonikConnectionConfig {
|
|
10
|
+
name: SlonikConnectionName;
|
|
11
|
+
options?: ClientConfigurationInput;
|
|
12
|
+
dsn: string;
|
|
13
|
+
tags?: SlonikConnectionTags[];
|
|
14
|
+
}
|
|
15
|
+
interface SlonikModuleOptions {
|
|
16
|
+
connections: SlonikConnectionConfig[];
|
|
17
|
+
}
|
|
18
|
+
interface SlonikModuleExtraOptions {
|
|
19
|
+
isGlobal?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare const SlonikCoreModule: _nestjs_common.ConfigurableModuleCls<SlonikModuleOptions, "register", "create", SlonikModuleExtraOptions>;
|
|
23
|
+
declare const MODULE_OPTIONS_TOKEN: string | symbol;
|
|
24
|
+
declare const OPTIONS_TYPE: SlonikModuleOptions & Partial<SlonikModuleExtraOptions>;
|
|
25
|
+
declare const ASYNC_OPTIONS_TYPE: _nestjs_common.ConfigurableModuleAsyncOptions<SlonikModuleOptions, "create"> & Partial<SlonikModuleExtraOptions>;
|
|
26
|
+
|
|
27
|
+
declare class SlonikModule extends SlonikCoreModule implements OnApplicationShutdown {
|
|
28
|
+
private static pools;
|
|
29
|
+
static register(options: typeof OPTIONS_TYPE): DynamicModule;
|
|
30
|
+
onApplicationShutdown(): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export { ASYNC_OPTIONS_TYPE, InjectPool, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE, SlonikCoreModule, SlonikModule };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var u=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames,b=Object.getOwnPropertySymbols;var y=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable;var S=(t,o,e)=>o in t?u(t,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[o]=e,E=(t,o)=>{for(var e in o||(o={}))y.call(o,e)&&S(t,e,o[e]);if(b)for(var e of b(o))D.call(o,e)&&S(t,e,o[e]);return t};var I=(t,o)=>{for(var e in o)u(t,e,{get:o[e],enumerable:!0})},h=(t,o,e,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of w(o))!y.call(t,r)&&r!==e&&u(t,r,{get:()=>o[r],enumerable:!(n=_(o,r))||n.enumerable});return t};var F=t=>h(u({},"__esModule",{value:!0}),t),g=(t,o,e,n)=>{for(var r=n>1?void 0:n?_(o,e):o,l=t.length-1,i;l>=0;l--)(i=t[l])&&(r=(n?i(o,e,r):i(r))||r);return n&&r&&u(o,e,r),r};var d=(t,o,e)=>new Promise((n,r)=>{var l=a=>{try{s(e.next(a))}catch(O){r(O)}},i=a=>{try{s(e.throw(a))}catch(O){r(O)}},s=a=>a.done?n(a.value):Promise.resolve(a.value).then(l,i);s((e=e.apply(t,o)).next())});var G={};I(G,{ASYNC_OPTIONS_TYPE:()=>$,InjectPool:()=>U,MODULE_OPTIONS_TOKEN:()=>Y,OPTIONS_TYPE:()=>j,SlonikCoreModule:()=>T,SlonikModule:()=>m});module.exports=F(G);var N=require("@nestjs/common");var k="__SLONIK_%s_POOL__",c="DEFAULT";var x=require("@nestjs/common"),P=require("util"),p=require("rxjs"),L=require("slonik");var f=(t=c)=>(0,P.format)(k,t.toUpperCase()),v=new x.Logger("SlonikModule");function C({dsn:t,name:o,options:e}){let n=f(o);return(0,p.lastValueFrom)((0,p.defer)(()=>d(null,null,function*(){let r=yield(0,L.createPool)(t,e);return yield r.connect(()=>Promise.resolve()).then(()=>{v.log(`Connected to database ${n}`)}),r})).pipe(K()))}function K(t=9,o=3e3,e=c,n=!1,r){return l=>l.pipe((0,p.retry)({count:t,delay:(i,s)=>{if(r&&!r(i))return(0,p.throwError)(()=>i);let a=e===c?"":` (${e})`,O=n?` Message: ${i.message}.`:"";return v.error((0,P.format)("unable to connect to the database#%s.%s Retrying (%s)",a,O,s),i.stack),(0,p.of)(null).pipe((0,p.delay)(o))}}))}function U(t=c){return(0,N.Inject)(f(t))}var A=require("@nestjs/common");var M=require("@nestjs/common");var{ConfigurableModuleClass:T,MODULE_OPTIONS_TOKEN:Y,OPTIONS_TYPE:j,ASYNC_OPTIONS_TYPE:$}=new M.ConfigurableModuleBuilder().setExtras({isGlobal:!0},(t,o)=>E({global:o.isGlobal},t)).build();var m=class extends T{static register(o){var l,i;let{connections:e}=o,n=super.register(o);(l=n.providers)!=null||(n.providers=[]),(i=n.exports)!=null||(n.exports=[]);let r=e.map(s=>({provide:f(s.name),useFactory:()=>d(this,null,function*(){let a=yield C({dsn:s.dsn,options:s.options,name:s.name});return this.pools.set(s.name,a),a})}));return n.providers.push(...r),n.exports.push(...e.map(s=>f(s.name))),n}onApplicationShutdown(){return d(this,null,function*(){yield Promise.allSettled(Array.from(m.pools.values()).map(o=>o.end()))})}};m.pools=new Map,m=g([(0,A.Module)({})],m);0&&(module.exports={ASYNC_OPTIONS_TYPE,InjectPool,MODULE_OPTIONS_TOKEN,OPTIONS_TYPE,SlonikCoreModule,SlonikModule});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/slonik.decorator.ts","../src/constants/index.ts","../src/slonik.util.ts","../src/slonik.module.ts","../src/slonik.module-definition.ts"],"sourcesContent":["export * from './slonik.decorator';\nexport * from './slonik.module';\nexport * from './slonik.module-definition';\n","import { Inject } from '@nestjs/common';\n\nimport { DEFAULT_POOL_NAME } from './constants';\nimport { createSlonikToken } from './slonik.util';\n\nexport function InjectPool(name = DEFAULT_POOL_NAME) {\n return Inject(createSlonikToken(name));\n}\n","/**\n * The token for the Slonik pool\n * @example `createSlonikToken('DEFAULT')` will return '__SLONIK_DEFAULT_POOL__'\n */\nexport const SLONIK_POOL_TOKEN = '__SLONIK_%s_POOL__';\n\n/**\n * The default name for the Slonik pool\n */\nexport const DEFAULT_POOL_NAME = 'DEFAULT';\n","import type { Observable } from 'rxjs';\nimport type { DatabasePool } from 'slonik';\n\nimport { Logger } from '@nestjs/common';\nimport { format } from 'node:util';\nimport { defer, delay, lastValueFrom, of, retry, throwError } from 'rxjs';\nimport { createPool } from 'slonik';\n\nimport type { SlonikConnectionConfig } from './slonik.interface';\n\nimport { DEFAULT_POOL_NAME, SLONIK_POOL_TOKEN } from './constants';\n\nexport const createSlonikToken = (name: string = DEFAULT_POOL_NAME) =>\n format(SLONIK_POOL_TOKEN, name.toUpperCase());\n\nconst logger = new Logger('SlonikModule');\n\nexport function createSlonikConnection({\n dsn,\n name,\n options,\n}: SlonikConnectionConfig): Promise<DatabasePool> {\n const token = createSlonikToken(name);\n\n return lastValueFrom(\n defer(async () => {\n const pool = await createPool(dsn, options);\n\n await pool\n .connect(() => Promise.resolve())\n .then(() => {\n logger.log(`Connected to database ${token}`);\n });\n return pool;\n }).pipe(handleRetry()),\n );\n}\n\nexport function handleRetry(\n retryAttempts = 9,\n retryDelay = 3000,\n poolName = DEFAULT_POOL_NAME,\n verboseRetryLog = false,\n toRetry?: (err: any) => boolean,\n): <T>(source: Observable<T>) => Observable<T> {\n return <T>(source: Observable<T>) =>\n source.pipe(\n retry({\n count: retryAttempts,\n delay: (error, retryCount) => {\n if (toRetry && !toRetry(error)) {\n return throwError(() => error);\n }\n\n const poolInfo =\n poolName === DEFAULT_POOL_NAME ? '' : ` (${poolName})`;\n const verboseMessage = verboseRetryLog\n ? ` Message: ${error.message}.`\n : '';\n\n logger.error(\n format(\n 'unable to connect to the database#%s.%s Retrying (%s)',\n poolInfo,\n verboseMessage,\n retryCount,\n ),\n error.stack,\n );\n\n return of(null).pipe(delay(retryDelay));\n },\n }),\n );\n}\n","import type {\n DynamicModule,\n OnApplicationShutdown,\n Provider,\n} from '@nestjs/common';\nimport type { DatabasePool } from 'slonik';\n\nimport { Module } from '@nestjs/common';\n\nimport type { OPTIONS_TYPE } from './slonik.module-definition';\n\nimport { SlonikCoreModule } from './slonik.module-definition';\nimport { createSlonikConnection, createSlonikToken } from './slonik.util';\n\n@Module({})\nexport class SlonikModule\n extends SlonikCoreModule\n implements OnApplicationShutdown\n{\n private static pools = new Map<string, DatabasePool>();\n\n static register(options: typeof OPTIONS_TYPE): DynamicModule {\n const { connections } = options;\n\n const module = super.register(options);\n\n module.providers ??= [];\n module.exports ??= [];\n\n const providers: Provider[] = connections.map(conn => ({\n provide: createSlonikToken(conn.name),\n useFactory: async () => {\n const connection = await createSlonikConnection({\n dsn: conn.dsn,\n options: conn.options,\n name: conn.name,\n });\n this.pools.set(conn.name, connection);\n return connection;\n },\n }));\n\n module.providers.push(...providers);\n module.exports.push(...connections.map(c => createSlonikToken(c.name)));\n\n return module;\n }\n\n async onApplicationShutdown() {\n await Promise.allSettled(\n Array.from(SlonikModule.pools.values()).map(pool => pool.end()),\n );\n }\n}\n","import { ConfigurableModuleBuilder } from '@nestjs/common';\n\nimport type {\n SlonikModuleExtraOptions,\n SlonikModuleOptions,\n} from './slonik.interface';\n\nexport const {\n ConfigurableModuleClass: SlonikCoreModule,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n ASYNC_OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<SlonikModuleOptions>()\n .setExtras<SlonikModuleExtraOptions>(\n {\n isGlobal: true,\n },\n (definition, extra) => {\n return { global: extra.isGlobal, ...definition };\n },\n )\n .build();\n"],"mappings":"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","names":["index_exports","__export","ASYNC_OPTIONS_TYPE","InjectPool","MODULE_OPTIONS_TOKEN","OPTIONS_TYPE","SlonikCoreModule","SlonikModule","__toCommonJS","import_common","SLONIK_POOL_TOKEN","DEFAULT_POOL_NAME","import_common","import_node_util","import_rxjs","import_slonik","createSlonikToken","name","DEFAULT_POOL_NAME","SLONIK_POOL_TOKEN","logger","createSlonikConnection","dsn","options","token","__async","pool","handleRetry","retryAttempts","retryDelay","poolName","verboseRetryLog","toRetry","source","error","retryCount","poolInfo","verboseMessage","InjectPool","name","DEFAULT_POOL_NAME","createSlonikToken","import_common","import_common","SlonikCoreModule","MODULE_OPTIONS_TOKEN","OPTIONS_TYPE","ASYNC_OPTIONS_TYPE","definition","extra","__spreadValues","SlonikModule","SlonikCoreModule","options","_a","_b","connections","module","providers","conn","createSlonikToken","__async","connection","createSlonikConnection","c","pool","__decorateClass"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var P=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertySymbols;var x=Object.prototype.hasOwnProperty,L=Object.prototype.propertyIsEnumerable;var d=(t,o,e)=>o in t?P(t,o,{enumerable:!0,configurable:!0,writable:!0,value:e}):t[o]=e,T=(t,o)=>{for(var e in o||(o={}))x.call(o,e)&&d(t,e,o[e]);if(u)for(var e of u(o))L.call(o,e)&&d(t,e,o[e]);return t};var b=(t,o,e,r)=>{for(var n=r>1?void 0:r?k(o,e):o,p=t.length-1,i;p>=0;p--)(i=t[p])&&(n=(r?i(o,e,n):i(n))||n);return r&&n&&P(o,e,n),n};var O=(t,o,e)=>new Promise((r,n)=>{var p=a=>{try{s(e.next(a))}catch(f){n(f)}},i=a=>{try{s(e.throw(a))}catch(f){n(f)}},s=a=>a.done?r(a.value):Promise.resolve(a.value).then(p,i);s((e=e.apply(t,o)).next())});import{Inject as F}from"@nestjs/common";var S="__SLONIK_%s_POOL__",m="DEFAULT";import{Logger as v}from"@nestjs/common";import{format as _}from"util";import{defer as C,delay as N,lastValueFrom as M,of as A,retry as w,throwError as D}from"rxjs";import{createPool as I}from"slonik";var c=(t=m)=>_(S,t.toUpperCase()),y=new v("SlonikModule");function E({dsn:t,name:o,options:e}){let r=c(o);return M(C(()=>O(null,null,function*(){let n=yield I(t,e);return yield n.connect(()=>Promise.resolve()).then(()=>{y.log(`Connected to database ${r}`)}),n})).pipe(h()))}function h(t=9,o=3e3,e=m,r=!1,n){return p=>p.pipe(w({count:t,delay:(i,s)=>{if(n&&!n(i))return D(()=>i);let a=e===m?"":` (${e})`,f=r?` Message: ${i.message}.`:"";return y.error(_("unable to connect to the database#%s.%s Retrying (%s)",a,f,s),i.stack),A(null).pipe(N(o))}}))}function W(t=m){return F(c(t))}import{Module as U}from"@nestjs/common";import{ConfigurableModuleBuilder as K}from"@nestjs/common";var{ConfigurableModuleClass:g,MODULE_OPTIONS_TOKEN:oo,OPTIONS_TYPE:eo,ASYNC_OPTIONS_TYPE:to}=new K().setExtras({isGlobal:!0},(t,o)=>T({global:o.isGlobal},t)).build();var l=class extends g{static register(o){var p,i;let{connections:e}=o,r=super.register(o);(p=r.providers)!=null||(r.providers=[]),(i=r.exports)!=null||(r.exports=[]);let n=e.map(s=>({provide:c(s.name),useFactory:()=>O(this,null,function*(){let a=yield E({dsn:s.dsn,options:s.options,name:s.name});return this.pools.set(s.name,a),a})}));return r.providers.push(...n),r.exports.push(...e.map(s=>c(s.name))),r}onApplicationShutdown(){return O(this,null,function*(){yield Promise.allSettled(Array.from(l.pools.values()).map(o=>o.end()))})}};l.pools=new Map,l=b([U({})],l);export{to as ASYNC_OPTIONS_TYPE,W as InjectPool,oo as MODULE_OPTIONS_TOKEN,eo as OPTIONS_TYPE,g as SlonikCoreModule,l as SlonikModule};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/slonik.decorator.ts","../src/constants/index.ts","../src/slonik.util.ts","../src/slonik.module.ts","../src/slonik.module-definition.ts"],"sourcesContent":["import { Inject } from '@nestjs/common';\n\nimport { DEFAULT_POOL_NAME } from './constants';\nimport { createSlonikToken } from './slonik.util';\n\nexport function InjectPool(name = DEFAULT_POOL_NAME) {\n return Inject(createSlonikToken(name));\n}\n","/**\n * The token for the Slonik pool\n * @example `createSlonikToken('DEFAULT')` will return '__SLONIK_DEFAULT_POOL__'\n */\nexport const SLONIK_POOL_TOKEN = '__SLONIK_%s_POOL__';\n\n/**\n * The default name for the Slonik pool\n */\nexport const DEFAULT_POOL_NAME = 'DEFAULT';\n","import type { Observable } from 'rxjs';\nimport type { DatabasePool } from 'slonik';\n\nimport { Logger } from '@nestjs/common';\nimport { format } from 'node:util';\nimport { defer, delay, lastValueFrom, of, retry, throwError } from 'rxjs';\nimport { createPool } from 'slonik';\n\nimport type { SlonikConnectionConfig } from './slonik.interface';\n\nimport { DEFAULT_POOL_NAME, SLONIK_POOL_TOKEN } from './constants';\n\nexport const createSlonikToken = (name: string = DEFAULT_POOL_NAME) =>\n format(SLONIK_POOL_TOKEN, name.toUpperCase());\n\nconst logger = new Logger('SlonikModule');\n\nexport function createSlonikConnection({\n dsn,\n name,\n options,\n}: SlonikConnectionConfig): Promise<DatabasePool> {\n const token = createSlonikToken(name);\n\n return lastValueFrom(\n defer(async () => {\n const pool = await createPool(dsn, options);\n\n await pool\n .connect(() => Promise.resolve())\n .then(() => {\n logger.log(`Connected to database ${token}`);\n });\n return pool;\n }).pipe(handleRetry()),\n );\n}\n\nexport function handleRetry(\n retryAttempts = 9,\n retryDelay = 3000,\n poolName = DEFAULT_POOL_NAME,\n verboseRetryLog = false,\n toRetry?: (err: any) => boolean,\n): <T>(source: Observable<T>) => Observable<T> {\n return <T>(source: Observable<T>) =>\n source.pipe(\n retry({\n count: retryAttempts,\n delay: (error, retryCount) => {\n if (toRetry && !toRetry(error)) {\n return throwError(() => error);\n }\n\n const poolInfo =\n poolName === DEFAULT_POOL_NAME ? '' : ` (${poolName})`;\n const verboseMessage = verboseRetryLog\n ? ` Message: ${error.message}.`\n : '';\n\n logger.error(\n format(\n 'unable to connect to the database#%s.%s Retrying (%s)',\n poolInfo,\n verboseMessage,\n retryCount,\n ),\n error.stack,\n );\n\n return of(null).pipe(delay(retryDelay));\n },\n }),\n );\n}\n","import type {\n DynamicModule,\n OnApplicationShutdown,\n Provider,\n} from '@nestjs/common';\nimport type { DatabasePool } from 'slonik';\n\nimport { Module } from '@nestjs/common';\n\nimport type { OPTIONS_TYPE } from './slonik.module-definition';\n\nimport { SlonikCoreModule } from './slonik.module-definition';\nimport { createSlonikConnection, createSlonikToken } from './slonik.util';\n\n@Module({})\nexport class SlonikModule\n extends SlonikCoreModule\n implements OnApplicationShutdown\n{\n private static pools = new Map<string, DatabasePool>();\n\n static register(options: typeof OPTIONS_TYPE): DynamicModule {\n const { connections } = options;\n\n const module = super.register(options);\n\n module.providers ??= [];\n module.exports ??= [];\n\n const providers: Provider[] = connections.map(conn => ({\n provide: createSlonikToken(conn.name),\n useFactory: async () => {\n const connection = await createSlonikConnection({\n dsn: conn.dsn,\n options: conn.options,\n name: conn.name,\n });\n this.pools.set(conn.name, connection);\n return connection;\n },\n }));\n\n module.providers.push(...providers);\n module.exports.push(...connections.map(c => createSlonikToken(c.name)));\n\n return module;\n }\n\n async onApplicationShutdown() {\n await Promise.allSettled(\n Array.from(SlonikModule.pools.values()).map(pool => pool.end()),\n );\n }\n}\n","import { ConfigurableModuleBuilder } from '@nestjs/common';\n\nimport type {\n SlonikModuleExtraOptions,\n SlonikModuleOptions,\n} from './slonik.interface';\n\nexport const {\n ConfigurableModuleClass: SlonikCoreModule,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n ASYNC_OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<SlonikModuleOptions>()\n .setExtras<SlonikModuleExtraOptions>(\n {\n isGlobal: true,\n },\n (definition, extra) => {\n return { global: extra.isGlobal, ...definition };\n },\n )\n .build();\n"],"mappings":"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","names":["Inject","SLONIK_POOL_TOKEN","DEFAULT_POOL_NAME","Logger","format","defer","delay","lastValueFrom","of","retry","throwError","createPool","createSlonikToken","name","DEFAULT_POOL_NAME","format","SLONIK_POOL_TOKEN","logger","Logger","createSlonikConnection","dsn","options","token","lastValueFrom","defer","__async","pool","createPool","handleRetry","retryAttempts","retryDelay","poolName","verboseRetryLog","toRetry","source","retry","error","retryCount","throwError","poolInfo","verboseMessage","of","delay","InjectPool","name","DEFAULT_POOL_NAME","Inject","createSlonikToken","Module","ConfigurableModuleBuilder","SlonikCoreModule","MODULE_OPTIONS_TOKEN","OPTIONS_TYPE","ASYNC_OPTIONS_TYPE","ConfigurableModuleBuilder","definition","extra","__spreadValues","SlonikModule","SlonikCoreModule","options","_a","_b","connections","module","providers","conn","createSlonikToken","__async","connection","createSlonikConnection","c","pool","__decorateClass","Module"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rineex/pg-slonik",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "PostgreSQL adapter using Slonik for Rineex core modules",
|
|
5
5
|
"author": "Rineex Team",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"@rineex/typescript-config": "0.0.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
26
|
+
"@nestjs/common": "11.1.11",
|
|
27
|
+
"rxjs": "7.8.2",
|
|
28
|
+
"slonik": "^39.0.0"
|
|
29
29
|
},
|
|
30
30
|
"keywords": [
|
|
31
31
|
"rineex",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
],
|
|
37
37
|
"license": "Apache-2.0",
|
|
38
38
|
"publishConfig": {
|
|
39
|
+
"provenance": true,
|
|
39
40
|
"access": "public",
|
|
40
41
|
"registry": "https://registry.npmjs.org"
|
|
41
42
|
},
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"scripts": {
|
|
48
49
|
"test": "vitest run",
|
|
49
50
|
"lint": "eslint 'src/**/*.ts'",
|
|
51
|
+
"lint:fix": "eslint 'src/**/*.ts' --fix",
|
|
50
52
|
"check-types": "tsc --noEmit",
|
|
51
53
|
"build": "tsup"
|
|
52
54
|
}
|