neonctl 2.27.1 → 2.29.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.
Files changed (136) hide show
  1. package/README.md +35 -3
  2. package/dist/analytics.js +52 -34
  3. package/dist/api.js +643 -13
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +8 -1
  6. package/dist/commands/auth.js +64 -51
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +160 -150
  9. package/dist/commands/bucket.js +183 -146
  10. package/dist/commands/checkout.js +51 -51
  11. package/dist/commands/config.js +228 -82
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +100 -101
  14. package/dist/commands/databases.js +29 -26
  15. package/dist/commands/deploy.js +12 -12
  16. package/dist/commands/dev.js +114 -114
  17. package/dist/commands/env.js +43 -43
  18. package/dist/commands/functions.js +101 -104
  19. package/dist/commands/index.js +27 -25
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +232 -182
  23. package/dist/commands/neon_auth.js +385 -370
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +103 -101
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +27 -24
  29. package/dist/commands/schema_diff.js +25 -26
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +40 -0
  32. package/dist/commands/user.js +5 -5
  33. package/dist/commands/vpc_endpoints.js +50 -50
  34. package/dist/config.js +7 -7
  35. package/dist/config_format.js +5 -5
  36. package/dist/context.js +37 -14
  37. package/dist/current_branch_fast_path.js +55 -0
  38. package/dist/dev/env.js +33 -33
  39. package/dist/dev/functions.js +4 -4
  40. package/dist/dev/inputs.js +6 -6
  41. package/dist/dev/runtime.js +25 -25
  42. package/dist/env.js +14 -14
  43. package/dist/env_file.js +13 -13
  44. package/dist/errors.js +68 -5
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +110 -107
  48. package/dist/log.js +2 -2
  49. package/dist/parameters.gen.js +14 -14
  50. package/dist/pkg.js +5 -5
  51. package/dist/psql/cli.js +4 -2
  52. package/dist/psql/command/cmd_cond.js +61 -61
  53. package/dist/psql/command/cmd_connect.js +159 -154
  54. package/dist/psql/command/cmd_copy.js +107 -97
  55. package/dist/psql/command/cmd_describe.js +368 -363
  56. package/dist/psql/command/cmd_format.js +276 -263
  57. package/dist/psql/command/cmd_io.js +269 -263
  58. package/dist/psql/command/cmd_lo.js +74 -66
  59. package/dist/psql/command/cmd_meta.js +148 -148
  60. package/dist/psql/command/cmd_misc.js +17 -17
  61. package/dist/psql/command/cmd_pipeline.js +142 -135
  62. package/dist/psql/command/cmd_restrict.js +25 -25
  63. package/dist/psql/command/cmd_show.js +183 -168
  64. package/dist/psql/command/dispatch.js +26 -26
  65. package/dist/psql/command/shared.js +14 -14
  66. package/dist/psql/complete/filenames.js +16 -16
  67. package/dist/psql/complete/index.js +4 -4
  68. package/dist/psql/complete/matcher.js +33 -32
  69. package/dist/psql/complete/psqlVars.js +173 -173
  70. package/dist/psql/complete/queries.js +5 -3
  71. package/dist/psql/complete/rules.js +900 -863
  72. package/dist/psql/core/common.js +136 -133
  73. package/dist/psql/core/help.js +343 -343
  74. package/dist/psql/core/mainloop.js +160 -153
  75. package/dist/psql/core/prompt.js +126 -123
  76. package/dist/psql/core/settings.js +111 -111
  77. package/dist/psql/core/sqlHelp.js +150 -150
  78. package/dist/psql/core/startup.js +211 -205
  79. package/dist/psql/core/syncVars.js +14 -14
  80. package/dist/psql/core/variables.js +24 -24
  81. package/dist/psql/describe/formatters.js +302 -289
  82. package/dist/psql/describe/processNamePattern.js +28 -28
  83. package/dist/psql/describe/queries.js +656 -651
  84. package/dist/psql/index.js +436 -411
  85. package/dist/psql/io/history.js +36 -36
  86. package/dist/psql/io/input.js +15 -15
  87. package/dist/psql/io/lineEditor/buffer.js +27 -25
  88. package/dist/psql/io/lineEditor/complete.js +15 -15
  89. package/dist/psql/io/lineEditor/filename.js +22 -22
  90. package/dist/psql/io/lineEditor/index.js +65 -62
  91. package/dist/psql/io/lineEditor/keymap.js +325 -318
  92. package/dist/psql/io/lineEditor/vt100.js +60 -60
  93. package/dist/psql/io/pgpass.js +18 -18
  94. package/dist/psql/io/pgservice.js +14 -14
  95. package/dist/psql/io/psqlrc.js +46 -46
  96. package/dist/psql/print/aligned.js +175 -166
  97. package/dist/psql/print/asciidoc.js +51 -51
  98. package/dist/psql/print/crosstab.js +34 -31
  99. package/dist/psql/print/csv.js +25 -22
  100. package/dist/psql/print/html.js +54 -54
  101. package/dist/psql/print/json.js +12 -12
  102. package/dist/psql/print/latex.js +118 -118
  103. package/dist/psql/print/pager.js +28 -26
  104. package/dist/psql/print/troff.js +48 -48
  105. package/dist/psql/print/unaligned.js +15 -14
  106. package/dist/psql/print/units.js +17 -17
  107. package/dist/psql/scanner/slash.js +48 -46
  108. package/dist/psql/scanner/sql.js +88 -84
  109. package/dist/psql/scanner/stringutils.js +21 -17
  110. package/dist/psql/types/index.js +7 -7
  111. package/dist/psql/types/scanner.js +8 -8
  112. package/dist/psql/wire/connection.js +341 -327
  113. package/dist/psql/wire/copy.js +7 -7
  114. package/dist/psql/wire/pipeline.js +26 -24
  115. package/dist/psql/wire/protocol.js +102 -102
  116. package/dist/psql/wire/sasl.js +62 -62
  117. package/dist/psql/wire/tls.js +79 -73
  118. package/dist/storage_api.js +22 -23
  119. package/dist/test_utils/fixtures.js +74 -41
  120. package/dist/test_utils/oauth_server.js +5 -5
  121. package/dist/utils/api_enums.js +33 -0
  122. package/dist/utils/branch_notice.js +5 -5
  123. package/dist/utils/branch_picker.js +26 -26
  124. package/dist/utils/compute_units.js +4 -4
  125. package/dist/utils/enrichers.js +28 -16
  126. package/dist/utils/esbuild.js +28 -28
  127. package/dist/utils/formats.js +1 -1
  128. package/dist/utils/middlewares.js +3 -3
  129. package/dist/utils/package_manager.js +68 -0
  130. package/dist/utils/point_in_time.js +12 -12
  131. package/dist/utils/psql.js +30 -30
  132. package/dist/utils/string.js +2 -2
  133. package/dist/utils/ui.js +9 -9
  134. package/dist/utils/zip.js +1 -1
  135. package/dist/writer.js +17 -17
  136. package/package.json +10 -12
@@ -44,12 +44,12 @@
44
44
  * - PSTDIN/PSTDOUT are treated as STDIN/STDOUT (no separate "psql stdin
45
45
  * vs current input source" distinction — REPL plumbing isn't wired yet).
46
46
  */
47
- import { spawn } from 'node:child_process';
48
- import { createReadStream, createWriteStream, promises as fsPromises, } from 'node:fs';
49
- import { Buffer } from 'node:buffer';
50
- import { pumpReadable } from '../wire/copy.js';
51
- import { getPipelineState } from './cmd_pipeline.js';
52
- import { writeErr, writeOut } from './shared.js';
47
+ import { Buffer } from "node:buffer";
48
+ import { spawn } from "node:child_process";
49
+ import { createReadStream, createWriteStream, promises as fsPromises, } from "node:fs";
50
+ import { pumpReadable } from "../wire/copy.js";
51
+ import { getPipelineState } from "./cmd_pipeline.js";
52
+ import { writeErr, writeOut } from "./shared.js";
53
53
  /**
54
54
  * Diagnostic emitted when the user tries to run `\copy` (or a raw COPY
55
55
  * statement) inside an active `\startpipeline` ... `\endpipeline` block.
@@ -58,8 +58,8 @@ import { writeErr, writeOut } from './shared.js';
58
58
  * the wire-layer abort path can reuse the same string and tests can match
59
59
  * via a single source of truth.
60
60
  */
61
- export const COPY_IN_PIPELINE_MSG = 'COPY in a pipeline is not supported, aborting connection';
62
- const WHITESPACE = ' \t\n\r';
61
+ export const COPY_IN_PIPELINE_MSG = "COPY in a pipeline is not supported, aborting connection";
62
+ const WHITESPACE = " \t\n\r";
63
63
  /**
64
64
  * Tokenise the next term of the `\copy` tail. Mirrors upstream's `strtokx`
65
65
  * call sites: each call passes a different combination of (delim chars,
@@ -83,7 +83,7 @@ const tokenize = (input, delim, quote, doubleQuoteEscape) => {
83
83
  while (i < n && WHITESPACE.includes(input[i]))
84
84
  i++;
85
85
  if (i >= n)
86
- return { token: null, rest: '' };
86
+ return { token: null, rest: "" };
87
87
  // 2. Delimiter character returned as single-char token.
88
88
  if (delim.length > 0 && delim.includes(input[i])) {
89
89
  const token = input[i];
@@ -101,7 +101,7 @@ const tokenize = (input, delim, quote, doubleQuoteEscape) => {
101
101
  i++;
102
102
  while (i < n) {
103
103
  const c = input[i];
104
- if (doubleQuoteEscape && c === '\\' && i + 1 < n) {
104
+ if (doubleQuoteEscape && c === "\\" && i + 1 < n) {
105
105
  i += 2;
106
106
  continue;
107
107
  }
@@ -146,7 +146,7 @@ const stripSingleQuotes = (token) => {
146
146
  if (token.length < 2 || !token.startsWith("'") || !token.endsWith("'")) {
147
147
  return token;
148
148
  }
149
- let out = '';
149
+ let out = "";
150
150
  let i = 1;
151
151
  const end = token.length - 1;
152
152
  while (i < end) {
@@ -167,9 +167,9 @@ const stripSingleQuotes = (token) => {
167
167
  * Node doesn't expose `getpwnam` cleanly).
168
168
  */
169
169
  const expandTilde = (filePath) => {
170
- if (!filePath.startsWith('~'))
170
+ if (!filePath.startsWith("~"))
171
171
  return filePath;
172
- if (filePath === '~' || filePath.startsWith('~/')) {
172
+ if (filePath === "~" || filePath.startsWith("~/")) {
173
173
  const home = process.env.HOME ?? process.env.USERPROFILE;
174
174
  if (home === undefined)
175
175
  return filePath;
@@ -186,7 +186,7 @@ const expandTilde = (filePath) => {
186
186
  * by the dispatcher's `BackslashContext.rawArgs`).
187
187
  */
188
188
  export const parseSlashCopy = (input) => {
189
- let beforeToFrom = '';
189
+ let beforeToFrom = "";
190
190
  let rest = input;
191
191
  let token;
192
192
  // Helper to keep the failure messages consistent with upstream.
@@ -194,18 +194,18 @@ export const parseSlashCopy = (input) => {
194
194
  ok: false,
195
195
  error: tok !== null && tok.length > 0
196
196
  ? `parse error at "${tok}"`
197
- : 'parse error at end of line',
197
+ : "parse error at end of line",
198
198
  });
199
199
  // First token: optional BINARY, or table-name / "(" for subquery.
200
- let r1 = tokenize(rest, '.,()', '"', false);
200
+ let r1 = tokenize(rest, ".,()", '"', false);
201
201
  token = r1.token;
202
202
  rest = r1.rest;
203
203
  if (token === null)
204
204
  return errAt(null);
205
205
  // Optional legacy BINARY keyword (pre-7.3 syntax). Re-emit then read next.
206
- if (token.toLowerCase() === 'binary') {
206
+ if (token.toLowerCase() === "binary") {
207
207
  beforeToFrom += token;
208
- r1 = tokenize(rest, '.,()', '"', false);
208
+ r1 = tokenize(rest, ".,()", '"', false);
209
209
  token = r1.token;
210
210
  rest = r1.rest;
211
211
  if (token === null)
@@ -213,62 +213,62 @@ export const parseSlashCopy = (input) => {
213
213
  }
214
214
  // `(query)` subquery form? Re-emit balanced-paren contents verbatim.
215
215
  let isSubquery = false;
216
- if (token === '(') {
216
+ if (token === "(") {
217
217
  isSubquery = true;
218
218
  let parens = 1;
219
219
  while (parens > 0) {
220
- beforeToFrom += ' ';
220
+ beforeToFrom += " ";
221
221
  beforeToFrom += token;
222
- const r = tokenize(rest, '()', '"\'', true);
222
+ const r = tokenize(rest, "()", "\"'", true);
223
223
  token = r.token;
224
224
  rest = r.rest;
225
225
  if (token === null)
226
226
  return errAt(null);
227
- if (token === '(')
227
+ if (token === "(")
228
228
  parens++;
229
- else if (token === ')')
229
+ else if (token === ")")
230
230
  parens--;
231
231
  }
232
232
  }
233
- beforeToFrom += beforeToFrom.length > 0 ? ' ' : '';
233
+ beforeToFrom += beforeToFrom.length > 0 ? " " : "";
234
234
  beforeToFrom += token;
235
235
  // Next token: schema-separator `.`, column-list opener `(`, or FROM/TO.
236
- let r2 = tokenize(rest, '.,()', '"', false);
236
+ let r2 = tokenize(rest, ".,()", '"', false);
237
237
  token = r2.token;
238
238
  rest = r2.rest;
239
239
  if (token === null)
240
240
  return errAt(null);
241
241
  // Schema-qualified `schema.table` — upstream just re-emits all three tokens.
242
- if (token === '.') {
242
+ if (token === ".") {
243
243
  beforeToFrom += token;
244
- r2 = tokenize(rest, '.,()', '"', false);
244
+ r2 = tokenize(rest, ".,()", '"', false);
245
245
  token = r2.token;
246
246
  rest = r2.rest;
247
247
  if (token === null)
248
248
  return errAt(null);
249
249
  beforeToFrom += token;
250
- r2 = tokenize(rest, '.,()', '"', false);
250
+ r2 = tokenize(rest, ".,()", '"', false);
251
251
  token = r2.token;
252
252
  rest = r2.rest;
253
253
  if (token === null)
254
254
  return errAt(null);
255
255
  }
256
256
  // Parenthesised column list `(col1, col2, …)`.
257
- if (token === '(') {
257
+ if (token === "(") {
258
258
  for (;;) {
259
- beforeToFrom += ' ';
259
+ beforeToFrom += " ";
260
260
  beforeToFrom += token;
261
- const r = tokenize(rest, '()', '"', false);
261
+ const r = tokenize(rest, "()", '"', false);
262
262
  token = r.token;
263
263
  rest = r.rest;
264
264
  if (token === null)
265
265
  return errAt(null);
266
- if (token === ')')
266
+ if (token === ")")
267
267
  break;
268
268
  }
269
- beforeToFrom += ' ';
269
+ beforeToFrom += " ";
270
270
  beforeToFrom += token;
271
- r2 = tokenize(rest, '.,()', '"', false);
271
+ r2 = tokenize(rest, ".,()", '"', false);
272
272
  token = r2.token;
273
273
  rest = r2.rest;
274
274
  if (token === null)
@@ -276,24 +276,24 @@ export const parseSlashCopy = (input) => {
276
276
  }
277
277
  // FROM / TO keyword.
278
278
  let direction;
279
- if (token.toLowerCase() === 'from') {
280
- direction = 'from';
279
+ if (token.toLowerCase() === "from") {
280
+ direction = "from";
281
281
  }
282
- else if (token.toLowerCase() === 'to') {
283
- direction = 'to';
282
+ else if (token.toLowerCase() === "to") {
283
+ direction = "to";
284
284
  }
285
285
  else {
286
286
  return errAt(token);
287
287
  }
288
288
  // \copy (subquery) FROM is invalid — subqueries only make sense with TO.
289
- if (isSubquery && direction === 'from') {
289
+ if (isSubquery && direction === "from") {
290
290
  return {
291
291
  ok: false,
292
- error: 'cannot use COPY FROM with a (subquery) source',
292
+ error: "cannot use COPY FROM with a (subquery) source",
293
293
  };
294
294
  }
295
295
  // Filename / PROGRAM / STDIN / STDOUT / PSTDIN / PSTDOUT.
296
- let r3 = tokenize(rest, ';', "'", false);
296
+ let r3 = tokenize(rest, ";", "'", false);
297
297
  token = r3.token;
298
298
  rest = r3.rest;
299
299
  if (token === null)
@@ -302,22 +302,24 @@ export const parseSlashCopy = (input) => {
302
302
  let program = false;
303
303
  let psqlInOut = false;
304
304
  const lower = token.toLowerCase();
305
- if (lower === 'program') {
306
- r3 = tokenize(rest, ';', "'", false);
305
+ if (lower === "program") {
306
+ r3 = tokenize(rest, ";", "'", false);
307
307
  token = r3.token;
308
308
  rest = r3.rest;
309
309
  if (token === null)
310
310
  return errAt(null);
311
- if (!token.startsWith("'") || !token.endsWith("'") || token.length < 2) {
311
+ if (!token.startsWith("'") ||
312
+ !token.endsWith("'") ||
313
+ token.length < 2) {
312
314
  return errAt(token);
313
315
  }
314
316
  file = stripSingleQuotes(token);
315
317
  program = true;
316
318
  }
317
- else if (lower === 'stdin' || lower === 'stdout') {
319
+ else if (lower === "stdin" || lower === "stdout") {
318
320
  file = null;
319
321
  }
320
- else if (lower === 'pstdin' || lower === 'pstdout') {
322
+ else if (lower === "pstdin" || lower === "pstdout") {
321
323
  file = null;
322
324
  psqlInOut = true;
323
325
  }
@@ -351,8 +353,8 @@ export const parseSlashCopy = (input) => {
351
353
  * invisible to the server because that's what frontend-driven COPY is for.
352
354
  */
353
355
  const buildCopySql = (opts) => {
354
- const tail = opts.direction === 'from' ? ' FROM STDIN ' : ' TO STDOUT ';
355
- const after = opts.afterToFrom !== null ? opts.afterToFrom : '';
356
+ const tail = opts.direction === "from" ? " FROM STDIN " : " TO STDOUT ";
357
+ const after = opts.afterToFrom !== null ? opts.afterToFrom : "";
356
358
  return `COPY ${opts.beforeToFrom}${tail}${after}`.trimEnd();
357
359
  };
358
360
  /**
@@ -404,7 +406,7 @@ export const isCopyTextFormat = (afterToFrom) => {
404
406
  // stripped form will have collapsed quoted values to `''`.
405
407
  const m = /\bformat\s+(?:'([A-Za-z_]+)'|([A-Za-z_]+))/i.exec(afterToFrom);
406
408
  if (m) {
407
- return (m[1] ?? m[2]).toLowerCase() === 'text';
409
+ return (m[1] ?? m[2]).toLowerCase() === "text";
408
410
  }
409
411
  return true;
410
412
  };
@@ -437,7 +439,7 @@ export const isCopyBinaryFormat = (beforeToFrom, afterToFrom) => {
437
439
  // FORMAT value may be optionally single-quoted in WITH (FORMAT 'binary').
438
440
  const m = /\bformat\s+(?:'([A-Za-z_]+)'|([A-Za-z_]+))/i.exec(afterToFrom);
439
441
  if (m) {
440
- return (m[1] ?? m[2]).toLowerCase() === 'binary';
442
+ return (m[1] ?? m[2]).toLowerCase() === "binary";
441
443
  }
442
444
  return false;
443
445
  };
@@ -471,11 +473,11 @@ export const COPY_BINARY_SIGNATURE = Buffer.from([
471
473
  */
472
474
  export const validateCopyBinarySignature = (buf) => {
473
475
  if (buf.length < COPY_BINARY_SIGNATURE.length) {
474
- return 'missing COPY binary signature (input too short)';
476
+ return "missing COPY binary signature (input too short)";
475
477
  }
476
478
  for (let i = 0; i < COPY_BINARY_SIGNATURE.length; i++) {
477
479
  if (buf[i] !== COPY_BINARY_SIGNATURE[i]) {
478
- return 'COPY binary signature mismatch';
480
+ return "COPY binary signature mismatch";
479
481
  }
480
482
  }
481
483
  return null;
@@ -493,11 +495,11 @@ const parseCopyTagRows = (tag) => {
493
495
  return parseInt(m[1], 10);
494
496
  };
495
497
  const spawnProgram = (cmd, direction) => {
496
- const child = spawn('sh', ['-c', cmd], {
498
+ const child = spawn("sh", ["-c", cmd], {
497
499
  stdio: [
498
- direction === 'to' ? 'pipe' : 'inherit',
499
- direction === 'from' ? 'pipe' : 'inherit',
500
- 'inherit',
500
+ direction === "to" ? "pipe" : "inherit",
501
+ direction === "from" ? "pipe" : "inherit",
502
+ "inherit",
501
503
  ],
502
504
  });
503
505
  // Capture the program's terminal status so the caller can surface a nonzero
@@ -505,17 +507,17 @@ const spawnProgram = (cmd, direction) => {
505
507
  // `close` carries (code, signal); `error` fires when the spawn itself
506
508
  // failed (e.g. sh missing).
507
509
  const closed = new Promise((resolve) => {
508
- child.once('close', (code, signal) => {
510
+ child.once("close", (code, signal) => {
509
511
  resolve({ code, signal, error: null });
510
512
  });
511
- child.once('error', (error) => {
513
+ child.once("error", (error) => {
512
514
  resolve({ code: null, signal: null, error });
513
515
  });
514
516
  });
515
517
  return {
516
518
  child,
517
- readable: direction === 'from' ? child.stdout : null,
518
- writable: direction === 'to' ? child.stdin : null,
519
+ readable: direction === "from" ? child.stdout : null,
520
+ writable: direction === "to" ? child.stdin : null,
519
521
  closed,
520
522
  };
521
523
  };
@@ -589,9 +591,9 @@ const pumpStdinWithEofMarker = async (readable, copyIn) => {
589
591
  if (settled)
590
592
  return;
591
593
  settled = true;
592
- readable.removeListener('data', onData);
593
- readable.removeListener('end', onEnd);
594
- readable.removeListener('error', onError);
594
+ readable.removeListener("data", onData);
595
+ readable.removeListener("end", onEnd);
596
+ readable.removeListener("error", onError);
595
597
  run().then(() => {
596
598
  resolve(markerHit);
597
599
  }, (err) => {
@@ -615,7 +617,9 @@ const pumpStdinWithEofMarker = async (readable, copyIn) => {
615
617
  // across chunk boundaries and any non-UTF-8 client_encoding byte
616
618
  // (LATIN1/SJIS) into U+FFFD. stdin yields Buffers;
617
619
  // guard the rare string case without assuming a lossy re-encode.
618
- const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, 'utf8');
620
+ const buf = Buffer.isBuffer(chunk)
621
+ ? chunk
622
+ : Buffer.from(chunk, "utf8");
619
623
  tail = tail.length === 0 ? buf : Buffer.concat([tail, buf]);
620
624
  let nl = tail.indexOf(0x0a); // '\n'
621
625
  while (nl !== -1) {
@@ -638,9 +642,9 @@ const pumpStdinWithEofMarker = async (readable, copyIn) => {
638
642
  // Pause + remove listeners BEFORE unshifting so the post-marker
639
643
  // bytes aren't re-emitted into our own data handler.
640
644
  readable.pause();
641
- readable.removeListener('data', onData);
642
- readable.removeListener('end', onEnd);
643
- readable.removeListener('error', onError);
645
+ readable.removeListener("data", onData);
646
+ readable.removeListener("end", onEnd);
647
+ readable.removeListener("error", onError);
644
648
  if (leftover.length > 0) {
645
649
  readable.unshift(leftover);
646
650
  }
@@ -650,7 +654,9 @@ const pumpStdinWithEofMarker = async (readable, copyIn) => {
650
654
  .then(() => {
651
655
  resolve(true);
652
656
  }, (err) => {
653
- reject(err instanceof Error ? err : new Error(String(err)));
657
+ reject(err instanceof Error
658
+ ? err
659
+ : new Error(String(err)));
654
660
  });
655
661
  return;
656
662
  }
@@ -701,9 +707,9 @@ const pumpStdinWithEofMarker = async (readable, copyIn) => {
701
707
  throw err;
702
708
  });
703
709
  };
704
- readable.on('data', onData);
705
- readable.once('end', onEnd);
706
- readable.once('error', onError);
710
+ readable.on("data", onData);
711
+ readable.once("end", onEnd);
712
+ readable.once("error", onError);
707
713
  // Trigger flowing mode in case the readable is paused.
708
714
  readable.resume();
709
715
  });
@@ -737,11 +743,11 @@ export const doCopy = async (conn, opts) => {
737
743
  let fromStdin = false;
738
744
  /** Cleanup callbacks run in `finally`. */
739
745
  const cleanups = [];
740
- if (opts.direction === 'from') {
746
+ if (opts.direction === "from") {
741
747
  if (opts.file !== null) {
742
748
  if (opts.program) {
743
749
  try {
744
- program = spawnProgram(opts.file, 'from');
750
+ program = spawnProgram(opts.file, "from");
745
751
  }
746
752
  catch (err) {
747
753
  return failWith(`could not execute command "${opts.file}": ${err instanceof Error ? err.message : String(err)}`);
@@ -776,7 +782,7 @@ export const doCopy = async (conn, opts) => {
776
782
  resolve();
777
783
  return;
778
784
  }
779
- stream.once('close', () => {
785
+ stream.once("close", () => {
780
786
  resolve();
781
787
  });
782
788
  stream.destroy();
@@ -795,7 +801,7 @@ export const doCopy = async (conn, opts) => {
795
801
  if (opts.file !== null) {
796
802
  if (opts.program) {
797
803
  try {
798
- program = spawnProgram(opts.file, 'to');
804
+ program = spawnProgram(opts.file, "to");
799
805
  }
800
806
  catch (err) {
801
807
  return failWith(`could not execute command "${opts.file}": ${err instanceof Error ? err.message : String(err)}`);
@@ -815,7 +821,9 @@ export const doCopy = async (conn, opts) => {
815
821
  else {
816
822
  try {
817
823
  // Reject if the path exists and is a directory.
818
- const stat = await fsPromises.stat(opts.file).catch(() => null);
824
+ const stat = await fsPromises
825
+ .stat(opts.file)
826
+ .catch(() => null);
819
827
  if (stat?.isDirectory()) {
820
828
  return failWith(`${opts.file}: cannot copy from/to a directory`);
821
829
  }
@@ -826,7 +834,7 @@ export const doCopy = async (conn, opts) => {
826
834
  const stream = createWriteStream(opts.file);
827
835
  // Trap the async open/write error synchronously so it can't crash the
828
836
  // process; surfaced as a COPY failure after the drive.
829
- stream.once('error', (e) => {
837
+ stream.once("error", (e) => {
830
838
  fileWriteErrors.push(e);
831
839
  });
832
840
  writable = stream;
@@ -849,9 +857,9 @@ export const doCopy = async (conn, opts) => {
849
857
  // Drive the COPY.
850
858
  let tag = null;
851
859
  try {
852
- if (opts.direction === 'from') {
860
+ if (opts.direction === "from") {
853
861
  if (readable === null) {
854
- return failWith('no input stream for COPY FROM');
862
+ return failWith("no input stream for COPY FROM");
855
863
  }
856
864
  const copyIn = await conn.startCopyIn(sql);
857
865
  // STDIN honours the `\.` EOF marker for BOTH text and CSV (psql treats
@@ -869,7 +877,7 @@ export const doCopy = async (conn, opts) => {
869
877
  }
870
878
  else {
871
879
  if (writable === null) {
872
- return failWith('no output stream for COPY TO');
880
+ return failWith("no output stream for COPY TO");
873
881
  }
874
882
  await drainCopyTo(conn, sql, writable);
875
883
  // A deferred open()/write() failure on the output file: report it as a
@@ -887,7 +895,7 @@ export const doCopy = async (conn, opts) => {
887
895
  program.writable?.end();
888
896
  const exit = await program.closed;
889
897
  // A program is only spawned when opts.file holds the command string.
890
- const progErr = describeProgramExit(opts.file ?? '', exit);
898
+ const progErr = describeProgramExit(opts.file ?? "", exit);
891
899
  if (progErr !== null)
892
900
  throw new Error(progErr);
893
901
  }
@@ -917,7 +925,7 @@ export const doCopy = async (conn, opts) => {
917
925
  */
918
926
  const readLastCopyTag = (conn) => {
919
927
  const maybe = conn.lastCopyTag;
920
- if (typeof maybe === 'string')
928
+ if (typeof maybe === "string")
921
929
  return maybe;
922
930
  return null;
923
931
  };
@@ -931,13 +939,15 @@ const readLastCopyTag = (conn) => {
931
939
  * result-printing pipeline emits the tag.
932
940
  */
933
941
  export const cmdCopy = {
934
- name: 'copy',
935
- helpKey: 'copy',
942
+ name: "copy",
943
+ helpKey: "copy",
936
944
  async run(ctx) {
937
945
  if (!ctx.settings.db) {
938
- ctx.settings.lastErrorResult = { message: 'no connection to the server' };
939
- writeErr('\\copy: no connection to the server\n');
940
- return { status: 'error' };
946
+ ctx.settings.lastErrorResult = {
947
+ message: "no connection to the server",
948
+ };
949
+ writeErr("\\copy: no connection to the server\n");
950
+ return { status: "error" };
941
951
  }
942
952
  // COPY is not supported inside a \startpipeline ... \endpipeline block:
943
953
  // upstream libpq aborts the connection with this exact diagnostic and
@@ -959,25 +969,25 @@ export const cmdCopy = {
959
969
  catch {
960
970
  // best-effort; the connection may already be dead
961
971
  }
962
- return { status: 'error' };
972
+ return { status: "error" };
963
973
  }
964
974
  const raw = ctx.restOfLine();
965
975
  if (raw.trim().length === 0) {
966
- ctx.settings.lastErrorResult = { message: 'arguments required' };
967
- writeErr('\\copy: arguments required\n');
968
- return { status: 'error' };
976
+ ctx.settings.lastErrorResult = { message: "arguments required" };
977
+ writeErr("\\copy: arguments required\n");
978
+ return { status: "error" };
969
979
  }
970
980
  const parsed = parseSlashCopy(raw);
971
981
  if (!parsed.ok) {
972
982
  ctx.settings.lastErrorResult = { message: parsed.error };
973
983
  writeErr(`\\copy: ${parsed.error}\n`);
974
- return { status: 'error' };
984
+ return { status: "error" };
975
985
  }
976
986
  const result = await doCopy(ctx.settings.db, parsed.value);
977
987
  if (!result.ok) {
978
988
  ctx.settings.lastErrorResult = { message: result.error };
979
989
  writeErr(`\\copy: ${result.error}\n`);
980
- return { status: 'error' };
990
+ return { status: "error" };
981
991
  }
982
992
  // Print the upstream-style command tag (e.g. "COPY 17") so users see the
983
993
  // same summary as `psql`. If the connection didn't surface a tag, just
@@ -990,7 +1000,7 @@ export const cmdCopy = {
990
1000
  // print path, so the tag has nowhere to land. Mirror that here: only
991
1001
  // print when the destination is a file, a program, or when the COPY is
992
1002
  // a FROM (where the data flowed *into* the server, not out to stdout).
993
- const suppressTag = parsed.value.direction === 'to' &&
1003
+ const suppressTag = parsed.value.direction === "to" &&
994
1004
  parsed.value.file === null &&
995
1005
  !parsed.value.program;
996
1006
  if (!suppressTag) {
@@ -1002,10 +1012,10 @@ export const cmdCopy = {
1002
1012
  writeOut(`${result.tag}\n`);
1003
1013
  }
1004
1014
  else {
1005
- writeOut('COPY\n');
1015
+ writeOut("COPY\n");
1006
1016
  }
1007
1017
  }
1008
- return { status: 'ok' };
1018
+ return { status: "ok" };
1009
1019
  },
1010
1020
  };
1011
1021
  /**
@@ -1022,4 +1032,4 @@ export { buildCopySql, pumpStdinWithEofMarker };
1022
1032
  * tests can feed a `Buffer` to {@link doCopy} without re-implementing the
1023
1033
  * Readable shim.
1024
1034
  */
1025
- export const toBuffer = (s) => Buffer.from(s, 'utf8');
1035
+ export const toBuffer = (s) => Buffer.from(s, "utf8");