neon 3.1.0 → 3.1.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.
- package/README.md +16 -5
- package/dist/{_shared/auth_selection.js → _chunks/auth_selection-DGgq6ifc.js} +3 -3
- package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
- package/dist/{_shared/credentials.js → _chunks/credentials-MYdHdKah.js} +3 -3
- package/dist/_chunks/env-NbA61JR3.js +585 -0
- package/dist/{_shared/env-core/env.js → _chunks/env_services-Tz9G4JeT.js} +179 -43
- package/dist/{_shared/paths.js → _chunks/paths-DMq0Lt7a.js} +2 -2
- package/dist/{_shared/profiles.js → _chunks/profiles-Ir29rqns.js} +4 -13
- package/dist/_chunks/psql-DWH-kc69.js +2169 -0
- package/dist/{_virtual/_rolldown/runtime.js → _chunks/rolldown-runtime-D7D4PA-g.js} +1 -1
- package/dist/{_shared/secure_file.js → _chunks/secure_file-BucZj4yQ.js} +2 -4
- package/dist/analytics.js +2 -2
- package/dist/auth_context.js +1 -1
- package/dist/commands/api.js +2 -2
- package/dist/commands/api_keys.js +2 -2
- package/dist/commands/auth.js +6 -6
- package/dist/commands/bootstrap.js +3 -3
- package/dist/commands/branches.js +2 -2
- package/dist/commands/bucket.js +2 -2
- package/dist/commands/checkout.js +2 -2
- package/dist/commands/config.js +2 -2
- package/dist/commands/connection_string.js +2 -2
- package/dist/commands/data_api.js +2 -2
- package/dist/commands/databases.js +2 -2
- package/dist/commands/deploy.js +2 -2
- package/dist/commands/dev.js +3 -3
- package/dist/commands/diff.js +2 -2
- package/dist/commands/env.js +42 -32
- package/dist/commands/functions.js +2 -2
- package/dist/commands/index.js +33 -33
- package/dist/commands/init.js +3 -3
- package/dist/commands/inspect.js +2 -2
- package/dist/commands/ip_allow.js +2 -2
- package/dist/commands/link.js +2 -2
- package/dist/commands/logs.js +2 -2
- package/dist/commands/neon_auth.js +2 -2
- package/dist/commands/operations.js +2 -2
- package/dist/commands/orgs.js +2 -2
- package/dist/commands/profile.js +7 -7
- package/dist/commands/projects.js +2 -2
- package/dist/commands/psql.js +2 -2
- package/dist/commands/roles.js +2 -2
- package/dist/commands/set_context.js +2 -2
- package/dist/commands/snapshots.js +2 -2
- package/dist/commands/status.js +2 -2
- package/dist/commands/user.js +2 -2
- package/dist/commands/vpc_endpoints.js +2 -2
- package/dist/config.js +1 -1
- package/dist/dev/env.js +1 -364
- package/dist/env_services.js +2 -49
- package/dist/index.js +1 -1
- package/dist/init/auth.js +4 -4
- package/dist/psql/cli.js +1 -1
- package/dist/psql/command/cmd_copy.js +1 -1
- package/dist/psql/command/cmd_io.js +1 -1403
- package/dist/psql/command/cmd_pipeline.js +1 -595
- package/dist/psql/command/dispatch.js +1 -2
- package/dist/psql/core/common.js +1 -834
- package/dist/psql/core/mainloop.js +1 -2
- package/dist/psql/core/startup.js +1 -656
- package/dist/psql/index.js +1 -1516
- package/dist/psql/io/psqlrc.js +1 -1
- package/dist/psql/types/index.js +6 -0
- package/dist/utils/inspect_db.js +1 -1
- package/dist/utils/middlewares.js +2 -1
- package/package.json +6 -3
- package/dist/_shared/env-core/reuse-secrets.js +0 -179
package/dist/psql/io/psqlrc.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { COND_COMMAND_NAMES, attachCondStack } from "../command/cmd_cond.js";
|
|
2
2
|
import { scanSlashArgs } from "../scanner/slash.js";
|
|
3
|
-
import { sendQuery } from "
|
|
3
|
+
import { U as sendQuery } from "../../_chunks/cmd_pipeline-CUbBO9U_.js";
|
|
4
4
|
import { dispatchBackslash } from "../command/dispatch.js";
|
|
5
5
|
import { initialScanState } from "../types/scanner.js";
|
|
6
6
|
import { scanSql } from "../scanner/sql.js";
|
package/dist/psql/types/index.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
import { initialScanState, isContinueStatus } from "./scanner.js";
|
|
2
|
+
import "./backslash.js";
|
|
3
|
+
import "./connection.js";
|
|
4
|
+
import "./printer.js";
|
|
5
|
+
import "./repl.js";
|
|
6
|
+
import "./settings.js";
|
|
7
|
+
import "./variables.js";
|
|
2
8
|
export { initialScanState, isContinueStatus };
|
package/dist/utils/inspect_db.js
CHANGED
|
@@ -2,7 +2,7 @@ import { EndpointType } from "./api_enums.js";
|
|
|
2
2
|
import { branchIdFromProps } from "./enrichers.js";
|
|
3
3
|
import { parsePITBranch } from "./point_in_time.js";
|
|
4
4
|
import { PgConnection } from "../psql/wire/connection.js";
|
|
5
|
-
import { parseConnectionUri } from "../psql
|
|
5
|
+
import { o as parseConnectionUri } from "../_chunks/psql-DWH-kc69.js";
|
|
6
6
|
//#region src/utils/inspect_db.ts
|
|
7
7
|
const SSL_MODES = [
|
|
8
8
|
"require",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { recordCredentialInputs } from "../
|
|
1
|
+
import { r as recordCredentialInputs } from "../_chunks/auth_selection-DGgq6ifc.js";
|
|
2
2
|
//#region src/utils/middlewares.ts
|
|
3
3
|
/**
|
|
4
4
|
* Resolves `--api-key` from `NEON_API_KEY` when the flag is absent, leaving it an
|
|
@@ -34,6 +34,7 @@ const resolveApiKeyFromEnv = (args) => {
|
|
|
34
34
|
const fillInArgs = (args, currentArgs = args, acc = []) => {
|
|
35
35
|
Object.entries(currentArgs).forEach(([k, v]) => {
|
|
36
36
|
if (k === "_" || k === "--") return;
|
|
37
|
+
if (Array.isArray(v)) return;
|
|
37
38
|
if (typeof v === "object" && v !== null) fillInArgs(args, v, [...acc, k]);
|
|
38
39
|
else if (acc.length > 0) args[acc.join(".") + "." + k] = v;
|
|
39
40
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
".": "./dist/index.js",
|
|
26
26
|
"./cli": "./dist/cli.js",
|
|
27
27
|
"./dist/_shared/*": null,
|
|
28
|
+
"./dist/_chunks/*": null,
|
|
28
29
|
"./dist/_virtual/*": null,
|
|
29
30
|
"./dist/*": "./dist/*",
|
|
30
31
|
"./package.json": "./package.json"
|
|
@@ -95,6 +96,8 @@
|
|
|
95
96
|
"tsx": "4.22.3",
|
|
96
97
|
"typescript": "^5.9.0",
|
|
97
98
|
"vitest": "^3.0.9",
|
|
99
|
+
"@neon-internals/cli-core": "0.0.0",
|
|
100
|
+
"@neon-internals/env-core": "0.0.0",
|
|
98
101
|
"@neon/e2e-harness": "0.0.0"
|
|
99
102
|
},
|
|
100
103
|
"publishConfig": {
|
|
@@ -117,10 +120,10 @@
|
|
|
117
120
|
},
|
|
118
121
|
"scripts": {
|
|
119
122
|
"generateParams": "tsx generateOptionsFromSpec.ts",
|
|
120
|
-
"codegen": "node
|
|
123
|
+
"codegen": "node scripts/set-vsx-gallery.mjs",
|
|
121
124
|
"clean": "rm -rf dist",
|
|
122
125
|
"build": "pnpm codegen && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
|
|
123
|
-
"build:internal": "node
|
|
126
|
+
"build:internal": "node scripts/set-vsx-gallery.mjs https://cursor-vsx-proxy.cloud.databricks.com/gallery && pnpm generateParams && tsc --noEmit && tsdown && cp src/*.html ./dist",
|
|
124
127
|
"bundle": "node pkg.js",
|
|
125
128
|
"typecheck": "pnpm codegen && tsc --noEmit",
|
|
126
129
|
"lint": "pnpm codegen && pnpm typecheck && biome check src",
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { NEON_ENV_VAR_KEYS, createApiFromOptions, credentialEnvKeys, credentialName, fetchEnvKeys, policyEnvKeys, previewCredentialScopes, resolveBranchPolicy, toEntries } from "./env.js";
|
|
2
|
-
import { credentialScopesSatisfied } from "@neon/config/v1";
|
|
3
|
-
//#region src/_shared/env-core/reuse-secrets.ts
|
|
4
|
-
/**
|
|
5
|
-
* Resolve a branch's env while keeping one-time secrets the caller already holds.
|
|
6
|
-
*
|
|
7
|
-
* {@link fetchEnvKeys} — and the public `fetchEnv` — only ever *fetch*. The Neon API returns a
|
|
8
|
-
* credential's `api_token` / `s3_secret_access_key` exactly once, at mint time, so "fetching"
|
|
9
|
-
* them means minting a new credential; a plain `fetchEnv` on every `neon dev` start or `env
|
|
10
|
-
* pull` would leave a live credential behind each time. This is the wrapper that avoids that:
|
|
11
|
-
* it looks at what the caller already has, decides what is still usable, and asks `fetchEnv`
|
|
12
|
-
* for only the rest.
|
|
13
|
-
*
|
|
14
|
-
* The check is a real verification, not a presence test. A persisted secret is kept only when
|
|
15
|
-
* it names a credential that still exists on this branch, is not revoked or expired, and
|
|
16
|
-
* carries every scope the policy needs. A `.env.example` placeholder, a credential revoked in
|
|
17
|
-
* the console, one copied in from another branch, or one predating a newly-enabled feature all
|
|
18
|
-
* fail that check and get replaced.
|
|
19
|
-
*
|
|
20
|
-
* None of this needs local bookkeeping, because the secrets carry their own credential id:
|
|
21
|
-
* `AWS_ACCESS_KEY_ID` **is** the credential's `tokenId` (the storage gateway authenticates
|
|
22
|
-
* against the full id), and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`,
|
|
23
|
-
* where `tokenIdShort` is what the credentials list reports. The env source being replaced is
|
|
24
|
-
* the record of what the last call issued.
|
|
25
|
-
*
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { fetchEnvReusingSecrets } from "../_shared/env-core/reuse-secrets.js";
|
|
28
|
-
*
|
|
29
|
-
* const { vars, credential } = await fetchEnvReusingSecrets(config, {
|
|
30
|
-
* projectId,
|
|
31
|
-
* branch: "main",
|
|
32
|
-
* env: { ...process.env, ...readEnvFile(".env") },
|
|
33
|
-
* });
|
|
34
|
-
* if (credential.issued) console.log(`new values for ${credential.keys.join(", ")}`);
|
|
35
|
-
* ```
|
|
36
|
-
*/
|
|
37
|
-
async function fetchEnvReusingSecrets(config, options) {
|
|
38
|
-
const { env: source = process.env, revokeSuperseded = true, ...fetchOptions } = options;
|
|
39
|
-
const api = options.api ?? createApiFromOptions(options);
|
|
40
|
-
const { branch, desired } = await resolveBranchPolicy(config, options, api);
|
|
41
|
-
const storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;
|
|
42
|
-
const gatewayEnabled = desired.preview?.aiGatewayEnabled ?? false;
|
|
43
|
-
const secretKeys = credentialEnvKeys({
|
|
44
|
-
storage: storageEnabled,
|
|
45
|
-
aiGateway: gatewayEnabled
|
|
46
|
-
});
|
|
47
|
-
if (secretKeys.length === 0) {
|
|
48
|
-
const fetched = await fetchEnvKeys(config, fetchOptions, null);
|
|
49
|
-
return {
|
|
50
|
-
vars: preferPersisted(toEntries(fetched), source),
|
|
51
|
-
credential: {
|
|
52
|
-
issued: false,
|
|
53
|
-
keys: [],
|
|
54
|
-
revoked: [],
|
|
55
|
-
superseded: []
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
const persisted = readPersistedSecrets(source);
|
|
60
|
-
const complete = (!storageEnabled || Boolean(persisted.accessKeyId && persisted.secretAccessKey)) && (!gatewayEnabled || Boolean(persisted.apiToken));
|
|
61
|
-
const named = persisted.accessKeyId !== "" || persisted.apiToken !== "" ? namedCredentials(await api.listCredentials(options.projectId, branch.id), persisted) : {
|
|
62
|
-
storage: null,
|
|
63
|
-
gateway: null
|
|
64
|
-
};
|
|
65
|
-
const reusable = complete ? reusableCredential(named, {
|
|
66
|
-
storageEnabled,
|
|
67
|
-
gatewayEnabled
|
|
68
|
-
}) : null;
|
|
69
|
-
const scopes = previewCredentialScopes(desired.preview);
|
|
70
|
-
const keep = reusable !== null && credentialScopesSatisfied(reusable.scopes, scopes);
|
|
71
|
-
const allKeys = policyEnvKeys(desired);
|
|
72
|
-
const fetchKeys = keep ? allKeys.filter((key) => !secretKeys.includes(key)) : allKeys;
|
|
73
|
-
const fetched = await fetchEnvKeys(config, {
|
|
74
|
-
...fetchOptions,
|
|
75
|
-
branchId: branch.id,
|
|
76
|
-
api
|
|
77
|
-
}, fetchKeys);
|
|
78
|
-
const vars = preferPersisted(toEntries(fetched), source);
|
|
79
|
-
if (keep) {
|
|
80
|
-
for (const key of secretKeys) {
|
|
81
|
-
const value = source[key];
|
|
82
|
-
if (value !== void 0) vars[key] = value;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
vars,
|
|
86
|
-
credential: {
|
|
87
|
-
issued: false,
|
|
88
|
-
keys: secretKeys,
|
|
89
|
-
revoked: [],
|
|
90
|
-
superseded: []
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
const ours = /* @__PURE__ */ new Set();
|
|
95
|
-
for (const meta of [named.storage, named.gateway]) if (meta !== null && meta.principalType === "user" && meta.name === credentialName(branch.name)) ours.add(meta.tokenId);
|
|
96
|
-
if (revokeSuperseded) for (const tokenId of ours) await api.revokeCredential(options.projectId, branch.id, tokenId);
|
|
97
|
-
return {
|
|
98
|
-
vars,
|
|
99
|
-
credential: {
|
|
100
|
-
issued: true,
|
|
101
|
-
keys: secretKeys,
|
|
102
|
-
revoked: revokeSuperseded ? [...ours] : [],
|
|
103
|
-
superseded: revokeSuperseded ? [] : [...ours]
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/** Read the branch credential's secrets out of an env source. */
|
|
108
|
-
function readPersistedSecrets(source) {
|
|
109
|
-
const storage = NEON_ENV_VAR_KEYS.storage;
|
|
110
|
-
const gateway = NEON_ENV_VAR_KEYS.aiGateway;
|
|
111
|
-
return {
|
|
112
|
-
accessKeyId: source[storage.accessKeyId] ?? "",
|
|
113
|
-
secretAccessKey: source[storage.secretAccessKey] ?? "",
|
|
114
|
-
apiToken: source[gateway.apiKey] ?? ""
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Keep a persisted value rather than overwriting it with an empty fetched one.
|
|
119
|
-
*
|
|
120
|
-
* Neon Auth's `base_url` is the case that needs this: integrations created before the API
|
|
121
|
-
* returned it answer with an empty string, and the persisted copy is the only one left. An
|
|
122
|
-
* empty fetched value never carries more information than a non-empty persisted one, so
|
|
123
|
-
* preferring the latter is safe for every var — and it keeps a pull from blanking a working
|
|
124
|
-
* line in someone's `.env`.
|
|
125
|
-
*/
|
|
126
|
-
function preferPersisted(vars, source) {
|
|
127
|
-
const out = { ...vars };
|
|
128
|
-
for (const [key, value] of Object.entries(out)) {
|
|
129
|
-
if (value !== "") continue;
|
|
130
|
-
const persisted = source[key];
|
|
131
|
-
if (persisted !== void 0 && persisted !== "") out[key] = persisted;
|
|
132
|
-
}
|
|
133
|
-
return out;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* The credential id embedded in an AI Gateway token. The API mints them as
|
|
137
|
-
* `nt_live_<tokenIdShort>_<secret>`, and `tokenIdShort` is the public identifier the credentials
|
|
138
|
-
* list reports — so a persisted token names the credential that issued it. Returns `null` for
|
|
139
|
-
* anything not in that shape (a `.env.example` placeholder, a hand-typed value), which callers
|
|
140
|
-
* treat as unverifiable.
|
|
141
|
-
*/
|
|
142
|
-
function gatewayTokenIdShort(apiToken) {
|
|
143
|
-
return /^nt_live_([^_]+)_.+$/.exec(apiToken)?.[1] ?? null;
|
|
144
|
-
}
|
|
145
|
-
/** Whether an issued credential can still be used: not revoked, not past its expiry. */
|
|
146
|
-
function isLiveCredential(meta, now) {
|
|
147
|
-
if (meta.revokedAt !== void 0) return false;
|
|
148
|
-
if (meta.expiresAt === void 0) return true;
|
|
149
|
-
const expiresAt = Date.parse(meta.expiresAt);
|
|
150
|
-
return Number.isNaN(expiresAt) || expiresAt > now;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* The live credentials the persisted secrets name — at most one per half. A half that names
|
|
154
|
-
* nothing contributes nothing, which is what a placeholder, a credential revoked in the
|
|
155
|
-
* console, and one copied in from another branch all look like from here.
|
|
156
|
-
*/
|
|
157
|
-
function namedCredentials(live, persisted) {
|
|
158
|
-
const usable = live.filter((meta) => isLiveCredential(meta, Date.now()));
|
|
159
|
-
const shortId = persisted.apiToken ? gatewayTokenIdShort(persisted.apiToken) : null;
|
|
160
|
-
return {
|
|
161
|
-
storage: persisted.accessKeyId ? usable.find((meta) => meta.tokenId === persisted.accessKeyId) ?? null : null,
|
|
162
|
-
gateway: shortId ? usable.find((meta) => meta.tokenIdShort === shortId) ?? null : null
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* The credential the persisted secrets can be *reused* as, or `null`.
|
|
167
|
-
*
|
|
168
|
-
* Strict on purpose: every half the policy enables has to name a live credential, and when both
|
|
169
|
-
* features are enabled they must name the *same* one — they share a single credential, so
|
|
170
|
-
* halves that disagree came from two different calls and neither can be trusted.
|
|
171
|
-
*/
|
|
172
|
-
function reusableCredential(named, enabled) {
|
|
173
|
-
if (enabled.storageEnabled && enabled.gatewayEnabled) return named.storage && named.gateway && named.storage.tokenId === named.gateway.tokenId ? named.storage : null;
|
|
174
|
-
if (enabled.storageEnabled) return named.storage;
|
|
175
|
-
if (enabled.gatewayEnabled) return named.gateway;
|
|
176
|
-
return null;
|
|
177
|
-
}
|
|
178
|
-
//#endregion
|
|
179
|
-
export { fetchEnvReusingSecrets };
|