@neondatabase/env 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -55,7 +55,7 @@ Both return the same namespaced `NeonEnv` shape: `postgres` is always present; `
55
55
  | Function | Description |
56
56
  | --- | --- |
57
57
  | `fetchEnv(config, { projectId, branch, ... })` | Async. Calls the Neon API for the given project + branch and returns live connection strings (and Auth/Data API values when enabled). `projectId` and `branch` are required; `branch` accepts a branch **name** (e.g. `main`) or a `br-…` id. (The legacy id-only `branchId` option still works.) Pass `keys` to fetch only some vars — see [Fetching a subset](#fetching-a-subset). Reads nothing from `process.env` or disk. |
58
- | `fetchEnvReusingSecrets(config, { projectId, branch, env })` | Async, from **`@neon/env/runtime`**. `fetchEnv` plus reuse of one-time secrets you already hold: verifies them against the branch, keeps what's valid, mints and revokes only when it must. Returns `{ vars, credential }`. Use this rather than `fetchEnv` anywhere the same branch is resolved repeatedly — see [The branch credential](#the-branch-credential). |
58
+ | `fetchEnvReusingSecrets(config, { projectId, branch, env })` | Async, from **`@neon/env/runtime`**. `fetchEnv` plus reuse of one-time secrets you already hold: verifies them against the branch, keeps what's valid, mints and revokes only when it must. Returns `{ vars, credential }`. Takes an optional `revokeSuperseded`, which keeps the replaced credential live when your call resolves only part of the branch. Use this rather than `fetchEnv` anywhere the same branch is resolved repeatedly — see [The branch credential](#the-branch-credential). |
59
59
  | `parseEnv(config)` / `parseEnv(config, slug)` / `parseEnv(config, keys)` | Sync. Reads/validates the Neon env vars already present in `process.env` against the static policy toggles. With a function `slug`, also returns a typed `function` namespace of that function's declared env keys. With a `keys` array (e.g. `["DATABASE_URL"]`), only those vars are required and returned, as a narrowed namespaced shape — the keys are typesafe against the policy. Throws `PlatformError(EnvNotInjected)` listing missing vars when the env isn't populated. |
60
60
  | `toEntries(env)` | Project a resolved `NeonEnv` into `{ KEY: value }` pairs for cross-process transport (named after the web `.entries()` convention; returns a `Record`). |
61
61
 
@@ -149,7 +149,8 @@ const { vars, credential } = await fetchEnvReusingSecrets(config, {
149
149
  // vars: { DATABASE_URL: "…", AWS_ACCESS_KEY_ID: "…", … } — ready to write or inject
150
150
  if (credential.issued) {
151
151
  console.log(`new values for ${credential.keys.join(", ")}`);
152
- // credential.revoked holds the token ids it superseded
152
+ // credential.revoked ids it replaced and revoked
153
+ // credential.superseded — ids it replaced but left live (`revokeSuperseded: false`)
153
154
  }
154
155
  ```
155
156
 
@@ -157,6 +158,8 @@ The check is a real verification, not a presence test. A persisted secret is kep
157
158
 
158
159
  No local bookkeeping backs this: `AWS_ACCESS_KEY_ID` **is** the credential's token id, and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`, so the persisted secrets already name the credential that issued them.
159
160
 
161
+ Revoking is only safe because the call resolves everything the policy enables. Pass `revokeSuperseded: false` when yours resolves a **subset** — the credential your persisted secrets name may also back a service you are not resolving, and revoking it would break that service while its vars, which you are not rewriting, stay in place. The cost is an orphaned credential, which is the safer of the two failures, and `credential.superseded` names it so you can report it rather than leave it invisible. `neon env pull --service` is the caller this exists for.
162
+
160
163
  ### Fetching a subset
161
164
 
162
165
  `fetchEnv` takes a `keys` filter, the same typesafe selection `parseEnv` accepts:
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":["ComputeUnit","DurationUnit","DurationString","SuspendTimeoutSuggestion","TtlSuggestion","DurationField","Suggestions","NonNullable","ComputeSettings","BranchTarget","ServiceToggle","ServiceToggleInput","ServiceEnabled","T","PostgresConfig","DATA_API_AUTH_PROVIDERS","DataApiAuthProvider","DataApiSettings","DataApiConfigBase","DataApiNeonAuthConfig","DataApiExternalAuthConfig","DataApiConfig","DataApiInput","FunctionRuntime","FunctionDevConfig","FunctionDef","Record","CredentialScope","CredentialPrincipalType","BucketAccessLevel","BucketDef","PreviewInput","FunctionTuning","PreviewTuning","Slug","Partial","BranchTuning","FunctionSlugsOf","Preview","F","Extract","BranchTuningFn","Config","Auth","DataApi","ResolvedFunctionConfig","ResolvedBucketConfig","ResolvedPreviewConfig","ResolvedDataApiConfig","ResolvedBranchConfig","AppliedChange","ConflictReport","PushResult"],"sources":["../../../../../config/dist/lib/types.d.ts"],"sourcesContent":["//#region src/lib/types.d.ts\n/**\n * Valid Neon Compute Unit values.\n * Most plans support 0.25, 0.5, 1, 2, 4, 8. Higher values may be available on Business plans.\n */\ntype ComputeUnit = 0.25 | 0.5 | 1 | 2 | 4 | 8;\n/** Time units accepted in a {@link DurationString}: seconds, minutes, hours, days, weeks. */\ntype DurationUnit = \"s\" | \"m\" | \"h\" | \"d\" | \"w\";\n/**\n * A Neon duration string: a positive integer **followed by a unit** — `s` (seconds),\n * `m` (minutes), `h` (hours), `d` (days), or `w` (weeks). Used by\n * {@link ComputeSettings.suspendTimeout} and {@link BranchTuning.ttl}.\n *\n * A **unit is required**: a bare numeric string like `\"7\"` is rejected at the type level. To\n * express a raw number of seconds, pass a `number` (`300`) — not a string (`\"300\"`). This\n * removes the old ambiguity where `\"7\"` silently meant 7 *seconds* instead of, say, `\"7d\"`.\n *\n * @example \"5m\" // 5 minutes\n * @example \"1h\" // 1 hour\n * @example \"7d\" // 7 days\n */\ntype DurationString = `${number}${DurationUnit}`;\n/**\n * Autocomplete suggestions for {@link ComputeSettings.suspendTimeout}. Every value sits inside\n * the Neon API's allowed scale-to-zero band: **60s–604800s** (1 minute – 1 week). This is *not*\n * a closed set — the field also accepts any other {@link DurationString} or a `number` of\n * seconds; out-of-range values type-check but are rejected at apply time.\n */\ntype SuspendTimeoutSuggestion = \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\" | \"6h\" | \"12h\" | \"1d\" | \"7d\";\n/**\n * Autocomplete suggestions for {@link BranchTuning.ttl}. Every value sits within the Neon API's\n * branch-expiration limit (**max 30 days** from creation; the Console's own presets are 1h / 1d\n * / 7d). This is *not* a closed set — the field also accepts any other {@link DurationString} or\n * a `number` of seconds; values over 30 days are rejected at apply time.\n */\ntype TtlSuggestion = \"1h\" | \"6h\" | \"12h\" | \"1d\" | \"3d\" | \"7d\" | \"14d\" | \"30d\";\n/**\n * Compose a field's duration type: its curated autocomplete `Suggestions` plus the open\n * `DurationString` template (so any `<integer><unit>` string still type-checks) and a `number`\n * of seconds. Intersecting the template arm with `NonNullable<unknown>` stops TypeScript from\n * collapsing the literal suggestions into the template, which is what preserves the autocomplete.\n */\ntype DurationField<Suggestions extends DurationString> = Suggestions | (DurationString & NonNullable<unknown>) | number;\n/**\n * Compute settings applied to the read/write endpoint of a branch.\n *\n * Mirrors the subset of {@link https://api-docs.neon.tech/reference/getting-started-with-neon-api Neon endpoint}\n * fields that we expose as IaC primitives. Anything left undefined falls back to the project's\n * `default_endpoint_settings` (which themselves fall back to Neon defaults).\n */\ninterface ComputeSettings {\n /**\n * Minimum number of Compute Units. Set to 0.25 for true scale-to-zero.\n * @example 0.25 // scale-to-zero\n * @example 1 // always-on with 1 CU minimum\n */\n autoscalingLimitMinCu?: ComputeUnit;\n /**\n * Maximum number of Compute Units for autoscaling.\n * @example 2\n * @example 8\n */\n autoscalingLimitMaxCu?: ComputeUnit;\n /**\n * How long an idle compute waits before suspending (Neon's scale-to-zero). Accepts a\n * {@link DurationString} (autocompletes common values), a number of seconds, or `false`.\n *\n * - `false` — never suspend (always-on compute)\n * - {@link DurationString} — e.g. `\"5m\"`; autocompletes the in-range values `\"1m\"`, `\"5m\"`,\n * `\"15m\"`, `\"30m\"`, `\"1h\"`, `\"6h\"`, `\"12h\"`, `\"1d\"`, `\"7d\"`, and accepts any other\n * `<integer><unit>` (units: `s`, `m`, `h`, `d`, `w`). A **unit is required** — for raw\n * seconds pass a `number`, not a string.\n * - `number` — custom timeout in **seconds**, must be in `60`–`604800` (1 minute to 1 week)\n * - `undefined` — use the Neon default (currently 300s / 5 minutes)\n *\n * Whichever form you use, the resolved timeout must fall in `60`–`604800` seconds (the Neon\n * API limit); the suggestions are all within that band, anything else is checked at apply.\n *\n * @example false // never suspend (always-on)\n * @example \"5m\" // suspend after 5 minutes idle\n * @example \"1h\" // suspend after 1 hour idle\n * @example 300 // 5 minutes, expressed in seconds\n */\n suspendTimeout?: false | DurationField<SuspendTimeoutSuggestion>;\n}\n/**\n * Read-only descriptor of the branch a {@link Config} policy is being evaluated for — the\n * `branch` argument passed to your `defineConfig({ branch: (branch) => … })` closure. It describes\n * **which** branch this invocation decides for; it is not a live branch handle and must not\n * be mutated. Switch on its fields and return the desired {@link BranchConfig}.\n */\ninterface BranchTarget {\n /** Branch name being evaluated. For `branch dev`, this is the generated branch name. */\n name: string;\n /** Neon branch id when the branch already exists. Undefined during pre-create eval. */\n id?: string;\n /** Whether this branch already exists on Neon. */\n exists: boolean;\n /** Parent branch id from Neon when known. */\n parentId?: string;\n /** Whether Neon marks this branch as the project default. */\n isDefault?: boolean;\n /** Whether Neon currently marks this branch protected. */\n isProtected?: boolean;\n /** Current expiration timestamp from Neon, when set. */\n expiresAt?: string;\n}\n/**\n * Object form of a branch-scoped service toggle. `{}` or `{ enabled: true }` enables it;\n * `{ enabled: false }` opts out. Used as the object half of {@link ServiceToggleInput}.\n */\ninterface ServiceToggle {\n /** Defaults to `true` when the service namespace is present. Set `false` to opt out. */\n enabled?: boolean;\n}\n/**\n * How a branch-scoped service (Neon Auth, Data API, AI Gateway) is toggled in a policy.\n *\n * - `true` / `{}` / `{ enabled: true }` — enabled.\n * - `false` / `{ enabled: false }` — disabled.\n * - omitted (`undefined`) — not part of the policy at all.\n *\n * These toggles are **static** (they live in the top-level `defineConfig({ … })` object,\n * not in the per-branch `branch` closure) so the secret set they imply can be derived at\n * the type level — that's what makes `NeonEnv<typeof config>` exact.\n */\ntype ServiceToggleInput = boolean | ServiceToggle;\n/**\n * Resolve a **static** service toggle (`true` / `false` / `{ enabled?: boolean }` / object /\n * `undefined`) to a type-level boolean. The tuple wrapping (`[T] extends […]`) disables\n * distribution so a union/`undefined` is judged as a single 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 | … | undefined` (no literal info) → `false`\n *\n * Shared by the {@link Config} static cross-field checks and the `@neon/env`\n * `NeonEnv` namespace derivation, so both read \"is this service on?\" identically.\n */\ntype ServiceEnabled<T> = [T] extends [false] ? false : [T] extends [{\n enabled: false;\n}] ? false : [T] extends [undefined] ? false : [T] extends [true] ? true : [T] extends [{\n enabled: true;\n}] ? true : [T] extends [object] ? true : false;\ninterface PostgresConfig {\n computeSettings?: ComputeSettings;\n}\n/**\n * Authentication providers a Data API integration can verify JWTs against, as written in\n * `neon.ts`. Friendly authoring values (mapped to the Neon API's `neon_auth` / `external`\n * at the API boundary):\n *\n * - `\"neon\"` — verify tokens minted by **Neon Auth** on the same branch. Neon supplies the\n * JWKS URL / provider wiring for you, so the `jwksUrl` / `providerName` / `jwtAudience`\n * fields are forbidden (a type error) on this variant — and the policy must also enable\n * top-level `auth` (Neon Auth) so the tokens exist.\n * - `\"external\"` — verify tokens from a third-party IdP (Clerk, Stytch, Auth0, …). You\n * provide `jwksUrl` (and optionally `providerName` / `jwtAudience`).\n */\ndeclare const DATA_API_AUTH_PROVIDERS: readonly [\"neon\", \"external\"];\ntype DataApiAuthProvider = (typeof DATA_API_AUTH_PROVIDERS)[number];\n/**\n * Reusable runtime settings for a Data API integration (the Neon API `DataAPISettings`,\n * camelCased to match the rest of `neon.ts`). Every field is optional; omitted fields keep\n * the Neon defaults shown below. These are the **only** Data API fields that can change on\n * an already-enabled integration — drift here is reconciled as an *update* (requires\n * `updateExisting` / `--update-existing`); the create-only auth wiring above cannot.\n */\ninterface DataApiSettings {\n /** Enable the aggregates feature (`db_aggregates_enabled`). Default `true`. */\n dbAggregatesEnabled?: boolean;\n /** Database role used for anonymous requests (`db_anon_role`). Default `\"anonymous\"`. */\n dbAnonRole?: string;\n /** Extra schemas appended to the search path (`db_extra_search_path`). */\n dbExtraSearchPath?: string;\n /** Maximum rows returned in a single request (`db_max_rows`). */\n dbMaxRows?: number;\n /** Schemas exposed via the API (`db_schemas`). Default `[\"public\"]`. */\n dbSchemas?: string[];\n /** JWT claim key used for role extraction (`jwt_role_claim_key`). Default `\".role\"`. */\n jwtRoleClaimKey?: string;\n /** Maximum lifetime of the JWT cache, in seconds (`jwt_cache_max_lifetime`). */\n jwtCacheMaxLifetime?: number;\n /** OpenAPI spec mode (`openapi_mode`). Default `\"disabled\"`. */\n openapiMode?: \"ignore-privileges\" | \"disabled\";\n /** CORS allowed origins (`server_cors_allowed_origins`). */\n serverCorsAllowedOrigins?: string;\n /** Emit server-timing headers (`server_timing_enabled`). */\n serverTimingEnabled?: boolean;\n}\n/** Fields shared by every {@link DataApiConfig} variant. */\ninterface DataApiConfigBase {\n /** Defaults to `true` when the `dataApi` namespace is present. Set `false` to opt out. */\n enabled?: boolean;\n /** Reusable runtime settings. Drift here is reconciled as an update. */\n settings?: DataApiSettings;\n}\n/**\n * Data API verified by **Neon Auth** (`authProvider: \"neon\"`, the default). The external\n * IdP fields are statically forbidden (`?: never`) because Neon supplies them; declaring any\n * of them is a type error directing you to `authProvider: \"external\"`.\n */\ninterface DataApiNeonAuthConfig extends DataApiConfigBase {\n authProvider?: \"neon\";\n /** Forbidden with `authProvider: \"neon\"` — Neon provides the JWKS URL. */\n jwksUrl?: never;\n /** Forbidden with `authProvider: \"neon\"` — the provider is Neon Auth. */\n providerName?: never;\n /** Forbidden with `authProvider: \"neon\"` — Neon manages the audience. */\n jwtAudience?: never;\n}\n/**\n * Data API verified by an **external** IdP (`authProvider: \"external\"`). You provide the\n * JWKS URL (and optionally a provider label / expected audience).\n */\ninterface DataApiExternalAuthConfig extends DataApiConfigBase {\n authProvider: \"external\";\n /** URL that publishes the IdP's JWKS (JSON Web Key Set). */\n jwksUrl?: string;\n /** Human label for the IdP (e.g. \"Clerk\", \"Stytch\", \"Auth0\"). */\n providerName?: string;\n /**\n * Expected `aud` claim. ⚠️ This only **rejects** tokens carrying a *different* audience;\n * tokens with no `aud` claim are still accepted.\n */\n jwtAudience?: string;\n}\n/**\n * Object form of the `dataApi` toggle. A discriminated union on {@link DataApiAuthProvider}:\n * the `\"neon\"` variant forbids the external-IdP fields, the `\"external\"` variant allows them.\n */\ntype DataApiConfig = DataApiNeonAuthConfig | DataApiExternalAuthConfig;\n/**\n * How the Data API is toggled in a policy: a bare boolean (like the other service toggles)\n * or the richer {@link DataApiConfig} object. `true` / `{}` / `{ enabled: true }` enable it\n * with Neon defaults; `false` / `{ enabled: false }` opt out.\n */\ntype DataApiInput = boolean | DataApiConfig;\n/**\n * Supported function runtimes. Mirrors the Neon Functions deploy API `runtime` enum.\n * Only `nodejs24` exists today; kept as a union so adding runtimes later is a\n * non-breaking, type-checked change.\n */\ntype FunctionRuntime = \"nodejs24\";\n/**\n * Local-development settings for a function, used by `neon dev` when it serves every\n * function declared in `neon.ts` (i.e. invoked with no `--source`). Never affects deploy.\n */\ninterface FunctionDevConfig {\n /**\n * Port the local server binds. Bound exactly (and `neon dev` fails loudly if it is taken)\n * when set; a free port is found automatically when omitted.\n */\n port?: number;\n}\n/**\n * Static definition of a Neon Function (Preview feature). Declares that the function\n * **exists** on every branch; its branch-unique slug is the **record key** in\n * {@link PreviewInput.functions} (not a field here), so slugs are statically enumerable,\n * cannot duplicate, and the `branch` closure can only tune slugs that are declared here.\n *\n * A function is invoked like a Cloudflare/Vercel handler — its source module\n * `export default { fetch }` or `export async function handler(req): Response`. The\n * `source` path is bundled (esbuild) and uploaded as a deployment; the newest deployment\n * becomes active.\n *\n * Runtime tuning is **not** here — it varies per branch and lives in the `branch` closure\n * (see {@link FunctionTuning}). Memory is fixed by the platform policy for now and is not\n * user-configurable.\n */\ninterface FunctionDef {\n /** Free-form display name. @example \"Hello World\" */\n name: string;\n /**\n * Path to the function's entry module, **relative to `neon.ts`** (or absolute). The\n * module's default export (`{ fetch }`) or `handler` export is the function entry. This\n * path is resolved against the loaded `neon.ts` location and bundled with esbuild at\n * deploy time.\n *\n * We require a string path rather than an imported handler because a JS function value\n * carries no reference back to its source file, so esbuild has nothing to bundle from.\n * @example \"./functions/hello-world.ts\"\n */\n source: string;\n /**\n * Environment variables injected into the deployed function, keyed by the var name the\n * function reads at runtime. The **keys** are static (preserved at the type level so\n * `parseEnv(config, \"<slug>\").function.<key>` is typed); the **values** are arbitrary\n * strings evaluated when `neon.ts` is loaded (typically `process.env.X`) and uploaded\n * at `config apply`. Every value must be a defined string — a `process.env.X` that is\n * `undefined` (unset) errors at validation time rather than silently shipping\n * `undefined`.\n * @example { resendApiKey: process.env.RESEND_API_KEY ?? \"\" }\n */\n env?: Record<string, string>;\n /**\n * Packages the bundler must leave alone, by name — the deploy-time equivalent of\n * Next.js's `serverExternalPackages`. Every entry is passed to esbuild's `external`,\n * so the import survives into the bundle instead of being followed.\n *\n * Reach for this when bundling a package is impossible rather than merely undesirable.\n * The cases that come up: a native `.node` addon or a `node-gyp` dependency esbuild has\n * no loader for, and an optional peer dependency a library references on a code path\n * this function never takes. Both fail the deploy at bundle time with a resolve or\n * loader error naming the package, and neither is fixable from the function's own\n * source.\n *\n * **An external package is not resolvable at runtime.** The deployed archive is a\n * single `index.mjs` with no `node_modules` beside it, so anything listed here throws\n * `Cannot find module` if the function actually reaches it. This option therefore only\n * unblocks an import that is never evaluated; it does not make a dependency usable.\n *\n * A dependency the handler actually calls has to be bundled, and whether that is\n * possible depends on what it is. A pure-JavaScript package can be bundled, and a\n * failure to do so is usually something specific and fixable. A package backed by a\n * native `.node` binary cannot be bundled by anything — the binary is a compiled\n * object the platform loads from a real path — so such a package cannot work on\n * Functions until the deployed archive can carry files alongside the bundle. Do not\n * reach for `externalPackages` to try: it moves the error from deploy to invoke.\n *\n * Note that a native package may bundle without ever needing this option. `sharp`, for\n * instance, loads its binary through `createRequire`, which esbuild does not follow, so\n * it bundles cleanly and then fails at invoke with \"Could not load the sharp module\".\n *\n * Entries are package names, optionally with a subpath (`pkg`, `@scope/pkg`,\n * `pkg/sub`), matching esbuild. A relative or absolute path is rejected at validation\n * time: those are local modules, and a local module that cannot be bundled is a\n * different problem.\n * @example [\"microsandbox\", \"@mongodb-js/zstd\"]\n */\n externalPackages?: string[];\n /**\n * Local-development settings used by `neon dev` when serving every function from\n * `neon.ts`. Ignored at deploy time. See {@link FunctionDevConfig}.\n */\n dev?: FunctionDevConfig;\n}\n/**\n * A single capability a branch-scoped service credential may exercise (Preview). A\n * credential is granted a set of these and may only perform the listed actions. Mirrors\n * the Neon API `CredentialScope` enum (`x-stability-level: beta`):\n *\n * - `storage:read` / `storage:write` — object-storage (bucket) access via the S3 key.\n * - `ai_gateway:invoke` — call the AI Gateway with the bearer `api_token`.\n * - `functions:invoke` — invoke Neon Functions with the bearer `api_token`.\n *\n * The set a policy needs is derived from its enabled Preview features (see\n * {@link deriveCredentialScopes}); it is never authored by hand.\n */\ntype CredentialScope = \"storage:read\" | \"storage:write\" | \"ai_gateway:invoke\" | \"functions:invoke\";\n/**\n * Who a credential acts as. `user` is the developer/app principal minted for local dev and\n * app bootstrap (`fetchEnv` / `env pull`); `function` is a deployed-function principal\n * (carries a `function_id`). The env tooling only mints `user` credentials today.\n */\ntype CredentialPrincipalType = \"user\" | \"function\";\n/** Anonymous-access level for a branchable object-storage bucket. */\ntype BucketAccessLevel = \"private\" | \"public_read\";\n/**\n * Static definition of a branchable object-storage bucket (Preview feature). The bucket's\n * name is the **record key** in {@link PreviewInput.buckets}, so names are statically\n * enumerable and cannot duplicate.\n */\ninterface BucketDef {\n /**\n * Anonymous access level. `private` (default) requires authenticated reads/writes;\n * `public_read` allows anonymous GetObject/HeadObject.\n */\n access?: BucketAccessLevel;\n}\n/**\n * Static, branch-scoped **Preview** features. Grouped under `preview` to signal they are\n * backed by Neon `x-stability-level: beta` endpoints and may change before GA. Everything\n * here is existential (it determines what exists on the branch); per-branch tuning lives in\n * the `branch` closure.\n */\ninterface PreviewInput {\n /** Enable/disable the AI Gateway on the branch (toggle, like auth / dataApi). */\n aiGateway?: ServiceToggleInput;\n /** Functions to deploy, keyed by branch-unique slug (`^[a-z0-9]{1,20}$`). */\n functions?: Record<string, FunctionDef>;\n /** Object-storage buckets to create, keyed by bucket name. */\n buckets?: Record<string, BucketDef>;\n}\n/**\n * Per-branch deploy tuning for a single function. Returned (per slug) by the `branch`\n * closure. Deliberately **cannot** change the function's existence, source, name, env\n * **keys**, or memory — only runtime selection is currently configurable — so the static\n * secret/function set stays sound.\n */\ninterface FunctionTuning {\n /** Runtime to execute the function with. Defaults to `\"nodejs24\"`. */\n runtime?: FunctionRuntime;\n}\n/**\n * Per-branch tuning of Preview features. Only existing function slugs (those declared in\n * the static {@link PreviewInput.functions}) may be tuned — `Slug` is constrained to the\n * declared keys by {@link BranchTuningFn}.\n */\ninterface PreviewTuning<Slug extends string = string> {\n functions?: Partial<Record<Slug, FunctionTuning>>;\n}\n/**\n * The per-branch tuning object returned by the `branch` closure. It can adjust branch\n * lifecycle (`parent`, `ttl`, `protected`), Postgres compute settings, and per-function\n * deploy tuning — but **cannot** add/remove services or functions. That guarantee is what\n * keeps the static secret set (and therefore `NeonEnv`) exact.\n */\ninterface BranchTuning<Slug extends string = string> {\n /** Parent branch name used when creating a new branch. Not a Postgres setting. */\n parent?: string;\n /**\n * Branch time-to-live: how long after creation the branch should auto-expire. Applied\n * when creating a new branch and reconciled on existing branches (when `updateExisting`\n * is set). Accepts a {@link DurationString} (autocompletes common values) or a number of\n * seconds. Omit to keep the branch indefinitely.\n *\n * - {@link DurationString} — e.g. `\"7d\"`; autocompletes `\"1h\"`, `\"6h\"`, `\"12h\"`, `\"1d\"`,\n * `\"3d\"`, `\"7d\"`, `\"14d\"`, `\"30d\"`, and accepts any other `<integer><unit>` (units: `s`,\n * `m`, `h`, `d`, `w` — e.g. `\"12h\"`, `\"2w\"`). A **unit is required** — `\"7\"` is rejected;\n * for raw seconds pass a `number`.\n * - `number` — custom TTL in **seconds** (e.g. `3600`)\n * - `undefined` — no expiry; the branch persists until explicitly deleted\n *\n * The Neon API caps branch expiration at **30 days** from creation, so the resolved TTL must\n * be `> 0` and `<= 30d`; the suggestions stay within that limit and anything longer is\n * rejected at apply.\n *\n * @example \"1d\" // ephemeral preview branch: expires a day after creation\n * @example \"7d\" // one-week TTL\n * @example \"30d\" // the maximum the API allows\n * @example 3600 // 1 hour, expressed in seconds\n */\n ttl?: DurationField<TtlSuggestion>;\n /** Whether the selected branch should be protected. Undefined means \"leave as-is\". */\n protected?: boolean;\n postgres?: PostgresConfig;\n preview?: PreviewTuning<Slug>;\n}\n/** Extract the declared function slugs from a {@link PreviewInput} for closure typing. */\ntype FunctionSlugsOf<Preview extends PreviewInput | undefined> = Preview extends {\n functions: infer F;\n} ? Extract<keyof F, string> : string;\n/**\n * Signature of the `branch` closure. Generic over the static {@link PreviewInput} so the\n * `preview.functions` keys it may tune are constrained to the slugs actually declared.\n */\ntype BranchTuningFn<Preview extends PreviewInput | undefined = PreviewInput | undefined> = (branch: BranchTarget) => BranchTuning<FunctionSlugsOf<Preview>>;\n/**\n * A validated Neon branch policy — the value `defineConfig({ … })` returns and `neon.ts`\n * default-exports.\n *\n * Split into a **static** existential set (top-level `auth` / `dataApi` GA toggles plus the\n * beta `preview` block) and a **dynamic** per-branch `branch` closure for tuning. The\n * static half is what makes the secret set — and therefore `NeonEnv<typeof config>` and\n * `parseEnv` — exact; the closure can tune but never change what exists.\n *\n * Generic over the three static fields so the type system can read the exact toggle/slug\n * literals; the defaults make the bare `Config` a usable \"any policy\" type for runtime\n * function signatures.\n */\ninterface Config<Auth extends ServiceToggleInput | undefined = ServiceToggleInput | undefined, DataApi extends DataApiInput | undefined = DataApiInput | undefined, Preview extends PreviewInput | undefined = PreviewInput | undefined> {\n /** Neon Auth integration toggle (GA). Static — drives `NeonEnv.auth`. */\n auth?: Auth;\n /**\n * Neon Data API integration (GA). Static — drives `NeonEnv.dataApi`. A boolean/toggle, or\n * a {@link DataApiConfig} object selecting the auth provider (`\"neon\"` / `\"external\"`) and\n * runtime {@link DataApiSettings}. With `authProvider: \"neon\"` the policy must also enable\n * top-level `auth`.\n */\n dataApi?: DataApi;\n /** Beta (Preview) feature set: AI Gateway, functions, buckets. Static. */\n preview?: Preview;\n /** Per-branch tuning closure. Cannot change the static existential set. */\n branch?: BranchTuningFn<Preview>;\n}\n/**\n * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` so\n * downstream diff/apply never has to re-derive it.\n */\ninterface ResolvedFunctionConfig {\n slug: string;\n name: string;\n source: string;\n env: Record<string, string>;\n /**\n * Packages the bundler leaves unresolved, passed through from\n * {@link FunctionDef.externalPackages}. Absent rather than empty when undeclared, so a\n * policy that never mentions it resolves to the same shape it always did.\n */\n externalPackages?: string[];\n runtime: FunctionRuntime;\n /**\n * Local-development settings, passed through untouched from {@link FunctionDef.dev}\n * (no defaults applied). Only consumed by `neon dev`; deploy ignores it.\n */\n dev?: FunctionDevConfig;\n}\n/** A bucket with its access level defaulted to `private`. */\ninterface ResolvedBucketConfig {\n name: string;\n access: BucketAccessLevel;\n}\n/**\n * Normalized {@link PreviewInput}. Only present on {@link ResolvedBranchConfig} when the\n * policy returned a `preview` block. `aiGatewayEnabled` follows the same\n * \"present-and-not-`false`\" semantics as `authEnabled` / `dataApiEnabled`.\n */\ninterface ResolvedPreviewConfig {\n functions: ResolvedFunctionConfig[];\n buckets: ResolvedBucketConfig[];\n aiGatewayEnabled: boolean;\n}\n/**\n * Normalized Data API integration. Present on {@link ResolvedBranchConfig} only when the\n * policy enables `dataApi`. `authProvider` always resolves (defaults to `\"neon\"`); the\n * external-IdP wiring is present only for `\"external\"`; `settings` carries the camelCase\n * runtime settings (reconciled as an update when they drift).\n */\ninterface ResolvedDataApiConfig {\n authProvider: DataApiAuthProvider;\n jwksUrl?: string;\n providerName?: string;\n jwtAudience?: string;\n settings?: DataApiSettings;\n}\ninterface ResolvedBranchConfig {\n parent?: string;\n ttlSeconds?: number;\n protected?: boolean;\n postgres?: PostgresConfig;\n authEnabled: boolean;\n dataApiEnabled: boolean;\n /**\n * Resolved Data API integration. Present iff {@link dataApiEnabled} is `true`. Carries the\n * create-time auth wiring and the updatable {@link DataApiSettings}.\n */\n dataApi?: ResolvedDataApiConfig;\n preview?: ResolvedPreviewConfig;\n}\n/**\n * One concrete change `pushConfig` made (or, in dry-run, would make) on the remote.\n */\ninterface AppliedChange {\n /**\n * `service` covers branch-scoped integrations driven by the branch policy (e.g.\n * Neon Auth, Data API).\n */\n kind: \"branch\" | \"service\";\n action: \"create\" | \"update\" | \"noop\";\n identifier: string;\n details?: Record<string, unknown>;\n}\n/**\n * A diff entry that conflicts with the desired config. `pushConfig` throws\n * {@link PushConflictError} on the first call when conflicts exist; pass\n * `updateExisting: true` to apply mutable drift (settings, `protected`, TTL, project\n * rename). Immutable fields (region, Postgres major version) are always conflicts —\n * recreate the project to change them.\n */\ninterface ConflictReport {\n kind: \"branch\";\n identifier: string;\n field: string;\n current: unknown;\n desired: unknown;\n reason: string;\n}\n/**\n * Result of a `pushConfig` invocation.\n */\ninterface PushResult {\n projectId: string;\n orgId?: string;\n branchId: string;\n branchName: string;\n /**\n * `true` when `pushConfig` was called with `{ dryRun: true }`. `applied` then records\n * what **would** be applied on a real push; no API mutations were performed.\n */\n dryRun: boolean;\n applied: AppliedChange[];\n conflicts: ConflictReport[];\n}\n//#endregion\nexport { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput };\n//# sourceMappingURL=types.d.ts.map"],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","names":["ComputeUnit","DurationUnit","DurationString","SuspendTimeoutSuggestion","TtlSuggestion","DurationField","Suggestions","NonNullable","ComputeSettings","BranchTarget","ServiceToggle","ServiceToggleInput","ServiceEnabled","T","PostgresConfig","DATA_API_AUTH_PROVIDERS","DataApiAuthProvider","DataApiSettings","DataApiConfigBase","DataApiNeonAuthConfig","DataApiExternalAuthConfig","DataApiConfig","DataApiInput","FunctionRuntime","FunctionDevConfig","FunctionDef","Record","CredentialScope","CredentialPrincipalType","BucketAccessLevel","BucketDef","PreviewInput","FunctionTuning","PreviewTuning","Slug","Partial","BranchTuning","FunctionSlugsOf","Preview","F","Extract","BranchTuningFn","Config","Auth","DataApi","ResolvedFunctionConfig","ResolvedBucketConfig","ResolvedPreviewConfig","ResolvedDataApiConfig","ResolvedBranchConfig","AppliedChange","ConflictReport","PushResult"],"sources":["../../../../../config/dist/lib/types.d.ts"],"sourcesContent":["//#region src/lib/types.d.ts\n/**\n * Valid Neon Compute Unit values.\n * Most plans support 0.25, 0.5, 1, 2, 4, 8. Higher values may be available on Business plans.\n */\ntype ComputeUnit = 0.25 | 0.5 | 1 | 2 | 4 | 8;\n/** Time units accepted in a {@link DurationString}: seconds, minutes, hours, days, weeks. */\ntype DurationUnit = \"s\" | \"m\" | \"h\" | \"d\" | \"w\";\n/**\n * A Neon duration string: a positive integer **followed by a unit** — `s` (seconds),\n * `m` (minutes), `h` (hours), `d` (days), or `w` (weeks). Used by\n * {@link ComputeSettings.suspendTimeout} and {@link BranchTuning.ttl}.\n *\n * A **unit is required**: a bare numeric string like `\"7\"` is rejected at the type level. To\n * express a raw number of seconds, pass a `number` (`300`) — not a string (`\"300\"`). This\n * removes the old ambiguity where `\"7\"` silently meant 7 *seconds* instead of, say, `\"7d\"`.\n *\n * @example \"5m\" // 5 minutes\n * @example \"1h\" // 1 hour\n * @example \"7d\" // 7 days\n */\ntype DurationString = `${number}${DurationUnit}`;\n/**\n * Autocomplete suggestions for {@link ComputeSettings.suspendTimeout}. Every value sits inside\n * the Neon API's allowed scale-to-zero band: **60s–604800s** (1 minute – 1 week). This is *not*\n * a closed set — the field also accepts any other {@link DurationString} or a `number` of\n * seconds; out-of-range values type-check but are rejected at apply time.\n */\ntype SuspendTimeoutSuggestion = \"1m\" | \"5m\" | \"15m\" | \"30m\" | \"1h\" | \"6h\" | \"12h\" | \"1d\" | \"7d\";\n/**\n * Autocomplete suggestions for {@link BranchTuning.ttl}. Every value sits within the Neon API's\n * branch-expiration limit (**max 30 days** from creation; the Console's own presets are 1h / 1d\n * / 7d). This is *not* a closed set — the field also accepts any other {@link DurationString} or\n * a `number` of seconds; values over 30 days are rejected at apply time.\n */\ntype TtlSuggestion = \"1h\" | \"6h\" | \"12h\" | \"1d\" | \"3d\" | \"7d\" | \"14d\" | \"30d\";\n/**\n * Compose a field's duration type: its curated autocomplete `Suggestions` plus the open\n * `DurationString` template (so any `<integer><unit>` string still type-checks) and a `number`\n * of seconds. Intersecting the template arm with `NonNullable<unknown>` stops TypeScript from\n * collapsing the literal suggestions into the template, which is what preserves the autocomplete.\n */\ntype DurationField<Suggestions extends DurationString> = Suggestions | (DurationString & NonNullable<unknown>) | number;\n/**\n * Compute settings applied to the read/write endpoint of a branch.\n *\n * Mirrors the subset of {@link https://api-docs.neon.tech/reference/getting-started-with-neon-api Neon endpoint}\n * fields that we expose as IaC primitives. Anything left undefined falls back to the project's\n * `default_endpoint_settings` (which themselves fall back to Neon defaults).\n */\ninterface ComputeSettings {\n /**\n * Minimum number of Compute Units. Set to 0.25 for true scale-to-zero.\n * @example 0.25 // scale-to-zero\n * @example 1 // always-on with 1 CU minimum\n */\n autoscalingLimitMinCu?: ComputeUnit;\n /**\n * Maximum number of Compute Units for autoscaling.\n * @example 2\n * @example 8\n */\n autoscalingLimitMaxCu?: ComputeUnit;\n /**\n * How long an idle compute waits before suspending (Neon's scale-to-zero). Accepts a\n * {@link DurationString} (autocompletes common values), a number of seconds, or `false`.\n *\n * - `false` — never suspend (always-on compute)\n * - {@link DurationString} — e.g. `\"5m\"`; autocompletes the in-range values `\"1m\"`, `\"5m\"`,\n * `\"15m\"`, `\"30m\"`, `\"1h\"`, `\"6h\"`, `\"12h\"`, `\"1d\"`, `\"7d\"`, and accepts any other\n * `<integer><unit>` (units: `s`, `m`, `h`, `d`, `w`). A **unit is required** — for raw\n * seconds pass a `number`, not a string.\n * - `number` — custom timeout in **seconds**, must be in `60`–`604800` (1 minute to 1 week)\n * - `undefined` — use the Neon default (currently 300s / 5 minutes)\n *\n * Whichever form you use, the resolved timeout must fall in `60`–`604800` seconds (the Neon\n * API limit); the suggestions are all within that band, anything else is checked at apply.\n *\n * @example false // never suspend (always-on)\n * @example \"5m\" // suspend after 5 minutes idle\n * @example \"1h\" // suspend after 1 hour idle\n * @example 300 // 5 minutes, expressed in seconds\n */\n suspendTimeout?: false | DurationField<SuspendTimeoutSuggestion>;\n}\n/**\n * Read-only descriptor of the branch a {@link Config} policy is being evaluated for — the\n * `branch` argument passed to your `defineConfig({ branch: (branch) => … })` closure. It describes\n * **which** branch this invocation decides for; it is not a live branch handle and must not\n * be mutated. Switch on its fields and return the desired {@link BranchConfig}.\n */\ninterface BranchTarget {\n /** Branch name being evaluated. For `branch dev`, this is the generated branch name. */\n name: string;\n /** Neon branch id when the branch already exists. Undefined during pre-create eval. */\n id?: string;\n /** Whether this branch already exists on Neon. */\n exists: boolean;\n /** Parent branch id from Neon when known. */\n parentId?: string;\n /** Whether Neon marks this branch as the project default. */\n isDefault?: boolean;\n /** Whether Neon currently marks this branch protected. */\n isProtected?: boolean;\n /** Current expiration timestamp from Neon, when set. */\n expiresAt?: string;\n}\n/**\n * Object form of a branch-scoped service toggle. `{}` or `{ enabled: true }` enables it;\n * `{ enabled: false }` opts out. Used as the object half of {@link ServiceToggleInput}.\n */\ninterface ServiceToggle {\n /** Defaults to `true` when the service namespace is present. Set `false` to opt out. */\n enabled?: boolean;\n}\n/**\n * How a branch-scoped service (Neon Auth, Data API, AI Gateway) is toggled in a policy.\n *\n * - `true` / `{}` / `{ enabled: true }` — enabled.\n * - `false` / `{ enabled: false }` — disabled.\n * - omitted (`undefined`) — not part of the policy at all.\n *\n * These toggles are **static** (they live in the top-level `defineConfig({ … })` object,\n * not in the per-branch `branch` closure) so the secret set they imply can be derived at\n * the type level — that's what makes `NeonEnv<typeof config>` exact.\n */\ntype ServiceToggleInput = boolean | ServiceToggle;\n/**\n * Resolve a **static** service toggle (`true` / `false` / `{ enabled?: boolean }` / object /\n * `undefined`) to a type-level boolean. The tuple wrapping (`[T] extends […]`) disables\n * distribution so a union/`undefined` is judged as a single 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 | … | undefined` (no literal info) → `false`\n *\n * Shared by the {@link Config} static cross-field checks and the `@neon/env`\n * `NeonEnv` namespace derivation, so both read \"is this service on?\" identically.\n */\ntype ServiceEnabled<T> = [T] extends [false] ? false : [T] extends [{\n enabled: false;\n}] ? false : [T] extends [undefined] ? false : [T] extends [true] ? true : [T] extends [{\n enabled: true;\n}] ? true : [T] extends [object] ? true : false;\ninterface PostgresConfig {\n computeSettings?: ComputeSettings;\n}\n/**\n * Authentication providers a Data API integration can verify JWTs against, as written in\n * `neon.ts`. Friendly authoring values (mapped to the Neon API's `neon_auth` / `external`\n * at the API boundary):\n *\n * - `\"neon\"` — verify tokens minted by **Neon Auth** on the same branch. Neon supplies the\n * JWKS URL / provider wiring for you, so the `jwksUrl` / `providerName` / `jwtAudience`\n * fields are forbidden (a type error) on this variant — and the policy must also enable\n * top-level `auth` (Neon Auth) so the tokens exist.\n * - `\"external\"` — verify tokens from a third-party IdP (Clerk, Stytch, Auth0, …). You\n * provide `jwksUrl` (and optionally `providerName` / `jwtAudience`).\n */\ndeclare const DATA_API_AUTH_PROVIDERS: readonly [\"neon\", \"external\"];\ntype DataApiAuthProvider = (typeof DATA_API_AUTH_PROVIDERS)[number];\n/**\n * Reusable runtime settings for a Data API integration (the Neon API `DataAPISettings`,\n * camelCased to match the rest of `neon.ts`). Every field is optional; omitted fields keep\n * the Neon defaults shown below. These are the **only** Data API fields that can change on\n * an already-enabled integration — drift here is reconciled as an *update* (requires\n * `updateExisting` / `--update-existing`); the create-only auth wiring above cannot.\n */\ninterface DataApiSettings {\n /** Enable the aggregates feature (`db_aggregates_enabled`). Default `true`. */\n dbAggregatesEnabled?: boolean;\n /** Database role used for anonymous requests (`db_anon_role`). Default `\"anonymous\"`. */\n dbAnonRole?: string;\n /** Extra schemas appended to the search path (`db_extra_search_path`). */\n dbExtraSearchPath?: string;\n /** Maximum rows returned in a single request (`db_max_rows`). */\n dbMaxRows?: number;\n /** Schemas exposed via the API (`db_schemas`). Default `[\"public\"]`. */\n dbSchemas?: string[];\n /** JWT claim key used for role extraction (`jwt_role_claim_key`). Default `\".role\"`. */\n jwtRoleClaimKey?: string;\n /** Maximum lifetime of the JWT cache, in seconds (`jwt_cache_max_lifetime`). */\n jwtCacheMaxLifetime?: number;\n /** OpenAPI spec mode (`openapi_mode`). Default `\"disabled\"`. */\n openapiMode?: \"ignore-privileges\" | \"disabled\";\n /** CORS allowed origins (`server_cors_allowed_origins`). */\n serverCorsAllowedOrigins?: string;\n /** Emit server-timing headers (`server_timing_enabled`). */\n serverTimingEnabled?: boolean;\n}\n/** Fields shared by every {@link DataApiConfig} variant. */\ninterface DataApiConfigBase {\n /** Defaults to `true` when the `dataApi` namespace is present. Set `false` to opt out. */\n enabled?: boolean;\n /** Reusable runtime settings. Drift here is reconciled as an update. */\n settings?: DataApiSettings;\n}\n/**\n * Data API verified by **Neon Auth** (`authProvider: \"neon\"`, the default). The external\n * IdP fields are statically forbidden (`?: never`) because Neon supplies them; declaring any\n * of them is a type error directing you to `authProvider: \"external\"`.\n */\ninterface DataApiNeonAuthConfig extends DataApiConfigBase {\n authProvider?: \"neon\";\n /** Forbidden with `authProvider: \"neon\"` — Neon provides the JWKS URL. */\n jwksUrl?: never;\n /** Forbidden with `authProvider: \"neon\"` — the provider is Neon Auth. */\n providerName?: never;\n /** Forbidden with `authProvider: \"neon\"` — Neon manages the audience. */\n jwtAudience?: never;\n}\n/**\n * Data API verified by an **external** IdP (`authProvider: \"external\"`). You provide the\n * JWKS URL (and optionally a provider label / expected audience).\n */\ninterface DataApiExternalAuthConfig extends DataApiConfigBase {\n authProvider: \"external\";\n /** URL that publishes the IdP's JWKS (JSON Web Key Set). */\n jwksUrl?: string;\n /** Human label for the IdP (e.g. \"Clerk\", \"Stytch\", \"Auth0\"). */\n providerName?: string;\n /**\n * Expected `aud` claim. ⚠️ This only **rejects** tokens carrying a *different* audience;\n * tokens with no `aud` claim are still accepted.\n */\n jwtAudience?: string;\n}\n/**\n * Object form of the `dataApi` toggle. A discriminated union on {@link DataApiAuthProvider}:\n * the `\"neon\"` variant forbids the external-IdP fields, the `\"external\"` variant allows them.\n */\ntype DataApiConfig = DataApiNeonAuthConfig | DataApiExternalAuthConfig;\n/**\n * How the Data API is toggled in a policy: a bare boolean (like the other service toggles)\n * or the richer {@link DataApiConfig} object. `true` / `{}` / `{ enabled: true }` enable it\n * with Neon defaults; `false` / `{ enabled: false }` opt out.\n */\ntype DataApiInput = boolean | DataApiConfig;\n/**\n * Supported function runtimes. Mirrors the Neon Functions deploy API `runtime` enum.\n * Only `nodejs24` exists today; kept as a union so adding runtimes later is a\n * non-breaking, type-checked change.\n */\ntype FunctionRuntime = \"nodejs24\";\n/**\n * Local-development settings for a function, used by `neon dev` when it serves every\n * function declared in `neon.ts` (i.e. invoked with no `--source`). Never affects deploy.\n */\ninterface FunctionDevConfig {\n /**\n * Port the local server binds. Bound exactly (and `neon dev` fails loudly if it is taken)\n * when set; a free port is found automatically when omitted.\n */\n port?: number;\n}\n/**\n * Static definition of a Neon Function (Preview feature). Declares that the function\n * **exists** on every branch; its branch-unique slug is the **record key** in\n * {@link PreviewInput.functions} (not a field here), so slugs are statically enumerable,\n * cannot duplicate, and the `branch` closure can only tune slugs that are declared here.\n *\n * A function is invoked like a Cloudflare/Vercel handler — its source module\n * `export default { fetch }` or `export async function handler(req): Response`. The\n * `source` path is bundled (esbuild) and uploaded as a deployment; the newest deployment\n * becomes active.\n *\n * Runtime tuning is **not** here — it varies per branch and lives in the `branch` closure\n * (see {@link FunctionTuning}). Memory is fixed by the platform policy for now and is not\n * user-configurable.\n */\ninterface FunctionDef {\n /** Free-form display name. @example \"Hello World\" */\n name: string;\n /**\n * Path to the function's entry module, **relative to `neon.ts`** (or absolute). The\n * module's default export (`{ fetch }`) or `handler` export is the function entry. This\n * path is resolved against the loaded `neon.ts` location and bundled with esbuild at\n * deploy time.\n *\n * We require a string path rather than an imported handler because a JS function value\n * carries no reference back to its source file, so esbuild has nothing to bundle from.\n * @example \"./functions/hello-world.ts\"\n */\n source: string;\n /**\n * Environment variables injected into the deployed function, keyed by the var name the\n * function reads at runtime. The **keys** are static (preserved at the type level so\n * `parseEnv(config, \"<slug>\").function.<key>` is typed); the **values** are arbitrary\n * strings evaluated when `neon.ts` is loaded (typically `process.env.X`) and uploaded\n * at `config apply`. Every value must be a defined string — a `process.env.X` that is\n * `undefined` (unset) errors at validation time rather than silently shipping\n * `undefined`.\n * @example { resendApiKey: process.env.RESEND_API_KEY ?? \"\" }\n */\n env?: Record<string, string>;\n /**\n * Packages the bundler must leave alone, by name — the deploy-time equivalent of\n * Next.js's `serverExternalPackages`. Every entry is passed to esbuild's `external`,\n * so the import survives into the bundle instead of being followed.\n *\n * Reach for this when bundling a package is impossible rather than merely undesirable.\n * The cases that come up: a native `.node` addon or a `node-gyp` dependency esbuild has\n * no loader for, and an optional peer dependency a library references on a code path\n * this function never takes. Both fail the deploy at bundle time with a resolve or\n * loader error naming the package, and neither is fixable from the function's own\n * source.\n *\n * **An external package is not resolvable at runtime.** The deployed archive is a\n * single `index.mjs` with no `node_modules` beside it, so anything listed here throws\n * `Cannot find module` if the function actually reaches it. This option therefore only\n * unblocks an import that is never evaluated; it does not make a dependency usable.\n *\n * A dependency the handler actually calls has to be bundled, and whether that is\n * possible depends on what it is. A pure-JavaScript package can be bundled, and a\n * failure to do so is usually something specific and fixable. A package backed by a\n * native `.node` binary cannot be bundled by anything — the binary is a compiled\n * object the platform loads from a real path — so such a package cannot work on\n * Functions until the deployed archive can carry files alongside the bundle. Do not\n * reach for `externalPackages` to try: it moves the error from deploy to invoke.\n *\n * Note that a native package may bundle without ever needing this option. `sharp`, for\n * instance, loads its binary through `createRequire`, which esbuild does not follow, so\n * it bundles cleanly and then fails at invoke with \"Could not load the sharp module\".\n *\n * Entries are package names, optionally with a subpath (`pkg`, `@scope/pkg`,\n * `pkg/sub`), matching esbuild. A relative or absolute path is rejected at validation\n * time: those are local modules, and a local module that cannot be bundled is a\n * different problem.\n * @example [\"microsandbox\", \"@mongodb-js/zstd\"]\n */\n externalPackages?: string[];\n /**\n * Local-development settings used by `neon dev` when serving every function from\n * `neon.ts`. Ignored at deploy time. See {@link FunctionDevConfig}.\n */\n dev?: FunctionDevConfig;\n}\n/**\n * A single capability a branch-scoped service credential may exercise (Preview). A\n * credential is granted a set of these and may only perform the listed actions. Mirrors\n * the Neon API `CredentialScope` enum (`x-stability-level: beta`):\n *\n * - `storage:read` / `storage:write` — object-storage (bucket) access via the S3 key.\n * - `ai_gateway:invoke` — call the AI Gateway with the bearer `api_token`.\n * - `functions:invoke` — invoke Neon Functions with the bearer `api_token`.\n *\n * The set a policy needs is derived from its enabled Preview features (see\n * {@link deriveCredentialScopes}); it is never authored by hand.\n */\ntype CredentialScope = \"storage:read\" | \"storage:write\" | \"ai_gateway:invoke\" | \"functions:invoke\";\n/**\n * Who a credential acts as. `user` is the developer/app principal minted for local dev and\n * app bootstrap (`fetchEnv` / `env pull`); `function` is a deployed-function principal\n * (carries a `function_id`). The env tooling only mints `user` credentials today.\n */\ntype CredentialPrincipalType = \"user\" | \"function\";\n/** Anonymous-access level for a branchable object-storage bucket. */\ntype BucketAccessLevel = \"private\" | \"public_read\";\n/**\n * Static definition of a branchable object-storage bucket (Preview feature). The bucket's\n * name is the **record key** in {@link PreviewInput.buckets}, so names are statically\n * enumerable and cannot duplicate.\n */\ninterface BucketDef {\n /**\n * Anonymous access level. `private` (default) requires authenticated reads/writes;\n * `public_read` allows anonymous GetObject/HeadObject.\n */\n access?: BucketAccessLevel;\n}\n/**\n * Static, branch-scoped **Preview** features. Grouped under `preview` to signal they are\n * backed by Neon `x-stability-level: beta` endpoints and may change before GA. Everything\n * here is existential (it determines what exists on the branch); per-branch tuning lives in\n * the `branch` closure.\n */\ninterface PreviewInput {\n /** Enable/disable the AI Gateway on the branch (toggle, like auth / dataApi). */\n aiGateway?: ServiceToggleInput;\n /** Functions to deploy, keyed by branch-unique slug (`^[a-z0-9]{1,20}$`). */\n functions?: Record<string, FunctionDef>;\n /** Object-storage buckets to create, keyed by bucket name. */\n buckets?: Record<string, BucketDef>;\n}\n/**\n * Per-branch deploy tuning for a single function. Returned (per slug) by the `branch`\n * closure. Deliberately **cannot** change the function's existence, source, name, env\n * **keys**, or memory — only runtime selection is currently configurable — so the static\n * secret/function set stays sound.\n */\ninterface FunctionTuning {\n /** Runtime to execute the function with. Defaults to `\"nodejs24\"`. */\n runtime?: FunctionRuntime;\n}\n/**\n * Per-branch tuning of Preview features. Only existing function slugs (those declared in\n * the static {@link PreviewInput.functions}) may be tuned — `Slug` is constrained to the\n * declared keys by {@link BranchTuningFn}.\n */\ninterface PreviewTuning<Slug extends string = string> {\n functions?: Partial<Record<Slug, FunctionTuning>>;\n}\n/**\n * The per-branch tuning object returned by the `branch` closure. It can adjust branch\n * lifecycle (`parent`, `ttl`, `protected`), Postgres compute settings, and per-function\n * deploy tuning — but **cannot** add/remove services or functions. That guarantee is what\n * keeps the static secret set (and therefore `NeonEnv`) exact.\n */\ninterface BranchTuning<Slug extends string = string> {\n /** Parent branch name used when creating a new branch. Not a Postgres setting. */\n parent?: string;\n /**\n * Branch time-to-live: how long after creation the branch should auto-expire. Applied\n * when creating a new branch and reconciled on existing branches (when `updateExisting`\n * is set). Accepts a {@link DurationString} (autocompletes common values) or a number of\n * seconds. Omit to keep the branch indefinitely.\n *\n * - {@link DurationString} — e.g. `\"7d\"`; autocompletes `\"1h\"`, `\"6h\"`, `\"12h\"`, `\"1d\"`,\n * `\"3d\"`, `\"7d\"`, `\"14d\"`, `\"30d\"`, and accepts any other `<integer><unit>` (units: `s`,\n * `m`, `h`, `d`, `w` — e.g. `\"12h\"`, `\"2w\"`). A **unit is required** — `\"7\"` is rejected;\n * for raw seconds pass a `number`.\n * - `number` — custom TTL in **seconds** (e.g. `3600`)\n * - `undefined` — no expiry; the branch persists until explicitly deleted\n *\n * The Neon API caps branch expiration at **30 days** from creation, so the resolved TTL must\n * be `> 0` and `<= 30d`; the suggestions stay within that limit and anything longer is\n * rejected at apply.\n *\n * @example \"1d\" // ephemeral preview branch: expires a day after creation\n * @example \"7d\" // one-week TTL\n * @example \"30d\" // the maximum the API allows\n * @example 3600 // 1 hour, expressed in seconds\n */\n ttl?: DurationField<TtlSuggestion>;\n /** Whether the selected branch should be protected. Undefined means \"leave as-is\". */\n protected?: boolean;\n postgres?: PostgresConfig;\n preview?: PreviewTuning<Slug>;\n}\n/** Extract the declared function slugs from a {@link PreviewInput} for closure typing. */\ntype FunctionSlugsOf<Preview extends PreviewInput | undefined> = Preview extends {\n functions: infer F;\n} ? Extract<keyof F, string> : string;\n/**\n * Signature of the `branch` closure. Generic over the static {@link PreviewInput} so the\n * `preview.functions` keys it may tune are constrained to the slugs actually declared.\n */\ntype BranchTuningFn<Preview extends PreviewInput | undefined = PreviewInput | undefined> = (branch: BranchTarget) => BranchTuning<FunctionSlugsOf<Preview>>;\n/**\n * A validated Neon branch policy — the value `defineConfig({ … })` returns and `neon.ts`\n * default-exports.\n *\n * Split into a **static** existential set (top-level `auth` / `dataApi` GA toggles plus the\n * beta `preview` block) and a **dynamic** per-branch `branch` closure for tuning. The\n * static half is what makes the secret set — and therefore `NeonEnv<typeof config>` and\n * `parseEnv` — exact; the closure can tune but never change what exists.\n *\n * Generic over the three static fields so the type system can read the exact toggle/slug\n * literals; the defaults make the bare `Config` a usable \"any policy\" type for runtime\n * function signatures.\n */\ninterface Config<Auth extends ServiceToggleInput | undefined = ServiceToggleInput | undefined, DataApi extends DataApiInput | undefined = DataApiInput | undefined, Preview extends PreviewInput | undefined = PreviewInput | undefined> {\n /** Neon Auth integration toggle (GA). Static — drives `NeonEnv.auth`. */\n auth?: Auth;\n /**\n * Neon Data API integration (GA). Static — drives `NeonEnv.dataApi`. A boolean/toggle, or\n * a {@link DataApiConfig} object selecting the auth provider (`\"neon\"` / `\"external\"`) and\n * runtime {@link DataApiSettings}. With `authProvider: \"neon\"` the policy must also enable\n * top-level `auth`.\n */\n dataApi?: DataApi;\n /** Beta (Preview) feature set: AI Gateway, functions, buckets. Static. */\n preview?: Preview;\n /** Per-branch tuning closure. Cannot change the static existential set. */\n branch?: BranchTuningFn<Preview>;\n}\n/**\n * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` so\n * downstream diff/apply never has to re-derive it.\n */\ninterface ResolvedFunctionConfig {\n slug: string;\n name: string;\n source: string;\n env: Record<string, string>;\n /**\n * Packages the bundler leaves unresolved, passed through from\n * {@link FunctionDef.externalPackages}. Absent rather than empty when undeclared, so a\n * policy that never mentions it resolves to the same shape it always did.\n */\n externalPackages?: string[];\n runtime: FunctionRuntime;\n /**\n * Local-development settings, passed through untouched from {@link FunctionDef.dev}\n * (no defaults applied). Only consumed by `neon dev`; deploy ignores it.\n */\n dev?: FunctionDevConfig;\n}\n/** A bucket with its access level defaulted to `private`. */\ninterface ResolvedBucketConfig {\n name: string;\n access: BucketAccessLevel;\n}\n/**\n * Normalized {@link PreviewInput}. Only present on {@link ResolvedBranchConfig} when the\n * policy returned a `preview` block. `aiGatewayEnabled` follows the same\n * \"present-and-not-`false`\" semantics as `authEnabled` / `dataApiEnabled`.\n */\ninterface ResolvedPreviewConfig {\n functions: ResolvedFunctionConfig[];\n buckets: ResolvedBucketConfig[];\n aiGatewayEnabled: boolean;\n}\n/**\n * Normalized Data API integration. Present on {@link ResolvedBranchConfig} only when the\n * policy enables `dataApi`. `authProvider` always resolves (defaults to `\"neon\"`); the\n * external-IdP wiring is present only for `\"external\"`; `settings` carries the camelCase\n * runtime settings (reconciled as an update when they drift).\n */\ninterface ResolvedDataApiConfig {\n authProvider: DataApiAuthProvider;\n jwksUrl?: string;\n providerName?: string;\n jwtAudience?: string;\n settings?: DataApiSettings;\n}\ninterface ResolvedBranchConfig {\n parent?: string;\n ttlSeconds?: number;\n protected?: boolean;\n postgres?: PostgresConfig;\n authEnabled: boolean;\n dataApiEnabled: boolean;\n /**\n * Resolved Data API integration. Present iff {@link dataApiEnabled} is `true`. Carries the\n * create-time auth wiring and the updatable {@link DataApiSettings}.\n */\n dataApi?: ResolvedDataApiConfig;\n preview?: ResolvedPreviewConfig;\n}\n/**\n * One concrete change `pushConfig` made (or, in dry-run, would make) on the remote.\n */\ninterface AppliedChange {\n /**\n * `service` covers branch-scoped integrations driven by the branch policy (e.g.\n * Neon Auth, Data API).\n */\n kind: \"branch\" | \"service\";\n action: \"create\" | \"update\" | \"noop\";\n identifier: string;\n details?: Record<string, unknown>;\n}\n/**\n * A diff entry that conflicts with the desired config. `pushConfig` throws\n * {@link PushConflictError} on the first call when conflicts exist; pass\n * `updateExisting: true` to apply mutable drift (settings, `protected`, TTL, project\n * rename). Immutable fields (region, Postgres major version) are always conflicts —\n * recreate the project to change them.\n */\ninterface ConflictReport {\n kind: \"branch\";\n identifier: string;\n field: string;\n current: unknown;\n desired: unknown;\n reason: string;\n}\n/**\n * Result of a `pushConfig` invocation.\n */\ninterface PushResult {\n projectId: string;\n orgId?: string;\n branchId: string;\n branchName: string;\n /**\n * `true` when `pushConfig` was called with `{ dryRun: true }`. `applied` then records\n * what **would** be applied on a real push; no API mutations were performed.\n */\n dryRun: boolean;\n applied: AppliedChange[];\n conflicts: ConflictReport[];\n}\n//#endregion\nexport { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput };\n//# sourceMappingURL=types.d.ts.map"],"mappings":"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"}
@@ -23,6 +23,13 @@ interface CredentialOutcome {
23
23
  * secrets named *and* that this tool issued; empty otherwise.
24
24
  */
25
25
  revoked: string[];
26
+ /**
27
+ * `tokenId`s this call superseded but left live, because `revokeSuperseded` was `false`.
28
+ * The counterpart to {@link CredentialOutcome.revoked}: exactly the ids that would be
29
+ * there instead. Lets a caller name what it orphaned rather than saying that it might
30
+ * have orphaned something — always empty on the default path.
31
+ */
32
+ superseded: string[];
26
33
  }
27
34
  /** A resolved branch env, ready to write to a dotenv file or inject into a process. */
28
35
  interface ReusedBranchEnv {
@@ -70,6 +77,18 @@ declare function fetchEnvReusingSecrets<const C extends Config>(config: C, optio
70
77
  * `.env` file, typically. Defaults to `process.env`.
71
78
  */
72
79
  env?: NodeJS.ProcessEnv;
80
+ /**
81
+ * Revoke the credential a freshly-minted one supersedes. Defaults to `true`.
82
+ *
83
+ * Pass `false` when this resolve covers only *part* of what the branch has. Object
84
+ * storage and the AI Gateway share one credential, so a partial resolve cannot tell
85
+ * whether the credential its persisted secrets name also backs a service it is not
86
+ * resolving — and revoking it would kill that service while its vars, which this call
87
+ * is not rewriting, stay on disk and stop working. The cost is an orphaned credential,
88
+ * which is the safer of the two failures. `neon env pull --service` is the caller that
89
+ * needs this.
90
+ */
91
+ revokeSuperseded?: boolean;
73
92
  }): Promise<ReusedBranchEnv>;
74
93
  //#endregion
75
94
  export { CredentialOutcome, ReusedBranchEnv, fetchEnvReusingSecrets };
@@ -1 +1 @@
1
- {"version":3,"file":"reuse-secrets.d.ts","names":[],"sources":["../../src/lib/reuse-secrets.ts"],"mappings":";;;;;;;;AAsBiB,UAAA,iBAAA,CAAiB;EAoBjB;AAAe;AAEzB;AAEM;AAAiB;EA2CR,MAAA,EAAA,OAAA;EAAsB;AAAiB;AACpD;AACC;EAKF,IAAA,EAAA,MAAO,EAAA;EAEJ;AAAR;AAAO;;;;;UAxDO,eAAA;;QAEV;;cAEM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2CS,uCAAuC,gBACpD,YACC;;;;;QAKF,MAAA,CAAO;IAEZ,QAAQ"}
1
+ {"version":3,"file":"reuse-secrets.d.ts","names":[],"sources":["../../src/lib/reuse-secrets.ts"],"mappings":";;;;;;;;AAsBiB,UAAA,iBAAA,CAAiB;EA2BjB;AAAe;AAEzB;AAEM;AAAiB;EA2CR,MAAA,EAAA,OAAA;EAAsB;AAAiB;AACpD;AACC;EAKF,IAAA,EAAA,MAAO,EAAA;EAcJ;AAAR;AAAO;;;;;;;;;;;;UApEO,eAAA;;QAEV;;cAEM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2CS,uCAAuC,gBACpD,YACC;;;;;QAKF,MAAA,CAAO;;;;;;;;;;;;;IAcZ,QAAQ"}
@@ -35,7 +35,7 @@ import { credentialScopesSatisfied } from "@neon/config/v1";
35
35
  * ```
36
36
  */
37
37
  async function fetchEnvReusingSecrets(config, options) {
38
- const { env: source = process.env, ...fetchOptions } = options;
38
+ const { env: source = process.env, revokeSuperseded = true, ...fetchOptions } = options;
39
39
  const api = options.api ?? createApiFromOptions(options);
40
40
  const { branch, desired } = await resolveBranchPolicy(config, options, api);
41
41
  const storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;
@@ -51,7 +51,8 @@ async function fetchEnvReusingSecrets(config, options) {
51
51
  credential: {
52
52
  issued: false,
53
53
  keys: [],
54
- revoked: []
54
+ revoked: [],
55
+ superseded: []
55
56
  }
56
57
  };
57
58
  }
@@ -85,19 +86,21 @@ async function fetchEnvReusingSecrets(config, options) {
85
86
  credential: {
86
87
  issued: false,
87
88
  keys: secretKeys,
88
- revoked: []
89
+ revoked: [],
90
+ superseded: []
89
91
  }
90
92
  };
91
93
  }
92
94
  const ours = /* @__PURE__ */ new Set();
93
95
  for (const meta of [named.storage, named.gateway]) if (meta !== null && meta.principalType === "user" && meta.name === credentialName(branch.name)) ours.add(meta.tokenId);
94
- for (const tokenId of ours) await api.revokeCredential(options.projectId, branch.id, tokenId);
96
+ if (revokeSuperseded) for (const tokenId of ours) await api.revokeCredential(options.projectId, branch.id, tokenId);
95
97
  return {
96
98
  vars,
97
99
  credential: {
98
100
  issued: true,
99
101
  keys: secretKeys,
100
- revoked: [...ours]
102
+ revoked: revokeSuperseded ? [...ours] : [],
103
+ superseded: revokeSuperseded ? [] : [...ours]
101
104
  }
102
105
  };
103
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reuse-secrets.js","names":[],"sources":["../../src/lib/reuse-secrets.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcredentialScopesSatisfied,\n\ttype NeonCredentialMeta,\n} from \"@neon/config/v1\";\n\nimport {\n\tcreateApiFromOptions,\n\tcredentialEnvKeys,\n\tcredentialName,\n\ttype FetchEnvOptions,\n\tfetchEnvKeys,\n\tNEON_ENV_VAR_KEYS,\n\tpolicyEnvKeys,\n\tpreviewCredentialScopes,\n\tresolveBranchPolicy,\n\ttoEntries,\n} from \"./env.js\";\n\n/**\n * What happened to the branch credential during a {@link fetchEnvReusingSecrets} call.\n */\nexport interface CredentialOutcome {\n\t/**\n\t * `true` when a new credential was minted — because none was persisted, or because the\n\t * persisted secrets could not be verified against this branch. `false` when the persisted\n\t * secrets were verified and kept, and when the policy enables nothing credential-backed.\n\t */\n\tissued: boolean;\n\t/**\n\t * The env-var keys the branch credential's secrets surface under, given what the policy\n\t * enables. Empty when the policy enables neither object storage nor the AI Gateway.\n\t */\n\tkeys: string[];\n\t/**\n\t * `tokenId`s revoked because this call superseded them. Only ever credentials the persisted\n\t * secrets named *and* that this tool issued; empty otherwise.\n\t */\n\trevoked: string[];\n}\n\n/** A resolved branch env, ready to write to a dotenv file or inject into a process. */\nexport interface ReusedBranchEnv {\n\t/** Every Neon env var for the branch, as `{ KEY: value }`. */\n\tvars: Record<string, string>;\n\t/** What happened to the branch credential. */\n\tcredential: CredentialOutcome;\n}\n\n/** The branch credential's secrets as persisted in an env source. Empty string means absent. */\ninterface PersistedSecrets {\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\tapiToken: string;\n}\n\n/**\n * Resolve a branch's env while keeping one-time secrets the caller already holds.\n *\n * {@link fetchEnvKeys} — and the public `fetchEnv` — only ever *fetch*. The Neon API returns a\n * credential's `api_token` / `s3_secret_access_key` exactly once, at mint time, so \"fetching\"\n * them means minting a new credential; a plain `fetchEnv` on every `neon dev` start or `env\n * pull` would leave a live credential behind each time. This is the wrapper that avoids that:\n * it looks at what the caller already has, decides what is still usable, and asks `fetchEnv`\n * for only the rest.\n *\n * The check is a real verification, not a presence test. A persisted secret is kept only when\n * it names a credential that still exists on this branch, is not revoked or expired, and\n * carries every scope the policy needs. A `.env.example` placeholder, a credential revoked in\n * the console, one copied in from another branch, or one predating a newly-enabled feature all\n * fail that check and get replaced.\n *\n * None of this needs local bookkeeping, because the secrets carry their own credential id:\n * `AWS_ACCESS_KEY_ID` **is** the credential's `tokenId` (the storage gateway authenticates\n * against the full id), and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`,\n * where `tokenIdShort` is what the credentials list reports. The env source being replaced is\n * the record of what the last call issued.\n *\n * ```ts\n * import { fetchEnvReusingSecrets } from \"@neon/env/runtime\";\n *\n * const { vars, credential } = await fetchEnvReusingSecrets(config, {\n * projectId,\n * branch: \"main\",\n * env: { ...process.env, ...readEnvFile(\".env\") },\n * });\n * if (credential.issued) console.log(`new values for ${credential.keys.join(\", \")}`);\n * ```\n */\nexport async function fetchEnvReusingSecrets<const C extends Config>(\n\tconfig: C,\n\toptions: FetchEnvOptions & {\n\t\t/**\n\t\t * Env source holding secrets a previous call persisted — `process.env` layered with a\n\t\t * `.env` file, typically. Defaults to `process.env`.\n\t\t */\n\t\tenv?: NodeJS.ProcessEnv;\n\t},\n): Promise<ReusedBranchEnv> {\n\tconst { env: source = process.env, ...fetchOptions } = options;\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst { branch, desired } = await resolveBranchPolicy(config, options, api);\n\n\tconst storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;\n\tconst gatewayEnabled = desired.preview?.aiGatewayEnabled ?? false;\n\tconst secretKeys = credentialEnvKeys({\n\t\tstorage: storageEnabled,\n\t\taiGateway: gatewayEnabled,\n\t});\n\n\t// Nothing credential-backed on this branch, so there is nothing to preserve and no\n\t// credential to spend: fetch everything and skip the credentials endpoint entirely.\n\tif (secretKeys.length === 0) {\n\t\tconst fetched = await fetchEnvKeys(config, fetchOptions, null);\n\t\treturn {\n\t\t\tvars: preferPersisted(toEntries(fetched), source),\n\t\t\tcredential: { issued: false, keys: [], revoked: [] },\n\t\t};\n\t}\n\n\tconst persisted = readPersistedSecrets(source);\n\tconst complete =\n\t\t(!storageEnabled ||\n\t\t\tBoolean(persisted.accessKeyId && persisted.secretAccessKey)) &&\n\t\t(!gatewayEnabled || Boolean(persisted.apiToken));\n\n\t// Look the persisted secrets up whenever there are any — not only when they're complete.\n\t// An incomplete set still names the credential a newly-enabled feature is about to\n\t// supersede (a storage-only credential on a branch that just gained the AI Gateway), and\n\t// that one should be revoked rather than left live.\n\tconst named =\n\t\tpersisted.accessKeyId !== \"\" || persisted.apiToken !== \"\"\n\t\t\t? namedCredentials(\n\t\t\t\t\tawait api.listCredentials(options.projectId, branch.id),\n\t\t\t\t\tpersisted,\n\t\t\t\t)\n\t\t\t: { storage: null, gateway: null };\n\n\tconst reusable = complete\n\t\t? reusableCredential(named, { storageEnabled, gatewayEnabled })\n\t\t: null;\n\tconst scopes = previewCredentialScopes(desired.preview);\n\tconst keep =\n\t\treusable !== null && credentialScopesSatisfied(reusable.scopes, scopes);\n\n\t// Ask for everything the policy produces, minus the secrets we're keeping — which is what\n\t// stops `fetchEnv` from minting a credential it doesn't need.\n\tconst allKeys = policyEnvKeys(desired);\n\tconst fetchKeys = keep\n\t\t? allKeys.filter((key) => !secretKeys.includes(key))\n\t\t: allKeys;\n\tconst fetched = await fetchEnvKeys(\n\t\tconfig,\n\t\t// Pass the resolved id so `fetchEnv` targets the same branch this call verified against,\n\t\t// even if `options.branch` was a name that has since been reused.\n\t\t{ ...fetchOptions, branchId: branch.id, api },\n\t\tfetchKeys,\n\t);\n\n\tconst vars = preferPersisted(toEntries(fetched), source);\n\tif (keep) {\n\t\tfor (const key of secretKeys) {\n\t\t\tconst value = source[key];\n\t\t\tif (value !== undefined) vars[key] = value;\n\t\t}\n\t\treturn {\n\t\t\tvars,\n\t\t\tcredential: { issued: false, keys: secretKeys, revoked: [] },\n\t\t};\n\t}\n\n\t// A replacement was minted, so revoke what it supersedes: the credentials the old secrets\n\t// named, minus any this tool did not issue. Their secrets lived nowhere but the env source\n\t// this call replaces, so revoking them strands nothing — and it keeps a branch from\n\t// accumulating a live credential per call. Everything else on the branch is left alone: it\n\t// may belong to a teammate, another checkout, or a deployed function, and nothing\n\t// observable distinguishes those from an orphan of our own.\n\t//\n\t// Revoked *after* the fetch, so a failed fetch leaves the caller's existing secrets working.\n\tconst ours = new Set<string>();\n\tfor (const meta of [named.storage, named.gateway]) {\n\t\tif (\n\t\t\tmeta !== null &&\n\t\t\tmeta.principalType === \"user\" &&\n\t\t\tmeta.name === credentialName(branch.name)\n\t\t) {\n\t\t\tours.add(meta.tokenId);\n\t\t}\n\t}\n\tfor (const tokenId of ours) {\n\t\tawait api.revokeCredential(options.projectId, branch.id, tokenId);\n\t}\n\n\treturn {\n\t\tvars,\n\t\tcredential: { issued: true, keys: secretKeys, revoked: [...ours] },\n\t};\n}\n\n/** Read the branch credential's secrets out of an env source. */\nfunction readPersistedSecrets(source: NodeJS.ProcessEnv): PersistedSecrets {\n\tconst storage = NEON_ENV_VAR_KEYS.storage;\n\tconst gateway = NEON_ENV_VAR_KEYS.aiGateway;\n\treturn {\n\t\taccessKeyId: source[storage.accessKeyId] ?? \"\",\n\t\tsecretAccessKey: source[storage.secretAccessKey] ?? \"\",\n\t\tapiToken: source[gateway.apiKey] ?? \"\",\n\t};\n}\n\n/**\n * Keep a persisted value rather than overwriting it with an empty fetched one.\n *\n * Neon Auth's `base_url` is the case that needs this: integrations created before the API\n * returned it answer with an empty string, and the persisted copy is the only one left. An\n * empty fetched value never carries more information than a non-empty persisted one, so\n * preferring the latter is safe for every var — and it keeps a pull from blanking a working\n * line in someone's `.env`.\n */\nfunction preferPersisted(\n\tvars: Record<string, string>,\n\tsource: NodeJS.ProcessEnv,\n): Record<string, string> {\n\tconst out = { ...vars };\n\tfor (const [key, value] of Object.entries(out)) {\n\t\tif (value !== \"\") continue;\n\t\tconst persisted = source[key];\n\t\tif (persisted !== undefined && persisted !== \"\") out[key] = persisted;\n\t}\n\treturn out;\n}\n\n/**\n * The credential id embedded in an AI Gateway token. The API mints them as\n * `nt_live_<tokenIdShort>_<secret>`, and `tokenIdShort` is the public identifier the credentials\n * list reports — so a persisted token names the credential that issued it. Returns `null` for\n * anything not in that shape (a `.env.example` placeholder, a hand-typed value), which callers\n * treat as unverifiable.\n */\nfunction gatewayTokenIdShort(apiToken: string): string | null {\n\treturn /^nt_live_([^_]+)_.+$/.exec(apiToken)?.[1] ?? null;\n}\n\n/** Whether an issued credential can still be used: not revoked, not past its expiry. */\nfunction isLiveCredential(meta: NeonCredentialMeta, now: number): boolean {\n\tif (meta.revokedAt !== undefined) return false;\n\tif (meta.expiresAt === undefined) return true;\n\tconst expiresAt = Date.parse(meta.expiresAt);\n\treturn Number.isNaN(expiresAt) || expiresAt > now;\n}\n\n/**\n * The live credentials the persisted secrets name — at most one per half. A half that names\n * nothing contributes nothing, which is what a placeholder, a credential revoked in the\n * console, and one copied in from another branch all look like from here.\n */\nfunction namedCredentials(\n\tlive: NeonCredentialMeta[],\n\tpersisted: PersistedSecrets,\n): { storage: NeonCredentialMeta | null; gateway: NeonCredentialMeta | null } {\n\tconst usable = live.filter((meta) => isLiveCredential(meta, Date.now()));\n\tconst shortId = persisted.apiToken\n\t\t? gatewayTokenIdShort(persisted.apiToken)\n\t\t: null;\n\treturn {\n\t\tstorage: persisted.accessKeyId\n\t\t\t? (usable.find((meta) => meta.tokenId === persisted.accessKeyId) ??\n\t\t\t\tnull)\n\t\t\t: null,\n\t\tgateway: shortId\n\t\t\t? (usable.find((meta) => meta.tokenIdShort === shortId) ?? null)\n\t\t\t: null,\n\t};\n}\n\n/**\n * The credential the persisted secrets can be *reused* as, or `null`.\n *\n * Strict on purpose: every half the policy enables has to name a live credential, and when both\n * features are enabled they must name the *same* one — they share a single credential, so\n * halves that disagree came from two different calls and neither can be trusted.\n */\nfunction reusableCredential(\n\tnamed: ReturnType<typeof namedCredentials>,\n\tenabled: { storageEnabled: boolean; gatewayEnabled: boolean },\n): NeonCredentialMeta | null {\n\tif (enabled.storageEnabled && enabled.gatewayEnabled) {\n\t\treturn named.storage &&\n\t\t\tnamed.gateway &&\n\t\t\tnamed.storage.tokenId === named.gateway.tokenId\n\t\t\t? named.storage\n\t\t\t: null;\n\t}\n\tif (enabled.storageEnabled) return named.storage;\n\tif (enabled.gatewayEnabled) return named.gateway;\n\treturn null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reuse-secrets.js","names":[],"sources":["../../src/lib/reuse-secrets.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcredentialScopesSatisfied,\n\ttype NeonCredentialMeta,\n} from \"@neon/config/v1\";\n\nimport {\n\tcreateApiFromOptions,\n\tcredentialEnvKeys,\n\tcredentialName,\n\ttype FetchEnvOptions,\n\tfetchEnvKeys,\n\tNEON_ENV_VAR_KEYS,\n\tpolicyEnvKeys,\n\tpreviewCredentialScopes,\n\tresolveBranchPolicy,\n\ttoEntries,\n} from \"./env.js\";\n\n/**\n * What happened to the branch credential during a {@link fetchEnvReusingSecrets} call.\n */\nexport interface CredentialOutcome {\n\t/**\n\t * `true` when a new credential was minted — because none was persisted, or because the\n\t * persisted secrets could not be verified against this branch. `false` when the persisted\n\t * secrets were verified and kept, and when the policy enables nothing credential-backed.\n\t */\n\tissued: boolean;\n\t/**\n\t * The env-var keys the branch credential's secrets surface under, given what the policy\n\t * enables. Empty when the policy enables neither object storage nor the AI Gateway.\n\t */\n\tkeys: string[];\n\t/**\n\t * `tokenId`s revoked because this call superseded them. Only ever credentials the persisted\n\t * secrets named *and* that this tool issued; empty otherwise.\n\t */\n\trevoked: string[];\n\t/**\n\t * `tokenId`s this call superseded but left live, because `revokeSuperseded` was `false`.\n\t * The counterpart to {@link CredentialOutcome.revoked}: exactly the ids that would be\n\t * there instead. Lets a caller name what it orphaned rather than saying that it might\n\t * have orphaned something — always empty on the default path.\n\t */\n\tsuperseded: string[];\n}\n\n/** A resolved branch env, ready to write to a dotenv file or inject into a process. */\nexport interface ReusedBranchEnv {\n\t/** Every Neon env var for the branch, as `{ KEY: value }`. */\n\tvars: Record<string, string>;\n\t/** What happened to the branch credential. */\n\tcredential: CredentialOutcome;\n}\n\n/** The branch credential's secrets as persisted in an env source. Empty string means absent. */\ninterface PersistedSecrets {\n\taccessKeyId: string;\n\tsecretAccessKey: string;\n\tapiToken: string;\n}\n\n/**\n * Resolve a branch's env while keeping one-time secrets the caller already holds.\n *\n * {@link fetchEnvKeys} — and the public `fetchEnv` — only ever *fetch*. The Neon API returns a\n * credential's `api_token` / `s3_secret_access_key` exactly once, at mint time, so \"fetching\"\n * them means minting a new credential; a plain `fetchEnv` on every `neon dev` start or `env\n * pull` would leave a live credential behind each time. This is the wrapper that avoids that:\n * it looks at what the caller already has, decides what is still usable, and asks `fetchEnv`\n * for only the rest.\n *\n * The check is a real verification, not a presence test. A persisted secret is kept only when\n * it names a credential that still exists on this branch, is not revoked or expired, and\n * carries every scope the policy needs. A `.env.example` placeholder, a credential revoked in\n * the console, one copied in from another branch, or one predating a newly-enabled feature all\n * fail that check and get replaced.\n *\n * None of this needs local bookkeeping, because the secrets carry their own credential id:\n * `AWS_ACCESS_KEY_ID` **is** the credential's `tokenId` (the storage gateway authenticates\n * against the full id), and the AI Gateway token is minted as `nt_live_<tokenIdShort>_<secret>`,\n * where `tokenIdShort` is what the credentials list reports. The env source being replaced is\n * the record of what the last call issued.\n *\n * ```ts\n * import { fetchEnvReusingSecrets } from \"@neon/env/runtime\";\n *\n * const { vars, credential } = await fetchEnvReusingSecrets(config, {\n * projectId,\n * branch: \"main\",\n * env: { ...process.env, ...readEnvFile(\".env\") },\n * });\n * if (credential.issued) console.log(`new values for ${credential.keys.join(\", \")}`);\n * ```\n */\nexport async function fetchEnvReusingSecrets<const C extends Config>(\n\tconfig: C,\n\toptions: FetchEnvOptions & {\n\t\t/**\n\t\t * Env source holding secrets a previous call persisted — `process.env` layered with a\n\t\t * `.env` file, typically. Defaults to `process.env`.\n\t\t */\n\t\tenv?: NodeJS.ProcessEnv;\n\t\t/**\n\t\t * Revoke the credential a freshly-minted one supersedes. Defaults to `true`.\n\t\t *\n\t\t * Pass `false` when this resolve covers only *part* of what the branch has. Object\n\t\t * storage and the AI Gateway share one credential, so a partial resolve cannot tell\n\t\t * whether the credential its persisted secrets name also backs a service it is not\n\t\t * resolving — and revoking it would kill that service while its vars, which this call\n\t\t * is not rewriting, stay on disk and stop working. The cost is an orphaned credential,\n\t\t * which is the safer of the two failures. `neon env pull --service` is the caller that\n\t\t * needs this.\n\t\t */\n\t\trevokeSuperseded?: boolean;\n\t},\n): Promise<ReusedBranchEnv> {\n\tconst {\n\t\tenv: source = process.env,\n\t\trevokeSuperseded = true,\n\t\t...fetchOptions\n\t} = options;\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst { branch, desired } = await resolveBranchPolicy(config, options, api);\n\n\tconst storageEnabled = (desired.preview?.buckets.length ?? 0) > 0;\n\tconst gatewayEnabled = desired.preview?.aiGatewayEnabled ?? false;\n\tconst secretKeys = credentialEnvKeys({\n\t\tstorage: storageEnabled,\n\t\taiGateway: gatewayEnabled,\n\t});\n\n\t// Nothing credential-backed on this branch, so there is nothing to preserve and no\n\t// credential to spend: fetch everything and skip the credentials endpoint entirely.\n\tif (secretKeys.length === 0) {\n\t\tconst fetched = await fetchEnvKeys(config, fetchOptions, null);\n\t\treturn {\n\t\t\tvars: preferPersisted(toEntries(fetched), source),\n\t\t\tcredential: {\n\t\t\t\tissued: false,\n\t\t\t\tkeys: [],\n\t\t\t\trevoked: [],\n\t\t\t\tsuperseded: [],\n\t\t\t},\n\t\t};\n\t}\n\n\tconst persisted = readPersistedSecrets(source);\n\tconst complete =\n\t\t(!storageEnabled ||\n\t\t\tBoolean(persisted.accessKeyId && persisted.secretAccessKey)) &&\n\t\t(!gatewayEnabled || Boolean(persisted.apiToken));\n\n\t// Look the persisted secrets up whenever there are any — not only when they're complete.\n\t// An incomplete set still names the credential a newly-enabled feature is about to\n\t// supersede (a storage-only credential on a branch that just gained the AI Gateway), and\n\t// that one should be revoked rather than left live.\n\tconst named =\n\t\tpersisted.accessKeyId !== \"\" || persisted.apiToken !== \"\"\n\t\t\t? namedCredentials(\n\t\t\t\t\tawait api.listCredentials(options.projectId, branch.id),\n\t\t\t\t\tpersisted,\n\t\t\t\t)\n\t\t\t: { storage: null, gateway: null };\n\n\tconst reusable = complete\n\t\t? reusableCredential(named, { storageEnabled, gatewayEnabled })\n\t\t: null;\n\tconst scopes = previewCredentialScopes(desired.preview);\n\tconst keep =\n\t\treusable !== null && credentialScopesSatisfied(reusable.scopes, scopes);\n\n\t// Ask for everything the policy produces, minus the secrets we're keeping — which is what\n\t// stops `fetchEnv` from minting a credential it doesn't need.\n\tconst allKeys = policyEnvKeys(desired);\n\tconst fetchKeys = keep\n\t\t? allKeys.filter((key) => !secretKeys.includes(key))\n\t\t: allKeys;\n\tconst fetched = await fetchEnvKeys(\n\t\tconfig,\n\t\t// Pass the resolved id so `fetchEnv` targets the same branch this call verified against,\n\t\t// even if `options.branch` was a name that has since been reused.\n\t\t{ ...fetchOptions, branchId: branch.id, api },\n\t\tfetchKeys,\n\t);\n\n\tconst vars = preferPersisted(toEntries(fetched), source);\n\tif (keep) {\n\t\tfor (const key of secretKeys) {\n\t\t\tconst value = source[key];\n\t\t\tif (value !== undefined) vars[key] = value;\n\t\t}\n\t\treturn {\n\t\t\tvars,\n\t\t\tcredential: {\n\t\t\t\tissued: false,\n\t\t\t\tkeys: secretKeys,\n\t\t\t\trevoked: [],\n\t\t\t\tsuperseded: [],\n\t\t\t},\n\t\t};\n\t}\n\n\t// A replacement was minted, so revoke what it supersedes: the credentials the old secrets\n\t// named, minus any this tool did not issue. Their secrets lived nowhere but the env source\n\t// this call replaces, so revoking them strands nothing — and it keeps a branch from\n\t// accumulating a live credential per call. Everything else on the branch is left alone: it\n\t// may belong to a teammate, another checkout, or a deployed function, and nothing\n\t// observable distinguishes those from an orphan of our own.\n\t//\n\t// Revoked *after* the fetch, so a failed fetch leaves the caller's existing secrets working.\n\tconst ours = new Set<string>();\n\tfor (const meta of [named.storage, named.gateway]) {\n\t\tif (\n\t\t\tmeta !== null &&\n\t\t\tmeta.principalType === \"user\" &&\n\t\t\tmeta.name === credentialName(branch.name)\n\t\t) {\n\t\t\tours.add(meta.tokenId);\n\t\t}\n\t}\n\tif (revokeSuperseded) {\n\t\tfor (const tokenId of ours) {\n\t\t\tawait api.revokeCredential(options.projectId, branch.id, tokenId);\n\t\t}\n\t}\n\n\treturn {\n\t\tvars,\n\t\tcredential: {\n\t\t\tissued: true,\n\t\t\tkeys: secretKeys,\n\t\t\trevoked: revokeSuperseded ? [...ours] : [],\n\t\t\tsuperseded: revokeSuperseded ? [] : [...ours],\n\t\t},\n\t};\n}\n\n/** Read the branch credential's secrets out of an env source. */\nfunction readPersistedSecrets(source: NodeJS.ProcessEnv): PersistedSecrets {\n\tconst storage = NEON_ENV_VAR_KEYS.storage;\n\tconst gateway = NEON_ENV_VAR_KEYS.aiGateway;\n\treturn {\n\t\taccessKeyId: source[storage.accessKeyId] ?? \"\",\n\t\tsecretAccessKey: source[storage.secretAccessKey] ?? \"\",\n\t\tapiToken: source[gateway.apiKey] ?? \"\",\n\t};\n}\n\n/**\n * Keep a persisted value rather than overwriting it with an empty fetched one.\n *\n * Neon Auth's `base_url` is the case that needs this: integrations created before the API\n * returned it answer with an empty string, and the persisted copy is the only one left. An\n * empty fetched value never carries more information than a non-empty persisted one, so\n * preferring the latter is safe for every var — and it keeps a pull from blanking a working\n * line in someone's `.env`.\n */\nfunction preferPersisted(\n\tvars: Record<string, string>,\n\tsource: NodeJS.ProcessEnv,\n): Record<string, string> {\n\tconst out = { ...vars };\n\tfor (const [key, value] of Object.entries(out)) {\n\t\tif (value !== \"\") continue;\n\t\tconst persisted = source[key];\n\t\tif (persisted !== undefined && persisted !== \"\") out[key] = persisted;\n\t}\n\treturn out;\n}\n\n/**\n * The credential id embedded in an AI Gateway token. The API mints them as\n * `nt_live_<tokenIdShort>_<secret>`, and `tokenIdShort` is the public identifier the credentials\n * list reports — so a persisted token names the credential that issued it. Returns `null` for\n * anything not in that shape (a `.env.example` placeholder, a hand-typed value), which callers\n * treat as unverifiable.\n */\nfunction gatewayTokenIdShort(apiToken: string): string | null {\n\treturn /^nt_live_([^_]+)_.+$/.exec(apiToken)?.[1] ?? null;\n}\n\n/** Whether an issued credential can still be used: not revoked, not past its expiry. */\nfunction isLiveCredential(meta: NeonCredentialMeta, now: number): boolean {\n\tif (meta.revokedAt !== undefined) return false;\n\tif (meta.expiresAt === undefined) return true;\n\tconst expiresAt = Date.parse(meta.expiresAt);\n\treturn Number.isNaN(expiresAt) || expiresAt > now;\n}\n\n/**\n * The live credentials the persisted secrets name — at most one per half. A half that names\n * nothing contributes nothing, which is what a placeholder, a credential revoked in the\n * console, and one copied in from another branch all look like from here.\n */\nfunction namedCredentials(\n\tlive: NeonCredentialMeta[],\n\tpersisted: PersistedSecrets,\n): { storage: NeonCredentialMeta | null; gateway: NeonCredentialMeta | null } {\n\tconst usable = live.filter((meta) => isLiveCredential(meta, Date.now()));\n\tconst shortId = persisted.apiToken\n\t\t? gatewayTokenIdShort(persisted.apiToken)\n\t\t: null;\n\treturn {\n\t\tstorage: persisted.accessKeyId\n\t\t\t? (usable.find((meta) => meta.tokenId === persisted.accessKeyId) ??\n\t\t\t\tnull)\n\t\t\t: null,\n\t\tgateway: shortId\n\t\t\t? (usable.find((meta) => meta.tokenIdShort === shortId) ?? null)\n\t\t\t: null,\n\t};\n}\n\n/**\n * The credential the persisted secrets can be *reused* as, or `null`.\n *\n * Strict on purpose: every half the policy enables has to name a live credential, and when both\n * features are enabled they must name the *same* one — they share a single credential, so\n * halves that disagree came from two different calls and neither can be trusted.\n */\nfunction reusableCredential(\n\tnamed: ReturnType<typeof namedCredentials>,\n\tenabled: { storageEnabled: boolean; gatewayEnabled: boolean },\n): NeonCredentialMeta | null {\n\tif (enabled.storageEnabled && enabled.gatewayEnabled) {\n\t\treturn named.storage &&\n\t\t\tnamed.gateway &&\n\t\t\tnamed.storage.tokenId === named.gateway.tokenId\n\t\t\t? named.storage\n\t\t\t: null;\n\t}\n\tif (enabled.storageEnabled) return named.storage;\n\tif (enabled.gatewayEnabled) return named.gateway;\n\treturn null;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neondatabase/env",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Resolve and inject Neon connection strings for the branch selected by your neon.ts policy. fetchEnv / parseEnv plus a `neon-env` CLI with `run` and `export`.",
5
5
  "keywords": [
6
6
  "neon",