@m2k-5f/pgtx 2.6.0 → 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.js CHANGED
@@ -1,76 +1,32 @@
1
1
  import { Connection } from "./connection";
2
2
  import { Queue, RingQueue } from "./queue";
3
3
  import { Begin, Future, Ok } from "fluent-future";
4
- import { PostgresError } from "./error";
5
- const ErrPoolClosed = new PostgresError('Pool closed');
4
+ import { ErrPoolClosed } from "./error";
6
5
  /**
7
- * The main entry point for Pgtx.
8
- * Manages a connection pool and provides high-level API for queries and transactions.
6
+ * Connection pool and the main entry point for Pgtx.
9
7
  *
10
8
  * @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
9
+ * const pool = new Pool({ host: 'localhost', user: 'postgres', password: 'postgres', database: 'test', max: 10 })
21
10
  * 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
- * ```
11
+ * await pool.begin(async tx => tx.query`INSERT INTO users ...`)
12
+ * await pool.close()
40
13
  */
41
14
  export class Pool {
42
- _checkClosed() {
43
- if (this._isClosed)
44
- throw new Error('Pool closed');
45
- }
46
- constructor(params) {
15
+ constructor(config) {
47
16
  this._total = 0;
48
17
  this._waiting = new Queue();
49
- this._isClosed = false;
50
- this._config = params;
51
- this._max = params.max || 20;
52
- this._available = new RingQueue(this._max);
18
+ this._isOpened = true;
19
+ const conf = { ...config, max: config.max || 20 };
20
+ this.config = conf;
21
+ this._available = new RingQueue(this.config.max);
53
22
  }
54
23
  /**
55
- * Acquires a dedicated connection from the pool.
56
- *
57
- * **Note:** You must call `pool.release(conn)` manually when finished.
58
- * For most cases, prefer using `pool.query()` or `pool.begin()` which handle this automatically.
59
- *
60
- * @returns A connection from the pool or a new one if available.
61
- *
62
- * @example
63
- * ```ts
64
- * const conn = await pool.acquire()
65
- * try {
66
- * await conn.query`SELECT 1`
67
- * } finally {
68
- * pool.release(conn)
69
- * }
70
- * ```
24
+ * Acquires a dedicated connection. Call `pool.release(conn)` when done —
25
+ * prefer `pool.query()`/`pool.begin()` where possible, they release automatically.
71
26
  */
72
27
  acquire() {
73
- this._checkClosed();
28
+ if (!this._isOpened)
29
+ return Future.reject(ErrPoolClosed);
74
30
  while (this._available.hasMore) {
75
31
  const conn = this._available.shift;
76
32
  if (conn.isOpened) {
@@ -78,9 +34,9 @@ export class Pool {
78
34
  }
79
35
  this._total--;
80
36
  }
81
- if (this._total < this._max) {
37
+ if (this._total < this.config.max) {
82
38
  this._total++;
83
- return Connection.new(this._config)
39
+ return Connection.new(this.config)
84
40
  .tapErr(() => this._total--);
85
41
  }
86
42
  const { future, reject, resolve } = Future.withResolvers();
@@ -88,22 +44,10 @@ export class Pool {
88
44
  return future;
89
45
  }
90
46
  /**
91
- * Provides a safe execution context for performing low-level operations
92
- * directly on a single, dedicated `Connection` instance.
93
- *
94
- * Automatically borrows a free socket from the pool, forwards it to the provided callback function,
95
- * and guarantees that the connection is released back to the pool once the execution completes,
96
- * even if errors or unexpected exceptions are thrown. Prevents connection descriptor leaks.
97
- *
98
- * @template T The return type of the provided callback function.
99
- * @param {(conn: Connection) => Promise<T>} fn A callback function that operates on the allocated Connection.
100
- * @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.
101
48
  *
102
49
  * @example
103
- * // Executing low-level engine commands on a single, pinned connection
104
- * const status = await pool.withAcquire(async (conn) => {
105
- * return await conn.query`SELECT pg_is_in_recovery()`;
106
- * });
50
+ * const status = await pool.withAcquire(conn => conn.query`SELECT pg_is_in_recovery()`)
107
51
  */
108
52
  withAcquire(fn) {
109
53
  return Begin()
@@ -111,26 +55,12 @@ export class Pool {
111
55
  .andThen(conn => Future.of(fn(conn))
112
56
  .finally(() => this.release(conn)));
113
57
  }
114
- /**
115
- * Releases the connection back to the pool.
116
- *
117
- * If there are pending `acquire()` calls, the connection is passed directly to the next waiter.
118
- * Otherwise, it's added to the available connections queue.
119
- *
120
- * @param conn - The connection to release.
121
- *
122
- * @example
123
- * ```ts
124
- * const conn = await pool.acquire()
125
- * try {
126
- * await conn.query`SELECT 1`
127
- * } finally {
128
- * pool.release(conn)
129
- * }
130
- * ```
131
- */
58
+ /** Returns `conn` to the pool, or hands it directly to the next waiting `acquire()`. */
132
59
  release(conn) {
133
- this._checkClosed();
60
+ if (!this._isOpened) {
61
+ void conn.close();
62
+ throw ErrPoolClosed;
63
+ }
134
64
  if (!conn.isOpened) {
135
65
  this._total--;
136
66
  return;
@@ -142,55 +72,28 @@ export class Pool {
142
72
  this._available.push(conn);
143
73
  }
144
74
  /**
145
- * Starts a managed transaction.
146
- *
147
- * Automatically acquires a connection and handles `BEGIN`, `COMMIT`, and `ROLLBACK`.
148
- * If the callback throws an error, the transaction is rolled back.
149
- *
150
- * @param txCallback - Async function that receives a `Transaction` instance.
151
- * @returns The value returned from the callback.
75
+ * Runs `txCallback` inside a transaction on a borrowed connection, releasing it afterward.
152
76
  *
153
77
  * @example
154
- * ```ts
155
- * const result = await pool.begin(async tx => {
156
- * await tx.query`INSERT INTO accounts (id, balance) VALUES (1, 100)`
78
+ * await pool.begin(async tx => {
157
79
  * await tx.query`UPDATE accounts SET balance = balance - 10 WHERE id = 1`
158
- * return { success: true }
159
80
  * })
160
- * ```
161
81
  */
162
82
  begin(txCallback) {
163
- this._checkClosed();
164
83
  return Begin()
165
84
  .andThen(() => this.acquire())
166
85
  .andThen(conn => conn.begin(txCallback)
167
86
  .finally(() => this.release(conn)));
168
87
  }
169
88
  /**
170
- * Executes a one-off query using pipeline.
171
- *
172
- * Automatically acquires and releases a connection from the pool.
173
- * For optimal performance, multiple queries can be pipelined through the same connection.
174
- *
175
- * @param templates - Tagged template string with SQL.
176
- * @param args - Query parameters.
177
- * @returns Array of rows with proper typing.
89
+ * Runs a one-off query on a borrowed connection.
178
90
  *
179
91
  * @example
180
- * ```ts
181
- * // Simple query
182
- * const users = await pool.query`SELECT * FROM users`
183
- *
184
- * // With parameters
185
- * const user = await pool.query`SELECT * FROM users WHERE id = ${1}`
186
- *
187
- * // With typed result
188
- * type User = { id: number, name: string }
189
- * const users = await pool.query<User>`SELECT * FROM users`
190
- * ```
92
+ * const users = await pool.query<User>`SELECT * FROM users WHERE id = ${1}`
191
93
  */
192
94
  query(templates, ...args) {
193
- this._checkClosed();
95
+ if (!this._isOpened)
96
+ return Future.reject(ErrPoolClosed);
194
97
  while (this._available.hasMore) {
195
98
  const conn = this._available.shift;
196
99
  if (!conn.isOpened) {
@@ -206,33 +109,34 @@ export class Pool {
206
109
  return conn.query(templates, ...args);
207
110
  });
208
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
+ }
209
131
  /**
210
- * Executes an SQL query in streaming mode.
211
- *
212
- * Data is streamed directly from the PostgreSQL binary network buffer into the Web Streams API
213
- * (`ReadableStream`), bypassing any intermediate array allocation or row accumulation in the JS heap.
214
- * This pattern provides a true Zero-Memory Footprint and is ideal for exporting massive tables
215
- * or piping database payloads directly into HTTP responses (e.g., via `Bun.serve` or fetch `Response`).
216
- *
217
- * @template T The expected shape of a single row interface.
218
- * @param {TemplateStringsArray} templates The SQL string parts from the tagged template literal.
219
- * @param {...any} args The parameterized query arguments.
220
- * @returns {ReadableStream<T>} Synchronously returns a native Web ReadableStream instance.
132
+ * Streams query results as a `ReadableStream`, without buffering rows in memory.
221
133
  *
222
134
  * @example
223
- * // Streaming a giant table directly to an HTTP response (Bun.serve)
224
- * const userStream = pool.stream<User>`SELECT id, name FROM users`;
225
- * return new Response(userStream, { headers: { 'Content-Type': 'application/json' } });
226
- *
227
- * @example
228
- * // Asynchronously iterating over rows as they arrive from the wire socket
229
- * const stream = pool.stream<User>`SELECT * FROM orders WHERE status = ${'processed'}`;
230
- * for await (const row of stream) {
231
- * console.log(row.id, row.amount); // Row object is eligible for GC immediately after iteration
232
- * }
135
+ * for await (const row of pool.stream<User>`SELECT * FROM orders`) { ... }
233
136
  */
234
137
  stream(templates, ...args) {
235
- this._checkClosed();
138
+ if (!this._isOpened)
139
+ throw ErrPoolClosed;
236
140
  while (this._available.hasMore) {
237
141
  const conn = this._available.shift;
238
142
  if (!conn.isOpened) {
@@ -251,90 +155,54 @@ export class Pool {
251
155
  this.acquire()
252
156
  .tap(conn => {
253
157
  this.release(conn);
254
- conn['_streamWithController'](templates, args, controller);
158
+ conn['_performStream'](templates, args, controller);
255
159
  })
256
160
  .catch(err => {
257
161
  controller.error(err);
258
162
  });
259
163
  return stream;
260
164
  }
261
- /**
262
- * Sends an asynchronous notification to a channel via `pg_notify`.
263
- *
264
- * @param channelName - The channel identifier
265
- * @param payload - Optional string data (max 8000 bytes)
266
- *
267
- * @example
268
- * ```ts
269
- * await pool.notify('events', 'hello')
270
- * ```
271
- */
165
+ /** Sends a `pg_notify` message on `channelName` (payload ≤ 8000 bytes). */
272
166
  notify(channelName, payload = "") {
273
- return this.query `select pg_notify(${channelName}, ${payload})`;
167
+ return this.acquire()
168
+ .andThen(conn => conn.notify(channelName, payload)
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()
301
181
  .andThen(conn => conn.listen(channel, callback)
302
- .map(() => async () => {
303
- await conn.unlisten(channel, callback);
304
- this.release(conn);
305
- }));
182
+ .map(() => () => conn.unlisten(channel, callback)
183
+ .tap(() => this.release(conn))));
306
184
  }
307
- /**
308
- * Number of available (idle) connections in the pool.
309
- */
185
+ /** Number of idle connections. */
310
186
  get size() {
311
187
  return this._available.size;
312
188
  }
313
- /**
314
- * Total number of connections currently managed by the pool
315
- * (available + in use).
316
- */
189
+ /** Total connections managed (idle + in use). */
317
190
  get total() {
318
191
  return this._total;
319
192
  }
320
193
  /**
321
- * Shuts down the pool and closes all active connections.
322
- *
323
- * All pending `acquire()` calls will be rejected with an error.
324
- * The pool cannot be used after calling `close()`.
325
- *
326
- * @example
327
- * ```ts
328
- * pool.close()
329
- * ```
194
+ * Closes idle connections and rejects pending `acquire()` calls. Not usable afterward.
330
195
  */
331
196
  close() {
197
+ this._isOpened = false;
198
+ const futures = [];
332
199
  while (this._available.hasMore) {
333
- this._available.shift.close();
200
+ futures.push(this._available.shift.close());
334
201
  }
335
202
  while (this._waiting.hasMore) {
336
203
  this._waiting.shift.reject(ErrPoolClosed);
337
204
  }
338
205
  this._total = 0;
206
+ return Future.all(futures).map(() => { });
339
207
  }
340
208
  }
@@ -1,7 +1,6 @@
1
1
  import { AuthenticationCode, ResponseType, TransactionStatus } from "./constants";
2
- import { ColumnDescription } from "../types";
2
+ import { ChannelName, ColumnDescription } from "../types";
3
3
  import { PostgresError } from "../error";
4
- import { ChannelName } from "../connection";
5
4
  export declare class ConnectionResponseBuffer {
6
5
  private buffer;
7
6
  private caret;
@@ -31,7 +30,6 @@ export declare class ConnectionResponseBuffer {
31
30
  }
32
31
  export declare class ConnectionResponseReader {
33
32
  private buffer;
34
- private currentPacketLength;
35
33
  private constructor();
36
34
  static from(buffer: Buffer): ConnectionResponseReader;
37
35
  readType(): {
@@ -53,7 +51,7 @@ export declare class ConnectionResponseReader {
53
51
  readSaslMechanisms(): string[];
54
52
  readSaslMessage(length: number): string;
55
53
  readRowDescription(): ColumnDescription[];
56
- readDataRow(descriptions: ColumnDescription[], int8toBigint?: boolean): Record<string, any>;
54
+ readDataRow(descriptions: ColumnDescription[], int8toBigint: boolean): Record<string, any>;
57
55
  readNotificationResponse(): {
58
56
  name: ChannelName;
59
57
  payload: string;
@@ -66,5 +64,6 @@ export declare class ConnectionResponseReader {
66
64
  readBindComplete(): void;
67
65
  readParameterDescription(): void;
68
66
  readNoData(): void;
67
+ skip(count: number): void;
69
68
  }
70
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,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAM3C,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;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,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,GAAE,OAAe,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IA0GlG,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"}
@@ -131,7 +131,6 @@ export class ConnectionResponseBuffer {
131
131
  export class ConnectionResponseReader {
132
132
  constructor(buffer) {
133
133
  this.buffer = buffer;
134
- this.currentPacketLength = 0;
135
134
  }
136
135
  static from(buffer) {
137
136
  return new ConnectionResponseReader(ConnectionResponseBuffer.from(buffer));
@@ -236,7 +235,7 @@ export class ConnectionResponseReader {
236
235
  }
237
236
  return columns;
238
237
  }
239
- readDataRow(descriptions, int8toBigint = false) {
238
+ readDataRow(descriptions, int8toBigint) {
240
239
  const fieldsCount = this.buffer.readInt16();
241
240
  const row = {};
242
241
  for (let i = 0; i < fieldsCount; i++) {
@@ -377,4 +376,7 @@ export class ConnectionResponseReader {
377
376
  }
378
377
  }
379
378
  readNoData() { }
379
+ skip(count) {
380
+ this.buffer.skipBytes(count);
381
+ }
380
382
  }
@@ -2,15 +2,6 @@ import { Socket } from "node:net";
2
2
  import { ConnectionRequestWriter } from "./connection-request-writer";
3
3
  import { PostgresError } from "../error";
4
4
  import { Future } from "fluent-future";
5
- export type AuthorizationParams = {
6
- host: string;
7
- port: number;
8
- user: string;
9
- database: string;
10
- password?: string;
11
- };
12
- export declare const ErrNonceMismatch: PostgresError;
13
- export declare const ErrPasswordRequired: PostgresError;
14
- export declare const ErrSocketFailedDuringAuth: PostgresError;
5
+ import { AuthorizationParams } from "../types";
15
6
  export declare const createAuthorizedSocket: (writer: ConnectionRequestWriter, params: AuthorizationParams) => Future<Socket, PostgresError>;
16
7
  //# sourceMappingURL=socket-authorization.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"socket-authorization.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-authorization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,MAAM,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAKrE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAGtC,MAAM,MAAM,mBAAmB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAGD,eAAO,MAAM,gBAAgB,eAAmF,CAAA;AAChH,eAAO,MAAM,mBAAmB,eAAqE,CAAA;AACrG,eAAO,MAAM,yBAAyB,eAAiD,CAAA;AAIvF,eAAO,MAAM,sBAAsB,GAAI,QAAQ,uBAAuB,EAAE,QAAQ,mBAAmB,kCAoHlG,CAAA"}
1
+ {"version":3,"file":"socket-authorization.d.ts","sourceRoot":"","sources":["../../src/protocol/socket-authorization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,MAAM,EAAE,MAAM,UAAU,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAA;AAKrE,OAAO,EAAoE,aAAa,EAAE,MAAM,UAAU,CAAA;AAC1G,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAG9C,eAAO,MAAM,sBAAsB,GAAI,QAAQ,uBAAuB,EAAE,QAAQ,mBAAmB,kCAoHlG,CAAA"}
@@ -3,11 +3,8 @@ import { calculateScramAuth, generateNonce } from "../security/sasl";
3
3
  import { SocketConnector } from "./socket-connector";
4
4
  import { AuthenticationCodes, ResponseTypes } from "./constants";
5
5
  import { encryptMd5 } from "../security/md5";
6
- import { PostgresError } from "../error";
6
+ import { ErrNonceMismatch, ErrPasswordRequired, ErrSocketFailedDuringAuth } from "../error";
7
7
  import { Future } from "fluent-future";
8
- export const ErrNonceMismatch = new PostgresError("Protocol violation: server nonce doesn't match client nonce");
9
- export const ErrPasswordRequired = new PostgresError('The authorization method requires a password.');
10
- export const ErrSocketFailedDuringAuth = new PostgresError("Socket failed during auth");
11
8
  export const createAuthorizedSocket = (writer, params) => {
12
9
  const { future, reject, resolve } = Future.withResolvers();
13
10
  const nonce = generateNonce();
@@ -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;IAOrC,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"}
@@ -1,13 +1,24 @@
1
1
  import { ConnectionResponseReader } from '../protocol/connection-response-reader';
2
2
  export class SocketConnector {
3
- constructor(_socket, _onData, _onError) {
3
+ constructor(_socket, onData, onError) {
4
4
  this._socket = _socket;
5
- this._onData = _onData;
6
- this._onError = _onError;
7
5
  this.residualBuffer = null;
8
- this._isDestroyed = false;
9
- _socket.setKeepAlive(true, 10000);
10
- _socket.on('data', buffer => {
6
+ this._destroyed = false;
7
+ this._onError = (err) => {
8
+ if (this._destroyed)
9
+ return;
10
+ this._destroyed = true;
11
+ onError(err);
12
+ this.destroy();
13
+ };
14
+ this._onClose = () => {
15
+ if (this._destroyed)
16
+ return;
17
+ this._destroyed = true;
18
+ onError(new Error("Socket closed"));
19
+ this.destroy();
20
+ };
21
+ this._onData = buffer => {
11
22
  const currentBuffer = this.residualBuffer
12
23
  ? Buffer.concat([this.residualBuffer, buffer])
13
24
  : buffer;
@@ -19,28 +30,24 @@ export class SocketConnector {
19
30
  return;
20
31
  }
21
32
  const { type, length } = reader.readType();
22
- this._onData(type, length, reader);
33
+ onData(type, length, reader);
23
34
  }
24
- });
25
- _socket.on('error', error => {
26
- this._onError(error);
27
- this._socket.destroy();
28
- this._isDestroyed = true;
29
- });
35
+ };
36
+ _socket.setKeepAlive(true, 10000);
37
+ _socket.on('data', this._onData);
38
+ _socket.on('error', this._onError);
39
+ _socket.on('close', this._onClose);
30
40
  }
31
41
  write(writer) {
32
- if (this._isDestroyed)
33
- throw new Error("SocketConnector is destroyed");
34
42
  this._socket.write(writer.asBuffer());
35
43
  }
36
44
  unwrapSocket() {
37
45
  this._socket.off('error', this._onError);
38
46
  this._socket.off("data", this._onData);
47
+ this._socket.off('close', this._onClose);
39
48
  return this._socket;
40
49
  }
41
50
  destroy() {
42
- this._isDestroyed = true;
43
51
  this._socket.destroy();
44
52
  }
45
- get isDestroyed() { return this._isDestroyed; }
46
53
  }
package/dist/query.d.ts CHANGED
@@ -1,53 +1,44 @@
1
1
  import { Future } from "fluent-future";
2
- import { QueryMeta, QueryText, StatementName } from "./connection";
3
- import { ColumnDescription, ValueOF } from "./types";
2
+ import { ColumnDescription, QueryText, Row, StatementName } from "./types";
4
3
  import { PostgresError } from "./error";
5
- export declare const QueryState: {
6
- readonly Parsing: 0;
7
- readonly Executing: 1;
8
- readonly Completed: 2;
9
- readonly Failed: 3;
10
- };
11
- export type State = ValueOF<typeof QueryState>;
12
4
  export declare abstract class Query {
13
5
  statement: StatementName;
6
+ text: QueryText;
7
+ args: (string | null)[];
14
8
  protected _timer?: NodeJS.Timeout;
15
- constructor(statement: StatementName);
16
- startTimeout(timeout: number): void;
9
+ constructor(statement: StatementName, text: QueryText, args: (string | null)[], timeout: number);
17
10
  abstract reject(cause: PostgresError): void;
18
11
  abstract resolve(...args: any[]): void;
12
+ abstract push(...args: any[]): void;
19
13
  }
20
- export declare class SimpleQuery<T> extends Query {
21
- text: QueryText;
22
- args: (string | null)[];
23
- columns: ColumnDescription[];
14
+ export declare class CollectQuery<T extends Row> extends Query {
15
+ columns: ColumnDescription[] | null;
24
16
  future: Future<T[], PostgresError>;
25
17
  private _resolve;
26
18
  private _reject;
27
19
  private _rows;
28
- constructor(statement: StatementName, text: QueryText, args: (string | null)[], columns: ColumnDescription[]);
20
+ constructor(statement: StatementName, text: QueryText, args: (string | null)[], columns: ColumnDescription[] | null, timeout: number);
29
21
  push(value: T): void;
30
22
  reject(cause: PostgresError): void;
31
23
  resolve(): void;
32
24
  }
33
- export declare class ParseQuery extends Query {
34
- text: QueryText;
35
- future: Future<QueryMeta, PostgresError>;
25
+ export declare class ExecuteQuery extends Query {
26
+ columns: ColumnDescription[] | null;
27
+ future: Future<void, PostgresError>;
36
28
  private _resolve;
37
29
  private _reject;
38
- constructor(statement: StatementName, text: QueryText);
30
+ constructor(statement: StatementName, text: QueryText, args: (string | null)[], columns: ColumnDescription[] | null, timeout: number);
39
31
  reject(cause: PostgresError): void;
40
- resolve(meta: QueryMeta): void;
32
+ resolve(): void;
33
+ push(): void;
41
34
  }
42
35
  export declare class StreamQuery<T> extends Query {
43
- text: QueryText;
44
- args: (string | null)[];
45
36
  controller: ReadableStreamDefaultController<T>;
46
- columns: ColumnDescription[];
47
- constructor(statement: StatementName, text: QueryText, args: (string | null)[], controller: ReadableStreamDefaultController<T>, columns: ColumnDescription[]);
37
+ columns: ColumnDescription[] | null;
38
+ constructor(statement: StatementName, text: QueryText, args: (string | null)[], controller: ReadableStreamDefaultController<T>, columns: ColumnDescription[] | null, timeout: number);
48
39
  push(value: T): void;
49
40
  reject(cause: PostgresError): void;
50
41
  resolve(): void;
51
42
  }
52
- export type PostgresQuery = SimpleQuery<any> | ParseQuery | StreamQuery<any>;
43
+ export type PostgresQuery = CollectQuery<any> | StreamQuery<any> | ExecuteQuery;
53
44
  //# sourceMappingURL=query.d.ts.map