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.
- package/README.md +2 -2
- package/dist/analytics.js +35 -33
- package/dist/api.js +34 -34
- package/dist/auth.js +50 -44
- package/dist/cli.js +2 -2
- package/dist/commands/auth.js +58 -52
- package/dist/commands/bootstrap.js +115 -157
- package/dist/commands/branches.js +154 -147
- package/dist/commands/bucket.js +124 -118
- package/dist/commands/checkout.js +49 -49
- package/dist/commands/config.js +212 -88
- package/dist/commands/connection_string.js +62 -62
- package/dist/commands/data_api.js +96 -96
- package/dist/commands/databases.js +23 -23
- package/dist/commands/deploy.js +12 -12
- package/dist/commands/dev.js +114 -114
- package/dist/commands/env.js +43 -43
- package/dist/commands/functions.js +97 -98
- package/dist/commands/index.js +26 -26
- package/dist/commands/init.js +23 -22
- package/dist/commands/ip_allow.js +29 -29
- package/dist/commands/link.js +223 -166
- package/dist/commands/neon_auth.js +381 -363
- package/dist/commands/operations.js +11 -11
- package/dist/commands/orgs.js +8 -8
- package/dist/commands/projects.js +101 -99
- package/dist/commands/psql.js +31 -31
- package/dist/commands/roles.js +21 -21
- package/dist/commands/schema_diff.js +23 -23
- package/dist/commands/set_context.js +17 -17
- package/dist/commands/status.js +17 -17
- package/dist/commands/user.js +5 -5
- package/dist/commands/vpc_endpoints.js +50 -50
- package/dist/config.js +7 -7
- package/dist/config_format.js +5 -5
- package/dist/context.js +23 -16
- package/dist/current_branch_fast_path.js +6 -6
- package/dist/dev/env.js +33 -33
- package/dist/dev/functions.js +4 -4
- package/dist/dev/inputs.js +6 -6
- package/dist/dev/runtime.js +25 -25
- package/dist/env.js +14 -14
- package/dist/env_file.js +13 -13
- package/dist/errors.js +19 -19
- package/dist/functions_api.js +10 -10
- package/dist/help.js +15 -15
- package/dist/index.js +94 -92
- package/dist/log.js +2 -2
- package/dist/pkg.js +5 -5
- package/dist/psql/cli.js +4 -2
- package/dist/psql/command/cmd_cond.js +61 -61
- package/dist/psql/command/cmd_connect.js +159 -154
- package/dist/psql/command/cmd_copy.js +107 -97
- package/dist/psql/command/cmd_describe.js +368 -363
- package/dist/psql/command/cmd_format.js +276 -263
- package/dist/psql/command/cmd_io.js +269 -263
- package/dist/psql/command/cmd_lo.js +74 -66
- package/dist/psql/command/cmd_meta.js +148 -148
- package/dist/psql/command/cmd_misc.js +17 -17
- package/dist/psql/command/cmd_pipeline.js +142 -135
- package/dist/psql/command/cmd_restrict.js +25 -25
- package/dist/psql/command/cmd_show.js +183 -168
- package/dist/psql/command/dispatch.js +26 -26
- package/dist/psql/command/shared.js +14 -14
- package/dist/psql/complete/filenames.js +16 -16
- package/dist/psql/complete/index.js +4 -4
- package/dist/psql/complete/matcher.js +33 -32
- package/dist/psql/complete/psqlVars.js +173 -173
- package/dist/psql/complete/queries.js +5 -3
- package/dist/psql/complete/rules.js +900 -863
- package/dist/psql/core/common.js +136 -133
- package/dist/psql/core/help.js +343 -343
- package/dist/psql/core/mainloop.js +160 -153
- package/dist/psql/core/prompt.js +126 -123
- package/dist/psql/core/settings.js +111 -111
- package/dist/psql/core/sqlHelp.js +150 -150
- package/dist/psql/core/startup.js +211 -205
- package/dist/psql/core/syncVars.js +14 -14
- package/dist/psql/core/variables.js +24 -24
- package/dist/psql/describe/formatters.js +302 -289
- package/dist/psql/describe/processNamePattern.js +28 -28
- package/dist/psql/describe/queries.js +656 -651
- package/dist/psql/index.js +436 -411
- package/dist/psql/io/history.js +36 -36
- package/dist/psql/io/input.js +15 -15
- package/dist/psql/io/lineEditor/buffer.js +27 -25
- package/dist/psql/io/lineEditor/complete.js +15 -15
- package/dist/psql/io/lineEditor/filename.js +22 -22
- package/dist/psql/io/lineEditor/index.js +65 -62
- package/dist/psql/io/lineEditor/keymap.js +325 -318
- package/dist/psql/io/lineEditor/vt100.js +60 -60
- package/dist/psql/io/pgpass.js +18 -18
- package/dist/psql/io/pgservice.js +14 -14
- package/dist/psql/io/psqlrc.js +46 -46
- package/dist/psql/print/aligned.js +175 -166
- package/dist/psql/print/asciidoc.js +51 -51
- package/dist/psql/print/crosstab.js +34 -31
- package/dist/psql/print/csv.js +25 -22
- package/dist/psql/print/html.js +54 -54
- package/dist/psql/print/json.js +12 -12
- package/dist/psql/print/latex.js +118 -118
- package/dist/psql/print/pager.js +28 -26
- package/dist/psql/print/troff.js +48 -48
- package/dist/psql/print/unaligned.js +15 -14
- package/dist/psql/print/units.js +17 -17
- package/dist/psql/scanner/slash.js +48 -46
- package/dist/psql/scanner/sql.js +88 -84
- package/dist/psql/scanner/stringutils.js +21 -17
- package/dist/psql/types/index.js +7 -7
- package/dist/psql/types/scanner.js +8 -8
- package/dist/psql/wire/connection.js +341 -327
- package/dist/psql/wire/copy.js +7 -7
- package/dist/psql/wire/pipeline.js +26 -24
- package/dist/psql/wire/protocol.js +102 -102
- package/dist/psql/wire/sasl.js +62 -62
- package/dist/psql/wire/tls.js +79 -73
- package/dist/storage_api.js +15 -15
- package/dist/test_utils/fixtures.js +34 -31
- package/dist/test_utils/oauth_server.js +5 -5
- package/dist/utils/api_enums.js +13 -13
- package/dist/utils/branch_notice.js +5 -5
- package/dist/utils/branch_picker.js +26 -26
- package/dist/utils/compute_units.js +4 -4
- package/dist/utils/enrichers.js +20 -15
- package/dist/utils/esbuild.js +28 -28
- package/dist/utils/formats.js +1 -1
- package/dist/utils/middlewares.js +3 -3
- package/dist/utils/package_manager.js +68 -0
- package/dist/utils/point_in_time.js +12 -12
- package/dist/utils/psql.js +30 -30
- package/dist/utils/string.js +2 -2
- package/dist/utils/ui.js +9 -9
- package/dist/utils/zip.js +1 -1
- package/dist/writer.js +17 -17
- package/package.json +6 -7
package/dist/commands/auth.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { getApiClient } from
|
|
5
|
-
import { auth, refreshToken } from
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { isCi } from
|
|
9
|
-
import { log } from
|
|
10
|
-
import { extendTokenSet } from
|
|
11
|
-
export const command =
|
|
12
|
-
export const aliases = [
|
|
13
|
-
export const describe =
|
|
14
|
-
export const builder = (yargs) => yargs.option(
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getApiClient } from "../api.js";
|
|
5
|
+
import { auth, refreshToken } from "../auth.js";
|
|
6
|
+
import { CREDENTIALS_FILE } from "../config.js";
|
|
7
|
+
import { isConfigInit, isCurrentBranchProbe } from "../context.js";
|
|
8
|
+
import { isCi } from "../env.js";
|
|
9
|
+
import { log } from "../log.js";
|
|
10
|
+
import { extendTokenSet } from "../utils/auth.js";
|
|
11
|
+
export const command = "auth";
|
|
12
|
+
export const aliases = ["login"];
|
|
13
|
+
export const describe = "Authenticate";
|
|
14
|
+
export const builder = (yargs) => yargs.option("context-file", {
|
|
15
15
|
hidden: true,
|
|
16
16
|
});
|
|
17
17
|
export const handler = async (args) => {
|
|
18
18
|
await authFlow(args);
|
|
19
19
|
};
|
|
20
|
-
export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth,
|
|
20
|
+
export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceAuth, "force-auth": forceAuthKebab, allowUnsafeTls, }) => {
|
|
21
21
|
const allowInteractiveAuth = forceAuth ?? forceAuthKebab;
|
|
22
22
|
if (!allowInteractiveAuth && isCi()) {
|
|
23
|
-
throw new Error(
|
|
23
|
+
throw new Error("Cannot run interactive auth in CI");
|
|
24
24
|
}
|
|
25
25
|
const tokenSet = await auth({
|
|
26
26
|
oauthHost: oauthHost,
|
|
@@ -30,16 +30,16 @@ export const authFlow = async ({ configDir, oauthHost, clientId, apiHost, forceA
|
|
|
30
30
|
const credentialsPath = join(configDir, CREDENTIALS_FILE);
|
|
31
31
|
try {
|
|
32
32
|
await preserveCredentials(credentialsPath, tokenSet, getApiClient({
|
|
33
|
-
apiKey: tokenSet.access_token ||
|
|
33
|
+
apiKey: tokenSet.access_token || "",
|
|
34
34
|
apiHost,
|
|
35
35
|
}));
|
|
36
36
|
}
|
|
37
37
|
catch {
|
|
38
|
-
log.error(
|
|
39
|
-
return
|
|
38
|
+
log.error("Failed to save credentials");
|
|
39
|
+
return "";
|
|
40
40
|
}
|
|
41
|
-
log.info(
|
|
42
|
-
return tokenSet.access_token ||
|
|
41
|
+
log.info("Auth complete");
|
|
42
|
+
return tokenSet.access_token || "";
|
|
43
43
|
};
|
|
44
44
|
const preserveCredentials = async (path, credentials, apiClient) => {
|
|
45
45
|
const { data: { id }, } = await apiClient.getCurrentUserInfo();
|
|
@@ -52,13 +52,13 @@ const preserveCredentials = async (path, credentials, apiClient) => {
|
|
|
52
52
|
writeFileSync(path, contents, {
|
|
53
53
|
mode: 0o700,
|
|
54
54
|
});
|
|
55
|
-
log.debug(
|
|
56
|
-
log.debug(
|
|
55
|
+
log.debug("Saved credentials to %s", path);
|
|
56
|
+
log.debug("Credentials MD5 hash: %s", md5hash(contents));
|
|
57
57
|
};
|
|
58
58
|
const handleExistingToken = async (tokenSet, props, credentialsPath) => {
|
|
59
59
|
// Use existing access_token, if present and valid
|
|
60
60
|
if (tokenSet.access_token && tokenSet.expires_at > Date.now()) {
|
|
61
|
-
log.debug(
|
|
61
|
+
log.debug("Using existing valid access_token");
|
|
62
62
|
const apiClient = getApiClient({
|
|
63
63
|
apiKey: tokenSet.access_token,
|
|
64
64
|
apiHost: props.apiHost,
|
|
@@ -67,10 +67,10 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
|
|
|
67
67
|
}
|
|
68
68
|
// Either access_token is missing or its expired. Refresh the token
|
|
69
69
|
log.debug(tokenSet.expires_at < Date.now()
|
|
70
|
-
?
|
|
71
|
-
:
|
|
70
|
+
? "Token is expired, attempting refresh"
|
|
71
|
+
: "Token is missing access_token, attempting refresh");
|
|
72
72
|
if (!tokenSet.refresh_token) {
|
|
73
|
-
log.debug(
|
|
73
|
+
log.debug("TokenSet is missing refresh_token, starting authentication");
|
|
74
74
|
return null;
|
|
75
75
|
}
|
|
76
76
|
try {
|
|
@@ -87,13 +87,13 @@ const handleExistingToken = async (tokenSet, props, credentialsPath) => {
|
|
|
87
87
|
apiHost: props.apiHost,
|
|
88
88
|
});
|
|
89
89
|
await preserveCredentials(credentialsPath, extendedTokenSet, apiClient);
|
|
90
|
-
log.debug(
|
|
90
|
+
log.debug("Token refresh successful");
|
|
91
91
|
return { apiKey, apiClient };
|
|
92
92
|
}
|
|
93
93
|
catch (err) {
|
|
94
|
-
const typedErr = err instanceof Error ? err : new Error(
|
|
95
|
-
log.debug(
|
|
96
|
-
throw new Error(
|
|
94
|
+
const typedErr = err instanceof Error ? err : new Error("Unknown error");
|
|
95
|
+
log.debug("Failed to refresh token: %s", typedErr.message);
|
|
96
|
+
throw new Error("AUTH_REFRESH_FAILED");
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
export const ensureAuth = async (props) => {
|
|
@@ -107,23 +107,28 @@ export const ensureAuth = async (props) => {
|
|
|
107
107
|
if (isCurrentBranchProbe(props)) {
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
|
+
// `config init` only scaffolds a neon.ts and installs npm packages locally; it
|
|
111
|
+
// never calls the Neon API, so skip auth entirely — no token refresh, no login.
|
|
112
|
+
if (isConfigInit(props)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
110
115
|
// `dev` runs a function locally. It injects the selected branch's env vars
|
|
111
116
|
// when credentials happen to be available, but must never trigger an
|
|
112
117
|
// interactive login: use an API key or existing stored credentials if
|
|
113
118
|
// present, otherwise run with no API client (env injection is skipped).
|
|
114
|
-
const isLocalDev = props._[0] ===
|
|
119
|
+
const isLocalDev = props._[0] === "dev";
|
|
115
120
|
// `bootstrap` only copies a public template repo; it never calls the Neon
|
|
116
121
|
// API, so it must work without credentials and must never pop a browser
|
|
117
122
|
// login. It uses an API key / stored credentials when present (harmless),
|
|
118
123
|
// otherwise it proceeds with no API client.
|
|
119
|
-
const isBootstrap = props._[0] ===
|
|
124
|
+
const isBootstrap = props._[0] === "bootstrap";
|
|
120
125
|
// `init` manages its own auth flow (asks the user if they have an account,
|
|
121
126
|
// then triggers OAuth at the right time). Skip the global auth middleware.
|
|
122
|
-
const isInit = props._[0] ===
|
|
127
|
+
const isInit = props._[0] === "init";
|
|
123
128
|
// Use existing API key or handle auth command
|
|
124
|
-
if (props.apiKey || props._[0] ===
|
|
129
|
+
if (props.apiKey || props._[0] === "auth") {
|
|
125
130
|
if (props.apiKey) {
|
|
126
|
-
log.debug(
|
|
131
|
+
log.debug("Using an API key to authorize requests");
|
|
127
132
|
}
|
|
128
133
|
props.apiClient = getApiClient({
|
|
129
134
|
apiKey: props.apiKey,
|
|
@@ -134,10 +139,10 @@ export const ensureAuth = async (props) => {
|
|
|
134
139
|
const credentialsPath = join(props.configDir, CREDENTIALS_FILE);
|
|
135
140
|
// Handle case when credentials file exists
|
|
136
141
|
if (existsSync(credentialsPath)) {
|
|
137
|
-
log.debug(
|
|
142
|
+
log.debug("Trying to read credentials from %s", credentialsPath);
|
|
138
143
|
try {
|
|
139
|
-
const contents = readFileSync(credentialsPath,
|
|
140
|
-
log.debug(
|
|
144
|
+
const contents = readFileSync(credentialsPath, "utf8");
|
|
145
|
+
log.debug("Credentials MD5 hash: %s", md5hash(contents));
|
|
141
146
|
const tokenSet = JSON.parse(contents);
|
|
142
147
|
// Try to use existing token or refresh it
|
|
143
148
|
const result = await handleExistingToken(tokenSet, props, credentialsPath);
|
|
@@ -148,32 +153,33 @@ export const ensureAuth = async (props) => {
|
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
catch (err) {
|
|
151
|
-
if (!(err instanceof Error &&
|
|
152
|
-
err.
|
|
156
|
+
if (!(err instanceof Error &&
|
|
157
|
+
err.message === "AUTH_REFRESH_FAILED") &&
|
|
158
|
+
err.code !== "ENOENT" &&
|
|
153
159
|
!(err instanceof SyntaxError)) {
|
|
154
160
|
// Throw for any errors except auth refresh failure, missing file, or invalid credentials file
|
|
155
161
|
throw err;
|
|
156
162
|
}
|
|
157
163
|
// Fall through to new auth flow for auth failures
|
|
158
|
-
log.debug(
|
|
164
|
+
log.debug("Ensure auth failed, starting authentication", err);
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
else {
|
|
162
|
-
log.debug(
|
|
168
|
+
log.debug("Credentials file %s does not exist, starting authentication", credentialsPath);
|
|
163
169
|
}
|
|
164
170
|
// `dev` never launches the interactive browser flow. With no usable
|
|
165
171
|
// credentials it proceeds without an API client; env injection is skipped
|
|
166
172
|
// and the function still runs locally.
|
|
167
173
|
if (isLocalDev) {
|
|
168
|
-
log.debug(
|
|
174
|
+
log.debug("dev: no usable credentials; running without env injection");
|
|
169
175
|
return;
|
|
170
176
|
}
|
|
171
177
|
if (isBootstrap) {
|
|
172
|
-
log.debug(
|
|
178
|
+
log.debug("bootstrap: no usable credentials; continuing without auth");
|
|
173
179
|
return;
|
|
174
180
|
}
|
|
175
181
|
if (isInit) {
|
|
176
|
-
log.debug(
|
|
182
|
+
log.debug("init: skipping global auth; init manages its own auth flow");
|
|
177
183
|
return;
|
|
178
184
|
}
|
|
179
185
|
// Start new auth flow if no valid token exists or refresh failed
|
|
@@ -193,16 +199,16 @@ export const deleteCredentials = (configDir) => {
|
|
|
193
199
|
try {
|
|
194
200
|
if (existsSync(credentialsPath)) {
|
|
195
201
|
rmSync(credentialsPath);
|
|
196
|
-
log.info(
|
|
202
|
+
log.info("Deleted credentials from %s", credentialsPath);
|
|
197
203
|
}
|
|
198
204
|
else {
|
|
199
|
-
log.debug(
|
|
205
|
+
log.debug("Credentials file %s does not exist", credentialsPath);
|
|
200
206
|
}
|
|
201
207
|
}
|
|
202
208
|
catch (err) {
|
|
203
|
-
const typedErr = err instanceof Error ? err : new Error(
|
|
204
|
-
log.error(
|
|
205
|
-
throw new Error(
|
|
209
|
+
const typedErr = err instanceof Error ? err : new Error("Unknown error");
|
|
210
|
+
log.error("Failed to delete credentials: %s", typedErr.message);
|
|
211
|
+
throw new Error("CREDENTIALS_DELETE_FAILED");
|
|
206
212
|
}
|
|
207
213
|
};
|
|
208
|
-
const md5hash = (s) => createHash(
|
|
214
|
+
const md5hash = (s) => createHash("md5").update(s).digest("hex");
|