neonctl 2.28.0 → 2.29.1

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 +71 -71
  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 +34 -34
  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
@@ -35,7 +35,7 @@
35
35
  * the line and trim, so `\sf myschema.foo ` round-trips cleanly without
36
36
  * splitting on the dot or whitespace inside parens.
37
37
  */
38
- import { writeErr, writeOut } from './shared.js';
38
+ import { writeErr, writeOut } from "./shared.js";
39
39
  // ---------------------------------------------------------------------------
40
40
  // Helpers
41
41
  // ---------------------------------------------------------------------------
@@ -48,7 +48,7 @@ import { writeErr, writeOut } from './shared.js';
48
48
  const errResult = (ctx, message) => {
49
49
  ctx.settings.lastErrorResult = { message };
50
50
  writeErr(`\\${ctx.cmdName}: ${message}\n`);
51
- return { status: 'error', errorWritten: true };
51
+ return { status: "error", errorWritten: true };
52
52
  };
53
53
  /**
54
54
  * Format a server error for stderr the way upstream's
@@ -57,10 +57,11 @@ const errResult = (ctx, message) => {
57
57
  * message fields (e.g. a wire-layer rejection).
58
58
  */
59
59
  const formatServerError = (err) => {
60
- if (err && typeof err === 'object') {
60
+ if (err && typeof err === "object") {
61
61
  const e = err;
62
- const sev = e.severity ?? 'ERROR';
63
- const msg = e.message ?? (err instanceof Error ? err.message : safeToString(err));
62
+ const sev = e.severity ?? "ERROR";
63
+ const msg = e.message ??
64
+ (err instanceof Error ? err.message : safeToString(err));
64
65
  return `${sev}: ${msg}`;
65
66
  }
66
67
  if (err instanceof Error)
@@ -75,14 +76,14 @@ const formatServerError = (err) => {
75
76
  */
76
77
  const safeToString = (v) => {
77
78
  if (v === null)
78
- return 'null';
79
+ return "null";
79
80
  if (v === undefined)
80
- return 'undefined';
81
- if (typeof v === 'string')
81
+ return "undefined";
82
+ if (typeof v === "string")
82
83
  return v;
83
- if (typeof v === 'number' ||
84
- typeof v === 'boolean' ||
85
- typeof v === 'bigint') {
84
+ if (typeof v === "number" ||
85
+ typeof v === "boolean" ||
86
+ typeof v === "bigint") {
86
87
  return String(v);
87
88
  }
88
89
  try {
@@ -101,17 +102,19 @@ const safeToString = (v) => {
101
102
  const queryErrResult = (ctx, err) => {
102
103
  const line = formatServerError(err);
103
104
  ctx.settings.lastErrorResult = {
104
- message: err && typeof err === 'object' && err.message
105
+ message: err &&
106
+ typeof err === "object" &&
107
+ err.message
105
108
  ? err.message
106
109
  : err instanceof Error
107
110
  ? err.message
108
111
  : safeToString(err),
109
112
  };
110
113
  writeErr(`${line}\n`);
111
- return { status: 'error', errorWritten: true };
114
+ return { status: "error", errorWritten: true };
112
115
  };
113
116
  const conn = (ctx) => ctx.settings.db;
114
- const noConn = (ctx) => errResult(ctx, 'no connection to the server');
117
+ const noConn = (ctx) => errResult(ctx, "no connection to the server");
115
118
  /**
116
119
  * Read the object descriptor as a whole-line argument with surrounding
117
120
  * whitespace AND trailing semicolons stripped. Returns `null` when no
@@ -129,7 +132,7 @@ const readObjDesc = (ctx) => {
129
132
  const raw = ctx.restOfLine();
130
133
  // Strip trailing whitespace and `;` (in any order, any count) so
131
134
  // `\sf foo(arg) ;; ` round-trips like vanilla psql.
132
- const trimmed = raw.replace(/[\s;]+$/, '').trimStart();
135
+ const trimmed = raw.replace(/[\s;]+$/, "").trimStart();
133
136
  return trimmed.length === 0 ? null : trimmed;
134
137
  };
135
138
  /**
@@ -139,7 +142,7 @@ const readObjDesc = (ctx) => {
139
142
  */
140
143
  const decodeShowSuffix = (cmdName, base) => {
141
144
  const tail = cmdName.slice(base.length);
142
- return { plus: tail.includes('+') };
145
+ return { plus: tail.includes("+") };
143
146
  };
144
147
  /**
145
148
  * Look up a function OID from `desc`. Mirrors upstream's
@@ -154,12 +157,15 @@ const decodeShowSuffix = (cmdName, base) => {
154
157
  * `escapeLiteral` to mirror libpq's `appendStringLiteralConn`.
155
158
  */
156
159
  const lookupFunctionOid = async (c, desc) => {
157
- const cast = desc.includes('(') ? 'regprocedure' : 'regproc';
160
+ const cast = desc.includes("(") ? "regprocedure" : "regproc";
158
161
  const sql = `SELECT ${c.escapeLiteral(desc)}::pg_catalog.${cast}::pg_catalog.oid`;
159
162
  try {
160
163
  const rs = await c.query(sql, []);
161
164
  if (rs.rows.length !== 1 || rs.rows[0][0] === null) {
162
- return { ok: false, err: new Error('object lookup returned no rows') };
165
+ return {
166
+ ok: false,
167
+ err: new Error("object lookup returned no rows"),
168
+ };
163
169
  }
164
170
  const raw = cellToString(rs.rows[0][0]);
165
171
  const oid = Number(raw);
@@ -186,7 +192,10 @@ const lookupRelationOid = async (c, desc) => {
186
192
  try {
187
193
  const rs = await c.query(sql, []);
188
194
  if (rs.rows.length !== 1 || rs.rows[0][0] === null) {
189
- return { ok: false, err: new Error('object lookup returned no rows') };
195
+ return {
196
+ ok: false,
197
+ err: new Error("object lookup returned no rows"),
198
+ };
190
199
  }
191
200
  const raw = cellToString(rs.rows[0][0]);
192
201
  const oid = Number(raw);
@@ -209,24 +218,24 @@ const lookupRelationOid = async (c, desc) => {
209
218
  */
210
219
  const cellToString = (v) => {
211
220
  if (v === null || v === undefined)
212
- return '';
213
- if (typeof v === 'string')
221
+ return "";
222
+ if (typeof v === "string")
214
223
  return v;
215
224
  if (Buffer.isBuffer(v))
216
- return v.toString('utf-8');
217
- if (typeof v === 'number' ||
218
- typeof v === 'boolean' ||
219
- typeof v === 'bigint') {
225
+ return v.toString("utf-8");
226
+ if (typeof v === "number" ||
227
+ typeof v === "boolean" ||
228
+ typeof v === "bigint") {
220
229
  return String(v);
221
230
  }
222
231
  // Non-primitive fallback: encode JSON so we never emit a stray
223
232
  // `[object Object]`. The wire layer hands us strings or nulls in
224
233
  // practice, so this branch is defensive only.
225
234
  try {
226
- return JSON.stringify(v) ?? '';
235
+ return JSON.stringify(v) ?? "";
227
236
  }
228
237
  catch {
229
- return '';
238
+ return "";
230
239
  }
231
240
  };
232
241
  /**
@@ -240,11 +249,14 @@ const getFunctionCreateCmd = async (c, oid) => {
240
249
  try {
241
250
  const rs = await c.query(sql, []);
242
251
  if (rs.rows.length !== 1) {
243
- return { ok: false, err: new Error('function definition not found') };
252
+ return {
253
+ ok: false,
254
+ err: new Error("function definition not found"),
255
+ };
244
256
  }
245
257
  let def = cellToString(rs.rows[0][0]);
246
- if (def.length > 0 && !def.endsWith('\n'))
247
- def += '\n';
258
+ if (def.length > 0 && !def.endsWith("\n"))
259
+ def += "\n";
248
260
  return { ok: true, def };
249
261
  }
250
262
  catch (err) {
@@ -274,83 +286,83 @@ const fmtId = (ident) => {
274
286
  * double-quote pair, which is still valid SQL.
275
287
  */
276
288
  const RESERVED_WORDS = new Set([
277
- 'all',
278
- 'analyse',
279
- 'analyze',
280
- 'and',
281
- 'any',
282
- 'array',
283
- 'as',
284
- 'asc',
285
- 'asymmetric',
286
- 'both',
287
- 'case',
288
- 'cast',
289
- 'check',
290
- 'collate',
291
- 'column',
292
- 'constraint',
293
- 'create',
294
- 'current_catalog',
295
- 'current_date',
296
- 'current_role',
297
- 'current_time',
298
- 'current_timestamp',
299
- 'current_user',
300
- 'default',
301
- 'deferrable',
302
- 'desc',
303
- 'distinct',
304
- 'do',
305
- 'else',
306
- 'end',
307
- 'except',
308
- 'false',
309
- 'fetch',
310
- 'for',
311
- 'foreign',
312
- 'from',
313
- 'grant',
314
- 'group',
315
- 'having',
316
- 'in',
317
- 'initially',
318
- 'intersect',
319
- 'into',
320
- 'lateral',
321
- 'leading',
322
- 'limit',
323
- 'localtime',
324
- 'localtimestamp',
325
- 'not',
326
- 'null',
327
- 'offset',
328
- 'on',
329
- 'only',
330
- 'or',
331
- 'order',
332
- 'placing',
333
- 'primary',
334
- 'references',
335
- 'returning',
336
- 'select',
337
- 'session_user',
338
- 'some',
339
- 'symmetric',
340
- 'table',
341
- 'then',
342
- 'to',
343
- 'trailing',
344
- 'true',
345
- 'union',
346
- 'unique',
347
- 'user',
348
- 'using',
349
- 'variadic',
350
- 'when',
351
- 'where',
352
- 'window',
353
- 'with',
289
+ "all",
290
+ "analyse",
291
+ "analyze",
292
+ "and",
293
+ "any",
294
+ "array",
295
+ "as",
296
+ "asc",
297
+ "asymmetric",
298
+ "both",
299
+ "case",
300
+ "cast",
301
+ "check",
302
+ "collate",
303
+ "column",
304
+ "constraint",
305
+ "create",
306
+ "current_catalog",
307
+ "current_date",
308
+ "current_role",
309
+ "current_time",
310
+ "current_timestamp",
311
+ "current_user",
312
+ "default",
313
+ "deferrable",
314
+ "desc",
315
+ "distinct",
316
+ "do",
317
+ "else",
318
+ "end",
319
+ "except",
320
+ "false",
321
+ "fetch",
322
+ "for",
323
+ "foreign",
324
+ "from",
325
+ "grant",
326
+ "group",
327
+ "having",
328
+ "in",
329
+ "initially",
330
+ "intersect",
331
+ "into",
332
+ "lateral",
333
+ "leading",
334
+ "limit",
335
+ "localtime",
336
+ "localtimestamp",
337
+ "not",
338
+ "null",
339
+ "offset",
340
+ "on",
341
+ "only",
342
+ "or",
343
+ "order",
344
+ "placing",
345
+ "primary",
346
+ "references",
347
+ "returning",
348
+ "select",
349
+ "session_user",
350
+ "some",
351
+ "symmetric",
352
+ "table",
353
+ "then",
354
+ "to",
355
+ "trailing",
356
+ "true",
357
+ "union",
358
+ "unique",
359
+ "user",
360
+ "using",
361
+ "variadic",
362
+ "when",
363
+ "where",
364
+ "window",
365
+ "with",
354
366
  ]);
355
367
  /**
356
368
  * Re-build a `CREATE OR REPLACE VIEW <schema>.<name>[ WITH (opts)] AS
@@ -360,8 +372,8 @@ const RESERVED_WORDS = new Set([
360
372
  * actually a view.
361
373
  */
362
374
  const getViewCreateCmd = async (c, oid) => {
363
- const ver = c.serverVersion >= 90400 ? 'modern' : 'legacy';
364
- const sql = ver === 'modern'
375
+ const ver = c.serverVersion >= 90400 ? "modern" : "legacy";
376
+ const sql = ver === "modern"
365
377
  ? `SELECT nspname, relname, relkind, ` +
366
378
  `pg_catalog.pg_get_viewdef(c.oid, true), ` +
367
379
  `pg_catalog.array_remove(pg_catalog.array_remove(c.reloptions,'check_option=local'),'check_option=cascaded') AS reloptions, ` +
@@ -385,7 +397,7 @@ const getViewCreateCmd = async (c, oid) => {
385
397
  return { ok: false, err };
386
398
  }
387
399
  if (rs.rows.length !== 1) {
388
- return { ok: false, err: new Error('view definition not found') };
400
+ return { ok: false, err: new Error("view definition not found") };
389
401
  }
390
402
  const row = rs.rows[0];
391
403
  const nspname = cellToString(row[0]);
@@ -394,33 +406,33 @@ const getViewCreateCmd = async (c, oid) => {
394
406
  const viewdef = cellToString(row[3]);
395
407
  const reloptions = row[4]; // may be string ("{a=b,c=d}") or null
396
408
  const checkoption = cellToString(row[5]);
397
- if (relkind !== 'v') {
409
+ if (relkind !== "v") {
398
410
  return {
399
411
  ok: false,
400
412
  err: new Error(`"${nspname}.${relname}" is not a view`),
401
413
  };
402
414
  }
403
- let out = 'CREATE OR REPLACE VIEW ';
415
+ let out = "CREATE OR REPLACE VIEW ";
404
416
  out += `${fmtId(nspname)}.${fmtId(relname)}`;
405
417
  // reloptions: postgres returns it as a text-mode array literal like
406
418
  // `{foo=bar,baz=qux}`; we only need to detect non-empty (different
407
419
  // from the literal `{}`) and split entries on `,` outside quotes.
408
420
  const reloptStr = reloptions === null ? null : cellToString(reloptions);
409
421
  if (reloptStr !== null && reloptStr.length > 2) {
410
- out += '\n WITH (';
422
+ out += "\n WITH (";
411
423
  out += renderReloptions(reloptStr);
412
- out += ')';
424
+ out += ")";
413
425
  }
414
426
  out += ` AS\n${viewdef}`;
415
427
  // Strip trailing semicolon from pg_get_viewdef.
416
- if (out.endsWith(';')) {
428
+ if (out.endsWith(";")) {
417
429
  out = out.slice(0, -1);
418
430
  }
419
- if (checkoption !== '') {
431
+ if (checkoption !== "") {
420
432
  out += `\n WITH ${checkoption} CHECK OPTION`;
421
433
  }
422
- if (!out.endsWith('\n'))
423
- out += '\n';
434
+ if (!out.endsWith("\n"))
435
+ out += "\n";
424
436
  return { ok: true, def: out };
425
437
  };
426
438
  /**
@@ -438,12 +450,12 @@ const getViewCreateCmd = async (c, oid) => {
438
450
  */
439
451
  const renderReloptions = (literal) => {
440
452
  // Strip surrounding `{}`.
441
- if (!literal.startsWith('{') || !literal.endsWith('}')) {
453
+ if (!literal.startsWith("{") || !literal.endsWith("}")) {
442
454
  return literal;
443
455
  }
444
456
  const inside = literal.slice(1, -1);
445
457
  if (inside.length === 0)
446
- return '';
458
+ return "";
447
459
  // Postgres array literals quote individual elements with `"…"` when
448
460
  // they contain commas or special chars. For reloptions on a view the
449
461
  // values are typically bare `key=value` strings, but we still need to
@@ -453,9 +465,12 @@ const renderReloptions = (literal) => {
453
465
  for (let entry of entries) {
454
466
  // unquote double-quoted entries
455
467
  if (entry.startsWith('"') && entry.endsWith('"')) {
456
- entry = entry.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
468
+ entry = entry
469
+ .slice(1, -1)
470
+ .replace(/\\"/g, '"')
471
+ .replace(/\\\\/g, "\\");
457
472
  }
458
- const eq = entry.indexOf('=');
473
+ const eq = entry.indexOf("=");
459
474
  if (eq < 0) {
460
475
  out.push(entry);
461
476
  continue;
@@ -470,17 +485,17 @@ const renderReloptions = (literal) => {
470
485
  out.push(`${key}='${value.replace(/'/g, "''")}'`);
471
486
  }
472
487
  }
473
- return out.join(', ');
488
+ return out.join(", ");
474
489
  };
475
490
  /** Split a Postgres text-mode array's inner content on top-level commas. */
476
491
  const splitArrayElems = (s) => {
477
492
  const out = [];
478
493
  let i = 0;
479
- let cur = '';
494
+ let cur = "";
480
495
  let inQuote = false;
481
496
  while (i < s.length) {
482
497
  const ch = s[i];
483
- if (ch === '\\' && i + 1 < s.length) {
498
+ if (ch === "\\" && i + 1 < s.length) {
484
499
  cur += s[i] + s[i + 1];
485
500
  i += 2;
486
501
  continue;
@@ -491,16 +506,16 @@ const splitArrayElems = (s) => {
491
506
  i++;
492
507
  continue;
493
508
  }
494
- if (ch === ',' && !inQuote) {
509
+ if (ch === "," && !inQuote) {
495
510
  out.push(cur);
496
- cur = '';
511
+ cur = "";
497
512
  i++;
498
513
  continue;
499
514
  }
500
515
  cur += ch;
501
516
  i++;
502
517
  }
503
- if (cur.length > 0 || s.endsWith(','))
518
+ if (cur.length > 0 || s.endsWith(","))
504
519
  out.push(cur);
505
520
  return out;
506
521
  };
@@ -522,12 +537,12 @@ const writeWithLineNumbers = (buf, isFunc, out) => {
522
537
  let i = 0;
523
538
  while (i < buf.length) {
524
539
  // Find end-of-line.
525
- const eol = buf.indexOf('\n', i);
540
+ const eol = buf.indexOf("\n", i);
526
541
  const line = eol === -1 ? buf.slice(i) : buf.slice(i, eol);
527
542
  if (inHeader &&
528
- (line.startsWith('AS ') ||
529
- line.startsWith('BEGIN ') ||
530
- line.startsWith('RETURN '))) {
543
+ (line.startsWith("AS ") ||
544
+ line.startsWith("BEGIN ") ||
545
+ line.startsWith("RETURN "))) {
531
546
  inHeader = false;
532
547
  }
533
548
  if (!inHeader)
@@ -538,7 +553,7 @@ const writeWithLineNumbers = (buf, isFunc, out) => {
538
553
  else {
539
554
  // %-7d → left-justified, padded to 7. Then literal space, then line.
540
555
  const numStr = String(lineno);
541
- const pad = numStr.length >= 7 ? '' : ' '.repeat(7 - numStr.length);
556
+ const pad = numStr.length >= 7 ? "" : " ".repeat(7 - numStr.length);
542
557
  out(`${numStr}${pad} ${line}\n`);
543
558
  }
544
559
  if (eol === -1)
@@ -570,7 +585,7 @@ const runShowFunction = async (ctx, cmdName, base) => {
570
585
  const { plus } = decodeShowSuffix(cmdName, base);
571
586
  const desc = readObjDesc(ctx);
572
587
  if (desc === null) {
573
- return errResult(ctx, 'function name is required');
588
+ return errResult(ctx, "function name is required");
574
589
  }
575
590
  const oidLookup = await lookupFunctionOid(c, desc);
576
591
  if (!oidLookup.ok)
@@ -579,7 +594,7 @@ const runShowFunction = async (ctx, cmdName, base) => {
579
594
  if (!defLookup.ok)
580
595
  return queryErrResult(ctx, defLookup.err);
581
596
  emitDefinition(defLookup.def, plus, /*isFunc=*/ true);
582
- return { status: 'ok' };
597
+ return { status: "ok" };
583
598
  };
584
599
  const runShowView = async (ctx, cmdName, base) => {
585
600
  const c = conn(ctx);
@@ -588,7 +603,7 @@ const runShowView = async (ctx, cmdName, base) => {
588
603
  const { plus } = decodeShowSuffix(cmdName, base);
589
604
  const desc = readObjDesc(ctx);
590
605
  if (desc === null) {
591
- return errResult(ctx, 'view name is required');
606
+ return errResult(ctx, "view name is required");
592
607
  }
593
608
  const oidLookup = await lookupRelationOid(c, desc);
594
609
  if (!oidLookup.ok)
@@ -597,38 +612,38 @@ const runShowView = async (ctx, cmdName, base) => {
597
612
  if (!defLookup.ok)
598
613
  return queryErrResult(ctx, defLookup.err);
599
614
  emitDefinition(defLookup.def, plus, /*isFunc=*/ false);
600
- return { status: 'ok' };
615
+ return { status: "ok" };
601
616
  };
602
617
  // ---------------------------------------------------------------------------
603
618
  // BackslashCmdSpec exports
604
619
  // ---------------------------------------------------------------------------
605
620
  /** `\sf [+] FUNCNAME` — show function source. */
606
621
  export const cmdShowFunction = {
607
- name: 'sf',
608
- argMode: 'whole-line',
609
- helpKey: 'sf',
610
- run: (ctx) => runShowFunction(ctx, ctx.cmdName, 'sf'),
622
+ name: "sf",
623
+ argMode: "whole-line",
624
+ helpKey: "sf",
625
+ run: (ctx) => runShowFunction(ctx, ctx.cmdName, "sf"),
611
626
  };
612
627
  /** `\sf+ FUNCNAME` — show function source with line numbers. */
613
628
  export const cmdShowFunctionPlus = {
614
- name: 'sf+',
615
- argMode: 'whole-line',
616
- helpKey: 'sf',
617
- run: (ctx) => runShowFunction(ctx, ctx.cmdName, 'sf'),
629
+ name: "sf+",
630
+ argMode: "whole-line",
631
+ helpKey: "sf",
632
+ run: (ctx) => runShowFunction(ctx, ctx.cmdName, "sf"),
618
633
  };
619
634
  /** `\sv [+] VIEWNAME` — show view source. */
620
635
  export const cmdShowView = {
621
- name: 'sv',
622
- argMode: 'whole-line',
623
- helpKey: 'sv',
624
- run: (ctx) => runShowView(ctx, ctx.cmdName, 'sv'),
636
+ name: "sv",
637
+ argMode: "whole-line",
638
+ helpKey: "sv",
639
+ run: (ctx) => runShowView(ctx, ctx.cmdName, "sv"),
625
640
  };
626
641
  /** `\sv+ VIEWNAME` — show view source with line numbers. */
627
642
  export const cmdShowViewPlus = {
628
- name: 'sv+',
629
- argMode: 'whole-line',
630
- helpKey: 'sv',
631
- run: (ctx) => runShowView(ctx, ctx.cmdName, 'sv'),
643
+ name: "sv+",
644
+ argMode: "whole-line",
645
+ helpKey: "sv",
646
+ run: (ctx) => runShowView(ctx, ctx.cmdName, "sv"),
632
647
  };
633
648
  /**
634
649
  * `\ef [+] [FUNCNAME [LINE]]` — upstream opens `$EDITOR` on the function's
@@ -638,17 +653,17 @@ export const cmdShowViewPlus = {
638
653
  * with a hint pointing at `\sf`.
639
654
  */
640
655
  export const cmdEditFunction = {
641
- name: 'ef',
642
- argMode: 'whole-line',
643
- helpKey: 'ef',
656
+ name: "ef",
657
+ argMode: "whole-line",
658
+ helpKey: "ef",
644
659
  async run(ctx) {
645
660
  const c = conn(ctx);
646
661
  if (!c)
647
662
  return noConn(ctx);
648
- const { plus } = decodeShowSuffix(ctx.cmdName, 'ef');
663
+ const { plus } = decodeShowSuffix(ctx.cmdName, "ef");
649
664
  const desc = readObjDesc(ctx);
650
665
  if (desc === null) {
651
- return errResult(ctx, 'editing not supported in embedded psql; supply a name to display the source');
666
+ return errResult(ctx, "editing not supported in embedded psql; supply a name to display the source");
652
667
  }
653
668
  // Strip a possible trailing LINE number (upstream behaviour for \ef
654
669
  // FUNCNAME LINE — the editor opens at that line; we just discard it).
@@ -660,24 +675,24 @@ export const cmdEditFunction = {
660
675
  if (!defLookup.ok)
661
676
  return queryErrResult(ctx, defLookup.err);
662
677
  emitDefinition(defLookup.def, plus, /*isFunc=*/ true);
663
- return { status: 'ok' };
678
+ return { status: "ok" };
664
679
  },
665
680
  };
666
681
  /**
667
682
  * `\ev [+] [VIEWNAME [LINE]]` — same contract as `\ef` but for views.
668
683
  */
669
684
  export const cmdEditView = {
670
- name: 'ev',
671
- argMode: 'whole-line',
672
- helpKey: 'ev',
685
+ name: "ev",
686
+ argMode: "whole-line",
687
+ helpKey: "ev",
673
688
  async run(ctx) {
674
689
  const c = conn(ctx);
675
690
  if (!c)
676
691
  return noConn(ctx);
677
- const { plus } = decodeShowSuffix(ctx.cmdName, 'ev');
692
+ const { plus } = decodeShowSuffix(ctx.cmdName, "ev");
678
693
  const desc = readObjDesc(ctx);
679
694
  if (desc === null) {
680
- return errResult(ctx, 'editing not supported in embedded psql; supply a name to display the source');
695
+ return errResult(ctx, "editing not supported in embedded psql; supply a name to display the source");
681
696
  }
682
697
  const objDesc = stripTrailingLine(desc);
683
698
  const oidLookup = await lookupRelationOid(c, objDesc);
@@ -687,7 +702,7 @@ export const cmdEditView = {
687
702
  if (!defLookup.ok)
688
703
  return queryErrResult(ctx, defLookup.err);
689
704
  emitDefinition(defLookup.def, plus, /*isFunc=*/ false);
690
- return { status: 'ok' };
705
+ return { status: "ok" };
691
706
  },
692
707
  };
693
708
  /**
@@ -697,11 +712,11 @@ export const cmdEditView = {
697
712
  */
698
713
  export const cmdEditFunctionPlus = {
699
714
  ...cmdEditFunction,
700
- name: 'ef+',
715
+ name: "ef+",
701
716
  };
702
717
  export const cmdEditViewPlus = {
703
718
  ...cmdEditView,
704
- name: 'ev+',
719
+ name: "ev+",
705
720
  };
706
721
  /**
707
722
  * Strip a trailing LINE number from an object descriptor, matching
@@ -728,7 +743,7 @@ const stripTrailingLine = (desc) => {
728
743
  if (i <= 0)
729
744
  return desc;
730
745
  const sep = desc[i];
731
- if (!(/\s/.test(sep) || sep === ')'))
746
+ if (!(/\s/.test(sep) || sep === ")"))
732
747
  return desc;
733
748
  return desc.slice(0, i + 1).trimEnd();
734
749
  };