@steadybrew/postgresjs 4.0.0-rc1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,24 +11,35 @@ This is an independently maintained fork of [Postgres.js](https://github.com/por
11
11
  - 🏄‍♀️ Simple surface API
12
12
  - 🖊️ Dynamic query support
13
13
 
14
- <br>
14
+ <br clear="all">
15
15
 
16
16
  ## Compatibility policy
17
17
 
18
- The next release raises the npm package's minimum Node.js version to **24**. This is a breaking compatibility change for applications using older Node versions. The minimum stays at Node 24 until an explicit policy change; newly maintained Node majors are tested before compatibility is claimed.
18
+ **Version 4 requires Node.js 24 or newer** and supports PostgreSQL 15–18. Version 4.0.0 raised the minimum Node.js version from 12. The minimum stays at Node 24 until an explicit policy change; newly maintained Node majors are tested before compatibility is claimed.
19
19
 
20
20
  | Runtime | PostgreSQL target | CI jobs | Validation status |
21
21
  | --- | --- | ---: | --- |
22
- | Node 24, ESM and CJS | 15, 16, 17, 18 | 4 | Locally validated |
23
- | Node 26, ESM and CJS | 15, 16, 17, 18 | 4 | Configured; local execution pending |
24
- | Deno 1.46.3 | 17 | 1 | Validated legacy runtime; no Deno 2 claim |
25
- | Cloudflare workerd, Wrangler 4.123.0 | 17 | 1 | Configured; startup/array, timer, `sql.end()` and TLS gates; #1202 "Stream was cancelled" still open |
22
+ | Node 24, ESM and CJS | 15, 16, 17, 18 | 4 | Passed release-candidate CI |
23
+ | Node 26, ESM and CJS | 15, 16, 17, 18 | 4 | Passed release-candidate CI |
24
+ | Deno 1.46.3 | 17 | 1 | Passed release-candidate CI; no Deno 2 claim |
25
+ | Cloudflare workerd, Wrangler 4.123.0 | 17 | 1 | Passed release-candidate CI; known "Stream was cancelled" rejection remains |
26
26
  | Bun export | No dedicated target | 0 | Retained as best effort; unverified, without dedicated CI |
27
27
 
28
- The core Node matrix has eight jobs. Deno and Cloudflare run independently, so they do not multiply that matrix. PostgreSQL versions below 15 are outside the next release's supported matrix.
28
+ All ten [CI jobs](https://github.com/steadybrew/postgresjs/actions/runs/37943516565) passed for 4.1.0. The core Node matrix has eight jobs; Deno and Cloudflare run independently. PostgreSQL versions below 15 are outside the supported matrix. See the [release notes](./CHANGELOG.md#v410---2026-10-09) for validation details and [known limitations](./CHANGELOG.md#known-limitations), including the Cloudflare cancellation rejection tolerated by its CI gate.
29
29
 
30
30
  Supabase PostgreSQL and Supabase Edge are separate environments. Deno 1.46.3 coverage does not imply Deno 2 or hosted Supabase Edge certification.
31
31
 
32
+ ## Upgrading to 4.0.0
33
+
34
+ Version 4.0.0 fixes connection startup, reservations, reconnects, shutdown, transaction ownership, and built-in array handling. When upgrading from `@steadybrew/postgresjs` 3.x:
35
+
36
+ - Upgrade to **Node.js 24 or newer** before installing.
37
+ - Keep queries within the reservation or transaction that owns them. Reserved handles used after `release()`, and transaction handles used after completion, reject with `CONNECTION_ENDED`. A handle whose connection has closed cannot run queries on a reconnected session.
38
+ - Pass a non-empty string without NUL characters to transaction `sql.prepare(name)`. Invalid names throw `INVALID_TRANSACTION_NAME`.
39
+ - If you use `fetch_types: false`, review code that expects built-in arrays as raw strings: supported built-in arrays are now decoded as JavaScript arrays. SQL `NULL` elements become JavaScript `null`; quoted `"NULL"` text remains a string.
40
+
41
+ See the [changelog](./CHANGELOG.md#breaking-changes-and-migration) for the full migration notes and fixes.
42
+
32
43
  ## Getting started
33
44
 
34
45
  <br>
@@ -92,6 +103,7 @@ const { default: postgres } = await import('@steadybrew/postgresjs')
92
103
 
93
104
  ## Table of Contents
94
105
 
106
+ * [Upgrading to 4.0.0](#upgrading-to-400)
95
107
  * [Connection](#connection)
96
108
  * [Queries](#queries)
97
109
  * [Building queries](#building-queries)
@@ -713,11 +725,13 @@ sql.begin('read write', async sql => {
713
725
  ```
714
726
 
715
727
 
716
- #### PREPARE TRANSACTION `await sql.prepare([name]) -> fn()`
728
+ #### PREPARE TRANSACTION `sql.prepare(name)`
717
729
 
718
730
  Indicates that the transactions should be prepared using the [`PREPARE TRANSACTION [NAME]`](https://www.postgresql.org/docs/current/sql-prepare-transaction.html) statement
719
731
  instead of being committed.
720
732
 
733
+ The name must be a non-empty string without NUL characters; invalid names throw `INVALID_TRANSACTION_NAME`. Quotes and backslashes in valid names are preserved.
734
+
721
735
  ```js
722
736
  sql.begin('read write', async sql => {
723
737
  const [user] = await sql`
@@ -1114,7 +1128,7 @@ By default, connections will not close until `.end()` is called. However, it may
1114
1128
  - using Postgres.js in a Serverless environment (Lambda, etc.)
1115
1129
  - using Postgres.js with a database service that automatically closes connections after some time (see [`ECONNRESET` issue](https://github.com/porsager/postgres/issues/179))
1116
1130
 
1117
- This can be done using the `idle_timeout` or `max_lifetime` options. These configuration options specify the number of seconds to wait before automatically closing an idle connection and the maximum time a connection can exist, respectively.
1131
+ This can be done using the `idle_timeout` or `max_lifetime` options. These configuration options specify the number of seconds to wait before automatically closing an idle connection and the maximum time a connection can exist, respectively. Both are also checked when an idle connection is handed out, so an idle connection that is past either limit (for example after a serverless instance was frozen) is closed instead of being used.
1118
1132
 
1119
1133
  For example, to close a connection that has either been idle for 20 seconds or existed for more than 30 minutes:
1120
1134
 
@@ -1291,6 +1305,8 @@ await reserved.release()
1291
1305
 
1292
1306
  Once you have finished with the reserved connection, call `release` to add it back to the pool.
1293
1307
 
1308
+ After release, queries through that reserved handle reject with `CONNECTION_ENDED`. Call `sql.reserve()` again to acquire a new handle. A reserved handle whose connection has closed cannot be reused after reconnect.
1309
+
1294
1310
  ## Error handling
1295
1311
 
1296
1312
  Errors are all thrown to related queries and never globally. Errors coming from database itself are always in the [native Postgres format](https://www.postgresql.org/docs/current/errcodes-appendix.html), and the same goes for any [Node.js errors](https://nodejs.org/api/errors.html#errors_common_system_errors) eg. coming from the underlying connection.
@@ -1331,6 +1347,16 @@ When using SASL authentication the server responds with a signature at the end o
1331
1347
 
1332
1348
  Making queries has to be done using the sql function as a [tagged template](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#Tagged_templates). This is to ensure parameters are serialized and passed to Postgres as query parameters with correct types and to avoid SQL injection.
1333
1349
 
1350
+ ##### SSL_NOT_SUPPORTED
1351
+ > The server does not support SSL connections
1352
+
1353
+ The server answered the SSL request with a refusal while `ssl` is set to `require` (or another mode that does not fall back to plain text). The attempt fails at once for a single host. Use `ssl: 'prefer'` to fall back to an unencrypted connection, or enable SSL on the server.
1354
+
1355
+ ##### TARGET_SESSION_ATTRS
1356
+ > No host matched target_session_attrs=read-write: localhost:5432 is read-only
1357
+
1358
+ No host satisfied [`target_session_attrs`](#connection-details). This error is the `cause` of the `CONNECTION_CLOSED` error the query rejects with, so retry logic keyed on `CONNECTION_CLOSED` keeps working; read `err.cause.code` and `err.cause.message` for the reason. The message names the requested value and each host with the reason it was rejected. With several hosts, the connection fails at once when every host answered and none matched; if any host failed for another reason (refused, closed, timed out), the client keeps retrying until `connect_timeout` instead, since a failover may be in progress. With a single host, the client keeps retrying until `connect_timeout` before failing, because a single endpoint that is read-only is often in the middle of a failover, for example a cluster DNS name that still points at the old primary.
1359
+
1334
1360
  ##### AUTH_TYPE_NOT_IMPLEMENTED
1335
1361
  > Auth type X not implemented
1336
1362
 
@@ -1339,7 +1365,7 @@ Postgres supports many different authentication types. This one is not supported
1339
1365
  ##### CONNECTION_CLOSED
1340
1366
  > write CONNECTION_CLOSED host:port
1341
1367
 
1342
- This error is thrown if the connection was closed without an error. This should not happen during normal operations, so please create an issue if this was unexpected.
1368
+ This error is thrown if the connection was closed, or could not be opened, without an error of its own. When the server or the network ended the session, or no host matched `target_session_attrs`, the underlying error is available as `err.cause`.
1343
1369
 
1344
1370
  ##### CONNECTION_ENDED
1345
1371
  > write CONNECTION_ENDED host:port
@@ -1356,7 +1382,7 @@ This error is thrown for any queries that were pending when the timeout to [`sql
1356
1382
  ##### CONNECT_TIMEOUT
1357
1383
  > write CONNECT_TIMEOUT host:port
1358
1384
 
1359
- The timeout applies to each connection attempt and covers socket creation, TCP and TLS negotiation, authentication, session checks and type discovery. When an attempt times out, the next host is tried. The acquisition as a whole is bounded by `connect_timeout` multiplied by the number of hosts, measured from the first attempt; time spent waiting on an inherited reconnect delay is not counted. Set it with `connect_timeout` or `PGCONNECT_TIMEOUT` (30 seconds by default); zero disables both limits.
1385
+ The timeout applies to each connection attempt and covers socket creation, TCP and TLS negotiation, authentication, session checks and type discovery. When an attempt times out, the next host is tried. The acquisition as a whole is bounded by `connect_timeout` multiplied by the number of hosts, measured from the first attempt; time spent waiting on an inherited reconnect delay is not counted. Set it with `connect_timeout` or `PGCONNECT_TIMEOUT` (30 seconds by default); zero disables both limits. The same value also bounds a graceful close: if the server does not close the socket within `connect_timeout` seconds after `end()` sends Terminate, the connection is closed anyway; zero leaves a graceful close unbounded.
1360
1386
 
1361
1387
  If the only attempt times out, the error is `CONNECT_TIMEOUT`. Once earlier attempts have failed, exhausting the overall budget reports the earlier error, such as the last server error or `CONNECTION_CLOSED`. With a single host, a socket or socket factory error rejects the query once; with several hosts, errors keep cycling through the hosts with backoff until the budget is spent. Queries already sent to the server are not replayed.
1362
1388