neon 2.30.1 → 2.31.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/dist/auth.js CHANGED
@@ -33,8 +33,7 @@ export const refreshToken = async ({ oauthHost, clientId, allowUnsafeTls }, toke
33
33
  const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
34
34
  timeout: SERVER_TIMEOUT,
35
35
  execute: allowUnsafeTls
36
- ? // eslint-disable-next-line @typescript-eslint/no-deprecated
37
- [client.allowInsecureRequests]
36
+ ? [client.allowInsecureRequests]
38
37
  : undefined,
39
38
  });
40
39
  return await client.refreshTokenGrant(configuration, tokenSet.refresh_token);
@@ -44,8 +43,7 @@ export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
44
43
  const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
45
44
  timeout: SERVER_TIMEOUT,
46
45
  execute: allowUnsafeTls
47
- ? // eslint-disable-next-line @typescript-eslint/no-deprecated
48
- [client.allowInsecureRequests]
46
+ ? [client.allowInsecureRequests]
49
47
  : undefined,
50
48
  });
51
49
  //
@@ -44,7 +44,7 @@ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceA
44
44
  const preserveCredentials = async (path, credentials, apiClient) => {
45
45
  const { data: { id }, } = await apiClient.getCurrentUserInfo();
46
46
  const contents = JSON.stringify({
47
- // Making the linter happy by explicitly confirming we don't care about @typescript-eslint/no-misused-spread
47
+ // Cast to a plain record: we intentionally spread the credentials object.
48
48
  ...credentials,
49
49
  user_id: id,
50
50
  });
@@ -354,7 +354,13 @@ const fileToWebStream = (path) => {
354
354
  return new ReadableStream({
355
355
  start(controller) {
356
356
  source.on("data", (chunk) => {
357
- controller.enqueue(new Uint8Array(chunk));
357
+ // `createReadStream` with no encoding always emits Buffers at
358
+ // runtime; @types/node types the `data` chunk as `string | Buffer`,
359
+ // so normalize (a string would only appear if an encoding were set).
360
+ const bytes = typeof chunk === "string"
361
+ ? new TextEncoder().encode(chunk)
362
+ : new Uint8Array(chunk);
363
+ controller.enqueue(bytes);
358
364
  if ((controller.desiredSize ?? 0) <= 0)
359
365
  source.pause();
360
366
  });
@@ -182,7 +182,6 @@ const get = async (props) => {
182
182
  });
183
183
  const { data } = await props.apiClient.getProjectBranchDataApi(props.projectId, branchId, database);
184
184
  // Drop available_schemas from json/yaml output (not part of the public surface).
185
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
186
185
  const { available_schemas: _ignored, ...publicData } = data;
187
186
  // For table output, flatten db_schemas onto the top-level for column rendering.
188
187
  const tableRow = {
@@ -52,17 +52,16 @@ const NEON_VAR_NAMES = Object.values(NEON_ENV_VAR_KEYS).flatMap((group) => Objec
52
52
  * `NEON_AUTH_*` / `NEON_DATA_API_*` lines instead of leaving credentials for features that
53
53
  * aren't enabled.
54
54
  *
55
- * Deliberately **excludes** the storage / AI Gateway vars Neon projects onto third-party SDK
56
- * names (`AWS_*`, `OPENAI_*`): those collide with credentials a user may set by hand, so
57
- * `env pull` only ever writes them, never prunes them. (Their Neon-branded siblings —
58
- * `NEON_STORAGE_*` / `NEON_AI_GATEWAY_*` are owned and pruned.)
55
+ * Deliberately **excludes** the storage vars Neon projects onto third-party SDK names
56
+ * (`AWS_*`): those collide with credentials a user may set by hand, so `env pull` only ever
57
+ * writes them, never prunes them. The AI Gateway is emitted solely under its Neon-branded
58
+ * vars (`NEON_AI_GATEWAY_*`), which are owned and pruned.
59
59
  */
60
60
  const NEON_OWNED_ENV_KEYS = [
61
61
  ...Object.values(NEON_ENV_VAR_KEYS.postgres),
62
62
  ...Object.values(NEON_ENV_VAR_KEYS.auth),
63
63
  ...Object.values(NEON_ENV_VAR_KEYS.dataApi),
64
- NEON_ENV_VAR_KEYS.aiGateway.neonToken,
65
- NEON_ENV_VAR_KEYS.aiGateway.neonBaseUrl,
64
+ ...Object.values(NEON_ENV_VAR_KEYS.aiGateway),
66
65
  ];
67
66
  export const pull = async (props, opts = {}) => {
68
67
  const cwd = props.cwd ?? process.cwd();
@@ -28,6 +28,10 @@ export const deleteFunction = async (apiClient, projectId, branchId, slug) => {
28
28
  };
29
29
  export const createDeployment = async (apiClient, projectId, branchId, slug, params) => {
30
30
  const form = new FormData();
31
+ // TS 5.9 types `Uint8Array` as `Uint8Array<ArrayBufferLike>`, which is not
32
+ // assignable to `BlobPart` (it wants an `ArrayBuffer`-backed view, not a
33
+ // possibly-`SharedArrayBuffer` one). The bundle is always a plain
34
+ // `Uint8Array` over a regular `ArrayBuffer`, so the cast is safe.
31
35
  form.append("zip", new Blob([params.zip]), "bundle.zip");
32
36
  form.append("runtime", params.runtime);
33
37
  if (params.environment)
@@ -546,10 +546,34 @@ silent = false) => {
546
546
  return { status: "ok" };
547
547
  }
548
548
  case "null": {
549
- topt.nullPrint = value ?? "";
549
+ // Upstream guards the assignment with `if (value)`: the bare form
550
+ // (`\pset null`, value === null) prints the current setting WITHOUT
551
+ // resetting it. Only an explicit value (including the empty string,
552
+ // `\pset null ''`) changes nullPrint.
553
+ if (value !== null) {
554
+ topt.nullPrint = value;
555
+ }
550
556
  writeOutMaybe(`Null display is "${topt.nullPrint}".\n`);
551
557
  return { status: "ok" };
552
558
  }
559
+ // `\pset display_true` / `\pset display_false` (PG 19+). Upstream guards
560
+ // the assignment with `if (value)` — the bare form (no value) prints the
561
+ // current setting WITHOUT resetting it, unlike `null`. The default when
562
+ // unset is the literal 't' / 'f'.
563
+ case "display_true": {
564
+ if (value !== null) {
565
+ topt.truePrint = value;
566
+ }
567
+ writeOutMaybe(`Boolean true display is "${topt.truePrint ?? "t"}".\n`);
568
+ return { status: "ok" };
569
+ }
570
+ case "display_false": {
571
+ if (value !== null) {
572
+ topt.falsePrint = value;
573
+ }
574
+ writeOutMaybe(`Boolean false display is "${topt.falsePrint ?? "f"}".\n`);
575
+ return { status: "ok" };
576
+ }
553
577
  case "csv_fieldsep": {
554
578
  if (value !== null) {
555
579
  // Upstream `do_pset` splits the validation in two: length-based
@@ -751,6 +775,8 @@ const printAllPset = (topt) => {
751
775
  writeOut(`border ${topt.border}\n`);
752
776
  writeOut(`columns ${topt.columns}\n`);
753
777
  writeOut(`csv_fieldsep ${psetQuotedString(topt.csvFieldSep)}\n`);
778
+ writeOut(`display_false ${psetQuotedString(topt.falsePrint ?? "f")}\n`);
779
+ writeOut(`display_true ${psetQuotedString(topt.truePrint ?? "t")}\n`);
754
780
  writeOut(`expanded ${topt.expanded}\n`);
755
781
  writeOut(`fieldsep ${psetQuotedString(topt.fieldSep)}\n`);
756
782
  // fieldsep_zero / recordsep_zero are derived: upstream emits "on" iff
@@ -382,7 +382,6 @@ export const cmdSetenv = {
382
382
  // each cursor advances exactly once per call.
383
383
  const value = ctx.nextArg("normal");
384
384
  if (value === null) {
385
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
386
385
  delete process.env[envname];
387
386
  }
388
387
  else {
@@ -8,8 +8,8 @@
8
8
  *
9
9
  * Why a shared file: the WP spec asks for cmd-isolated test factories, not
10
10
  * for the command implementations themselves to duplicate one-line
11
- * primitives. Going through these helpers also keeps the eslint
12
- * `no-console` rule satisfied — every write touches `process.stdout` /
11
+ * primitives. Going through these helpers also keeps the `noConsole`
12
+ * lint rule satisfied — every write touches `process.stdout` /
13
13
  * `process.stderr` directly rather than `console.log` / `console.error`.
14
14
  */
15
15
  /** Write to stdout. */
@@ -54,6 +54,8 @@ export const PSET_OPTIONS = [
54
54
  "border",
55
55
  "columns",
56
56
  "csv_fieldsep",
57
+ "display_false",
58
+ "display_true",
57
59
  "expanded",
58
60
  "fieldsep",
59
61
  "fieldsep_zero",
@@ -356,7 +356,6 @@ const isConditionalActive = (cond) => {
356
356
  const visibleWidth = (s) => {
357
357
  // Strip CSI escapes (ESC `[` … letter). Conservative — only trims the
358
358
  // common ANSI color form psql emits. Newlines reset the count.
359
- // eslint-disable-next-line no-control-regex
360
359
  const stripped = s.replace(/\x1b\[[0-9;]*[A-Za-z]/g, "");
361
360
  const lastNl = stripped.lastIndexOf("\n");
362
361
  const tail = lastNl === -1 ? stripped : stripped.slice(lastNl + 1);
@@ -91,6 +91,8 @@ const buildDefaultPrintOpts = () => ({
91
91
  translateHeader: false,
92
92
  translateColumns: null,
93
93
  nullPrint: "",
94
+ truePrint: "t",
95
+ falsePrint: "f",
94
96
  csvFieldSep: DEFAULT_CSV_FIELD_SEP,
95
97
  // Upstream `pset.popt.topt.expanded_header_width_type =
96
98
  // PRINT_XHEADER_FULL` — default rendered as the literal "full" in the
@@ -45,7 +45,7 @@ import { latexLongtablePrinter, latexPrinter } from "../print/latex.js";
45
45
  import { troffMsPrinter } from "../print/troff.js";
46
46
  import { unalignedPrinter } from "../print/unaligned.js";
47
47
  import { applyPattern, processSQLNamePattern, } from "./processNamePattern.js";
48
- import { fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchNotNullConstraints, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, } from "./queries.js";
48
+ import { fetchExcludedFromPublications, fetchForeignTableInfo, fetchInheritedBy, fetchInherits, fetchNotNullConstraints, fetchPartitionKey, fetchPartitionOf, fetchPerColumnFdwOptions, fetchPolicies, fetchSequencePublications, fetchStatisticsObjects, fetchTableInfo, fetchTablePublications, fetchTableSubscriptions, } from "./queries.js";
49
49
  import { PG_14, serverAtLeast } from "./versionGate.js";
50
50
  /**
51
51
  * Pick the printer for the active output format. Mirrors `pickPrinter`
@@ -530,6 +530,13 @@ export const describeOneTableDetails = async (conn, oid, schema, name, relkind,
530
530
  relkind === "f") {
531
531
  push(await captureSection((b) => renderPublicationsSection(conn, oid, b)));
532
532
  }
533
+ // ----- Excluded from publications (PG 19+ FOR ALL TABLES ... EXCEPT) -----
534
+ if (relkind === "r" ||
535
+ relkind === "p" ||
536
+ relkind === "m" ||
537
+ relkind === "f") {
538
+ push(await captureSection((b) => renderExcludedFromPublicationsSection(conn, oid, b)));
539
+ }
533
540
  // ----- Subscriptions (any publishable relkind; permission-denied silent) -----
534
541
  if (relkind === "r" ||
535
542
  relkind === "p" ||
@@ -1088,6 +1095,25 @@ const renderPublicationsSection = async (conn, oid, out) => {
1088
1095
  out.write(` "${cellToString(r[0] ?? "")}"\n`);
1089
1096
  }
1090
1097
  };
1098
+ /**
1099
+ * Render `Excluded from publications:\n "name"` (one per row) for any
1100
+ * FOR ALL TABLES publication that excludes this relation via an EXCEPT
1101
+ * clause (PG 19+). No-op when the result set is empty, which includes all
1102
+ * pre-PG-19 servers (the query builder returns an empty set there).
1103
+ */
1104
+ const renderExcludedFromPublicationsSection = async (conn, oid, out) => {
1105
+ const q = fetchExcludedFromPublications({
1106
+ oid,
1107
+ serverVersion: conn.serverVersion,
1108
+ });
1109
+ const rs = await conn.query(q.sql, q.params);
1110
+ if (rs.rows.length === 0)
1111
+ return;
1112
+ out.write("Excluded from publications:\n");
1113
+ for (const r of rs.rows) {
1114
+ out.write(` "${cellToString(r[0] ?? "")}"\n`);
1115
+ }
1116
+ };
1091
1117
  /**
1092
1118
  * Render `Subscriptions:\n "name"` (one per row). Requires superuser
1093
1119
  * access to `pg_subscription` — when the query fails with a permission
@@ -1207,6 +1233,20 @@ export const describeOneSequence = async (conn, oid, schema, name, out, popt) =>
1207
1233
  if (ownRs.rows.length > 0) {
1208
1234
  footers.push(`Owned by: ${cellToString(ownRs.rows[0][0])}`);
1209
1235
  }
1236
+ // PG 19 replicates sequences via FOR ALL SEQUENCES publications; upstream
1237
+ // appends an "Included in publications:" footer listing them (one pub per
1238
+ // indented line). The query builder returns an empty set pre-PG-19.
1239
+ const pubQ = fetchSequencePublications({
1240
+ oid,
1241
+ serverVersion: conn.serverVersion,
1242
+ });
1243
+ const pubRs = await conn.query(pubQ.sql, pubQ.params);
1244
+ if (pubRs.rows.length > 0) {
1245
+ const lines = pubRs.rows
1246
+ .map((r) => ` "${cellToString(r[0] ?? "")}"`)
1247
+ .join("\n");
1248
+ footers.push(`Included in publications:\n${lines}`);
1249
+ }
1210
1250
  // Suppress the row-count footer — upstream's sequence detail output is
1211
1251
  // a single row with no `(1 row)` line. Pass the Owned-by line as a
1212
1252
  // user footer so the printer places it before the trailing blank.
@@ -17,7 +17,7 @@
17
17
  * branches in the C source collapse into TS conditional concatenation gated
18
18
  * on `serverVersion`.
19
19
  */
20
- import { PG_9_5, PG_9_6, PG_10, PG_11, PG_12, PG_13, PG_14, PG_15, PG_16, PG_17, PG_18, serverAtLeast, serverLess, } from "./versionGate.js";
20
+ import { PG_9_5, PG_9_6, PG_10, PG_11, PG_12, PG_13, PG_14, PG_15, PG_16, PG_17, PG_18, PG_19, serverAtLeast, serverLess, } from "./versionGate.js";
21
21
  /* ------------------------------------------------------------------ */
22
22
  /* Shared helpers */
23
23
  /* ------------------------------------------------------------------ */
@@ -1665,7 +1665,7 @@ export const listPublications = (opts) => {
1665
1665
  let sql = 'SELECT pubname AS "Name",\n' +
1666
1666
  ' pg_catalog.pg_get_userbyid(pubowner) AS "Owner",\n' +
1667
1667
  ' puballtables AS "All tables"';
1668
- if (serverAtLeast(serverVersion, 19)) {
1668
+ if (serverAtLeast(serverVersion, PG_19)) {
1669
1669
  sql += ',\n puballsequences AS "All sequences"';
1670
1670
  }
1671
1671
  sql +=
@@ -1702,7 +1702,7 @@ export const describePublications = (opts) => {
1702
1702
  description: "Details of publications",
1703
1703
  };
1704
1704
  }
1705
- const hasSeq = serverAtLeast(serverVersion, 19);
1705
+ const hasSeq = serverAtLeast(serverVersion, PG_19);
1706
1706
  const hasTrunc = serverAtLeast(serverVersion, PG_11);
1707
1707
  const hasGen = serverAtLeast(serverVersion, PG_18);
1708
1708
  const hasViaRoot = serverAtLeast(serverVersion, PG_13);
@@ -1769,9 +1769,24 @@ export const describeSubscriptions = (opts) => {
1769
1769
  if (serverAtLeast(serverVersion, PG_17)) {
1770
1770
  sql += ', subfailover AS "Failover"\n';
1771
1771
  }
1772
+ // PG 19 added foreign-server-backed subscriptions and dead-tuple
1773
+ // retention. Upstream surfaces these five columns in `\dRs+`:
1774
+ // the source server name (resolved from subserver), the retention
1775
+ // toggles/limits, and the WAL-receiver timeout (placed after
1776
+ // Conninfo, matching describe.c).
1777
+ if (serverAtLeast(serverVersion, PG_19)) {
1778
+ sql +=
1779
+ ', (select srvname from pg_foreign_server where oid=subserver) AS "Server"\n' +
1780
+ ', subretaindeadtuples AS "Retain dead tuples"\n' +
1781
+ ', submaxretention AS "Max retention duration"\n' +
1782
+ ', subretentionactive AS "Retention active"\n';
1783
+ }
1772
1784
  sql +=
1773
1785
  ', subsynccommit AS "Synchronous commit"\n' +
1774
1786
  ', subconninfo AS "Conninfo"\n';
1787
+ if (serverAtLeast(serverVersion, PG_19)) {
1788
+ sql += ', subwalrcvtimeout AS "Receiver timeout"\n';
1789
+ }
1775
1790
  if (serverAtLeast(serverVersion, PG_15)) {
1776
1791
  sql += ', subskiplsn AS "Skip LSN"\n';
1777
1792
  }
@@ -2303,12 +2318,19 @@ export const fetchTablePublications = (opts) => {
2303
2318
  };
2304
2319
  }
2305
2320
  const hasPubNs = serverAtLeast(serverVersion, PG_15);
2321
+ // PG 19 added publication EXCEPT lists (`pg_publication_rel.prexcept`).
2322
+ // An excepted relation still has a `pg_publication_rel` row, so the
2323
+ // explicit-membership check must skip those rows or the table would be
2324
+ // wrongly reported as "Included in publications". The excepted set is
2325
+ // surfaced separately via `fetchExcludedFromPublications`.
2326
+ const hasExcept = serverAtLeast(serverVersion, PG_19);
2327
+ const exceptGuard = hasExcept ? " AND NOT pr.prexcept" : "";
2306
2328
  let sql = "SELECT pub.pubname\n" +
2307
2329
  "FROM pg_catalog.pg_publication pub\n" +
2308
2330
  "WHERE pub.puballtables\n" +
2309
2331
  " OR EXISTS (\n" +
2310
2332
  " SELECT 1 FROM pg_catalog.pg_publication_rel pr\n" +
2311
- ` WHERE pr.prpubid = pub.oid AND pr.prrelid = '${oid}'\n` +
2333
+ ` WHERE pr.prpubid = pub.oid AND pr.prrelid = '${oid}'${exceptGuard}\n` +
2312
2334
  " )";
2313
2335
  if (hasPubNs) {
2314
2336
  sql +=
@@ -2345,6 +2367,58 @@ export const fetchTableSubscriptions = (opts) => {
2345
2367
  "ORDER BY 1;";
2346
2368
  return { sql, params: [], description: "Table subscriptions" };
2347
2369
  };
2370
+ /**
2371
+ * Publications that exclude this relation via a FOR ALL TABLES ... EXCEPT
2372
+ * clause (PG 19+, `pg_publication_rel.prexcept`). Mirrors the upstream
2373
+ * "Excluded from publications:" footer in `describeOneTableDetails`.
2374
+ * Considers the partition root as well, since an EXCEPT can be declared on
2375
+ * the root of a partitioned table.
2376
+ *
2377
+ * Returns an empty set on servers older than PG 19 (the column and the
2378
+ * feature don't exist), so the caller suppresses the footer without a
2379
+ * separate version branch at the call site.
2380
+ */
2381
+ export const fetchExcludedFromPublications = (opts) => {
2382
+ const { oid, serverVersion } = opts;
2383
+ if (serverLess(serverVersion, PG_19)) {
2384
+ return {
2385
+ sql: "/* server < 19 does not support publication EXCEPT lists */ SELECT 1 WHERE false;",
2386
+ params: [],
2387
+ description: "Excluded from publications",
2388
+ };
2389
+ }
2390
+ const sql = "SELECT pubname\n" +
2391
+ "FROM pg_catalog.pg_publication p\n" +
2392
+ "JOIN pg_catalog.pg_publication_rel pr ON p.oid = pr.prpubid\n" +
2393
+ `WHERE (pr.prrelid = '${oid}' OR pr.prrelid = pg_catalog.pg_partition_root('${oid}'))\n` +
2394
+ "AND pr.prexcept\n" +
2395
+ "ORDER BY 1;";
2396
+ return { sql, params: [], description: "Excluded from publications" };
2397
+ };
2398
+ /**
2399
+ * Publications a sequence belongs to. PG 19 added sequence replication
2400
+ * (`pg_publication.puballsequences`); a sequence is included when a
2401
+ * publication is declared FOR ALL SEQUENCES and the sequence itself is
2402
+ * publishable. Mirrors the "Included in publications:" footer emitted for
2403
+ * sequences in upstream `describeOneTableDetails`.
2404
+ *
2405
+ * Returns an empty set on servers older than PG 19.
2406
+ */
2407
+ export const fetchSequencePublications = (opts) => {
2408
+ const { oid, serverVersion } = opts;
2409
+ if (serverLess(serverVersion, PG_19)) {
2410
+ return {
2411
+ sql: "/* server < 19 does not support sequence publications */ SELECT 1 WHERE false;",
2412
+ params: [],
2413
+ description: "Sequence publications",
2414
+ };
2415
+ }
2416
+ const sql = "SELECT pubname FROM pg_catalog.pg_publication p\n" +
2417
+ "WHERE p.puballsequences\n" +
2418
+ ` AND pg_catalog.pg_relation_is_publishable('${oid}')\n` +
2419
+ "ORDER BY 1;";
2420
+ return { sql, params: [], description: "Sequence publications" };
2421
+ };
2348
2422
  /**
2349
2423
  * Per-column FDW options for a foreign table. Returns one row per column
2350
2424
  * that has at least one `attfdwoptions` entry. Output columns:
@@ -41,3 +41,4 @@ export const PG_15 = 150000;
41
41
  export const PG_16 = 160000;
42
42
  export const PG_17 = 170000;
43
43
  export const PG_18 = 180000;
44
+ export const PG_19 = 190000;
@@ -773,7 +773,6 @@ export const renderSearchLine = (pattern, entry) => {
773
773
  const stripAnsi = (text) =>
774
774
  // Targets the SGR forms we emit (e.g. \x1b[7m, \x1b[27m). Kept narrow on
775
775
  // purpose so it doesn't accidentally eat legitimate `[` characters.
776
- // eslint-disable-next-line no-control-regex
777
776
  text.replace(/\x1b\[[0-9;]*m/g, "");
778
777
  // ---------------------------------------------------------------------------
779
778
  // Display-width helpers (inlined copy of WP-09's tables; kept minimal).
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * Aligned tabular printer (psql's default output mode).
4
4
  *
@@ -354,27 +354,7 @@ const RIGHT_ALIGNED_OIDS = new Set([
354
354
  1700, // numeric
355
355
  ]);
356
356
  const isRightAlignedField = (oid) => RIGHT_ALIGNED_OIDS.has(oid);
357
- const renderCell = (cell, nullPrint, numericLocale) => {
358
- if (cell === null || cell === undefined)
359
- return nullPrint;
360
- if (typeof cell === "string") {
361
- return formatNumericLocale(cell, numericLocale);
362
- }
363
- if (typeof cell === "number" || typeof cell === "bigint") {
364
- return formatNumericLocale(cell.toString(), numericLocale);
365
- }
366
- if (typeof cell === "boolean")
367
- return cell ? "t" : "f";
368
- if (cell instanceof Date)
369
- return cell.toISOString();
370
- if (cell instanceof Uint8Array) {
371
- let hex = "\\x";
372
- for (const b of cell)
373
- hex += b.toString(16).padStart(2, "0");
374
- return hex;
375
- }
376
- return JSON.stringify(cell);
377
- };
357
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
378
358
  const ASCII_GLYPHS = {
379
359
  hrule: "-",
380
360
  vrule: "|",
@@ -498,10 +478,11 @@ const glyphsFor = (style) => {
498
478
  export const computeColumnWidths = (rs, topt) => {
499
479
  const nullPrint = topt.nullPrint;
500
480
  const numericLocale = topt.numericLocale;
481
+ const boolDisplay = boolDisplayOf(topt);
501
482
  const widths = rs.fields.map((f) => displayWidth(f.name));
502
483
  for (const row of rs.rows) {
503
484
  for (let i = 0; i < rs.fields.length; i++) {
504
- const cellText = renderCell(row[i], nullPrint, numericLocale);
485
+ const cellText = renderCell(row[i], nullPrint, numericLocale, rs.fields[i]?.dataTypeID, boolDisplay);
505
486
  for (const line of cellText.split("\n")) {
506
487
  const w = displayWidth(line);
507
488
  if (w > widths[i])
@@ -585,7 +566,8 @@ const renderHorizontal = (rs, opts, wrapped) => {
585
566
  const headers = rs.fields.map((f) => f.name);
586
567
  const aligns = rs.fields.map((f) => isRightAlignedField(f.dataTypeID) ? "right" : "left");
587
568
  // Pre-render & measure every cell.
588
- const cellGrid = rs.rows.map((row) => row.map((cell) => formatCell(renderCell(cell, nullPrint, topt.numericLocale))));
569
+ const boolDisplay = boolDisplayOf(topt);
570
+ const cellGrid = rs.rows.map((row) => row.map((cell, i) => formatCell(renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay))));
589
571
  const headerCells = headers.map((h) => formatCell(h));
590
572
  const colCount = rs.fields.length;
591
573
  const widths = headerCells.map((c) => c.width);
@@ -1141,7 +1123,8 @@ const renderVertical = (rs, opts) => {
1141
1123
  // Width of the value column = max value width across all rows.
1142
1124
  let valueWidth = 0;
1143
1125
  let dmultiline = false;
1144
- const cellGrid = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
1126
+ const boolDisplay = boolDisplayOf(topt);
1127
+ const cellGrid = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
1145
1128
  for (const row of cellGrid) {
1146
1129
  for (const v of row) {
1147
1130
  if (v.includes("\n"))
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * AsciiDoc printer.
4
4
  *
@@ -69,7 +69,8 @@ const printFlat = (rs, opts, out) => {
69
69
  const headers = rs.fields.map((f) => f.name);
70
70
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
71
71
  const ncols = rs.fields.length;
72
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
72
+ const boolDisplay = boolDisplayOf(topt);
73
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
73
74
  let buf = "";
74
75
  if (startTable) {
75
76
  // Force a paragraph break (upstream always emits a leading "\n").
@@ -135,7 +136,8 @@ const printExpanded = (rs, opts, out) => {
135
136
  const footers = opts.footers ?? topt.footers;
136
137
  const headers = rs.fields.map((f) => f.name);
137
138
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
138
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
139
+ const boolDisplay = boolDisplayOf(topt);
140
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
139
141
  let buf = "";
140
142
  if (startTable) {
141
143
  buf += "\n";
@@ -225,24 +227,4 @@ const escapeAsciidoc = (input) => {
225
227
  }
226
228
  return out;
227
229
  };
228
- const renderCell = (cell, nullPrint, numericLocale) => {
229
- if (cell === null || cell === undefined)
230
- return nullPrint;
231
- if (typeof cell === "string") {
232
- return formatNumericLocale(cell, numericLocale);
233
- }
234
- if (typeof cell === "number" || typeof cell === "bigint") {
235
- return formatNumericLocale(cell.toString(), numericLocale);
236
- }
237
- if (typeof cell === "boolean")
238
- return cell ? "t" : "f";
239
- if (cell instanceof Date)
240
- return cell.toISOString();
241
- if (cell instanceof Uint8Array) {
242
- let hex = "\\x";
243
- for (const b of cell)
244
- hex += b.toString(16).padStart(2, "0");
245
- return hex;
246
- }
247
- return JSON.stringify(cell);
248
- };
230
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * RFC 4180 CSV printer.
4
4
  *
@@ -34,7 +34,8 @@ export const csvPrinter = {
34
34
  const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
35
35
  const expanded = topt.expanded === "on";
36
36
  const headers = rs.fields.map((f) => f.name);
37
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
37
+ const boolDisplay = boolDisplayOf(topt);
38
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
38
39
  let outBuf = "";
39
40
  if (expanded) {
40
41
  for (const row of cells) {
@@ -60,27 +61,7 @@ export const csvPrinter = {
60
61
  return Promise.resolve();
61
62
  },
62
63
  };
63
- const renderCell = (cell, nullPrint, numericLocale) => {
64
- if (cell === null || cell === undefined)
65
- return nullPrint;
66
- if (typeof cell === "string") {
67
- return formatNumericLocale(cell, numericLocale);
68
- }
69
- if (typeof cell === "number" || typeof cell === "bigint") {
70
- return formatNumericLocale(cell.toString(), numericLocale);
71
- }
72
- if (typeof cell === "boolean")
73
- return cell ? "t" : "f";
74
- if (cell instanceof Date)
75
- return cell.toISOString();
76
- if (cell instanceof Uint8Array) {
77
- let hex = "\\x";
78
- for (const b of cell)
79
- hex += b.toString(16).padStart(2, "0");
80
- return hex;
81
- }
82
- return JSON.stringify(cell);
83
- };
64
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
84
65
  const csvField = (value, sep) => {
85
66
  const needsQuote = value.includes(sep) ||
86
67
  value.includes('"') ||
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * HTML printer.
4
4
  *
@@ -71,7 +71,8 @@ const printFlat = (rs, opts, out) => {
71
71
  const footers = opts.footers ?? topt.footers;
72
72
  const headers = rs.fields.map((f) => f.name);
73
73
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
74
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
74
+ const boolDisplay = boolDisplayOf(topt);
75
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
75
76
  let buf = "";
76
77
  if (startTable) {
77
78
  buf += `<table border="${String(topt.border)}"`;
@@ -133,7 +134,8 @@ const printExpanded = (rs, opts, out) => {
133
134
  const footers = opts.footers ?? topt.footers;
134
135
  const headers = rs.fields.map((f) => f.name);
135
136
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "right" : "left");
136
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
137
+ const boolDisplay = boolDisplayOf(topt);
138
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
137
139
  let buf = "";
138
140
  if (startTable) {
139
141
  buf += `<table border="${String(topt.border)}"`;
@@ -235,24 +237,4 @@ const escapeHtml = (input) => {
235
237
  }
236
238
  return out;
237
239
  };
238
- const renderCell = (cell, nullPrint, numericLocale) => {
239
- if (cell === null || cell === undefined)
240
- return nullPrint;
241
- if (typeof cell === "string") {
242
- return formatNumericLocale(cell, numericLocale);
243
- }
244
- if (typeof cell === "number" || typeof cell === "bigint") {
245
- return formatNumericLocale(cell.toString(), numericLocale);
246
- }
247
- if (typeof cell === "boolean")
248
- return cell ? "t" : "f";
249
- if (cell instanceof Date)
250
- return cell.toISOString();
251
- if (cell instanceof Uint8Array) {
252
- let hex = "\\x";
253
- for (const b of cell)
254
- hex += b.toString(16).padStart(2, "0");
255
- return hex;
256
- }
257
- return JSON.stringify(cell);
258
- };
240
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * LaTeX printers — `latex` (tabular) and `latex-longtable` (longtable).
4
4
  *
@@ -53,7 +53,8 @@ const printLatexFlat = (rs, opts, out) => {
53
53
  const headers = rs.fields.map((f) => f.name);
54
54
  const ncols = rs.fields.length;
55
55
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
56
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
56
+ const boolDisplay = boolDisplayOf(topt);
57
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
57
58
  let buf = "";
58
59
  if (startTable) {
59
60
  if (!tuplesOnly && title) {
@@ -122,7 +123,8 @@ const printLatexVertical = (rs, opts, out) => {
122
123
  const title = opts.title ?? topt.title;
123
124
  const footers = opts.footers ?? topt.footers;
124
125
  const headers = rs.fields.map((f) => f.name);
125
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
126
+ const boolDisplay = boolDisplayOf(topt);
127
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
126
128
  let buf = "";
127
129
  if (startTable) {
128
130
  if (!tuplesOnly && title) {
@@ -194,7 +196,8 @@ export const latexLongtablePrinter = {
194
196
  const headers = rs.fields.map((f) => f.name);
195
197
  const ncols = rs.fields.length;
196
198
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
197
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
199
+ const boolDisplay = boolDisplayOf(topt);
200
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
198
201
  // `topt.tableAttr` for longtable encodes per-column widths in a
199
202
  // whitespace-separated list, consumed left-to-right with a fall
200
203
  // back to the previous value once exhausted.
@@ -373,24 +376,4 @@ const escapeLatex = (input) => {
373
376
  }
374
377
  return out;
375
378
  };
376
- const renderCell = (cell, nullPrint, numericLocale) => {
377
- if (cell === null || cell === undefined)
378
- return nullPrint;
379
- if (typeof cell === "string") {
380
- return formatNumericLocale(cell, numericLocale);
381
- }
382
- if (typeof cell === "number" || typeof cell === "bigint") {
383
- return formatNumericLocale(cell.toString(), numericLocale);
384
- }
385
- if (typeof cell === "boolean")
386
- return cell ? "t" : "f";
387
- if (cell instanceof Date)
388
- return cell.toISOString();
389
- if (cell instanceof Uint8Array) {
390
- let hex = "\\x";
391
- for (const b of cell)
392
- hex += b.toString(16).padStart(2, "0");
393
- return hex;
394
- }
395
- return JSON.stringify(cell);
396
- };
379
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * Troff MS printer.
4
4
  *
@@ -78,7 +78,8 @@ const printFlat = (rs, opts, out) => {
78
78
  const headers = rs.fields.map((f) => f.name);
79
79
  const ncols = rs.fields.length;
80
80
  const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
81
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
81
+ const boolDisplay = boolDisplayOf(topt);
82
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
82
83
  let buf = "";
83
84
  if (startTable) {
84
85
  if (!tuplesOnly && title) {
@@ -135,7 +136,8 @@ const printExpanded = (rs, opts, out) => {
135
136
  const title = opts.title ?? topt.title;
136
137
  const footers = opts.footers ?? topt.footers;
137
138
  const headers = rs.fields.map((f) => f.name);
138
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
139
+ const boolDisplay = boolDisplayOf(topt);
140
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
139
141
  let buf = "";
140
142
  // currentFormat: 0 = none yet, 1 = "Record N" header (c s),
141
143
  // 2 = body (c l or c | l). Upstream uses the same tri-state to
@@ -235,24 +237,4 @@ const escapeTroff = (input) => {
235
237
  }
236
238
  return out;
237
239
  };
238
- const renderCell = (cell, nullPrint, numericLocale) => {
239
- if (cell === null || cell === undefined)
240
- return nullPrint;
241
- if (typeof cell === "string") {
242
- return formatNumericLocale(cell, numericLocale);
243
- }
244
- if (typeof cell === "number" || typeof cell === "bigint") {
245
- return formatNumericLocale(cell.toString(), numericLocale);
246
- }
247
- if (typeof cell === "boolean")
248
- return cell ? "t" : "f";
249
- if (cell instanceof Date)
250
- return cell.toISOString();
251
- if (cell instanceof Uint8Array) {
252
- let hex = "\\x";
253
- for (const b of cell)
254
- hex += b.toString(16).padStart(2, "0");
255
- return hex;
256
- }
257
- return JSON.stringify(cell);
258
- };
240
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from "./units.js";
1
+ import { boolDisplayOf, renderCellValue } from "./units.js";
2
2
  /**
3
3
  * Unaligned tabular printer.
4
4
  *
@@ -24,7 +24,8 @@ export const unalignedPrinter = {
24
24
  const expanded = topt.expanded === "on";
25
25
  const tuplesOnly = topt.tuplesOnly;
26
26
  const headers = rs.fields.map((f) => f.name);
27
- const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
27
+ const boolDisplay = boolDisplayOf(topt);
28
+ const cells = rs.rows.map((row) => row.map((cell, i) => renderCell(cell, nullPrint, topt.numericLocale, rs.fields[i]?.dataTypeID, boolDisplay)));
28
29
  let outBuf = "";
29
30
  if (expanded) {
30
31
  // Vertical mode mirrors print.c `print_unaligned_vertical`: each
@@ -95,25 +96,4 @@ export const unalignedPrinter = {
95
96
  return Promise.resolve();
96
97
  },
97
98
  };
98
- const renderCell = (cell, nullPrint, numericLocale) => {
99
- if (cell === null || cell === undefined)
100
- return nullPrint;
101
- if (typeof cell === "string") {
102
- return formatNumericLocale(cell, numericLocale);
103
- }
104
- if (typeof cell === "number" || typeof cell === "bigint") {
105
- return formatNumericLocale(cell.toString(), numericLocale);
106
- }
107
- if (typeof cell === "boolean")
108
- return cell ? "t" : "f";
109
- if (cell instanceof Date)
110
- return cell.toISOString();
111
- if (cell instanceof Uint8Array) {
112
- // Bytea -> hex escape, matching libpq's `\x` form.
113
- let hex = "\\x";
114
- for (const b of cell)
115
- hex += b.toString(16).padStart(2, "0");
116
- return hex;
117
- }
118
- return JSON.stringify(cell);
119
- };
99
+ const renderCell = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay) => renderCellValue(cell, nullPrint, numericLocale, dataTypeID, boolDisplay);
@@ -5,6 +5,59 @@
5
5
  * `pg_size_pretty()` output, and the `\timing` line format.
6
6
  */
7
7
  const NUMERIC_RE = /^-?\d+(\.\d+)?$/;
8
+ /** PostgreSQL `bool` type OID. Cells of this column type honour the
9
+ * `\pset display_true` / `\pset display_false` strings (PG 19+). */
10
+ export const BOOLOID = 16;
11
+ /**
12
+ * Extract the `\pset display_true`/`display_false` pair from a print-table
13
+ * options object, defaulting to psql's `'t'`/`'f'`. Kept here so every
14
+ * printer derives `boolDisplay` the same way.
15
+ */
16
+ export const boolDisplayOf = (topt) => ({
17
+ true: topt.truePrint ?? "t",
18
+ false: topt.falsePrint ?? "f",
19
+ });
20
+ /**
21
+ * Render a single result cell to its display string, shared by every
22
+ * table-format printer (aligned/unaligned/csv/html/latex/troff/asciidoc).
23
+ *
24
+ * Boolean handling mirrors upstream print.c: a cell in a BOOLOID column
25
+ * arriving over the wire as the text `'t'`/`'f'` (or a JS boolean, for
26
+ * synthetic result sets) renders through `boolDisplay`, which carries the
27
+ * `\pset display_true`/`display_false` values. `dataTypeID`/`boolDisplay`
28
+ * are optional so existing callers that don't thread column type through
29
+ * keep the prior behaviour (`'t'`/`'f'`).
30
+ */
31
+ export const renderCellValue = (cell, nullPrint, numericLocale, dataTypeID, boolDisplay, locale) => {
32
+ if (cell === null || cell === undefined)
33
+ return nullPrint;
34
+ const trueStr = boolDisplay?.true ?? "t";
35
+ const falseStr = boolDisplay?.false ?? "f";
36
+ // BOOLOID column: the text-format wire value is 't'/'f'. Map through the
37
+ // configured display strings (upstream keys on the 't' first byte).
38
+ if (dataTypeID === BOOLOID && typeof cell === "string") {
39
+ if (cell === "t" || cell === "f")
40
+ return cell === "t" ? trueStr : falseStr;
41
+ }
42
+ if (typeof cell === "string") {
43
+ return formatNumericLocale(cell, numericLocale, locale);
44
+ }
45
+ if (typeof cell === "number" || typeof cell === "bigint") {
46
+ return formatNumericLocale(cell.toString(), numericLocale, locale);
47
+ }
48
+ if (typeof cell === "boolean")
49
+ return cell ? trueStr : falseStr;
50
+ if (cell instanceof Date)
51
+ return cell.toISOString();
52
+ if (cell instanceof Uint8Array) {
53
+ // Bytea -> hex escape, matching libpq's `\x` form.
54
+ let hex = "\\x";
55
+ for (const b of cell)
56
+ hex += b.toString(16).padStart(2, "0");
57
+ return hex;
58
+ }
59
+ return JSON.stringify(cell);
60
+ };
8
61
  /**
9
62
  * Format a numeric string using locale-aware thousand separators and
10
63
  * decimal point. Equivalent to print.c `format_numeric_locale` but
@@ -741,7 +741,6 @@ export class PgConnection {
741
741
  throw new Error("PgConnection.prepare: server returned no parameter description");
742
742
  }
743
743
  const { paramOids, fields } = descResult;
744
- // eslint-disable-next-line @typescript-eslint/no-this-alias
745
744
  const conn = this;
746
745
  return {
747
746
  name,
@@ -279,7 +279,6 @@ function xor(a, b) {
279
279
  const NON_ASCII_SPACE_RE = new RegExp("[" + "\u00A0\u1680" + "\u2000-\u200B" + "\u202F\u205F\u3000" + "]", "g");
280
280
  // Combining marks and ZWJ are intentionally in this class; RFC 3454 Table B.1
281
281
  // strips them precisely because they combine with neighbouring code points.
282
- /* eslint-disable no-misleading-character-class */
283
282
  const MAPPED_TO_NOTHING_RE = new RegExp("[" +
284
283
  "\u00AD\u034F\u1806" +
285
284
  "\u180B-\u180D" +
@@ -287,7 +286,6 @@ const MAPPED_TO_NOTHING_RE = new RegExp("[" +
287
286
  "\uFE00-\uFE0F" +
288
287
  "\uFEFF" +
289
288
  "]", "g");
290
- /* eslint-enable no-misleading-character-class */
291
289
  function saslprep(password) {
292
290
  return password
293
291
  .replace(NON_ASCII_SPACE_RE, " ")
@@ -27,7 +27,6 @@ const reserveClosedPort = () => new Promise((resolve, reject) => {
27
27
  });
28
28
  });
29
29
  export const test = originalTest.extend({
30
- // eslint-disable-next-line no-empty-pattern
31
30
  runMockServer: async ({}, use) => {
32
31
  let startedServer;
33
32
  await use(async (mockDir) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neon",
3
- "version": "2.30.1",
3
+ "version": "2.31.0",
4
4
  "description": "CLI tool for Neon Serverless Postgres",
5
5
  "keywords": [
6
6
  "neon",
@@ -43,41 +43,38 @@
43
43
  "diff": "5.2.0",
44
44
  "fflate": "^0.8.3",
45
45
  "neon-init": "0.20.0",
46
- "open": "10.1.0",
46
+ "open": "^10.2.0",
47
47
  "openid-client": "6.8.1",
48
48
  "pg-protocol": "^1.14.0",
49
49
  "prompts": "2.4.2",
50
50
  "undici": "^7.28.0",
51
51
  "which": "3.0.1",
52
- "yaml": "2.4.5",
52
+ "yaml": "^2.9.0",
53
53
  "yargs": "17.7.2",
54
- "@neon/config": "0.9.1",
55
54
  "@neon/sdk": "1.0.0",
55
+ "@neon/config": "0.9.1",
56
56
  "@neon/config-runtime": "0.9.1",
57
- "@neon/env": "0.10.1"
57
+ "@neon/env": "0.11.0"
58
58
  },
59
59
  "optionalDependencies": {
60
60
  "esbuild": "0.28.1"
61
61
  },
62
62
  "devDependencies": {
63
63
  "@apidevtools/swagger-parser": "12.1.0",
64
- "@eslint/js": "9.29.0",
65
64
  "@rollup/plugin-commonjs": "25.0.8",
66
65
  "@rollup/plugin-json": "6.1.0",
67
66
  "@rollup/plugin-node-resolve": "15.2.3",
68
67
  "@testcontainers/postgresql": "^11.14.0",
69
68
  "@types/cli-table": "0.3.4",
70
69
  "@types/diff": "5.2.1",
71
- "@types/eslint__js": "8.42.3",
72
70
  "@types/express": "4.17.21",
73
- "@types/node": "18.19.41",
71
+ "@types/node": "^20.19.0",
74
72
  "@types/prompts": "2.4.9",
75
73
  "@types/which": "3.0.4",
76
- "@types/yargs": "17.0.32",
77
- "@vitest/coverage-v8": "1.6.1",
74
+ "@types/yargs": "^17.0.35",
75
+ "@vitest/coverage-v8": "^3.0.9",
78
76
  "@yao-pkg/pkg": "6.10.0",
79
77
  "emocks": "3.0.4",
80
- "eslint": "9.29.0",
81
78
  "express": "4.19.2",
82
79
  "node-pty": "1.1.0",
83
80
  "oauth2-mock-server": "8.1.0",
@@ -85,9 +82,8 @@
85
82
  "rollup": "3.29.4",
86
83
  "strip-ansi": "7.1.0",
87
84
  "tsx": "4.22.3",
88
- "typescript": "5.8.3",
89
- "typescript-eslint": "8.28.0",
90
- "vitest": "1.6.1"
85
+ "typescript": "^5.9.0",
86
+ "vitest": "^3.0.9"
91
87
  },
92
88
  "publishConfig": {
93
89
  "access": "public",
@@ -113,8 +109,8 @@
113
109
  "build": "pnpm generateParams && pnpm clean && tsc -p tsconfig.build.json && cp src/*.html ./dist",
114
110
  "bundle": "node pkg.js",
115
111
  "typecheck": "tsc --noEmit",
116
- "lint": "pnpm typecheck && eslint src",
117
- "lint:fix": "pnpm typecheck && eslint src --fix",
112
+ "lint": "pnpm typecheck && biome check src",
113
+ "lint:fix": "pnpm typecheck && biome check src --write",
118
114
  "test": "pnpm build && vitest run",
119
115
  "test:ci": "pnpm build && vitest run",
120
116
  "test:conformance": "vitest run --config tests/psql-conformance/vitest.config.ts"