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
package/README.md CHANGED
@@ -361,7 +361,7 @@ neonctl env pull
361
361
  neonctl env pull --branch preview --file .env.preview
362
362
  ```
363
363
 
364
- If you'd rather not keep env vars on disk, inject them at runtime instead with `neon-env run -- <your dev command>` (from `@neondatabase/env`) or `neonctl dev`, and pass `--no-env-pull` to `link` / `checkout`.
364
+ If you'd rather not keep env vars on disk, inject them at runtime instead with `neon-env run -- <your dev command>` (from `@neon/env`) or `neonctl dev`, and pass `--no-env-pull` to `link` / `checkout`.
365
365
 
366
366
  **Where `.neon` lives**: `link` writes `.neon` into the **current working directory** by default. If an existing `.neon` is found in any parent directory, that file is reused — so commands run from a sub-directory of a linked project still pick up the project's context. To pin the location explicitly, pass `--context-file <path>`.
367
367
 
@@ -373,7 +373,7 @@ Describe a branch's desired state in a `neon.ts` policy and reconcile it from th
373
373
 
374
374
  ```ts
375
375
  // neon.ts
376
- import { defineConfig } from '@neondatabase/config/v1';
376
+ import { defineConfig } from '@neon/config/v1';
377
377
 
378
378
  export default defineConfig({
379
379
  // Static: what exists on every branch (drives the typed env).
package/dist/analytics.js CHANGED
@@ -1,22 +1,22 @@
1
- import { readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { Analytics } from '@segment/analytics-node';
4
- import { CREDENTIALS_FILE } from './config.js';
5
- import { isCurrentBranchProbe } from './context.js';
6
- import { getGithubEnvVars, isCi } from './env.js';
7
- import { log } from './log.js';
8
- import pkg from './pkg.js';
9
- import { getApiClient, isNeonApiError } from './api.js';
10
- const WRITE_KEY = '3SQXn5ejjXWLEJ8xU2PRYhAotLtTaeeV';
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Analytics } from "@segment/analytics-node";
4
+ import { getApiClient, isNeonApiError } from "./api.js";
5
+ import { CREDENTIALS_FILE } from "./config.js";
6
+ import { isCurrentBranchProbe } from "./context.js";
7
+ import { getGithubEnvVars, isCi } from "./env.js";
8
+ import { log } from "./log.js";
9
+ import pkg from "./pkg.js";
10
+ const WRITE_KEY = "3SQXn5ejjXWLEJ8xU2PRYhAotLtTaeeV";
11
11
  /**
12
12
  * Raw-argv fallback for the offline `--current-branch` probe. The init
13
13
  * middleware runs before validation, where the parsed `currentBranch` flag may
14
14
  * not be populated yet, so we also scan `process.argv` directly to be safe.
15
15
  */
16
- const hasCurrentBranchArgv = () => process.argv.includes('--current-branch');
16
+ const hasCurrentBranchArgv = () => process.argv.includes("--current-branch");
17
17
  let client;
18
18
  let clientInitialized = false;
19
- let userId = '';
19
+ let userId = "";
20
20
  /**
21
21
  * Phase 1: Run before validation so the Segment client exists if any
22
22
  * middleware (e.g. auth) fails. Enables sendError() in the fail handler.
@@ -36,11 +36,11 @@ export const initAnalyticsClientMiddleware = (args) => {
36
36
  clientInitialized = true;
37
37
  client = new Analytics({
38
38
  writeKey: WRITE_KEY,
39
- host: 'https://track.neon.tech',
39
+ host: "https://track.neon.tech",
40
40
  });
41
- log.debug('Initialized CLI analytics client');
41
+ log.debug("Initialized CLI analytics client");
42
42
  client.identify({
43
- userId: 'anonymous',
43
+ userId: "anonymous",
44
44
  });
45
45
  };
46
46
  /**
@@ -56,11 +56,13 @@ export const analyticsMiddleware = async (args) => {
56
56
  }
57
57
  try {
58
58
  const credentialsPath = join(args.configDir, CREDENTIALS_FILE);
59
- const credentials = readFileSync(credentialsPath, { encoding: 'utf-8' });
59
+ const credentials = readFileSync(credentialsPath, {
60
+ encoding: "utf-8",
61
+ });
60
62
  userId = JSON.parse(credentials).user_id;
61
63
  }
62
64
  catch (err) {
63
- log.debug('Failed to read credentials file', err);
65
+ log.debug("Failed to read credentials file", err);
64
66
  }
65
67
  try {
66
68
  if (args.apiKey) {
@@ -75,25 +77,25 @@ export const analyticsMiddleware = async (args) => {
75
77
  args.authMethod = authDetails.auth_method;
76
78
  args.authData = authDetails.auth_data;
77
79
  // Get user id if not org api key
78
- if (!userId && authDetails.auth_method !== 'api_key_org') {
80
+ if (!userId && authDetails.auth_method !== "api_key_org") {
79
81
  const resp = await apiClient?.getCurrentUserInfo?.();
80
82
  userId = resp?.data?.id;
81
83
  }
82
84
  }
83
85
  else {
84
86
  args.accountId = userId;
85
- args.authMethod = 'oauth';
87
+ args.authMethod = "oauth";
86
88
  }
87
89
  }
88
90
  catch (err) {
89
- log.debug('Failed to get user id from api', err);
91
+ log.debug("Failed to get user id from api", err);
90
92
  }
91
93
  client.identify({
92
- userId: userId?.toString() ?? 'anonymous',
94
+ userId: userId?.toString() ?? "anonymous",
93
95
  });
94
96
  client.track({
95
- userId: userId || 'anonymous',
96
- event: 'CLI Started',
97
+ userId: userId || "anonymous",
98
+ event: "CLI Started",
97
99
  properties: getAnalyticsEventProperties(args),
98
100
  context: {
99
101
  direct: true,
@@ -102,12 +104,12 @@ export const analyticsMiddleware = async (args) => {
102
104
  };
103
105
  export const closeAnalytics = async (opts) => {
104
106
  if (client) {
105
- log.debug('Flushing CLI analytics');
107
+ log.debug("Flushing CLI analytics");
106
108
  // `timeout` bounds how long we wait for in-flight events to flush so a
107
109
  // slow / unreachable track.neon.tech can't hang a short-lived command
108
110
  // (e.g. the psql launch path, which flushes here before process.exit).
109
111
  await client.closeAndFlush(opts);
110
- log.debug('Flushed CLI analytics');
112
+ log.debug("Flushed CLI analytics");
111
113
  }
112
114
  };
113
115
  export const sendError = (err, errCode) => {
@@ -115,13 +117,13 @@ export const sendError = (err, errCode) => {
115
117
  return;
116
118
  }
117
119
  const apiError = isNeonApiError(err) ? err : undefined;
118
- const requestId = apiError?.headers?.['x-neon-ret-request-id'];
120
+ const requestId = apiError?.headers?.["x-neon-ret-request-id"];
119
121
  if (requestId) {
120
- log.debug('Failed request ID: %s', requestId);
122
+ log.debug("Failed request ID: %s", requestId);
121
123
  }
122
124
  client.track({
123
- event: 'CLI Error',
124
- userId: userId || 'anonymous',
125
+ event: "CLI Error",
126
+ userId: userId || "anonymous",
125
127
  properties: {
126
128
  message: err.message,
127
129
  stack: err.stack,
@@ -130,7 +132,7 @@ export const sendError = (err, errCode) => {
130
132
  requestId: requestId,
131
133
  },
132
134
  });
133
- log.debug('Sent CLI error event: %s', errCode);
135
+ log.debug("Sent CLI error event: %s", errCode);
134
136
  };
135
137
  export const trackEvent = (event, properties) => {
136
138
  if (!client) {
@@ -138,14 +140,14 @@ export const trackEvent = (event, properties) => {
138
140
  }
139
141
  client.track({
140
142
  event,
141
- userId: userId || 'anonymous',
143
+ userId: userId || "anonymous",
142
144
  properties,
143
145
  });
144
- log.debug('Sent CLI event: %s', event);
146
+ log.debug("Sent CLI event: %s", event);
145
147
  };
146
148
  export const getAnalyticsEventProperties = (args) => ({
147
149
  version: pkg.version,
148
- command: args._.join(' '),
150
+ command: args._.join(" "),
149
151
  flags: {
150
152
  output: args.output,
151
153
  },
package/dist/api.js CHANGED
@@ -11,29 +11,29 @@
11
11
  // {@link isNeonApiError} and reads `error.status` / `error.data`. There is no
12
12
  // axios anywhere in neonctl: requests go through the global `fetch`, and this is
13
13
  // the one place HTTP errors are shaped.
14
- import { Readable } from 'node:stream';
15
- import { EnvHttpProxyAgent, setGlobalDispatcher } from 'undici';
16
- import { createClient, createConfig } from '@neon/sdk/raw';
17
- import * as raw from '@neon/sdk/raw';
18
- import { log } from './log.js';
19
- import pkg from './pkg.js';
14
+ import { Readable } from "node:stream";
15
+ import * as raw from "@neon/sdk/raw";
16
+ import { createClient, createConfig } from "@neon/sdk/raw";
17
+ import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
18
+ import { log } from "./log.js";
19
+ import pkg from "./pkg.js";
20
20
  // Node's global `fetch` (undici) ignores HTTP_PROXY / HTTPS_PROXY / NO_PROXY,
21
21
  // whereas the axios-based client neonctl used previously honored them. Restore
22
22
  // that behaviour by installing a proxy-aware global dispatcher — but only when a
23
23
  // proxy is actually configured, so the default (no-proxy) path stays untouched.
24
24
  // This covers every `fetch` neonctl makes, including the direct S3 upload.
25
25
  const PROXY_ENV_VARS = [
26
- 'HTTP_PROXY',
27
- 'http_proxy',
28
- 'HTTPS_PROXY',
29
- 'https_proxy',
30
- 'ALL_PROXY',
31
- 'all_proxy',
26
+ "HTTP_PROXY",
27
+ "http_proxy",
28
+ "HTTPS_PROXY",
29
+ "https_proxy",
30
+ "ALL_PROXY",
31
+ "all_proxy",
32
32
  ];
33
33
  if (PROXY_ENV_VARS.some((name) => process.env[name])) {
34
34
  setGlobalDispatcher(new EnvHttpProxyAgent());
35
35
  }
36
- const DEFAULT_API_HOST = 'https://console.neon.tech/api/v2';
36
+ const DEFAULT_API_HOST = "https://console.neon.tech/api/v2";
37
37
  const REQUEST_TIMEOUT_MS = 60000;
38
38
  const USER_AGENT = `neonctl v${pkg.version}`;
39
39
  /** Mirrors the api-client `ContentType` enum used by the `request()` escape hatch. */
@@ -53,7 +53,7 @@ export var ContentType;
53
53
  export class NeonApiError extends Error {
54
54
  constructor(message, init = {}) {
55
55
  super(message);
56
- this.name = 'NeonApiError';
56
+ this.name = "NeonApiError";
57
57
  this.status = init.status;
58
58
  this.statusText = init.statusText;
59
59
  this.data = init.data;
@@ -68,18 +68,18 @@ export function isNeonApiError(err) {
68
68
  }
69
69
  /** Extract a `message` string from a parsed error body, if present. */
70
70
  export function messageFromBody(body) {
71
- if (body && typeof body === 'object' && 'message' in body) {
71
+ if (body && typeof body === "object" && "message" in body) {
72
72
  const message = body.message;
73
- if (typeof message === 'string')
73
+ if (typeof message === "string")
74
74
  return message;
75
75
  }
76
76
  return undefined;
77
77
  }
78
78
  /** Extract a machine-readable `code` string from a parsed error body, if present. */
79
79
  export function codeFromBody(body) {
80
- if (body && typeof body === 'object' && 'code' in body) {
80
+ if (body && typeof body === "object" && "code" in body) {
81
81
  const code = body.code;
82
- if (typeof code === 'string')
82
+ if (typeof code === "string")
83
83
  return code;
84
84
  }
85
85
  return undefined;
@@ -113,7 +113,7 @@ function headersToObject(headers) {
113
113
  }
114
114
  function isAbortError(err) {
115
115
  return (err instanceof Error &&
116
- (err.name === 'AbortError' || err.name === 'TimeoutError'));
116
+ (err.name === "AbortError" || err.name === "TimeoutError"));
117
117
  }
118
118
  /**
119
119
  * Walk an error's `cause` chain to find the underlying socket/DNS `code` (e.g.
@@ -124,9 +124,9 @@ function isAbortError(err) {
124
124
  function readSocketCode(err) {
125
125
  let current = err;
126
126
  for (let depth = 0; depth < 6 && current != null; depth++) {
127
- if (typeof current === 'object' && 'code' in current) {
127
+ if (typeof current === "object" && "code" in current) {
128
128
  const code = current.code;
129
- if (typeof code === 'string')
129
+ if (typeof code === "string")
130
130
  return code;
131
131
  }
132
132
  current = current.cause;
@@ -160,10 +160,10 @@ function httpError(response, body) {
160
160
  */
161
161
  function networkError(err) {
162
162
  if (isAbortError(err)) {
163
- return new NeonApiError('Request timed out', { code: 'ECONNABORTED' });
163
+ return new NeonApiError("Request timed out", { code: "ECONNABORTED" });
164
164
  }
165
165
  return new NeonApiError(err instanceof Error ? err.message : String(err), {
166
- code: readSocketCode(err) ?? 'ENETWORK',
166
+ code: readSocketCode(err) ?? "ENETWORK",
167
167
  });
168
168
  }
169
169
  /**
@@ -176,11 +176,11 @@ const timedFetch = async (input, init) => {
176
176
  const signal = init?.signal
177
177
  ? AbortSignal.any([init.signal, timeout])
178
178
  : timeout;
179
- const method = init?.method ?? (input instanceof Request ? input.method : 'GET');
179
+ const method = init?.method ?? (input instanceof Request ? input.method : "GET");
180
180
  const url = input instanceof Request ? input.url : String(input);
181
- log.debug('%s %s', method.toUpperCase(), url);
181
+ log.debug("%s %s", method.toUpperCase(), url);
182
182
  const response = await fetch(input, { ...init, signal });
183
- log.debug('%d %s', response.status, response.statusText);
183
+ log.debug("%d %s", response.status, response.statusText);
184
184
  return response;
185
185
  };
186
186
  const RETRY_COUNT = 5;
@@ -211,7 +211,7 @@ export const retryOnLock = async (fn) => {
211
211
  throw errOut;
212
212
  };
213
213
  function buildUrl(apiHost, path, query) {
214
- const url = new URL(`${apiHost.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`);
214
+ const url = new URL(`${apiHost.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`);
215
215
  if (query) {
216
216
  for (const [key, value] of Object.entries(query)) {
217
217
  if (value === undefined || value === null)
@@ -223,7 +223,7 @@ function buildUrl(apiHost, path, query) {
223
223
  }
224
224
  async function readJsonBody(response) {
225
225
  const text = await response.text();
226
- if (text.trim() === '')
226
+ if (text.trim() === "")
227
227
  return undefined;
228
228
  try {
229
229
  return JSON.parse(text);
@@ -241,7 +241,7 @@ export const getApiClient = ({ apiKey, apiHost }) => {
241
241
  auth: () => apiKey,
242
242
  baseUrl,
243
243
  fetch: timedFetch,
244
- headers: { 'User-Agent': USER_AGENT },
244
+ headers: { "User-Agent": USER_AGENT },
245
245
  }));
246
246
  /** Await a raw call, unwrap to a `{ data, status, headers }` envelope, or throw {@link NeonApiError}. */
247
247
  async function call(run) {
@@ -254,7 +254,7 @@ export const getApiClient = ({ apiKey, apiHost }) => {
254
254
  }
255
255
  const response = result.response;
256
256
  if (!response) {
257
- throw networkError(result.error ?? new Error('No response from Neon API'));
257
+ throw networkError(result.error ?? new Error("No response from Neon API"));
258
258
  }
259
259
  if (!response.ok) {
260
260
  throw httpError(response, result.error ?? result.data);
@@ -274,7 +274,7 @@ export const getApiClient = ({ apiKey, apiHost }) => {
274
274
  */
275
275
  async function request(params) {
276
276
  const url = buildUrl(baseUrl, params.path, params.query);
277
- const headers = { 'User-Agent': USER_AGENT };
277
+ const headers = { "User-Agent": USER_AGENT };
278
278
  if (params.secure !== false) {
279
279
  headers.Authorization = `Bearer ${apiKey}`;
280
280
  }
@@ -284,7 +284,7 @@ export const getApiClient = ({ apiKey, apiHost }) => {
284
284
  payload = params.body;
285
285
  }
286
286
  else if (params.body !== undefined) {
287
- headers['Content-Type'] = ContentType.Json;
287
+ headers["Content-Type"] = ContentType.Json;
288
288
  payload = JSON.stringify(params.body);
289
289
  }
290
290
  let response;
@@ -301,13 +301,13 @@ export const getApiClient = ({ apiKey, apiHost }) => {
301
301
  if (!response.ok) {
302
302
  // For a streamed download the error body arrives as a stream too; hand it
303
303
  // back as a Node `Readable` so the caller can drain it for a message.
304
- const errorBody = params.format === 'stream' && response.body
304
+ const errorBody = params.format === "stream" && response.body
305
305
  ? webStreamToNodeReadable(response.body)
306
306
  : await readJsonBody(response);
307
307
  throw httpError(response, errorBody);
308
308
  }
309
309
  let data;
310
- if (params.format === 'stream') {
310
+ if (params.format === "stream") {
311
311
  data = response.body
312
312
  ? webStreamToNodeReadable(response.body)
313
313
  : undefined;
package/dist/auth.js CHANGED
@@ -1,58 +1,62 @@
1
- import * as client from 'openid-client';
2
- import { createServer } from 'node:http';
3
- import { createReadStream } from 'node:fs';
4
- import { join } from 'node:path';
5
- import open from 'open';
6
- import { log } from './log.js';
7
- import { fileURLToPath } from 'node:url';
8
- import { sendError } from './analytics.js';
9
- import { matchErrorCode } from './errors.js';
10
- import { extendTokenSet } from './utils/auth.js';
1
+ import { createReadStream } from "node:fs";
2
+ import { createServer, } from "node:http";
3
+ import { join } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import open from "open";
6
+ import * as client from "openid-client";
7
+ import { sendError } from "./analytics.js";
8
+ import { matchErrorCode } from "./errors.js";
9
+ import { log } from "./log.js";
10
+ import { extendTokenSet } from "./utils/auth.js";
11
11
  // oauth server timeouts
12
12
  const SERVER_TIMEOUT = 10000;
13
13
  // where to wait for incoming redirect request from oauth server to arrive
14
14
  const REDIRECT_URI = (port) => `http://127.0.0.1:${port}/callback`;
15
15
  // These scopes cannot be cancelled, they are always needed.
16
- const ALWAYS_PRESENT_SCOPES = ['openid', 'offline', 'offline_access'];
16
+ const ALWAYS_PRESENT_SCOPES = ["openid", "offline", "offline_access"];
17
17
  const NEONCTL_SCOPES = [
18
18
  ...ALWAYS_PRESENT_SCOPES,
19
- 'urn:neoncloud:projects:create',
20
- 'urn:neoncloud:projects:read',
21
- 'urn:neoncloud:projects:update',
22
- 'urn:neoncloud:projects:delete',
23
- 'urn:neoncloud:orgs:create',
24
- 'urn:neoncloud:orgs:read',
25
- 'urn:neoncloud:orgs:update',
26
- 'urn:neoncloud:orgs:delete',
27
- 'urn:neoncloud:orgs:permission',
19
+ "urn:neoncloud:projects:create",
20
+ "urn:neoncloud:projects:read",
21
+ "urn:neoncloud:projects:update",
22
+ "urn:neoncloud:projects:delete",
23
+ "urn:neoncloud:orgs:create",
24
+ "urn:neoncloud:orgs:read",
25
+ "urn:neoncloud:orgs:update",
26
+ "urn:neoncloud:orgs:delete",
27
+ "urn:neoncloud:orgs:permission",
28
28
  ];
29
29
  const AUTH_TIMEOUT_SECONDS = 60;
30
- export const defaultClientID = 'neonctl';
30
+ export const defaultClientID = "neonctl";
31
31
  export const refreshToken = async ({ oauthHost, clientId, allowUnsafeTls }, tokenSet) => {
32
- log.debug('Discovering oauth server');
33
- const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: 'none' }, client.None(), {
32
+ log.debug("Discovering oauth server");
33
+ const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
34
34
  timeout: SERVER_TIMEOUT,
35
- // eslint-disable-next-line @typescript-eslint/no-deprecated
36
- execute: allowUnsafeTls ? [client.allowInsecureRequests] : undefined,
35
+ execute: allowUnsafeTls
36
+ ? // eslint-disable-next-line @typescript-eslint/no-deprecated
37
+ [client.allowInsecureRequests]
38
+ : undefined,
37
39
  });
38
40
  return await client.refreshTokenGrant(configuration, tokenSet.refresh_token);
39
41
  };
40
42
  export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
41
- log.debug('Discovering oauth server');
42
- const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: 'none' }, client.None(), {
43
+ log.debug("Discovering oauth server");
44
+ const configuration = await client.discovery(new URL(oauthHost), clientId, { token_endpoint_auth_method: "none" }, client.None(), {
43
45
  timeout: SERVER_TIMEOUT,
44
- // eslint-disable-next-line @typescript-eslint/no-deprecated
45
- execute: allowUnsafeTls ? [client.allowInsecureRequests] : undefined,
46
+ execute: allowUnsafeTls
47
+ ? // eslint-disable-next-line @typescript-eslint/no-deprecated
48
+ [client.allowInsecureRequests]
49
+ : undefined,
46
50
  });
47
51
  //
48
52
  // Start HTTP server and wait till /callback is hit
49
53
  //
50
- log.debug('Starting HTTP Server for callback');
54
+ log.debug("Starting HTTP Server for callback");
51
55
  const server = createServer();
52
- server.listen(0, '127.0.0.1', function () {
56
+ server.listen(0, "127.0.0.1", function () {
53
57
  log.debug(`Listening on port ${this.address().port}`);
54
58
  });
55
- await new Promise((resolve) => server.once('listening', resolve));
59
+ await new Promise((resolve) => server.once("listening", resolve));
56
60
  const listen_port = server.address().port;
57
61
  // https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.1.8
58
62
  const state = client.randomState();
@@ -67,17 +71,17 @@ export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
67
71
  //
68
72
  // Wait for callback and follow oauth flow.
69
73
  //
70
- if (!request.url?.startsWith('/callback')) {
74
+ if (!request.url?.startsWith("/callback")) {
71
75
  response.writeHead(404);
72
76
  response.end();
73
77
  return;
74
78
  }
75
79
  // process the CORS preflight OPTIONS request
76
- if (request.method === 'OPTIONS') {
80
+ if (request.method === "OPTIONS") {
77
81
  response.writeHead(200, {
78
- 'Access-Control-Allow-Origin': '*',
79
- 'Access-Control-Allow-Methods': 'GET, POST',
80
- 'Access-Control-Allow-Headers': 'Content-Type',
82
+ "Access-Control-Allow-Origin": "*",
83
+ "Access-Control-Allow-Methods": "GET, POST",
84
+ "Access-Control-Allow-Headers": "Content-Type",
81
85
  });
82
86
  response.end();
83
87
  return;
@@ -87,29 +91,31 @@ export const auth = async ({ oauthHost, clientId, allowUnsafeTls, }) => {
87
91
  pkceCodeVerifier: codeVerifier,
88
92
  expectedState: state,
89
93
  });
90
- response.writeHead(200, { 'Content-Type': 'text/html' });
91
- createReadStream(join(fileURLToPath(new URL('.', import.meta.url)), './callback.html')).pipe(response);
94
+ response.writeHead(200, { "Content-Type": "text/html" });
95
+ createReadStream(join(fileURLToPath(new URL(".", import.meta.url)), "./callback.html")).pipe(response);
92
96
  clearTimeout(timer);
93
97
  const exp = new Date();
94
98
  exp.setSeconds(exp.getSeconds() + (tokenSet.expires_in ?? 0));
95
99
  resolve(extendTokenSet(tokenSet));
96
100
  server.close();
97
101
  };
98
- server.on('request', (req, res) => {
102
+ server.on("request", (req, res) => {
99
103
  void onRequest(req, res);
100
104
  });
101
105
  //
102
106
  // Open browser to let user authenticate
103
107
  //
104
- const scopes = clientId == defaultClientID ? NEONCTL_SCOPES : ALWAYS_PRESENT_SCOPES;
108
+ const scopes = clientId == defaultClientID
109
+ ? NEONCTL_SCOPES
110
+ : ALWAYS_PRESENT_SCOPES;
105
111
  const authUrl = client.buildAuthorizationUrl(configuration, {
106
- scope: scopes.join(' '),
112
+ scope: scopes.join(" "),
107
113
  state,
108
114
  code_challenge: codeChallenge,
109
- code_challenge_method: 'S256',
115
+ code_challenge_method: "S256",
110
116
  redirect_uri: REDIRECT_URI(listen_port),
111
117
  });
112
- log.info('Awaiting authentication in web browser.');
118
+ log.info("Awaiting authentication in web browser.");
113
119
  log.info(`Auth Url: ${authUrl}`);
114
120
  open(authUrl.href).catch((err) => {
115
121
  const msg = `Failed to open web browser. Please copy & paste auth url to authenticate in browser.`;
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { tryCurrentBranchFastPath } from './current_branch_fast_path.js';
2
+ import { tryCurrentBranchFastPath } from "./current_branch_fast_path.js";
3
3
  // Fast path for the offline `(config) status --current-branch` probe (used by shell
4
4
  // prompts): read the pinned branch from `.neon` without loading the full command tree,
5
5
  // api-client, and yargs (~200ms). Falls through to the full CLI for everything else, so
6
6
  // the heavy `index.js` is imported lazily and only when actually needed.
7
7
  if (!tryCurrentBranchFastPath(process.argv)) {
8
- void import('./index.js');
8
+ void import("./index.js");
9
9
  }