@m2k-5f/pgtx 2.6.1 → 2.6.2

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/dist/pool.d.ts CHANGED
@@ -2,41 +2,15 @@ import { Connection } from "./connection";
2
2
  import { Transaction } from "./transaction";
3
3
  import { Future } from "fluent-future";
4
4
  import { PostgresError } from "./error";
5
- import { PoolPartialConfig } from "./types";
5
+ import { PoolPartialConfig, Row } from "./types";
6
6
  /**
7
- * The main entry point for Pgtx.
8
- * Manages a connection pool and provides high-level API for queries and transactions.
7
+ * Connection pool and the main entry point for Pgtx.
9
8
  *
10
9
  * @example
11
- * ```ts
12
- * const pool = new Pool({
13
- * host: 'localhost',
14
- * user: 'postgres',
15
- * password: 'postgres',
16
- * database: 'test',
17
- * max: 10
18
- * })
19
- *
20
- * // Simple query
10
+ * const pool = new Pool({ host: 'localhost', user: 'postgres', password: 'postgres', database: 'test', max: 10 })
21
11
  * const users = await pool.query`SELECT * FROM users WHERE id = ${1}`
22
- *
23
- * // Transaction
24
- * const result = await pool.begin(async tx => {
25
- * await tx.query`INSERT INTO users ...`
26
- * return 'success'
27
- * })
28
- *
29
- * // Manual acquire/release
30
- * const conn = await pool.acquire()
31
- * try {
32
- * await conn.query`SELECT 1`
33
- * } finally {
34
- * pool.release(conn)
35
- * }
36
- *
37
- * // Clean up
38
- * pool.close()
39
- * ```
12
+ * await pool.begin(async tx => tx.query`INSERT INTO users ...`)
13
+ * await pool.close()
40
14
  */
41
15
  export declare class Pool {
42
16
  private _available;
@@ -46,188 +20,62 @@ export declare class Pool {
46
20
  private _isOpened;
47
21
  constructor(config: PoolPartialConfig);
48
22
  /**
49
- * Acquires a dedicated connection from the pool.
50
- *
51
- * **Note:** You must call `pool.release(conn)` manually when finished.
52
- * For most cases, prefer using `pool.query()` or `pool.begin()` which handle this automatically.
53
- *
54
- * @returns A connection from the pool or a new one if available.
55
- *
56
- * @example
57
- * ```ts
58
- * const conn = await pool.acquire()
59
- * try {
60
- * await conn.query`SELECT 1`
61
- * } finally {
62
- * pool.release(conn)
63
- * }
64
- * ```
23
+ * Acquires a dedicated connection. Call `pool.release(conn)` when done —
24
+ * prefer `pool.query()`/`pool.begin()` where possible, they release automatically.
65
25
  */
66
26
  acquire(): Future<Connection, PostgresError>;
67
27
  /**
68
- * Provides a safe execution context for performing low-level operations
69
- * directly on a single, dedicated `Connection` instance.
70
- *
71
- * Automatically borrows a free socket from the pool, forwards it to the provided callback function,
72
- * and guarantees that the connection is released back to the pool once the execution completes,
73
- * even if errors or unexpected exceptions are thrown. Prevents connection descriptor leaks.
74
- *
75
- * @template T The return type of the provided callback function.
76
- * @param {(conn: Connection) => Promise<T>} fn A callback function that operates on the allocated Connection.
77
- * @returns {Future<T, PostgresError>} A `Future` that resolves with the return value of the callback.
28
+ * Runs `fn` with a borrowed connection and releases it afterward, even on error.
78
29
  *
79
30
  * @example
80
- * // Executing low-level engine commands on a single, pinned connection
81
- * const status = await pool.withAcquire(async (conn) => {
82
- * return await conn.query`SELECT pg_is_in_recovery()`;
83
- * });
31
+ * const status = await pool.withAcquire(conn => conn.query`SELECT pg_is_in_recovery()`)
84
32
  */
85
33
  withAcquire<T>(fn: (conn: Connection) => Promise<T>): Future<T, unknown>;
86
- /**
87
- * Releases the connection back to the pool.
88
- *
89
- * If there are pending `acquire()` calls, the connection is passed directly to the next waiter.
90
- * Otherwise, it's added to the available connections queue.
91
- *
92
- * @param conn - The connection to release.
93
- *
94
- * @example
95
- * ```ts
96
- * const conn = await pool.acquire()
97
- * try {
98
- * await conn.query`SELECT 1`
99
- * } finally {
100
- * pool.release(conn)
101
- * }
102
- * ```
103
- */
34
+ /** Returns `conn` to the pool, or hands it directly to the next waiting `acquire()`. */
104
35
  release(conn: Connection): void;
105
36
  /**
106
- * Starts a managed transaction.
107
- *
108
- * Automatically acquires a connection and handles `BEGIN`, `COMMIT`, and `ROLLBACK`.
109
- * If the callback throws an error, the transaction is rolled back.
110
- *
111
- * @param txCallback - Async function that receives a `Transaction` instance.
112
- * @returns The value returned from the callback.
37
+ * Runs `txCallback` inside a transaction on a borrowed connection, releasing it afterward.
113
38
  *
114
39
  * @example
115
- * ```ts
116
- * const result = await pool.begin(async tx => {
117
- * await tx.query`INSERT INTO accounts (id, balance) VALUES (1, 100)`
40
+ * await pool.begin(async tx => {
118
41
  * await tx.query`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
119
- * return { success: true }
120
42
  * })
121
- * ```
122
43
  */
123
44
  begin<T>(txCallback: (transaction: Transaction) => Promise<T>): Future<T, unknown>;
124
45
  /**
125
- * Executes a one-off query using pipeline.
126
- *
127
- * Automatically acquires and releases a connection from the pool.
128
- * For optimal performance, multiple queries can be pipelined through the same connection.
129
- *
130
- * @param templates - Tagged template string with SQL.
131
- * @param args - Query parameters.
132
- * @returns Array of rows with proper typing.
133
- *
134
- * @example
135
- * ```ts
136
- * // Simple query
137
- * const users = await pool.query`SELECT * FROM users`
138
- *
139
- * // With parameters
140
- * const user = await pool.query`SELECT * FROM users WHERE id = ${1}`
141
- *
142
- * // With typed result
143
- * type User = { id: number, name: string }
144
- * const users = await pool.query<User>`SELECT * FROM users`
145
- * ```
146
- */
147
- query<T extends Record<string, any>>(templates: TemplateStringsArray, ...args: any[]): Future<T[], PostgresError>;
148
- /**
149
- * Executes an SQL query in streaming mode.
150
- *
151
- * Data is streamed directly from the PostgreSQL binary network buffer into the Web Streams API
152
- * (`ReadableStream`), bypassing any intermediate array allocation or row accumulation in the JS heap.
153
- * This pattern provides a true Zero-Memory Footprint and is ideal for exporting massive tables
154
- * or piping database payloads directly into HTTP responses (e.g., via `Bun.serve` or fetch `Response`).
155
- *
156
- * @template T The expected shape of a single row interface.
157
- * @param {TemplateStringsArray} templates The SQL string parts from the tagged template literal.
158
- * @param {...any} args The parameterized query arguments.
159
- * @returns {ReadableStream<T>} Synchronously returns a native Web ReadableStream instance.
160
- *
161
- * @example
162
- * // Streaming a giant table directly to an HTTP response (Bun.serve)
163
- * const userStream = pool.stream<User>`SELECT id, name FROM users`;
164
- * return new Response(userStream, { headers: { 'Content-Type': 'application/json' } });
46
+ * Runs a one-off query on a borrowed connection.
165
47
  *
166
48
  * @example
167
- * // Asynchronously iterating over rows as they arrive from the wire socket
168
- * const stream = pool.stream<User>`SELECT * FROM orders WHERE status = ${'processed'}`;
169
- * for await (const row of stream) {
170
- * console.log(row.id, row.amount); // Row object is eligible for GC immediately after iteration
171
- * }
49
+ * const users = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
172
50
  */
173
- stream<T extends Record<string, any>>(templates: TemplateStringsArray, ...args: any[]): ReadableStream<T>;
51
+ query<T extends Row>(templates: TemplateStringsArray, ...args: any[]): Future<T[], PostgresError>;
52
+ /** Like {@link query}, but for statements that don't return rows. */
53
+ execute(templates: TemplateStringsArray, ...params: any[]): Future<void, PostgresError>;
174
54
  /**
175
- * Sends an asynchronous notification to a channel via `pg_notify`.
176
- *
177
- * @param channelName - The channel identifier
178
- * @param payload - Optional string data (max 8000 bytes)
55
+ * Streams query results as a `ReadableStream`, without buffering rows in memory.
179
56
  *
180
57
  * @example
181
- * ```ts
182
- * await pool.notify('events', 'hello')
183
- * ```
58
+ * for await (const row of pool.stream<User>`SELECT * FROM orders`) { ... }
184
59
  */
60
+ stream<T extends Row>(templates: TemplateStringsArray, ...args: any[]): ReadableStream<T>;
61
+ /** Sends a `pg_notify` message on `channelName` (payload ≤ 8000 bytes). */
185
62
  notify(channelName: string, payload?: string): Future<void, PostgresError>;
186
63
  /**
187
- * Asynchronously subscribes to pub/sub events on a specific PostgreSQL channel (LISTEN).
188
- *
189
- * This method automatically claims a dedicated connection from the pool, registers the callback
190
- * to handle incoming asynchronous database notices (`NotificationResponse` packets), and returns
191
- * a lazy unsubscribe function wrapped in a `Future`.
192
- *
193
- * Invoking the returned unsubscribe function will automatically issue the `UNLISTEN` command
194
- * to the database backend, clean up the memory callback, and safely release the connection back to the pool.
195
- *
196
- * @param {string} channel The name of the PostgreSQL notification channel.
197
- * @param {(payload: string) => void} callback The event handler invoked when a NOTIFY message arrives.
198
- * @returns {Future<() => Promise<void>, PostgresError>} A `Future` resolving to an async unsubscribe function.
64
+ * Subscribes `callback` to `channel` on a dedicated connection.
65
+ * Returns an unsubscribe function that issues `UNLISTEN` and releases the connection.
199
66
  *
200
67
  * @example
201
- * // Subscribing to database events directly from the Pool
202
- * const unlisten = await pool.listen('order_created', (payload) => {
203
- * const order = JSON.parse(payload);
204
- * console.log(`New order received: ${order.id}`);
205
- * });
206
- *
207
- * // When the subscription is no longer needed (e.g., during teardown or server stop):
208
- * await unlisten(); // The socket cleanly issues UNLISTEN and returns to the pool of free connections.
68
+ * const unlisten = await pool.listen('order_created', payload => console.log(payload))
69
+ * await unlisten()
209
70
  */
210
71
  listen(channel: string, callback: (payload: string) => void): Future<() => Future<void, PostgresError>, PostgresError>;
211
- /**
212
- * Number of available (idle) connections in the pool.
213
- */
72
+ /** Number of idle connections. */
214
73
  get size(): number;
215
- /**
216
- * Total number of connections currently managed by the pool
217
- * (available + in use).
218
- */
74
+ /** Total connections managed (idle + in use). */
219
75
  get total(): number;
220
76
  /**
221
- * Shuts down the pool and closes all active connections.
222
- *
223
- * All pending `acquire()` calls will be rejected with an error.
224
- * The pool cannot be used after calling `close()`.
225
- *
226
- * @example
227
- * ```ts
228
- * pool.close()
229
- * ```
77
+ * Closes idle connections and rejects pending `acquire()` calls. Not usable afterward.
230
78
  */
231
- close(): void;
79
+ close(): Future<void, PostgresError>;
232
80
  }
233
81
  //# sourceMappingURL=pool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAC;AAClD,OAAO,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAc,iBAAiB,EAAU,MAAM,SAAS,CAAC;AAGhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAO;gBAEZ,MAAM,EAAE,iBAAiB;IAOrC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO;IA2BP;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC;IAUnD;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU;IAiBxB;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAU7D;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAuBpF;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;IAoCzG;;;;;;;;;;OAUG;IACH,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAQhD;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAY3D;;OAEG;IACH,IAAI,IAAI,WAEP;IAGD;;;OAGG;IACH,IAAI,KAAK,WAER;IAGD;;;;;;;;;;OAUG;IACH,KAAK;CAaR"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAS,MAAM,EAAM,MAAM,eAAe,CAAC;AAClD,OAAO,EAAiB,aAAa,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAc,iBAAiB,EAAE,GAAG,EAAU,MAAM,SAAS,CAAC;AAGrE;;;;;;;;GAQG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAO;gBAEZ,MAAM,EAAE,iBAAiB;IAOrC;;;OAGG;IACH,OAAO;IA2BP;;;;;OAKG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC;IAUnD,wFAAwF;IACxF,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC;IAU7D;;;;;OAKG;IACH,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE;IAuBpE,qEAAqE;IACrE,OAAO,CAAC,SAAS,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE;IAuBzD;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,GAAG,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC;IAoCzF,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW;IAQhD;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAY3D,kCAAkC;IAClC,IAAI,IAAI,WAEP;IAGD,iDAAiD;IACjD,IAAI,KAAK,WAER;IAGD;;OAEG;IACH,KAAK;CAiBR"}
package/dist/pool.js CHANGED
@@ -3,39 +3,13 @@ import { Queue, RingQueue } from "./queue";
3
3
  import { Begin, Future, Ok } from "fluent-future";
4
4
  import { ErrPoolClosed } from "./error";
5
5
  /**
6
- * The main entry point for Pgtx.
7
- * Manages a connection pool and provides high-level API for queries and transactions.
6
+ * Connection pool and the main entry point for Pgtx.
8
7
  *
9
8
  * @example
10
- * ```ts
11
- * const pool = new Pool({
12
- * host: 'localhost',
13
- * user: 'postgres',
14
- * password: 'postgres',
15
- * database: 'test',
16
- * max: 10
17
- * })
18
- *
19
- * // Simple query
9
+ * const pool = new Pool({ host: 'localhost', user: 'postgres', password: 'postgres', database: 'test', max: 10 })
20
10
  * const users = await pool.query`SELECT * FROM users WHERE id = ${1}`
21
- *
22
- * // Transaction
23
- * const result = await pool.begin(async tx => {
24
- * await tx.query`INSERT INTO users ...`
25
- * return 'success'
26
- * })
27
- *
28
- * // Manual acquire/release
29
- * const conn = await pool.acquire()
30
- * try {
31
- * await conn.query`SELECT 1`
32
- * } finally {
33
- * pool.release(conn)
34
- * }
35
- *
36
- * // Clean up
37
- * pool.close()
38
- * ```
11
+ * await pool.begin(async tx => tx.query`INSERT INTO users ...`)
12
+ * await pool.close()
39
13
  */
40
14
  export class Pool {
41
15
  constructor(config) {
@@ -47,22 +21,8 @@ export class Pool {
47
21
  this._available = new RingQueue(this.config.max);
48
22
  }
49
23
  /**
50
- * Acquires a dedicated connection from the pool.
51
- *
52
- * **Note:** You must call `pool.release(conn)` manually when finished.
53
- * For most cases, prefer using `pool.query()` or `pool.begin()` which handle this automatically.
54
- *
55
- * @returns A connection from the pool or a new one if available.
56
- *
57
- * @example
58
- * ```ts
59
- * const conn = await pool.acquire()
60
- * try {
61
- * await conn.query`SELECT 1`
62
- * } finally {
63
- * pool.release(conn)
64
- * }
65
- * ```
24
+ * Acquires a dedicated connection. Call `pool.release(conn)` when done —
25
+ * prefer `pool.query()`/`pool.begin()` where possible, they release automatically.
66
26
  */
67
27
  acquire() {
68
28
  if (!this._isOpened)
@@ -84,22 +44,10 @@ export class Pool {
84
44
  return future;
85
45
  }
86
46
  /**
87
- * Provides a safe execution context for performing low-level operations
88
- * directly on a single, dedicated `Connection` instance.
89
- *
90
- * Automatically borrows a free socket from the pool, forwards it to the provided callback function,
91
- * and guarantees that the connection is released back to the pool once the execution completes,
92
- * even if errors or unexpected exceptions are thrown. Prevents connection descriptor leaks.
93
- *
94
- * @template T The return type of the provided callback function.
95
- * @param {(conn: Connection) => Promise<T>} fn A callback function that operates on the allocated Connection.
96
- * @returns {Future<T, PostgresError>} A `Future` that resolves with the return value of the callback.
47
+ * Runs `fn` with a borrowed connection and releases it afterward, even on error.
97
48
  *
98
49
  * @example
99
- * // Executing low-level engine commands on a single, pinned connection
100
- * const status = await pool.withAcquire(async (conn) => {
101
- * return await conn.query`SELECT pg_is_in_recovery()`;
102
- * });
50
+ * const status = await pool.withAcquire(conn => conn.query`SELECT pg_is_in_recovery()`)
103
51
  */
104
52
  withAcquire(fn) {
105
53
  return Begin()
@@ -107,27 +55,12 @@ export class Pool {
107
55
  .andThen(conn => Future.of(fn(conn))
108
56
  .finally(() => this.release(conn)));
109
57
  }
110
- /**
111
- * Releases the connection back to the pool.
112
- *
113
- * If there are pending `acquire()` calls, the connection is passed directly to the next waiter.
114
- * Otherwise, it's added to the available connections queue.
115
- *
116
- * @param conn - The connection to release.
117
- *
118
- * @example
119
- * ```ts
120
- * const conn = await pool.acquire()
121
- * try {
122
- * await conn.query`SELECT 1`
123
- * } finally {
124
- * pool.release(conn)
125
- * }
126
- * ```
127
- */
58
+ /** Returns `conn` to the pool, or hands it directly to the next waiting `acquire()`. */
128
59
  release(conn) {
129
- if (!this._isOpened)
60
+ if (!this._isOpened) {
61
+ void conn.close();
130
62
  throw ErrPoolClosed;
63
+ }
131
64
  if (!conn.isOpened) {
132
65
  this._total--;
133
66
  return;
@@ -139,22 +72,12 @@ export class Pool {
139
72
  this._available.push(conn);
140
73
  }
141
74
  /**
142
- * Starts a managed transaction.
143
- *
144
- * Automatically acquires a connection and handles `BEGIN`, `COMMIT`, and `ROLLBACK`.
145
- * If the callback throws an error, the transaction is rolled back.
146
- *
147
- * @param txCallback - Async function that receives a `Transaction` instance.
148
- * @returns The value returned from the callback.
75
+ * Runs `txCallback` inside a transaction on a borrowed connection, releasing it afterward.
149
76
  *
150
77
  * @example
151
- * ```ts
152
- * const result = await pool.begin(async tx => {
153
- * await tx.query`INSERT INTO accounts (id, balance) VALUES (1, 100)`
78
+ * await pool.begin(async tx => {
154
79
  * await tx.query`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
155
- * return { success: true }
156
80
  * })
157
- * ```
158
81
  */
159
82
  begin(txCallback) {
160
83
  return Begin()
@@ -163,27 +86,10 @@ export class Pool {
163
86
  .finally(() => this.release(conn)));
164
87
  }
165
88
  /**
166
- * Executes a one-off query using pipeline.
167
- *
168
- * Automatically acquires and releases a connection from the pool.
169
- * For optimal performance, multiple queries can be pipelined through the same connection.
170
- *
171
- * @param templates - Tagged template string with SQL.
172
- * @param args - Query parameters.
173
- * @returns Array of rows with proper typing.
89
+ * Runs a one-off query on a borrowed connection.
174
90
  *
175
91
  * @example
176
- * ```ts
177
- * // Simple query
178
- * const users = await pool.query`SELECT * FROM users`
179
- *
180
- * // With parameters
181
- * const user = await pool.query`SELECT * FROM users WHERE id = ${1}`
182
- *
183
- * // With typed result
184
- * type User = { id: number, name: string }
185
- * const users = await pool.query<User>`SELECT * FROM users`
186
- * ```
92
+ * const users = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
187
93
  */
188
94
  query(templates, ...args) {
189
95
  if (!this._isOpened)
@@ -203,30 +109,30 @@ export class Pool {
203
109
  return conn.query(templates, ...args);
204
110
  });
205
111
  }
112
+ /** Like {@link query}, but for statements that don't return rows. */
113
+ execute(templates, ...params) {
114
+ if (!this._isOpened)
115
+ return Future.reject(ErrPoolClosed);
116
+ while (this._available.hasMore) {
117
+ const conn = this._available.shift;
118
+ if (conn.isClosed) {
119
+ this._total--;
120
+ continue;
121
+ }
122
+ this._available.push(conn);
123
+ return conn.execute(templates, ...params);
124
+ }
125
+ return this.acquire()
126
+ .andThen(conn => {
127
+ this.release(conn);
128
+ return conn.execute(templates, ...params);
129
+ });
130
+ }
206
131
  /**
207
- * Executes an SQL query in streaming mode.
208
- *
209
- * Data is streamed directly from the PostgreSQL binary network buffer into the Web Streams API
210
- * (`ReadableStream`), bypassing any intermediate array allocation or row accumulation in the JS heap.
211
- * This pattern provides a true Zero-Memory Footprint and is ideal for exporting massive tables
212
- * or piping database payloads directly into HTTP responses (e.g., via `Bun.serve` or fetch `Response`).
213
- *
214
- * @template T The expected shape of a single row interface.
215
- * @param {TemplateStringsArray} templates The SQL string parts from the tagged template literal.
216
- * @param {...any} args The parameterized query arguments.
217
- * @returns {ReadableStream<T>} Synchronously returns a native Web ReadableStream instance.
132
+ * Streams query results as a `ReadableStream`, without buffering rows in memory.
218
133
  *
219
134
  * @example
220
- * // Streaming a giant table directly to an HTTP response (Bun.serve)
221
- * const userStream = pool.stream<User>`SELECT id, name FROM users`;
222
- * return new Response(userStream, { headers: { 'Content-Type': 'application/json' } });
223
- *
224
- * @example
225
- * // Asynchronously iterating over rows as they arrive from the wire socket
226
- * const stream = pool.stream<User>`SELECT * FROM orders WHERE status = ${'processed'}`;
227
- * for await (const row of stream) {
228
- * console.log(row.id, row.amount); // Row object is eligible for GC immediately after iteration
229
- * }
135
+ * for await (const row of pool.stream<User>`SELECT * FROM orders`) { ... }
230
136
  */
231
137
  stream(templates, ...args) {
232
138
  if (!this._isOpened)
@@ -249,52 +155,26 @@ export class Pool {
249
155
  this.acquire()
250
156
  .tap(conn => {
251
157
  this.release(conn);
252
- conn['_streamWithController'](templates, args, controller);
158
+ conn['_performStream'](templates, args, controller);
253
159
  })
254
160
  .catch(err => {
255
161
  controller.error(err);
256
162
  });
257
163
  return stream;
258
164
  }
259
- /**
260
- * Sends an asynchronous notification to a channel via `pg_notify`.
261
- *
262
- * @param channelName - The channel identifier
263
- * @param payload - Optional string data (max 8000 bytes)
264
- *
265
- * @example
266
- * ```ts
267
- * await pool.notify('events', 'hello')
268
- * ```
269
- */
165
+ /** Sends a `pg_notify` message on `channelName` (payload ≤ 8000 bytes). */
270
166
  notify(channelName, payload = "") {
271
167
  return this.acquire()
272
168
  .andThen(conn => conn.notify(channelName, payload)
273
169
  .finally(() => this.release(conn)));
274
170
  }
275
171
  /**
276
- * Asynchronously subscribes to pub/sub events on a specific PostgreSQL channel (LISTEN).
277
- *
278
- * This method automatically claims a dedicated connection from the pool, registers the callback
279
- * to handle incoming asynchronous database notices (`NotificationResponse` packets), and returns
280
- * a lazy unsubscribe function wrapped in a `Future`.
281
- *
282
- * Invoking the returned unsubscribe function will automatically issue the `UNLISTEN` command
283
- * to the database backend, clean up the memory callback, and safely release the connection back to the pool.
284
- *
285
- * @param {string} channel The name of the PostgreSQL notification channel.
286
- * @param {(payload: string) => void} callback The event handler invoked when a NOTIFY message arrives.
287
- * @returns {Future<() => Promise<void>, PostgresError>} A `Future` resolving to an async unsubscribe function.
172
+ * Subscribes `callback` to `channel` on a dedicated connection.
173
+ * Returns an unsubscribe function that issues `UNLISTEN` and releases the connection.
288
174
  *
289
175
  * @example
290
- * // Subscribing to database events directly from the Pool
291
- * const unlisten = await pool.listen('order_created', (payload) => {
292
- * const order = JSON.parse(payload);
293
- * console.log(`New order received: ${order.id}`);
294
- * });
295
- *
296
- * // When the subscription is no longer needed (e.g., during teardown or server stop):
297
- * await unlisten(); // The socket cleanly issues UNLISTEN and returns to the pool of free connections.
176
+ * const unlisten = await pool.listen('order_created', payload => console.log(payload))
177
+ * await unlisten()
298
178
  */
299
179
  listen(channel, callback) {
300
180
  return this.acquire()
@@ -302,38 +182,27 @@ export class Pool {
302
182
  .map(() => () => conn.unlisten(channel, callback)
303
183
  .tap(() => this.release(conn))));
304
184
  }
305
- /**
306
- * Number of available (idle) connections in the pool.
307
- */
185
+ /** Number of idle connections. */
308
186
  get size() {
309
187
  return this._available.size;
310
188
  }
311
- /**
312
- * Total number of connections currently managed by the pool
313
- * (available + in use).
314
- */
189
+ /** Total connections managed (idle + in use). */
315
190
  get total() {
316
191
  return this._total;
317
192
  }
318
193
  /**
319
- * Shuts down the pool and closes all active connections.
320
- *
321
- * All pending `acquire()` calls will be rejected with an error.
322
- * The pool cannot be used after calling `close()`.
323
- *
324
- * @example
325
- * ```ts
326
- * pool.close()
327
- * ```
194
+ * Closes idle connections and rejects pending `acquire()` calls. Not usable afterward.
328
195
  */
329
196
  close() {
330
197
  this._isOpened = false;
198
+ const futures = [];
331
199
  while (this._available.hasMore) {
332
- this._available.shift.close();
200
+ futures.push(this._available.shift.close());
333
201
  }
334
202
  while (this._waiting.hasMore) {
335
203
  this._waiting.shift.reject(ErrPoolClosed);
336
204
  }
337
205
  this._total = 0;
206
+ return Future.all(futures).map(() => { });
338
207
  }
339
208
  }
@@ -64,5 +64,6 @@ export declare class ConnectionResponseReader {
64
64
  readBindComplete(): void;
65
65
  readParameterDescription(): void;
66
66
  readNoData(): void;
67
+ skip(count: number): void;
67
68
  }
68
69
  //# sourceMappingURL=connection-response-reader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA6B,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAOxC,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,OAAO,IAAI,OAAO;IAKlB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO3C,cAAc,IAAI,IAAI;IAOtB,mBAAmB,IAAI,IAAI;IAO3B,cAAc,IAAI,MAAM;IAaxB,SAAS,CAAC,SAAS,EAAE,MAAM;IAK3B,aAAa,IAAI,OAAO;IAaxB,iBAAiB;IAIjB,QAAQ;IAMR,YAAY,IAAI,MAAM;IAOtB,SAAS;IAST,WAAW;IAOX,WAAW,IAAI,MAAM;IAMrB,QAAQ;IAWR,SAAS,CAAC,MAAM,EAAE,MAAM;IAQxB,aAAa,CAAC,MAAM,EAAE,MAAM;CAW/B;AAGD,qBAAa,wBAAwB;IAG7B,OAAO,CAAC,MAAM;IADlB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ;cACoC,YAAY;;;IAIxD,kBAAkB,IACoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAQnB,kBAAkB;;;;IAQlB,iBAAiB,IAAI,aAAa;IA4ClC,iBAAiB,IACoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKvC,kBAAkB;IAsBlB,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0G1F,wBAAwB;;;;IASxB,mBAAmB,IAAI,MAAM;IAK7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAGjB,gBAAgB;IAGhB,wBAAwB;IAOxB,UAAU;CACb"}
1
+ {"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA6B,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC5G,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAQxC,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,KAAK,CAAI;IAEjB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAAI,MAAM;IAOlB,SAAS,IAAI,MAAM;IAOnB,SAAS,IAAI,MAAM;IAOnB,WAAW,IAAI,MAAM;IAQrB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOrC,OAAO,IAAI,OAAO;IAKlB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAO3C,cAAc,IAAI,IAAI;IAOtB,mBAAmB,IAAI,IAAI;IAO3B,cAAc,IAAI,MAAM;IAaxB,SAAS,CAAC,SAAS,EAAE,MAAM;IAK3B,aAAa,IAAI,OAAO;IAaxB,iBAAiB;IAIjB,QAAQ;IAMR,YAAY,IAAI,MAAM;IAOtB,SAAS;IAST,WAAW;IAOX,WAAW,IAAI,MAAM;IAMrB,QAAQ;IAWR,SAAS,CAAC,MAAM,EAAE,MAAM;IAQxB,aAAa,CAAC,MAAM,EAAE,MAAM;CAW/B;AAGD,qBAAa,wBAAwB;IAG7B,OAAO,CAAC,MAAM;IADlB,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ;cACoC,YAAY;;;IAIxD,kBAAkB,IACoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAQnB,kBAAkB;;;;IAQlB,iBAAiB,IAAI,aAAa;IA4ClC,iBAAiB,IACoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKvC,kBAAkB;IAsBlB,WAAW,CAAC,YAAY,EAAE,iBAAiB,EAAE,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0G1F,wBAAwB;;;;IASxB,mBAAmB,IAAI,MAAM;IAK7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAGjB,gBAAgB;IAGhB,wBAAwB;IAOxB,UAAU;IAEV,IAAI,CAAC,KAAK,EAAE,MAAM;CAGrB"}
@@ -376,4 +376,7 @@ export class ConnectionResponseReader {
376
376
  }
377
377
  }
378
378
  readNoData() { }
379
+ skip(count) {
380
+ this.buffer.skipBytes(count);
381
+ }
379
382
  }
@@ -4,14 +4,14 @@ import { ConnectionResponseReader } from '../protocol/connection-response-reader
4
4
  import { ConnectionRequestWriter } from '../protocol/connection-request-writer';
5
5
  export declare class SocketConnector {
6
6
  private _socket;
7
- private _onData;
8
- private _onError;
9
7
  private residualBuffer;
10
- private _isDestroyed;
11
- constructor(_socket: Socket, _onData: (type: ResponseType, length: number, reader: ConnectionResponseReader) => void, _onError: (error: Error) => void);
8
+ private _onError;
9
+ private _onClose;
10
+ private _onData;
11
+ private _destroyed;
12
+ constructor(_socket: Socket, onData: (type: ResponseType, length: number, reader: ConnectionResponseReader) => void, onError: (error: unknown) => void);
12
13
  write(writer: ConnectionRequestWriter): void;
13
14
  unwrapSocket(): Socket;
14
15
  destroy(): void;
15
- get isDestroyed(): boolean;
16
16
  }
17
17
  //# sourceMappingURL=socket-connector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"socket-connector.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAE/E,qBAAa,eAAe;IAKpB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,QAAQ;IANpB,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,YAAY,CAAQ;gBAGhB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,wBAAwB,KAAK,IAAI,EACvF,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;IA8B5C,KAAK,CAAC,MAAM,EAAE,uBAAuB;IAMrC,YAAY;IAQZ,OAAO;IAMP,IAAI,WAAW,YAA6B;CAC/C"}
1
+ {"version":3,"file":"socket-connector.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,wCAAwC,CAAA;AACjF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAE/E,qBAAa,eAAe;IAQpB,OAAO,CAAC,OAAO;IAPnB,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,UAAU,CAAQ;gBAGd,OAAO,EAAE,MAAM,EACvB,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,wBAAwB,KAAK,IAAI,EACtF,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI;IA4CrC,KAAK,CAAC,MAAM,EAAE,uBAAuB;IAKrC,YAAY;IASZ,OAAO;CAGV"}