@xfcfam/xf-sql-postgres 0.1.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/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/src/api/A.d.ts +17 -0
- package/dist/src/api/A.d.ts.map +1 -0
- package/dist/src/api/A.js +17 -0
- package/dist/src/api/A.js.map +1 -0
- package/dist/src/business/B.d.ts +17 -0
- package/dist/src/business/B.d.ts.map +1 -0
- package/dist/src/business/B.js +17 -0
- package/dist/src/business/B.js.map +1 -0
- package/dist/src/repository/R.d.ts +17 -0
- package/dist/src/repository/R.d.ts.map +1 -0
- package/dist/src/repository/R.js +17 -0
- package/dist/src/repository/R.js.map +1 -0
- package/dist/src/repository/base/PostgresDatabaseRepository.d.ts +91 -0
- package/dist/src/repository/base/PostgresDatabaseRepository.d.ts.map +1 -0
- package/dist/src/repository/base/PostgresDatabaseRepository.js +88 -0
- package/dist/src/repository/base/PostgresDatabaseRepository.js.map +1 -0
- package/dist/src/repository/general/PostgresDatabaseRepository.d.ts +91 -0
- package/dist/src/repository/general/PostgresDatabaseRepository.d.ts.map +1 -0
- package/dist/src/repository/general/PostgresDatabaseRepository.js +88 -0
- package/dist/src/repository/general/PostgresDatabaseRepository.js.map +1 -0
- package/dist/src/repository/utils/PostgresErrorUtils.d.ts +48 -0
- package/dist/src/repository/utils/PostgresErrorUtils.d.ts.map +1 -0
- package/dist/src/repository/utils/PostgresErrorUtils.js +102 -0
- package/dist/src/repository/utils/PostgresErrorUtils.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Israel Sanjurjo and the XF contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# `@xfcfam/xf-sql-postgres`
|
|
2
|
+
|
|
3
|
+
PostgreSQL dialect adapter for [`@xfcfam/xf-sql`](https://www.npmjs.com/package/@xfcfam/xf-sql).
|
|
4
|
+
Encapsulates `kysely`'s `PostgresDialect` over the [`pg`](https://node-postgres.com)
|
|
5
|
+
driver and ships the SQLSTATE → typed-Exception translation so the
|
|
6
|
+
Business Layer never sees `pg` errors.
|
|
7
|
+
|
|
8
|
+
Peer dependencies: [`@xfcfam/xf`](https://www.npmjs.com/package/@xfcfam/xf),
|
|
9
|
+
[`@xfcfam/xf-sql`](https://www.npmjs.com/package/@xfcfam/xf-sql),
|
|
10
|
+
[`kysely`](https://kysely.dev).
|
|
11
|
+
Runtime dependency: `pg`.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @xfcfam/xf @xfcfam/xf-sql @xfcfam/xf-sql-postgres kysely pg
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What ships here
|
|
20
|
+
|
|
21
|
+
| Export | Role |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `PostgresDatabaseRepository<Schema>` | Ready-to-use Access Layer Generalization. Extends `TransactionalDatabaseRepository` from `@xfcfam/xf-sql`. Wires `PostgresDialect` + `pg.Pool`. Overrides `translateError` with the SQLSTATE mapping. |
|
|
24
|
+
| `PostgresOptions` | Constructor options: `connectionString`, optional `pool: pg.PoolConfig`. |
|
|
25
|
+
| `PostgresErrorUtils` | Static utility class. `translate(err)` maps any `pg`/Kysely error to the corresponding `@xfcfam/xf-sql` Exception. Constants for the SQLSTATEs it recognises (`SQLSTATE_UNIQUE_VIOLATION`, …). |
|
|
26
|
+
|
|
27
|
+
## Quick usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { PostgresDatabaseRepository } from '@xfcfam/xf-sql-postgres'
|
|
31
|
+
import { UniqueViolationException, ConnectionException } from '@xfcfam/xf-sql'
|
|
32
|
+
|
|
33
|
+
interface Schema {
|
|
34
|
+
users: {
|
|
35
|
+
id: number
|
|
36
|
+
name: string
|
|
37
|
+
email: string
|
|
38
|
+
created_at: Date
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class UsersDb extends PostgresDatabaseRepository<Schema> {
|
|
43
|
+
constructor() {
|
|
44
|
+
super({
|
|
45
|
+
connectionString: process.env.DATABASE_URL!,
|
|
46
|
+
pool: { max: 10, idleTimeoutMillis: 30_000 },
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
async init() { await super.init() }
|
|
50
|
+
async terminate() { await super.terminate() }
|
|
51
|
+
|
|
52
|
+
getUser(id: number) {
|
|
53
|
+
return this.exec(() =>
|
|
54
|
+
this.db.selectFrom('users').where('id', '=', id).selectAll().executeTakeFirstOrThrow()
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async createUser(input: { name: string; email: string }) {
|
|
59
|
+
try {
|
|
60
|
+
return await this.exec(() =>
|
|
61
|
+
this.db
|
|
62
|
+
.insertInto('users')
|
|
63
|
+
.values({ ...input, created_at: new Date() })
|
|
64
|
+
.returningAll()
|
|
65
|
+
.executeTakeFirstOrThrow()
|
|
66
|
+
)
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (err instanceof UniqueViolationException && err.column === 'email') {
|
|
69
|
+
throw new DomainError('Email already registered')
|
|
70
|
+
}
|
|
71
|
+
throw err
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async transferOrder(orderId: number, newOwner: number) {
|
|
76
|
+
// transaction() commits on success, rolls back on throw.
|
|
77
|
+
return this.transaction(async (trx) => {
|
|
78
|
+
await trx.updateTable('orders').set({ owner_id: newOwner }).where('id', '=', orderId).execute()
|
|
79
|
+
await trx.insertInto('order_audit').values({ order_id: orderId, action: 'transfer' }).execute()
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## SQLSTATE → Exception mapping
|
|
86
|
+
|
|
87
|
+
The default `translateError` covers the most common PostgreSQL error
|
|
88
|
+
codes. Anything not in this table is returned unchanged (so
|
|
89
|
+
implementer-specific exceptions still propagate cleanly).
|
|
90
|
+
|
|
91
|
+
| SQLSTATE | Postgres condition | Translates to |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `23505` | unique_violation | `UniqueViolationException` |
|
|
94
|
+
| `23503` | foreign_key_violation | `ForeignKeyViolationException` |
|
|
95
|
+
| `23514` | check_violation | `CheckViolationException` |
|
|
96
|
+
| `23502` | not_null_violation | `NotNullViolationException` |
|
|
97
|
+
| `40P01` | deadlock_detected | `DeadlockException` |
|
|
98
|
+
| `ECONNREFUSED`, `ENOTFOUND`, `ETIMEDOUT`, `ECONNRESET`, `EHOSTUNREACH`, `EAI_AGAIN` | transport-level (not a SQLSTATE) | `ConnectionException` |
|
|
99
|
+
| anything else | — | passed through untouched |
|
|
100
|
+
|
|
101
|
+
When the driver attaches `table`, `column`, `constraint` to the
|
|
102
|
+
error, these are forwarded to the Exception so the Business Layer can
|
|
103
|
+
branch on them without parsing strings.
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
- Specification — [xfcfam.org](https://xfcfam.org)
|
|
108
|
+
- Kysely docs — [kysely.dev](https://kysely.dev)
|
|
109
|
+
- Postgres SQLSTATE codes — [postgresql.org docs](https://www.postgresql.org/docs/current/errcodes-appendix.html)
|
|
110
|
+
- Source — [github.com/xfcfam/lib-npm](https://github.com/xfcfam/lib-npm)
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@xfcfam/xf-sql-postgres` — PostgreSQL dialect adapter for
|
|
3
|
+
* `@xfcfam/xf-sql`.
|
|
4
|
+
*
|
|
5
|
+
* Encapsulates `kysely`'s `PostgresDialect` over the `pg` driver
|
|
6
|
+
* behind a single XF-canonical class (`PostgresDatabaseRepository`)
|
|
7
|
+
* and ships the SQLSTATE → `@xfcfam/xf-sql` Exception translation
|
|
8
|
+
* (`PostgresErrorUtils`).
|
|
9
|
+
*
|
|
10
|
+
* Peer dependencies: `@xfcfam/xf`, `@xfcfam/xf-sql`, `kysely`.
|
|
11
|
+
* Runtime dependency: `pg`.
|
|
12
|
+
*/
|
|
13
|
+
export { PostgresDatabaseRepository } from './src/repository/general/PostgresDatabaseRepository.js';
|
|
14
|
+
export type { PostgresOptions } from './src/repository/general/PostgresDatabaseRepository.js';
|
|
15
|
+
export { PostgresErrorUtils } from './src/repository/utils/PostgresErrorUtils.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,0BAA0B,EAAE,MAAM,wDAAwD,CAAA;AACnG,YAAY,EAAE,eAAe,EAAE,MAAM,wDAAwD,CAAA;AAG7F,OAAO,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@xfcfam/xf-sql-postgres` — PostgreSQL dialect adapter for
|
|
3
|
+
* `@xfcfam/xf-sql`.
|
|
4
|
+
*
|
|
5
|
+
* Encapsulates `kysely`'s `PostgresDialect` over the `pg` driver
|
|
6
|
+
* behind a single XF-canonical class (`PostgresDatabaseRepository`)
|
|
7
|
+
* and ships the SQLSTATE → `@xfcfam/xf-sql` Exception translation
|
|
8
|
+
* (`PostgresErrorUtils`).
|
|
9
|
+
*
|
|
10
|
+
* Peer dependencies: `@xfcfam/xf`, `@xfcfam/xf-sql`, `kysely`.
|
|
11
|
+
* Runtime dependency: `pg`.
|
|
12
|
+
*/
|
|
13
|
+
// ── Access — base ─────────────────────────────────────────
|
|
14
|
+
export { PostgresDatabaseRepository } from './src/repository/general/PostgresDatabaseRepository.js';
|
|
15
|
+
// ── Access — utils ────────────────────────────────────────
|
|
16
|
+
export { PostgresErrorUtils } from './src/repository/utils/PostgresErrorUtils.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,6DAA6D;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,wDAAwD,CAAA;AAGnG,6DAA6D;AAC7D,OAAO,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `A` is the canonical injection of the Interaction Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `A` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `A` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class A {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=A.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A.d.ts","sourceRoot":"","sources":["../../../src/api/A.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interaction Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `A` is the canonical injection of the Interaction Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `A` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `A` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class A {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=A.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"A.js","sourceRoot":"","sources":["../../../src/api/A.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `B` is the canonical injection of the Business Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `B` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `B` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class B {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=B.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"B.d.ts","sourceRoot":"","sources":["../../../src/business/B.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Business Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `B` is the canonical injection of the Business Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `B` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `B` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class B {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"B.js","sourceRoot":"","sources":["../../../src/business/B.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `R` is the canonical injection of the Access Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `R` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `R` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export declare class R {
|
|
13
|
+
private constructor();
|
|
14
|
+
static init(): Promise<void>;
|
|
15
|
+
static terminate(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=R.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"R.d.ts","sourceRoot":"","sources":["../../../src/repository/R.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,qBAAa,CAAC;IACZ,OAAO;WACM,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;WACrB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CACxC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access Layer Injection — placeholder.
|
|
3
|
+
*
|
|
4
|
+
* `R` is the canonical injection of the Access Layer. `@xfcfam/xf-sql-postgres`
|
|
5
|
+
* is a library that contributes Generalizations and Transfer objects;
|
|
6
|
+
* it does not own any Logical of this layer, so its own `R` declares
|
|
7
|
+
* no static slots. The class is kept structurally complete (private
|
|
8
|
+
* constructor + empty `init` / `terminate`) so the artefact passes XF
|
|
9
|
+
* validation. It is NOT exported from the package — consumers import
|
|
10
|
+
* `R` from their own artefact.
|
|
11
|
+
*/
|
|
12
|
+
export class R {
|
|
13
|
+
constructor() { }
|
|
14
|
+
static async init() { }
|
|
15
|
+
static async terminate() { }
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"R.js","sourceRoot":"","sources":["../../../src/repository/R.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,CAAC;IACZ,gBAAuB,CAAC;IACxB,MAAM,CAAC,KAAK,CAAC,IAAI,KAAmB,CAAC;IACrC,MAAM,CAAC,KAAK,CAAC,SAAS,KAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { TransactionalDatabaseRepository } from '@xfarch/xf-sql';
|
|
2
|
+
import { type PoolConfig } from 'pg';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration accepted by {@link PostgresDatabaseRepository}'s
|
|
5
|
+
* constructor.
|
|
6
|
+
*
|
|
7
|
+
* Either provide a `connectionString` (the simplest path), a full
|
|
8
|
+
* `pg.PoolConfig`, or both — they are merged with the connection
|
|
9
|
+
* string taking precedence for connection coordinates.
|
|
10
|
+
*/
|
|
11
|
+
export interface PostgresOptions {
|
|
12
|
+
/** Postgres connection string (`postgres://user:pass@host:port/db?…`). */
|
|
13
|
+
connectionString?: string;
|
|
14
|
+
/** Extra `pg.PoolConfig` fields: `max`, `idleTimeoutMillis`, `ssl`, … */
|
|
15
|
+
pool?: PoolConfig;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Ready-to-use Generalization for the Access Layer when the backing
|
|
19
|
+
* store is **PostgreSQL**.
|
|
20
|
+
*
|
|
21
|
+
* Wraps `kysely`'s `PostgresDialect` over the `pg` driver behind a
|
|
22
|
+
* single XF-canonical class. The implementer's concrete Logical
|
|
23
|
+
* extends this and exposes domain-meaningful methods that compose
|
|
24
|
+
* queries against `this.db`. Transactions via `this.transaction(...)`,
|
|
25
|
+
* one-shot error translation via `this.exec(...)`.
|
|
26
|
+
*
|
|
27
|
+
* The default `translateError` maps Postgres SQLSTATE codes to the
|
|
28
|
+
* typed Exceptions exported from `@xfarch/xf-sql`
|
|
29
|
+
* (`UniqueViolationException`, `ForeignKeyViolationException`,
|
|
30
|
+
* `CheckViolationException`, `NotNullViolationException`,
|
|
31
|
+
* `DeadlockException`) and transport-level errors (`ECONNREFUSED`,
|
|
32
|
+
* `ETIMEDOUT`, …) to `ConnectionException`. The implementer's
|
|
33
|
+
* Business Layer never sees `pg` types.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam Schema Implementer-defined TypeScript interface mapping
|
|
36
|
+
* table names to their column shapes.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { PostgresDatabaseRepository } from '@xfarch/xf-sql-postgres'
|
|
41
|
+
* import { UniqueViolationException } from '@xfarch/xf-sql'
|
|
42
|
+
*
|
|
43
|
+
* interface Schema {
|
|
44
|
+
* users: { id: number; name: string; email: string; created_at: Date }
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* export class UsersDb extends PostgresDatabaseRepository<Schema> {
|
|
48
|
+
* constructor() {
|
|
49
|
+
* super({
|
|
50
|
+
* connectionString: process.env.DATABASE_URL!,
|
|
51
|
+
* pool: { max: 10, idleTimeoutMillis: 30_000 },
|
|
52
|
+
* })
|
|
53
|
+
* }
|
|
54
|
+
* async init() { await super.init() } // ← required
|
|
55
|
+
* async terminate() { await super.terminate() } // ← required
|
|
56
|
+
*
|
|
57
|
+
* getUser(id: number) {
|
|
58
|
+
* return this.exec(() =>
|
|
59
|
+
* this.db.selectFrom('users').where('id', '=', id).selectAll().executeTakeFirstOrThrow()
|
|
60
|
+
* )
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* async createUser(input: { name: string; email: string }) {
|
|
64
|
+
* try {
|
|
65
|
+
* return await this.exec(() =>
|
|
66
|
+
* this.db.insertInto('users').values({ ...input, created_at: new Date() }).returningAll().executeTakeFirstOrThrow()
|
|
67
|
+
* )
|
|
68
|
+
* } catch (err) {
|
|
69
|
+
* if (err instanceof UniqueViolationException && err.column === 'email') {
|
|
70
|
+
* throw new DomainError('Email already registered')
|
|
71
|
+
* }
|
|
72
|
+
* throw err
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare abstract class PostgresDatabaseRepository<Schema = unknown> extends TransactionalDatabaseRepository<Schema> {
|
|
79
|
+
constructor(options: PostgresOptions);
|
|
80
|
+
/**
|
|
81
|
+
* Translate `pg` / Kysely driver errors into the typed Exceptions
|
|
82
|
+
* exported from `@xfarch/xf-sql`. Delegates to
|
|
83
|
+
* {@link PostgresErrorUtils.translate}.
|
|
84
|
+
*
|
|
85
|
+
* Subclasses may override to layer additional mappings on top, but
|
|
86
|
+
* should `return super.translateError(err)` for unrecognised
|
|
87
|
+
* errors so the base policy still applies.
|
|
88
|
+
*/
|
|
89
|
+
protected translateError(err: unknown): unknown;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=PostgresDatabaseRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresDatabaseRepository.d.ts","sourceRoot":"","sources":["../../../../src/repository/base/PostgresDatabaseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAA;AAG1C;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,8BAAsB,0BAA0B,CAAC,MAAM,GAAG,OAAO,CAC/D,SAAQ,+BAA+B,CAAC,MAAM,CAAC;gBAEnC,OAAO,EAAE,eAAe;IASpC;;;;;;;;OAQG;cACgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;CAGzD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TransactionalDatabaseRepository } from '@xfarch/xf-sql';
|
|
2
|
+
import { PostgresDialect } from 'kysely';
|
|
3
|
+
import { Pool } from 'pg';
|
|
4
|
+
import { PostgresErrorUtils } from '../utils/PostgresErrorUtils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Ready-to-use Generalization for the Access Layer when the backing
|
|
7
|
+
* store is **PostgreSQL**.
|
|
8
|
+
*
|
|
9
|
+
* Wraps `kysely`'s `PostgresDialect` over the `pg` driver behind a
|
|
10
|
+
* single XF-canonical class. The implementer's concrete Logical
|
|
11
|
+
* extends this and exposes domain-meaningful methods that compose
|
|
12
|
+
* queries against `this.db`. Transactions via `this.transaction(...)`,
|
|
13
|
+
* one-shot error translation via `this.exec(...)`.
|
|
14
|
+
*
|
|
15
|
+
* The default `translateError` maps Postgres SQLSTATE codes to the
|
|
16
|
+
* typed Exceptions exported from `@xfarch/xf-sql`
|
|
17
|
+
* (`UniqueViolationException`, `ForeignKeyViolationException`,
|
|
18
|
+
* `CheckViolationException`, `NotNullViolationException`,
|
|
19
|
+
* `DeadlockException`) and transport-level errors (`ECONNREFUSED`,
|
|
20
|
+
* `ETIMEDOUT`, …) to `ConnectionException`. The implementer's
|
|
21
|
+
* Business Layer never sees `pg` types.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam Schema Implementer-defined TypeScript interface mapping
|
|
24
|
+
* table names to their column shapes.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { PostgresDatabaseRepository } from '@xfarch/xf-sql-postgres'
|
|
29
|
+
* import { UniqueViolationException } from '@xfarch/xf-sql'
|
|
30
|
+
*
|
|
31
|
+
* interface Schema {
|
|
32
|
+
* users: { id: number; name: string; email: string; created_at: Date }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* export class UsersDb extends PostgresDatabaseRepository<Schema> {
|
|
36
|
+
* constructor() {
|
|
37
|
+
* super({
|
|
38
|
+
* connectionString: process.env.DATABASE_URL!,
|
|
39
|
+
* pool: { max: 10, idleTimeoutMillis: 30_000 },
|
|
40
|
+
* })
|
|
41
|
+
* }
|
|
42
|
+
* async init() { await super.init() } // ← required
|
|
43
|
+
* async terminate() { await super.terminate() } // ← required
|
|
44
|
+
*
|
|
45
|
+
* getUser(id: number) {
|
|
46
|
+
* return this.exec(() =>
|
|
47
|
+
* this.db.selectFrom('users').where('id', '=', id).selectAll().executeTakeFirstOrThrow()
|
|
48
|
+
* )
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* async createUser(input: { name: string; email: string }) {
|
|
52
|
+
* try {
|
|
53
|
+
* return await this.exec(() =>
|
|
54
|
+
* this.db.insertInto('users').values({ ...input, created_at: new Date() }).returningAll().executeTakeFirstOrThrow()
|
|
55
|
+
* )
|
|
56
|
+
* } catch (err) {
|
|
57
|
+
* if (err instanceof UniqueViolationException && err.column === 'email') {
|
|
58
|
+
* throw new DomainError('Email already registered')
|
|
59
|
+
* }
|
|
60
|
+
* throw err
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export class PostgresDatabaseRepository extends TransactionalDatabaseRepository {
|
|
67
|
+
constructor(options) {
|
|
68
|
+
const poolConfig = { ...options.pool };
|
|
69
|
+
if (options.connectionString !== undefined) {
|
|
70
|
+
poolConfig.connectionString = options.connectionString;
|
|
71
|
+
}
|
|
72
|
+
const pool = new Pool(poolConfig);
|
|
73
|
+
super({ dialect: new PostgresDialect({ pool }) });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Translate `pg` / Kysely driver errors into the typed Exceptions
|
|
77
|
+
* exported from `@xfarch/xf-sql`. Delegates to
|
|
78
|
+
* {@link PostgresErrorUtils.translate}.
|
|
79
|
+
*
|
|
80
|
+
* Subclasses may override to layer additional mappings on top, but
|
|
81
|
+
* should `return super.translateError(err)` for unrecognised
|
|
82
|
+
* errors so the base policy still applies.
|
|
83
|
+
*/
|
|
84
|
+
translateError(err) {
|
|
85
|
+
return PostgresErrorUtils.translate(err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=PostgresDatabaseRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresDatabaseRepository.js","sourceRoot":"","sources":["../../../../src/repository/base/PostgresDatabaseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAiBnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,MAAM,OAAgB,0BACpB,SAAQ,+BAAuC;IAE/C,YAAY,OAAwB;QAClC,MAAM,UAAU,GAAe,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAClD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC3C,UAAU,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAA;QACxD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;QACjC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IAED;;;;;;;;OAQG;IACgB,cAAc,CAAC,GAAY;QAC5C,OAAO,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { TransactionalDatabaseRepository } from '@xfcfam/xf-sql';
|
|
2
|
+
import { type PoolConfig } from 'pg';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration accepted by {@link PostgresDatabaseRepository}'s
|
|
5
|
+
* constructor.
|
|
6
|
+
*
|
|
7
|
+
* Either provide a `connectionString` (the simplest path), a full
|
|
8
|
+
* `pg.PoolConfig`, or both — they are merged with the connection
|
|
9
|
+
* string taking precedence for connection coordinates.
|
|
10
|
+
*/
|
|
11
|
+
export interface PostgresOptions {
|
|
12
|
+
/** Postgres connection string (`postgres://user:pass@host:port/db?…`). */
|
|
13
|
+
connectionString?: string;
|
|
14
|
+
/** Extra `pg.PoolConfig` fields: `max`, `idleTimeoutMillis`, `ssl`, … */
|
|
15
|
+
pool?: PoolConfig;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Ready-to-use Generalization for the Access Layer when the backing
|
|
19
|
+
* store is **PostgreSQL**.
|
|
20
|
+
*
|
|
21
|
+
* Wraps `kysely`'s `PostgresDialect` over the `pg` driver behind a
|
|
22
|
+
* single XF-canonical class. The implementer's concrete Logical
|
|
23
|
+
* extends this and exposes domain-meaningful methods that compose
|
|
24
|
+
* queries against `this.db`. Transactions via `this.transaction(...)`,
|
|
25
|
+
* one-shot error translation via `this.exec(...)`.
|
|
26
|
+
*
|
|
27
|
+
* The default `translateError` maps Postgres SQLSTATE codes to the
|
|
28
|
+
* typed Exceptions exported from `@xfcfam/xf-sql`
|
|
29
|
+
* (`UniqueViolationException`, `ForeignKeyViolationException`,
|
|
30
|
+
* `CheckViolationException`, `NotNullViolationException`,
|
|
31
|
+
* `DeadlockException`) and transport-level errors (`ECONNREFUSED`,
|
|
32
|
+
* `ETIMEDOUT`, …) to `ConnectionException`. The implementer's
|
|
33
|
+
* Business Layer never sees `pg` types.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam Schema Implementer-defined TypeScript interface mapping
|
|
36
|
+
* table names to their column shapes.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { PostgresDatabaseRepository } from '@xfcfam/xf-sql-postgres'
|
|
41
|
+
* import { UniqueViolationException } from '@xfcfam/xf-sql'
|
|
42
|
+
*
|
|
43
|
+
* interface Schema {
|
|
44
|
+
* users: { id: number; name: string; email: string; created_at: Date }
|
|
45
|
+
* }
|
|
46
|
+
*
|
|
47
|
+
* export class UsersDb extends PostgresDatabaseRepository<Schema> {
|
|
48
|
+
* constructor() {
|
|
49
|
+
* super({
|
|
50
|
+
* connectionString: process.env.DATABASE_URL!,
|
|
51
|
+
* pool: { max: 10, idleTimeoutMillis: 30_000 },
|
|
52
|
+
* })
|
|
53
|
+
* }
|
|
54
|
+
* async init() { await super.init() } // ← required
|
|
55
|
+
* async terminate() { await super.terminate() } // ← required
|
|
56
|
+
*
|
|
57
|
+
* getUser(id: number) {
|
|
58
|
+
* return this.exec(() =>
|
|
59
|
+
* this.db.selectFrom('users').where('id', '=', id).selectAll().executeTakeFirstOrThrow()
|
|
60
|
+
* )
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* async createUser(input: { name: string; email: string }) {
|
|
64
|
+
* try {
|
|
65
|
+
* return await this.exec(() =>
|
|
66
|
+
* this.db.insertInto('users').values({ ...input, created_at: new Date() }).returningAll().executeTakeFirstOrThrow()
|
|
67
|
+
* )
|
|
68
|
+
* } catch (err) {
|
|
69
|
+
* if (err instanceof UniqueViolationException && err.column === 'email') {
|
|
70
|
+
* throw new DomainError('Email already registered')
|
|
71
|
+
* }
|
|
72
|
+
* throw err
|
|
73
|
+
* }
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare abstract class PostgresDatabaseRepository<Schema = unknown> extends TransactionalDatabaseRepository<Schema> {
|
|
79
|
+
constructor(options: PostgresOptions);
|
|
80
|
+
/**
|
|
81
|
+
* Translate `pg` / Kysely driver errors into the typed Exceptions
|
|
82
|
+
* exported from `@xfcfam/xf-sql`. Delegates to
|
|
83
|
+
* {@link PostgresErrorUtils.translate}.
|
|
84
|
+
*
|
|
85
|
+
* Subclasses may override to layer additional mappings on top, but
|
|
86
|
+
* should `return super.translateError(err)` for unrecognised
|
|
87
|
+
* errors so the base policy still applies.
|
|
88
|
+
*/
|
|
89
|
+
protected translateError(err: unknown): unknown;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=PostgresDatabaseRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresDatabaseRepository.d.ts","sourceRoot":"","sources":["../../../../src/repository/general/PostgresDatabaseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAA;AAEhE,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,IAAI,CAAA;AAG1C;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,yEAAyE;IACzE,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,8BAAsB,0BAA0B,CAAC,MAAM,GAAG,OAAO,CAC/D,SAAQ,+BAA+B,CAAC,MAAM,CAAC;gBAEnC,OAAO,EAAE,eAAe;IASpC;;;;;;;;OAQG;cACgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;CAGzD"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { TransactionalDatabaseRepository } from '@xfcfam/xf-sql';
|
|
2
|
+
import { PostgresDialect } from 'kysely';
|
|
3
|
+
import { Pool } from 'pg';
|
|
4
|
+
import { PostgresErrorUtils } from '../utils/PostgresErrorUtils.js';
|
|
5
|
+
/**
|
|
6
|
+
* Ready-to-use Generalization for the Access Layer when the backing
|
|
7
|
+
* store is **PostgreSQL**.
|
|
8
|
+
*
|
|
9
|
+
* Wraps `kysely`'s `PostgresDialect` over the `pg` driver behind a
|
|
10
|
+
* single XF-canonical class. The implementer's concrete Logical
|
|
11
|
+
* extends this and exposes domain-meaningful methods that compose
|
|
12
|
+
* queries against `this.db`. Transactions via `this.transaction(...)`,
|
|
13
|
+
* one-shot error translation via `this.exec(...)`.
|
|
14
|
+
*
|
|
15
|
+
* The default `translateError` maps Postgres SQLSTATE codes to the
|
|
16
|
+
* typed Exceptions exported from `@xfcfam/xf-sql`
|
|
17
|
+
* (`UniqueViolationException`, `ForeignKeyViolationException`,
|
|
18
|
+
* `CheckViolationException`, `NotNullViolationException`,
|
|
19
|
+
* `DeadlockException`) and transport-level errors (`ECONNREFUSED`,
|
|
20
|
+
* `ETIMEDOUT`, …) to `ConnectionException`. The implementer's
|
|
21
|
+
* Business Layer never sees `pg` types.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam Schema Implementer-defined TypeScript interface mapping
|
|
24
|
+
* table names to their column shapes.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { PostgresDatabaseRepository } from '@xfcfam/xf-sql-postgres'
|
|
29
|
+
* import { UniqueViolationException } from '@xfcfam/xf-sql'
|
|
30
|
+
*
|
|
31
|
+
* interface Schema {
|
|
32
|
+
* users: { id: number; name: string; email: string; created_at: Date }
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* export class UsersDb extends PostgresDatabaseRepository<Schema> {
|
|
36
|
+
* constructor() {
|
|
37
|
+
* super({
|
|
38
|
+
* connectionString: process.env.DATABASE_URL!,
|
|
39
|
+
* pool: { max: 10, idleTimeoutMillis: 30_000 },
|
|
40
|
+
* })
|
|
41
|
+
* }
|
|
42
|
+
* async init() { await super.init() } // ← required
|
|
43
|
+
* async terminate() { await super.terminate() } // ← required
|
|
44
|
+
*
|
|
45
|
+
* getUser(id: number) {
|
|
46
|
+
* return this.exec(() =>
|
|
47
|
+
* this.db.selectFrom('users').where('id', '=', id).selectAll().executeTakeFirstOrThrow()
|
|
48
|
+
* )
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* async createUser(input: { name: string; email: string }) {
|
|
52
|
+
* try {
|
|
53
|
+
* return await this.exec(() =>
|
|
54
|
+
* this.db.insertInto('users').values({ ...input, created_at: new Date() }).returningAll().executeTakeFirstOrThrow()
|
|
55
|
+
* )
|
|
56
|
+
* } catch (err) {
|
|
57
|
+
* if (err instanceof UniqueViolationException && err.column === 'email') {
|
|
58
|
+
* throw new DomainError('Email already registered')
|
|
59
|
+
* }
|
|
60
|
+
* throw err
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export class PostgresDatabaseRepository extends TransactionalDatabaseRepository {
|
|
67
|
+
constructor(options) {
|
|
68
|
+
const poolConfig = { ...options.pool };
|
|
69
|
+
if (options.connectionString !== undefined) {
|
|
70
|
+
poolConfig.connectionString = options.connectionString;
|
|
71
|
+
}
|
|
72
|
+
const pool = new Pool(poolConfig);
|
|
73
|
+
super({ dialect: new PostgresDialect({ pool }) });
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Translate `pg` / Kysely driver errors into the typed Exceptions
|
|
77
|
+
* exported from `@xfcfam/xf-sql`. Delegates to
|
|
78
|
+
* {@link PostgresErrorUtils.translate}.
|
|
79
|
+
*
|
|
80
|
+
* Subclasses may override to layer additional mappings on top, but
|
|
81
|
+
* should `return super.translateError(err)` for unrecognised
|
|
82
|
+
* errors so the base policy still applies.
|
|
83
|
+
*/
|
|
84
|
+
translateError(err) {
|
|
85
|
+
return PostgresErrorUtils.translate(err);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=PostgresDatabaseRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresDatabaseRepository.js","sourceRoot":"","sources":["../../../../src/repository/general/PostgresDatabaseRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAA;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,EAAE,IAAI,EAAmB,MAAM,IAAI,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAiBnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,MAAM,OAAgB,0BACpB,SAAQ,+BAAuC;IAE/C,YAAY,OAAwB;QAClC,MAAM,UAAU,GAAe,EAAE,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAClD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC3C,UAAU,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAA;QACxD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,CAAA;QACjC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;IACnD,CAAC;IAED;;;;;;;;OAQG;IACgB,cAAc,CAAC,GAAY;QAC5C,OAAO,kBAAkB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;IAC1C,CAAC;CACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static utility component that translates Postgres / `pg` driver
|
|
3
|
+
* errors into the typed Exceptions exported from `@xfcfam/xf-sql`.
|
|
4
|
+
*
|
|
5
|
+
* The translation is based on the SQLSTATE code attached to the
|
|
6
|
+
* error by `pg` (see
|
|
7
|
+
* [Postgres docs](https://www.postgresql.org/docs/current/errcodes-appendix.html)).
|
|
8
|
+
*
|
|
9
|
+
* Non-instantiable. All members are static.
|
|
10
|
+
*/
|
|
11
|
+
export declare class PostgresErrorUtils {
|
|
12
|
+
private constructor();
|
|
13
|
+
/** Class 23 — integrity constraint violation: unique. */
|
|
14
|
+
static readonly SQLSTATE_UNIQUE_VIOLATION = "23505";
|
|
15
|
+
/** Class 23 — integrity constraint violation: foreign key. */
|
|
16
|
+
static readonly SQLSTATE_FOREIGN_KEY_VIOLATION = "23503";
|
|
17
|
+
/** Class 23 — integrity constraint violation: check. */
|
|
18
|
+
static readonly SQLSTATE_CHECK_VIOLATION = "23514";
|
|
19
|
+
/** Class 23 — integrity constraint violation: not null. */
|
|
20
|
+
static readonly SQLSTATE_NOT_NULL_VIOLATION = "23502";
|
|
21
|
+
/** Class 40 — transaction rollback: deadlock detected. */
|
|
22
|
+
static readonly SQLSTATE_DEADLOCK_DETECTED = "40P01";
|
|
23
|
+
/**
|
|
24
|
+
* Translate any `pg` / Kysely error to the corresponding `@xfcfam/xf-sql`
|
|
25
|
+
* Exception. Returns the input unchanged when:
|
|
26
|
+
*
|
|
27
|
+
* - The error is already a `DatabaseException` (already translated).
|
|
28
|
+
* - The error has no SQLSTATE `code` and isn't a recognised
|
|
29
|
+
* transport-level failure.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* try {
|
|
34
|
+
* await this.db.insertInto('users').values(input).execute()
|
|
35
|
+
* } catch (err) {
|
|
36
|
+
* throw PostgresErrorUtils.translate(err)
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* (`PostgresDatabaseRepository` overrides `translateError` to call
|
|
41
|
+
* this automatically — you don't need to wrap manually if you
|
|
42
|
+
* extend it.)
|
|
43
|
+
*/
|
|
44
|
+
static translate(err: unknown): unknown;
|
|
45
|
+
private static isErrorLike;
|
|
46
|
+
private static isTransportCode;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=PostgresErrorUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresErrorUtils.d.ts","sourceRoot":"","sources":["../../../../src/repository/utils/PostgresErrorUtils.ts"],"names":[],"mappings":"AA2BA;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAC7B,OAAO;IAEP,yDAAyD;IACzD,MAAM,CAAC,QAAQ,CAAC,yBAAyB,WAAe;IACxD,8DAA8D;IAC9D,MAAM,CAAC,QAAQ,CAAC,8BAA8B,WAAU;IACxD,wDAAwD;IACxD,MAAM,CAAC,QAAQ,CAAC,wBAAwB,WAAgB;IACxD,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,CAAC,2BAA2B,WAAa;IACxD,0DAA0D;IAC1D,MAAM,CAAC,QAAQ,CAAC,0BAA0B,WAAc;IAExD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IAkDvC,OAAO,CAAC,MAAM,CAAC,WAAW;IAI1B,OAAO,CAAC,MAAM,CAAC,eAAe;CAW/B"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { DatabaseException, ConnectionException, UniqueViolationException, ForeignKeyViolationException, CheckViolationException, NotNullViolationException, DeadlockException, } from '@xfcfam/xf-sql';
|
|
2
|
+
/**
|
|
3
|
+
* Static utility component that translates Postgres / `pg` driver
|
|
4
|
+
* errors into the typed Exceptions exported from `@xfcfam/xf-sql`.
|
|
5
|
+
*
|
|
6
|
+
* The translation is based on the SQLSTATE code attached to the
|
|
7
|
+
* error by `pg` (see
|
|
8
|
+
* [Postgres docs](https://www.postgresql.org/docs/current/errcodes-appendix.html)).
|
|
9
|
+
*
|
|
10
|
+
* Non-instantiable. All members are static.
|
|
11
|
+
*/
|
|
12
|
+
export class PostgresErrorUtils {
|
|
13
|
+
constructor() { }
|
|
14
|
+
/** Class 23 — integrity constraint violation: unique. */
|
|
15
|
+
static SQLSTATE_UNIQUE_VIOLATION = '23505';
|
|
16
|
+
/** Class 23 — integrity constraint violation: foreign key. */
|
|
17
|
+
static SQLSTATE_FOREIGN_KEY_VIOLATION = '23503';
|
|
18
|
+
/** Class 23 — integrity constraint violation: check. */
|
|
19
|
+
static SQLSTATE_CHECK_VIOLATION = '23514';
|
|
20
|
+
/** Class 23 — integrity constraint violation: not null. */
|
|
21
|
+
static SQLSTATE_NOT_NULL_VIOLATION = '23502';
|
|
22
|
+
/** Class 40 — transaction rollback: deadlock detected. */
|
|
23
|
+
static SQLSTATE_DEADLOCK_DETECTED = '40P01';
|
|
24
|
+
/**
|
|
25
|
+
* Translate any `pg` / Kysely error to the corresponding `@xfcfam/xf-sql`
|
|
26
|
+
* Exception. Returns the input unchanged when:
|
|
27
|
+
*
|
|
28
|
+
* - The error is already a `DatabaseException` (already translated).
|
|
29
|
+
* - The error has no SQLSTATE `code` and isn't a recognised
|
|
30
|
+
* transport-level failure.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* try {
|
|
35
|
+
* await this.db.insertInto('users').values(input).execute()
|
|
36
|
+
* } catch (err) {
|
|
37
|
+
* throw PostgresErrorUtils.translate(err)
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* (`PostgresDatabaseRepository` overrides `translateError` to call
|
|
42
|
+
* this automatically — you don't need to wrap manually if you
|
|
43
|
+
* extend it.)
|
|
44
|
+
*/
|
|
45
|
+
static translate(err) {
|
|
46
|
+
if (err instanceof DatabaseException)
|
|
47
|
+
return err;
|
|
48
|
+
if (!PostgresErrorUtils.isErrorLike(err))
|
|
49
|
+
return err;
|
|
50
|
+
const message = err.message ?? 'Database error';
|
|
51
|
+
// Connection-level errors do not have a SQLSTATE; pg sets `code`
|
|
52
|
+
// to a Node errno like 'ECONNREFUSED' or 'ENOTFOUND'.
|
|
53
|
+
if (PostgresErrorUtils.isTransportCode(err.code)) {
|
|
54
|
+
return new ConnectionException(message, { cause: err });
|
|
55
|
+
}
|
|
56
|
+
switch (err.code) {
|
|
57
|
+
case PostgresErrorUtils.SQLSTATE_UNIQUE_VIOLATION:
|
|
58
|
+
return new UniqueViolationException(message, {
|
|
59
|
+
...(err.constraint !== undefined ? { constraint: err.constraint } : {}),
|
|
60
|
+
...(err.table !== undefined ? { table: err.table } : {}),
|
|
61
|
+
...(err.column !== undefined ? { column: err.column } : {}),
|
|
62
|
+
cause: err,
|
|
63
|
+
});
|
|
64
|
+
case PostgresErrorUtils.SQLSTATE_FOREIGN_KEY_VIOLATION:
|
|
65
|
+
return new ForeignKeyViolationException(message, {
|
|
66
|
+
...(err.constraint !== undefined ? { constraint: err.constraint } : {}),
|
|
67
|
+
...(err.table !== undefined ? { table: err.table } : {}),
|
|
68
|
+
cause: err,
|
|
69
|
+
});
|
|
70
|
+
case PostgresErrorUtils.SQLSTATE_CHECK_VIOLATION:
|
|
71
|
+
return new CheckViolationException(message, {
|
|
72
|
+
...(err.constraint !== undefined ? { constraint: err.constraint } : {}),
|
|
73
|
+
...(err.table !== undefined ? { table: err.table } : {}),
|
|
74
|
+
cause: err,
|
|
75
|
+
});
|
|
76
|
+
case PostgresErrorUtils.SQLSTATE_NOT_NULL_VIOLATION:
|
|
77
|
+
return new NotNullViolationException(message, {
|
|
78
|
+
...(err.table !== undefined ? { table: err.table } : {}),
|
|
79
|
+
...(err.column !== undefined ? { column: err.column } : {}),
|
|
80
|
+
cause: err,
|
|
81
|
+
});
|
|
82
|
+
case PostgresErrorUtils.SQLSTATE_DEADLOCK_DETECTED:
|
|
83
|
+
return new DeadlockException(message, { cause: err });
|
|
84
|
+
default:
|
|
85
|
+
return err;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
static isErrorLike(err) {
|
|
89
|
+
return typeof err === 'object' && err !== null;
|
|
90
|
+
}
|
|
91
|
+
static isTransportCode(code) {
|
|
92
|
+
if (code === undefined)
|
|
93
|
+
return false;
|
|
94
|
+
return (code === 'ECONNREFUSED' ||
|
|
95
|
+
code === 'ENOTFOUND' ||
|
|
96
|
+
code === 'ETIMEDOUT' ||
|
|
97
|
+
code === 'ECONNRESET' ||
|
|
98
|
+
code === 'EHOSTUNREACH' ||
|
|
99
|
+
code === 'EAI_AGAIN');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=PostgresErrorUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PostgresErrorUtils.js","sourceRoot":"","sources":["../../../../src/repository/utils/PostgresErrorUtils.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xfcfam/xf-sql-postgres",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "PostgreSQL dialect adapter for @xfcfam/xf-sql. Encapsulates kysely's PostgresDialect and the `pg` driver. Translates Postgres SQLSTATE codes into the typed Exceptions of @xfcfam/xf-sql.",
|
|
5
|
+
"author": "XF Contributors",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://xfcfam.org",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/xfcfam/lib-npm",
|
|
11
|
+
"directory": "packages/xf-sql-postgres"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"xf",
|
|
15
|
+
"cfam",
|
|
16
|
+
"sql",
|
|
17
|
+
"postgres",
|
|
18
|
+
"postgresql",
|
|
19
|
+
"kysely",
|
|
20
|
+
"pg"
|
|
21
|
+
],
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@xfcfam/xf-sql": "^0.1.0",
|
|
39
|
+
"@xfcfam/xf": "^0.2.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"pg": "^8.11.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.4.0",
|
|
46
|
+
"vitest": "^2.0.0",
|
|
47
|
+
"kysely": "^0.27.0",
|
|
48
|
+
"@types/pg": "^8.11.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc",
|
|
52
|
+
"typecheck": "tsc --noEmit",
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest"
|
|
56
|
+
}
|
|
57
|
+
}
|