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
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from './units.js';
1
+ import { formatNumericLocale } from "./units.js";
2
2
  /**
3
3
  * AsciiDoc printer.
4
4
  *
@@ -49,10 +49,10 @@ import { formatNumericLocale } from './units.js';
49
49
  // INT2, INT4, INT8, FLOAT4, FLOAT8, NUMERIC, INTERVAL.
50
50
  const NUMERIC_OIDS = new Set([21, 23, 20, 700, 701, 1700, 1186]);
51
51
  export const asciidocPrinter = {
52
- format: 'asciidoc',
52
+ format: "asciidoc",
53
53
  printQuery(rs, opts, out) {
54
54
  const topt = opts.topt;
55
- if (topt.expanded === 'on') {
55
+ if (topt.expanded === "on") {
56
56
  return printExpanded(rs, opts, out);
57
57
  }
58
58
  return printFlat(rs, opts, out);
@@ -63,63 +63,63 @@ const printFlat = (rs, opts, out) => {
63
63
  const tuplesOnly = topt.tuplesOnly;
64
64
  const startTable = topt.startTable;
65
65
  const stopTable = topt.stopTable;
66
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
66
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
67
67
  const title = opts.title ?? topt.title;
68
68
  const footers = opts.footers ?? topt.footers;
69
69
  const headers = rs.fields.map((f) => f.name);
70
- const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? 'r' : 'l');
70
+ const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
71
71
  const ncols = rs.fields.length;
72
72
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
73
- let buf = '';
73
+ let buf = "";
74
74
  if (startTable) {
75
75
  // Force a paragraph break (upstream always emits a leading "\n").
76
- buf += '\n';
76
+ buf += "\n";
77
77
  if (!tuplesOnly && title) {
78
- buf += '.' + title + '\n';
78
+ buf += "." + title + "\n";
79
79
  }
80
- buf += '[';
80
+ buf += "[";
81
81
  if (!tuplesOnly)
82
82
  buf += 'options="header",';
83
83
  buf += 'cols="';
84
- buf += aligns.map((a) => (a === 'r' ? '>l' : '<l')).join(',');
84
+ buf += aligns.map((a) => (a === "r" ? ">l" : "<l")).join(",");
85
85
  buf += '"';
86
86
  buf += borderClause(topt.border);
87
- buf += ']\n';
88
- buf += '|====\n';
87
+ buf += "]\n";
88
+ buf += "|====\n";
89
89
  if (!tuplesOnly) {
90
90
  headers.forEach((h, idx) => {
91
91
  if (idx !== 0)
92
- buf += ' ';
93
- buf += '^l|' + escapeAsciidoc(h);
92
+ buf += " ";
93
+ buf += "^l|" + escapeAsciidoc(h);
94
94
  });
95
- buf += '\n';
95
+ buf += "\n";
96
96
  }
97
97
  }
98
98
  for (const row of cells) {
99
99
  row.forEach((value, idx) => {
100
100
  if (idx !== 0)
101
- buf += ' ';
102
- buf += '|';
101
+ buf += " ";
102
+ buf += "|";
103
103
  if (isWhitespaceOnly(value)) {
104
104
  // The upstream code emits a trailing space only for cells
105
105
  // that are not the last in their row.
106
106
  if (idx !== ncols - 1)
107
- buf += ' ';
107
+ buf += " ";
108
108
  }
109
109
  else {
110
110
  buf += escapeAsciidoc(value);
111
111
  }
112
112
  });
113
- buf += '\n';
113
+ buf += "\n";
114
114
  }
115
- buf += '|====\n';
115
+ buf += "|====\n";
116
116
  if (stopTable && !tuplesOnly) {
117
117
  const effective = effectiveFooters(rs, topt, footers);
118
118
  if (effective.length > 0) {
119
- buf += '\n....\n';
119
+ buf += "\n....\n";
120
120
  for (const f of effective)
121
- buf += f + '\n';
122
- buf += '....\n';
121
+ buf += f + "\n";
122
+ buf += "....\n";
123
123
  }
124
124
  }
125
125
  out.write(buf);
@@ -130,22 +130,22 @@ const printExpanded = (rs, opts, out) => {
130
130
  const tuplesOnly = topt.tuplesOnly;
131
131
  const startTable = topt.startTable;
132
132
  const stopTable = topt.stopTable;
133
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
133
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
134
134
  const title = opts.title ?? topt.title;
135
135
  const footers = opts.footers ?? topt.footers;
136
136
  const headers = rs.fields.map((f) => f.name);
137
- const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? 'r' : 'l');
137
+ const aligns = rs.fields.map((f) => NUMERIC_OIDS.has(f.dataTypeID) ? "r" : "l");
138
138
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
139
- let buf = '';
139
+ let buf = "";
140
140
  if (startTable) {
141
- buf += '\n';
141
+ buf += "\n";
142
142
  if (!tuplesOnly && title) {
143
- buf += '.' + title + '\n';
143
+ buf += "." + title + "\n";
144
144
  }
145
145
  buf += '[cols="h,l"';
146
146
  buf += borderClause(topt.border);
147
- buf += ']\n';
148
- buf += '|====\n';
147
+ buf += "]\n";
148
+ buf += "|====\n";
149
149
  }
150
150
  let record = topt.prior + 1;
151
151
  cells.forEach((row) => {
@@ -154,29 +154,29 @@ const printExpanded = (rs, opts, out) => {
154
154
  record += 1;
155
155
  }
156
156
  else {
157
- buf += '2+|\n';
157
+ buf += "2+|\n";
158
158
  }
159
159
  row.forEach((value, idx) => {
160
- buf += '<l|' + escapeAsciidoc(headers[idx]);
161
- buf += ' ' + (aligns[idx] === 'r' ? '>l' : '<l') + '|';
160
+ buf += "<l|" + escapeAsciidoc(headers[idx]);
161
+ buf += " " + (aligns[idx] === "r" ? ">l" : "<l") + "|";
162
162
  if (isWhitespaceOnly(value)) {
163
- buf += ' ';
163
+ buf += " ";
164
164
  }
165
165
  else {
166
166
  buf += escapeAsciidoc(value);
167
167
  }
168
- buf += '\n';
168
+ buf += "\n";
169
169
  });
170
170
  });
171
- buf += '|====\n';
171
+ buf += "|====\n";
172
172
  if (stopTable && !tuplesOnly) {
173
173
  // Expanded mode does NOT emit the default "(N rows)" footer —
174
174
  // only user-supplied footers (matches print_asciidoc_vertical).
175
175
  if (footers && footers.length > 0) {
176
- buf += '\n....\n';
176
+ buf += "\n....\n";
177
177
  for (const f of footers)
178
- buf += f + '\n';
179
- buf += '....\n';
178
+ buf += f + "\n";
179
+ buf += "....\n";
180
180
  }
181
181
  }
182
182
  out.write(buf);
@@ -191,7 +191,7 @@ const borderClause = (border) => {
191
191
  case 2:
192
192
  return ',frame="all",grid="all"';
193
193
  default:
194
- return '';
194
+ return "";
195
195
  }
196
196
  };
197
197
  const effectiveFooters = (rs, topt, footers) => {
@@ -199,7 +199,7 @@ const effectiveFooters = (rs, topt, footers) => {
199
199
  return footers;
200
200
  if (topt.defaultFooter) {
201
201
  const n = rs.rows.length;
202
- return [`(${String(n)} ${n === 1 ? 'row' : 'rows'})`];
202
+ return [`(${String(n)} ${n === 1 ? "row" : "rows"})`];
203
203
  }
204
204
  return [];
205
205
  };
@@ -207,7 +207,7 @@ const isWhitespaceOnly = (s) => {
207
207
  if (s.length === 0)
208
208
  return true;
209
209
  for (const ch of s) {
210
- if (ch !== ' ' && ch !== '\t')
210
+ if (ch !== " " && ch !== "\t")
211
211
  return false;
212
212
  }
213
213
  return true;
@@ -216,10 +216,10 @@ const escapeAsciidoc = (input) => {
216
216
  // Only `|` is structurally hostile (closes a cell). Newlines and
217
217
  // every other character pass through; AsciiDoc treats embedded `\n`
218
218
  // as a soft line break within a cell.
219
- let out = '';
219
+ let out = "";
220
220
  for (const ch of input) {
221
- if (ch === '|')
222
- out += '\\|';
221
+ if (ch === "|")
222
+ out += "\\|";
223
223
  else
224
224
  out += ch;
225
225
  }
@@ -228,20 +228,20 @@ const escapeAsciidoc = (input) => {
228
228
  const renderCell = (cell, nullPrint, numericLocale) => {
229
229
  if (cell === null || cell === undefined)
230
230
  return nullPrint;
231
- if (typeof cell === 'string') {
231
+ if (typeof cell === "string") {
232
232
  return formatNumericLocale(cell, numericLocale);
233
233
  }
234
- if (typeof cell === 'number' || typeof cell === 'bigint') {
234
+ if (typeof cell === "number" || typeof cell === "bigint") {
235
235
  return formatNumericLocale(cell.toString(), numericLocale);
236
236
  }
237
- if (typeof cell === 'boolean')
238
- return cell ? 't' : 'f';
237
+ if (typeof cell === "boolean")
238
+ return cell ? "t" : "f";
239
239
  if (cell instanceof Date)
240
240
  return cell.toISOString();
241
241
  if (cell instanceof Uint8Array) {
242
- let hex = '\\x';
242
+ let hex = "\\x";
243
243
  for (const b of cell)
244
- hex += b.toString(16).padStart(2, '0');
244
+ hex += b.toString(16).padStart(2, "0");
245
245
  return hex;
246
246
  }
247
247
  return JSON.stringify(cell);
@@ -34,11 +34,11 @@
34
34
  * - "maximum number of columns (1600) exceeded",
35
35
  * - "query result contains multiple data values for row \"…\", column \"…\"".
36
36
  */
37
- import { alignedPrinter } from './aligned.js';
37
+ import { alignedPrinter } from "./aligned.js";
38
38
  const DIGIT_RE = /^\d+$/;
39
39
  const SIGNED_DIGIT_RE = /^[+-]?\d+$/;
40
40
  const dequoteDowncase = (raw) => {
41
- let out = '';
41
+ let out = "";
42
42
  let inquotes = false;
43
43
  let hadQuotes = false;
44
44
  let i = 0;
@@ -62,7 +62,7 @@ const dequoteDowncase = (raw) => {
62
62
  };
63
63
  const indexOfColumn = (arg, fields, allowSign) => {
64
64
  // Numeric arg path: number type, or string that parses as integer.
65
- if (typeof arg === 'number') {
65
+ if (typeof arg === "number") {
66
66
  if (!Number.isInteger(arg)) {
67
67
  return {
68
68
  ok: false,
@@ -81,8 +81,8 @@ const indexOfColumn = (arg, fields, allowSign) => {
81
81
  }
82
82
  let str = arg.trim();
83
83
  let sign = 1;
84
- if (allowSign && (str.startsWith('+') || str.startsWith('-'))) {
85
- if (str.startsWith('-'))
84
+ if (allowSign && (str.startsWith("+") || str.startsWith("-"))) {
85
+ if (str.startsWith("-"))
86
86
  sign = -1;
87
87
  // Peel only when the rest looks like it could be a referencing token.
88
88
  const rest = str.slice(1);
@@ -90,7 +90,7 @@ const indexOfColumn = (arg, fields, allowSign) => {
90
90
  str = rest;
91
91
  }
92
92
  if (str.length === 0) {
93
- return { ok: false, error: 'empty column reference' };
93
+ return { ok: false, error: "empty column reference" };
94
94
  }
95
95
  if (DIGIT_RE.test(str)) {
96
96
  const n = parseInt(str, 10);
@@ -120,7 +120,10 @@ const indexOfColumn = (arg, fields, allowSign) => {
120
120
  // in the error message, not the raw arg with its leading
121
121
  // quotes. Matches `pg_log_error("ambiguous column name: \"%s\"")`
122
122
  // after the in-place `dequote_downcase_identifier(arg)` mutation.
123
- return { ok: false, error: `ambiguous column name: "${needle}"` };
123
+ return {
124
+ ok: false,
125
+ error: `ambiguous column name: "${needle}"`,
126
+ };
124
127
  }
125
128
  found = i;
126
129
  }
@@ -147,24 +150,24 @@ const indexOfColumn = (arg, fields, allowSign) => {
147
150
  * Null is represented by a sentinel so the printer can substitute
148
151
  * `popt.nullPrint`.
149
152
  */
150
- const NULL_SENTINEL = Symbol.for('neonctl.psql.crosstab.null');
153
+ const NULL_SENTINEL = Symbol.for("neonctl.psql.crosstab.null");
151
154
  const headerKey = (value) => {
152
155
  if (value === null || value === undefined)
153
156
  return NULL_SENTINEL;
154
- if (typeof value === 'string')
157
+ if (typeof value === "string")
155
158
  return `s:${value}`;
156
- if (typeof value === 'number')
159
+ if (typeof value === "number")
157
160
  return `n:${String(value)}`;
158
- if (typeof value === 'bigint')
161
+ if (typeof value === "bigint")
159
162
  return `i:${String(value)}`;
160
- if (typeof value === 'boolean')
163
+ if (typeof value === "boolean")
161
164
  return `b:${String(value)}`;
162
165
  if (value instanceof Date)
163
166
  return `d:${value.toISOString()}`;
164
167
  if (value instanceof Uint8Array) {
165
- let hex = 'x:';
168
+ let hex = "x:";
166
169
  for (const b of value)
167
- hex += b.toString(16).padStart(2, '0');
170
+ hex += b.toString(16).padStart(2, "0");
168
171
  return hex;
169
172
  }
170
173
  return `j:${JSON.stringify(value)}`;
@@ -177,19 +180,19 @@ const headerKey = (value) => {
177
180
  const headerDisplay = (value, nullPrint) => {
178
181
  if (value === null || value === undefined)
179
182
  return nullPrint;
180
- if (typeof value === 'string')
183
+ if (typeof value === "string")
181
184
  return value;
182
- if (typeof value === 'number' || typeof value === 'bigint') {
185
+ if (typeof value === "number" || typeof value === "bigint") {
183
186
  return value.toString();
184
187
  }
185
- if (typeof value === 'boolean')
186
- return value ? 't' : 'f';
188
+ if (typeof value === "boolean")
189
+ return value ? "t" : "f";
187
190
  if (value instanceof Date)
188
191
  return value.toISOString();
189
192
  if (value instanceof Uint8Array) {
190
- let hex = '\\x';
193
+ let hex = "\\x";
191
194
  for (const b of value)
192
- hex += b.toString(16).padStart(2, '0');
195
+ hex += b.toString(16).padStart(2, "0");
193
196
  return hex;
194
197
  }
195
198
  return JSON.stringify(value);
@@ -209,8 +212,8 @@ const cmpForSort = (a, b, sign) => {
209
212
  return 1; // null last
210
213
  if (bNull)
211
214
  return -1;
212
- const aStr = typeof a === 'string' ? a : headerDisplay(a, '');
213
- const bStr = typeof b === 'string' ? b : headerDisplay(b, '');
215
+ const aStr = typeof a === "string" ? a : headerDisplay(a, "");
216
+ const bStr = typeof b === "string" ? b : headerDisplay(b, "");
214
217
  if (SIGNED_DIGIT_RE.test(aStr) && SIGNED_DIGIT_RE.test(bStr)) {
215
218
  const an = parseInt(aStr, 10);
216
219
  const bn = parseInt(bStr, 10);
@@ -264,13 +267,13 @@ export const pivotResultSet = (rs, opts,
264
267
  * empty string by default). Callers that don't care can omit; tests
265
268
  * that drive the function in isolation can supply a sentinel.
266
269
  */
267
- nullPrint = '') => {
270
+ nullPrint = "") => {
268
271
  // (1) Field resolution. Upstream `crosstabview.c` requires PQnfields >= 3
269
272
  // unconditionally — pivoting two columns is degenerate (V × H with no
270
273
  // payload). Match the error text verbatim so the conformance test sees
271
274
  // the same line.
272
275
  if (rs.fields.length < 3) {
273
- return { error: 'query must return at least three columns' };
276
+ return { error: "query must return at least three columns" };
274
277
  }
275
278
  const colV = opts.colV ?? 1;
276
279
  const colH = opts.colH ?? 2;
@@ -282,7 +285,7 @@ nullPrint = '') => {
282
285
  return { error: hRes.error };
283
286
  if (vRes.index === hRes.index) {
284
287
  return {
285
- error: 'vertical and horizontal headers must be different columns',
288
+ error: "vertical and horizontal headers must be different columns",
286
289
  };
287
290
  }
288
291
  let dataIdx;
@@ -299,7 +302,7 @@ nullPrint = '') => {
299
302
  }
300
303
  }
301
304
  if (candidate < 0) {
302
- return { error: 'no data column available' };
305
+ return { error: "no data column available" };
303
306
  }
304
307
  dataIdx = candidate;
305
308
  }
@@ -349,7 +352,7 @@ nullPrint = '') => {
349
352
  // result wouldn't be printable in a reasonable width anyway, so
350
353
  // we mirror the cap and the error text verbatim.
351
354
  if (hHeaders.size >= 1600) {
352
- return { error: 'maximum number of columns (1600) exceeded' };
355
+ return { error: "maximum number of columns (1600) exceeded" };
353
356
  }
354
357
  hEntry = {
355
358
  key: hk,
@@ -361,8 +364,8 @@ nullPrint = '') => {
361
364
  }
362
365
  const cellKey = `${String(vEntry.rank)}|${String(hEntry.rank)}`;
363
366
  if (matrix.has(cellKey)) {
364
- const vDisp = headerDisplay(vEntry.value, '(null)');
365
- const hDisp = headerDisplay(hEntry.value, '(null)');
367
+ const vDisp = headerDisplay(vEntry.value, "(null)");
368
+ const hDisp = headerDisplay(hEntry.value, "(null)");
366
369
  return {
367
370
  error: `query result contains multiple data values for row "${vDisp}", column "${hDisp}"`,
368
371
  };
@@ -417,7 +420,7 @@ nullPrint = '') => {
417
420
  // Unfilled cells: empty string so the aligned printer emits nothing
418
421
  // (rather than substituting nullPrint). Matches upstream's
419
422
  // "non-initialized cells must be set to an empty string" pass.
420
- row[i + 1] = matrix.has(key) ? matrix.get(key) : '';
423
+ row[i + 1] = matrix.has(key) ? matrix.get(key) : "";
421
424
  }
422
425
  return row;
423
426
  });
@@ -453,7 +456,7 @@ export const printCrosstab = async (rs, opts, printOpts, out) => {
453
456
  // cell). Without this, the synthetic FieldDescription.name comes out
454
457
  // as the empty string and the column header is just whitespace.
455
458
  const result = pivotResultSet(rs, opts, printOpts.topt.nullPrint);
456
- if ('error' in result)
459
+ if ("error" in result)
457
460
  return result;
458
461
  await alignedPrinter.printQuery(result.rs, printOpts, out);
459
462
  return undefined;
@@ -1,4 +1,4 @@
1
- import { formatNumericLocale } from './units.js';
1
+ import { formatNumericLocale } from "./units.js";
2
2
  /**
3
3
  * RFC 4180 CSV printer.
4
4
  *
@@ -22,35 +22,38 @@ import { formatNumericLocale } from './units.js';
22
22
  * sentinel) when the separator is `\` or `.`, matching upstream.
23
23
  */
24
24
  export const csvPrinter = {
25
- format: 'csv',
25
+ format: "csv",
26
26
  printQuery(rs, opts, out) {
27
27
  const topt = opts.topt;
28
- const sep = topt.csvFieldSep !== undefined && topt.csvFieldSep !== ''
28
+ const sep = topt.csvFieldSep !== undefined && topt.csvFieldSep !== ""
29
29
  ? topt.csvFieldSep
30
- : ',';
31
- if (sep.length !== 1 || sep === '"' || sep === '\n' || sep === '\r') {
30
+ : ",";
31
+ if (sep.length !== 1 || sep === '"' || sep === "\n" || sep === "\r") {
32
32
  throw new RangeError(`csv_fieldsep must be a single character other than '"', '\\n', or '\\r' (got ${JSON.stringify(sep)})`);
33
33
  }
34
- const nullPrint = opts.nullPrint !== '' ? opts.nullPrint : topt.nullPrint;
35
- const expanded = topt.expanded === 'on';
34
+ const nullPrint = opts.nullPrint !== "" ? opts.nullPrint : topt.nullPrint;
35
+ const expanded = topt.expanded === "on";
36
36
  const headers = rs.fields.map((f) => f.name);
37
37
  const cells = rs.rows.map((row) => row.map((cell) => renderCell(cell, nullPrint, topt.numericLocale)));
38
- let outBuf = '';
38
+ let outBuf = "";
39
39
  if (expanded) {
40
40
  for (const row of cells) {
41
41
  row.forEach((value, colIdx) => {
42
42
  outBuf +=
43
- csvField(headers[colIdx], sep) + sep + csvField(value, sep) + '\n';
43
+ csvField(headers[colIdx], sep) +
44
+ sep +
45
+ csvField(value, sep) +
46
+ "\n";
44
47
  });
45
48
  }
46
49
  }
47
50
  else {
48
51
  // Header is gated on startTable && !tuplesOnly (cf. print.c).
49
52
  if (topt.startTable && !topt.tuplesOnly) {
50
- outBuf += headers.map((h) => csvField(h, sep)).join(sep) + '\n';
53
+ outBuf += headers.map((h) => csvField(h, sep)).join(sep) + "\n";
51
54
  }
52
55
  for (const row of cells) {
53
- outBuf += row.map((c) => csvField(c, sep)).join(sep) + '\n';
56
+ outBuf += row.map((c) => csvField(c, sep)).join(sep) + "\n";
54
57
  }
55
58
  }
56
59
  out.write(outBuf);
@@ -60,20 +63,20 @@ export const csvPrinter = {
60
63
  const renderCell = (cell, nullPrint, numericLocale) => {
61
64
  if (cell === null || cell === undefined)
62
65
  return nullPrint;
63
- if (typeof cell === 'string') {
66
+ if (typeof cell === "string") {
64
67
  return formatNumericLocale(cell, numericLocale);
65
68
  }
66
- if (typeof cell === 'number' || typeof cell === 'bigint') {
69
+ if (typeof cell === "number" || typeof cell === "bigint") {
67
70
  return formatNumericLocale(cell.toString(), numericLocale);
68
71
  }
69
- if (typeof cell === 'boolean')
70
- return cell ? 't' : 'f';
72
+ if (typeof cell === "boolean")
73
+ return cell ? "t" : "f";
71
74
  if (cell instanceof Date)
72
75
  return cell.toISOString();
73
76
  if (cell instanceof Uint8Array) {
74
- let hex = '\\x';
77
+ let hex = "\\x";
75
78
  for (const b of cell)
76
- hex += b.toString(16).padStart(2, '0');
79
+ hex += b.toString(16).padStart(2, "0");
77
80
  return hex;
78
81
  }
79
82
  return JSON.stringify(cell);
@@ -81,11 +84,11 @@ const renderCell = (cell, nullPrint, numericLocale) => {
81
84
  const csvField = (value, sep) => {
82
85
  const needsQuote = value.includes(sep) ||
83
86
  value.includes('"') ||
84
- value.includes('\n') ||
85
- value.includes('\r') ||
86
- value === '\\.' ||
87
- sep === '\\' ||
88
- sep === '.';
87
+ value.includes("\n") ||
88
+ value.includes("\r") ||
89
+ value === "\\." ||
90
+ sep === "\\" ||
91
+ sep === ".";
89
92
  if (!needsQuote)
90
93
  return value;
91
94
  return `"${value.replace(/"/g, '""')}"`;