@neondatabase/env 0.1.2 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @neondatabase/env
2
2
 
3
- Resolve and inject Neon connection strings for the branch selected by your `neon.ts` policy. Exposes `fetchEnv` / `parseEnv` functions plus a single CLI command: `neon-env run -- <cmd>`.
3
+ Resolve and inject Neon connection strings for the branch selected by your `neon.ts` policy. Exposes `fetchEnv` / `parseEnv` functions plus a `neon-env` CLI with `run` (inject env into a command) and `export` (print env to stdout).
4
4
 
5
5
  Builds on [`@neondatabase/config`](../config) — it reuses the `Config` policy type and the Neon API client.
6
6
 
@@ -12,9 +12,9 @@ npm install @neondatabase/env
12
12
 
13
13
  ## Functions
14
14
 
15
- The library functions are **filesystem- and env-agnostic**: `fetchEnv` requires an explicit `projectId` + `branchId`, and `parseEnv` requires an explicit `branchName`. (The `neon-env` CLI does the `.neon`/`NEON_*` resolution and passes these in.)
15
+ The library functions are **filesystem- and env-agnostic**: `fetchEnv` requires an explicit `projectId` + `branchId`. (The `neon-env` CLI does the `.neon`/`NEON_*` resolution and passes these in.)
16
16
 
17
- > `parseEnv` takes a branch **name**, not an id, because it makes no API call it only needs the branch to evaluate your `neon.ts` policy, which switches on `branch.name`. The API-backed functions take a `branchId` (`br-…`) and read the name back from Neon.
17
+ > `parseEnv` takes **no branch name**: the secret set is static (top-level `config.auth` / `config.dataApi`), so it reads those toggles directly without evaluating the per-branch closure. Its optional second argument is a **scope** — omit it for external (app/build) env, or pass a **function slug** when running inside that deployed function to also get a typed `function` namespace of its declared env keys.
18
18
 
19
19
  ```ts
20
20
  import config from "../neon";
@@ -26,7 +26,11 @@ const db = drizzle(neon(env.postgres.databaseUrl), { schema });
26
26
 
27
27
  // Sync — reads already-injected process.env and validates it (no network).
28
28
  // Use in app bootstrap where async isn't available.
29
- const env2 = parseEnv(config, process.env.NEON_BRANCH_NAME ?? "main");
29
+ const env2 = parseEnv(config);
30
+
31
+ // Inside a deployed function, pass its slug for the typed `function` namespace:
32
+ const fnEnv = parseEnv(config, "hello");
33
+ fnEnv.function.resendApiKey; // typed from hello's declared env keys
30
34
  ```
31
35
 
32
36
  Both return the same namespaced `NeonEnv` shape: `postgres` is always present; `auth` and `dataApi` are included (and statically typed) when the evaluated branch policy enables them.
@@ -34,12 +38,14 @@ Both return the same namespaced `NeonEnv` shape: `postgres` is always present; `
34
38
  | Function | Description |
35
39
  | --- | --- |
36
40
  | `fetchEnv(config, { projectId, branchId, ... })` | Async. Calls the Neon API for the given project + branch and returns live connection strings (and Auth/Data API values when enabled). `projectId` and `branchId` are required (`branchId` is a `br-…` id). |
37
- | `parseEnv(config, branchName)` | Sync. Reads/validates the Neon env vars already present in `process.env`, evaluating the policy for `branchName`. Throws `PlatformError(EnvNotInjected)` listing missing vars when the env isn't populated. |
41
+ | `parseEnv(config)` / `parseEnv(config, slug)` | 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. Throws `PlatformError(EnvNotInjected)` listing missing vars when the env isn't populated. |
38
42
  | `toEntries(env)` | Project a resolved `NeonEnv` into `{ KEY: value }` pairs for cross-process transport (named after the web `.entries()` convention; returns a `Record`). |
39
43
 
40
44
  ## CLI
41
45
 
42
- One command — inject the env vars for your `neon.ts` branch into a dev command:
46
+ ### `run` — inject env into a command
47
+
48
+ Inject the env vars for your `neon.ts` branch into a dev command:
43
49
 
44
50
  ```bash
45
51
  neon-env run -- npm run dev
@@ -48,7 +54,23 @@ neon-env run -- pnpm dev
48
54
 
49
55
  `run` loads `neon.ts`, resolves the branch (via `--branch`, `NEON_BRANCH_ID`, or `.neon[/project.json]`), fetches the connection strings from Neon, and spawns the command with `DATABASE_URL` / `DATABASE_URL_UNPOOLED` (and `NEON_AUTH_BASE_URL` / `NEON_DATA_API_URL` when the policy enables them) injected on top of the inherited environment. Stdio is inherited so interactive dev servers keep working, and the parent exits with the child's exit code.
50
56
 
51
- Flags: `--config <path>`, `--project-id`, `--branch`, `--api-key`, `--debug`.
57
+ ### `export` print env to stdout
58
+
59
+ Resolve the same branch env, but print it instead of spawning a process — for piping into other env tools:
60
+
61
+ ```bash
62
+ neon-env export # dotenv KEY=value lines (default)
63
+ neon-env export --format json # JSON object
64
+ ```
65
+
66
+ For example, [varlock](https://varlock.dev) can bulk-load Neon's branch env via its `exec()` resolver:
67
+
68
+ ```bash
69
+ # .env.schema
70
+ # @setValuesBulk(exec(`neon-env export --format json`), format=json)
71
+ ```
72
+
73
+ Flags (both commands): `--config <path>`, `--project-id`, `--branch`, `--api-key`, `--debug`. `export` also takes `--format dotenv|json`.
52
74
 
53
75
  ## Env vars produced
54
76
 
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { runEnvRun } from "./lib/cli/commands.js";
2
+ import { runEnvExport, runEnvRun } from "./lib/cli/commands.js";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import yargs from "yargs";
6
6
  import { hideBin } from "yargs/helpers";
7
7
  //#region src/cli.ts
8
8
  const pkgVersion = readPackageVersion();
9
- const argv = yargs(hideBin(process.argv)).scriptName("neon-env").usage("$0 run -- <command> [options]").parserConfiguration({ "populate--": true }).option("debug", {
9
+ const argv = yargs(hideBin(process.argv)).scriptName("neon-env").usage("$0 <command> [options]").parserConfiguration({ "populate--": true }).option("debug", {
10
10
  type: "boolean",
11
11
  default: false,
12
12
  describe: "Print stack traces and structured error details when something fails"
@@ -22,6 +22,22 @@ const argv = yargs(hideBin(process.argv)).scriptName("neon-env").usage("$0 run -
22
22
  }).option("api-key", {
23
23
  type: "string",
24
24
  describe: "Neon API key (defaults to NEON_API_KEY)"
25
+ })).command("export", "Print the branch's Neon env vars (from your neon.ts policy) to stdout, as dotenv lines or JSON. Useful for piping into other env tools, e.g. `neon-env export --format json`.", (y) => y.option("format", {
26
+ choices: ["dotenv", "json"],
27
+ default: "dotenv",
28
+ describe: "Output format: dotenv (KEY=value lines) or json"
29
+ }).option("config", {
30
+ type: "string",
31
+ describe: "Path to neon.ts (defaults to walking up from cwd)"
32
+ }).option("project-id", {
33
+ type: "string",
34
+ describe: "Override the .neon/project.json projectId"
35
+ }).option("branch", {
36
+ type: "string",
37
+ describe: "Override the .neon/project.json branchId / NEON_BRANCH_ID"
38
+ }).option("api-key", {
39
+ type: "string",
40
+ describe: "Neon API key (defaults to NEON_API_KEY)"
25
41
  })).demandCommand(1, "Run `neon-env --help` to see the available commands.").strict().help().version(pkgVersion).parseSync();
26
42
  const command = String(argv._[0]);
27
43
  const cwd = process.cwd();
@@ -36,6 +52,15 @@ switch (command) {
36
52
  ...typeof argv["api-key"] === "string" ? { apiKey: argv["api-key"] } : {}
37
53
  }, { cwd });
38
54
  break;
55
+ case "export":
56
+ result = await runEnvExport({
57
+ format: argv.format === "json" ? "json" : "dotenv",
58
+ ...typeof argv.config === "string" ? { configPath: argv.config } : {},
59
+ ...typeof argv["project-id"] === "string" ? { projectId: argv["project-id"] } : {},
60
+ ...typeof argv.branch === "string" ? { branch: argv.branch } : {},
61
+ ...typeof argv["api-key"] === "string" ? { apiKey: argv["api-key"] } : {}
62
+ }, { cwd });
63
+ break;
39
64
  default: result = {
40
65
  exitCode: 1,
41
66
  stdout: "",
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { type CommandResult, runEnvRun } from \"./lib/cli/commands.js\";\n\nconst pkgVersion = readPackageVersion();\n\nconst argv = yargs(hideBin(process.argv))\n\t.scriptName(\"neon-env\")\n\t.usage(\"$0 run -- <command> [options]\")\n\t.parserConfiguration({ \"populate--\": true })\n\t.option(\"debug\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescribe:\n\t\t\t\"Print stack traces and structured error details when something fails\",\n\t})\n\t.command(\n\t\t\"run\",\n\t\t\"Run a command with Neon env vars (from your neon.ts policy) injected into its environment. Use `--` to separate the command: `neon-env run -- npm run dev`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Override the .neon/project.json branchId / NEON_BRANCH_ID\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t}),\n\t)\n\t.demandCommand(1, \"Run `neon-env --help` to see the available commands.\")\n\t.strict()\n\t.help()\n\t.version(pkgVersion)\n\t.parseSync();\n\nconst command = String(argv._[0]);\nconst cwd = process.cwd();\n\nlet result: CommandResult;\nswitch (command) {\n\tcase \"run\": {\n\t\tconst passthrough = Array.isArray(argv[\"--\"])\n\t\t\t? argv[\"--\"].map(String)\n\t\t\t: [];\n\t\tresult = await runEnvRun(\n\t\t\t{\n\t\t\t\tcommand: passthrough,\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tdefault:\n\t\tresult = {\n\t\t\texitCode: 1,\n\t\t\tstdout: \"\",\n\t\t\tstderr: `Unknown command: ${command}\\n`,\n\t\t};\n}\n\nif (result.stdout) process.stdout.write(result.stdout);\nif (result.stderr) process.stderr.write(result.stderr);\nif (argv.debug && result.exitCode !== 0 && result.debugInfo) {\n\tprocess.stderr.write(`\\n--- debug ---\\n${result.debugInfo}\\n`);\n}\nprocess.exit(result.exitCode);\n\nfunction readPackageVersion(): string {\n\t// The built CLI lives at `dist/cli.js`, so `package.json` is one directory up. When\n\t// running from source (tsx, vitest), the file lives at `src/cli.ts` and `package.json`\n\t// is again one directory up. Single resolution covers both layouts.\n\ttry {\n\t\tconst pkgUrl = new URL(\"../package.json\", import.meta.url);\n\t\tconst raw = readFileSync(fileURLToPath(pkgUrl), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as { version?: unknown };\n\t\treturn typeof parsed.version === \"string\" ? parsed.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n"],"mappings":";;;;;;;AAQA,MAAM,aAAa,mBAAmB;AAEtC,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,EACtC,WAAW,UAAU,EACrB,MAAM,+BAA+B,EACrC,oBAAoB,EAAE,cAAc,KAAK,CAAC,EAC1C,OAAO,SAAS;CAChB,MAAM;CACN,SAAS;CACT,UACC;AACF,CAAC,EACA,QACA,OACA,gKACC,MACA,EACE,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,cAAc;CACrB,MAAM;CACN,UAAU;AACX,CAAC,EACA,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,WAAW;CAClB,MAAM;CACN,UAAU;AACX,CAAC,CACJ,EACC,cAAc,GAAG,sDAAsD,EACvE,OAAO,EACP,KAAK,EACL,QAAQ,UAAU,EAClB,UAAU;AAEZ,MAAM,UAAU,OAAO,KAAK,EAAE,EAAE;AAChC,MAAM,MAAM,QAAQ,IAAI;AAExB,IAAI;AACJ,QAAQ,SAAR;CACC,KAAK;EAIJ,SAAS,MAAM,UACd;GACC,SALkB,MAAM,QAAQ,KAAK,KAAK,IACzC,KAAK,MAAM,IAAI,MAAM,IACrB,CAAC;GAIF,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,YAAY,KAAK,OAAO,IAC1B,CAAC;GACJ,GAAI,OAAO,KAAK,kBAAkB,WAC/B,EAAE,WAAW,KAAK,cAAc,IAChC,CAAC;GACJ,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,QAAQ,KAAK,OAAO,IACtB,CAAC;GACJ,GAAI,OAAO,KAAK,eAAe,WAC5B,EAAE,QAAQ,KAAK,WAAW,IAC1B,CAAC;EACL,GACA,EAAE,IAAI,CACP;EACA;CAED,SACC,SAAS;EACR,UAAU;EACV,QAAQ;EACR,QAAQ,oBAAoB,QAAQ;CACrC;AACF;AAEA,IAAI,OAAO,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,IAAI,OAAO,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,IAAI,KAAK,SAAS,OAAO,aAAa,KAAK,OAAO,WACjD,QAAQ,OAAO,MAAM,oBAAoB,OAAO,UAAU,GAAG;AAE9D,QAAQ,KAAK,OAAO,QAAQ;AAE5B,SAAS,qBAA6B;CAIrC,IAAI;EAEH,MAAM,MAAM,aAAa,cAAc,IADpB,IAAI,mBAAmB,OAAO,KAAK,GACV,CAAC,GAAG,OAAO;EACvD,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,OAAO,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;CAC9D,QAAQ;EACP,OAAO;CACR;AACD"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readFileSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport {\n\ttype CommandResult,\n\trunEnvExport,\n\trunEnvRun,\n} from \"./lib/cli/commands.js\";\n\nconst pkgVersion = readPackageVersion();\n\nconst argv = yargs(hideBin(process.argv))\n\t.scriptName(\"neon-env\")\n\t.usage(\"$0 <command> [options]\")\n\t.parserConfiguration({ \"populate--\": true })\n\t.option(\"debug\", {\n\t\ttype: \"boolean\",\n\t\tdefault: false,\n\t\tdescribe:\n\t\t\t\"Print stack traces and structured error details when something fails\",\n\t})\n\t.command(\n\t\t\"run\",\n\t\t\"Run a command with Neon env vars (from your neon.ts policy) injected into its environment. Use `--` to separate the command: `neon-env run -- npm run dev`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Override the .neon/project.json branchId / NEON_BRANCH_ID\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t}),\n\t)\n\t.command(\n\t\t\"export\",\n\t\t\"Print the branch's Neon env vars (from your neon.ts policy) to stdout, as dotenv lines or JSON. Useful for piping into other env tools, e.g. `neon-env export --format json`.\",\n\t\t(y) =>\n\t\t\ty\n\t\t\t\t.option(\"format\", {\n\t\t\t\t\tchoices: [\"dotenv\", \"json\"] as const,\n\t\t\t\t\tdefault: \"dotenv\",\n\t\t\t\t\tdescribe: \"Output format: dotenv (KEY=value lines) or json\",\n\t\t\t\t})\n\t\t\t\t.option(\"config\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Path to neon.ts (defaults to walking up from cwd)\",\n\t\t\t\t})\n\t\t\t\t.option(\"project-id\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Override the .neon/project.json projectId\",\n\t\t\t\t})\n\t\t\t\t.option(\"branch\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe:\n\t\t\t\t\t\t\"Override the .neon/project.json branchId / NEON_BRANCH_ID\",\n\t\t\t\t})\n\t\t\t\t.option(\"api-key\", {\n\t\t\t\t\ttype: \"string\",\n\t\t\t\t\tdescribe: \"Neon API key (defaults to NEON_API_KEY)\",\n\t\t\t\t}),\n\t)\n\t.demandCommand(1, \"Run `neon-env --help` to see the available commands.\")\n\t.strict()\n\t.help()\n\t.version(pkgVersion)\n\t.parseSync();\n\nconst command = String(argv._[0]);\nconst cwd = process.cwd();\n\nlet result: CommandResult;\nswitch (command) {\n\tcase \"run\": {\n\t\tconst passthrough = Array.isArray(argv[\"--\"])\n\t\t\t? argv[\"--\"].map(String)\n\t\t\t: [];\n\t\tresult = await runEnvRun(\n\t\t\t{\n\t\t\t\tcommand: passthrough,\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tcase \"export\": {\n\t\tresult = await runEnvExport(\n\t\t\t{\n\t\t\t\tformat: argv.format === \"json\" ? \"json\" : \"dotenv\",\n\t\t\t\t...(typeof argv.config === \"string\"\n\t\t\t\t\t? { configPath: argv.config }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"project-id\"] === \"string\"\n\t\t\t\t\t? { projectId: argv[\"project-id\"] }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv.branch === \"string\"\n\t\t\t\t\t? { branch: argv.branch }\n\t\t\t\t\t: {}),\n\t\t\t\t...(typeof argv[\"api-key\"] === \"string\"\n\t\t\t\t\t? { apiKey: argv[\"api-key\"] }\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t\t{ cwd },\n\t\t);\n\t\tbreak;\n\t}\n\tdefault:\n\t\tresult = {\n\t\t\texitCode: 1,\n\t\t\tstdout: \"\",\n\t\t\tstderr: `Unknown command: ${command}\\n`,\n\t\t};\n}\n\nif (result.stdout) process.stdout.write(result.stdout);\nif (result.stderr) process.stderr.write(result.stderr);\nif (argv.debug && result.exitCode !== 0 && result.debugInfo) {\n\tprocess.stderr.write(`\\n--- debug ---\\n${result.debugInfo}\\n`);\n}\nprocess.exit(result.exitCode);\n\nfunction readPackageVersion(): string {\n\t// The built CLI lives at `dist/cli.js`, so `package.json` is one directory up. When\n\t// running from source (tsx, vitest), the file lives at `src/cli.ts` and `package.json`\n\t// is again one directory up. Single resolution covers both layouts.\n\ttry {\n\t\tconst pkgUrl = new URL(\"../package.json\", import.meta.url);\n\t\tconst raw = readFileSync(fileURLToPath(pkgUrl), \"utf-8\");\n\t\tconst parsed = JSON.parse(raw) as { version?: unknown };\n\t\treturn typeof parsed.version === \"string\" ? parsed.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n"],"mappings":";;;;;;;AAYA,MAAM,aAAa,mBAAmB;AAEtC,MAAM,OAAO,MAAM,QAAQ,QAAQ,IAAI,CAAC,EACtC,WAAW,UAAU,EACrB,MAAM,wBAAwB,EAC9B,oBAAoB,EAAE,cAAc,KAAK,CAAC,EAC1C,OAAO,SAAS;CAChB,MAAM;CACN,SAAS;CACT,UACC;AACF,CAAC,EACA,QACA,OACA,gKACC,MACA,EACE,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,cAAc;CACrB,MAAM;CACN,UAAU;AACX,CAAC,EACA,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,WAAW;CAClB,MAAM;CACN,UAAU;AACX,CAAC,CACJ,EACC,QACA,UACA,kLACC,MACA,EACE,OAAO,UAAU;CACjB,SAAS,CAAC,UAAU,MAAM;CAC1B,SAAS;CACT,UAAU;AACX,CAAC,EACA,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,cAAc;CACrB,MAAM;CACN,UAAU;AACX,CAAC,EACA,OAAO,UAAU;CACjB,MAAM;CACN,UACC;AACF,CAAC,EACA,OAAO,WAAW;CAClB,MAAM;CACN,UAAU;AACX,CAAC,CACJ,EACC,cAAc,GAAG,sDAAsD,EACvE,OAAO,EACP,KAAK,EACL,QAAQ,UAAU,EAClB,UAAU;AAEZ,MAAM,UAAU,OAAO,KAAK,EAAE,EAAE;AAChC,MAAM,MAAM,QAAQ,IAAI;AAExB,IAAI;AACJ,QAAQ,SAAR;CACC,KAAK;EAIJ,SAAS,MAAM,UACd;GACC,SALkB,MAAM,QAAQ,KAAK,KAAK,IACzC,KAAK,MAAM,IAAI,MAAM,IACrB,CAAC;GAIF,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,YAAY,KAAK,OAAO,IAC1B,CAAC;GACJ,GAAI,OAAO,KAAK,kBAAkB,WAC/B,EAAE,WAAW,KAAK,cAAc,IAChC,CAAC;GACJ,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,QAAQ,KAAK,OAAO,IACtB,CAAC;GACJ,GAAI,OAAO,KAAK,eAAe,WAC5B,EAAE,QAAQ,KAAK,WAAW,IAC1B,CAAC;EACL,GACA,EAAE,IAAI,CACP;EACA;CAED,KAAK;EACJ,SAAS,MAAM,aACd;GACC,QAAQ,KAAK,WAAW,SAAS,SAAS;GAC1C,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,YAAY,KAAK,OAAO,IAC1B,CAAC;GACJ,GAAI,OAAO,KAAK,kBAAkB,WAC/B,EAAE,WAAW,KAAK,cAAc,IAChC,CAAC;GACJ,GAAI,OAAO,KAAK,WAAW,WACxB,EAAE,QAAQ,KAAK,OAAO,IACtB,CAAC;GACJ,GAAI,OAAO,KAAK,eAAe,WAC5B,EAAE,QAAQ,KAAK,WAAW,IAC1B,CAAC;EACL,GACA,EAAE,IAAI,CACP;EACA;CAED,SACC,SAAS;EACR,UAAU;EACV,QAAQ;EACR,QAAQ,oBAAoB,QAAQ;CACrC;AACF;AAEA,IAAI,OAAO,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,IAAI,OAAO,QAAQ,QAAQ,OAAO,MAAM,OAAO,MAAM;AACrD,IAAI,KAAK,SAAS,OAAO,aAAa,KAAK,OAAO,WACjD,QAAQ,OAAO,MAAM,oBAAoB,OAAO,UAAU,GAAG;AAE9D,QAAQ,KAAK,OAAO,QAAQ;AAE5B,SAAS,qBAA6B;CAIrC,IAAI;EAEH,MAAM,MAAM,aAAa,cAAc,IADpB,IAAI,mBAAmB,OAAO,KAAK,GACV,CAAC,GAAG,OAAO;EACvD,MAAM,SAAS,KAAK,MAAM,GAAG;EAC7B,OAAO,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;CAC9D,QAAQ;EACP,OAAO;CACR;AACD"}
@@ -1,4 +1,4 @@
1
- import { BucketAccessLevel, ComputeSettings, FunctionMemoryMib, FunctionRuntime } from "./types.js";
1
+ import { BucketAccessLevel, ComputeSettings, FunctionRuntime } from "./types.js";
2
2
 
3
3
  //#region ../config/dist/lib/neon-api.d.ts
4
4
 
@@ -141,7 +141,6 @@ interface NeonFunctionSnapshot {
141
141
  interface DeployFunctionInput {
142
142
  bundle: Uint8Array;
143
143
  runtime: FunctionRuntime;
144
- memoryMib: FunctionMemoryMib;
145
144
  environment: Record<string, string>;
146
145
  }
147
146
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"neon-api.d.ts","names":["BucketAccessLevel","ComputeSettings","FunctionMemoryMib","FunctionRuntime","NeonProjectSnapshot","NeonBranchSnapshot","NeonEndpointSnapshot","CreateProjectInput","CreateBranchInput","UpdateBranchInput","NeonRoleSnapshot","NeonDatabaseSnapshot","NeonAuthSnapshot","NeonDataApiSnapshot","NeonBucketSnapshot","CreateBucketInput","NeonFunctionSnapshot","DeployFunctionInput","Uint8Array","Record","NeonFunctionDeploymentSnapshot","GetConnectionUriInput","NeonApi","Promise"],"sources":["../../../../../config/dist/lib/neon-api.d.ts"],"sourcesContent":["import { BucketAccessLevel, ComputeSettings, FunctionMemoryMib, FunctionRuntime } from \"./types.js\";\n\n//#region src/lib/neon-api.d.ts\n\n/**\n * Snapshot of a Neon project field set we care about. Maps onto a subset of the upstream\n * `@neondatabase/api-client` `Project` type. We do **not** widen this to the full upstream\n * shape — keeping the surface narrow makes the in-memory fake practical to maintain.\n */\ninterface NeonProjectSnapshot {\n id: string;\n name: string;\n regionId: string;\n pgVersion: number;\n orgId?: string;\n defaultEndpointSettings?: ComputeSettings;\n}\ninterface NeonBranchSnapshot {\n id: string;\n name: string;\n parentId?: string;\n isDefault: boolean;\n /** Whether the branch is marked protected on Neon. */\n protected: boolean;\n expiresAt?: string;\n}\ninterface NeonEndpointSnapshot {\n id: string;\n branchId: string;\n type: \"read_only\" | \"read_write\";\n autoscalingLimitMinCu: ComputeSettings[\"autoscalingLimitMinCu\"];\n autoscalingLimitMaxCu: ComputeSettings[\"autoscalingLimitMaxCu\"];\n suspendTimeout: ComputeSettings[\"suspendTimeout\"];\n}\ninterface CreateProjectInput {\n name: string;\n regionId: string;\n pgVersion?: number;\n orgId?: string;\n defaultEndpointSettings?: ComputeSettings;\n /**\n * Optional name for the project's auto-created default branch. When omitted, Neon\n * uses its own default (`main`).\n */\n defaultBranchName?: string;\n}\ninterface CreateBranchInput {\n name: string;\n parentId?: string;\n expiresAt?: string;\n /** When `true`, the branch is created with the `protected` flag set on Neon. */\n protected?: boolean;\n computeSettings?: ComputeSettings;\n}\ninterface UpdateBranchInput {\n name?: string;\n expiresAt?: string | null;\n /** When set, toggles the branch's `protected` flag on Neon. */\n protected?: boolean;\n}\n/**\n * A role on a Neon branch (e.g. `neondb_owner`). Passwords are never returned by\n * {@link NeonApi.listBranchRoles}; use {@link NeonApi.getConnectionUri} to fetch a URI\n * with the role's password baked in.\n */\ninterface NeonRoleSnapshot {\n name: string;\n branchId: string;\n /** Whether the role is system-protected (cannot be deleted). */\n protected: boolean;\n}\n/**\n * A database on a Neon branch (e.g. `neondb`).\n */\ninterface NeonDatabaseSnapshot {\n name: string;\n branchId: string;\n /** The role that owns the database (one role can own multiple databases). */\n ownerName: string;\n}\n/**\n * Bits of a Neon Auth integration. The key fields are optional because the Neon API only\n * includes them on create / rotate responses; `GET /auth` returns the public fields.\n */\ninterface NeonAuthSnapshot {\n /** The Neon Auth project id (`auth_provider_project_id` on the Neon API). */\n projectId: string;\n /** Public client key (`pub_client_key`), only present on create / rotate responses. */\n publishableClientKey?: string;\n /** Secret server key (`secret_server_key`), only present on create / rotate responses. */\n secretServerKey?: string;\n /** JWKS URL for verifying tokens issued by Neon Auth. */\n jwksUrl: string;\n /** Optional base URL of the Neon Auth deployment. */\n baseUrl?: string;\n}\n/**\n * Public, fetchable bits of a Neon Data API integration on a specific branch.\n */\ninterface NeonDataApiSnapshot {\n /** REST endpoint URL. */\n url: string;\n}\n/**\n * A branchable object-storage bucket (Preview). Backed by the Neon Platform\n * branchable-storage service.\n */\ninterface NeonBucketSnapshot {\n name: string;\n accessLevel: BucketAccessLevel;\n}\n/**\n * Input for creating a bucket on a branch.\n */\ninterface CreateBucketInput {\n name: string;\n accessLevel?: BucketAccessLevel;\n}\n/**\n * A Neon Function on a branch (Preview). Mirrors the subset of the Functions API we model:\n * the immutable `slug`, the display `name`, and the active deployment id when one exists.\n */\ninterface NeonFunctionSnapshot {\n /** Opaque, stable function identifier. */\n id: string;\n /** Branch-unique slug (the invocation path segment). Immutable. */\n slug: string;\n /** Free-form display name. */\n name: string;\n /** URL at which the function is invoked. */\n invocationUrl: string;\n /** Id (platform version number) of the active deployment, when any code is deployed. */\n activeDeploymentId?: number;\n}\n/**\n * Input for deploying code to a function. `bundle` is the already-built ZIP archive of the\n * function source — building it (esbuild + zip) is an imperative step performed by the\n * caller, not by the {@link NeonApi} adapter.\n */\ninterface DeployFunctionInput {\n bundle: Uint8Array;\n runtime: FunctionRuntime;\n memoryMib: FunctionMemoryMib;\n environment: Record<string, string>;\n}\n/**\n * A function deployment (Preview).\n */\ninterface NeonFunctionDeploymentSnapshot {\n /** The deployment id (monotonic per function). */\n id: number;\n status: \"pending\" | \"building\" | \"completed\" | \"failed\";\n}\n/**\n * Parameters accepted by {@link NeonApi.getConnectionUri}. `branchId` and `endpointId`\n * are optional — when omitted, the API uses the project's default branch and that\n * branch's read-write endpoint, respectively.\n */\ninterface GetConnectionUriInput {\n branchId?: string;\n endpointId?: string;\n databaseName: string;\n roleName: string;\n /** When `true`, returns the pooled (PgBouncer) URI instead of the direct URI. */\n pooled?: boolean;\n}\n/**\n * Narrow façade over the Neon management API. `pullConfig`, `pushConfig`, and `fetchEnv`\n * depend on this interface — *not* on `@neondatabase/api-client` directly — which lets us\n * inject a real in-memory fake during tests without resorting to module mocks.\n */\ninterface NeonApi {\n listProjects(filter: {\n orgId?: string;\n }): Promise<NeonProjectSnapshot[]>;\n getProject(projectId: string): Promise<NeonProjectSnapshot>;\n createProject(input: CreateProjectInput): Promise<NeonProjectSnapshot>;\n updateProject(projectId: string, input: {\n name?: string;\n defaultEndpointSettings?: ComputeSettings;\n }): Promise<NeonProjectSnapshot>;\n listBranches(projectId: string): Promise<NeonBranchSnapshot[]>;\n createBranch(projectId: string, input: CreateBranchInput): Promise<{\n branch: NeonBranchSnapshot;\n endpoints: NeonEndpointSnapshot[];\n }>;\n updateBranch(projectId: string, branchId: string, input: UpdateBranchInput): Promise<NeonBranchSnapshot>;\n listEndpoints(projectId: string): Promise<NeonEndpointSnapshot[]>;\n updateEndpoint(projectId: string, endpointId: string, settings: ComputeSettings): Promise<NeonEndpointSnapshot>;\n /** List roles on a branch. Used by {@link fetchEnv} to auto-pick the role when only one exists. */\n listBranchRoles(projectId: string, branchId: string): Promise<NeonRoleSnapshot[]>;\n /** List databases on a branch. Used by {@link fetchEnv} to auto-pick the database when only one exists. */\n listBranchDatabases(projectId: string, branchId: string): Promise<NeonDatabaseSnapshot[]>;\n /**\n * Fetch a Postgres connection URI for the given role + database on a branch.\n * Returns the same string the Neon Console shows under \"Connection Details\".\n */\n getConnectionUri(projectId: string, input: GetConnectionUriInput): Promise<{\n uri: string;\n }>;\n /**\n * Fetch the Neon Auth integration attached to a specific branch. Returns `null` when\n * no integration is enabled — used by `fetchEnv` to decide whether the `env.auth`\n * namespace can be populated.\n */\n getNeonAuth(projectId: string, branchId: string): Promise<NeonAuthSnapshot | null>;\n /**\n * Enable the Neon Auth integration on a specific branch. Idempotent: if an integration\n * is already enabled, the existing snapshot is returned unchanged. Used by\n * `pushConfig` and `branch` to honour branch policy `auth: {}` / `auth.enabled: true`.\n */\n enableNeonAuth(projectId: string, branchId: string, input?: {\n databaseName?: string;\n }): Promise<NeonAuthSnapshot>;\n /**\n * Fetch the Neon Data API integration attached to a specific branch + database.\n * Returns `null` when no integration is enabled — used by `fetchEnv` to decide\n * whether the `env.dataApi` namespace can be populated.\n */\n getNeonDataApi(projectId: string, branchId: string, databaseName: string): Promise<NeonDataApiSnapshot | null>;\n /**\n * Enable the Neon Data API integration on a specific branch + database. Idempotent:\n * if an integration is already enabled, the existing snapshot is returned unchanged.\n * Used by `pushConfig` and `branch` to honour branch policy `dataApi: {}` / `dataApi.enabled: true`.\n */\n enableProjectBranchDataApi(projectId: string, branchId: string, databaseName: string): Promise<NeonDataApiSnapshot>;\n /** List branchable object-storage buckets visible on a branch. */\n listBranchBuckets(projectId: string, branchId: string): Promise<NeonBucketSnapshot[]>;\n /** Create a bucket on a branch. Used by `pushConfig` to honour `preview.buckets`. */\n createBranchBucket(projectId: string, branchId: string, input: CreateBucketInput): Promise<NeonBucketSnapshot>;\n /** Delete a bucket from a branch. */\n deleteBranchBucket(projectId: string, branchId: string, bucketName: string): Promise<void>;\n /** List functions on a branch. */\n listBranchFunctions(projectId: string, branchId: string): Promise<NeonFunctionSnapshot[]>;\n /**\n * Create a function on a branch. The function has no deployment until code is deployed\n * to it with {@link deployBranchFunction}.\n */\n createBranchFunction(projectId: string, branchId: string, input: {\n slug: string;\n name: string;\n }): Promise<NeonFunctionSnapshot>;\n /** Delete a function (by slug) from a branch. */\n deleteBranchFunction(projectId: string, branchId: string, slug: string): Promise<void>;\n /**\n * Deploy a built bundle to a function. The newest deployment becomes active. The\n * `bundle` is built (esbuild + zip) by the caller and passed in as bytes.\n */\n deployBranchFunction(projectId: string, branchId: string, slug: string, input: DeployFunctionInput): Promise<NeonFunctionDeploymentSnapshot>;\n /**\n * Whether the AI Gateway is enabled on a branch. Toggle-style, like Neon Auth / Data\n * API: used by both `fetchEnv` (to decide visibility) and `pushConfig` (to diff intent).\n */\n getAiGatewayEnabled(projectId: string, branchId: string): Promise<boolean>;\n /** Enable the AI Gateway on a branch. Idempotent. */\n enableAiGateway(projectId: string, branchId: string): Promise<void>;\n /** Disable the AI Gateway on a branch. Idempotent. */\n disableAiGateway(projectId: string, branchId: string): Promise<void>;\n}\n//#endregion\nexport { CreateBranchInput, CreateBucketInput, CreateProjectInput, DeployFunctionInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBucketSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, UpdateBranchInput };\n//# sourceMappingURL=neon-api.d.ts.map"],"mappings":";;;;;AAe2C;AAEf;;;;UARlBI,mBAAAA,CAuBQH;EAAe,EAAA,EAAA,MAAA;EAAA,IAEvBM,EAAAA,MAAAA;EAKiC,QAOjCC,EAAAA,MAAAA;EAMyB,SAEzBC,EAAAA,MAAAA;EAAiB,KAWjBC,CAAAA,EAAAA,MAAAA;EAAgB,uBAShBC,CAAAA,EA3DkBV,eA2DE;AAAA;AAUJ,UAnEhBI,kBAAAA,CAkFmB;EAAA,EAQnBS,EAAAA,MAAAA;EAEsB,IAKtBC,EAAAA,MAAAA;EAEuB,QAMvBC,CAAAA,EAAAA,MAAAA;EAAoB,SAiBpBC,EAAAA,OAAAA;EAAmB;WACnBC,EAAAA,OAAAA;WACCf,CAAAA,EAAAA,MAAAA;;UAnHDG,oBAAAA,CAqHKa;EAAM,EAAA,EAAA,MAAA;EAAA,QAKXC,EAAAA,MAAAA;EAA8B,IAU9BC,EAAAA,WAAAA,GAAAA,YAAqB;EAAA,qBAad,EA7IQpB,eA6IR,CAAA,uBAAA,CAAA;EAAA,qBAAA,EA5IQA,eA4IR,CAAA,uBAAA,CAAA;gBAGHG,EA9IIH,eA8IJG,CAAAA,gBAAAA,CAAAA;;UA5IJG,kBAAAA,CA6I+BH;QAARmB,MAAAA;UACVhB,EAAAA,MAAAA;WAA6BH,CAAAA,EAAAA,MAAAA;OAARmB,CAAAA,EAAAA,MAAAA;yBAGdtB,CAAAA,EA5IFA,eA4IEA;;;;;mBAGWO,CAAAA,EAAAA,MAAAA;;UAxI/BA,iBAAAA,CA0IKF;QAF8CiB,MAAAA;UAIFd,CAAAA,EAAAA,MAAAA;WAA4BJ,CAAAA,EAAAA,MAAAA;;WAC3CC,CAAAA,EAAAA,OAAAA;iBAARiB,CAAAA,EAvIhBtB,eAuIgBsB;;UArI1Bd,iBAAAA,CAsIkFH;OAARiB,EAAAA,MAAAA;WAEpBb,CAAAA,EAAAA,MAAAA,GAAAA,IAAAA;;WAEIC,CAAAA,EAAAA,OAAAA;;;;;;;UA/H1DD,gBAAAA,CAoJJa;QAM+EV,MAAAA;UAARU,EAAAA,MAAAA;;WAMYA,EAAAA,OAAAA;;;;;UAvJ/EZ,oBAAAA,CA2J2EY;QAENA,MAAAA;UAEXP,EAAAA,MAAAA;;WAQtDA,EAAAA,MAAAA;;;;;;UA7JJJ,gBAAAA,CAyKkDW;;WAIHA,EAAAA,MAAAA;EAAO;;;;;;;;;;;;UA9JtDV,mBAAAA;;;;;;;;UAQAC,kBAAAA;;eAEKd;;;;;UAKLe,iBAAAA;;gBAEMf;;;;;;UAMNgB,oBAAAA;;;;;;;;;;;;;;;;;UAiBAC,mBAAAA;UACAC;WACCf;aACED;eACEiB;;;;;UAKLC,8BAAAA;;;;;;;;;;UAUAC,qBAAAA;;;;;;;;;;;;;UAaAC,OAAAA;;;MAGJC,QAAQnB;iCACmBmB,QAAQnB;uBAClBG,qBAAqBgB,QAAQnB;;;8BAGtBH;MACxBsB,QAAQnB;mCACqBmB,QAAQlB;yCACFG,oBAAoBe;YACjDlB;eACGC;;2DAE4CG,oBAAoBc,QAAQlB;oCACnDkB,QAAQjB;kEACsBL,kBAAkBsB,QAAQjB;;wDAEpCiB,QAAQb;;4DAEJa,QAAQZ;;;;;6CAKvBU,wBAAwBE;;;;;;;;oDAQjBA,QAAQX;;;;;;;;MAQtDW,QAAQX;;;;;;6EAM+DW,QAAQV;;;;;;yFAMIU,QAAQV;;0DAEvCU,QAAQT;;iEAEDC,oBAAoBQ,QAAQT;;+EAEdS;;4DAEnBA,QAAQP;;;;;;;;MAQ9DO,QAAQP;;2EAE6DO;;;;;iFAKMN,sBAAsBM,QAAQH;;;;;4DAKnDG;;wDAEJA;;yDAECA"}
1
+ {"version":3,"file":"neon-api.d.ts","names":["BucketAccessLevel","ComputeSettings","FunctionRuntime","NeonProjectSnapshot","NeonBranchSnapshot","NeonEndpointSnapshot","CreateProjectInput","CreateBranchInput","UpdateBranchInput","NeonRoleSnapshot","NeonDatabaseSnapshot","NeonAuthSnapshot","NeonDataApiSnapshot","NeonBucketSnapshot","CreateBucketInput","NeonFunctionSnapshot","DeployFunctionInput","Uint8Array","Record","NeonFunctionDeploymentSnapshot","GetConnectionUriInput","NeonApi","Promise"],"sources":["../../../../../config/dist/lib/neon-api.d.ts"],"sourcesContent":["import { BucketAccessLevel, ComputeSettings, FunctionRuntime } from \"./types.js\";\n\n//#region src/lib/neon-api.d.ts\n\n/**\n * Snapshot of a Neon project field set we care about. Maps onto a subset of the upstream\n * `@neondatabase/api-client` `Project` type. We do **not** widen this to the full upstream\n * shape — keeping the surface narrow makes the in-memory fake practical to maintain.\n */\ninterface NeonProjectSnapshot {\n id: string;\n name: string;\n regionId: string;\n pgVersion: number;\n orgId?: string;\n defaultEndpointSettings?: ComputeSettings;\n}\ninterface NeonBranchSnapshot {\n id: string;\n name: string;\n parentId?: string;\n isDefault: boolean;\n /** Whether the branch is marked protected on Neon. */\n protected: boolean;\n expiresAt?: string;\n}\ninterface NeonEndpointSnapshot {\n id: string;\n branchId: string;\n type: \"read_only\" | \"read_write\";\n autoscalingLimitMinCu: ComputeSettings[\"autoscalingLimitMinCu\"];\n autoscalingLimitMaxCu: ComputeSettings[\"autoscalingLimitMaxCu\"];\n suspendTimeout: ComputeSettings[\"suspendTimeout\"];\n}\ninterface CreateProjectInput {\n name: string;\n regionId: string;\n pgVersion?: number;\n orgId?: string;\n defaultEndpointSettings?: ComputeSettings;\n /**\n * Optional name for the project's auto-created default branch. When omitted, Neon\n * uses its own default (`main`).\n */\n defaultBranchName?: string;\n}\ninterface CreateBranchInput {\n name: string;\n parentId?: string;\n expiresAt?: string;\n /** When `true`, the branch is created with the `protected` flag set on Neon. */\n protected?: boolean;\n computeSettings?: ComputeSettings;\n}\ninterface UpdateBranchInput {\n name?: string;\n expiresAt?: string | null;\n /** When set, toggles the branch's `protected` flag on Neon. */\n protected?: boolean;\n}\n/**\n * A role on a Neon branch (e.g. `neondb_owner`). Passwords are never returned by\n * {@link NeonApi.listBranchRoles}; use {@link NeonApi.getConnectionUri} to fetch a URI\n * with the role's password baked in.\n */\ninterface NeonRoleSnapshot {\n name: string;\n branchId: string;\n /** Whether the role is system-protected (cannot be deleted). */\n protected: boolean;\n}\n/**\n * A database on a Neon branch (e.g. `neondb`).\n */\ninterface NeonDatabaseSnapshot {\n name: string;\n branchId: string;\n /** The role that owns the database (one role can own multiple databases). */\n ownerName: string;\n}\n/**\n * Bits of a Neon Auth integration. The key fields are optional because the Neon API only\n * includes them on create / rotate responses; `GET /auth` returns the public fields.\n */\ninterface NeonAuthSnapshot {\n /** The Neon Auth project id (`auth_provider_project_id` on the Neon API). */\n projectId: string;\n /** Public client key (`pub_client_key`), only present on create / rotate responses. */\n publishableClientKey?: string;\n /** Secret server key (`secret_server_key`), only present on create / rotate responses. */\n secretServerKey?: string;\n /** JWKS URL for verifying tokens issued by Neon Auth. */\n jwksUrl: string;\n /** Optional base URL of the Neon Auth deployment. */\n baseUrl?: string;\n}\n/**\n * Public, fetchable bits of a Neon Data API integration on a specific branch.\n */\ninterface NeonDataApiSnapshot {\n /** REST endpoint URL. */\n url: string;\n}\n/**\n * A branchable object-storage bucket (Preview). Backed by the Neon Platform\n * branchable-storage service.\n */\ninterface NeonBucketSnapshot {\n name: string;\n accessLevel: BucketAccessLevel;\n}\n/**\n * Input for creating a bucket on a branch.\n */\ninterface CreateBucketInput {\n name: string;\n accessLevel?: BucketAccessLevel;\n}\n/**\n * A Neon Function on a branch (Preview). Mirrors the subset of the Functions API we model:\n * the immutable `slug`, the display `name`, and the active deployment id when one exists.\n */\ninterface NeonFunctionSnapshot {\n /** Opaque, stable function identifier. */\n id: string;\n /** Branch-unique slug (the invocation path segment). Immutable. */\n slug: string;\n /** Free-form display name. */\n name: string;\n /** URL at which the function is invoked. */\n invocationUrl: string;\n /** Id (platform version number) of the active deployment, when any code is deployed. */\n activeDeploymentId?: number;\n}\n/**\n * Input for deploying code to a function. `bundle` is the already-built ZIP archive of the\n * function source — building it (esbuild + zip) is an imperative step performed by the\n * caller, not by the {@link NeonApi} adapter.\n */\ninterface DeployFunctionInput {\n bundle: Uint8Array;\n runtime: FunctionRuntime;\n environment: Record<string, string>;\n}\n/**\n * A function deployment (Preview).\n */\ninterface NeonFunctionDeploymentSnapshot {\n /** The deployment id (monotonic per function). */\n id: number;\n status: \"pending\" | \"building\" | \"completed\" | \"failed\";\n}\n/**\n * Parameters accepted by {@link NeonApi.getConnectionUri}. `branchId` and `endpointId`\n * are optional — when omitted, the API uses the project's default branch and that\n * branch's read-write endpoint, respectively.\n */\ninterface GetConnectionUriInput {\n branchId?: string;\n endpointId?: string;\n databaseName: string;\n roleName: string;\n /** When `true`, returns the pooled (PgBouncer) URI instead of the direct URI. */\n pooled?: boolean;\n}\n/**\n * Narrow façade over the Neon management API. `pullConfig`, `pushConfig`, and `fetchEnv`\n * depend on this interface — *not* on `@neondatabase/api-client` directly — which lets us\n * inject a real in-memory fake during tests without resorting to module mocks.\n */\ninterface NeonApi {\n listProjects(filter: {\n orgId?: string;\n }): Promise<NeonProjectSnapshot[]>;\n getProject(projectId: string): Promise<NeonProjectSnapshot>;\n createProject(input: CreateProjectInput): Promise<NeonProjectSnapshot>;\n updateProject(projectId: string, input: {\n name?: string;\n defaultEndpointSettings?: ComputeSettings;\n }): Promise<NeonProjectSnapshot>;\n listBranches(projectId: string): Promise<NeonBranchSnapshot[]>;\n createBranch(projectId: string, input: CreateBranchInput): Promise<{\n branch: NeonBranchSnapshot;\n endpoints: NeonEndpointSnapshot[];\n }>;\n updateBranch(projectId: string, branchId: string, input: UpdateBranchInput): Promise<NeonBranchSnapshot>;\n listEndpoints(projectId: string): Promise<NeonEndpointSnapshot[]>;\n updateEndpoint(projectId: string, endpointId: string, settings: ComputeSettings): Promise<NeonEndpointSnapshot>;\n /** List roles on a branch. Used by {@link fetchEnv} to auto-pick the role when only one exists. */\n listBranchRoles(projectId: string, branchId: string): Promise<NeonRoleSnapshot[]>;\n /** List databases on a branch. Used by {@link fetchEnv} to auto-pick the database when only one exists. */\n listBranchDatabases(projectId: string, branchId: string): Promise<NeonDatabaseSnapshot[]>;\n /**\n * Fetch a Postgres connection URI for the given role + database on a branch.\n * Returns the same string the Neon Console shows under \"Connection Details\".\n */\n getConnectionUri(projectId: string, input: GetConnectionUriInput): Promise<{\n uri: string;\n }>;\n /**\n * Fetch the Neon Auth integration attached to a specific branch. Returns `null` when\n * no integration is enabled — used by `fetchEnv` to decide whether the `env.auth`\n * namespace can be populated.\n */\n getNeonAuth(projectId: string, branchId: string): Promise<NeonAuthSnapshot | null>;\n /**\n * Enable the Neon Auth integration on a specific branch. Idempotent: if an integration\n * is already enabled, the existing snapshot is returned unchanged. Used by\n * `pushConfig` and `branch` to honour branch policy `auth: {}` / `auth.enabled: true`.\n */\n enableNeonAuth(projectId: string, branchId: string, input?: {\n databaseName?: string;\n }): Promise<NeonAuthSnapshot>;\n /**\n * Fetch the Neon Data API integration attached to a specific branch + database.\n * Returns `null` when no integration is enabled — used by `fetchEnv` to decide\n * whether the `env.dataApi` namespace can be populated.\n */\n getNeonDataApi(projectId: string, branchId: string, databaseName: string): Promise<NeonDataApiSnapshot | null>;\n /**\n * Enable the Neon Data API integration on a specific branch + database. Idempotent:\n * if an integration is already enabled, the existing snapshot is returned unchanged.\n * Used by `pushConfig` and `branch` to honour branch policy `dataApi: {}` / `dataApi.enabled: true`.\n */\n enableProjectBranchDataApi(projectId: string, branchId: string, databaseName: string): Promise<NeonDataApiSnapshot>;\n /** List branchable object-storage buckets visible on a branch. */\n listBranchBuckets(projectId: string, branchId: string): Promise<NeonBucketSnapshot[]>;\n /** Create a bucket on a branch. Used by `pushConfig` to honour `preview.buckets`. */\n createBranchBucket(projectId: string, branchId: string, input: CreateBucketInput): Promise<NeonBucketSnapshot>;\n /** Delete a bucket from a branch. */\n deleteBranchBucket(projectId: string, branchId: string, bucketName: string): Promise<void>;\n /** List functions on a branch. */\n listBranchFunctions(projectId: string, branchId: string): Promise<NeonFunctionSnapshot[]>;\n /**\n * Create a function on a branch. The function has no deployment until code is deployed\n * to it with {@link deployBranchFunction}.\n */\n createBranchFunction(projectId: string, branchId: string, input: {\n slug: string;\n name: string;\n }): Promise<NeonFunctionSnapshot>;\n /** Delete a function (by slug) from a branch. */\n deleteBranchFunction(projectId: string, branchId: string, slug: string): Promise<void>;\n /**\n * Deploy a built bundle to a function. The newest deployment becomes active. The\n * `bundle` is built (esbuild + zip) by the caller and passed in as bytes.\n */\n deployBranchFunction(projectId: string, branchId: string, slug: string, input: DeployFunctionInput): Promise<NeonFunctionDeploymentSnapshot>;\n /**\n * Whether the AI Gateway is enabled on a branch. Toggle-style, like Neon Auth / Data\n * API: used by both `fetchEnv` (to decide visibility) and `pushConfig` (to diff intent).\n */\n getAiGatewayEnabled(projectId: string, branchId: string): Promise<boolean>;\n /** Enable the AI Gateway on a branch. Idempotent. */\n enableAiGateway(projectId: string, branchId: string): Promise<void>;\n /** Disable the AI Gateway on a branch. Idempotent. */\n disableAiGateway(projectId: string, branchId: string): Promise<void>;\n}\n//#endregion\nexport { CreateBranchInput, CreateBucketInput, CreateProjectInput, DeployFunctionInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBucketSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, UpdateBranchInput };\n//# sourceMappingURL=neon-api.d.ts.map"],"mappings":";;;;;AAe2C;AAEf;;;;UARlBG,mBAAAA,CAuBQF;EAAe,EAAA,EAAA,MAAA;EAAA,IAEvBK,EAAAA,MAAAA;EAKiC,QAOjCC,EAAAA,MAAAA;EAMyB,SAEzBC,EAAAA,MAAAA;EAAiB,KAWjBC,CAAAA,EAAAA,MAAAA;EAAgB,uBAShBC,CAAAA,EA3DkBT,eA2DE;AAAA;AAUJ,UAnEhBG,kBAAAA,CAkFmB;EAAA,EAQnBS,EAAAA,MAAAA;EAEsB,IAKtBC,EAAAA,MAAAA;EAEuB,QAMvBC,CAAAA,EAAAA,MAAAA;EAAoB,SAiBpBC,EAAAA,OAAAA;EAAmB;WACnBC,EAAAA,OAAAA;WACCf,CAAAA,EAAAA,MAAAA;;AACU,UApHXG,oBAAAA,CAoHW;EAAA,EAKXc,EAAAA,MAAAA;EAA8B,QAU9BC,EAAAA,MAAAA;EAAqB,IAarBC,EAAAA,WAAO,GAAA,YAAA;EAAA,qBAAA,EA5IQpB,eA4IR,CAAA,uBAAA,CAAA;uBAGHE,EA9IWF,eA8IXE,CAAAA,uBAAAA,CAAAA;gBAARmB,EA7IYrB,eA6IZqB,CAAAA,gBAAAA,CAAAA;;UA3IIhB,kBAAAA,CA4IuBgB;QACVhB,MAAAA;UAA6BH,EAAAA,MAAAA;WAARmB,CAAAA,EAAAA,MAAAA;OAGdrB,CAAAA,EAAAA,MAAAA;yBAChBE,CAAAA,EA5IcF,eA4IdE;;;;;mBAGFC,CAAAA,EAAAA,MAAAA;;UAxIFG,iBAAAA,CAuImDe;QAIFd,MAAAA;UAA4BJ,CAAAA,EAAAA,MAAAA;WAARkB,CAAAA,EAAAA,MAAAA;;WAC3CA,CAAAA,EAAAA,OAAAA;iBAC8BrB,CAAAA,EAvI9CA,eAuI8CA;;UArIxDO,iBAAAA,CAqI0Ec;OAEpBb,EAAAA,MAAAA;WAARa,CAAAA,EAAAA,MAAAA,GAAAA,IAAAA;;WAEIA,CAAAA,EAAAA,OAAAA;;;;;;;UA9HlDb,gBAAAA,CAyJ2EG;QAARU,MAAAA;UAMoBV,EAAAA,MAAAA;;WAE/BC,EAAAA,OAAAA;;;;;UAxJxDH,oBAAAA,CA4JqEY;QAEXP,MAAAA;UAARO,EAAAA,MAAAA;;WAQtDA,EAAAA,MAAAA;;;;;;UA5JIX,gBAAAA,CA0K8CW;;EAEQ,SAAA,EAAA,MAAA;;;;;;;;;;;;;UA7JtDV,mBAAAA;;;;;;;;UAQAC,kBAAAA;;eAEKb;;;;;UAKLc,iBAAAA;;gBAEMd;;;;;;UAMNe,oBAAAA;;;;;;;;;;;;;;;;;UAiBAC,mBAAAA;UACAC;WACCf;eACIgB;;;;;UAKLC,8BAAAA;;;;;;;;;;UAUAC,qBAAAA;;;;;;;;;;;;;UAaAC,OAAAA;;;MAGJC,QAAQnB;iCACmBmB,QAAQnB;uBAClBG,qBAAqBgB,QAAQnB;;;8BAGtBF;MACxBqB,QAAQnB;mCACqBmB,QAAQlB;yCACFG,oBAAoBe;YACjDlB;eACGC;;2DAE4CG,oBAAoBc,QAAQlB;oCACnDkB,QAAQjB;kEACsBJ,kBAAkBqB,QAAQjB;;wDAEpCiB,QAAQb;;4DAEJa,QAAQZ;;;;;6CAKvBU,wBAAwBE;;;;;;;;oDAQjBA,QAAQX;;;;;;;;MAQtDW,QAAQX;;;;;;6EAM+DW,QAAQV;;;;;;yFAMIU,QAAQV;;0DAEvCU,QAAQT;;iEAEDC,oBAAoBQ,QAAQT;;+EAEdS;;4DAEnBA,QAAQP;;;;;;;;MAQ9DO,QAAQP;;2EAE6DO;;;;;iFAKMN,sBAAsBM,QAAQH;;;;;4DAKnDG;;wDAEJA;;yDAECA"}
@@ -5,6 +5,43 @@
5
5
  * Most plans support 0.25, 0.5, 1, 2, 4, 8. Higher values may be available on Business plans.
6
6
  */
7
7
  type ComputeUnit = 0.25 | 0.5 | 1 | 2 | 4 | 8;
8
+ /** Time units accepted in a {@link DurationString}: seconds, minutes, hours, days, weeks. */
9
+ type DurationUnit = "s" | "m" | "h" | "d" | "w";
10
+ /**
11
+ * A Neon duration string: a positive integer **followed by a unit** — `s` (seconds),
12
+ * `m` (minutes), `h` (hours), `d` (days), or `w` (weeks). Used by
13
+ * {@link ComputeSettings.suspendTimeout} and {@link BranchTuning.ttl}.
14
+ *
15
+ * A **unit is required**: a bare numeric string like `"7"` is rejected at the type level. To
16
+ * express a raw number of seconds, pass a `number` (`300`) — not a string (`"300"`). This
17
+ * removes the old ambiguity where `"7"` silently meant 7 *seconds* instead of, say, `"7d"`.
18
+ *
19
+ * @example "5m" // 5 minutes
20
+ * @example "1h" // 1 hour
21
+ * @example "7d" // 7 days
22
+ */
23
+ type DurationString = `${number}${DurationUnit}`;
24
+ /**
25
+ * Autocomplete suggestions for {@link ComputeSettings.suspendTimeout}. Every value sits inside
26
+ * the Neon API's allowed scale-to-zero band: **60s–604800s** (1 minute – 1 week). This is *not*
27
+ * a closed set — the field also accepts any other {@link DurationString} or a `number` of
28
+ * seconds; out-of-range values type-check but are rejected at apply time.
29
+ */
30
+ type SuspendTimeoutSuggestion = "1m" | "5m" | "15m" | "30m" | "1h" | "6h" | "12h" | "1d" | "7d";
31
+ /**
32
+ * Autocomplete suggestions for {@link BranchTuning.ttl}. Every value sits within the Neon API's
33
+ * branch-expiration limit (**max 30 days** from creation; the Console's own presets are 1h / 1d
34
+ * / 7d). This is *not* a closed set — the field also accepts any other {@link DurationString} or
35
+ * a `number` of seconds; values over 30 days are rejected at apply time.
36
+ */
37
+ type TtlSuggestion = "1h" | "6h" | "12h" | "1d" | "3d" | "7d" | "14d" | "30d";
38
+ /**
39
+ * Compose a field's duration type: its curated autocomplete `Suggestions` plus the open
40
+ * `DurationString` template (so any `<integer><unit>` string still type-checks) and a `number`
41
+ * of seconds. Intersecting the template arm with `NonNullable<unknown>` stops TypeScript from
42
+ * collapsing the literal suggestions into the template, which is what preserves the autocomplete.
43
+ */
44
+ type DurationField<Suggestions extends DurationString> = Suggestions | (DurationString & NonNullable<unknown>) | number;
8
45
  /**
9
46
  * Compute settings applied to the read/write endpoint of a branch.
10
47
  *
@@ -26,19 +63,26 @@ interface ComputeSettings {
26
63
  */
27
64
  autoscalingLimitMaxCu?: ComputeUnit;
28
65
  /**
29
- * How long to wait before suspending an idle compute.
66
+ * How long an idle compute waits before suspending (Neon's scale-to-zero). Accepts a
67
+ * {@link DurationString} (autocompletes common values), a number of seconds, or `false`.
30
68
  *
31
69
  * - `false` — never suspend (always-on compute)
32
- * - `"5m"` duration string (supports "30s", "5m", "1h", "7d", etc)
33
- * - `300` custom timeout in seconds (60-604800)
34
- * - `undefined` use Neon platform default (currently 300s / 5 minutes)
70
+ * - {@link DurationString} — e.g. `"5m"`; autocompletes the in-range values `"1m"`, `"5m"`,
71
+ * `"15m"`, `"30m"`, `"1h"`, `"6h"`, `"12h"`, `"1d"`, `"7d"`, and accepts any other
72
+ * `<integer><unit>` (units: `s`, `m`, `h`, `d`, `w`). A **unit is required** for raw
73
+ * seconds pass a `number`, not a string.
74
+ * - `number` — custom timeout in **seconds**, must be in `60`–`604800` (1 minute to 1 week)
75
+ * - `undefined` — use the Neon platform default (currently 300s / 5 minutes)
35
76
  *
36
- * @example false // never suspend
37
- * @example "5m" // 5 minutes
38
- * @example "1h" // 1 hour
39
- * @example 300 // 5 minutes in seconds
77
+ * Whichever form you use, the resolved timeout must fall in `60`–`604800` seconds (the Neon
78
+ * API limit); the suggestions are all within that band, anything else is checked at apply.
79
+ *
80
+ * @example false // never suspend (always-on)
81
+ * @example "5m" // suspend after 5 minutes idle
82
+ * @example "1h" // suspend after 1 hour idle
83
+ * @example 300 // 5 minutes, expressed in seconds
40
84
  */
41
- suspendTimeout?: false | "5m" | "1h" | string | number;
85
+ suspendTimeout?: false | DurationField<SuspendTimeoutSuggestion>;
42
86
  }
43
87
  /**
44
88
  * Read-only descriptor of the branch a {@link Config} policy is being evaluated for — the
@@ -62,10 +106,26 @@ interface BranchTarget {
62
106
  /** Current expiration timestamp from Neon, when set. */
63
107
  expiresAt?: string;
64
108
  }
109
+ /**
110
+ * Object form of a branch-scoped service toggle. `{}` or `{ enabled: true }` enables it;
111
+ * `{ enabled: false }` opts out. Used as the object half of {@link ServiceToggleInput}.
112
+ */
65
113
  interface ServiceToggle {
66
114
  /** Defaults to `true` when the service namespace is present. Set `false` to opt out. */
67
115
  enabled?: boolean;
68
116
  }
117
+ /**
118
+ * How a branch-scoped service (Neon Auth, Data API, AI Gateway) is toggled in a policy.
119
+ *
120
+ * - `true` / `{}` / `{ enabled: true }` — enabled.
121
+ * - `false` / `{ enabled: false }` — disabled.
122
+ * - omitted (`undefined`) — not part of the policy at all.
123
+ *
124
+ * These toggles are **static** (they live in the top-level `defineConfig({ … })` object,
125
+ * not in the per-branch `branch` closure) so the secret set they imply can be derived at
126
+ * the type level — that's what makes `NeonEnv<typeof config>` exact.
127
+ */
128
+ type ServiceToggleInput = boolean | ServiceToggle;
69
129
  interface PostgresConfig {
70
130
  computeSettings?: ComputeSettings;
71
131
  }
@@ -75,11 +135,6 @@ interface PostgresConfig {
75
135
  * non-breaking, type-checked change.
76
136
  */
77
137
  type FunctionRuntime = "nodejs24";
78
- /**
79
- * Memory sizes (MiB) accepted by the Neon Functions deploy API. Mirrors the
80
- * `memory_mib` enum in the spec.
81
- */
82
- type FunctionMemoryMib = 256 | 512 | 1024 | 2048 | 4096 | 8192;
83
138
  /**
84
139
  * Local-development settings for a function, used by `neon dev` when it serves every
85
140
  * function declared in `neon.ts` (i.e. invoked with no `--source`). Never affects deploy.
@@ -105,20 +160,21 @@ interface FunctionDevConfig {
105
160
  portless?: boolean;
106
161
  }
107
162
  /**
108
- * A single Neon Function deployed to a branch (Preview feature).
163
+ * Static definition of a Neon Function (Preview feature). Declares that the function
164
+ * **exists** on every branch; its branch-unique slug is the **record key** in
165
+ * {@link PreviewInput.functions} (not a field here), so slugs are statically enumerable,
166
+ * cannot duplicate, and the `branch` closure can only tune slugs that are declared here.
109
167
  *
110
168
  * A function is invoked like a Cloudflare/Vercel handler — its source module
111
169
  * `export default { fetch }` or `export async function handler(req): Response`. The
112
- * `source` path is bundled (esbuild) and uploaded as a deployment; the newest
113
- * deployment becomes active.
170
+ * `source` path is bundled (esbuild) and uploaded as a deployment; the newest deployment
171
+ * becomes active.
172
+ *
173
+ * Runtime tuning is **not** here — it varies per branch and lives in the `branch` closure
174
+ * (see {@link FunctionTuning}). Memory is fixed by the platform policy for now and is not
175
+ * user-configurable.
114
176
  */
115
- interface FunctionConfig {
116
- /**
117
- * Branch-unique slug used as the path segment in the function's invocation URL.
118
- * Immutable once created. 1–20 lowercase letters and digits: `^[a-z0-9]{1,20}$`.
119
- * @example "hellofn"
120
- */
121
- slug: string;
177
+ interface FunctionDef {
122
178
  /** Free-form display name. @example "Hello World" */
123
179
  name: string;
124
180
  /**
@@ -133,16 +189,16 @@ interface FunctionConfig {
133
189
  */
134
190
  source: string;
135
191
  /**
136
- * Environment variables injected into the deployed function. Every value must be a
137
- * defined string a `process.env.X` that is `undefined` (unset) errors at validation
138
- * time rather than silently shipping `undefined`.
139
- * @example { RESEND_API_KEY: process.env.RESEND_API_KEY }
192
+ * Environment variables injected into the deployed function, keyed by the var name the
193
+ * function reads at runtime. The **keys** are static (preserved at the type level so
194
+ * `parseEnv(config, "<slug>").function.<key>` is typed); the **values** are arbitrary
195
+ * strings evaluated when `neon.ts` is loaded (typically `process.env.X`) and uploaded
196
+ * at `config apply`. Every value must be a defined string — a `process.env.X` that is
197
+ * `undefined` (unset) errors at validation time rather than silently shipping
198
+ * `undefined`.
199
+ * @example { resendApiKey: process.env.RESEND_API_KEY ?? "" }
140
200
  */
141
201
  env?: Record<string, string>;
142
- /** Runtime to execute the function with. Defaults to `"nodejs24"`. */
143
- runtime?: FunctionRuntime;
144
- /** Memory allotted to each invocation, in MiB. Defaults to `512`. */
145
- memoryMib?: FunctionMemoryMib;
146
202
  /**
147
203
  * Local-development settings used by `neon dev` when serving every function from
148
204
  * `neon.ts`. Ignored at deploy time. See {@link FunctionDevConfig}.
@@ -152,11 +208,11 @@ interface FunctionConfig {
152
208
  /** Anonymous-access level for a branchable object-storage bucket. */
153
209
  type BucketAccessLevel = "private" | "public_read";
154
210
  /**
155
- * A branchable object-storage bucket on a branch (Preview feature).
211
+ * Static definition of a branchable object-storage bucket (Preview feature). The bucket's
212
+ * name is the **record key** in {@link PreviewInput.buckets}, so names are statically
213
+ * enumerable and cannot duplicate.
156
214
  */
157
- interface BucketConfig {
158
- /** Bucket name, unique within a branch. 1–255 chars. */
159
- name: string;
215
+ interface BucketDef {
160
216
  /**
161
217
  * Anonymous access level. `private` (default) requires authenticated reads/writes;
162
218
  * `public_read` allows anonymous GetObject/HeadObject.
@@ -164,50 +220,110 @@ interface BucketConfig {
164
220
  access?: BucketAccessLevel;
165
221
  }
166
222
  /**
167
- * Branch-scoped Preview features. Grouped under `preview` to signal they are backed by
168
- * Neon `x-stability-level: beta` endpoints and may change before GA.
223
+ * Static, branch-scoped **Preview** features. Grouped under `preview` to signal they are
224
+ * backed by Neon `x-stability-level: beta` endpoints and may change before GA. Everything
225
+ * here is existential (it determines what exists on the branch); per-branch tuning lives in
226
+ * the `branch` closure.
169
227
  */
170
- interface PreviewConfig {
171
- /** Functions to deploy on the branch. */
172
- functions?: FunctionConfig[];
173
- /** Object-storage buckets to create on the branch. */
174
- buckets?: BucketConfig[];
228
+ interface PreviewInput {
175
229
  /** Enable/disable the AI Gateway on the branch (toggle, like auth / dataApi). */
176
- aiGateway?: ServiceToggle;
230
+ aiGateway?: ServiceToggleInput;
231
+ /** Functions to deploy, keyed by branch-unique slug (`^[a-z0-9]{1,20}$`). */
232
+ functions?: Record<string, FunctionDef>;
233
+ /** Object-storage buckets to create, keyed by bucket name. */
234
+ buckets?: Record<string, BucketDef>;
177
235
  }
178
- interface BranchConfigBase {
236
+ /**
237
+ * Per-branch deploy tuning for a single function. Returned (per slug) by the `branch`
238
+ * closure. Deliberately **cannot** change the function's existence, source, name, env
239
+ * **keys**, or memory — only runtime selection is currently configurable — so the static
240
+ * secret/function set stays sound.
241
+ */
242
+ interface FunctionTuning {
243
+ /** Runtime to execute the function with. Defaults to `"nodejs24"`. */
244
+ runtime?: FunctionRuntime;
245
+ }
246
+ /**
247
+ * Per-branch tuning of Preview features. Only existing function slugs (those declared in
248
+ * the static {@link PreviewInput.functions}) may be tuned — `Slug` is constrained to the
249
+ * declared keys by {@link BranchTuningFn}.
250
+ */
251
+ interface PreviewTuning<Slug extends string = string> {
252
+ functions?: Partial<Record<Slug, FunctionTuning>>;
253
+ }
254
+ /**
255
+ * The per-branch tuning object returned by the `branch` closure. It can adjust branch
256
+ * lifecycle (`parent`, `ttl`, `protected`), Postgres compute settings, and per-function
257
+ * deploy tuning — but **cannot** add/remove services or functions. That guarantee is what
258
+ * keeps the static secret set (and therefore `NeonEnv`) exact.
259
+ */
260
+ interface BranchTuning<Slug extends string = string> {
179
261
  /** Parent branch name used when creating a new branch. Not a Postgres setting. */
180
262
  parent?: string;
181
- /** Time-to-live applied when creating a new branch, or reconciled on existing branches. */
182
- ttl?: string | number;
263
+ /**
264
+ * Branch time-to-live: how long after creation the branch should auto-expire. Applied
265
+ * when creating a new branch and reconciled on existing branches (when `updateExisting`
266
+ * is set). Accepts a {@link DurationString} (autocompletes common values) or a number of
267
+ * seconds. Omit to keep the branch indefinitely.
268
+ *
269
+ * - {@link DurationString} — e.g. `"7d"`; autocompletes `"1h"`, `"6h"`, `"12h"`, `"1d"`,
270
+ * `"3d"`, `"7d"`, `"14d"`, `"30d"`, and accepts any other `<integer><unit>` (units: `s`,
271
+ * `m`, `h`, `d`, `w` — e.g. `"12h"`, `"2w"`). A **unit is required** — `"7"` is rejected;
272
+ * for raw seconds pass a `number`.
273
+ * - `number` — custom TTL in **seconds** (e.g. `3600`)
274
+ * - `undefined` — no expiry; the branch persists until explicitly deleted
275
+ *
276
+ * The Neon API caps branch expiration at **30 days** from creation, so the resolved TTL must
277
+ * be `> 0` and `<= 30d`; the suggestions stay within that limit and anything longer is
278
+ * rejected at apply.
279
+ *
280
+ * @example "1d" // ephemeral preview branch: expires a day after creation
281
+ * @example "7d" // one-week TTL
282
+ * @example "30d" // the maximum the API allows
283
+ * @example 3600 // 1 hour, expressed in seconds
284
+ */
285
+ ttl?: DurationField<TtlSuggestion>;
183
286
  /** Whether the selected branch should be protected. Undefined means "leave as-is". */
184
287
  protected?: boolean;
185
288
  postgres?: PostgresConfig;
186
- /**
187
- * Branch-scoped Preview features (functions, object-storage buckets, AI Gateway).
188
- * Backed by Neon `x-stability-level: beta` endpoints see {@link PreviewConfig}.
189
- */
190
- preview?: PreviewConfig;
289
+ preview?: PreviewTuning<Slug>;
290
+ }
291
+ /** Extract the declared function slugs from a {@link PreviewInput} for closure typing. */
292
+ type FunctionSlugsOf<Preview extends PreviewInput | undefined> = Preview extends {
293
+ functions: infer F;
294
+ } ? Extract<keyof F, string> : string;
295
+ /**
296
+ * Signature of the `branch` closure. Generic over the static {@link PreviewInput} so the
297
+ * `preview.functions` keys it may tune are constrained to the slugs actually declared.
298
+ */
299
+ type BranchTuningFn<Preview extends PreviewInput | undefined = PreviewInput | undefined> = (branch: BranchTarget) => BranchTuning<FunctionSlugsOf<Preview>>;
300
+ /**
301
+ * A validated Neon branch policy — the value `defineConfig({ … })` returns and `neon.ts`
302
+ * default-exports.
303
+ *
304
+ * Split into a **static** existential set (top-level `auth` / `dataApi` GA toggles plus the
305
+ * beta `preview` block) and a **dynamic** per-branch `branch` closure for tuning. The
306
+ * static half is what makes the secret set — and therefore `NeonEnv<typeof config>` and
307
+ * `parseEnv` — exact; the closure can tune but never change what exists.
308
+ *
309
+ * Generic over the three static fields so the type system can read the exact toggle/slug
310
+ * literals; the defaults make the bare `Config` a usable "any policy" type for runtime
311
+ * function signatures.
312
+ */
313
+ interface Config<Auth extends ServiceToggleInput | undefined = ServiceToggleInput | undefined, DataApi extends ServiceToggleInput | undefined = ServiceToggleInput | undefined, Preview extends PreviewInput | undefined = PreviewInput | undefined> {
314
+ /** Neon Auth integration toggle (GA). Static — drives `NeonEnv.auth`. */
315
+ auth?: Auth;
316
+ /** Neon Data API integration toggle (GA). Static — drives `NeonEnv.dataApi`. */
317
+ dataApi?: DataApi;
318
+ /** Beta (Preview) feature set: AI Gateway, functions, buckets. Static. */
319
+ preview?: Preview;
320
+ /** Per-branch tuning closure. Cannot change the static existential set. */
321
+ branch?: BranchTuningFn<Preview>;
191
322
  }
192
- type BranchServiceConfig = {
193
- auth?: never;
194
- dataApi?: never;
195
- } | {
196
- auth: ServiceToggle;
197
- dataApi?: never;
198
- } | {
199
- auth?: never;
200
- dataApi: ServiceToggle;
201
- } | {
202
- auth: ServiceToggle;
203
- dataApi: ServiceToggle;
204
- };
205
- type BranchConfig = BranchConfigBase & BranchServiceConfig;
206
- type Config = (branch: BranchTarget) => BranchConfig;
207
- /**
208
- * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` and
209
- * `memoryMib` so downstream diff/apply never has to re-derive them.
323
+ /**
324
+ * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` so
325
+ * downstream diff/apply never has to re-derive it.
210
326
  */
211
327
  //#endregion
212
- export { BranchConfig, BranchTarget, BucketAccessLevel, BucketConfig, ComputeSettings, Config, FunctionConfig, FunctionDevConfig, FunctionMemoryMib, FunctionRuntime, PostgresConfig, PreviewConfig, ServiceToggle };
328
+ export { BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, DurationString, DurationUnit, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, ServiceToggle, ServiceToggleInput };
213
329
  //# sourceMappingURL=types.d.ts.map