@kronos-ts/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/README.md +176 -0
- package/dist/adapter.d.ts +89 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +29 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/bun-sql.d.ts +23 -0
- package/dist/adapters/bun-sql.d.ts.map +1 -0
- package/dist/adapters/bun-sql.js +175 -0
- package/dist/adapters/bun-sql.js.map +1 -0
- package/dist/adapters/pg.d.ts +24 -0
- package/dist/adapters/pg.d.ts.map +1 -0
- package/dist/adapters/pg.js +156 -0
- package/dist/adapters/pg.js.map +1 -0
- package/dist/adapters/postgres.d.ts +27 -0
- package/dist/adapters/postgres.d.ts.map +1 -0
- package/dist/adapters/postgres.js +99 -0
- package/dist/adapters/postgres.js.map +1 -0
- package/dist/advisory-locks.d.ts +56 -0
- package/dist/advisory-locks.d.ts.map +1 -0
- package/dist/advisory-locks.js +112 -0
- package/dist/advisory-locks.js.map +1 -0
- package/dist/criteria-sql.d.ts +29 -0
- package/dist/criteria-sql.d.ts.map +1 -0
- package/dist/criteria-sql.js +69 -0
- package/dist/criteria-sql.js.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/postgres-event-store.d.ts +52 -0
- package/dist/postgres-event-store.d.ts.map +1 -0
- package/dist/postgres-event-store.js +496 -0
- package/dist/postgres-event-store.js.map +1 -0
- package/dist/postgres-snapshot-store.d.ts +34 -0
- package/dist/postgres-snapshot-store.d.ts.map +1 -0
- package/dist/postgres-snapshot-store.js +122 -0
- package/dist/postgres-snapshot-store.js.map +1 -0
- package/dist/postgres.d.ts +34 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +42 -0
- package/dist/postgres.js.map +1 -0
- package/dist/schema.d.ts +96 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +174 -0
- package/dist/schema.js.map +1 -0
- package/package.json +93 -0
- package/src/adapter.ts +104 -0
- package/src/adapters/bun-sql.ts +228 -0
- package/src/adapters/pg.ts +189 -0
- package/src/adapters/postgres.ts +134 -0
- package/src/advisory-locks.ts +139 -0
- package/src/criteria-sql.ts +89 -0
- package/src/errors.ts +47 -0
- package/src/index.ts +56 -0
- package/src/postgres-event-store.ts +593 -0
- package/src/postgres-snapshot-store.ts +153 -0
- package/src/postgres.ts +66 -0
- package/src/schema.ts +204 -0
package/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# @kronos-ts/postgres
|
|
2
|
+
|
|
3
|
+
First-party Postgres extension for Kronos. Provides DCB-compliant event storage and snapshots via the framework's `eventStore` + `snapshotStore` slots.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @kronos-ts/postgres
|
|
9
|
+
# Choose one driver:
|
|
10
|
+
bun add pg # for pgAdapter (Node + Bun)
|
|
11
|
+
bun add postgres # for postgresAdapter (porsager — Node + Bun)
|
|
12
|
+
# bunSqlAdapter has no external dep (uses Bun.sql built-in, requires Bun >= 1.2)
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Postgres 14+ (for `xid8` and `pg_snapshot_xmin`).
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { kronos } from "@kronos-ts/app"
|
|
21
|
+
import { postgres } from "@kronos-ts/postgres"
|
|
22
|
+
import { pgAdapter } from "@kronos-ts/postgres/adapters/pg"
|
|
23
|
+
|
|
24
|
+
const app = await kronos()
|
|
25
|
+
.use(postgres({
|
|
26
|
+
adapter: pgAdapter({ connectionString: "postgresql://user:pass@host/db" }),
|
|
27
|
+
// Default: auto-create schema on connect. Set false to manage migrations yourself.
|
|
28
|
+
bootstrap: true,
|
|
29
|
+
}))
|
|
30
|
+
.start()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Accessing slots
|
|
34
|
+
|
|
35
|
+
In normal usage you don't touch the `eventStore` directly — command handlers, `load()`, and tracking processors handle it. If you need the raw store (e.g. for tests, scripts, or low-level access), capture it via a probe decorator before `start()` — `RunningApp` doesn't expose it on its public surface:
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import type { App } from "@kronos-ts/app"
|
|
39
|
+
import type { EventStore } from "@kronos-ts/eventsourcing"
|
|
40
|
+
import { EventCriteria } from "@kronos-ts/messaging"
|
|
41
|
+
import { tag } from "@kronos-ts/common"
|
|
42
|
+
|
|
43
|
+
let eventStore: EventStore | undefined
|
|
44
|
+
const capture = (a: App) => {
|
|
45
|
+
a.decorate("eventStore", (inner) => { eventStore = inner; return inner })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const app = await kronos()
|
|
49
|
+
.use(capture)
|
|
50
|
+
.use(postgres({ adapter: pgAdapter({ connectionString }) }))
|
|
51
|
+
.start()
|
|
52
|
+
|
|
53
|
+
// 1. Source events for a state (criteria + optional start position).
|
|
54
|
+
// Returns the matched events plus a consistency marker.
|
|
55
|
+
const criteria = EventCriteria.havingTags(tag("order", "123"))
|
|
56
|
+
const { events: sourced, marker } = await eventStore!.source({ criteria })
|
|
57
|
+
|
|
58
|
+
// 2. Append with a DCB precondition. The marker locks in the prefix you read
|
|
59
|
+
// from source() — if anything matching `criteria` was appended in the
|
|
60
|
+
// meantime, AppendConditionError is thrown.
|
|
61
|
+
const newMarker = await eventStore!.append(newEvents, { criteria, marker })
|
|
62
|
+
|
|
63
|
+
// 3. Tail events. open() returns a pull-based MessageStream<SequencedEvent>,
|
|
64
|
+
// not an AsyncIterable. Pull with next() and register a callback for
|
|
65
|
+
// wake-ups when more events arrive.
|
|
66
|
+
const stream = eventStore!.open({ position: 0n, criteria })
|
|
67
|
+
stream.setCallback(() => {
|
|
68
|
+
while (stream.hasNextAvailable()) {
|
|
69
|
+
const seq = stream.next()
|
|
70
|
+
if (!seq) break
|
|
71
|
+
// seq.event is the EventMessage; seq.sequence is the bigint position.
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
// remember to stream.close() when you're done
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Adapters
|
|
78
|
+
|
|
79
|
+
Three reference adapters ship with the package:
|
|
80
|
+
|
|
81
|
+
| Adapter | Runtime | Driver | LISTEN support |
|
|
82
|
+
|---------|---------|--------|----------------|
|
|
83
|
+
| `pgAdapter` | Node, Bun | `pg` (node-postgres) 8.20+ | native |
|
|
84
|
+
| `postgresAdapter` | Node, Bun | `postgres` (porsager) 3.4+ | native |
|
|
85
|
+
| `bunSqlAdapter` | Bun 1.2+ | built-in `Bun.sql` | native (1.2+) or polling shim |
|
|
86
|
+
|
|
87
|
+
You can also implement your own — `PostgresAdapter` from `@kronos-ts/postgres` is the contract.
|
|
88
|
+
|
|
89
|
+
### pgAdapter
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { pgAdapter } from "@kronos-ts/postgres/adapters/pg"
|
|
93
|
+
|
|
94
|
+
const adapter = pgAdapter({
|
|
95
|
+
connectionString: "postgresql://user:pass@host/db",
|
|
96
|
+
poolConfig: { max: 10 }, // optional pg.Pool overrides
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### postgresAdapter
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { postgresAdapter } from "@kronos-ts/postgres/adapters/postgres"
|
|
104
|
+
|
|
105
|
+
const adapter = postgresAdapter({
|
|
106
|
+
connectionString: "postgresql://user:pass@host/db",
|
|
107
|
+
clientOptions: { idle_timeout: 20 }, // optional porsager/postgres options
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### bunSqlAdapter
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { bunSqlAdapter } from "@kronos-ts/postgres/adapters/bun-sql"
|
|
115
|
+
|
|
116
|
+
// Bun runtime only — throws at connect() if run under Node
|
|
117
|
+
const adapter = bunSqlAdapter({
|
|
118
|
+
connectionString: "postgresql://user:pass@host/db",
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## DCB Semantics
|
|
123
|
+
|
|
124
|
+
The engine implements [Dynamic Consistency Boundaries](https://dcb.events):
|
|
125
|
+
- **Atomic multi-tag conflict check** on append with advisory-lock serialization
|
|
126
|
+
- **Criteria-based sourcing** with `@>` (contains-all) tag semantics
|
|
127
|
+
- **Gap-free tailing** using `xid8` + `pg_snapshot_xmin(pg_current_snapshot())`
|
|
128
|
+
|
|
129
|
+
Concurrent writers on **disjoint tags** run in parallel (advisory-lock taxonomy permits this). Concurrent writers on the **same tag** serialise — exactly one commits; the other receives `AppendConditionError`.
|
|
130
|
+
|
|
131
|
+
### Gap-free streaming
|
|
132
|
+
|
|
133
|
+
The `open()` streaming method uses a two-phase cursor to prevent the concurrent-commit gap bug:
|
|
134
|
+
|
|
135
|
+
- **Initial fetch:** `WHERE sequence_position > $cursor AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())`
|
|
136
|
+
- **Subsequent fetches:** `WHERE (transaction_id, sequence_position) > ($xid, $pos) AND transaction_id < pg_snapshot_xmin(pg_current_snapshot())`
|
|
137
|
+
|
|
138
|
+
The `pg_snapshot_xmin` watermark hides events from in-flight transactions, preventing the cursor from advancing past events that haven't committed yet. When a slow transaction finally commits, its events become visible in the correct xid8 order — no gaps, no re-ordering.
|
|
139
|
+
|
|
140
|
+
## Schema
|
|
141
|
+
|
|
142
|
+
`bootstrap: true` (default) auto-creates these tables on connect:
|
|
143
|
+
- `kronos_events` — append-only event log with `xid8` watermark, BIGSERIAL position, JSONB payload, GIN index on tags
|
|
144
|
+
- `kronos_snapshots` — composite-PK `(state_name, state_id)` with BYTEA payload
|
|
145
|
+
|
|
146
|
+
Custom table names: `postgres({ adapter, tableNames: { events: "my_events", snapshots: "my_snaps" } })`.
|
|
147
|
+
|
|
148
|
+
## SQLSTATE
|
|
149
|
+
|
|
150
|
+
DCB violations raise SQLSTATE `KR001`. The adapter catches this and throws `AppendConditionError`. To detect violations from your own code:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
import { AppendConditionError } from "@kronos-ts/postgres"
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await eventStore.append(events, condition)
|
|
157
|
+
} catch (e) {
|
|
158
|
+
if (e instanceof AppendConditionError) {
|
|
159
|
+
// Re-source the state to get a fresh marker, then retry.
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If you're working directly against the driver (bypassing the adapter), `isDcbViolation(err)` from `@kronos-ts/postgres` inspects a raw pg / postgres.js / Bun.sql error's `.code` field for the KR001 SQLSTATE.
|
|
165
|
+
|
|
166
|
+
## FAQ
|
|
167
|
+
|
|
168
|
+
**Q: Can I run my own migrations?** Yes — set `bootstrap: false` and apply equivalent DDL via your own migration tooling. The reference DDL lives in `packages/extensions/postgres/src/schema.ts` (`buildEventsTableDDL`, `buildEventsIndexesDDL`, `buildSnapshotsTableDDL`).
|
|
169
|
+
|
|
170
|
+
**Q: PgBouncer compatibility?** Yes — the adapter uses xact-scoped advisory locks (never session-scoped), so transaction-pooling mode is safe.
|
|
171
|
+
|
|
172
|
+
**Q: Older Postgres support?** No. `xid8` requires PG14+. PG14 has been GA since 2021 and is universally supported across managed services.
|
|
173
|
+
|
|
174
|
+
**Q: Is Bun.sql LISTEN available in all Bun versions?** `Bun.SQL` was introduced in Bun 1.2. The `bunSqlAdapter` feature-detects native LISTEN; if unavailable (older Bun), it falls back to a 250ms polling shim. For production streaming workloads, use Bun 1.2+ or switch to `pgAdapter` / `postgresAdapter` which always have native LISTEN.
|
|
175
|
+
|
|
176
|
+
**Q: TokenStore?** Out of scope for this package. Compose with `@kronos-ts/drizzle` / `@kronos-ts/knex` / etc. for token-store needs.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver-agnostic adapter contract for @kronos-ts/postgres.
|
|
3
|
+
*
|
|
4
|
+
* Per D-12.05 the package itself has zero direct dependency on any specific
|
|
5
|
+
* Postgres client library. Engine code (Plan 04) talks to this interface;
|
|
6
|
+
* adapter implementations (pg, postgres.js, Bun.sql) live under
|
|
7
|
+
* `./adapters/*` and are imported via package sub-path exports (D-12.08).
|
|
8
|
+
*
|
|
9
|
+
* Capability coverage per D-12.07:
|
|
10
|
+
* - query execution (parameterised) -> query / queryOne
|
|
11
|
+
* - transactions with isolation level -> transaction(isolationLevel, fn)
|
|
12
|
+
* - LISTEN-style subscription -> listen(channel, onNotification)
|
|
13
|
+
* - lifecycle -> connect / disconnect
|
|
14
|
+
*
|
|
15
|
+
* Error contract per D-12.12: SQLSTATE on thrown errors as `.code` (string).
|
|
16
|
+
* Adapter implementations MUST NOT swallow / rewrap SQLSTATE-bearing errors
|
|
17
|
+
* — the engine relies on `isDcbViolation(err)` reading `err.code === "KR001"`.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Postgres isolation levels the engine actually emits. SQL string values are
|
|
21
|
+
* the verbatim Postgres syntax used after `SET TRANSACTION ISOLATION LEVEL`,
|
|
22
|
+
* so adapters can interpolate directly without a lookup table.
|
|
23
|
+
*/
|
|
24
|
+
export declare const IsolationLevel: {
|
|
25
|
+
readonly READ_COMMITTED: "READ COMMITTED";
|
|
26
|
+
readonly REPEATABLE_READ: "REPEATABLE READ";
|
|
27
|
+
readonly SERIALIZABLE: "SERIALIZABLE";
|
|
28
|
+
};
|
|
29
|
+
export type IsolationLevel = (typeof IsolationLevel)[keyof typeof IsolationLevel];
|
|
30
|
+
/** Plain row shape returned by query/queryOne. Adapter implementations cast
|
|
31
|
+
* driver-specific row objects to this. */
|
|
32
|
+
export type QueryRow = Record<string, unknown>;
|
|
33
|
+
/**
|
|
34
|
+
* Active in-transaction handle. Pinned to a single underlying connection so
|
|
35
|
+
* statements never interleave with sibling pool traffic. Intentionally NO
|
|
36
|
+
* nested-transaction method — Plan 04's engine does not need it and savepoints
|
|
37
|
+
* would invite confusion about which level a SQLSTATE error propagates from.
|
|
38
|
+
*/
|
|
39
|
+
export interface PostgresAdapterTransaction {
|
|
40
|
+
query<R extends QueryRow = QueryRow>(sql: string, params?: unknown[]): Promise<R[]>;
|
|
41
|
+
}
|
|
42
|
+
/** Handle to a live LISTEN subscription. unlisten() unregisters + releases
|
|
43
|
+
* the dedicated connection (if any). */
|
|
44
|
+
export interface ListenSubscription {
|
|
45
|
+
unlisten(): Promise<void>;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The single interface the engine layer (Plan 04+) consumes. All concurrency
|
|
49
|
+
* (pool sizing, idle eviction, reconnect) lives below this seam — the engine
|
|
50
|
+
* code MUST NOT know about pools.
|
|
51
|
+
*/
|
|
52
|
+
export interface PostgresAdapter {
|
|
53
|
+
/**
|
|
54
|
+
* Run a parameterised SQL statement on a pool-borrowed connection.
|
|
55
|
+
* Returns rows; empty array if none. Thrown errors carry SQLSTATE on
|
|
56
|
+
* `.code` unchanged so `isDcbViolation(err)` works at any call site.
|
|
57
|
+
*/
|
|
58
|
+
query<R extends QueryRow = QueryRow>(sql: string, params?: unknown[]): Promise<R[]>;
|
|
59
|
+
/**
|
|
60
|
+
* Convenience for single-row queries. Returns `null` if zero rows.
|
|
61
|
+
* Throws if more than one row is returned (caller bug — use query()).
|
|
62
|
+
*/
|
|
63
|
+
queryOne<R extends QueryRow = QueryRow>(sql: string, params?: unknown[]): Promise<R | null>;
|
|
64
|
+
/**
|
|
65
|
+
* Open a transaction at the given isolation level, run `fn` against a
|
|
66
|
+
* pinned-connection handle, and COMMIT on resolution or ROLLBACK on
|
|
67
|
+
* rejection. If `fn` throws, the original error is re-thrown after
|
|
68
|
+
* ROLLBACK; rollback failures do NOT mask the original.
|
|
69
|
+
*
|
|
70
|
+
* The framework's AppendTransaction has a synchronous `rollback(): void`
|
|
71
|
+
* (see packages/eventsourcing/src/event-storage-engine.ts) — the
|
|
72
|
+
* implementation translates that into a fire-and-forget reject inside
|
|
73
|
+
* this method's promise, NOT an awaited rollback round-trip.
|
|
74
|
+
*/
|
|
75
|
+
transaction<T>(isolationLevel: IsolationLevel, fn: (tx: PostgresAdapterTransaction) => Promise<T>): Promise<T>;
|
|
76
|
+
/**
|
|
77
|
+
* Subscribe to `LISTEN <channel>` on a dedicated long-lived connection.
|
|
78
|
+
* Adapter implementations that lack a real LISTEN channel (e.g. Bun.sql
|
|
79
|
+
* pre-1.x) MAY implement a polling shim — the subscription handle is the
|
|
80
|
+
* same regardless. Plan 05's streaming open() uses this to wake up
|
|
81
|
+
* tailers immediately instead of waiting for a poll tick.
|
|
82
|
+
*/
|
|
83
|
+
listen(channel: string, onNotification: (payload: string | undefined) => void): Promise<ListenSubscription>;
|
|
84
|
+
/** Initialise pool / verify reachability. Idempotent. */
|
|
85
|
+
connect(): Promise<void>;
|
|
86
|
+
/** Drain pool, close LISTEN connections, release sockets. Idempotent. */
|
|
87
|
+
disconnect(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;CAIjB,CAAA;AACV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,OAAO,cAAc,CAAC,CAAA;AAEjF;2CAC2C;AAC3C,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACpF;AAED;yCACyC;AACzC,MAAM,WAAW,kBAAkB;IACjC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,KAAK,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IAEnF;;;OAGG;IACH,QAAQ,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IAE3F;;;;;;;;;;OAUG;IACH,WAAW,CAAC,CAAC,EACX,cAAc,EAAE,cAAc,EAC9B,EAAE,EAAE,CAAC,EAAE,EAAE,0BAA0B,KAAK,OAAO,CAAC,CAAC,CAAC,GACjD,OAAO,CAAC,CAAC,CAAC,CAAA;IAEb;;;;;;OAMG;IACH,MAAM,CACJ,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,GACpD,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAE9B,yDAAyD;IACzD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAExB,yEAAyE;IACzE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B"}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Driver-agnostic adapter contract for @kronos-ts/postgres.
|
|
3
|
+
*
|
|
4
|
+
* Per D-12.05 the package itself has zero direct dependency on any specific
|
|
5
|
+
* Postgres client library. Engine code (Plan 04) talks to this interface;
|
|
6
|
+
* adapter implementations (pg, postgres.js, Bun.sql) live under
|
|
7
|
+
* `./adapters/*` and are imported via package sub-path exports (D-12.08).
|
|
8
|
+
*
|
|
9
|
+
* Capability coverage per D-12.07:
|
|
10
|
+
* - query execution (parameterised) -> query / queryOne
|
|
11
|
+
* - transactions with isolation level -> transaction(isolationLevel, fn)
|
|
12
|
+
* - LISTEN-style subscription -> listen(channel, onNotification)
|
|
13
|
+
* - lifecycle -> connect / disconnect
|
|
14
|
+
*
|
|
15
|
+
* Error contract per D-12.12: SQLSTATE on thrown errors as `.code` (string).
|
|
16
|
+
* Adapter implementations MUST NOT swallow / rewrap SQLSTATE-bearing errors
|
|
17
|
+
* — the engine relies on `isDcbViolation(err)` reading `err.code === "KR001"`.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Postgres isolation levels the engine actually emits. SQL string values are
|
|
21
|
+
* the verbatim Postgres syntax used after `SET TRANSACTION ISOLATION LEVEL`,
|
|
22
|
+
* so adapters can interpolate directly without a lookup table.
|
|
23
|
+
*/
|
|
24
|
+
export const IsolationLevel = {
|
|
25
|
+
READ_COMMITTED: "READ COMMITTED",
|
|
26
|
+
REPEATABLE_READ: "REPEATABLE READ",
|
|
27
|
+
SERIALIZABLE: "SERIALIZABLE",
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,YAAY,EAAE,cAAc;CACpB,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun.sql adapter for @kronos-ts/postgres.
|
|
3
|
+
*
|
|
4
|
+
* Import via the sub-path (Bun runtime only):
|
|
5
|
+
* import { bunSqlAdapter } from "@kronos-ts/postgres/adapters/bun-sql"
|
|
6
|
+
*
|
|
7
|
+
* Requires Bun >= 1.2 for Bun.SQL (sql.transaction()). LISTEN support is
|
|
8
|
+
* feature-detected; if Bun.SQL lacks native LISTEN on the running version,
|
|
9
|
+
* the adapter falls back to a 250ms polling shim — slower wake-up but
|
|
10
|
+
* correct semantics.
|
|
11
|
+
*
|
|
12
|
+
* This file uses `globalThis as { Bun?: ... }` access to avoid a hard
|
|
13
|
+
* compile-time reference to Bun's global, so the package can ship under
|
|
14
|
+
* Node (where this file would never be imported) without TypeScript
|
|
15
|
+
* compilation errors. Users importing the sub-path under Node will get
|
|
16
|
+
* a clear runtime error from the connect() call.
|
|
17
|
+
*/
|
|
18
|
+
import type { PostgresAdapter } from "../adapter.js";
|
|
19
|
+
export interface BunSqlAdapterConfig {
|
|
20
|
+
readonly connectionString: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function bunSqlAdapter(config: BunSqlAdapterConfig): PostgresAdapter;
|
|
23
|
+
//# sourceMappingURL=bun-sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun-sql.d.ts","sourceRoot":"","sources":["../../src/adapters/bun-sql.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,eAAe,CAAA;AAGtB,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CAClC;AA6ED,wBAAgB,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CA0H1E"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bun.sql adapter for @kronos-ts/postgres.
|
|
3
|
+
*
|
|
4
|
+
* Import via the sub-path (Bun runtime only):
|
|
5
|
+
* import { bunSqlAdapter } from "@kronos-ts/postgres/adapters/bun-sql"
|
|
6
|
+
*
|
|
7
|
+
* Requires Bun >= 1.2 for Bun.SQL (sql.transaction()). LISTEN support is
|
|
8
|
+
* feature-detected; if Bun.SQL lacks native LISTEN on the running version,
|
|
9
|
+
* the adapter falls back to a 250ms polling shim — slower wake-up but
|
|
10
|
+
* correct semantics.
|
|
11
|
+
*
|
|
12
|
+
* This file uses `globalThis as { Bun?: ... }` access to avoid a hard
|
|
13
|
+
* compile-time reference to Bun's global, so the package can ship under
|
|
14
|
+
* Node (where this file would never be imported) without TypeScript
|
|
15
|
+
* compilation errors. Users importing the sub-path under Node will get
|
|
16
|
+
* a clear runtime error from the connect() call.
|
|
17
|
+
*/
|
|
18
|
+
import { IsolationLevel } from "../adapter.js";
|
|
19
|
+
/**
|
|
20
|
+
* Bun.SQL surfaces SQLSTATE in `err.errno` (not `err.code`). The adapter
|
|
21
|
+
* contract (D-12.12) requires SQLSTATE on `.code` so that `isDcbViolation(err)`
|
|
22
|
+
* works uniformly across all adapters.
|
|
23
|
+
*
|
|
24
|
+
* This helper normalises Bun.SQL errors: if `err.code === "ERR_POSTGRES_SERVER_ERROR"`
|
|
25
|
+
* and `err.errno` is a non-empty string, we copy `errno` to `code` before
|
|
26
|
+
* re-throwing, giving callers the SQLSTATE they expect on `.code`.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Bun.SQL's `.unsafe(text, params)` does not auto-encode JS arrays as Postgres
|
|
30
|
+
* array literals — single-element arrays get unwrapped to their scalar value
|
|
31
|
+
* and bound as TEXT, which fails when the column is TEXT[]. We pre-encode any
|
|
32
|
+
* plain Array param to the canonical `{"v1","v2"}` literal so it round-trips
|
|
33
|
+
* to TEXT[] / array operators (`@>`, `ANY`) correctly.
|
|
34
|
+
*
|
|
35
|
+
* Uint8Array / Buffer are not plain arrays (Array.isArray returns false), so
|
|
36
|
+
* BYTEA params remain untouched.
|
|
37
|
+
*/
|
|
38
|
+
function encodeArrayParam(arr) {
|
|
39
|
+
const escaped = arr.map((v) => {
|
|
40
|
+
if (v === null || v === undefined)
|
|
41
|
+
return "NULL";
|
|
42
|
+
const s = String(v);
|
|
43
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
44
|
+
});
|
|
45
|
+
return `{${escaped.join(",")}}`;
|
|
46
|
+
}
|
|
47
|
+
function normalizeParams(params) {
|
|
48
|
+
if (!params)
|
|
49
|
+
return [];
|
|
50
|
+
return params.map((p) => (Array.isArray(p) ? encodeArrayParam(p) : p));
|
|
51
|
+
}
|
|
52
|
+
function normalizeBunSqlError(err) {
|
|
53
|
+
if (typeof err === "object" &&
|
|
54
|
+
err !== null &&
|
|
55
|
+
err.code === "ERR_POSTGRES_SERVER_ERROR" &&
|
|
56
|
+
typeof err.errno === "string" &&
|
|
57
|
+
err.errno.length > 0) {
|
|
58
|
+
;
|
|
59
|
+
err.code = err.errno;
|
|
60
|
+
}
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
function getBunSql() {
|
|
64
|
+
const g = globalThis;
|
|
65
|
+
if (!g.Bun?.SQL) {
|
|
66
|
+
throw new Error("bunSqlAdapter requires the Bun runtime with built-in Bun.SQL (>= 1.2). " +
|
|
67
|
+
"Detected non-Bun runtime — use pgAdapter or postgresAdapter instead.");
|
|
68
|
+
}
|
|
69
|
+
return g.Bun.SQL;
|
|
70
|
+
}
|
|
71
|
+
export function bunSqlAdapter(config) {
|
|
72
|
+
let sql;
|
|
73
|
+
let disconnected = false;
|
|
74
|
+
function getInstance() {
|
|
75
|
+
if (!sql) {
|
|
76
|
+
const SQL = getBunSql();
|
|
77
|
+
sql = new SQL({ url: config.connectionString });
|
|
78
|
+
}
|
|
79
|
+
return sql;
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
async connect() {
|
|
83
|
+
const inst = getInstance();
|
|
84
|
+
const rows = await inst.unsafe("SELECT 1 AS ok").catch(normalizeBunSqlError);
|
|
85
|
+
if (rows[0]?.ok !== 1) {
|
|
86
|
+
throw new Error("bunSqlAdapter.connect: unexpected health-check response");
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
async disconnect() {
|
|
90
|
+
if (disconnected)
|
|
91
|
+
return;
|
|
92
|
+
disconnected = true;
|
|
93
|
+
if (sql) {
|
|
94
|
+
// Bun.SQL exposes both close() and end() — try end() first (graceful
|
|
95
|
+
// drain), fall back to close() if end is unavailable.
|
|
96
|
+
try {
|
|
97
|
+
if (typeof sql.end === "function") {
|
|
98
|
+
await sql.end();
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await sql.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
/* ignore disconnect errors */
|
|
106
|
+
}
|
|
107
|
+
sql = undefined;
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
async query(text, params) {
|
|
111
|
+
const inst = getInstance();
|
|
112
|
+
return inst.unsafe(text, normalizeParams(params)).catch(normalizeBunSqlError);
|
|
113
|
+
},
|
|
114
|
+
async queryOne(text, params) {
|
|
115
|
+
const rows = await this.query(text, params);
|
|
116
|
+
if (rows.length === 0)
|
|
117
|
+
return null;
|
|
118
|
+
if (rows.length > 1) {
|
|
119
|
+
throw new Error(`bunSqlAdapter.queryOne: more than one row returned (got ${rows.length}). Use query() for multi-row results.`);
|
|
120
|
+
}
|
|
121
|
+
return rows[0] ?? null;
|
|
122
|
+
},
|
|
123
|
+
async transaction(isolationLevel, fn) {
|
|
124
|
+
const inst = getInstance();
|
|
125
|
+
// Bun.SQL.begin(isolation, fn) starts a transaction at the given isolation
|
|
126
|
+
// level. The callback receives a scoped sql instance pinned to the
|
|
127
|
+
// underlying connection. SQLSTATE errors from within the transaction are
|
|
128
|
+
// normalized (errno -> code) via normalizeBunSqlError before propagating.
|
|
129
|
+
return inst.begin(`ISOLATION LEVEL ${isolationLevel}`, async (txSql) => {
|
|
130
|
+
const tx = {
|
|
131
|
+
async query(text, params) {
|
|
132
|
+
return txSql.unsafe(text, normalizeParams(params)).catch(normalizeBunSqlError);
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
return fn(tx);
|
|
136
|
+
}).catch(normalizeBunSqlError);
|
|
137
|
+
},
|
|
138
|
+
async listen(channel, onNotification) {
|
|
139
|
+
if (!/^[A-Za-z0-9_]+$/.test(channel)) {
|
|
140
|
+
throw new Error(`bunSqlAdapter.listen: channel name must match /^[A-Za-z0-9_]+$/, got: ${channel}`);
|
|
141
|
+
}
|
|
142
|
+
const inst = getInstance();
|
|
143
|
+
// Feature-detect native LISTEN
|
|
144
|
+
if (typeof inst.listen === "function") {
|
|
145
|
+
const sub = await inst.listen(channel, (p) => onNotification(p || undefined));
|
|
146
|
+
return {
|
|
147
|
+
async unlisten() {
|
|
148
|
+
await sub.unlisten();
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Polling fallback for Bun versions without native LISTEN.
|
|
153
|
+
// The fallback fires the callback on every tick with undefined payload,
|
|
154
|
+
// which causes the streaming engine to re-poll. This is coarse but
|
|
155
|
+
// semantically correct — the caller (open() in postgres-event-store.ts)
|
|
156
|
+
// already falls back to 250ms polling as a safety net, so this shim
|
|
157
|
+
// merely triggers additional pump cycles.
|
|
158
|
+
let stopped = false;
|
|
159
|
+
const tick = setInterval(() => {
|
|
160
|
+
if (stopped) {
|
|
161
|
+
clearInterval(tick);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
onNotification(undefined);
|
|
165
|
+
}, 250);
|
|
166
|
+
return {
|
|
167
|
+
async unlisten() {
|
|
168
|
+
stopped = true;
|
|
169
|
+
clearInterval(tick);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=bun-sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bun-sql.js","sourceRoot":"","sources":["../../src/adapters/bun-sql.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pg (node-postgres 8.20+) reference adapter for @kronos-ts/postgres.
|
|
3
|
+
*
|
|
4
|
+
* Import via the sub-path:
|
|
5
|
+
* import { pgAdapter } from "@kronos-ts/postgres/adapters/pg"
|
|
6
|
+
*
|
|
7
|
+
* Implements every PostgresAdapter method per the contract in
|
|
8
|
+
* ../adapter.ts. Pool sizing follows pg defaults; override via the
|
|
9
|
+
* `poolConfig` field if needed.
|
|
10
|
+
*
|
|
11
|
+
* LISTEN uses a dedicated long-lived PoolClient pinned outside the pool
|
|
12
|
+
* (never released) so notification delivery is not racing pool eviction.
|
|
13
|
+
* That client is closed by `disconnect()`.
|
|
14
|
+
*/
|
|
15
|
+
import { type PoolConfig } from "pg";
|
|
16
|
+
import type { PostgresAdapter } from "../adapter.js";
|
|
17
|
+
export interface PgAdapterConfig {
|
|
18
|
+
/** Standard libpq URI: postgresql://user:pass@host:port/db */
|
|
19
|
+
readonly connectionString: string;
|
|
20
|
+
/** Optional pg.Pool config overrides (max connections, idleTimeoutMillis, etc.). */
|
|
21
|
+
readonly poolConfig?: Omit<PoolConfig, "connectionString">;
|
|
22
|
+
}
|
|
23
|
+
export declare function pgAdapter(config: PgAdapterConfig): PostgresAdapter;
|
|
24
|
+
//# sourceMappingURL=pg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg.d.ts","sourceRoot":"","sources":["../../src/adapters/pg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,IAAI,CAAA;AAC3D,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,eAAe,CAAA;AAGtB,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;IACjC,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,EAAE,kBAAkB,CAAC,CAAA;CAC3D;AAOD,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,CAwJlE"}
|