@neondatabase/env 0.3.2 → 0.5.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/dist/cli.js.map +1 -1
- package/dist/config/dist/lib/neon-api.d.ts +101 -19
- package/dist/config/dist/lib/neon-api.d.ts.map +1 -1
- package/dist/config/dist/lib/types.d.ts +22 -16
- package/dist/config/dist/lib/types.d.ts.map +1 -1
- package/dist/config/dist/v1.d.ts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/lib/cli/commands.js.map +1 -1
- package/dist/lib/cli/resolve-context.js.map +1 -1
- package/dist/lib/env.d.ts +92 -1
- package/dist/lib/env.d.ts.map +1 -1
- package/dist/lib/env.js +203 -2
- package/dist/lib/env.js.map +1 -1
- package/dist/v1.d.ts +2 -2
- package/package.json +2 -2
package/dist/lib/env.d.ts
CHANGED
|
@@ -15,6 +15,35 @@ declare const NEON_ENV_VAR_KEYS: {
|
|
|
15
15
|
readonly dataApi: {
|
|
16
16
|
readonly url: "NEON_DATA_API_URL";
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Object storage (Preview). The S3 SDKs read `AWS_*` from their standard config chain, so
|
|
20
|
+
* a branch credential + `neon dev` / `env pull` makes object storage work from env alone.
|
|
21
|
+
* `region` is injected under both `AWS_REGION` (SDK-standard) and `NEON_STORAGE_REGION`;
|
|
22
|
+
* `forcePathStyle` has no cross-SDK AWS env var, so it keeps its `NEON_` name (the AWS SDKs
|
|
23
|
+
* default to path-style for custom endpoints anyway).
|
|
24
|
+
*/
|
|
25
|
+
readonly storage: {
|
|
26
|
+
readonly accessKeyId: "AWS_ACCESS_KEY_ID";
|
|
27
|
+
readonly secretAccessKey: "AWS_SECRET_ACCESS_KEY";
|
|
28
|
+
readonly endpoint: "AWS_ENDPOINT_URL_S3";
|
|
29
|
+
readonly region: "AWS_REGION";
|
|
30
|
+
readonly regionNeon: "NEON_STORAGE_REGION";
|
|
31
|
+
readonly forcePathStyle: "NEON_STORAGE_FORCE_PATH_STYLE";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* AI Gateway (Preview). Mapped onto the OpenAI SDK's standard env vars so the OpenAI
|
|
35
|
+
* clients work from env alone; `baseUrl` carries the gateway's OpenAI-dialect route prefix
|
|
36
|
+
* (`/ai-gateway/openai/v1`). The `NEON_AI_GATEWAY_*` aliases are also emitted: `neonToken`
|
|
37
|
+
* mirrors the OpenAI key, and `neonBaseUrl` is the bare branch gateway host
|
|
38
|
+
* (`scheme://host`, no path) — the `@ai-sdk/neon` provider appends the
|
|
39
|
+
* `/ai-gateway/<dialect>/…` routes itself (https://github.com/vercel/ai/pull/15997).
|
|
40
|
+
*/
|
|
41
|
+
readonly aiGateway: {
|
|
42
|
+
readonly apiKey: "OPENAI_API_KEY";
|
|
43
|
+
readonly baseUrl: "OPENAI_BASE_URL";
|
|
44
|
+
readonly neonToken: "NEON_AI_GATEWAY_TOKEN";
|
|
45
|
+
readonly neonBaseUrl: "NEON_AI_GATEWAY_BASE_URL";
|
|
46
|
+
};
|
|
18
47
|
};
|
|
19
48
|
/** Per-namespace inner shapes. Exposed so consumers can name the parts independently. */
|
|
20
49
|
interface NeonPostgresEnv {
|
|
@@ -48,6 +77,36 @@ interface NeonAuthEnv {
|
|
|
48
77
|
interface NeonDataApiEnv {
|
|
49
78
|
url: string;
|
|
50
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* S3-compatible object-storage access for the branch (Preview). Present on `NeonEnv` only
|
|
82
|
+
* when the policy declares `preview.buckets`. Combines a minted branch credential's access
|
|
83
|
+
* keys (`accessKeyId` = the credential's short token id, `secretAccessKey` = its
|
|
84
|
+
* `s3_secret_access_key`) with the branch's non-secret connection details
|
|
85
|
+
* (`endpoint`/`region`/`forcePathStyle`, from `GET .../storage`). Projects to the AWS SDK's
|
|
86
|
+
* standard config env (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_ENDPOINT_URL_S3`,
|
|
87
|
+
* `AWS_REGION`) so the S3 client works from env alone.
|
|
88
|
+
*/
|
|
89
|
+
interface NeonStorageEnv {
|
|
90
|
+
accessKeyId: string;
|
|
91
|
+
secretAccessKey: string;
|
|
92
|
+
/** S3-compatible endpoint URL for the branch. */
|
|
93
|
+
endpoint: string;
|
|
94
|
+
/** AWS region string (e.g. `us-east-2`). Injected as both `AWS_REGION` and `NEON_STORAGE_REGION`. */
|
|
95
|
+
region: string;
|
|
96
|
+
/** Whether the S3 client must use path-style addressing (always `true` today). */
|
|
97
|
+
forcePathStyle: boolean;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* AI Gateway access for the branch (Preview). Present on `NeonEnv` only when the policy
|
|
101
|
+
* enables `preview.aiGateway`. `apiKey` is the minted credential's bearer (`api_token`);
|
|
102
|
+
* `baseUrl` is the gateway's OpenAI-dialect endpoint on the branch-scoped gateway host
|
|
103
|
+
* (`https://<branchId>-api.ai.<region>.…/ai-gateway/openai/v1`). Projects to the OpenAI SDK's
|
|
104
|
+
* standard env (`OPENAI_API_KEY`, `OPENAI_BASE_URL`), plus the `NEON_AI_GATEWAY_*` aliases.
|
|
105
|
+
*/
|
|
106
|
+
interface NeonAiGatewayEnv {
|
|
107
|
+
apiKey: string;
|
|
108
|
+
baseUrl: string;
|
|
109
|
+
}
|
|
51
110
|
/**
|
|
52
111
|
* Empty record alias used as the "false" branch of the conditional namespace adds below.
|
|
53
112
|
* `Record<never, never>` is the no-op for intersection — the cleaner alternative to `{}`,
|
|
@@ -70,6 +129,32 @@ type ServiceOn<T> = [T] extends [false] ? false : [T] extends [{
|
|
|
70
129
|
}] ? false : [T] extends [undefined] ? false : [T] extends [true] ? true : [T] extends [{
|
|
71
130
|
enabled: true;
|
|
72
131
|
}] ? true : [T] extends [object] ? true : false;
|
|
132
|
+
/** True when `T` has at least one known key; `false` for `{}` / `never`. */
|
|
133
|
+
type HasKeys<T> = [keyof T] extends [never] ? false : true;
|
|
134
|
+
/**
|
|
135
|
+
* Whether the policy's **static** `preview` block declares at least one object-storage bucket
|
|
136
|
+
* (`preview.buckets`). Drives whether {@link NeonEnv} carries the `storage` namespace.
|
|
137
|
+
*
|
|
138
|
+
* The leading `[never]` guard is load-bearing: when a policy has no `preview` at all,
|
|
139
|
+
* `NonNullable<C["preview"]>` is `never`, and without the guard the `extends { … }` probe
|
|
140
|
+
* below would vacuously match (everything extends `never`-derived shapes) and `HasKeys<never>`
|
|
141
|
+
* would resolve `true`, wrongly adding the namespace. The guard short-circuits to `false`.
|
|
142
|
+
*/
|
|
143
|
+
type HasBuckets<C extends Config> = [NonNullable<C["preview"]>] extends [never] ? false : NonNullable<C["preview"]> extends {
|
|
144
|
+
buckets: infer B;
|
|
145
|
+
} ? HasKeys<NonNullable<B>> : false;
|
|
146
|
+
/**
|
|
147
|
+
* Whether the policy's **static** `preview` block enables the AI Gateway
|
|
148
|
+
* (`preview.aiGateway`). Drives whether {@link NeonEnv} carries the `aiGateway` namespace.
|
|
149
|
+
*
|
|
150
|
+
* The leading `[never]` guard is load-bearing for the same reason as {@link HasBuckets}: when
|
|
151
|
+
* a policy has no `preview`, `NonNullable<C["preview"]>` is `never`, and a naked `never` in the
|
|
152
|
+
* `extends` below would *distribute* (collapsing the result — and the whole `NeonEnv`
|
|
153
|
+
* intersection — to `never`). The tuple-wrapped guard short-circuits that to `false`.
|
|
154
|
+
*/
|
|
155
|
+
type AiGatewayOn<C extends Config> = [NonNullable<C["preview"]>] extends [never] ? false : NonNullable<C["preview"]> extends {
|
|
156
|
+
aiGateway: infer A;
|
|
157
|
+
} ? ServiceOn<NonNullable<A>> : false;
|
|
73
158
|
/**
|
|
74
159
|
* Static, namespaced shape of `fetchEnv` / `parseEnv`'s return value. Generic over the
|
|
75
160
|
* {@link Config} so the type system knows which optional namespaces are present.
|
|
@@ -81,6 +166,8 @@ type ServiceOn<T> = [T] extends [false] ? false : [T] extends [{
|
|
|
81
166
|
* - `postgres` is always present.
|
|
82
167
|
* - `auth` is added iff `config.auth` is statically enabled.
|
|
83
168
|
* - `dataApi` is added iff `config.dataApi` is statically enabled.
|
|
169
|
+
* - `storage` is added iff `config.preview.buckets` declares at least one bucket.
|
|
170
|
+
* - `aiGateway` is added iff `config.preview.aiGateway` is statically enabled.
|
|
84
171
|
*/
|
|
85
172
|
type NeonEnv<C extends Config = Config> = {
|
|
86
173
|
postgres: NeonPostgresEnv;
|
|
@@ -88,6 +175,10 @@ type NeonEnv<C extends Config = Config> = {
|
|
|
88
175
|
auth: NeonAuthEnv;
|
|
89
176
|
} : NoNamespace) & (ServiceOn<NonNullable<C["dataApi"]>> extends true ? {
|
|
90
177
|
dataApi: NeonDataApiEnv;
|
|
178
|
+
} : NoNamespace) & (HasBuckets<C> extends true ? {
|
|
179
|
+
storage: NeonStorageEnv;
|
|
180
|
+
} : NoNamespace) & (AiGatewayOn<C> extends true ? {
|
|
181
|
+
aiGateway: NeonAiGatewayEnv;
|
|
91
182
|
} : NoNamespace);
|
|
92
183
|
/** The static `preview.functions` record of a config, or an empty record when absent. */
|
|
93
184
|
type PreviewFunctionsOf<C extends Config> = NonNullable<C["preview"]> extends {
|
|
@@ -227,5 +318,5 @@ declare function parseEnv<const C extends Config, const S extends FunctionSlugOf
|
|
|
227
318
|
*/
|
|
228
319
|
declare function toEntries(env: NeonEnv<Config>): Record<string, string>;
|
|
229
320
|
//#endregion
|
|
230
|
-
export { FetchEnvOptions, FunctionSlugOf, NEON_ENV_VAR_KEYS, NeonAuthEnv, NeonDataApiEnv, NeonEnv, NeonFunctionEnv, NeonPostgresEnv, fetchEnv, parseEnv, toEntries };
|
|
321
|
+
export { FetchEnvOptions, FunctionSlugOf, NEON_ENV_VAR_KEYS, NeonAiGatewayEnv, NeonAuthEnv, NeonDataApiEnv, NeonEnv, NeonFunctionEnv, NeonPostgresEnv, NeonStorageEnv, fetchEnv, parseEnv, toEntries };
|
|
231
322
|
//# sourceMappingURL=env.d.ts.map
|
package/dist/lib/env.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","names":[],"sources":["../../src/lib/env.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"env.d.ts","names":[],"sources":["../../src/lib/env.ts"],"mappings":"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"}
|
package/dist/lib/env.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorCode, PlatformError, createNeonApiFromOptions, resolveConfig } from "@neondatabase/config/v1";
|
|
1
|
+
import { ErrorCode, PlatformError, createNeonApiFromOptions, deriveCredentialScopes, resolveConfig } from "@neondatabase/config/v1";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/lib/env.ts
|
|
4
4
|
/**
|
|
@@ -37,8 +37,39 @@ const NEON_ENV_VAR_KEYS = {
|
|
|
37
37
|
baseUrl: "NEON_AUTH_BASE_URL",
|
|
38
38
|
jwksUrl: "NEON_AUTH_JWKS_URL"
|
|
39
39
|
},
|
|
40
|
-
dataApi: { url: "NEON_DATA_API_URL" }
|
|
40
|
+
dataApi: { url: "NEON_DATA_API_URL" },
|
|
41
|
+
/**
|
|
42
|
+
* Object storage (Preview). The S3 SDKs read `AWS_*` from their standard config chain, so
|
|
43
|
+
* a branch credential + `neon dev` / `env pull` makes object storage work from env alone.
|
|
44
|
+
* `region` is injected under both `AWS_REGION` (SDK-standard) and `NEON_STORAGE_REGION`;
|
|
45
|
+
* `forcePathStyle` has no cross-SDK AWS env var, so it keeps its `NEON_` name (the AWS SDKs
|
|
46
|
+
* default to path-style for custom endpoints anyway).
|
|
47
|
+
*/
|
|
48
|
+
storage: {
|
|
49
|
+
accessKeyId: "AWS_ACCESS_KEY_ID",
|
|
50
|
+
secretAccessKey: "AWS_SECRET_ACCESS_KEY",
|
|
51
|
+
endpoint: "AWS_ENDPOINT_URL_S3",
|
|
52
|
+
region: "AWS_REGION",
|
|
53
|
+
regionNeon: "NEON_STORAGE_REGION",
|
|
54
|
+
forcePathStyle: "NEON_STORAGE_FORCE_PATH_STYLE"
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* AI Gateway (Preview). Mapped onto the OpenAI SDK's standard env vars so the OpenAI
|
|
58
|
+
* clients work from env alone; `baseUrl` carries the gateway's OpenAI-dialect route prefix
|
|
59
|
+
* (`/ai-gateway/openai/v1`). The `NEON_AI_GATEWAY_*` aliases are also emitted: `neonToken`
|
|
60
|
+
* mirrors the OpenAI key, and `neonBaseUrl` is the bare branch gateway host
|
|
61
|
+
* (`scheme://host`, no path) — the `@ai-sdk/neon` provider appends the
|
|
62
|
+
* `/ai-gateway/<dialect>/…` routes itself (https://github.com/vercel/ai/pull/15997).
|
|
63
|
+
*/
|
|
64
|
+
aiGateway: {
|
|
65
|
+
apiKey: "OPENAI_API_KEY",
|
|
66
|
+
baseUrl: "OPENAI_BASE_URL",
|
|
67
|
+
neonToken: "NEON_AI_GATEWAY_TOKEN",
|
|
68
|
+
neonBaseUrl: "NEON_AI_GATEWAY_BASE_URL"
|
|
69
|
+
}
|
|
41
70
|
};
|
|
71
|
+
/** OpenAI-dialect route prefix on the branch AI Gateway host. */
|
|
72
|
+
const AI_GATEWAY_OPENAI_PATH = "/ai-gateway/openai/v1";
|
|
42
73
|
/**
|
|
43
74
|
* Resolve the project + branch this process should target, then fetch live Neon
|
|
44
75
|
* connection strings for that branch over the network. Async — calls the Neon API.
|
|
@@ -121,9 +152,109 @@ async function fetchEnv(config, options) {
|
|
|
121
152
|
} });
|
|
122
153
|
result.dataApi = { url: dataApiSnapshot.url };
|
|
123
154
|
}
|
|
155
|
+
const wantsStorage = (desired.preview?.buckets.length ?? 0) > 0;
|
|
156
|
+
const wantsAiGateway = desired.preview?.aiGatewayEnabled ?? false;
|
|
157
|
+
if (wantsStorage || wantsAiGateway) {
|
|
158
|
+
const secrets = await resolveCredentialSecrets({
|
|
159
|
+
api,
|
|
160
|
+
projectId,
|
|
161
|
+
branchId: branch.id,
|
|
162
|
+
branchName: branch.name,
|
|
163
|
+
scopes: previewCredentialScopes(desired.preview),
|
|
164
|
+
env: options.env ?? process.env,
|
|
165
|
+
needStorage: wantsStorage,
|
|
166
|
+
needApiToken: wantsAiGateway
|
|
167
|
+
});
|
|
168
|
+
if (wantsStorage) {
|
|
169
|
+
const storage = await api.getProjectBranchStorage(projectId, branch.id);
|
|
170
|
+
if (!storage) throw new PlatformError(ErrorCode.NotFound, [`fetchEnv: branch policy declares object storage (preview.buckets) but storage is not enabled on branch ${branch.name} (${branch.id}).`, "Enable it via `apply(config, { projectId, branchId })` (or in the Neon Console) — then re-run fetchEnv. Or remove preview.buckets."].join(" "), { details: {
|
|
171
|
+
projectId,
|
|
172
|
+
branchId: branch.id
|
|
173
|
+
} });
|
|
174
|
+
result.storage = {
|
|
175
|
+
accessKeyId: secrets.accessKeyId,
|
|
176
|
+
secretAccessKey: secrets.secretAccessKey,
|
|
177
|
+
endpoint: storage.s3Endpoint,
|
|
178
|
+
region: storage.region,
|
|
179
|
+
forcePathStyle: storage.forcePathStyle
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (wantsAiGateway) result.aiGateway = {
|
|
183
|
+
apiKey: secrets.apiToken,
|
|
184
|
+
baseUrl: aiGatewayBaseUrl(branch.id, unpooled.uri)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
124
187
|
return result;
|
|
125
188
|
}
|
|
126
189
|
/**
|
|
190
|
+
* Scopes the branch credential should carry for a resolved branch policy. Only object storage
|
|
191
|
+
* and the AI Gateway *require* a credential; functions never force one (they have no credential
|
|
192
|
+
* of their own), but `functions:invoke` is added to the scope set when a credential is already
|
|
193
|
+
* being minted for storage / the AI Gateway, so the one credential can invoke the branch's
|
|
194
|
+
* functions too. Returns `[]` only when nothing credential-bearing is enabled.
|
|
195
|
+
*/
|
|
196
|
+
function previewCredentialScopes(preview) {
|
|
197
|
+
if (!preview) return [];
|
|
198
|
+
const storage = preview.buckets.length > 0;
|
|
199
|
+
const aiGateway = preview.aiGatewayEnabled;
|
|
200
|
+
if (!storage && !aiGateway) return [];
|
|
201
|
+
return deriveCredentialScopes({
|
|
202
|
+
storage,
|
|
203
|
+
aiGateway,
|
|
204
|
+
functions: preview.functions.length > 0
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Resolve the branch credential's secrets, reusing the ones already in the env source when
|
|
209
|
+
* present and minting a fresh `user` credential otherwise. The Neon API returns `api_token` /
|
|
210
|
+
* `s3_secret_access_key` exactly once at mint time, so the persisted copies (e.g. in
|
|
211
|
+
* `.env.local`, surfaced as `OPENAI_API_KEY` / `AWS_SECRET_ACCESS_KEY`) are the only way to
|
|
212
|
+
* recover them — exactly how one-time Auth keys are round-tripped. Reuse is presence-based
|
|
213
|
+
* (no extra bookkeeping vars): if every secret the enabled features need is already present,
|
|
214
|
+
* reuse it; otherwise mint one credential covering all currently-needed scopes.
|
|
215
|
+
*/
|
|
216
|
+
async function resolveCredentialSecrets(args) {
|
|
217
|
+
const sKeys = NEON_ENV_VAR_KEYS.storage;
|
|
218
|
+
const aKeys = NEON_ENV_VAR_KEYS.aiGateway;
|
|
219
|
+
const haveStorage = !args.needStorage || Boolean(args.env[sKeys.accessKeyId] && args.env[sKeys.secretAccessKey]);
|
|
220
|
+
const haveApiToken = !args.needApiToken || Boolean(args.env[aKeys.apiKey]);
|
|
221
|
+
if (haveStorage && haveApiToken) return {
|
|
222
|
+
accessKeyId: args.env[sKeys.accessKeyId] ?? "",
|
|
223
|
+
secretAccessKey: args.env[sKeys.secretAccessKey] ?? "",
|
|
224
|
+
apiToken: args.env[aKeys.apiKey] ?? ""
|
|
225
|
+
};
|
|
226
|
+
const minted = await args.api.createCredential(args.projectId, args.branchId, {
|
|
227
|
+
scopes: args.scopes,
|
|
228
|
+
principalType: "user",
|
|
229
|
+
name: `neon-env ${args.branchName}`
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
accessKeyId: minted.tokenIdShort,
|
|
233
|
+
secretAccessKey: minted.s3SecretAccessKey,
|
|
234
|
+
apiToken: minted.apiToken
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* The AI Gateway is a **branch-scoped host** — `<branchId>-api.ai.<region-suffix>` — NOT the
|
|
239
|
+
* control-plane API origin. Derive the `<region>.<cloud>.neon.<tld>` suffix from the branch's
|
|
240
|
+
* own Postgres connection host (e.g. a connection host of `ep-x.us-east-2.aws.neon.build`
|
|
241
|
+
* yields the gateway host `<branchId>-api.ai.us-east-2.aws.neon.build`). Any infra cell prefix
|
|
242
|
+
* (`c-N.`) on the connection host is dropped — the gateway resolves with or without it.
|
|
243
|
+
*/
|
|
244
|
+
function aiGatewayHost(branchId, connectionUri) {
|
|
245
|
+
let connectionHost = "";
|
|
246
|
+
try {
|
|
247
|
+
connectionHost = new URL(connectionUri).hostname;
|
|
248
|
+
} catch {
|
|
249
|
+
connectionHost = "";
|
|
250
|
+
}
|
|
251
|
+
return `${branchId}-api.ai.${connectionHost.split(".").slice(1).join(".").replace(/^c-\d+\./, "")}`;
|
|
252
|
+
}
|
|
253
|
+
/** The AI Gateway's OpenAI-dialect base URL (`OPENAI_BASE_URL`) on the branch gateway host. */
|
|
254
|
+
function aiGatewayBaseUrl(branchId, connectionUri) {
|
|
255
|
+
return `https://${aiGatewayHost(branchId, connectionUri)}${AI_GATEWAY_OPENAI_PATH}`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
127
258
|
* Resolve the Neon Auth base URL to surface in `env.auth`. Prefer the value returned by
|
|
128
259
|
* the integration (`getNeonAuth` includes it); fall back to whatever is already in the
|
|
129
260
|
* caller's env source so older integrations created before `base_url` was returned still
|
|
@@ -211,6 +342,29 @@ const authEnvSchema = z.object({
|
|
|
211
342
|
NEON_AUTH_JWKS_URL: z.string({ message: "NEON_AUTH_JWKS_URL is missing" }).min(1, "NEON_AUTH_JWKS_URL must not be empty")
|
|
212
343
|
});
|
|
213
344
|
const dataApiEnvSchema = z.object({ NEON_DATA_API_URL: z.string({ message: "NEON_DATA_API_URL is missing" }).min(1, "NEON_DATA_API_URL must not be empty") });
|
|
345
|
+
const storageEnvSchema = z.object({
|
|
346
|
+
AWS_ACCESS_KEY_ID: z.string({ message: "AWS_ACCESS_KEY_ID is missing" }).min(1, "AWS_ACCESS_KEY_ID must not be empty"),
|
|
347
|
+
AWS_SECRET_ACCESS_KEY: z.string({ message: "AWS_SECRET_ACCESS_KEY is missing" }).min(1, "AWS_SECRET_ACCESS_KEY must not be empty"),
|
|
348
|
+
AWS_ENDPOINT_URL_S3: z.string({ message: "AWS_ENDPOINT_URL_S3 is missing" }).min(1, "AWS_ENDPOINT_URL_S3 must not be empty"),
|
|
349
|
+
AWS_REGION: z.string({ message: "AWS_REGION is missing" }).min(1, "AWS_REGION must not be empty")
|
|
350
|
+
});
|
|
351
|
+
const aiGatewayEnvSchema = z.object({
|
|
352
|
+
OPENAI_API_KEY: z.string({ message: "OPENAI_API_KEY is missing" }).min(1, "OPENAI_API_KEY must not be empty"),
|
|
353
|
+
OPENAI_BASE_URL: z.string({ message: "OPENAI_BASE_URL is missing" }).min(1, "OPENAI_BASE_URL must not be empty")
|
|
354
|
+
});
|
|
355
|
+
/** Whether a **static** policy declares object storage (`preview.buckets`). No network. */
|
|
356
|
+
function configWantsStorage(config) {
|
|
357
|
+
return Object.keys(config.preview?.buckets ?? {}).length > 0;
|
|
358
|
+
}
|
|
359
|
+
/** Whether a **static** policy enables the AI Gateway (`preview.aiGateway`). No network. */
|
|
360
|
+
function configWantsAiGateway(config) {
|
|
361
|
+
return isServiceEnabledInput(config.preview?.aiGateway);
|
|
362
|
+
}
|
|
363
|
+
/** Parse the `NEON_STORAGE_FORCE_PATH_STYLE` env string into a boolean (defaults to `true`). */
|
|
364
|
+
function parseForcePathStyle(value) {
|
|
365
|
+
if (value === void 0) return true;
|
|
366
|
+
return value.trim().toLowerCase() !== "false";
|
|
367
|
+
}
|
|
214
368
|
/** Static-toggle helper mirroring `config`'s `isServiceEnabled` for the env reader. */
|
|
215
369
|
function isServiceEnabledInput(toggle) {
|
|
216
370
|
if (toggle === void 0) return false;
|
|
@@ -246,6 +400,33 @@ function parseEnv(config, scope) {
|
|
|
246
400
|
if (dataApi.success) result.dataApi = { url: dataApi.data.NEON_DATA_API_URL };
|
|
247
401
|
else for (const issue of dataApi.error.issues) issues.push(issue.message);
|
|
248
402
|
}
|
|
403
|
+
if (configWantsStorage(config)) {
|
|
404
|
+
const storage = storageEnvSchema.safeParse({
|
|
405
|
+
AWS_ACCESS_KEY_ID: source.AWS_ACCESS_KEY_ID,
|
|
406
|
+
AWS_SECRET_ACCESS_KEY: source.AWS_SECRET_ACCESS_KEY,
|
|
407
|
+
AWS_ENDPOINT_URL_S3: source.AWS_ENDPOINT_URL_S3,
|
|
408
|
+
AWS_REGION: source.AWS_REGION
|
|
409
|
+
});
|
|
410
|
+
if (storage.success) result.storage = {
|
|
411
|
+
accessKeyId: storage.data.AWS_ACCESS_KEY_ID,
|
|
412
|
+
secretAccessKey: storage.data.AWS_SECRET_ACCESS_KEY,
|
|
413
|
+
endpoint: storage.data.AWS_ENDPOINT_URL_S3,
|
|
414
|
+
region: storage.data.AWS_REGION,
|
|
415
|
+
forcePathStyle: parseForcePathStyle(source.NEON_STORAGE_FORCE_PATH_STYLE)
|
|
416
|
+
};
|
|
417
|
+
else for (const issue of storage.error.issues) issues.push(issue.message);
|
|
418
|
+
}
|
|
419
|
+
if (configWantsAiGateway(config)) {
|
|
420
|
+
const aiGateway = aiGatewayEnvSchema.safeParse({
|
|
421
|
+
OPENAI_API_KEY: source.OPENAI_API_KEY,
|
|
422
|
+
OPENAI_BASE_URL: source.OPENAI_BASE_URL
|
|
423
|
+
});
|
|
424
|
+
if (aiGateway.success) result.aiGateway = {
|
|
425
|
+
apiKey: aiGateway.data.OPENAI_API_KEY,
|
|
426
|
+
baseUrl: aiGateway.data.OPENAI_BASE_URL
|
|
427
|
+
};
|
|
428
|
+
else for (const issue of aiGateway.error.issues) issues.push(issue.message);
|
|
429
|
+
}
|
|
249
430
|
if (scope !== void 0) {
|
|
250
431
|
const fn = config.preview?.functions?.[scope];
|
|
251
432
|
if (!fn) throw new PlatformError(ErrorCode.EnvNotInjected, [`parseEnv: no function "${scope}" is declared in this policy's preview.functions.`, "Pass a declared function slug (or omit the scope to read external env)."].join("\n"), { details: { scope } });
|
|
@@ -291,6 +472,26 @@ function toEntries(env) {
|
|
|
291
472
|
}
|
|
292
473
|
const withDataApi = env;
|
|
293
474
|
if (withDataApi.dataApi) out[NEON_ENV_VAR_KEYS.dataApi.url] = withDataApi.dataApi.url;
|
|
475
|
+
const withStorage = env;
|
|
476
|
+
if (withStorage.storage) {
|
|
477
|
+
const s = withStorage.storage;
|
|
478
|
+
const keys = NEON_ENV_VAR_KEYS.storage;
|
|
479
|
+
out[keys.accessKeyId] = s.accessKeyId;
|
|
480
|
+
out[keys.secretAccessKey] = s.secretAccessKey;
|
|
481
|
+
out[keys.endpoint] = s.endpoint;
|
|
482
|
+
out[keys.region] = s.region;
|
|
483
|
+
out[keys.regionNeon] = s.region;
|
|
484
|
+
out[keys.forcePathStyle] = String(s.forcePathStyle);
|
|
485
|
+
}
|
|
486
|
+
const withAiGateway = env;
|
|
487
|
+
if (withAiGateway.aiGateway) {
|
|
488
|
+
const keys = NEON_ENV_VAR_KEYS.aiGateway;
|
|
489
|
+
const ai = withAiGateway.aiGateway;
|
|
490
|
+
out[keys.apiKey] = ai.apiKey;
|
|
491
|
+
out[keys.baseUrl] = ai.baseUrl;
|
|
492
|
+
out[keys.neonToken] = ai.apiKey;
|
|
493
|
+
out[keys.neonBaseUrl] = new URL(ai.baseUrl).origin;
|
|
494
|
+
}
|
|
294
495
|
return out;
|
|
295
496
|
}
|
|
296
497
|
//#endregion
|
package/dist/lib/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","names":[],"sources":["../../src/lib/env.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonRoleSnapshot,\n\tPlatformError,\n\tresolveConfig,\n\ttype ServiceToggleInput,\n} from \"@neondatabase/config/v1\";\nimport { z } from \"zod\";\n\n/**\n * Mapping between the {@link NeonEnv} property paths and the OS-level env-var keys used\n * for cross-process transport (via `.env` files, `env run -- <cmd>`, or anything else\n * that talks to `process.env`).\n *\n * Each top-level key here is a {@link NeonEnv} namespace; the inner record maps the\n * camelCase property names exposed to TypeScript to the UPPER_SNAKE env-var names used\n * by the OS. Keep this in sync with {@link postgresEnvSchema} / {@link authEnvSchema} /\n * {@link dataApiEnvSchema}.\n */\n/**\n * Neon's default branch owner role, created with every project. This is the role a\n * `DATABASE_URL` should connect as.\n */\nconst NEON_DEFAULT_OWNER_ROLE = \"neondb_owner\";\n\n/**\n * Roles Neon provisions for the Auth / Data API (PostgREST) stack. They exist to back\n * RLS-scoped Data API requests authenticated by JWT — never to hold a `DATABASE_URL` —\n * so they're skipped when auto-picking the connection role. Enabling Neon Auth or the\n * Data API (`neon config apply`) adds these next to the owner role, which is why a plain\n * branch routinely reports more than one role.\n */\nconst NEON_MANAGED_AUTH_ROLES: ReadonlySet<string> = new Set([\n\t\"authenticator\",\n\t\"anonymous\",\n\t\"authenticated\",\n]);\n\nexport const NEON_ENV_VAR_KEYS = {\n\tpostgres: {\n\t\tdatabaseUrl: \"DATABASE_URL\",\n\t\tdatabaseUrlUnpooled: \"DATABASE_URL_UNPOOLED\",\n\t},\n\tauth: {\n\t\tbaseUrl: \"NEON_AUTH_BASE_URL\",\n\t\tjwksUrl: \"NEON_AUTH_JWKS_URL\",\n\t},\n\tdataApi: {\n\t\turl: \"NEON_DATA_API_URL\",\n\t},\n} as const;\n\n/** Per-namespace inner shapes. Exposed so consumers can name the parts independently. */\nexport interface NeonPostgresEnv {\n\t/**\n\t * Pooled connection string (via Neon's PgBouncer pooler). The right default for\n\t * serverless drivers (`@neondatabase/serverless`, edge runtimes, Postgres.js, …).\n\t */\n\tdatabaseUrl: string;\n\t/**\n\t * Direct (unpooled) connection string. Use this when you need session-level\n\t * features (`LISTEN`/`NOTIFY`, prepared statements across calls, transactions\n\t * spanning round-trips) that PgBouncer's transaction-mode pooling drops.\n\t */\n\tdatabaseUrlUnpooled: string;\n}\n\n/**\n * Bits of a Neon Auth integration for the resolved branch. Only present on `NeonEnv`\n * when the branch policy enables `auth`.\n *\n * Neon Auth exposes the `baseUrl` (which doubles as the publishable client identifier) and\n * the `jwksUrl` used to verify tokens it issues. `fetchEnv` reads both from the live\n * integration; `parseEnv` reads them from `process.env` (`NEON_AUTH_BASE_URL` /\n * `NEON_AUTH_JWKS_URL`).\n */\nexport interface NeonAuthEnv {\n\tbaseUrl: string;\n\t/** JWKS URL for verifying tokens issued by Neon Auth (`NEON_AUTH_JWKS_URL`). */\n\tjwksUrl: string;\n}\n\n/** Bits of a Neon Data API integration. Only present when the branch policy enables it. */\nexport interface NeonDataApiEnv {\n\turl: string;\n}\n\n/**\n * Empty record alias used as the \"false\" branch of the conditional namespace adds below.\n * `Record<never, never>` is the no-op for intersection — the cleaner alternative to `{}`,\n * which biome rejects (it means \"any non-null\", not \"empty object\").\n */\ntype NoNamespace = Record<never, never>;\n\n/**\n * Resolve a **static** service toggle (the value of `config.auth` / `config.dataApi`) to a\n * type-level boolean. The whole-thing wrapping (`[T] extends […]`) turns off distribution\n * so a union/`undefined` is checked as one unit:\n *\n * - `false` / `{ enabled: false }` / `undefined` → `false`\n * - `true` / `{ enabled: true }` / any other object (`{}`, `{ enabled?: boolean }`) → `true`\n * (a present toggle defaults to enabled)\n * - the bare `boolean | ServiceToggle | undefined` (the default `Config` param, no literal\n * info) → `false`, so an untyped policy yields just `{ postgres }`.\n */\ntype ServiceOn<T> = [T] extends [false]\n\t? false\n\t: [T] extends [{ enabled: false }]\n\t\t? false\n\t\t: [T] extends [undefined]\n\t\t\t? false\n\t\t\t: [T] extends [true]\n\t\t\t\t? true\n\t\t\t\t: [T] extends [{ enabled: true }]\n\t\t\t\t\t? true\n\t\t\t\t\t: [T] extends [object]\n\t\t\t\t\t\t? true\n\t\t\t\t\t\t: false;\n\n/**\n * Static, namespaced shape of `fetchEnv` / `parseEnv`'s return value. Generic over the\n * {@link Config} so the type system knows which optional namespaces are present.\n *\n * Because the secret-bearing toggles now live in the **static** top-level `config.auth` /\n * `config.dataApi` (not inside a per-branch closure), the namespace presence is a direct\n * read of those fields — no union-across-branches, no default-config escape hatch:\n *\n * - `postgres` is always present.\n * - `auth` is added iff `config.auth` is statically enabled.\n * - `dataApi` is added iff `config.dataApi` is statically enabled.\n */\nexport type NeonEnv<C extends Config = Config> = {\n\tpostgres: NeonPostgresEnv;\n} & (ServiceOn<NonNullable<C[\"auth\"]>> extends true\n\t? { auth: NeonAuthEnv }\n\t: NoNamespace) &\n\t(ServiceOn<NonNullable<C[\"dataApi\"]>> extends true\n\t\t? { dataApi: NeonDataApiEnv }\n\t\t: NoNamespace);\n\n/** The static `preview.functions` record of a config, or an empty record when absent. */\ntype PreviewFunctionsOf<C extends Config> = NonNullable<C[\"preview\"]> extends {\n\tfunctions: infer F;\n}\n\t? F\n\t: Record<never, never>;\n\n/** The declared function slugs of a config (record keys), as a string union. */\nexport type FunctionSlugOf<C extends Config> = Extract<\n\tkeyof PreviewFunctionsOf<C>,\n\tstring\n>;\n\n/** The declared env-var keys of one function `S`, as a string union. */\ntype FunctionEnvKeysOf<\n\tC extends Config,\n\tS extends string,\n> = S extends keyof PreviewFunctionsOf<C>\n\t? NonNullable<PreviewFunctionsOf<C>[S]> extends { env: infer E }\n\t\t? Extract<keyof E, string>\n\t\t: never\n\t: never;\n\n/**\n * The extra `function` namespace added to `parseEnv`'s result when called with a function\n * slug scope: the declared env-var keys for that function, each resolved to a `string`.\n */\nexport type NeonFunctionEnv<C extends Config, S extends string> = {\n\tfunction: Record<FunctionEnvKeysOf<C, S>, string>;\n};\n\nexport interface FetchEnvOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses branches through their\n\t * project. Resolve it in your CLI (e.g. neonctl) and pass it in.\n\t */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). **Required.** Resolve names to ids before calling. */\n\tbranchId: string;\n\t/**\n\t * Neon API key. Resolved via the standard chain (option → `NEON_API_KEY` →\n\t * `~/.config/neonctl/credentials.json`) when omitted. Ignored when a custom `api`\n\t * is supplied.\n\t */\n\tapiKey?: string;\n\t/**\n\t * Neon **management** API base URL (not the Auth base URL). Falls back to\n\t * `NEON_API_HOST`, then production. Ignored when a custom `api` is supplied.\n\t */\n\tapiHost?: string;\n\t/**\n\t * Inject a custom NeonApi adapter. Primarily used by tests; production callers can rely\n\t * on the default real adapter built from `apiKey`.\n\t */\n\tapi?: NeonApi;\n\t/**\n\t * Role name to fetch credentials for. When omitted, the connection role is auto-picked:\n\t * the only role on the branch, else Neon's default owner (`neondb_owner`), else the\n\t * single role left after dropping the managed Auth/Data API roles\n\t * (`authenticator`/`anonymous`/`authenticated`). Throws {@link PlatformError} with\n\t * `PLATFORM_AMBIGUOUS_BRANCH_AUTH` only when more than one app role remains.\n\t */\n\troleName?: string;\n\t/**\n\t * Database name. When omitted, the only database on the branch is auto-picked; throws\n\t * {@link PlatformError} with `PLATFORM_AMBIGUOUS_BRANCH_AUTH` if the branch has more\n\t * than one database.\n\t */\n\tdatabaseName?: string;\n\t/**\n\t * Env source used for one-time Auth keys that cannot be refetched after integration\n\t * creation. Defaults to `process.env`; callers may layer values from `.env.local`.\n\t */\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve the project + branch this process should target, then fetch live Neon\n * connection strings for that branch over the network. Async — calls the Neon API.\n *\n * Use this from build scripts and the `neon-env run` command, where top-level await is\n * fine. For application code that needs a synchronous bootstrap (most frameworks: Drizzle\n * config, Next.js, Vite, etc.), inject env vars via `neon-env run -- <cmd>` and use\n * {@link parseEnv} instead — same {@link NeonEnv} shape, but a sync call against\n * `process.env`.\n *\n * Filesystem- and env-agnostic: pass `projectId` and the target `branchId` explicitly\n * (resolve them in your CLI, e.g. neonctl).\n *\n * ```ts\n * import config from \"../neon\";\n * import { fetchEnv } from \"@neondatabase/env/v1\";\n *\n * const env = await fetchEnv(config, { projectId: \"patient-art-12345\", branchId: \"br-…\" });\n * const db = drizzle(neon(env.postgres.databaseUrl), { schema });\n * ```\n *\n * The package does **not** mutate `process.env` or the filesystem itself.\n */\nexport async function fetchEnv<const C extends Config>(\n\tconfig: C,\n\toptions: FetchEnvOptions,\n): Promise<NeonEnv<C>> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\n\tconst branches = await api.listBranches(projectId);\n\tif (branches.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: project ${projectId} has no branches.`,\n\t\t\t\t\"Deploy your neon.ts policy (or create a branch) first, or pick a different project id.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { projectId } },\n\t\t);\n\t}\n\n\tconst branch = resolveBranch(options.branchId, branches);\n\tconst desired = resolveConfig(config, {\n\t\tname: branch.name,\n\t\tid: branch.id,\n\t\texists: true,\n\t\t...(branch.parentId ? { parentId: branch.parentId } : {}),\n\t\tisDefault: branch.isDefault,\n\t\tisProtected: branch.protected,\n\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t});\n\n\tconst [roles, databases] = await Promise.all([\n\t\tapi.listBranchRoles(projectId, branch.id),\n\t\tapi.listBranchDatabases(projectId, branch.id),\n\t]);\n\n\tconst roleName = pickRoleName(roles, branch, options.roleName);\n\tconst databaseName = pickDatabaseName(\n\t\tdatabases,\n\t\tbranch,\n\t\troleName,\n\t\toptions.databaseName,\n\t);\n\n\t// Fan out: always fetch both Postgres URIs. Conditionally fetch auth + dataApi based\n\t// on the branch policy. Auth key fields are only returned at integration creation time;\n\t// for Better Auth they may legitimately be empty, so absence in the local env becomes\n\t// empty string values while still emitting the required variable names.\n\tconst wantsAuth = desired.authEnabled;\n\tconst wantsDataApi = desired.dataApiEnabled;\n\n\tconst [pooled, unpooled, authSnapshot, dataApiSnapshot] = await Promise.all(\n\t\t[\n\t\t\tapi.getConnectionUri(projectId, {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tdatabaseName,\n\t\t\t\troleName,\n\t\t\t\tpooled: true,\n\t\t\t}),\n\t\t\tapi.getConnectionUri(projectId, {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tdatabaseName,\n\t\t\t\troleName,\n\t\t\t\tpooled: false,\n\t\t\t}),\n\t\t\twantsAuth\n\t\t\t\t? api.getNeonAuth(projectId, branch.id)\n\t\t\t\t: Promise.resolve(null),\n\t\t\twantsDataApi\n\t\t\t\t? api.getNeonDataApi(projectId, branch.id, databaseName)\n\t\t\t\t: Promise.resolve(null),\n\t\t],\n\t);\n\n\tconst result: Record<string, unknown> = {\n\t\tpostgres: {\n\t\t\tdatabaseUrl: pooled.uri,\n\t\t\tdatabaseUrlUnpooled: unpooled.uri,\n\t\t},\n\t};\n\n\tif (wantsAuth) {\n\t\tif (!authSnapshot) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.NotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: branch policy enables auth but no Neon Auth integration is enabled on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t\"Enable it via `apply(config, { projectId, branchId })` (or `npx neonctl …`), in the Neon Console — then re-run fetchEnv. Or return auth.enabled=false.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: { projectId, branchId: branch.id },\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tconst envSource = options.env ?? process.env;\n\t\tconst baseUrl = resolveAuthBaseUrl(authSnapshot.baseUrl, envSource);\n\t\tconst jwksUrl = resolveAuthJwksUrl(authSnapshot.jwksUrl, envSource);\n\t\tresult.auth = { baseUrl, jwksUrl } satisfies NeonAuthEnv;\n\t}\n\n\tif (wantsDataApi) {\n\t\tif (!dataApiSnapshot) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.NotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: branch policy enables dataApi but no Data API integration is enabled on branch ${branch.name} (${branch.id}) database ${databaseName}.`,\n\t\t\t\t\t\"Enable it via `apply(config, { projectId, branchId })` or in the Neon Console — then re-run fetchEnv. Or return dataApi.enabled=false.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tprojectId,\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\tdatabaseName,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tresult.dataApi = { url: dataApiSnapshot.url } satisfies NeonDataApiEnv;\n\t}\n\n\treturn result as NeonEnv<C>;\n}\n\n/**\n * Resolve the Neon Auth base URL to surface in `env.auth`. Prefer the value returned by\n * the integration (`getNeonAuth` includes it); fall back to whatever is already in the\n * caller's env source so older integrations created before `base_url` was returned still\n * round-trip through `env run`.\n */\nfunction resolveAuthBaseUrl(\n\tsnapshotBaseUrl: string | undefined,\n\tsource: NodeJS.ProcessEnv,\n): string {\n\tif (snapshotBaseUrl && snapshotBaseUrl !== \"\") return snapshotBaseUrl;\n\treturn source[NEON_ENV_VAR_KEYS.auth.baseUrl] ?? \"\";\n}\n\n/**\n * Resolve the Neon Auth JWKS URL to surface in `env.auth`. Prefer the value returned by the\n * integration (`getNeonAuth` always includes `jwks_url`); fall back to the caller's env\n * source so the value still round-trips through `env run` if a snapshot ever omits it.\n */\nfunction resolveAuthJwksUrl(\n\tsnapshotJwksUrl: string | undefined,\n\tsource: NodeJS.ProcessEnv,\n): string {\n\tif (snapshotJwksUrl && snapshotJwksUrl !== \"\") return snapshotJwksUrl;\n\treturn source[NEON_ENV_VAR_KEYS.auth.jwksUrl] ?? \"\";\n}\n\nfunction createApiFromOptions(options: FetchEnvOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"fetchEnv\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`fetchEnv: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Existing branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction pickRoleName(\n\troles: NeonRoleSnapshot[],\n\tbranch: NeonBranchSnapshot,\n\trequested: string | undefined,\n): string {\n\tif (requested) {\n\t\tif (!roles.some((r) => r.name === requested)) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: role \"${requested}\" not found on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t`Existing roles: ${roles.map((r) => r.name).join(\", \") || \"(none)\"}.`,\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\troleName: requested,\n\t\t\t\t\t\tavailableRoles: roles.map((r) => r.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn requested;\n\t}\n\tif (roles.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has no roles.`,\n\t\t\t\t\"Create one via the Neon console or pass `roleName` explicitly.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { branchId: branch.id } },\n\t\t);\n\t}\n\tif (roles.length === 1) return roles[0].name;\n\n\t// Multiple roles. Enabling Neon Auth / the Data API provisions the PostgREST roles\n\t// (authenticator/anonymous/authenticated) alongside the project owner, so a normal\n\t// branch ends up with >1 role even though only the owner backs a `DATABASE_URL`.\n\t// Default to Neon's owner role; if the project was created with a custom owner name,\n\t// fall back to the single role left after dropping the managed auth roles. Only a\n\t// genuinely ambiguous set (more than one app role) still asks the caller to choose.\n\tconst owner = roles.find((r) => r.name === NEON_DEFAULT_OWNER_ROLE);\n\tif (owner) return owner.name;\n\n\tconst appRoles = roles.filter((r) => !NEON_MANAGED_AUTH_ROLES.has(r.name));\n\tif (appRoles.length === 1) return appRoles[0].name;\n\n\tthrow new PlatformError(\n\t\tErrorCode.AmbiguousBranchAuth,\n\t\t[\n\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has ${roles.length} roles and none is \"${NEON_DEFAULT_OWNER_ROLE}\"; cannot auto-pick.`,\n\t\t\t`Pass \\`roleName\\` explicitly. Available: ${roles.map((r) => r.name).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tavailableRoles: roles.map((r) => r.name),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction pickDatabaseName(\n\tdatabases: NeonDatabaseSnapshot[],\n\tbranch: NeonBranchSnapshot,\n\troleName: string,\n\trequested: string | undefined,\n): string {\n\tif (requested) {\n\t\tif (!databases.some((d) => d.name === requested)) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: database \"${requested}\" not found on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t`Existing databases: ${databases.map((d) => d.name).join(\", \") || \"(none)\"}.`,\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\tdatabaseName: requested,\n\t\t\t\t\t\tavailableDatabases: databases.map((d) => d.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn requested;\n\t}\n\tif (databases.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has no databases.`,\n\t\t\t\t\"Create one via the Neon console or pass `databaseName` explicitly.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { branchId: branch.id } },\n\t\t);\n\t}\n\tif (databases.length === 1) return databases[0].name;\n\n\t// Prefer a database owned by the role we're connecting as.\n\tconst owned = databases.filter((d) => d.ownerName === roleName);\n\tif (owned.length === 1) return owned[0].name;\n\n\tthrow new PlatformError(\n\t\tErrorCode.AmbiguousBranchAuth,\n\t\t[\n\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has ${databases.length} databases; cannot auto-pick.`,\n\t\t\t`Pass \\`databaseName\\` explicitly. Available: ${databases.map((d) => d.name).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tavailableDatabases: databases.map((d) => d.name),\n\t\t\t},\n\t\t},\n\t);\n}\n\n// ───────────────────────── parseEnv ─────────────────────────\n\n/**\n * Per-namespace zod schemas. Each defines exactly the OS-level keys parsed from\n * `process.env` for its namespace. Keep in sync with {@link NEON_ENV_VAR_KEYS}.\n *\n * `z.string().url()` would be tighter than `min(1)` but Postgres URIs that include\n * URL-illegal characters in the password (rare but legal in Neon's connection-string\n * format) fail the WHATWG `URL` parse, so we settle for \"non-empty string\".\n */\nconst postgresEnvSchema = z.object({\n\tDATABASE_URL: z\n\t\t.string({ message: \"DATABASE_URL is missing\" })\n\t\t.min(1, \"DATABASE_URL must not be empty\"),\n\tDATABASE_URL_UNPOOLED: z\n\t\t.string({ message: \"DATABASE_URL_UNPOOLED is missing\" })\n\t\t.min(1, \"DATABASE_URL_UNPOOLED must not be empty\"),\n});\n\nconst authEnvSchema = z.object({\n\tNEON_AUTH_BASE_URL: z\n\t\t.string({ message: \"NEON_AUTH_BASE_URL is missing\" })\n\t\t.min(1, \"NEON_AUTH_BASE_URL must not be empty\"),\n\tNEON_AUTH_JWKS_URL: z\n\t\t.string({ message: \"NEON_AUTH_JWKS_URL is missing\" })\n\t\t.min(1, \"NEON_AUTH_JWKS_URL must not be empty\"),\n});\n\nconst dataApiEnvSchema = z.object({\n\tNEON_DATA_API_URL: z\n\t\t.string({ message: \"NEON_DATA_API_URL is missing\" })\n\t\t.min(1, \"NEON_DATA_API_URL must not be empty\"),\n});\n\n/** Static-toggle helper mirroring `config`'s `isServiceEnabled` for the env reader. */\nfunction isServiceEnabledInput(\n\ttoggle: ServiceToggleInput | undefined,\n): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/**\n * Synchronous, network-free counterpart to {@link fetchEnv}. Reads `process.env`, validates\n * the required Neon env vars with zod, and returns the same {@link NeonEnv} shape — so the\n * rest of your app touches `env.postgres.databaseUrl` instead of stringly-typed\n * `process.env.DATABASE_URL` lookups.\n *\n * Designed for the **\"env-vars-already-injected\"** path:\n * - You wrapped your dev command with `neon-env run -- <cmd>` or `neon dev`.\n * - Your platform (Vercel, Fly, Railway, …) injected the vars via its own integration.\n * - You are **inside a deployed Neon Function**, whose env was uploaded at `config apply`.\n *\n * Unlike the old API, `parseEnv` does **not** take a branch name: the secret set is now\n * static (top-level `config.auth` / `config.dataApi`), so it reads those directly without\n * evaluating the per-branch closure.\n *\n * The second argument is a **scope**:\n * - omitted — *external* scope (app bootstrap, build scripts, your dev machine). Returns\n * `{ postgres, auth?, dataApi? }`.\n * - a **function slug** (a key of `config.preview.functions`) — *function* scope: you are\n * running inside that function. Returns the same branch secrets **plus** a typed\n * `function` namespace with the function's declared env-var keys.\n *\n * Throws `PlatformError(EnvNotInjected)` listing every missing/invalid var when the env\n * isn't fully populated, with a fix hint pointing back at `neon dev` / `neon-env run`.\n *\n * ```ts\n * import config from \"../neon\";\n * import { parseEnv } from \"@neondatabase/env/v1\";\n *\n * // External (app / build):\n * const env = parseEnv(config);\n * const db = drizzle(neon(env.postgres.databaseUrl), { schema });\n *\n * // Inside the \"hello\" function:\n * const env = parseEnv(config, \"hello\");\n * env.function.resendApiKey; // typed from hello's declared env keys\n * ```\n */\nexport function parseEnv<const C extends Config>(config: C): NeonEnv<C>;\nexport function parseEnv<\n\tconst C extends Config,\n\tconst S extends FunctionSlugOf<C>,\n>(config: C, scope: S): NeonEnv<C> & NeonFunctionEnv<C, S>;\nexport function parseEnv(config: Config, scope?: string): unknown {\n\tconst source = process.env;\n\tconst issues: string[] = [];\n\tconst result: Record<string, unknown> = {};\n\n\tconst pg = postgresEnvSchema.safeParse({\n\t\tDATABASE_URL: source.DATABASE_URL,\n\t\tDATABASE_URL_UNPOOLED: source.DATABASE_URL_UNPOOLED,\n\t});\n\tif (pg.success) {\n\t\tresult.postgres = {\n\t\t\tdatabaseUrl: pg.data.DATABASE_URL,\n\t\t\tdatabaseUrlUnpooled: pg.data.DATABASE_URL_UNPOOLED,\n\t\t} satisfies NeonPostgresEnv;\n\t} else {\n\t\tfor (const issue of pg.error.issues) issues.push(issue.message);\n\t}\n\n\tif (isServiceEnabledInput(config.auth)) {\n\t\tconst auth = authEnvSchema.safeParse({\n\t\t\tNEON_AUTH_BASE_URL: source.NEON_AUTH_BASE_URL,\n\t\t\tNEON_AUTH_JWKS_URL: source.NEON_AUTH_JWKS_URL,\n\t\t});\n\t\tif (auth.success) {\n\t\t\tresult.auth = {\n\t\t\t\tbaseUrl: auth.data.NEON_AUTH_BASE_URL,\n\t\t\t\tjwksUrl: auth.data.NEON_AUTH_JWKS_URL,\n\t\t\t} satisfies NeonAuthEnv;\n\t\t} else {\n\t\t\tfor (const issue of auth.error.issues) issues.push(issue.message);\n\t\t}\n\t}\n\n\tif (isServiceEnabledInput(config.dataApi)) {\n\t\tconst dataApi = dataApiEnvSchema.safeParse({\n\t\t\tNEON_DATA_API_URL: source.NEON_DATA_API_URL,\n\t\t});\n\t\tif (dataApi.success) {\n\t\t\tresult.dataApi = {\n\t\t\t\turl: dataApi.data.NEON_DATA_API_URL,\n\t\t\t} satisfies NeonDataApiEnv;\n\t\t} else {\n\t\t\tfor (const issue of dataApi.error.issues)\n\t\t\t\tissues.push(issue.message);\n\t\t}\n\t}\n\n\tif (scope !== undefined) {\n\t\tconst fn = config.preview?.functions?.[scope];\n\t\tif (!fn) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.EnvNotInjected,\n\t\t\t\t[\n\t\t\t\t\t`parseEnv: no function \"${scope}\" is declared in this policy's preview.functions.`,\n\t\t\t\t\t\"Pass a declared function slug (or omit the scope to read external env).\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\t{ details: { scope } },\n\t\t\t);\n\t\t}\n\t\tconst envOut: Record<string, string> = {};\n\t\tfor (const key of Object.keys(fn.env ?? {})) {\n\t\t\tconst value = source[key];\n\t\t\t// Only a truly *unset* var is \"not injected\". Function env values carry no\n\t\t\t// non-empty constraint (unlike DATABASE_URL / NEON_AUTH_BASE_URL), so a\n\t\t\t// deliberately empty value is a present, valid value and is passed through.\n\t\t\tif (value === undefined) {\n\t\t\t\tissues.push(`${key} is missing (function \"${scope}\")`);\n\t\t\t} else {\n\t\t\t\tenvOut[key] = value;\n\t\t\t}\n\t\t}\n\t\tresult.function = envOut;\n\t}\n\n\tif (issues.length > 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.EnvNotInjected,\n\t\t\t[\n\t\t\t\t\"parseEnv: the required Neon env variables are not present in process.env.\",\n\t\t\t\t...issues.map((i) => ` - ${i}`),\n\t\t\t\t\"Inject them via one of:\",\n\t\t\t\t\" - `neon dev` / `neon-env run -- <your dev command>` (wraps the command with the vars injected)\",\n\t\t\t\t\" - your hosting platform's Neon integration (Vercel, Fly, Railway, …)\",\n\t\t\t\t\" - for the `function` namespace: deploy the function (`neon deploy` / `config apply`) so its env is uploaded.\",\n\t\t\t\t\"Or switch the call to `await fetchEnv(config, …)` if you're in a context that can do async I/O.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ details: { missing: issues } },\n\t\t);\n\t}\n\n\treturn result;\n}\n\n// ───────────────────────── env-var mapping helpers ─────────────────────────\n\n/**\n * Project a fully-resolved {@link NeonEnv} into the OS-level `{ KEY: value }` pairs used\n * for cross-process transport. Named after the web-platform `.entries()` convention\n * (`URLSearchParams` / `Headers` / `FormData`); returns a `Record` rather than an\n * iterator of tuples since that's the shape env injection needs (wrap with\n * `Object.entries(...)` if you want literal `[key, value]` pairs). Used by `neon-env run`\n * to inject the vars into a subprocess's `process.env`.\n *\n * Walks the value at runtime so it works for any `NeonEnv<C>` regardless of which\n * conditional namespaces are present.\n */\nexport function toEntries(env: NeonEnv<Config>): Record<string, string> {\n\tconst out: Record<string, string> = {\n\t\t[NEON_ENV_VAR_KEYS.postgres.databaseUrl]: env.postgres.databaseUrl,\n\t\t[NEON_ENV_VAR_KEYS.postgres.databaseUrlUnpooled]:\n\t\t\tenv.postgres.databaseUrlUnpooled,\n\t};\n\tconst withAuth = env as { auth?: NeonAuthEnv };\n\tif (withAuth.auth) {\n\t\tout[NEON_ENV_VAR_KEYS.auth.baseUrl] = withAuth.auth.baseUrl;\n\t\tout[NEON_ENV_VAR_KEYS.auth.jwksUrl] = withAuth.auth.jwksUrl;\n\t}\n\tconst withDataApi = env as { dataApi?: NeonDataApiEnv };\n\tif (withDataApi.dataApi) {\n\t\tout[NEON_ENV_VAR_KEYS.dataApi.url] = withDataApi.dataApi.url;\n\t}\n\treturn out;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"env.js","names":[],"sources":["../../src/lib/env.ts"],"sourcesContent":["import {\n\ttype Config,\n\ttype CredentialScope,\n\tcreateNeonApiFromOptions,\n\tderiveCredentialScopes,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonRoleSnapshot,\n\tPlatformError,\n\ttype ResolvedPreviewConfig,\n\tresolveConfig,\n\ttype ServiceToggleInput,\n} from \"@neondatabase/config/v1\";\nimport { z } from \"zod\";\n\n/**\n * Mapping between the {@link NeonEnv} property paths and the OS-level env-var keys used\n * for cross-process transport (via `.env` files, `env run -- <cmd>`, or anything else\n * that talks to `process.env`).\n *\n * Each top-level key here is a {@link NeonEnv} namespace; the inner record maps the\n * camelCase property names exposed to TypeScript to the UPPER_SNAKE env-var names used\n * by the OS. Keep this in sync with {@link postgresEnvSchema} / {@link authEnvSchema} /\n * {@link dataApiEnvSchema}.\n */\n/**\n * Neon's default branch owner role, created with every project. This is the role a\n * `DATABASE_URL` should connect as.\n */\nconst NEON_DEFAULT_OWNER_ROLE = \"neondb_owner\";\n\n/**\n * Roles Neon provisions for the Auth / Data API (PostgREST) stack. They exist to back\n * RLS-scoped Data API requests authenticated by JWT — never to hold a `DATABASE_URL` —\n * so they're skipped when auto-picking the connection role. Enabling Neon Auth or the\n * Data API (`neon config apply`) adds these next to the owner role, which is why a plain\n * branch routinely reports more than one role.\n */\nconst NEON_MANAGED_AUTH_ROLES: ReadonlySet<string> = new Set([\n\t\"authenticator\",\n\t\"anonymous\",\n\t\"authenticated\",\n]);\n\nexport const NEON_ENV_VAR_KEYS = {\n\tpostgres: {\n\t\tdatabaseUrl: \"DATABASE_URL\",\n\t\tdatabaseUrlUnpooled: \"DATABASE_URL_UNPOOLED\",\n\t},\n\tauth: {\n\t\tbaseUrl: \"NEON_AUTH_BASE_URL\",\n\t\tjwksUrl: \"NEON_AUTH_JWKS_URL\",\n\t},\n\tdataApi: {\n\t\turl: \"NEON_DATA_API_URL\",\n\t},\n\t/**\n\t * Object storage (Preview). The S3 SDKs read `AWS_*` from their standard config chain, so\n\t * a branch credential + `neon dev` / `env pull` makes object storage work from env alone.\n\t * `region` is injected under both `AWS_REGION` (SDK-standard) and `NEON_STORAGE_REGION`;\n\t * `forcePathStyle` has no cross-SDK AWS env var, so it keeps its `NEON_` name (the AWS SDKs\n\t * default to path-style for custom endpoints anyway).\n\t */\n\tstorage: {\n\t\taccessKeyId: \"AWS_ACCESS_KEY_ID\",\n\t\tsecretAccessKey: \"AWS_SECRET_ACCESS_KEY\",\n\t\tendpoint: \"AWS_ENDPOINT_URL_S3\",\n\t\tregion: \"AWS_REGION\",\n\t\tregionNeon: \"NEON_STORAGE_REGION\",\n\t\tforcePathStyle: \"NEON_STORAGE_FORCE_PATH_STYLE\",\n\t},\n\t/**\n\t * AI Gateway (Preview). Mapped onto the OpenAI SDK's standard env vars so the OpenAI\n\t * clients work from env alone; `baseUrl` carries the gateway's OpenAI-dialect route prefix\n\t * (`/ai-gateway/openai/v1`). The `NEON_AI_GATEWAY_*` aliases are also emitted: `neonToken`\n\t * mirrors the OpenAI key, and `neonBaseUrl` is the bare branch gateway host\n\t * (`scheme://host`, no path) — the `@ai-sdk/neon` provider appends the\n\t * `/ai-gateway/<dialect>/…` routes itself (https://github.com/vercel/ai/pull/15997).\n\t */\n\taiGateway: {\n\t\tapiKey: \"OPENAI_API_KEY\",\n\t\tbaseUrl: \"OPENAI_BASE_URL\",\n\t\tneonToken: \"NEON_AI_GATEWAY_TOKEN\",\n\t\tneonBaseUrl: \"NEON_AI_GATEWAY_BASE_URL\",\n\t},\n} as const;\n\n/** OpenAI-dialect route prefix on the branch AI Gateway host. */\nconst AI_GATEWAY_OPENAI_PATH = \"/ai-gateway/openai/v1\";\n\n/** Per-namespace inner shapes. Exposed so consumers can name the parts independently. */\nexport interface NeonPostgresEnv {\n\t/**\n\t * Pooled connection string (via Neon's PgBouncer pooler). The right default for\n\t * serverless drivers (`@neondatabase/serverless`, edge runtimes, Postgres.js, …).\n\t */\n\tdatabaseUrl: string;\n\t/**\n\t * Direct (unpooled) connection string. Use this when you need session-level\n\t * features (`LISTEN`/`NOTIFY`, prepared statements across calls, transactions\n\t * spanning round-trips) that PgBouncer's transaction-mode pooling drops.\n\t */\n\tdatabaseUrlUnpooled: string;\n}\n\n/**\n * Bits of a Neon Auth integration for the resolved branch. Only present on `NeonEnv`\n * when the branch policy enables `auth`.\n *\n * Neon Auth exposes the `baseUrl` (which doubles as the publishable client identifier) and\n * the `jwksUrl` used to verify tokens it issues. `fetchEnv` reads both from the live\n * integration; `parseEnv` reads them from `process.env` (`NEON_AUTH_BASE_URL` /\n * `NEON_AUTH_JWKS_URL`).\n */\nexport interface NeonAuthEnv {\n\tbaseUrl: string;\n\t/** JWKS URL for verifying tokens issued by Neon Auth (`NEON_AUTH_JWKS_URL`). */\n\tjwksUrl: string;\n}\n\n/** Bits of a Neon Data API integration. Only present when the branch policy enables it. */\nexport interface NeonDataApiEnv {\n\turl: string;\n}\n\n/**\n * S3-compatible object-storage access for the branch (Preview). Present on `NeonEnv` only\n * when the policy declares `preview.buckets`. Combines a minted branch credential's access\n * keys (`accessKeyId` = the credential's short token id, `secretAccessKey` = its\n * `s3_secret_access_key`) with the branch's non-secret connection details\n * (`endpoint`/`region`/`forcePathStyle`, from `GET .../storage`). Projects to the AWS SDK's\n * standard config env (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_ENDPOINT_URL_S3`,\n * `AWS_REGION`) so the S3 client works from env alone.\n */\nexport interface NeonStorageEnv {\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\t/** S3-compatible endpoint URL for the branch. */\n\tendpoint: string;\n\t/** AWS region string (e.g. `us-east-2`). Injected as both `AWS_REGION` and `NEON_STORAGE_REGION`. */\n\tregion: string;\n\t/** Whether the S3 client must use path-style addressing (always `true` today). */\n\tforcePathStyle: boolean;\n}\n\n/**\n * AI Gateway access for the branch (Preview). Present on `NeonEnv` only when the policy\n * enables `preview.aiGateway`. `apiKey` is the minted credential's bearer (`api_token`);\n * `baseUrl` is the gateway's OpenAI-dialect endpoint on the branch-scoped gateway host\n * (`https://<branchId>-api.ai.<region>.…/ai-gateway/openai/v1`). Projects to the OpenAI SDK's\n * standard env (`OPENAI_API_KEY`, `OPENAI_BASE_URL`), plus the `NEON_AI_GATEWAY_*` aliases.\n */\nexport interface NeonAiGatewayEnv {\n\tapiKey: string;\n\tbaseUrl: string;\n}\n\n/**\n * Empty record alias used as the \"false\" branch of the conditional namespace adds below.\n * `Record<never, never>` is the no-op for intersection — the cleaner alternative to `{}`,\n * which biome rejects (it means \"any non-null\", not \"empty object\").\n */\ntype NoNamespace = Record<never, never>;\n\n/**\n * Resolve a **static** service toggle (the value of `config.auth` / `config.dataApi`) to a\n * type-level boolean. The whole-thing wrapping (`[T] extends […]`) turns off distribution\n * so a union/`undefined` is checked as one unit:\n *\n * - `false` / `{ enabled: false }` / `undefined` → `false`\n * - `true` / `{ enabled: true }` / any other object (`{}`, `{ enabled?: boolean }`) → `true`\n * (a present toggle defaults to enabled)\n * - the bare `boolean | ServiceToggle | undefined` (the default `Config` param, no literal\n * info) → `false`, so an untyped policy yields just `{ postgres }`.\n */\ntype ServiceOn<T> = [T] extends [false]\n\t? false\n\t: [T] extends [{ enabled: false }]\n\t\t? false\n\t\t: [T] extends [undefined]\n\t\t\t? false\n\t\t\t: [T] extends [true]\n\t\t\t\t? true\n\t\t\t\t: [T] extends [{ enabled: true }]\n\t\t\t\t\t? true\n\t\t\t\t\t: [T] extends [object]\n\t\t\t\t\t\t? true\n\t\t\t\t\t\t: false;\n\n/** True when `T` has at least one known key; `false` for `{}` / `never`. */\ntype HasKeys<T> = [keyof T] extends [never] ? false : true;\n\n/**\n * Whether the policy's **static** `preview` block declares at least one object-storage bucket\n * (`preview.buckets`). Drives whether {@link NeonEnv} carries the `storage` namespace.\n *\n * The leading `[never]` guard is load-bearing: when a policy has no `preview` at all,\n * `NonNullable<C[\"preview\"]>` is `never`, and without the guard the `extends { … }` probe\n * below would vacuously match (everything extends `never`-derived shapes) and `HasKeys<never>`\n * would resolve `true`, wrongly adding the namespace. The guard short-circuits to `false`.\n */\ntype HasBuckets<C extends Config> = [NonNullable<C[\"preview\"]>] extends [never]\n\t? false\n\t: NonNullable<C[\"preview\"]> extends { buckets: infer B }\n\t\t? HasKeys<NonNullable<B>>\n\t\t: false;\n\n/**\n * Whether the policy's **static** `preview` block enables the AI Gateway\n * (`preview.aiGateway`). Drives whether {@link NeonEnv} carries the `aiGateway` namespace.\n *\n * The leading `[never]` guard is load-bearing for the same reason as {@link HasBuckets}: when\n * a policy has no `preview`, `NonNullable<C[\"preview\"]>` is `never`, and a naked `never` in the\n * `extends` below would *distribute* (collapsing the result — and the whole `NeonEnv`\n * intersection — to `never`). The tuple-wrapped guard short-circuits that to `false`.\n */\ntype AiGatewayOn<C extends Config> = [NonNullable<C[\"preview\"]>] extends [never]\n\t? false\n\t: NonNullable<C[\"preview\"]> extends { aiGateway: infer A }\n\t\t? ServiceOn<NonNullable<A>>\n\t\t: false;\n\n/**\n * Static, namespaced shape of `fetchEnv` / `parseEnv`'s return value. Generic over the\n * {@link Config} so the type system knows which optional namespaces are present.\n *\n * Because the secret-bearing toggles now live in the **static** top-level `config.auth` /\n * `config.dataApi` (not inside a per-branch closure), the namespace presence is a direct\n * read of those fields — no union-across-branches, no default-config escape hatch:\n *\n * - `postgres` is always present.\n * - `auth` is added iff `config.auth` is statically enabled.\n * - `dataApi` is added iff `config.dataApi` is statically enabled.\n * - `storage` is added iff `config.preview.buckets` declares at least one bucket.\n * - `aiGateway` is added iff `config.preview.aiGateway` is statically enabled.\n */\nexport type NeonEnv<C extends Config = Config> = {\n\tpostgres: NeonPostgresEnv;\n} & (ServiceOn<NonNullable<C[\"auth\"]>> extends true\n\t? { auth: NeonAuthEnv }\n\t: NoNamespace) &\n\t(ServiceOn<NonNullable<C[\"dataApi\"]>> extends true\n\t\t? { dataApi: NeonDataApiEnv }\n\t\t: NoNamespace) &\n\t(HasBuckets<C> extends true ? { storage: NeonStorageEnv } : NoNamespace) &\n\t(AiGatewayOn<C> extends true\n\t\t? { aiGateway: NeonAiGatewayEnv }\n\t\t: NoNamespace);\n\n/** The static `preview.functions` record of a config, or an empty record when absent. */\ntype PreviewFunctionsOf<C extends Config> = NonNullable<C[\"preview\"]> extends {\n\tfunctions: infer F;\n}\n\t? F\n\t: Record<never, never>;\n\n/** The declared function slugs of a config (record keys), as a string union. */\nexport type FunctionSlugOf<C extends Config> = Extract<\n\tkeyof PreviewFunctionsOf<C>,\n\tstring\n>;\n\n/** The declared env-var keys of one function `S`, as a string union. */\ntype FunctionEnvKeysOf<\n\tC extends Config,\n\tS extends string,\n> = S extends keyof PreviewFunctionsOf<C>\n\t? NonNullable<PreviewFunctionsOf<C>[S]> extends { env: infer E }\n\t\t? Extract<keyof E, string>\n\t\t: never\n\t: never;\n\n/**\n * The extra `function` namespace added to `parseEnv`'s result when called with a function\n * slug scope: the declared env-var keys for that function, each resolved to a `string`.\n */\nexport type NeonFunctionEnv<C extends Config, S extends string> = {\n\tfunction: Record<FunctionEnvKeysOf<C, S>, string>;\n};\n\nexport interface FetchEnvOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses branches through their\n\t * project. Resolve it in your CLI (e.g. neonctl) and pass it in.\n\t */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). **Required.** Resolve names to ids before calling. */\n\tbranchId: string;\n\t/**\n\t * Neon API key. Resolved via the standard chain (option → `NEON_API_KEY` →\n\t * `~/.config/neonctl/credentials.json`) when omitted. Ignored when a custom `api`\n\t * is supplied.\n\t */\n\tapiKey?: string;\n\t/**\n\t * Neon **management** API base URL (not the Auth base URL). Falls back to\n\t * `NEON_API_HOST`, then production. Ignored when a custom `api` is supplied.\n\t */\n\tapiHost?: string;\n\t/**\n\t * Inject a custom NeonApi adapter. Primarily used by tests; production callers can rely\n\t * on the default real adapter built from `apiKey`.\n\t */\n\tapi?: NeonApi;\n\t/**\n\t * Role name to fetch credentials for. When omitted, the connection role is auto-picked:\n\t * the only role on the branch, else Neon's default owner (`neondb_owner`), else the\n\t * single role left after dropping the managed Auth/Data API roles\n\t * (`authenticator`/`anonymous`/`authenticated`). Throws {@link PlatformError} with\n\t * `PLATFORM_AMBIGUOUS_BRANCH_AUTH` only when more than one app role remains.\n\t */\n\troleName?: string;\n\t/**\n\t * Database name. When omitted, the only database on the branch is auto-picked; throws\n\t * {@link PlatformError} with `PLATFORM_AMBIGUOUS_BRANCH_AUTH` if the branch has more\n\t * than one database.\n\t */\n\tdatabaseName?: string;\n\t/**\n\t * Env source used for one-time Auth keys that cannot be refetched after integration\n\t * creation. Defaults to `process.env`; callers may layer values from `.env.local`.\n\t */\n\tenv?: NodeJS.ProcessEnv;\n}\n\n/**\n * Resolve the project + branch this process should target, then fetch live Neon\n * connection strings for that branch over the network. Async — calls the Neon API.\n *\n * Use this from build scripts and the `neon-env run` command, where top-level await is\n * fine. For application code that needs a synchronous bootstrap (most frameworks: Drizzle\n * config, Next.js, Vite, etc.), inject env vars via `neon-env run -- <cmd>` and use\n * {@link parseEnv} instead — same {@link NeonEnv} shape, but a sync call against\n * `process.env`.\n *\n * Filesystem- and env-agnostic: pass `projectId` and the target `branchId` explicitly\n * (resolve them in your CLI, e.g. neonctl).\n *\n * ```ts\n * import config from \"../neon\";\n * import { fetchEnv } from \"@neondatabase/env/v1\";\n *\n * const env = await fetchEnv(config, { projectId: \"patient-art-12345\", branchId: \"br-…\" });\n * const db = drizzle(neon(env.postgres.databaseUrl), { schema });\n * ```\n *\n * The package does **not** mutate `process.env` or the filesystem itself.\n */\nexport async function fetchEnv<const C extends Config>(\n\tconfig: C,\n\toptions: FetchEnvOptions,\n): Promise<NeonEnv<C>> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\n\tconst branches = await api.listBranches(projectId);\n\tif (branches.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: project ${projectId} has no branches.`,\n\t\t\t\t\"Deploy your neon.ts policy (or create a branch) first, or pick a different project id.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { projectId } },\n\t\t);\n\t}\n\n\tconst branch = resolveBranch(options.branchId, branches);\n\tconst desired = resolveConfig(config, {\n\t\tname: branch.name,\n\t\tid: branch.id,\n\t\texists: true,\n\t\t...(branch.parentId ? { parentId: branch.parentId } : {}),\n\t\tisDefault: branch.isDefault,\n\t\tisProtected: branch.protected,\n\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t});\n\n\tconst [roles, databases] = await Promise.all([\n\t\tapi.listBranchRoles(projectId, branch.id),\n\t\tapi.listBranchDatabases(projectId, branch.id),\n\t]);\n\n\tconst roleName = pickRoleName(roles, branch, options.roleName);\n\tconst databaseName = pickDatabaseName(\n\t\tdatabases,\n\t\tbranch,\n\t\troleName,\n\t\toptions.databaseName,\n\t);\n\n\t// Fan out: always fetch both Postgres URIs. Conditionally fetch auth + dataApi based\n\t// on the branch policy. Auth key fields are only returned at integration creation time;\n\t// for Better Auth they may legitimately be empty, so absence in the local env becomes\n\t// empty string values while still emitting the required variable names.\n\tconst wantsAuth = desired.authEnabled;\n\tconst wantsDataApi = desired.dataApiEnabled;\n\n\tconst [pooled, unpooled, authSnapshot, dataApiSnapshot] = await Promise.all(\n\t\t[\n\t\t\tapi.getConnectionUri(projectId, {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tdatabaseName,\n\t\t\t\troleName,\n\t\t\t\tpooled: true,\n\t\t\t}),\n\t\t\tapi.getConnectionUri(projectId, {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tdatabaseName,\n\t\t\t\troleName,\n\t\t\t\tpooled: false,\n\t\t\t}),\n\t\t\twantsAuth\n\t\t\t\t? api.getNeonAuth(projectId, branch.id)\n\t\t\t\t: Promise.resolve(null),\n\t\t\twantsDataApi\n\t\t\t\t? api.getNeonDataApi(projectId, branch.id, databaseName)\n\t\t\t\t: Promise.resolve(null),\n\t\t],\n\t);\n\n\tconst result: Record<string, unknown> = {\n\t\tpostgres: {\n\t\t\tdatabaseUrl: pooled.uri,\n\t\t\tdatabaseUrlUnpooled: unpooled.uri,\n\t\t},\n\t};\n\n\tif (wantsAuth) {\n\t\tif (!authSnapshot) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.NotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: branch policy enables auth but no Neon Auth integration is enabled on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t\"Enable it via `apply(config, { projectId, branchId })` (or `npx neonctl …`), in the Neon Console — then re-run fetchEnv. Or return auth.enabled=false.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: { projectId, branchId: branch.id },\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tconst envSource = options.env ?? process.env;\n\t\tconst baseUrl = resolveAuthBaseUrl(authSnapshot.baseUrl, envSource);\n\t\tconst jwksUrl = resolveAuthJwksUrl(authSnapshot.jwksUrl, envSource);\n\t\tresult.auth = { baseUrl, jwksUrl } satisfies NeonAuthEnv;\n\t}\n\n\tif (wantsDataApi) {\n\t\tif (!dataApiSnapshot) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.NotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: branch policy enables dataApi but no Data API integration is enabled on branch ${branch.name} (${branch.id}) database ${databaseName}.`,\n\t\t\t\t\t\"Enable it via `apply(config, { projectId, branchId })` or in the Neon Console — then re-run fetchEnv. Or return dataApi.enabled=false.\",\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tprojectId,\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\tdatabaseName,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tresult.dataApi = { url: dataApiSnapshot.url } satisfies NeonDataApiEnv;\n\t}\n\n\t// Object storage + AI Gateway (Preview). A single branch credential is minted (once) to\n\t// back whichever of these the policy enables; functions never force a credential but ride\n\t// along on its scopes. None of this runs when the policy enables neither, so the\n\t// Postgres / Auth / Data API path never touches the credentials/storage endpoints (and\n\t// keeps working on production, where they may not exist yet).\n\tconst wantsStorage = (desired.preview?.buckets.length ?? 0) > 0;\n\tconst wantsAiGateway = desired.preview?.aiGatewayEnabled ?? false;\n\tif (wantsStorage || wantsAiGateway) {\n\t\tconst secrets = await resolveCredentialSecrets({\n\t\t\tapi,\n\t\t\tprojectId,\n\t\t\tbranchId: branch.id,\n\t\t\tbranchName: branch.name,\n\t\t\tscopes: previewCredentialScopes(desired.preview),\n\t\t\tenv: options.env ?? process.env,\n\t\t\tneedStorage: wantsStorage,\n\t\t\tneedApiToken: wantsAiGateway,\n\t\t});\n\t\tif (wantsStorage) {\n\t\t\tconst storage = await api.getProjectBranchStorage(\n\t\t\t\tprojectId,\n\t\t\t\tbranch.id,\n\t\t\t);\n\t\t\tif (!storage) {\n\t\t\t\tthrow new PlatformError(\n\t\t\t\t\tErrorCode.NotFound,\n\t\t\t\t\t[\n\t\t\t\t\t\t`fetchEnv: branch policy declares object storage (preview.buckets) but storage is not enabled on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t\t\"Enable it via `apply(config, { projectId, branchId })` (or in the Neon Console) — then re-run fetchEnv. Or remove preview.buckets.\",\n\t\t\t\t\t].join(\" \"),\n\t\t\t\t\t{ details: { projectId, branchId: branch.id } },\n\t\t\t\t);\n\t\t\t}\n\t\t\tresult.storage = {\n\t\t\t\taccessKeyId: secrets.accessKeyId,\n\t\t\t\tsecretAccessKey: secrets.secretAccessKey,\n\t\t\t\tendpoint: storage.s3Endpoint,\n\t\t\t\tregion: storage.region,\n\t\t\t\tforcePathStyle: storage.forcePathStyle,\n\t\t\t} satisfies NeonStorageEnv;\n\t\t}\n\t\tif (wantsAiGateway) {\n\t\t\tresult.aiGateway = {\n\t\t\t\tapiKey: secrets.apiToken,\n\t\t\t\t// Branch-scoped gateway host derived from the branch's connection URI — not the\n\t\t\t\t// control-plane API origin (which doesn't serve the gateway).\n\t\t\t\tbaseUrl: aiGatewayBaseUrl(branch.id, unpooled.uri),\n\t\t\t} satisfies NeonAiGatewayEnv;\n\t\t}\n\t}\n\n\treturn result as NeonEnv<C>;\n}\n\n/**\n * Scopes the branch credential should carry for a resolved branch policy. Only object storage\n * and the AI Gateway *require* a credential; functions never force one (they have no credential\n * of their own), but `functions:invoke` is added to the scope set when a credential is already\n * being minted for storage / the AI Gateway, so the one credential can invoke the branch's\n * functions too. Returns `[]` only when nothing credential-bearing is enabled.\n */\nfunction previewCredentialScopes(\n\tpreview: ResolvedPreviewConfig | undefined,\n): CredentialScope[] {\n\tif (!preview) return [];\n\tconst storage = preview.buckets.length > 0;\n\tconst aiGateway = preview.aiGatewayEnabled;\n\tif (!storage && !aiGateway) return [];\n\treturn deriveCredentialScopes({\n\t\tstorage,\n\t\taiGateway,\n\t\tfunctions: preview.functions.length > 0,\n\t});\n}\n\n/**\n * Resolve the branch credential's secrets, reusing the ones already in the env source when\n * present and minting a fresh `user` credential otherwise. The Neon API returns `api_token` /\n * `s3_secret_access_key` exactly once at mint time, so the persisted copies (e.g. in\n * `.env.local`, surfaced as `OPENAI_API_KEY` / `AWS_SECRET_ACCESS_KEY`) are the only way to\n * recover them — exactly how one-time Auth keys are round-tripped. Reuse is presence-based\n * (no extra bookkeeping vars): if every secret the enabled features need is already present,\n * reuse it; otherwise mint one credential covering all currently-needed scopes.\n */\nasync function resolveCredentialSecrets(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranchId: string;\n\tbranchName: string;\n\tscopes: CredentialScope[];\n\tenv: NodeJS.ProcessEnv;\n\tneedStorage: boolean;\n\tneedApiToken: boolean;\n}): Promise<{\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\tapiToken: string;\n}> {\n\tconst sKeys = NEON_ENV_VAR_KEYS.storage;\n\tconst aKeys = NEON_ENV_VAR_KEYS.aiGateway;\n\tconst haveStorage =\n\t\t!args.needStorage ||\n\t\tBoolean(args.env[sKeys.accessKeyId] && args.env[sKeys.secretAccessKey]);\n\tconst haveApiToken = !args.needApiToken || Boolean(args.env[aKeys.apiKey]);\n\tif (haveStorage && haveApiToken) {\n\t\treturn {\n\t\t\taccessKeyId: args.env[sKeys.accessKeyId] ?? \"\",\n\t\t\tsecretAccessKey: args.env[sKeys.secretAccessKey] ?? \"\",\n\t\t\tapiToken: args.env[aKeys.apiKey] ?? \"\",\n\t\t};\n\t}\n\tconst minted = await args.api.createCredential(\n\t\targs.projectId,\n\t\targs.branchId,\n\t\t{\n\t\t\tscopes: args.scopes,\n\t\t\tprincipalType: \"user\",\n\t\t\tname: `neon-env ${args.branchName}`,\n\t\t},\n\t);\n\treturn {\n\t\taccessKeyId: minted.tokenIdShort,\n\t\tsecretAccessKey: minted.s3SecretAccessKey,\n\t\tapiToken: minted.apiToken,\n\t};\n}\n\n/**\n * The AI Gateway is a **branch-scoped host** — `<branchId>-api.ai.<region-suffix>` — NOT the\n * control-plane API origin. Derive the `<region>.<cloud>.neon.<tld>` suffix from the branch's\n * own Postgres connection host (e.g. a connection host of `ep-x.us-east-2.aws.neon.build`\n * yields the gateway host `<branchId>-api.ai.us-east-2.aws.neon.build`). Any infra cell prefix\n * (`c-N.`) on the connection host is dropped — the gateway resolves with or without it.\n */\nfunction aiGatewayHost(branchId: string, connectionUri: string): string {\n\tlet connectionHost = \"\";\n\ttry {\n\t\tconnectionHost = new URL(connectionUri).hostname;\n\t} catch {\n\t\tconnectionHost = \"\";\n\t}\n\t// Drop the endpoint label (first segment, e.g. `ep-x` / `ep-x-pooler`) and any infra cell\n\t// prefix (`c-N.`), keeping `<region>.<cloud>.neon.<tld>`.\n\tconst suffix = connectionHost\n\t\t.split(\".\")\n\t\t.slice(1)\n\t\t.join(\".\")\n\t\t.replace(/^c-\\d+\\./, \"\");\n\treturn `${branchId}-api.ai.${suffix}`;\n}\n\n/** The AI Gateway's OpenAI-dialect base URL (`OPENAI_BASE_URL`) on the branch gateway host. */\nfunction aiGatewayBaseUrl(branchId: string, connectionUri: string): string {\n\treturn `https://${aiGatewayHost(branchId, connectionUri)}${AI_GATEWAY_OPENAI_PATH}`;\n}\n\n/**\n * Resolve the Neon Auth base URL to surface in `env.auth`. Prefer the value returned by\n * the integration (`getNeonAuth` includes it); fall back to whatever is already in the\n * caller's env source so older integrations created before `base_url` was returned still\n * round-trip through `env run`.\n */\nfunction resolveAuthBaseUrl(\n\tsnapshotBaseUrl: string | undefined,\n\tsource: NodeJS.ProcessEnv,\n): string {\n\tif (snapshotBaseUrl && snapshotBaseUrl !== \"\") return snapshotBaseUrl;\n\treturn source[NEON_ENV_VAR_KEYS.auth.baseUrl] ?? \"\";\n}\n\n/**\n * Resolve the Neon Auth JWKS URL to surface in `env.auth`. Prefer the value returned by the\n * integration (`getNeonAuth` always includes `jwks_url`); fall back to the caller's env\n * source so the value still round-trips through `env run` if a snapshot ever omits it.\n */\nfunction resolveAuthJwksUrl(\n\tsnapshotJwksUrl: string | undefined,\n\tsource: NodeJS.ProcessEnv,\n): string {\n\tif (snapshotJwksUrl && snapshotJwksUrl !== \"\") return snapshotJwksUrl;\n\treturn source[NEON_ENV_VAR_KEYS.auth.jwksUrl] ?? \"\";\n}\n\nfunction createApiFromOptions(options: FetchEnvOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"fetchEnv\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`fetchEnv: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Existing branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction pickRoleName(\n\troles: NeonRoleSnapshot[],\n\tbranch: NeonBranchSnapshot,\n\trequested: string | undefined,\n): string {\n\tif (requested) {\n\t\tif (!roles.some((r) => r.name === requested)) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: role \"${requested}\" not found on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t`Existing roles: ${roles.map((r) => r.name).join(\", \") || \"(none)\"}.`,\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\troleName: requested,\n\t\t\t\t\t\tavailableRoles: roles.map((r) => r.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn requested;\n\t}\n\tif (roles.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has no roles.`,\n\t\t\t\t\"Create one via the Neon console or pass `roleName` explicitly.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { branchId: branch.id } },\n\t\t);\n\t}\n\tif (roles.length === 1) return roles[0].name;\n\n\t// Multiple roles. Enabling Neon Auth / the Data API provisions the PostgREST roles\n\t// (authenticator/anonymous/authenticated) alongside the project owner, so a normal\n\t// branch ends up with >1 role even though only the owner backs a `DATABASE_URL`.\n\t// Default to Neon's owner role; if the project was created with a custom owner name,\n\t// fall back to the single role left after dropping the managed auth roles. Only a\n\t// genuinely ambiguous set (more than one app role) still asks the caller to choose.\n\tconst owner = roles.find((r) => r.name === NEON_DEFAULT_OWNER_ROLE);\n\tif (owner) return owner.name;\n\n\tconst appRoles = roles.filter((r) => !NEON_MANAGED_AUTH_ROLES.has(r.name));\n\tif (appRoles.length === 1) return appRoles[0].name;\n\n\tthrow new PlatformError(\n\t\tErrorCode.AmbiguousBranchAuth,\n\t\t[\n\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has ${roles.length} roles and none is \"${NEON_DEFAULT_OWNER_ROLE}\"; cannot auto-pick.`,\n\t\t\t`Pass \\`roleName\\` explicitly. Available: ${roles.map((r) => r.name).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tavailableRoles: roles.map((r) => r.name),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction pickDatabaseName(\n\tdatabases: NeonDatabaseSnapshot[],\n\tbranch: NeonBranchSnapshot,\n\troleName: string,\n\trequested: string | undefined,\n): string {\n\tif (requested) {\n\t\tif (!databases.some((d) => d.name === requested)) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t[\n\t\t\t\t\t`fetchEnv: database \"${requested}\" not found on branch ${branch.name} (${branch.id}).`,\n\t\t\t\t\t`Existing databases: ${databases.map((d) => d.name).join(\", \") || \"(none)\"}.`,\n\t\t\t\t].join(\" \"),\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchId: branch.id,\n\t\t\t\t\t\tdatabaseName: requested,\n\t\t\t\t\t\tavailableDatabases: databases.map((d) => d.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn requested;\n\t}\n\tif (databases.length === 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.BranchNotFound,\n\t\t\t[\n\t\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has no databases.`,\n\t\t\t\t\"Create one via the Neon console or pass `databaseName` explicitly.\",\n\t\t\t].join(\" \"),\n\t\t\t{ details: { branchId: branch.id } },\n\t\t);\n\t}\n\tif (databases.length === 1) return databases[0].name;\n\n\t// Prefer a database owned by the role we're connecting as.\n\tconst owned = databases.filter((d) => d.ownerName === roleName);\n\tif (owned.length === 1) return owned[0].name;\n\n\tthrow new PlatformError(\n\t\tErrorCode.AmbiguousBranchAuth,\n\t\t[\n\t\t\t`fetchEnv: branch ${branch.name} (${branch.id}) has ${databases.length} databases; cannot auto-pick.`,\n\t\t\t`Pass \\`databaseName\\` explicitly. Available: ${databases.map((d) => d.name).join(\", \")}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId: branch.id,\n\t\t\t\tavailableDatabases: databases.map((d) => d.name),\n\t\t\t},\n\t\t},\n\t);\n}\n\n// ───────────────────────── parseEnv ─────────────────────────\n\n/**\n * Per-namespace zod schemas. Each defines exactly the OS-level keys parsed from\n * `process.env` for its namespace. Keep in sync with {@link NEON_ENV_VAR_KEYS}.\n *\n * `z.string().url()` would be tighter than `min(1)` but Postgres URIs that include\n * URL-illegal characters in the password (rare but legal in Neon's connection-string\n * format) fail the WHATWG `URL` parse, so we settle for \"non-empty string\".\n */\nconst postgresEnvSchema = z.object({\n\tDATABASE_URL: z\n\t\t.string({ message: \"DATABASE_URL is missing\" })\n\t\t.min(1, \"DATABASE_URL must not be empty\"),\n\tDATABASE_URL_UNPOOLED: z\n\t\t.string({ message: \"DATABASE_URL_UNPOOLED is missing\" })\n\t\t.min(1, \"DATABASE_URL_UNPOOLED must not be empty\"),\n});\n\nconst authEnvSchema = z.object({\n\tNEON_AUTH_BASE_URL: z\n\t\t.string({ message: \"NEON_AUTH_BASE_URL is missing\" })\n\t\t.min(1, \"NEON_AUTH_BASE_URL must not be empty\"),\n\tNEON_AUTH_JWKS_URL: z\n\t\t.string({ message: \"NEON_AUTH_JWKS_URL is missing\" })\n\t\t.min(1, \"NEON_AUTH_JWKS_URL must not be empty\"),\n});\n\nconst dataApiEnvSchema = z.object({\n\tNEON_DATA_API_URL: z\n\t\t.string({ message: \"NEON_DATA_API_URL is missing\" })\n\t\t.min(1, \"NEON_DATA_API_URL must not be empty\"),\n});\n\nconst storageEnvSchema = z.object({\n\tAWS_ACCESS_KEY_ID: z\n\t\t.string({ message: \"AWS_ACCESS_KEY_ID is missing\" })\n\t\t.min(1, \"AWS_ACCESS_KEY_ID must not be empty\"),\n\tAWS_SECRET_ACCESS_KEY: z\n\t\t.string({ message: \"AWS_SECRET_ACCESS_KEY is missing\" })\n\t\t.min(1, \"AWS_SECRET_ACCESS_KEY must not be empty\"),\n\tAWS_ENDPOINT_URL_S3: z\n\t\t.string({ message: \"AWS_ENDPOINT_URL_S3 is missing\" })\n\t\t.min(1, \"AWS_ENDPOINT_URL_S3 must not be empty\"),\n\tAWS_REGION: z\n\t\t.string({ message: \"AWS_REGION is missing\" })\n\t\t.min(1, \"AWS_REGION must not be empty\"),\n});\n\nconst aiGatewayEnvSchema = z.object({\n\tOPENAI_API_KEY: z\n\t\t.string({ message: \"OPENAI_API_KEY is missing\" })\n\t\t.min(1, \"OPENAI_API_KEY must not be empty\"),\n\tOPENAI_BASE_URL: z\n\t\t.string({ message: \"OPENAI_BASE_URL is missing\" })\n\t\t.min(1, \"OPENAI_BASE_URL must not be empty\"),\n});\n\n/** Whether a **static** policy declares object storage (`preview.buckets`). No network. */\nfunction configWantsStorage(config: Config): boolean {\n\treturn Object.keys(config.preview?.buckets ?? {}).length > 0;\n}\n\n/** Whether a **static** policy enables the AI Gateway (`preview.aiGateway`). No network. */\nfunction configWantsAiGateway(config: Config): boolean {\n\treturn isServiceEnabledInput(config.preview?.aiGateway);\n}\n\n/** Parse the `NEON_STORAGE_FORCE_PATH_STYLE` env string into a boolean (defaults to `true`). */\nfunction parseForcePathStyle(value: string | undefined): boolean {\n\tif (value === undefined) return true;\n\treturn value.trim().toLowerCase() !== \"false\";\n}\n\n/** Static-toggle helper mirroring `config`'s `isServiceEnabled` for the env reader. */\nfunction isServiceEnabledInput(\n\ttoggle: ServiceToggleInput | undefined,\n): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/**\n * Synchronous, network-free counterpart to {@link fetchEnv}. Reads `process.env`, validates\n * the required Neon env vars with zod, and returns the same {@link NeonEnv} shape — so the\n * rest of your app touches `env.postgres.databaseUrl` instead of stringly-typed\n * `process.env.DATABASE_URL` lookups.\n *\n * Designed for the **\"env-vars-already-injected\"** path:\n * - You wrapped your dev command with `neon-env run -- <cmd>` or `neon dev`.\n * - Your platform (Vercel, Fly, Railway, …) injected the vars via its own integration.\n * - You are **inside a deployed Neon Function**, whose env was uploaded at `config apply`.\n *\n * Unlike the old API, `parseEnv` does **not** take a branch name: the secret set is now\n * static (top-level `config.auth` / `config.dataApi`), so it reads those directly without\n * evaluating the per-branch closure.\n *\n * The second argument is a **scope**:\n * - omitted — *external* scope (app bootstrap, build scripts, your dev machine). Returns\n * `{ postgres, auth?, dataApi? }`.\n * - a **function slug** (a key of `config.preview.functions`) — *function* scope: you are\n * running inside that function. Returns the same branch secrets **plus** a typed\n * `function` namespace with the function's declared env-var keys.\n *\n * Throws `PlatformError(EnvNotInjected)` listing every missing/invalid var when the env\n * isn't fully populated, with a fix hint pointing back at `neon dev` / `neon-env run`.\n *\n * ```ts\n * import config from \"../neon\";\n * import { parseEnv } from \"@neondatabase/env/v1\";\n *\n * // External (app / build):\n * const env = parseEnv(config);\n * const db = drizzle(neon(env.postgres.databaseUrl), { schema });\n *\n * // Inside the \"hello\" function:\n * const env = parseEnv(config, \"hello\");\n * env.function.resendApiKey; // typed from hello's declared env keys\n * ```\n */\nexport function parseEnv<const C extends Config>(config: C): NeonEnv<C>;\nexport function parseEnv<\n\tconst C extends Config,\n\tconst S extends FunctionSlugOf<C>,\n>(config: C, scope: S): NeonEnv<C> & NeonFunctionEnv<C, S>;\nexport function parseEnv(config: Config, scope?: string): unknown {\n\tconst source = process.env;\n\tconst issues: string[] = [];\n\tconst result: Record<string, unknown> = {};\n\n\tconst pg = postgresEnvSchema.safeParse({\n\t\tDATABASE_URL: source.DATABASE_URL,\n\t\tDATABASE_URL_UNPOOLED: source.DATABASE_URL_UNPOOLED,\n\t});\n\tif (pg.success) {\n\t\tresult.postgres = {\n\t\t\tdatabaseUrl: pg.data.DATABASE_URL,\n\t\t\tdatabaseUrlUnpooled: pg.data.DATABASE_URL_UNPOOLED,\n\t\t} satisfies NeonPostgresEnv;\n\t} else {\n\t\tfor (const issue of pg.error.issues) issues.push(issue.message);\n\t}\n\n\tif (isServiceEnabledInput(config.auth)) {\n\t\tconst auth = authEnvSchema.safeParse({\n\t\t\tNEON_AUTH_BASE_URL: source.NEON_AUTH_BASE_URL,\n\t\t\tNEON_AUTH_JWKS_URL: source.NEON_AUTH_JWKS_URL,\n\t\t});\n\t\tif (auth.success) {\n\t\t\tresult.auth = {\n\t\t\t\tbaseUrl: auth.data.NEON_AUTH_BASE_URL,\n\t\t\t\tjwksUrl: auth.data.NEON_AUTH_JWKS_URL,\n\t\t\t} satisfies NeonAuthEnv;\n\t\t} else {\n\t\t\tfor (const issue of auth.error.issues) issues.push(issue.message);\n\t\t}\n\t}\n\n\tif (isServiceEnabledInput(config.dataApi)) {\n\t\tconst dataApi = dataApiEnvSchema.safeParse({\n\t\t\tNEON_DATA_API_URL: source.NEON_DATA_API_URL,\n\t\t});\n\t\tif (dataApi.success) {\n\t\t\tresult.dataApi = {\n\t\t\t\turl: dataApi.data.NEON_DATA_API_URL,\n\t\t\t} satisfies NeonDataApiEnv;\n\t\t} else {\n\t\t\tfor (const issue of dataApi.error.issues)\n\t\t\t\tissues.push(issue.message);\n\t\t}\n\t}\n\n\tif (configWantsStorage(config)) {\n\t\tconst storage = storageEnvSchema.safeParse({\n\t\t\tAWS_ACCESS_KEY_ID: source.AWS_ACCESS_KEY_ID,\n\t\t\tAWS_SECRET_ACCESS_KEY: source.AWS_SECRET_ACCESS_KEY,\n\t\t\tAWS_ENDPOINT_URL_S3: source.AWS_ENDPOINT_URL_S3,\n\t\t\tAWS_REGION: source.AWS_REGION,\n\t\t});\n\t\tif (storage.success) {\n\t\t\tresult.storage = {\n\t\t\t\taccessKeyId: storage.data.AWS_ACCESS_KEY_ID,\n\t\t\t\tsecretAccessKey: storage.data.AWS_SECRET_ACCESS_KEY,\n\t\t\t\tendpoint: storage.data.AWS_ENDPOINT_URL_S3,\n\t\t\t\tregion: storage.data.AWS_REGION,\n\t\t\t\tforcePathStyle: parseForcePathStyle(\n\t\t\t\t\tsource.NEON_STORAGE_FORCE_PATH_STYLE,\n\t\t\t\t),\n\t\t\t} satisfies NeonStorageEnv;\n\t\t} else {\n\t\t\tfor (const issue of storage.error.issues)\n\t\t\t\tissues.push(issue.message);\n\t\t}\n\t}\n\n\tif (configWantsAiGateway(config)) {\n\t\tconst aiGateway = aiGatewayEnvSchema.safeParse({\n\t\t\tOPENAI_API_KEY: source.OPENAI_API_KEY,\n\t\t\tOPENAI_BASE_URL: source.OPENAI_BASE_URL,\n\t\t});\n\t\tif (aiGateway.success) {\n\t\t\tresult.aiGateway = {\n\t\t\t\tapiKey: aiGateway.data.OPENAI_API_KEY,\n\t\t\t\tbaseUrl: aiGateway.data.OPENAI_BASE_URL,\n\t\t\t} satisfies NeonAiGatewayEnv;\n\t\t} else {\n\t\t\tfor (const issue of aiGateway.error.issues)\n\t\t\t\tissues.push(issue.message);\n\t\t}\n\t}\n\n\tif (scope !== undefined) {\n\t\tconst fn = config.preview?.functions?.[scope];\n\t\tif (!fn) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.EnvNotInjected,\n\t\t\t\t[\n\t\t\t\t\t`parseEnv: no function \"${scope}\" is declared in this policy's preview.functions.`,\n\t\t\t\t\t\"Pass a declared function slug (or omit the scope to read external env).\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t\t{ details: { scope } },\n\t\t\t);\n\t\t}\n\t\tconst envOut: Record<string, string> = {};\n\t\tfor (const key of Object.keys(fn.env ?? {})) {\n\t\t\tconst value = source[key];\n\t\t\t// Only a truly *unset* var is \"not injected\". Function env values carry no\n\t\t\t// non-empty constraint (unlike DATABASE_URL / NEON_AUTH_BASE_URL), so a\n\t\t\t// deliberately empty value is a present, valid value and is passed through.\n\t\t\tif (value === undefined) {\n\t\t\t\tissues.push(`${key} is missing (function \"${scope}\")`);\n\t\t\t} else {\n\t\t\t\tenvOut[key] = value;\n\t\t\t}\n\t\t}\n\t\tresult.function = envOut;\n\t}\n\n\tif (issues.length > 0) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.EnvNotInjected,\n\t\t\t[\n\t\t\t\t\"parseEnv: the required Neon env variables are not present in process.env.\",\n\t\t\t\t...issues.map((i) => ` - ${i}`),\n\t\t\t\t\"Inject them via one of:\",\n\t\t\t\t\" - `neon dev` / `neon-env run -- <your dev command>` (wraps the command with the vars injected)\",\n\t\t\t\t\" - your hosting platform's Neon integration (Vercel, Fly, Railway, …)\",\n\t\t\t\t\" - for the `function` namespace: deploy the function (`neon deploy` / `config apply`) so its env is uploaded.\",\n\t\t\t\t\"Or switch the call to `await fetchEnv(config, …)` if you're in a context that can do async I/O.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ details: { missing: issues } },\n\t\t);\n\t}\n\n\treturn result;\n}\n\n// ───────────────────────── env-var mapping helpers ─────────────────────────\n\n/**\n * Project a fully-resolved {@link NeonEnv} into the OS-level `{ KEY: value }` pairs used\n * for cross-process transport. Named after the web-platform `.entries()` convention\n * (`URLSearchParams` / `Headers` / `FormData`); returns a `Record` rather than an\n * iterator of tuples since that's the shape env injection needs (wrap with\n * `Object.entries(...)` if you want literal `[key, value]` pairs). Used by `neon-env run`\n * to inject the vars into a subprocess's `process.env`.\n *\n * Walks the value at runtime so it works for any `NeonEnv<C>` regardless of which\n * conditional namespaces are present.\n */\nexport function toEntries(env: NeonEnv<Config>): Record<string, string> {\n\tconst out: Record<string, string> = {\n\t\t[NEON_ENV_VAR_KEYS.postgres.databaseUrl]: env.postgres.databaseUrl,\n\t\t[NEON_ENV_VAR_KEYS.postgres.databaseUrlUnpooled]:\n\t\t\tenv.postgres.databaseUrlUnpooled,\n\t};\n\tconst withAuth = env as { auth?: NeonAuthEnv };\n\tif (withAuth.auth) {\n\t\tout[NEON_ENV_VAR_KEYS.auth.baseUrl] = withAuth.auth.baseUrl;\n\t\tout[NEON_ENV_VAR_KEYS.auth.jwksUrl] = withAuth.auth.jwksUrl;\n\t}\n\tconst withDataApi = env as { dataApi?: NeonDataApiEnv };\n\tif (withDataApi.dataApi) {\n\t\tout[NEON_ENV_VAR_KEYS.dataApi.url] = withDataApi.dataApi.url;\n\t}\n\tconst withStorage = env as { storage?: NeonStorageEnv };\n\tif (withStorage.storage) {\n\t\tconst s = withStorage.storage;\n\t\tconst keys = NEON_ENV_VAR_KEYS.storage;\n\t\tout[keys.accessKeyId] = s.accessKeyId;\n\t\tout[keys.secretAccessKey] = s.secretAccessKey;\n\t\tout[keys.endpoint] = s.endpoint;\n\t\t// Region is injected under both the AWS-standard and the Neon-specific name.\n\t\tout[keys.region] = s.region;\n\t\tout[keys.regionNeon] = s.region;\n\t\tout[keys.forcePathStyle] = String(s.forcePathStyle);\n\t}\n\tconst withAiGateway = env as { aiGateway?: NeonAiGatewayEnv };\n\tif (withAiGateway.aiGateway) {\n\t\tconst keys = NEON_ENV_VAR_KEYS.aiGateway;\n\t\tconst ai = withAiGateway.aiGateway;\n\t\tout[keys.apiKey] = ai.apiKey;\n\t\tout[keys.baseUrl] = ai.baseUrl;\n\t\t// Neon-branded aliases: the same bearer, plus the bare branch gateway host\n\t\t// (scheme://host, no path) — the @ai-sdk/neon provider appends the\n\t\t// /ai-gateway/<dialect>/… routes itself (https://github.com/vercel/ai/pull/15997).\n\t\tout[keys.neonToken] = ai.apiKey;\n\t\tout[keys.neonBaseUrl] = new URL(ai.baseUrl).origin;\n\t}\n\treturn out;\n}\n"],"mappings":"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"}
|
package/dist/v1.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { FetchEnvOptions, FunctionSlugOf, NEON_ENV_VAR_KEYS, NeonAuthEnv, NeonDataApiEnv, NeonEnv, NeonFunctionEnv, NeonPostgresEnv, fetchEnv, parseEnv, toEntries } from "./lib/env.js";
|
|
2
|
-
export { type FetchEnvOptions, type FunctionSlugOf, NEON_ENV_VAR_KEYS, type NeonAuthEnv, type NeonDataApiEnv, type NeonEnv, type NeonFunctionEnv, type NeonPostgresEnv, fetchEnv, parseEnv, toEntries };
|
|
1
|
+
import { FetchEnvOptions, FunctionSlugOf, NEON_ENV_VAR_KEYS, NeonAiGatewayEnv, NeonAuthEnv, NeonDataApiEnv, NeonEnv, NeonFunctionEnv, NeonPostgresEnv, NeonStorageEnv, fetchEnv, parseEnv, toEntries } from "./lib/env.js";
|
|
2
|
+
export { type FetchEnvOptions, type FunctionSlugOf, NEON_ENV_VAR_KEYS, type NeonAiGatewayEnv, type NeonAuthEnv, type NeonDataApiEnv, type NeonEnv, type NeonFunctionEnv, type NeonPostgresEnv, type NeonStorageEnv, fetchEnv, parseEnv, toEntries };
|