neonctl 2.28.0 → 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 (135) hide show
  1. package/README.md +2 -2
  2. package/dist/analytics.js +35 -33
  3. package/dist/api.js +34 -34
  4. package/dist/auth.js +50 -44
  5. package/dist/cli.js +2 -2
  6. package/dist/commands/auth.js +58 -52
  7. package/dist/commands/bootstrap.js +115 -157
  8. package/dist/commands/branches.js +154 -147
  9. package/dist/commands/bucket.js +124 -118
  10. package/dist/commands/checkout.js +49 -49
  11. package/dist/commands/config.js +212 -88
  12. package/dist/commands/connection_string.js +62 -62
  13. package/dist/commands/data_api.js +96 -96
  14. package/dist/commands/databases.js +23 -23
  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 +97 -98
  19. package/dist/commands/index.js +26 -26
  20. package/dist/commands/init.js +23 -22
  21. package/dist/commands/ip_allow.js +29 -29
  22. package/dist/commands/link.js +223 -166
  23. package/dist/commands/neon_auth.js +381 -363
  24. package/dist/commands/operations.js +11 -11
  25. package/dist/commands/orgs.js +8 -8
  26. package/dist/commands/projects.js +101 -99
  27. package/dist/commands/psql.js +31 -31
  28. package/dist/commands/roles.js +21 -21
  29. package/dist/commands/schema_diff.js +23 -23
  30. package/dist/commands/set_context.js +17 -17
  31. package/dist/commands/status.js +17 -17
  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 +23 -16
  37. package/dist/current_branch_fast_path.js +6 -6
  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 +19 -19
  45. package/dist/functions_api.js +10 -10
  46. package/dist/help.js +15 -15
  47. package/dist/index.js +94 -92
  48. package/dist/log.js +2 -2
  49. package/dist/pkg.js +5 -5
  50. package/dist/psql/cli.js +4 -2
  51. package/dist/psql/command/cmd_cond.js +61 -61
  52. package/dist/psql/command/cmd_connect.js +159 -154
  53. package/dist/psql/command/cmd_copy.js +107 -97
  54. package/dist/psql/command/cmd_describe.js +368 -363
  55. package/dist/psql/command/cmd_format.js +276 -263
  56. package/dist/psql/command/cmd_io.js +269 -263
  57. package/dist/psql/command/cmd_lo.js +74 -66
  58. package/dist/psql/command/cmd_meta.js +148 -148
  59. package/dist/psql/command/cmd_misc.js +17 -17
  60. package/dist/psql/command/cmd_pipeline.js +142 -135
  61. package/dist/psql/command/cmd_restrict.js +25 -25
  62. package/dist/psql/command/cmd_show.js +183 -168
  63. package/dist/psql/command/dispatch.js +26 -26
  64. package/dist/psql/command/shared.js +14 -14
  65. package/dist/psql/complete/filenames.js +16 -16
  66. package/dist/psql/complete/index.js +4 -4
  67. package/dist/psql/complete/matcher.js +33 -32
  68. package/dist/psql/complete/psqlVars.js +173 -173
  69. package/dist/psql/complete/queries.js +5 -3
  70. package/dist/psql/complete/rules.js +900 -863
  71. package/dist/psql/core/common.js +136 -133
  72. package/dist/psql/core/help.js +343 -343
  73. package/dist/psql/core/mainloop.js +160 -153
  74. package/dist/psql/core/prompt.js +126 -123
  75. package/dist/psql/core/settings.js +111 -111
  76. package/dist/psql/core/sqlHelp.js +150 -150
  77. package/dist/psql/core/startup.js +211 -205
  78. package/dist/psql/core/syncVars.js +14 -14
  79. package/dist/psql/core/variables.js +24 -24
  80. package/dist/psql/describe/formatters.js +302 -289
  81. package/dist/psql/describe/processNamePattern.js +28 -28
  82. package/dist/psql/describe/queries.js +656 -651
  83. package/dist/psql/index.js +436 -411
  84. package/dist/psql/io/history.js +36 -36
  85. package/dist/psql/io/input.js +15 -15
  86. package/dist/psql/io/lineEditor/buffer.js +27 -25
  87. package/dist/psql/io/lineEditor/complete.js +15 -15
  88. package/dist/psql/io/lineEditor/filename.js +22 -22
  89. package/dist/psql/io/lineEditor/index.js +65 -62
  90. package/dist/psql/io/lineEditor/keymap.js +325 -318
  91. package/dist/psql/io/lineEditor/vt100.js +60 -60
  92. package/dist/psql/io/pgpass.js +18 -18
  93. package/dist/psql/io/pgservice.js +14 -14
  94. package/dist/psql/io/psqlrc.js +46 -46
  95. package/dist/psql/print/aligned.js +175 -166
  96. package/dist/psql/print/asciidoc.js +51 -51
  97. package/dist/psql/print/crosstab.js +34 -31
  98. package/dist/psql/print/csv.js +25 -22
  99. package/dist/psql/print/html.js +54 -54
  100. package/dist/psql/print/json.js +12 -12
  101. package/dist/psql/print/latex.js +118 -118
  102. package/dist/psql/print/pager.js +28 -26
  103. package/dist/psql/print/troff.js +48 -48
  104. package/dist/psql/print/unaligned.js +15 -14
  105. package/dist/psql/print/units.js +17 -17
  106. package/dist/psql/scanner/slash.js +48 -46
  107. package/dist/psql/scanner/sql.js +88 -84
  108. package/dist/psql/scanner/stringutils.js +21 -17
  109. package/dist/psql/types/index.js +7 -7
  110. package/dist/psql/types/scanner.js +8 -8
  111. package/dist/psql/wire/connection.js +341 -327
  112. package/dist/psql/wire/copy.js +7 -7
  113. package/dist/psql/wire/pipeline.js +26 -24
  114. package/dist/psql/wire/protocol.js +102 -102
  115. package/dist/psql/wire/sasl.js +62 -62
  116. package/dist/psql/wire/tls.js +79 -73
  117. package/dist/storage_api.js +15 -15
  118. package/dist/test_utils/fixtures.js +34 -31
  119. package/dist/test_utils/oauth_server.js +5 -5
  120. package/dist/utils/api_enums.js +13 -13
  121. package/dist/utils/branch_notice.js +5 -5
  122. package/dist/utils/branch_picker.js +26 -26
  123. package/dist/utils/compute_units.js +4 -4
  124. package/dist/utils/enrichers.js +20 -15
  125. package/dist/utils/esbuild.js +28 -28
  126. package/dist/utils/formats.js +1 -1
  127. package/dist/utils/middlewares.js +3 -3
  128. package/dist/utils/package_manager.js +68 -0
  129. package/dist/utils/point_in_time.js +12 -12
  130. package/dist/utils/psql.js +30 -30
  131. package/dist/utils/string.js +2 -2
  132. package/dist/utils/ui.js +9 -9
  133. package/dist/utils/zip.js +1 -1
  134. package/dist/writer.js +17 -17
  135. package/package.json +6 -7
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from './units.js';
1
+ import { formatNumericLocale } from "./units.js";
2
2
  /**
3
3
  * Troff MS printer.
4
4
  *
@@ -57,10 +57,10 @@ import { formatNumericLocale } from './units.js';
57
57
  // INT2, INT4, INT8, FLOAT4, FLOAT8, NUMERIC, INTERVAL.
58
58
  const NUMERIC_OIDS = new Set([21, 23, 20, 700, 701, 1700, 1186]);
59
59
  export const troffMsPrinter = {
60
- format: 'troff-ms',
60
+ format: "troff-ms",
61
61
  printQuery(rs, opts, out) {
62
62
  const topt = opts.topt;
63
- if (topt.expanded === 'on') {
63
+ if (topt.expanded === "on") {
64
64
  return printExpanded(rs, opts, out);
65
65
  }
66
66
  return printFlat(rs, opts, out);
@@ -72,55 +72,55 @@ const printFlat = (rs, opts, out) => {
72
72
  const startTable = topt.startTable;
73
73
  const stopTable = topt.stopTable;
74
74
  const border = clampBorder(topt.border);
75
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
75
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
76
76
  const title = opts.title ?? topt.title;
77
77
  const footers = opts.footers ?? topt.footers;
78
78
  const headers = rs.fields.map((f) => f.name);
79
79
  const ncols = rs.fields.length;
80
- const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? 'r' : 'l');
80
+ const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
81
81
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
82
- let buf = '';
82
+ let buf = "";
83
83
  if (startTable) {
84
84
  if (!tuplesOnly && title) {
85
- buf += '.LP\n.DS C\n';
85
+ buf += ".LP\n.DS C\n";
86
86
  buf += escapeTroff(title);
87
- buf += '\n.DE\n';
87
+ buf += "\n.DE\n";
88
88
  }
89
- buf += '.LP\n.TS\n';
90
- buf += border === 2 ? 'center box;\n' : 'center;\n';
89
+ buf += ".LP\n.TS\n";
90
+ buf += border === 2 ? "center box;\n" : "center;\n";
91
91
  aligns.forEach((a, idx) => {
92
92
  buf += a;
93
93
  if (border > 0 && idx < ncols - 1)
94
- buf += ' | ';
94
+ buf += " | ";
95
95
  });
96
- buf += '.\n';
96
+ buf += ".\n";
97
97
  if (!tuplesOnly) {
98
98
  headers.forEach((h, idx) => {
99
99
  if (idx !== 0)
100
- buf += '\t';
101
- buf += '\\fI' + escapeTroff(h) + '\\fP';
100
+ buf += "\t";
101
+ buf += "\\fI" + escapeTroff(h) + "\\fP";
102
102
  });
103
- buf += '\n_\n';
103
+ buf += "\n_\n";
104
104
  }
105
105
  }
106
106
  cells.forEach((row) => {
107
107
  row.forEach((value, idx) => {
108
108
  buf += escapeTroff(value);
109
109
  if (idx === ncols - 1)
110
- buf += '\n';
110
+ buf += "\n";
111
111
  else
112
- buf += '\t';
112
+ buf += "\t";
113
113
  });
114
114
  });
115
115
  if (stopTable) {
116
- buf += '.TE\n.DS L\n';
116
+ buf += ".TE\n.DS L\n";
117
117
  if (!tuplesOnly) {
118
118
  const effective = effectiveFooters(rs, topt, footers);
119
119
  for (const f of effective) {
120
- buf += escapeTroff(f) + '\n';
120
+ buf += escapeTroff(f) + "\n";
121
121
  }
122
122
  }
123
- buf += '.DE\n';
123
+ buf += ".DE\n";
124
124
  }
125
125
  out.write(buf);
126
126
  return Promise.resolve();
@@ -131,28 +131,28 @@ const printExpanded = (rs, opts, out) => {
131
131
  const startTable = topt.startTable;
132
132
  const stopTable = topt.stopTable;
133
133
  const border = clampBorder(topt.border);
134
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
134
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
135
135
  const title = opts.title ?? topt.title;
136
136
  const footers = opts.footers ?? topt.footers;
137
137
  const headers = rs.fields.map((f) => f.name);
138
138
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
139
- let buf = '';
139
+ let buf = "";
140
140
  // currentFormat: 0 = none yet, 1 = "Record N" header (c s),
141
141
  // 2 = body (c l or c | l). Upstream uses the same tri-state to
142
142
  // decide when to emit `.T&` separators.
143
143
  let currentFormat = 0;
144
144
  if (startTable) {
145
145
  if (!tuplesOnly && title) {
146
- buf += '.LP\n.DS C\n';
146
+ buf += ".LP\n.DS C\n";
147
147
  buf += escapeTroff(title);
148
- buf += '\n.DE\n';
148
+ buf += "\n.DE\n";
149
149
  }
150
- buf += '.LP\n.TS\n';
151
- buf += border === 2 ? 'center box;\n' : 'center;\n';
150
+ buf += ".LP\n.TS\n";
151
+ buf += border === 2 ? "center box;\n" : "center;\n";
152
152
  // Under tuples-only, upstream emits a one-shot `c l;` body spec
153
153
  // here so each Record's first .T& block is omitted.
154
154
  if (tuplesOnly) {
155
- buf += 'c l;\n';
155
+ buf += "c l;\n";
156
156
  }
157
157
  }
158
158
  else {
@@ -164,41 +164,41 @@ const printExpanded = (rs, opts, out) => {
164
164
  if (!tuplesOnly) {
165
165
  if (currentFormat !== 1) {
166
166
  if (border === 2 && rowIdx > 0)
167
- buf += '_\n';
167
+ buf += "_\n";
168
168
  if (currentFormat !== 0)
169
- buf += '.T&\n';
170
- buf += 'c s.\n';
169
+ buf += ".T&\n";
170
+ buf += "c s.\n";
171
171
  currentFormat = 1;
172
172
  }
173
173
  buf += `\\fIRecord ${String(record)}\\fP\n`;
174
174
  record += 1;
175
175
  }
176
176
  if (border >= 1)
177
- buf += '_\n';
177
+ buf += "_\n";
178
178
  if (!tuplesOnly) {
179
179
  if (currentFormat !== 2) {
180
180
  if (currentFormat !== 0)
181
- buf += '.T&\n';
182
- buf += border !== 1 ? 'c l.\n' : 'c | l.\n';
181
+ buf += ".T&\n";
182
+ buf += border !== 1 ? "c l.\n" : "c | l.\n";
183
183
  currentFormat = 2;
184
184
  }
185
185
  }
186
186
  row.forEach((value, idx) => {
187
187
  buf += escapeTroff(headers[idx]);
188
- buf += '\t';
188
+ buf += "\t";
189
189
  buf += escapeTroff(value);
190
- buf += '\n';
190
+ buf += "\n";
191
191
  });
192
192
  });
193
193
  if (stopTable) {
194
- buf += '.TE\n.DS L\n';
194
+ buf += ".TE\n.DS L\n";
195
195
  // Expanded mode does NOT emit the default "(N rows)" footer.
196
196
  if (!tuplesOnly && footers && footers.length > 0) {
197
197
  for (const f of footers) {
198
- buf += escapeTroff(f) + '\n';
198
+ buf += escapeTroff(f) + "\n";
199
199
  }
200
200
  }
201
- buf += '.DE\n';
201
+ buf += ".DE\n";
202
202
  }
203
203
  out.write(buf);
204
204
  return Promise.resolve();
@@ -215,7 +215,7 @@ const effectiveFooters = (rs, topt, footers) => {
215
215
  return footers;
216
216
  if (topt.defaultFooter) {
217
217
  const n = rs.rows.length;
218
- return [`(${String(n)} ${n === 1 ? 'row' : 'rows'})`];
218
+ return [`(${String(n)} ${n === 1 ? "row" : "rows"})`];
219
219
  }
220
220
  return [];
221
221
  };
@@ -226,10 +226,10 @@ const escapeTroff = (input) => {
226
226
  // raw newlines inside a cell; that constraint is enforced upstream
227
227
  // by the caller, not here. (`renderCell` similarly never emits a raw
228
228
  // newline today.)
229
- let out = '';
229
+ let out = "";
230
230
  for (const ch of input) {
231
- if (ch === '\\')
232
- out += '\\(rs';
231
+ if (ch === "\\")
232
+ out += "\\(rs";
233
233
  else
234
234
  out += ch;
235
235
  }
@@ -238,20 +238,20 @@ const escapeTroff = (input) => {
238
238
  const renderCell = (cell, nullPrint, numericLocale) => {
239
239
  if (cell === null || cell === undefined)
240
240
  return nullPrint;
241
- if (typeof cell === 'string') {
241
+ if (typeof cell === "string") {
242
242
  return formatNumericLocale(cell, numericLocale);
243
243
  }
244
- if (typeof cell === 'number' || typeof cell === 'bigint') {
244
+ if (typeof cell === "number" || typeof cell === "bigint") {
245
245
  return formatNumericLocale(cell.toString(), numericLocale);
246
246
  }
247
- if (typeof cell === 'boolean')
248
- return cell ? 't' : 'f';
247
+ if (typeof cell === "boolean")
248
+ return cell ? "t" : "f";
249
249
  if (cell instanceof Date)
250
250
  return cell.toISOString();
251
251
  if (cell instanceof Uint8Array) {
252
- let hex = '\\x';
252
+ let hex = "\\x";
253
253
  for (const b of cell)
254
- hex += b.toString(16).padStart(2, '0');
254
+ hex += b.toString(16).padStart(2, "0");
255
255
  return hex;
256
256
  }
257
257
  return JSON.stringify(cell);
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from './units.js';
1
+ import { formatNumericLocale } from "./units.js";
2
2
  /**
3
3
  * Unaligned tabular printer.
4
4
  *
@@ -15,17 +15,17 @@ import { formatNumericLocale } from './units.js';
15
15
  * mode and `topt.defaultFooter` is true; same as upstream psql.
16
16
  */
17
17
  export const unalignedPrinter = {
18
- format: 'unaligned',
18
+ format: "unaligned",
19
19
  printQuery(rs, opts, out) {
20
20
  const topt = opts.topt;
21
- const fieldSep = topt.fieldSep !== '' ? topt.fieldSep : '|';
22
- const recordSep = topt.recordSep !== '' ? topt.recordSep : '\n';
23
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
24
- const expanded = topt.expanded === 'on';
21
+ const fieldSep = topt.fieldSep !== "" ? topt.fieldSep : "|";
22
+ const recordSep = topt.recordSep !== "" ? topt.recordSep : "\n";
23
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
24
+ const expanded = topt.expanded === "on";
25
25
  const tuplesOnly = topt.tuplesOnly;
26
26
  const headers = rs.fields.map((f) => f.name);
27
27
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
28
- let outBuf = '';
28
+ let outBuf = "";
29
29
  if (expanded) {
30
30
  // Vertical mode mirrors print.c `print_unaligned_vertical`: each
31
31
  // record gap is a DOUBLE recordSep, the title (if any) emits
@@ -82,7 +82,8 @@ export const unalignedPrinter = {
82
82
  opts.footers.length > 0;
83
83
  if (!tuplesOnly && topt.defaultFooter && !hasUserFooters) {
84
84
  const n = rs.rows.length;
85
- outBuf += `(${String(n)} ${n === 1 ? 'row' : 'rows'})` + recordSep;
85
+ outBuf +=
86
+ `(${String(n)} ${n === 1 ? "row" : "rows"})` + recordSep;
86
87
  }
87
88
  if (!tuplesOnly && hasUserFooters) {
88
89
  for (const footer of opts.footers ?? []) {
@@ -97,21 +98,21 @@ export const unalignedPrinter = {
97
98
  const renderCell = (cell, nullPrint, numericLocale) => {
98
99
  if (cell === null || cell === undefined)
99
100
  return nullPrint;
100
- if (typeof cell === 'string') {
101
+ if (typeof cell === "string") {
101
102
  return formatNumericLocale(cell, numericLocale);
102
103
  }
103
- if (typeof cell === 'number' || typeof cell === 'bigint') {
104
+ if (typeof cell === "number" || typeof cell === "bigint") {
104
105
  return formatNumericLocale(cell.toString(), numericLocale);
105
106
  }
106
- if (typeof cell === 'boolean')
107
- return cell ? 't' : 'f';
107
+ if (typeof cell === "boolean")
108
+ return cell ? "t" : "f";
108
109
  if (cell instanceof Date)
109
110
  return cell.toISOString();
110
111
  if (cell instanceof Uint8Array) {
111
112
  // Bytea -> hex escape, matching libpq's `\x` form.
112
- let hex = '\\x';
113
+ let hex = "\\x";
113
114
  for (const b of cell)
114
- hex += b.toString(16).padStart(2, '0');
115
+ hex += b.toString(16).padStart(2, "0");
115
116
  return hex;
116
117
  }
117
118
  return JSON.stringify(cell);
@@ -21,14 +21,14 @@ export const formatNumericLocale = (value, useLocale, locale) => {
21
21
  return value;
22
22
  if (!NUMERIC_RE.test(value))
23
23
  return value;
24
- const dotIdx = value.indexOf('.');
24
+ const dotIdx = value.indexOf(".");
25
25
  const fractionDigits = dotIdx === -1 ? 0 : value.length - dotIdx - 1;
26
26
  // Use BigInt for the integer part to avoid float precision loss on
27
27
  // very large integers; format the fractional component as a separate
28
28
  // number so we keep the exact digit count the input had.
29
- const negative = value.startsWith('-');
29
+ const negative = value.startsWith("-");
30
30
  const unsigned = negative ? value.slice(1) : value;
31
- const [intPart, fracPart = ''] = unsigned.split('.');
31
+ const [intPart, fracPart = ""] = unsigned.split(".");
32
32
  const intFormatted = new Intl.NumberFormat(locale, {
33
33
  useGrouping: true,
34
34
  maximumFractionDigits: 0,
@@ -39,25 +39,25 @@ export const formatNumericLocale = (value, useLocale, locale) => {
39
39
  // Discover this locale's decimal separator.
40
40
  const decimalSep = new Intl.NumberFormat(locale)
41
41
  .formatToParts(1.1)
42
- .find((p) => p.type === 'decimal')?.value ?? '.';
42
+ .find((p) => p.type === "decimal")?.value ?? ".";
43
43
  const out = `${intFormatted}${decimalSep}${fracPart}`;
44
44
  return negative ? `-${out}` : out;
45
45
  };
46
46
  const BYTE_UNITS = [
47
- { name: 'bytes', bits: 0 },
48
- { name: 'kB', bits: 10 },
49
- { name: 'MB', bits: 20 },
50
- { name: 'GB', bits: 30 },
51
- { name: 'TB', bits: 40 },
52
- { name: 'PB', bits: 50 },
47
+ { name: "bytes", bits: 0 },
48
+ { name: "kB", bits: 10 },
49
+ { name: "MB", bits: 20 },
50
+ { name: "GB", bits: 30 },
51
+ { name: "TB", bits: 40 },
52
+ { name: "PB", bits: 50 },
53
53
  ];
54
54
  const SI_UNITS = [
55
- { name: 'B', bits: 0 },
56
- { name: 'kB', bits: 10 },
57
- { name: 'MB', bits: 20 },
58
- { name: 'GB', bits: 30 },
59
- { name: 'TB', bits: 40 },
60
- { name: 'PB', bits: 50 },
55
+ { name: "B", bits: 0 },
56
+ { name: "kB", bits: 10 },
57
+ { name: "MB", bits: 20 },
58
+ { name: "GB", bits: 30 },
59
+ { name: "TB", bits: 40 },
60
+ { name: "PB", bits: 50 },
61
61
  ];
62
62
  /**
63
63
  * Pretty-print a byte count.
@@ -72,7 +72,7 @@ const SI_UNITS = [
72
72
  */
73
73
  export const formatByteSize = (bytes, opts) => {
74
74
  const units = opts?.si ? SI_UNITS : BYTE_UNITS;
75
- const sign = bytes < 0 ? '-' : '';
75
+ const sign = bytes < 0 ? "-" : "";
76
76
  let abs = Math.abs(bytes);
77
77
  // Bytes unit: render integer count.
78
78
  if (abs < 10 * 1024) {
@@ -58,9 +58,9 @@
58
58
  * octal `\ooo`, hex `\xhh`, and `\<other>` as a literal character. We
59
59
  * apply them in-line so the returned arg contains the decoded value.
60
60
  */
61
- import { execSync } from 'node:child_process';
62
- import { dequote } from './stringutils.js';
63
- const WHITESPACE = ' \t\n\r\f\v';
61
+ import { execSync } from "node:child_process";
62
+ import { dequote } from "./stringutils.js";
63
+ const WHITESPACE = " \t\n\r\f\v";
64
64
  const VARIABLE_CHAR_RE = /[A-Za-z0-9_\x80-\xff]/;
65
65
  const isVarChar = (c) => c !== undefined && VARIABLE_CHAR_RE.test(c);
66
66
  const isWhitespace = (c) => c !== undefined && WHITESPACE.includes(c);
@@ -74,12 +74,12 @@ const isWhitespace = (c) => c !== undefined && WHITESPACE.includes(c);
74
74
  */
75
75
  const quoteSqlLiteral = (value) => {
76
76
  let needsEscape = false;
77
- let inner = '';
77
+ let inner = "";
78
78
  for (const c of value) {
79
79
  if (c === "'")
80
80
  inner += "''";
81
- else if (c === '\\') {
82
- inner += '\\\\';
81
+ else if (c === "\\") {
82
+ inner += "\\\\";
83
83
  needsEscape = true;
84
84
  }
85
85
  else {
@@ -93,7 +93,7 @@ const quoteSqlLiteral = (value) => {
93
93
  * Wraps the value in `"…"` and doubles any embedded `"`.
94
94
  */
95
95
  const quoteSqlIdent = (value) => {
96
- let inner = '';
96
+ let inner = "";
97
97
  for (const c of value) {
98
98
  inner += c === '"' ? '""' : c;
99
99
  }
@@ -107,7 +107,7 @@ const quoteSqlIdent = (value) => {
107
107
  * (otherwise).
108
108
  */
109
109
  const dequoteDowncaseIdentifier = (str, downcase) => {
110
- let out = '';
110
+ let out = "";
111
111
  let inquotes = false;
112
112
  let i = 0;
113
113
  while (i < str.length) {
@@ -140,21 +140,21 @@ const dequoteDowncaseIdentifier = (str, downcase) => {
140
140
  const tryConsumeVarSubstitution = (s, i, varLookup) => {
141
141
  if (varLookup === undefined)
142
142
  return null;
143
- if (s[i] !== ':')
143
+ if (s[i] !== ":")
144
144
  return null;
145
145
  // :{?varname} — defined-variable test, emits literal TRUE / FALSE.
146
146
  // Mirrors upstream `psqlscanslash.l`'s `:\{\?{variable_char}+\}` rule
147
147
  // (calls `psqlscan_test_variable`). A malformed expression (missing
148
148
  // closing `}` or empty name) falls through to `null` so the caller emits
149
149
  // the literal `:` and continues.
150
- if (s[i + 1] === '{' && s[i + 2] === '?') {
150
+ if (s[i + 1] === "{" && s[i + 2] === "?") {
151
151
  let j = i + 3;
152
152
  while (j < s.length && isVarChar(s[j]))
153
153
  j++;
154
- if (j > i + 3 && s[j] === '}') {
154
+ if (j > i + 3 && s[j] === "}") {
155
155
  const name = s.slice(i + 3, j);
156
156
  const value = varLookup(name);
157
- return { end: j + 1, text: value !== undefined ? 'TRUE' : 'FALSE' };
157
+ return { end: j + 1, text: value !== undefined ? "TRUE" : "FALSE" };
158
158
  }
159
159
  return null;
160
160
  }
@@ -216,7 +216,7 @@ const tryConsumeVarSubstitution = (s, i, varLookup) => {
216
216
  * just past the closing quote).
217
217
  */
218
218
  const consumeSingleQuoted = (s, start) => {
219
- let out = '';
219
+ let out = "";
220
220
  let i = start;
221
221
  while (i < s.length) {
222
222
  const c = s[i];
@@ -228,41 +228,41 @@ const consumeSingleQuoted = (s, start) => {
228
228
  }
229
229
  return { end: i + 1, text: out };
230
230
  }
231
- if (c === '\\' && i + 1 < s.length) {
231
+ if (c === "\\" && i + 1 < s.length) {
232
232
  const next = s[i + 1];
233
- if (next === 'n') {
234
- out += '\n';
233
+ if (next === "n") {
234
+ out += "\n";
235
235
  i += 2;
236
236
  continue;
237
237
  }
238
- if (next === 't') {
239
- out += '\t';
238
+ if (next === "t") {
239
+ out += "\t";
240
240
  i += 2;
241
241
  continue;
242
242
  }
243
- if (next === 'b') {
244
- out += '\b';
243
+ if (next === "b") {
244
+ out += "\b";
245
245
  i += 2;
246
246
  continue;
247
247
  }
248
- if (next === 'r') {
249
- out += '\r';
248
+ if (next === "r") {
249
+ out += "\r";
250
250
  i += 2;
251
251
  continue;
252
252
  }
253
- if (next === 'f') {
254
- out += '\f';
253
+ if (next === "f") {
254
+ out += "\f";
255
255
  i += 2;
256
256
  continue;
257
257
  }
258
258
  // Octal: \ooo (1–3 digits)
259
- if (next >= '0' && next <= '7') {
259
+ if (next >= "0" && next <= "7") {
260
260
  const j = i + 1;
261
261
  let octEnd = j;
262
262
  while (octEnd < s.length &&
263
263
  octEnd - j < 3 &&
264
- s[octEnd] >= '0' &&
265
- s[octEnd] <= '7') {
264
+ s[octEnd] >= "0" &&
265
+ s[octEnd] <= "7") {
266
266
  octEnd++;
267
267
  }
268
268
  const code = parseInt(s.slice(j, octEnd), 8);
@@ -271,11 +271,13 @@ const consumeSingleQuoted = (s, start) => {
271
271
  continue;
272
272
  }
273
273
  // Hex: \xhh (1–2 digits)
274
- if (next === 'x') {
274
+ if (next === "x") {
275
275
  const j = i + 2;
276
276
  const hexRe = /[0-9a-fA-F]/;
277
277
  let hexEnd = j;
278
- while (hexEnd < s.length && hexEnd - j < 2 && hexRe.test(s[hexEnd])) {
278
+ while (hexEnd < s.length &&
279
+ hexEnd - j < 2 &&
280
+ hexRe.test(s[hexEnd])) {
279
281
  hexEnd++;
280
282
  }
281
283
  if (hexEnd > j) {
@@ -324,12 +326,12 @@ const consumeDoubleQuoted = (s, start) => {
324
326
  */
325
327
  export const BACKTICK_EXECUTOR = {
326
328
  current: (cmd) => execSync(cmd, {
327
- shell: '/bin/sh',
328
- encoding: 'utf8',
329
+ shell: "/bin/sh",
330
+ encoding: "utf8",
329
331
  // Children inherit the parent env but get no stdin pipe — matches
330
332
  // upstream's `popen(cmd, "r")` semantics. stderr passes through so
331
333
  // shell error output is visible to the user.
332
- stdio: ['ignore', 'pipe', 'inherit'],
334
+ stdio: ["ignore", "pipe", "inherit"],
333
335
  // Defensive cap: backtick output goes into a slash arg, so a runaway
334
336
  // command shouldn't be able to fill arbitrary memory.
335
337
  maxBuffer: 1 << 20,
@@ -348,12 +350,12 @@ export const BACKTICK_EXECUTOR = {
348
350
  */
349
351
  const runBacktickCommand = (cmd) => {
350
352
  if (cmd.length === 0)
351
- return '';
353
+ return "";
352
354
  try {
353
355
  const out = BACKTICK_EXECUTOR.current(cmd);
354
356
  // Trim a single trailing newline; preserve interior newlines so multi-line
355
357
  // output (e.g. `\set FOO `cat file``) lands as-is.
356
- return out.endsWith('\n') ? out.slice(0, -1) : out;
358
+ return out.endsWith("\n") ? out.slice(0, -1) : out;
357
359
  }
358
360
  catch (err) {
359
361
  const msg = err instanceof Error ? err.message : String(err);
@@ -361,7 +363,7 @@ const runBacktickCommand = (cmd) => {
361
363
  // have file/line context in the scanner; emit the prefix verbatim and
362
364
  // include the command for diagnosis.
363
365
  process.stderr.write(`psql: error: \\!: ${cmd}: ${msg}\n`);
364
- return '';
366
+ return "";
365
367
  }
366
368
  };
367
369
  /**
@@ -378,11 +380,11 @@ const runBacktickCommand = (cmd) => {
378
380
  * succeed; tests cover both well-formed and unterminated cases.
379
381
  */
380
382
  const consumeBackQuoted = (s, start, varLookup) => {
381
- let inner = '';
383
+ let inner = "";
382
384
  let i = start;
383
385
  while (i < s.length) {
384
386
  const c = s[i];
385
- if (c === '`') {
387
+ if (c === "`") {
386
388
  return { end: i + 1, text: runBacktickCommand(inner) };
387
389
  }
388
390
  const sub = tryConsumeVarSubstitution(s, i, varLookup);
@@ -410,7 +412,7 @@ const scanOneArg = (s, i, mode, varLookup) => {
410
412
  if (i >= s.length)
411
413
  return null;
412
414
  // filepipe special: a leading `|` flips into whole-line mode for this arg.
413
- if (mode === 'filepipe' && s[i] === '|') {
415
+ if (mode === "filepipe" && s[i] === "|") {
414
416
  const rest = s.slice(i);
415
417
  return { end: s.length, arg: rest };
416
418
  }
@@ -421,12 +423,12 @@ const scanOneArg = (s, i, mode, varLookup) => {
421
423
  // - a :var / :'var' / :"var" substitution
422
424
  // - a literal character (the catch-all)
423
425
  // We stop on whitespace or `\` (which begins the next slash command).
424
- let out = '';
426
+ let out = "";
425
427
  while (i < s.length) {
426
428
  const c = s[i];
427
429
  if (isWhitespace(c))
428
430
  break;
429
- if (c === '\\')
431
+ if (c === "\\")
430
432
  break;
431
433
  if (c === "'") {
432
434
  const r = consumeSingleQuoted(s, i + 1);
@@ -440,7 +442,7 @@ const scanOneArg = (s, i, mode, varLookup) => {
440
442
  i = r.end;
441
443
  continue;
442
444
  }
443
- if (c === '`') {
445
+ if (c === "`") {
444
446
  const r = consumeBackQuoted(s, i + 1, varLookup);
445
447
  out += r.text;
446
448
  i = r.end;
@@ -473,7 +475,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
473
475
  // Whole-line: return everything, with leading whitespace suppressed and a
474
476
  // single trailing newline (if any) preserved verbatim. Empty (or
475
477
  // whitespace-only) input yields no args.
476
- if (mode === 'whole-line') {
478
+ if (mode === "whole-line") {
477
479
  let start = 0;
478
480
  while (start < input.length && isWhitespace(input[start]))
479
481
  start++;
@@ -481,7 +483,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
481
483
  return [];
482
484
  return [input.slice(start)];
483
485
  }
484
- const effectiveLookup = mode === 'no-vars' ? undefined : varLookup;
486
+ const effectiveLookup = mode === "no-vars" ? undefined : varLookup;
485
487
  const args = [];
486
488
  let i = 0;
487
489
  while (i < input.length) {
@@ -491,10 +493,10 @@ export const scanSlashArgs = (input, mode, varLookup) => {
491
493
  let arg = result.arg;
492
494
  // sql-id / sql-id-keep-case post-process: collapse SQL-identifier
493
495
  // quoting, optionally downcasing unquoted letters.
494
- if (mode === 'sql-id') {
496
+ if (mode === "sql-id") {
495
497
  arg = dequoteDowncaseIdentifier(arg, true);
496
498
  }
497
- else if (mode === 'sql-id-keep-case') {
499
+ else if (mode === "sql-id-keep-case") {
498
500
  arg = dequoteDowncaseIdentifier(arg, false);
499
501
  }
500
502
  args.push(arg);
@@ -503,7 +505,7 @@ export const scanSlashArgs = (input, mode, varLookup) => {
503
505
  while (i < input.length && isWhitespace(input[i]))
504
506
  i++;
505
507
  // Stop on a `\` — start of the next backslash command.
506
- if (input[i] === '\\')
508
+ if (input[i] === "\\")
507
509
  break;
508
510
  }
509
511
  return args;