@m2k-5f/pgtx 2.0.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,10 +25,12 @@ npm install @m2k-5f/pgtx
25
25
 
26
26
  | Tool | RPS | Avg Time | Connections |
27
27
  |------|-----|----------|-------------|
28
- | **Pgtx** | **1584** | **0.631ms** | **20 (used only one)** |
29
- | Native `pg` | 179 | 5.585ms | 20 |
28
+ | **Pgtx** | **1584** | **0.631ms** | **20 pool connections (pipeline multiplexing)** |
29
+ | Native `pg` | 179 | 5.585ms | 20 pool connections |
30
30
 
31
- **9x faster**
31
+ **Up to 9x faster in concurrent pipeline workloads**
32
+
33
+ **Pgtx achieves higher throughput by multiplexing concurrent queries over PostgreSQL connections using pipeline execution.**
32
34
 
33
35
 
34
36
  > Benchmark source available in the [repository](https://github.com/M2K-5F/pgtx).
@@ -43,7 +45,7 @@ npm install @m2k-5f/pgtx
43
45
  - **Bulk inserts** — Auto-extract columns from objects
44
46
  - **Dynamic updates** — Generate SET clauses from objects
45
47
  - **Recursive fragments** — Compose SQL like Lego
46
- - **Prepared statements** — Pre-parse any SQL with parameters
48
+ - **Prepared statements** — Automatic prepared statement caching
47
49
  - **Connection pool** — Auto-management connections with support for pipeline queries via the pool itself.
48
50
  - **Zero dependencies** — Lightweight and blazing
49
51
 
@@ -111,6 +113,36 @@ await pool.begin(async (tx) => {
111
113
  })
112
114
  ```
113
115
 
116
+
117
+ ### Async Notifications (LISTEN / NOTIFY)
118
+
119
+ Pgtx natively handles PostgreSQL `LISTEN/NOTIFY` protocol messages asynchronously without interrupting multiplexed query pipeline.
120
+
121
+ ```typescript
122
+ // 1. Sending a notification
123
+ await pool.notify('user_events', JSON.stringify({ id: 42, action: 'signup' }))
124
+
125
+ // 2. Receiving notifications (Requires a dedicated connection from the pool)
126
+ const conn = await pool.acquire()
127
+
128
+ const onEvent = (payload: string) => {
129
+ console.log(`Received payload: ${payload}`)
130
+ }
131
+
132
+ // Multiplexes multiple callbacks onto a single LISTEN command seamlessly
133
+ await conn.listen('user_events', onEvent)
134
+ await conn.listen('user_events', (data) => logToFile(data))
135
+
136
+ // Clean up callbacks (Sends UNLISTEN only when the channel has zero callbacks left)
137
+ await conn.unlisten('user_events', onEvent)
138
+
139
+ // Keep the connection active as long as you need notifications!
140
+ // Do NOT release it back to the pool prematurely.
141
+ ```
142
+
143
+ > ⚠️ **Architecture Note:** While `notify` is atomic and can be triggered directly from the `Pool` on any random socket, `listen` and `unlisten` are stateful commands tied to a specific PostgreSQL backend process. Therefore, subscription methods are **strictly available only on explicit `Connection` instances** fetched via `pool.acquire()`.
144
+
145
+
114
146
  ### Bulk Inserts
115
147
 
116
148
  ```typescript
@@ -250,6 +282,9 @@ class Connection {
250
282
 
251
283
  query<T>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>
252
284
  begin<T>(callback: (tx: Transaction) => Promise<T>): Promise<T>
285
+ notify(channelName: string, payload?: string): Promise<[]>
286
+ listen(channelName: string, callback: (payload: string) => void): Promise<[]>
287
+ unlisten(channelName: string, callback: (payload: string) => void): Promise<[]>
253
288
 
254
289
  get isAlive(): boolean
255
290
  close(): void
@@ -273,6 +308,7 @@ class Pool {
273
308
 
274
309
  query<T>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>
275
310
  begin<T>(callback: (tx: Transaction) => Promise<T>): Promise<T>
311
+ notify(channelName: string, payload?: string): Promise<[]>
276
312
  acquire(): Promise<Connection>
277
313
  release(conn: Connection): void
278
314
  close(): Promise<void>
@@ -1,6 +1,5 @@
1
- import { ColumnDescription } from "./types";
1
+ import { Branded, ColumnDescription } from "./types";
2
2
  import { Transaction } from "./transaction";
3
- import { Queue } from "./queue";
4
3
  type LogLevel = "none" | "error" | "notice" | "query";
5
4
  export type ConnectionParams = {
6
5
  user: string;
@@ -10,17 +9,26 @@ export type ConnectionParams = {
10
9
  database: string;
11
10
  logLevel?: LogLevel;
12
11
  };
13
- export type ConnectionQueryContext = {
14
- columns: ColumnDescription[];
12
+ export type ExecuteQueueUnit = {
15
13
  rows: (string | null)[][];
16
14
  resolve: (value: any) => void;
17
15
  reject: (err: Error) => void;
18
- text: string;
19
- statementName: string | null;
16
+ statementName: StatementName;
20
17
  };
21
- export declare class ConnectionQueryQueue extends Queue<ConnectionQueryContext> {
22
- rejectAllNext(error: Error): void;
23
- }
18
+ export type ParsingQueueUnit = {
19
+ resolve: (statementName: StatementName) => void;
20
+ reject: (error: Error) => void;
21
+ text: QueryText;
22
+ statementName: StatementName;
23
+ };
24
+ export type DescribeQueueUnit = {
25
+ resolve: (value: ColumnDescription[]) => void;
26
+ reject: (error: Error) => void;
27
+ statementName: StatementName;
28
+ };
29
+ export type StatementName = Branded<string, 'StatementName'>;
30
+ export type QueryText = Branded<string, 'QueryText'>;
31
+ export type ChannelName = Branded<string, "ChannelName">;
24
32
  /**
25
33
  * Represents a single dedicated connection to the PostgreSQL database.
26
34
  *
@@ -52,14 +60,18 @@ export declare class ConnectionQueryQueue extends Queue<ConnectionQueryContext>
52
60
  * ```
53
61
  */
54
62
  export declare class Connection {
63
+ private readonly params;
55
64
  private _isFlushing;
56
- private _isAlive;
65
+ private _isOpened;
66
+ private _isReconnecting;
57
67
  private _socket;
58
68
  private _writer;
59
- private _queue;
60
- private _statementDescriptions;
61
- private _statements;
62
- private _parsePending;
69
+ private _pipelinesQueue;
70
+ private _described;
71
+ private _describingPending;
72
+ private _parsed;
73
+ private _parsingPending;
74
+ private _listeningCallbacks;
63
75
  private _stmtCounter;
64
76
  private _logLevel;
65
77
  private _nextStatement;
@@ -69,7 +81,7 @@ export declare class Connection {
69
81
  *
70
82
  * @param params - Connection parameters
71
83
  * @returns A new Connection instance
72
- * @throws {Error} If authentication fails or connection cannot be established
84
+ * @throws {PostgresError} If authentication fails or connection cannot be established
73
85
  *
74
86
  * @example
75
87
  * ```ts
@@ -107,7 +119,7 @@ export declare class Connection {
107
119
  * const users = await conn.query<User>`SELECT * FROM users`
108
120
  * ```
109
121
  */
110
- query<T extends Record<string, unknown>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
122
+ query<T extends Record<string, any>>(templates: TemplateStringsArray, ...params: any[]): Promise<T[]>;
111
123
  /**
112
124
  * Starts a managed transaction on this connection.
113
125
  *
@@ -130,15 +142,57 @@ export declare class Connection {
130
142
  * ```
131
143
  */
132
144
  begin<T>(txCallback: (transaction: Transaction) => Promise<T>): Promise<T>;
145
+ /**
146
+ * Sends an asynchronous notification to a channel via `pg_notify`.
147
+ *
148
+ * @param channelName - The channel identifier
149
+ * @param payload - Optional string data (max 8000 bytes)
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * await conn.notify('events', 'hello')
154
+ * ```
155
+ */
156
+ notify(channelName: string, payload?: string): Promise<[]>;
157
+ /**
158
+ * Subscribes a callback to a channel. Sends `LISTEN` on the first subscription.
159
+ *
160
+ * @param channelName - The channel identifier
161
+ * @param callback - Function invoked when a notification arrives
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * await conn.listen('events', data => console.log(data))
166
+ * ```
167
+ */
168
+ listen(channelName: string, callback: (payload: string) => void): Promise<Record<string, any>[]>;
169
+ /**
170
+ * Unsubscribes a callback. Sends `UNLISTEN` if no callbacks remain for the channel.
171
+ *
172
+ * @param channelName - The channel identifier
173
+ * @param callback - The registered callback to remove
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * await conn.unlisten('events', callback)
178
+ * ```
179
+ */
180
+ unlisten(channelName: string, callback: (payload: string) => void): Promise<void> | Promise<Record<string, any>[]>;
181
+ private _createQuery;
182
+ private _writeQuery;
133
183
  private _registerFlush;
134
184
  private _flush;
185
+ private _reconnect;
186
+ private _restoreSubscriptions;
187
+ private _getCurrentQuery;
188
+ private _rejectPipeline;
135
189
  private _handlePacket;
136
- private _handleError;
137
190
  /**
138
191
  * Checks if the connection is still alive and usable.
139
192
  * Returns `false` if the socket is destroyed or connection is dead.
140
193
  */
141
- get isAlive(): boolean;
194
+ get isOpened(): boolean;
195
+ private _destroyConnection;
142
196
  /**
143
197
  * Closes the connection immediately.
144
198
  * All pending queries will be rejected with an error.
@@ -1 +1 @@
1
- {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAE/B,KAAK,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAA;AAErD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACtB,CAAA;AAKD,MAAM,MAAM,sBAAsB,GAAG;IACjC,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACzB,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAA;IAC7B,MAAM,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAA;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;CAC/B,CAAA;AAGD,qBAAa,oBAAqB,SAAQ,KAAK,CAAC,sBAAsB,CAAC;IAEnE,aAAa,CAAC,KAAK,EAAE,KAAK;CAM7B;AAGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAO;IACvB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,sBAAsB,CAAyC;IACvE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,aAAa,CAA4B;IACjD,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,SAAS,CAAU;IAG3B,OAAO,CAAC,cAAc;IAItB,OAAO;IAcP;;;;;;;;;;;;;;;;;OAiBG;WACU,GAAG,CAAC,MAAM,EAAE,gBAAgB;IAQzC;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IA0DvG;;;;;;;;;;;;;;;;;;;;OAoBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBhF,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,MAAM;IAkBd,OAAO,CAAC,aAAa;IAoFrB,OAAO,CAAC,YAAY;IAQpB;;;OAGG;IACH,IAAI,OAAO,YAEV;IAGD;;;;;;;;;OASG;IACH,KAAK;CAIR"}
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Connection = exports.ConnectionQueryQueue = void 0;
3
+ exports.Connection = void 0;
4
4
  const process_1 = require("process");
5
5
  const connection_request_writer_1 = require("./protocol/connection-request-writer");
6
6
  const socket_authorization_1 = require("./protocol/socket-authorization");
@@ -10,16 +10,9 @@ const template_compiler_1 = require("./utils/template-compiler");
10
10
  const transaction_1 = require("./transaction");
11
11
  const socket_connector_1 = require("./protocol/socket-connector");
12
12
  const queue_1 = require("./queue");
13
+ const query_1 = require("./query");
14
+ const _1 = require(".");
13
15
  const ErrConnectionDead = new Error("Connection is Dead");
14
- class ConnectionQueryQueue extends queue_1.Queue {
15
- rejectAllNext(error) {
16
- while (!this.isFree) {
17
- this.get().reject(error);
18
- this.next();
19
- }
20
- }
21
- }
22
- exports.ConnectionQueryQueue = ConnectionQueryQueue;
23
16
  /**
24
17
  * Represents a single dedicated connection to the PostgreSQL database.
25
18
  *
@@ -54,16 +47,23 @@ class Connection {
54
47
  _nextStatement() {
55
48
  return `s-${this._stmtCounter++}`;
56
49
  }
57
- constructor(socket, writer, logLevel) {
50
+ constructor(socket, writer, logLevel, params) {
58
51
  this._isFlushing = false;
59
- this._isAlive = true;
60
- this._queue = new ConnectionQueryQueue();
61
- this._statementDescriptions = new Map();
62
- this._statements = new Map();
63
- this._parsePending = new Map();
52
+ this._isOpened = true;
53
+ this._isReconnecting = false;
54
+ this._pipelinesQueue = new queue_1.Queue();
55
+ this._described = new Map();
56
+ this._describingPending = new Set();
57
+ this._parsed = new Map();
58
+ this._parsingPending = new Map();
59
+ this._listeningCallbacks = new Map();
64
60
  this._stmtCounter = 0;
61
+ this.params = params;
65
62
  this._logLevel = logLevel;
66
- this._socket = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), error => this._handleError(error));
63
+ this._socket = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), (err) => {
64
+ this._isReconnecting = true;
65
+ this._rejectPipeline(ErrConnectionDead);
66
+ });
67
67
  this._writer = writer;
68
68
  }
69
69
  /**
@@ -71,7 +71,7 @@ class Connection {
71
71
  *
72
72
  * @param params - Connection parameters
73
73
  * @returns A new Connection instance
74
- * @throws {Error} If authentication fails or connection cannot be established
74
+ * @throws {PostgresError} If authentication fails or connection cannot be established
75
75
  *
76
76
  * @example
77
77
  * ```ts
@@ -87,7 +87,7 @@ class Connection {
87
87
  static async new(params) {
88
88
  const writer = connection_request_writer_1.ConnectionRequestWriter.new();
89
89
  const socket = await (0, socket_authorization_1.createAuthorizedSocket)(writer, params);
90
- return new Connection(socket, writer, params.logLevel || 'error');
90
+ return new Connection(socket, writer, params.logLevel || 'error', params);
91
91
  }
92
92
  /**
93
93
  * Executes a query using tagged template literals.
@@ -113,47 +113,17 @@ class Connection {
113
113
  * const users = await conn.query<User>`SELECT * FROM users`
114
114
  * ```
115
115
  */
116
- query(templates, ...args) {
117
- if (!this._isAlive)
116
+ query(templates, ...params) {
117
+ this._registerFlush();
118
+ if (!this._isOpened)
118
119
  throw ErrConnectionDead;
119
- const query = (0, template_compiler_1.compileSqlTemplate)({ templates, args });
120
+ const { text, args } = (0, template_compiler_1.compileSqlTemplate)({ templates, args: params });
120
121
  if (this._logLevel === 'query') {
121
- console.log(`\nQUERY: ${query.text}\n${query.args.length !== 0 ? `ARGUMENTS: [${query.args}]\n` : ""}`);
122
+ console.log(`\nQUERY: ${text}\n${args.length !== 0 ? `ARGUMENTS: [${args}]\n` : ""}`);
122
123
  }
123
- return new Promise((resolve, reject) => {
124
- this._registerFlush();
125
- if (query.args.length === 0) {
126
- this._writer.writeQuery(query.text);
127
- this._queue.push({
128
- resolve, reject, columns: [], rows: [], statementName: null, text: query.text
129
- });
130
- }
131
- else {
132
- let rejectParsed = reject;
133
- let statementName = '';
134
- if (!this._statements.has(query.text) && !this._parsePending.has(query.text)) {
135
- statementName = this._nextStatement();
136
- this._parsePending.set(query.text, statementName);
137
- rejectParsed = (error) => {
138
- this._parsePending.delete(query.text);
139
- reject(error);
140
- };
141
- this._writer
142
- .writeParse(statementName, query.text);
143
- console.log('parsed once', statementName, query.text);
144
- }
145
- statementName = this._statements.get(query.text) || this._parsePending.get(query.text);
146
- if (!this._statementDescriptions.has(statementName)) {
147
- this._writer.writeDescribe(statementName);
148
- }
149
- this._queue.push({
150
- resolve, reject: rejectParsed, columns: [], rows: [], statementName: statementName, text: query.text
151
- });
152
- this._writer
153
- .writeBind("", statementName, query.args)
154
- .writeExecute("");
155
- }
156
- });
124
+ const query = this._createQuery(text, args);
125
+ this._writeQuery(query);
126
+ return query.promise;
157
127
  }
158
128
  /**
159
129
  * Starts a managed transaction on this connection.
@@ -177,7 +147,7 @@ class Connection {
177
147
  * ```
178
148
  */
179
149
  async begin(txCallback) {
180
- if (!this._isAlive)
150
+ if (!this._isOpened)
181
151
  throw ErrConnectionDead;
182
152
  const tx = new transaction_1.Transaction(this);
183
153
  try {
@@ -193,35 +163,192 @@ class Connection {
193
163
  throw err;
194
164
  }
195
165
  }
166
+ /**
167
+ * Sends an asynchronous notification to a channel via `pg_notify`.
168
+ *
169
+ * @param channelName - The channel identifier
170
+ * @param payload - Optional string data (max 8000 bytes)
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * await conn.notify('events', 'hello')
175
+ * ```
176
+ */
177
+ notify(channelName, payload = "") {
178
+ return this.query `select pg_notify(${channelName}, ${payload})`;
179
+ }
180
+ /**
181
+ * Subscribes a callback to a channel. Sends `LISTEN` on the first subscription.
182
+ *
183
+ * @param channelName - The channel identifier
184
+ * @param callback - Function invoked when a notification arrives
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * await conn.listen('events', data => console.log(data))
189
+ * ```
190
+ */
191
+ listen(channelName, callback) {
192
+ if (!this._listeningCallbacks.has(channelName)) {
193
+ this._listeningCallbacks.set(channelName, new Set());
194
+ }
195
+ const callbackSet = this._listeningCallbacks.get(channelName);
196
+ callbackSet.add(callback);
197
+ return this.query `listen ${_1.sql.ident(channelName)};`;
198
+ }
199
+ /**
200
+ * Unsubscribes a callback. Sends `UNLISTEN` if no callbacks remain for the channel.
201
+ *
202
+ * @param channelName - The channel identifier
203
+ * @param callback - The registered callback to remove
204
+ *
205
+ * @example
206
+ * ```ts
207
+ * await conn.unlisten('events', callback)
208
+ * ```
209
+ */
210
+ unlisten(channelName, callback) {
211
+ if (!this._listeningCallbacks.has(channelName)) {
212
+ return Promise.resolve();
213
+ }
214
+ const callbackSet = this._listeningCallbacks.get(channelName);
215
+ callbackSet.delete(callback);
216
+ if (callbackSet.size === 0) {
217
+ this._listeningCallbacks.delete(channelName);
218
+ return this.query `unlisten ${_1.sql.ident(channelName)};`;
219
+ }
220
+ return Promise.resolve();
221
+ }
222
+ _createQuery(text, args) {
223
+ if (this._parsed.has(text)) {
224
+ const statementName = this._parsed.get(text);
225
+ if (this._described.has(statementName)) {
226
+ const columns = this._described.get(statementName);
227
+ return new query_1.Query(text, args, query_1.QueryState.Executing, statementName, columns);
228
+ }
229
+ else {
230
+ if (this._describingPending.has(statementName)) {
231
+ return new query_1.Query(text, args, query_1.QueryState.Executing, statementName);
232
+ }
233
+ else {
234
+ this._describingPending.add(statementName);
235
+ return new query_1.Query(text, args, query_1.QueryState.Describing, statementName);
236
+ }
237
+ }
238
+ }
239
+ else {
240
+ if (this._parsingPending.has(text)) {
241
+ const statementName = this._parsingPending.get(text);
242
+ return new query_1.Query(text, args, query_1.QueryState.Describing, statementName);
243
+ }
244
+ else {
245
+ const statementName = this._nextStatement();
246
+ this._parsingPending.set(text, statementName);
247
+ return new query_1.Query(text, args, query_1.QueryState.Parsing, statementName);
248
+ }
249
+ }
250
+ }
251
+ _writeQuery(query) {
252
+ if (query.state === query_1.QueryState.Parsing) {
253
+ this._writer
254
+ .writeParse(query.statementName, query.text)
255
+ .writeDescribe(query.statementName)
256
+ .writeBind("", query.statementName, query.args)
257
+ .writeExecute("");
258
+ }
259
+ if (query.state === query_1.QueryState.Describing) {
260
+ this._writer
261
+ .writeDescribe(query.statementName)
262
+ .writeBind("", query.statementName, query.args)
263
+ .writeExecute("");
264
+ }
265
+ if (query.state === query_1.QueryState.Executing) {
266
+ this._writer
267
+ .writeBind("", query.statementName, query.args)
268
+ .writeExecute("");
269
+ }
270
+ this._pipelinesQueue.get().push(query);
271
+ }
196
272
  _registerFlush() {
197
273
  if (!this._isFlushing) {
198
274
  this._isFlushing = true;
199
- (0, process_1.nextTick)(() => this._flush());
275
+ this._pipelinesQueue.push(new queue_1.Queue());
276
+ (0, process_1.nextTick)(() => {
277
+ this._flush();
278
+ });
200
279
  }
201
280
  }
202
281
  _flush() {
203
- if (!this._isAlive) {
204
- this._queue.rejectAllNext(ErrConnectionDead);
282
+ if (!this._isOpened) {
283
+ this._rejectPipeline(ErrConnectionDead);
205
284
  return;
206
285
  }
207
- try {
286
+ if (this._isReconnecting) {
287
+ this._reconnect().then(socket => {
288
+ this._isFlushing = false;
289
+ socket.write(this._writer.writeSync());
290
+ this._writer.clear();
291
+ });
292
+ }
293
+ else {
294
+ this._isFlushing = false;
208
295
  this._socket.write(this._writer.writeSync());
209
296
  this._writer.clear();
210
297
  }
211
- catch (err) {
212
- this._isAlive = false;
213
- this._socket.destroy();
214
- this._queue.rejectAllNext(ErrConnectionDead);
298
+ }
299
+ _reconnect() {
300
+ this._parsed.clear();
301
+ this._described.clear();
302
+ this._describingPending.clear();
303
+ this._parsingPending.clear();
304
+ this._writer.clear();
305
+ while (this._pipelinesQueue.hasMore) {
306
+ this._rejectPipeline(ErrConnectionDead);
307
+ this._pipelinesQueue.next();
308
+ }
309
+ this._pipelinesQueue = new queue_1.Queue();
310
+ this._pipelinesQueue.push(new queue_1.Queue());
311
+ return (0, socket_authorization_1.createAuthorizedSocket)(connection_request_writer_1.ConnectionRequestWriter.new(), this.params)
312
+ .then(socket => {
313
+ const connector = new socket_connector_1.SocketConnector(socket, (type, reader) => this._handlePacket(type, reader), (err) => {
314
+ this._isReconnecting = true;
315
+ this._rejectPipeline(ErrConnectionDead);
316
+ });
317
+ this._socket = connector;
318
+ this._restoreSubscriptions();
319
+ this._isReconnecting = false;
320
+ return connector;
321
+ });
322
+ }
323
+ _restoreSubscriptions() {
324
+ if (this._listeningCallbacks.size === 0)
325
+ return Promise.resolve();
326
+ const promises = Array.from(this._listeningCallbacks.keys()).map(channel => {
327
+ return this.query `LISTEN ${_1.sql.ident(channel)};`;
328
+ });
329
+ return Promise.all(promises);
330
+ }
331
+ _getCurrentQuery() {
332
+ return this._pipelinesQueue.get().get();
333
+ }
334
+ _rejectPipeline(error) {
335
+ if (this._pipelinesQueue.isFree)
336
+ return;
337
+ const queue = this._pipelinesQueue.get();
338
+ while (queue.hasMore) {
339
+ queue.get().reject(error);
340
+ queue.next();
215
341
  }
216
342
  }
217
343
  _handlePacket(type, reader) {
218
- const context = this._queue.get();
219
344
  switch (type) {
220
345
  case constants_1.ResponseTypes.ParseComplete:
221
346
  {
222
347
  reader.readParseComplete();
223
- this._statements.set(context.text, context.statementName);
224
- this._parsePending.delete(context.text);
348
+ const query = this._getCurrentQuery();
349
+ this._parsingPending.delete(query.text);
350
+ this._parsed.set(query.text, query.statementName);
351
+ query.state = query_1.QueryState.Describing;
225
352
  }
226
353
  break;
227
354
  case constants_1.ResponseTypes.BindComplete:
@@ -242,50 +369,74 @@ class Connection {
242
369
  case constants_1.ResponseTypes.NoData:
243
370
  {
244
371
  reader.readNoData();
245
- if (context.statementName) {
246
- this._statementDescriptions.set(context.statementName, []);
247
- }
372
+ const query = this._getCurrentQuery();
373
+ this._describingPending.delete(query.statementName);
374
+ this._described.set(query.statementName, []);
375
+ query.state = query_1.QueryState.Executing;
248
376
  }
249
377
  break;
250
378
  case constants_1.ResponseTypes.RowDescription:
251
379
  {
252
- if (context.statementName) {
253
- this._statementDescriptions.set(context.statementName, reader.readRowDescription());
254
- }
255
- else {
256
- context.columns = reader.readRowDescription();
257
- }
380
+ const columns = reader.readRowDescription();
381
+ const query = this._getCurrentQuery();
382
+ this._describingPending.delete(query.statementName);
383
+ this._described.set(query.statementName, columns);
384
+ query.state = query_1.QueryState.Executing;
385
+ query.columns = columns;
258
386
  }
259
387
  break;
260
388
  case constants_1.ResponseTypes.DataRow:
261
389
  {
262
- context.rows.push(reader.readDataRow());
390
+ const query = this._getCurrentQuery();
391
+ query.rows.push(reader.readDataRow());
263
392
  }
264
393
  break;
265
394
  case constants_1.ResponseTypes.ComandComplete:
266
395
  {
267
396
  reader.readCommandComplete();
268
- if (context.statementName) {
269
- context.columns = this._statementDescriptions.get(context.statementName);
397
+ if (this._pipelinesQueue.isFree) {
398
+ this._destroyConnection();
399
+ break;
270
400
  }
271
- context.resolve((0, value_parser_1.parseRowValues)(context.columns, context.rows));
272
- this._queue.next();
401
+ const query = this._getCurrentQuery();
402
+ if (!query) {
403
+ this._destroyConnection();
404
+ break;
405
+ }
406
+ if (!query.columns) {
407
+ query.columns = this._described.get(query.statementName);
408
+ }
409
+ query.state = query_1.QueryState.Completed;
410
+ this._pipelinesQueue.get().next();
411
+ query.resolve((0, value_parser_1.parseRowValues)(query.columns, query.rows));
273
412
  }
274
413
  break;
275
414
  case constants_1.ResponseTypes.ErrorResponse:
276
415
  {
277
- const message = reader.readErrorResponse();
416
+ const error = reader.readErrorResponse();
278
417
  if (this._logLevel === 'error' || this._logLevel === 'notice' || this._logLevel === 'query') {
279
- console.log(`\nError: ${message}\n`);
418
+ console.log(`\nError: ${error}\n`);
419
+ }
420
+ const query = this._getCurrentQuery();
421
+ switch (query.state) {
422
+ case query_1.QueryState.Parsing:
423
+ this._parsingPending.delete(query.text);
424
+ break;
425
+ case query_1.QueryState.Describing:
426
+ this._describingPending.delete(query.statementName);
427
+ break;
280
428
  }
281
- const error = new Error(message);
282
- this._queue.rejectAllNext(error);
429
+ query.state = query_1.QueryState.Failed;
430
+ this._rejectPipeline(error);
283
431
  }
284
432
  break;
285
433
  case constants_1.ResponseTypes.ReadyForQuery:
286
434
  {
287
- this._isFlushing = false;
288
435
  reader.readReadyForQuery();
436
+ const pipeline = this._pipelinesQueue.get();
437
+ if (!pipeline.hasMore) {
438
+ this._pipelinesQueue.next();
439
+ }
289
440
  }
290
441
  break;
291
442
  case constants_1.ResponseTypes.Notice:
@@ -296,20 +447,32 @@ class Connection {
296
447
  }
297
448
  }
298
449
  break;
450
+ case constants_1.ResponseTypes.NotificationResponse:
451
+ {
452
+ const { name, payload } = reader.readNotificationResponse();
453
+ const callbackSet = this._listeningCallbacks.get(name);
454
+ if (!callbackSet)
455
+ break;
456
+ callbackSet.forEach(cb => cb(payload));
457
+ }
458
+ break;
299
459
  default: console.warn('Undeclared response type: ', type);
300
460
  }
301
461
  }
302
- _handleError(error) {
303
- this._isAlive = false;
304
- this._queue.rejectAllNext(error);
305
- this._socket.destroy();
306
- }
307
462
  /**
308
463
  * Checks if the connection is still alive and usable.
309
464
  * Returns `false` if the socket is destroyed or connection is dead.
310
465
  */
311
- get isAlive() {
312
- return this._isAlive && !this._socket.isDestroyed;
466
+ get isOpened() {
467
+ return this._isOpened;
468
+ }
469
+ _destroyConnection() {
470
+ this._isOpened = false;
471
+ this._socket.destroy();
472
+ while (this._pipelinesQueue.hasMore) {
473
+ this._rejectPipeline(ErrConnectionDead);
474
+ this._pipelinesQueue.next();
475
+ }
313
476
  }
314
477
  /**
315
478
  * Closes the connection immediately.
@@ -322,8 +485,7 @@ class Connection {
322
485
  * ```
323
486
  */
324
487
  close() {
325
- this._isAlive = false;
326
- this._socket.destroy();
488
+ this._destroyConnection();
327
489
  }
328
490
  }
329
491
  exports.Connection = Connection;
@@ -0,0 +1,18 @@
1
+ export declare class PostgresError extends Error {
2
+ message: string;
3
+ code: string;
4
+ detail: string;
5
+ severity: string;
6
+ where: string;
7
+ hint: string;
8
+ position: string;
9
+ dataType: string;
10
+ constraint: string;
11
+ constructor(message: string, code: string, detail: string, severity: string, where: string, hint: string, position: string, dataType: string, constraint: string);
12
+ get isParseError(): boolean;
13
+ get isDeadlock(): boolean;
14
+ get isConstraintViolation(): boolean;
15
+ get isTimeout(): boolean;
16
+ get isConnectionFailure(): boolean;
17
+ }
18
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;IAEhB,OAAO,EAAE,MAAM;IACxB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,MAAM;IACd,QAAQ,EAAE,MAAM;IAChB,KAAK,EAAE,MAAM;IACb,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,MAAM;IAChB,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;gBART,OAAO,EAAE,MAAM,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM;IAQ7B,IAAI,YAAY,IAAI,OAAO,CAE1B;IAID,IAAI,UAAU,IAAI,OAAO,CAExB;IAID,IAAI,qBAAqB,IAAI,OAAO,CAEnC;IAGD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAGD,IAAI,mBAAmB,IAAI,OAAO,CAEjC;CACJ"}
package/dist/error.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresError = void 0;
4
+ class PostgresError extends Error {
5
+ constructor(message, code, detail, severity, where, hint, position, dataType, constraint) {
6
+ super(message);
7
+ this.message = message;
8
+ this.code = code;
9
+ this.detail = detail;
10
+ this.severity = severity;
11
+ this.where = where;
12
+ this.hint = hint;
13
+ this.position = position;
14
+ this.dataType = dataType;
15
+ this.constraint = constraint;
16
+ this.name = "PostgresError";
17
+ }
18
+ // Parse error
19
+ get isParseError() {
20
+ return this.code.startsWith('42');
21
+ }
22
+ // Execute error
23
+ get isDeadlock() {
24
+ return this.code === '40P01';
25
+ }
26
+ // Execute error
27
+ get isConstraintViolation() {
28
+ return this.code.startsWith('23');
29
+ }
30
+ get isTimeout() {
31
+ return this.code === '57014';
32
+ }
33
+ get isConnectionFailure() {
34
+ return this.code.startsWith('08') || this.code.startsWith('57') && this.code !== '57014';
35
+ }
36
+ }
37
+ exports.PostgresError = PostgresError;
package/dist/index.d.ts CHANGED
@@ -121,4 +121,5 @@ export { Connection as Connection };
121
121
  export { Transaction as Transaction };
122
122
  export { PgtxPool as Pool };
123
123
  export { setTypeParser as setTypeParser };
124
+ export * from "./clauses";
124
125
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EACH,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,mBAAmB,EAEnB,WAAW,EACX,aAAa,EACb,cAAc,EACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;KAWC;;IAGD;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAGD;;;GAGG;AAEH,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,CAAA;AAEnC,OAAO,EAAE,WAAW,IAAI,WAAW,EAAE,CAAA;AAErC,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAA;AAE3B,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EACH,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,mBAAmB,EAEnB,WAAW,EACX,aAAa,EACb,cAAc,EACjB,MAAM,WAAW,CAAA;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD;;;GAGG;AACH,eAAO,MAAM,GAAG;IACZ;;;;;;;;;;;OAWG;;IAGH;;;;;;OAMG;;IAGH;;;;;;OAMG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;KAWC;;IAGD;;;;;;;;;;OAUG;;IAGH;;;;;;;OAOG;;IAGH;;;;;;;;OAQG;;IAGH;;;;;;;;;;;;;;;;;;;OAmBG;;CAEN,CAAA;AAGD;;;GAGG;AAEH,OAAO,EAAE,UAAU,IAAI,UAAU,EAAE,CAAA;AAEnC,OAAO,EAAE,WAAW,IAAI,WAAW,EAAE,CAAA;AAErC,OAAO,EAAE,QAAQ,IAAI,IAAI,EAAE,CAAA;AAE3B,OAAO,EAAE,aAAa,IAAI,aAAa,EAAE,CAAA;AAEzC,cAAc,WAAW,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,18 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
17
  exports.setTypeParser = exports.Pool = exports.Transaction = exports.Connection = exports.sql = void 0;
4
18
  const pool_1 = require("./pool");
@@ -120,3 +134,4 @@ exports.sql = {
120
134
  */
121
135
  array: clauses_1.ArrayClause.create,
122
136
  };
137
+ __exportStar(require("./clauses"), exports);
package/dist/pool.d.ts CHANGED
@@ -127,7 +127,19 @@ export declare class Pool {
127
127
  * const users = await pool.query<User>`SELECT * FROM users`
128
128
  * ```
129
129
  */
130
- query<T extends Record<string, unknown>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
130
+ query<T extends Record<string, any>>(templates: TemplateStringsArray, ...args: any[]): Promise<T[]>;
131
+ /**
132
+ * Sends an asynchronous notification to a channel via `pg_notify`.
133
+ *
134
+ * @param channelName - The channel identifier
135
+ * @param payload - Optional string data (max 8000 bytes)
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * await pool.notify('events', 'hello')
140
+ * ```
141
+ */
142
+ notify(channelName: string, payload?: string): Promise<[]>;
131
143
  /**
132
144
  * Number of available (idle) connections in the pool.
133
145
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAI3C,KAAK,UAAU,GAAG,gBAAgB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,YAAY;gBAIR,MAAM,EAAE,UAAU;IAM9B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BpC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;;;;;;;;;;;OAiBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAYhF;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAmCvG;;OAEG;IACH,IAAI,IAAI,WAEP;IAGD;;;OAGG;IACH,IAAI,KAAK,WAER;IAGD;;;;;;;;;;OAUG;IACG,KAAK;CAed"}
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAK3C,KAAK,UAAU,GAAG,gBAAgB,GAAG;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,qBAAa,IAAI;IACb,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,SAAS,CAAQ;IAEzB,OAAO,CAAC,YAAY;gBAIR,MAAM,EAAE,UAAU;IAM9B;;;;;;;;;;;;;;;;;OAiBG;IACG,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BpC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,IAAI,EAAE,UAAU;IAoBxB;;;;;;;;;;;;;;;;;OAiBG;IACG,KAAK,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAYhF;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAmCnG;;;;;;;;;;OAUG;IACH,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,MAAW,GACuB,OAAO,CAAC,EAAE,CAAC;IAIlF;;OAEG;IACH,IAAI,IAAI,WAEP;IAGD;;;OAGG;IACH,IAAI,KAAK,WAER;IAGD;;;;;;;;;;OAUG;IACG,KAAK;CAed"}
package/dist/pool.js CHANGED
@@ -71,10 +71,10 @@ class Pool {
71
71
  */
72
72
  async acquire() {
73
73
  this._checkClosed();
74
- while (!this._available.isFree) {
74
+ while (this._available.hasMore) {
75
75
  const conn = this._available.get();
76
76
  this._available.next();
77
- if (conn.isAlive) {
77
+ if (conn.isOpened) {
78
78
  return conn;
79
79
  }
80
80
  this._total--;
@@ -113,11 +113,11 @@ class Pool {
113
113
  */
114
114
  release(conn) {
115
115
  this._checkClosed();
116
- if (!conn.isAlive) {
116
+ if (!conn.isOpened) {
117
117
  this._total--;
118
118
  return;
119
119
  }
120
- if (!this._waiting.isFree) {
120
+ if (this._waiting.hasMore) {
121
121
  const waiter = this._waiting.get();
122
122
  this._waiting.next();
123
123
  waiter.resolve(conn);
@@ -178,9 +178,9 @@ class Pool {
178
178
  */
179
179
  query(templates, ...args) {
180
180
  this._checkClosed();
181
- while (!this._available.isFree) {
181
+ while (this._available.hasMore) {
182
182
  const conn = this._available.get();
183
- if (conn.isAlive) {
183
+ if (conn.isOpened) {
184
184
  return conn.query(templates, ...args);
185
185
  }
186
186
  this._available.next();
@@ -204,6 +204,20 @@ class Pool {
204
204
  return conn.query(templates, ...args);
205
205
  });
206
206
  }
207
+ /**
208
+ * Sends an asynchronous notification to a channel via `pg_notify`.
209
+ *
210
+ * @param channelName - The channel identifier
211
+ * @param payload - Optional string data (max 8000 bytes)
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * await pool.notify('events', 'hello')
216
+ * ```
217
+ */
218
+ notify(channelName, payload = "") {
219
+ return this.query `select pg_notify(${channelName}, ${payload})`;
220
+ }
207
221
  /**
208
222
  * Number of available (idle) connections in the pool.
209
223
  */
@@ -229,12 +243,12 @@ class Pool {
229
243
  * ```
230
244
  */
231
245
  async close() {
232
- while (!this._available.isFree) {
246
+ while (this._available.hasMore) {
233
247
  const conn = this._available.get();
234
248
  this._available.next();
235
249
  conn.close();
236
250
  }
237
- while (!this._waiting.isFree) {
251
+ while (this._waiting.hasMore) {
238
252
  const waiter = this._waiting.get();
239
253
  this._waiting.next();
240
254
  waiter.reject(new Error('Pool closed'));
@@ -1,5 +1,7 @@
1
1
  import { AuthenticationCode, ResponseType, TransactionStatus } from "./constants";
2
2
  import { ColumnDescription } from "../types";
3
+ import { PostgresError } from "../error";
4
+ import { ChannelName } from "../connection";
3
5
  export declare class ConnectionResponseBuffer {
4
6
  private buffer;
5
7
  private caret;
@@ -31,12 +33,16 @@ export declare class ConnectionResponseReader {
31
33
  PID: number;
32
34
  secret: number;
33
35
  };
34
- readErrorResponse(): string;
36
+ readErrorResponse(): PostgresError;
35
37
  readReadyForQuery(): TransactionStatus;
36
38
  readSaslMechanisms(): string[];
37
39
  readSaslMessage(): string;
38
40
  readRowDescription(): ColumnDescription[];
39
41
  readDataRow(): (string | null)[];
42
+ readNotificationResponse(): {
43
+ name: ChannelName;
44
+ payload: string;
45
+ };
40
46
  readCommandComplete(): string;
41
47
  hasMore(): boolean;
42
48
  hasFullPacket(): boolean;
@@ -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,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,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,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOjC,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,OAAO;IAaxB,iBAAiB;CAGpB;AAGD,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAC6B,YAAY;IAIjD,kBAAkB,IAGoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAUnB,kBAAkB;;;;IAUlB,iBAAiB;IAgBjB,iBAAiB,IAEoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,IAAI,MAAM;IAOzB,kBAAkB;IAuBlB,WAAW;IAmBX,mBAAmB,IAAI,MAAM;IAM7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAKjB,gBAAgB;IAKhB,wBAAwB;IAQxB,UAAU;CAGb"}
1
+ {"version":3,"file":"connection-response-reader.d.ts","sourceRoot":"","sources":["../../src/protocol/connection-response-reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAG3C,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,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAOjC,OAAO,IAAI,OAAO;IAKlB,aAAa,IAAI,OAAO;IAaxB,iBAAiB;CAGpB;AAGD,qBAAa,wBAAwB;IAI7B,OAAO,CAAC,MAAM;IAHlB,OAAO,CAAC,mBAAmB,CAAK;IAEhC,OAAO;IAKP,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM;IAK1B,QAAQ,IAC6B,YAAY;IAIjD,kBAAkB,IAGoB,kBAAkB;IAIxD,WAAW;IAKX,mBAAmB;;;;IAUnB,kBAAkB;;;;IAUlB,iBAAiB,IAAI,aAAa;IA8ClC,iBAAiB,IAEoB,iBAAiB;IAItD,kBAAkB,IAAI,MAAM,EAAE;IAa9B,eAAe,IAAI,MAAM;IAOzB,kBAAkB;IAuBlB,WAAW;IAmBX,wBAAwB;;;;IAUxB,mBAAmB,IAAI,MAAM;IAM7B,OAAO;IAKP,aAAa;IAKb,iBAAiB;IAKjB,iBAAiB;IAKjB,gBAAgB;IAKhB,wBAAwB;IAQxB,UAAU;CAGb"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConnectionResponseReader = exports.ConnectionResponseBuffer = void 0;
4
+ const error_1 = require("../error");
4
5
  class ConnectionResponseBuffer {
5
6
  constructor(buffer) {
6
7
  this.buffer = buffer;
@@ -91,16 +92,51 @@ class ConnectionResponseReader {
91
92
  }
92
93
  readErrorResponse() {
93
94
  this.buffer.readInt32();
95
+ let severity = '';
96
+ let code = '';
94
97
  let message = '';
98
+ let detail = '';
99
+ let where = '';
100
+ let hint = '';
101
+ let position = '';
102
+ let dataType = '';
103
+ let constraint = '';
95
104
  while (true) {
96
105
  const marker = this.buffer.readChar();
97
106
  if (marker === '\0')
98
107
  break;
99
108
  const text = this.buffer.readCString();
100
- if (marker === 'M')
101
- message += text;
109
+ switch (marker) {
110
+ case 'S':
111
+ severity = text;
112
+ break;
113
+ case 'C':
114
+ code = text;
115
+ break;
116
+ case 'M':
117
+ message = text;
118
+ break;
119
+ case 'D':
120
+ detail = text;
121
+ break;
122
+ case 'W':
123
+ where = text;
124
+ break;
125
+ case 'H':
126
+ hint = text;
127
+ break;
128
+ case 'P':
129
+ position = text;
130
+ break;
131
+ case 'd':
132
+ dataType = text;
133
+ break;
134
+ case 'n':
135
+ constraint = text;
136
+ break;
137
+ }
102
138
  }
103
- return message;
139
+ return new error_1.PostgresError(message, code, detail, severity, where, hint, position, dataType, constraint);
104
140
  }
105
141
  readReadyForQuery() {
106
142
  this.buffer.readInt32();
@@ -152,6 +188,13 @@ class ConnectionResponseReader {
152
188
  }
153
189
  return rowValues;
154
190
  }
191
+ readNotificationResponse() {
192
+ this.buffer.readInt32();
193
+ this.buffer.readInt32();
194
+ const name = this.buffer.readCString();
195
+ const payload = this.buffer.readCString();
196
+ return { name, payload };
197
+ }
155
198
  readCommandComplete() {
156
199
  this.buffer.readInt32();
157
200
  return this.buffer.readCString();
@@ -26,6 +26,7 @@ export declare const ResponseTypes: {
26
26
  readonly ParameterDescription: "t";
27
27
  readonly NoData: "n";
28
28
  readonly Notice: "N";
29
+ readonly NotificationResponse: "A";
29
30
  };
30
31
  export type ResponseType = ValueOF<typeof ResponseTypes>;
31
32
  export declare const AuthenticationCodes: {
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/protocol/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,YAAY,CAAC,CAAA;AAGtD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;CAgBhB,CAAA;AAGV,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,aAAa,CAAC,CAAA;AAGxD,eAAO,MAAM,mBAAmB;;;;;;;CAOtB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAGpE,eAAO,MAAM,mBAAmB;;;;CAItB,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/protocol/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAElC,eAAO,MAAM,YAAY;;;;;;;;;CASf,CAAA;AAEV,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,YAAY,CAAC,CAAA;AAGtD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;CAiBhB,CAAA;AAGV,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,aAAa,CAAC,CAAA;AAGxD,eAAO,MAAM,mBAAmB;;;;;;;CAOtB,CAAA;AAEV,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA;AAGpE,eAAO,MAAM,mBAAmB;;;;CAItB,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
@@ -26,7 +26,8 @@ exports.ResponseTypes = {
26
26
  CloseComplete: "3",
27
27
  ParameterDescription: "t",
28
28
  NoData: "n",
29
- Notice: "N"
29
+ Notice: "N",
30
+ NotificationResponse: "A"
30
31
  };
31
32
  exports.AuthenticationCodes = {
32
33
  Ok: 0,
@@ -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,wBAAwB,KAAK,IAAI,EACvE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;IA4B5C,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;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,wBAAwB,KAAK,IAAI,EACvE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI;IA6B5C,KAAK,CAAC,MAAM,EAAE,uBAAuB;IAOrC,YAAY;IAQZ,OAAO;IAMP,IAAI,WAAW,YAA6B;CAC/C"}
@@ -9,6 +9,7 @@ class SocketConnector {
9
9
  this._onError = _onError;
10
10
  this.residualBuffer = null;
11
11
  this._isDestroyed = false;
12
+ _socket.setKeepAlive(true, 10000);
12
13
  _socket.on('data', buffer => {
13
14
  const currentBuffer = this.residualBuffer
14
15
  ? Buffer.concat([this.residualBuffer, buffer])
@@ -0,0 +1,24 @@
1
+ import { QueryText, StatementName } from "./connection";
2
+ import { ColumnDescription, ValueOF } from "./types";
3
+ export declare const QueryState: {
4
+ readonly Parsing: 0;
5
+ readonly Describing: 1;
6
+ readonly Executing: 2;
7
+ readonly Completed: 3;
8
+ readonly Failed: 4;
9
+ };
10
+ export type State = ValueOF<typeof QueryState>;
11
+ export declare class Query<T> {
12
+ text: QueryText;
13
+ args: (string | null)[];
14
+ state: State;
15
+ statementName: StatementName;
16
+ columns?: ColumnDescription[] | undefined;
17
+ promise: Promise<T[]>;
18
+ resolve: (value: T[]) => void;
19
+ reject: (error: Error) => void;
20
+ rows: (string | null)[][];
21
+ constructor(text: QueryText, args: (string | null)[], state: State, statementName: StatementName, columns?: ColumnDescription[] | undefined);
22
+ setState(state: State): void;
23
+ }
24
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGrD,eAAO,MAAM,UAAU;;;;;;CAMb,CAAA;AAGV,MAAM,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,UAAU,CAAC,CAAA;AAG9C,qBAAa,KAAK,CAAC,CAAC;IAOL,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE;IACvB,KAAK,EAAE,KAAK;IACZ,aAAa,EAAE,aAAa;IAC5B,OAAO,CAAC,EAAE,iBAAiB,EAAE;IAVxC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;IACrB,OAAO,EAAG,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAA;IAC9B,MAAM,EAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAK;gBAGnB,IAAI,EAAE,SAAS,EACf,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EACvB,KAAK,EAAE,KAAK,EACZ,aAAa,EAAE,aAAa,EAC5B,OAAO,CAAC,EAAE,iBAAiB,EAAE,YAAA;IAQxC,QAAQ,CAAC,KAAK,EAAE,KAAK;CAGxB"}
package/dist/query.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Query = exports.QueryState = void 0;
4
+ exports.QueryState = {
5
+ Parsing: 0,
6
+ Describing: 1,
7
+ Executing: 2,
8
+ Completed: 3,
9
+ Failed: 4
10
+ };
11
+ class Query {
12
+ constructor(text, args, state, statementName, columns) {
13
+ this.text = text;
14
+ this.args = args;
15
+ this.state = state;
16
+ this.statementName = statementName;
17
+ this.columns = columns;
18
+ this.rows = [];
19
+ this.promise = new Promise((a, b) => {
20
+ this.resolve = a;
21
+ this.reject = b;
22
+ });
23
+ }
24
+ setState(state) {
25
+ this.state = state;
26
+ }
27
+ }
28
+ exports.Query = Query;
package/dist/queue.d.ts CHANGED
@@ -3,8 +3,10 @@ export declare class Queue<T> {
3
3
  private _pointer;
4
4
  next(): void;
5
5
  get(): T;
6
+ shift(): T;
6
7
  push(item: T): void;
7
8
  get size(): number;
9
+ get hasMore(): boolean;
8
10
  get isFree(): boolean;
9
11
  }
10
12
  //# sourceMappingURL=queue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK,CAAC,CAAC;IAChB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAI;IAEpB,IAAI;IAQJ,GAAG;IAIH,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAED,IAAI,MAAM,YAAgD;CAC7D"}
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,qBAAa,KAAK,CAAC,CAAC;IAChB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAI;IAEpB,IAAI;IASJ,GAAG;IAKH,KAAK;IAYL,IAAI,CAAC,IAAI,EAAE,CAAC;IAIZ,IAAI,IAAI,WAEP;IAGD,IAAI,OAAO,YAEV;IAGD,IAAI,MAAM,YAA+C;CAC5D"}
package/dist/queue.js CHANGED
@@ -8,7 +8,7 @@ class Queue {
8
8
  }
9
9
  next() {
10
10
  this._pointer++;
11
- if (this._pointer === this._queue.length) {
11
+ if (this._pointer >= this._queue.length) {
12
12
  this._queue = [];
13
13
  this._pointer = 0;
14
14
  }
@@ -16,12 +16,24 @@ class Queue {
16
16
  get() {
17
17
  return this._queue[this._pointer];
18
18
  }
19
+ shift() {
20
+ const item = this._queue[this._pointer];
21
+ this._pointer++;
22
+ if (this._pointer >= this._queue.length) {
23
+ this._queue = [];
24
+ this._pointer = 0;
25
+ }
26
+ return item;
27
+ }
19
28
  push(item) {
20
29
  this._queue.push(item);
21
30
  }
22
31
  get size() {
23
32
  return this._queue.length - this._pointer;
24
33
  }
25
- get isFree() { return this._pointer === this._queue.length; }
34
+ get hasMore() {
35
+ return this._pointer < this._queue.length;
36
+ }
37
+ get isFree() { return this._pointer >= this._queue.length; }
26
38
  }
27
39
  exports.Queue = Queue;
@@ -23,7 +23,7 @@ export declare class Transaction {
23
23
  /**
24
24
  * Executes a query within the current transaction.
25
25
  */
26
- query<T extends Record<string, unknown>>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
26
+ query<T extends Record<string, any>>(strings: TemplateStringsArray, ...values: any[]): Promise<T[]>;
27
27
  /**
28
28
  * Creates a sub-transaction using PostgreSQL SAVEPOINT.
29
29
  * If the callback throws, only the actions within this savepoint are rolled back.
@@ -1 +1 @@
1
- {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC;;OAEG;IACU,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKpH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CAe5G"}
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAEzC;;;GAGG;AACH,qBAAa,WAAW;IAIhB,QAAQ,CAAC,IAAI,EAAE,UAAU;IAH7B,OAAO,CAAC,UAAU,CAAiB;gBAGtB,IAAI,EAAE,UAAU;IAG7B;;OAEG;IACH,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,OAAO,CAAC,WAAW;IAMnB;;OAEG;IACU,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOtC;;OAEG;IACU,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKhH;;;;;;;;;OASG;IACU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC;CAe5G"}
package/dist/types.d.ts CHANGED
@@ -19,5 +19,8 @@ export type ColumnDescription = {
19
19
  name: string;
20
20
  typeOID: number;
21
21
  };
22
+ export type Branded<T, Brand> = T & {
23
+ __brand: Brand;
24
+ };
22
25
  export type ValueOF<T extends Record<string, unknown>> = T[keyof T];
23
26
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,IAAI;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAGD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAGD,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,CAAC,OAAO,SAAS,GAAG,EAAE,OAAO,SAAS,GAAG,EAAE,IAAI;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAA;CACpD,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IAC/B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC3B,SAAS,EAAE,oBAAoB,CAAC;IAChC,IAAI,EAAE,GAAG,EAAE,CAAC;CACf,CAAA;AAGD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG;IAAC,OAAO,EAAE,KAAK,CAAA;CAAC,CAAA;AAEpD,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { ColumnDescription } from "../types";
2
2
  declare const Parsers: Record<number, (value: string) => unknown>;
3
- export declare function parseRowValues(columns: ColumnDescription[], rows: (string | null)[][]): Record<string, any>;
3
+ export declare function parseRowValues(columns: ColumnDescription[], rows: (string | null)[][]): Record<string, any>[];
4
4
  export declare const setTypeParser: (typeOID: keyof typeof Parsers, parser: (value: string) => unknown) => void;
5
5
  export {};
6
6
  //# sourceMappingURL=value-parser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"value-parser.d.ts","sourceRoot":"","sources":["../../src/utils/value-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA+B5C,QAAA,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAkD9C,CAAA;AAMV,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA8B3G;AAGD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,SAE9F,CAAA"}
1
+ {"version":3,"file":"value-parser.d.ts","sourceRoot":"","sources":["../../src/utils/value-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AA+B5C,QAAA,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAkD9C,CAAA;AAMV,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CA8B7G;AAGD,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,OAAO,OAAO,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,SAE9F,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m2k-5f/pgtx",
3
- "version": "2.0.1",
3
+ "version": "2.2.0",
4
4
  "description": "Blazing-fast PostgreSQL driver with pipeline support.",
5
5
  "files": [
6
6
  "dist",
@@ -40,8 +40,9 @@
40
40
  }
41
41
  },
42
42
  "devDependencies": {
43
+ "@types/node": "^26.1.1",
44
+ "pg": "^8.22.0",
43
45
  "tsx": "^4.21.0",
44
- "typescript": "^5.0.0",
45
- "@types/node": "^26.1.1"
46
+ "typescript": "^5.0.0"
46
47
  }
47
48
  }