@neondatabase/config-runtime 0.4.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@ import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
2
2
  import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
3
3
  import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
4
4
  import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
5
- import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, loadConfigFromFile } from "./v1.js";
6
- export { AppliedChange, ApplyOptions, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, ErrorCode, FunctionBundler, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, loadConfigFromFile, plan, pullConfig, pushConfig };
5
+ import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "./v1.js";
6
+ export { AppliedChange, ApplyOptions, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, ErrorCode, FunctionBundler, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
package/dist/index.js CHANGED
@@ -2,5 +2,5 @@ import { buildFunctionBundle } from "./lib/function-bundle.js";
2
2
  import { pullConfig } from "./lib/pull-config.js";
3
3
  import { pushConfig } from "./lib/push-config.js";
4
4
  import { apply, createBranch, inspect, plan } from "./lib/operations.js";
5
- import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, loadConfigFromFile } from "./v1.js";
6
- export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, loadConfigFromFile, plan, pullConfig, pushConfig };
5
+ import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "./v1.js";
6
+ export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
@@ -25,8 +25,10 @@ type FunctionBundler = (fn: ResolvedFunctionConfig) => Promise<Uint8Array>;
25
25
  * that nothing in this package's static graph names esbuild until a deploy actually runs —
26
26
  * a second layer of protection on top of the package split.
27
27
  *
28
- * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify`, then zips
29
- * the emitted files into the archive the Functions deploy endpoint expects.
28
+ * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify --format=esm
29
+ * --platform=node --banner:js=<createRequire shim>`, then zips the emitted files into the
30
+ * archive the Functions deploy endpoint expects. Dependencies are bundled into the entry
31
+ * (Node built-ins stay external); see {@link ESM_CJS_INTEROP_BANNER} for why the banner.
30
32
  */
31
33
  declare function buildFunctionBundle(fn: ResolvedFunctionConfig): Promise<Uint8Array>;
32
34
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"function-bundle.d.ts","names":[],"sources":["../../src/lib/function-bundle.ts"],"mappings":";;;;;;AAeA;;;;;AAEY;AAmBU,KArBV,eAAA,GAqB6B,CAAA,EAAA,EApBpC,sBAoBoC,EAAA,GAnBpC,OAmBoC,CAnB5B,UAmB4B,CAAA;;;;;AAE/B;;;;;;;;;;;;;iBAFY,mBAAA,KACjB,yBACF,QAAQ"}
1
+ {"version":3,"file":"function-bundle.d.ts","names":[],"sources":["../../src/lib/function-bundle.ts"],"mappings":";;;;;;AAeA;;;;;AAEY;AA+BU,KAjCV,eAAA,GAiC6B,CAAA,EAAA,EAhCpC,sBAgCoC,EAAA,GA/BpC,OA+BoC,CA/B5B,UA+B4B,CAAA;;;;;AAE/B;;;;;;;;;;;;;;;iBAFY,mBAAA,KACjB,yBACF,QAAQ"}
@@ -2,6 +2,14 @@ import { ErrorCode, PlatformError } from "@neondatabase/config";
2
2
  import { basename } from "node:path";
3
3
  //#region src/lib/function-bundle.ts
4
4
  /**
5
+ * Prepended to the ESM bundle. Bundled dependencies are frequently CommonJS, but an ESM
6
+ * output (`format: "esm"`) has no `require` / `__filename` / `__dirname` in scope — so any
7
+ * bundled CJS code that calls `require(...)` would fail at load with
8
+ * `Dynamic require of "x" is not supported`. Re-create those globals via `createRequire`
9
+ * so CJS and ESM dependencies coexist in the single `index.mjs`.
10
+ */
11
+ const ESM_CJS_INTEROP_BANNER = "import{createRequire as ___cr}from'module';import{fileURLToPath as ___f}from'url';import{dirname as ___d}from'path';const require=___cr(import.meta.url);const __filename=___f(import.meta.url);const __dirname=___d(__filename);";
12
+ /**
5
13
  * Build the deployable bundle (a ZIP archive of the esbuild-bundled source) for a function.
6
14
  *
7
15
  * This is the **imperative shell** step of function deploys, and the reason it lives in
@@ -15,8 +23,10 @@ import { basename } from "node:path";
15
23
  * that nothing in this package's static graph names esbuild until a deploy actually runs —
16
24
  * a second layer of protection on top of the package split.
17
25
  *
18
- * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify`, then zips
19
- * the emitted files into the archive the Functions deploy endpoint expects.
26
+ * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify --format=esm
27
+ * --platform=node --banner:js=<createRequire shim>`, then zips the emitted files into the
28
+ * archive the Functions deploy endpoint expects. Dependencies are bundled into the entry
29
+ * (Node built-ins stay external); see {@link ESM_CJS_INTEROP_BANNER} for why the banner.
20
30
  */
21
31
  async function buildFunctionBundle(fn) {
22
32
  const esbuild = await loadEsbuild();
@@ -31,7 +41,7 @@ async function buildFunctionBundle(fn) {
31
41
  minify: true,
32
42
  format: "esm",
33
43
  platform: "node",
34
- packages: "external",
44
+ banner: { js: ESM_CJS_INTEROP_BANNER },
35
45
  logLevel: "silent"
36
46
  });
37
47
  } catch (cause) {
@@ -1 +1 @@
1
- {"version":3,"file":"function-bundle.js","names":[],"sources":["../../src/lib/function-bundle.ts"],"sourcesContent":["import { basename } from \"node:path\";\nimport {\n\tErrorCode,\n\tPlatformError,\n\ttype ResolvedFunctionConfig,\n} from \"@neondatabase/config\";\n\n/**\n * Builds the deployable ZIP bundle for a single function. The default\n * implementation ({@link buildFunctionBundle}) shells out to esbuild, but\n * `pushConfig` / `apply` accept a custom bundler so a consumer that can't ship\n * esbuild's native binary (e.g. a single-file CLI) can supply its own — a WASM\n * build, an esbuild binary on PATH, etc. — without this package dragging esbuild\n * into their bundle.\n */\nexport type FunctionBundler = (\n\tfn: ResolvedFunctionConfig,\n) => Promise<Uint8Array>;\n\n/**\n * Build the deployable bundle (a ZIP archive of the esbuild-bundled source) for a function.\n *\n * This is the **imperative shell** step of function deploys, and the reason it lives in\n * `@neondatabase/config-runtime` rather than `@neondatabase/config`: it pulls in `esbuild`\n * (a native binary) and `fflate`. Keeping it out of `@neondatabase/config` means a `neon.ts`\n * that only imports `defineConfig` never drags esbuild into the user's dependency tree or\n * bundle. Deploy-side consumers (the neonctl CLI, CI) import this package and get esbuild as\n * a normal, auto-installed dependency.\n *\n * esbuild and fflate are loaded with a dynamic `import()` (not a static top-level import) so\n * that nothing in this package's static graph names esbuild until a deploy actually runs —\n * a second layer of protection on top of the package split.\n *\n * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify`, then zips\n * the emitted files into the archive the Functions deploy endpoint expects.\n */\nexport async function buildFunctionBundle(\n\tfn: ResolvedFunctionConfig,\n): Promise<Uint8Array> {\n\tconst esbuild = await loadEsbuild();\n\n\tlet result: Awaited<ReturnType<typeof esbuild.build>>;\n\ttry {\n\t\tresult = await esbuild.build({\n\t\t\tentryPoints: [fn.source],\n\t\t\tbundle: true,\n\t\t\twrite: false,\n\t\t\t// Emit `index.mjs` / `index.mjs.map`: the Functions runtime imports the archive's\n\t\t\t// entry by the conventional `index.{js,mjs}` name, and `.mjs` makes Node load the\n\t\t\t// ESM output directly. (With `write: false` and no outfile, esbuild would label the\n\t\t\t// buffer `<stdout>`.)\n\t\t\toutfile: \"index.mjs\",\n\t\t\tsourcemap: true,\n\t\t\tminify: true,\n\t\t\tformat: \"esm\",\n\t\t\tplatform: \"node\",\n\t\t\t// The Functions runtime provides Node built-ins; don't try to bundle them.\n\t\t\tpackages: \"external\",\n\t\t\tlogLevel: \"silent\",\n\t\t});\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t`Failed to bundle function \"${fn.slug}\" from ${fn.source}.`,\n\t\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst entries: Record<string, Uint8Array> = {};\n\t// `write: false` guarantees `outputFiles`, but the type is optional — guard for safety.\n\tfor (const file of result.outputFiles ?? []) {\n\t\t// esbuild returns absolute output paths; archive them under their basename\n\t\t// (`index.mjs`, `index.mjs.map`) so the bundle layout is stable regardless of cwd.\n\t\tentries[basename(file.path)] = file.contents;\n\t}\n\n\treturn zipBundle(entries);\n}\n\nasync function zipBundle(\n\tentries: Record<string, Uint8Array>,\n): Promise<Uint8Array> {\n\tconst { zipSync } = await loadFflate();\n\treturn zipSync(entries, { level: 6 });\n}\n\nasync function loadEsbuild(): Promise<typeof import(\"esbuild\")> {\n\ttry {\n\t\treturn await import(\"esbuild\");\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t\"Deploying Neon Functions requires `esbuild`, which could not be loaded.\",\n\t\t\t\t\"It is a dependency of @neondatabase/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`).\",\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n}\n\nasync function loadFflate(): Promise<typeof import(\"fflate\")> {\n\ttry {\n\t\treturn await import(\"fflate\");\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t\"Deploying Neon Functions requires `fflate`, which could not be loaded.\",\n\t\t\t\t\"It is a dependency of @neondatabase/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`).\",\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoCA,eAAsB,oBACrB,IACsB;CACtB,MAAM,UAAU,MAAM,YAAY;CAElC,IAAI;CACJ,IAAI;EACH,SAAS,MAAM,QAAQ,MAAM;GAC5B,aAAa,CAAC,GAAG,MAAM;GACvB,QAAQ;GACR,OAAO;GAKP,SAAS;GACT,WAAW;GACX,QAAQ;GACR,QAAQ;GACR,UAAU;GAEV,UAAU;GACV,UAAU;EACX,CAAC;CACF,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,8BAA8B,GAAG,KAAK,SAAS,GAAG,OAAO,IACxD,OAAiB,WAAW,OAAO,KAAK,CAC1C,EAAE,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;CAEA,MAAM,UAAsC,CAAC;CAE7C,KAAK,MAAM,QAAQ,OAAO,eAAe,CAAC,GAGzC,QAAQ,SAAS,KAAK,IAAI,KAAK,KAAK;CAGrC,OAAO,UAAU,OAAO;AACzB;AAEA,eAAe,UACd,SACsB;CACtB,MAAM,EAAE,YAAY,MAAM,WAAW;CACrC,OAAO,QAAQ,SAAS,EAAE,OAAO,EAAE,CAAC;AACrC;AAEA,eAAe,cAAiD;CAC/D,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,2EACA,oHACD,EAAE,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD;AAEA,eAAe,aAA+C;CAC7D,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,0EACA,oHACD,EAAE,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD"}
1
+ {"version":3,"file":"function-bundle.js","names":[],"sources":["../../src/lib/function-bundle.ts"],"sourcesContent":["import { basename } from \"node:path\";\nimport {\n\tErrorCode,\n\tPlatformError,\n\ttype ResolvedFunctionConfig,\n} from \"@neondatabase/config\";\n\n/**\n * Builds the deployable ZIP bundle for a single function. The default\n * implementation ({@link buildFunctionBundle}) shells out to esbuild, but\n * `pushConfig` / `apply` accept a custom bundler so a consumer that can't ship\n * esbuild's native binary (e.g. a single-file CLI) can supply its own — a WASM\n * build, an esbuild binary on PATH, etc. — without this package dragging esbuild\n * into their bundle.\n */\nexport type FunctionBundler = (\n\tfn: ResolvedFunctionConfig,\n) => Promise<Uint8Array>;\n\n/**\n * Prepended to the ESM bundle. Bundled dependencies are frequently CommonJS, but an ESM\n * output (`format: \"esm\"`) has no `require` / `__filename` / `__dirname` in scope — so any\n * bundled CJS code that calls `require(...)` would fail at load with\n * `Dynamic require of \"x\" is not supported`. Re-create those globals via `createRequire`\n * so CJS and ESM dependencies coexist in the single `index.mjs`.\n */\nconst ESM_CJS_INTEROP_BANNER =\n\t\"import{createRequire as ___cr}from'module';import{fileURLToPath as ___f}from'url';import{dirname as ___d}from'path';const require=___cr(import.meta.url);const __filename=___f(import.meta.url);const __dirname=___d(__filename);\";\n\n/**\n * Build the deployable bundle (a ZIP archive of the esbuild-bundled source) for a function.\n *\n * This is the **imperative shell** step of function deploys, and the reason it lives in\n * `@neondatabase/config-runtime` rather than `@neondatabase/config`: it pulls in `esbuild`\n * (a native binary) and `fflate`. Keeping it out of `@neondatabase/config` means a `neon.ts`\n * that only imports `defineConfig` never drags esbuild into the user's dependency tree or\n * bundle. Deploy-side consumers (the neonctl CLI, CI) import this package and get esbuild as\n * a normal, auto-installed dependency.\n *\n * esbuild and fflate are loaded with a dynamic `import()` (not a static top-level import) so\n * that nothing in this package's static graph names esbuild until a deploy actually runs —\n * a second layer of protection on top of the package split.\n *\n * Mirrors: `esbuild <source> --bundle --outfile=index.mjs --sourcemap --minify --format=esm\n * --platform=node --banner:js=<createRequire shim>`, then zips the emitted files into the\n * archive the Functions deploy endpoint expects. Dependencies are bundled into the entry\n * (Node built-ins stay external); see {@link ESM_CJS_INTEROP_BANNER} for why the banner.\n */\nexport async function buildFunctionBundle(\n\tfn: ResolvedFunctionConfig,\n): Promise<Uint8Array> {\n\tconst esbuild = await loadEsbuild();\n\n\tlet result: Awaited<ReturnType<typeof esbuild.build>>;\n\ttry {\n\t\tresult = await esbuild.build({\n\t\t\tentryPoints: [fn.source],\n\t\t\tbundle: true,\n\t\t\twrite: false,\n\t\t\t// Emit `index.mjs` / `index.mjs.map`: the Functions runtime imports the archive's\n\t\t\t// entry by the conventional `index.{js,mjs}` name, and `.mjs` makes Node load the\n\t\t\t// ESM output directly. (With `write: false` and no outfile, esbuild would label the\n\t\t\t// buffer `<stdout>`.)\n\t\t\toutfile: \"index.mjs\",\n\t\t\tsourcemap: true,\n\t\t\tminify: true,\n\t\t\tformat: \"esm\",\n\t\t\tplatform: \"node\",\n\t\t\t// Bundle dependencies into the entry so the deployed archive is self-contained\n\t\t\t// (the Functions runtime has no node_modules). Node built-ins stay external on\n\t\t\t// `platform: \"node\"`. The banner re-creates require/__filename/__dirname so\n\t\t\t// bundled CommonJS deps work inside the ESM output.\n\t\t\tbanner: { js: ESM_CJS_INTEROP_BANNER },\n\t\t\tlogLevel: \"silent\",\n\t\t});\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t`Failed to bundle function \"${fn.slug}\" from ${fn.source}.`,\n\t\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst entries: Record<string, Uint8Array> = {};\n\t// `write: false` guarantees `outputFiles`, but the type is optional — guard for safety.\n\tfor (const file of result.outputFiles ?? []) {\n\t\t// esbuild returns absolute output paths; archive them under their basename\n\t\t// (`index.mjs`, `index.mjs.map`) so the bundle layout is stable regardless of cwd.\n\t\tentries[basename(file.path)] = file.contents;\n\t}\n\n\treturn zipBundle(entries);\n}\n\nasync function zipBundle(\n\tentries: Record<string, Uint8Array>,\n): Promise<Uint8Array> {\n\tconst { zipSync } = await loadFflate();\n\treturn zipSync(entries, { level: 6 });\n}\n\nasync function loadEsbuild(): Promise<typeof import(\"esbuild\")> {\n\ttry {\n\t\treturn await import(\"esbuild\");\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t\"Deploying Neon Functions requires `esbuild`, which could not be loaded.\",\n\t\t\t\t\"It is a dependency of @neondatabase/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`).\",\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n}\n\nasync function loadFflate(): Promise<typeof import(\"fflate\")> {\n\ttry {\n\t\treturn await import(\"fflate\");\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t[\n\t\t\t\t\"Deploying Neon Functions requires `fflate`, which could not be loaded.\",\n\t\t\t\t\"It is a dependency of @neondatabase/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`).\",\n\t\t\t].join(\" \"),\n\t\t\t{ cause },\n\t\t);\n\t}\n}\n"],"mappings":";;;;;;;;;;AA0BA,MAAM,yBACL;;;;;;;;;;;;;;;;;;;;AAqBD,eAAsB,oBACrB,IACsB;CACtB,MAAM,UAAU,MAAM,YAAY;CAElC,IAAI;CACJ,IAAI;EACH,SAAS,MAAM,QAAQ,MAAM;GAC5B,aAAa,CAAC,GAAG,MAAM;GACvB,QAAQ;GACR,OAAO;GAKP,SAAS;GACT,WAAW;GACX,QAAQ;GACR,QAAQ;GACR,UAAU;GAKV,QAAQ,EAAE,IAAI,uBAAuB;GACrC,UAAU;EACX,CAAC;CACF,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,8BAA8B,GAAG,KAAK,SAAS,GAAG,OAAO,IACxD,OAAiB,WAAW,OAAO,KAAK,CAC1C,CAAC,CAAC,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;CAEA,MAAM,UAAsC,CAAC;CAE7C,KAAK,MAAM,QAAQ,OAAO,eAAe,CAAC,GAGzC,QAAQ,SAAS,KAAK,IAAI,KAAK,KAAK;CAGrC,OAAO,UAAU,OAAO;AACzB;AAEA,eAAe,UACd,SACsB;CACtB,MAAM,EAAE,YAAY,MAAM,WAAW;CACrC,OAAO,QAAQ,SAAS,EAAE,OAAO,EAAE,CAAC;AACrC;AAEA,eAAe,cAAiD;CAC/D,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,2EACA,oHACD,CAAC,CAAC,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD;AAEA,eAAe,aAA+C;CAC7D,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,0EACA,oHACD,CAAC,CAAC,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"operations.js","names":[],"sources":["../../src/lib/operations.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\tPlatformError,\n\ttype PushResult,\n\tresolveConfig,\n} from \"@neondatabase/config\";\nimport type { FunctionBundler } from \"./function-bundle.js\";\nimport { type PulledBranchConfig, pullConfig } from \"./pull-config.js\";\nimport { type PushConfigOptions, pushConfig } from \"./push-config.js\";\n\n/**\n * Where to run the operation and how to authenticate. Filesystem- and env-agnostic: the\n * `projectId` and `branchId` are always passed explicitly by the caller (e.g. neonctl\n * resolves them from `.neon` / `NEON_*` and forwards them here).\n */\nexport interface ConfigOperationOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses branches through their\n\t * project, so operations cannot run without it.\n\t */\n\tprojectId: string;\n\t/**\n\t * Neon branch id (`br-…`). **Required.** Must already exist on the project; resolve\n\t * branch names to ids before calling.\n\t */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: PushConfigOptions[\"api\"];\n}\n\n/**\n * Options accepted by {@link apply} on top of {@link ConfigOperationOptions}.\n */\nexport interface ApplyOptions extends ConfigOperationOptions {\n\t/**\n\t * Auto-confirm overriding existing remote settings (TTL, `protected`, compute\n\t * settings) on the selected branch. Without it, drift is reported as a conflict.\n\t */\n\tupdateExisting?: boolean;\n\t/** Auto-confirm applying to a branch marked `protected` on Neon. */\n\tallowProtectedBranch?: boolean;\n\t/**\n\t * Custom function bundler. Defaults to esbuild (`buildFunctionBundle`); inject\n\t * your own to deploy functions without pulling esbuild's native binary into\n\t * your build. See {@link FunctionBundler}.\n\t */\n\tbundleFunction?: FunctionBundler;\n}\n\n/**\n * Read a branch's live Neon state as a plain object (project + branch metadata and the\n * reverse-engineered `BranchConfig`). Network read only — never mutates.\n *\n * `projectId` and `branchId` are **required** (both in `options`).\n */\nexport async function inspect(\n\toptions: ConfigOperationOptions,\n): Promise<PulledBranchConfig> {\n\treturn pullConfig({\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\n/**\n * Compute what {@link apply} would do for the given branch without mutating anything\n * (dry-run plan). Returns the full {@link PushResult} with the planned changes in\n * `applied` and any blocking drift in `conflicts` — the Neon equivalent of\n * `terraform plan`.\n *\n * `projectId` and `branchId` are **required** (both in `options`).\n */\nexport async function plan(\n\tconfig: Config,\n\toptions: ConfigOperationOptions,\n): Promise<PushResult> {\n\treturn pushConfig(config, {\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\tdryRun: true,\n\t\t// Surface the full would-apply list as plan steps without mutating anything.\n\t\tupdateExisting: true,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\n/**\n * Apply a `neon.ts` policy to the given Neon branch and return the {@link PushResult}\n * describing what changed — the Neon equivalent of `terraform apply`.\n *\n * `projectId` and `branchId` are **required** (both in `options`). Pass `updateExisting`\n * to auto-confirm overriding existing remote settings and `allowProtectedBranch` to\n * auto-confirm applying to a protected branch; otherwise drift is reported as a\n * `PushConflictError`.\n *\n * Never creates projects or branches — both must already exist.\n */\nexport async function apply(\n\tconfig: Config,\n\toptions: ApplyOptions,\n): Promise<PushResult> {\n\treturn pushConfig(config, {\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t\t...(options.updateExisting ? { updateExisting: true } : {}),\n\t\t...(options.allowProtectedBranch ? { allowProtectedBranch: true } : {}),\n\t\t...(options.bundleFunction\n\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t: {}),\n\t});\n}\n\n/**\n * Options accepted by {@link createBranch}. Unlike {@link ConfigOperationOptions} this takes a\n * branch **name** (the branch does not exist yet) rather than an id.\n */\nexport interface CreateBranchOptions {\n\t/** Neon project id to create the branch in. **Required.** */\n\tprojectId: string;\n\t/** Name of the branch to create. **Required.** Must not already exist on the project. */\n\tbranchName: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. Ignored when `api` is set. */\n\tapiKey?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n\t/** Custom function bundler (defaults to esbuild). See {@link FunctionBundler}. */\n\tbundleFunction?: FunctionBundler;\n}\n\n/**\n * Result of {@link createBranch}: the created branch's id/name plus the {@link PushResult}\n * describing the policy that was applied to it.\n */\nexport interface CreateBranchResult {\n\tbranchId: string;\n\tbranchName: string;\n\t/** What applying the policy to the freshly created branch changed. */\n\tresult: PushResult;\n}\n\n/**\n * Create a Neon branch **from a `neon.ts` policy** and bring it up with its declared\n * settings/infra in one step — the operation `neonctl checkout <new-name>` needs.\n *\n * The flow is the one a creation actually wants:\n * 1. Evaluate the policy for the new branch with `exists: false` (so creation-time tuning —\n * `parent`, `ttl`, compute settings, `protected` — resolves instead of the\n * \"existing branch, leave as-is\" path most policies guard with `if (branch.exists)`).\n * 2. Create the branch, branched from the policy's `parent` (falling back to the project's\n * default branch).\n * 3. {@link pushConfig} the policy onto it with `branchExists: false`, so TTL / compute /\n * `protected` and the services (Neon Auth, Data API, functions) are applied.\n *\n * This is why `apply` alone couldn't do it: `apply` operates on an *existing* branch\n * (`exists: true`), so a policy keyed on `!branch.exists` never returns the creation tuning.\n *\n * Throws {@link PlatformError} (`Conflict`) if a branch with `branchName` already exists, or\n * (`BranchNotFound`) if the policy names a `parent` that isn't on the project.\n */\nexport async function createBranch(\n\tconfig: Config,\n\toptions: CreateBranchOptions,\n): Promise<CreateBranchResult> {\n\tconst api =\n\t\toptions.api ??\n\t\tcreateNeonApiFromOptions(\n\t\t\t\"createBranch\",\n\t\t\toptions.apiKey ? { apiKey: options.apiKey } : {},\n\t\t);\n\tconst { projectId, branchName } = options;\n\n\tconst branches = await api.listBranches(projectId);\n\tif (branches.some((b) => b.name === branchName)) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.Conflict,\n\t\t\t`createBranch: a branch named \"${branchName}\" already exists on project ${projectId}. Pick a different name or check it out instead of creating it.`,\n\t\t\t{ details: { projectId, branchName } },\n\t\t);\n\t}\n\n\t// Evaluate the policy as a creation so `parent` (only settable at create time) resolves.\n\tconst resolved = resolveConfig(config, {\n\t\tname: branchName,\n\t\texists: false,\n\t});\n\tconst parentId = resolveParentBranchId(\n\t\tresolved.parent,\n\t\tbranches,\n\t\tbranchName,\n\t);\n\n\tconst { branch } = await api.createBranch(projectId, {\n\t\tname: branchName,\n\t\t...(parentId ? { parentId } : {}),\n\t});\n\n\t// Reconcile the rest as a new branch (`branchExists: false`): TTL, compute settings,\n\t// `protected`, and the services/functions the policy declares are applied onto the\n\t// freshly created branch. `updateExisting`/`allowProtectedBranch` are safe here — there is\n\t// no pre-existing state a user would be surprised to see overridden.\n\tconst result = await pushConfig(config, {\n\t\tprojectId,\n\t\tbranchId: branch.id,\n\t\tapi,\n\t\tbranchExists: false,\n\t\tupdateExisting: true,\n\t\tallowProtectedBranch: true,\n\t\t...(options.bundleFunction\n\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t: {}),\n\t});\n\n\treturn { branchId: branch.id, branchName: branch.name, result };\n}\n\n/**\n * Resolve the parent branch id for a new branch. A policy-declared `parent` (a branch name) is\n * looked up by name; an unknown name is a hard error. With no `parent`, fall back to the\n * project's default branch, or `undefined` (let the API pick the project default) when none is\n * marked default.\n */\nfunction resolveParentBranchId(\n\tparentName: string | undefined,\n\tbranches: NeonBranchSnapshot[],\n\tbranchName: string,\n): string | undefined {\n\tif (parentName !== undefined) {\n\t\tconst match = branches.find((b) => b.name === parentName);\n\t\tif (!match) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t`createBranch: the policy for \"${branchName}\" sets parent \"${parentName}\", but no branch with that name exists. Existing branches: ${\n\t\t\t\t\tbranches.map((b) => b.name).join(\", \") || \"(none)\"\n\t\t\t\t}.`,\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchName,\n\t\t\t\t\t\tparent: parentName,\n\t\t\t\t\t\tavailable: branches.map((b) => b.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn match.id;\n\t}\n\treturn branches.find((b) => b.isDefault)?.id;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"operations.js","names":[],"sources":["../../src/lib/operations.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\tPlatformError,\n\ttype PushResult,\n\tresolveConfig,\n} from \"@neondatabase/config\";\nimport type { FunctionBundler } from \"./function-bundle.js\";\nimport { type PulledBranchConfig, pullConfig } from \"./pull-config.js\";\nimport { type PushConfigOptions, pushConfig } from \"./push-config.js\";\n\n/**\n * Where to run the operation and how to authenticate. Filesystem- and env-agnostic: the\n * `projectId` and `branchId` are always passed explicitly by the caller (e.g. neonctl\n * resolves them from `.neon` / `NEON_*` and forwards them here).\n */\nexport interface ConfigOperationOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses branches through their\n\t * project, so operations cannot run without it.\n\t */\n\tprojectId: string;\n\t/**\n\t * Neon branch id (`br-…`). **Required.** Must already exist on the project; resolve\n\t * branch names to ids before calling.\n\t */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: PushConfigOptions[\"api\"];\n}\n\n/**\n * Options accepted by {@link apply} on top of {@link ConfigOperationOptions}.\n */\nexport interface ApplyOptions extends ConfigOperationOptions {\n\t/**\n\t * Auto-confirm overriding existing remote settings (TTL, `protected`, compute\n\t * settings) on the selected branch. Without it, drift is reported as a conflict.\n\t */\n\tupdateExisting?: boolean;\n\t/** Auto-confirm applying to a branch marked `protected` on Neon. */\n\tallowProtectedBranch?: boolean;\n\t/**\n\t * Custom function bundler. Defaults to esbuild (`buildFunctionBundle`); inject\n\t * your own to deploy functions without pulling esbuild's native binary into\n\t * your build. See {@link FunctionBundler}.\n\t */\n\tbundleFunction?: FunctionBundler;\n}\n\n/**\n * Read a branch's live Neon state as a plain object (project + branch metadata and the\n * reverse-engineered `BranchConfig`). Network read only — never mutates.\n *\n * `projectId` and `branchId` are **required** (both in `options`).\n */\nexport async function inspect(\n\toptions: ConfigOperationOptions,\n): Promise<PulledBranchConfig> {\n\treturn pullConfig({\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\n/**\n * Compute what {@link apply} would do for the given branch without mutating anything\n * (dry-run plan). Returns the full {@link PushResult} with the planned changes in\n * `applied` and any blocking drift in `conflicts` — the Neon equivalent of\n * `terraform plan`.\n *\n * `projectId` and `branchId` are **required** (both in `options`).\n */\nexport async function plan(\n\tconfig: Config,\n\toptions: ConfigOperationOptions,\n): Promise<PushResult> {\n\treturn pushConfig(config, {\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\tdryRun: true,\n\t\t// Surface the full would-apply list as plan steps without mutating anything.\n\t\tupdateExisting: true,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\n/**\n * Apply a `neon.ts` policy to the given Neon branch and return the {@link PushResult}\n * describing what changed — the Neon equivalent of `terraform apply`.\n *\n * `projectId` and `branchId` are **required** (both in `options`). Pass `updateExisting`\n * to auto-confirm overriding existing remote settings and `allowProtectedBranch` to\n * auto-confirm applying to a protected branch; otherwise drift is reported as a\n * `PushConflictError`.\n *\n * Never creates projects or branches — both must already exist.\n */\nexport async function apply(\n\tconfig: Config,\n\toptions: ApplyOptions,\n): Promise<PushResult> {\n\treturn pushConfig(config, {\n\t\tprojectId: options.projectId,\n\t\tbranchId: options.branchId,\n\t\t...(options.api ? { api: options.api } : {}),\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t\t...(options.updateExisting ? { updateExisting: true } : {}),\n\t\t...(options.allowProtectedBranch ? { allowProtectedBranch: true } : {}),\n\t\t...(options.bundleFunction\n\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t: {}),\n\t});\n}\n\n/**\n * Options accepted by {@link createBranch}. Unlike {@link ConfigOperationOptions} this takes a\n * branch **name** (the branch does not exist yet) rather than an id.\n */\nexport interface CreateBranchOptions {\n\t/** Neon project id to create the branch in. **Required.** */\n\tprojectId: string;\n\t/** Name of the branch to create. **Required.** Must not already exist on the project. */\n\tbranchName: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. Ignored when `api` is set. */\n\tapiKey?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n\t/** Custom function bundler (defaults to esbuild). See {@link FunctionBundler}. */\n\tbundleFunction?: FunctionBundler;\n}\n\n/**\n * Result of {@link createBranch}: the created branch's id/name plus the {@link PushResult}\n * describing the policy that was applied to it.\n */\nexport interface CreateBranchResult {\n\tbranchId: string;\n\tbranchName: string;\n\t/** What applying the policy to the freshly created branch changed. */\n\tresult: PushResult;\n}\n\n/**\n * Create a Neon branch **from a `neon.ts` policy** and bring it up with its declared\n * settings/infra in one step — the operation `neonctl checkout <new-name>` needs.\n *\n * The flow is the one a creation actually wants:\n * 1. Evaluate the policy for the new branch with `exists: false` (so creation-time tuning —\n * `parent`, `ttl`, compute settings, `protected` — resolves instead of the\n * \"existing branch, leave as-is\" path most policies guard with `if (branch.exists)`).\n * 2. Create the branch, branched from the policy's `parent` (falling back to the project's\n * default branch).\n * 3. {@link pushConfig} the policy onto it with `branchExists: false`, so TTL / compute /\n * `protected` and the services (Neon Auth, Data API, functions) are applied.\n *\n * This is why `apply` alone couldn't do it: `apply` operates on an *existing* branch\n * (`exists: true`), so a policy keyed on `!branch.exists` never returns the creation tuning.\n *\n * Throws {@link PlatformError} (`Conflict`) if a branch with `branchName` already exists, or\n * (`BranchNotFound`) if the policy names a `parent` that isn't on the project.\n */\nexport async function createBranch(\n\tconfig: Config,\n\toptions: CreateBranchOptions,\n): Promise<CreateBranchResult> {\n\tconst api =\n\t\toptions.api ??\n\t\tcreateNeonApiFromOptions(\n\t\t\t\"createBranch\",\n\t\t\toptions.apiKey ? { apiKey: options.apiKey } : {},\n\t\t);\n\tconst { projectId, branchName } = options;\n\n\tconst branches = await api.listBranches(projectId);\n\tif (branches.some((b) => b.name === branchName)) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.Conflict,\n\t\t\t`createBranch: a branch named \"${branchName}\" already exists on project ${projectId}. Pick a different name or check it out instead of creating it.`,\n\t\t\t{ details: { projectId, branchName } },\n\t\t);\n\t}\n\n\t// Evaluate the policy as a creation so `parent` (only settable at create time) resolves.\n\tconst resolved = resolveConfig(config, {\n\t\tname: branchName,\n\t\texists: false,\n\t});\n\tconst parentId = resolveParentBranchId(\n\t\tresolved.parent,\n\t\tbranches,\n\t\tbranchName,\n\t);\n\n\tconst { branch } = await api.createBranch(projectId, {\n\t\tname: branchName,\n\t\t...(parentId ? { parentId } : {}),\n\t});\n\n\t// Reconcile the rest as a new branch (`branchExists: false`): TTL, compute settings,\n\t// `protected`, and the services/functions the policy declares are applied onto the\n\t// freshly created branch. `updateExisting`/`allowProtectedBranch` are safe here — there is\n\t// no pre-existing state a user would be surprised to see overridden.\n\tconst result = await pushConfig(config, {\n\t\tprojectId,\n\t\tbranchId: branch.id,\n\t\tapi,\n\t\tbranchExists: false,\n\t\tupdateExisting: true,\n\t\tallowProtectedBranch: true,\n\t\t...(options.bundleFunction\n\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t: {}),\n\t});\n\n\treturn { branchId: branch.id, branchName: branch.name, result };\n}\n\n/**\n * Resolve the parent branch id for a new branch. A policy-declared `parent` (a branch name) is\n * looked up by name; an unknown name is a hard error. With no `parent`, fall back to the\n * project's default branch, or `undefined` (let the API pick the project default) when none is\n * marked default.\n */\nfunction resolveParentBranchId(\n\tparentName: string | undefined,\n\tbranches: NeonBranchSnapshot[],\n\tbranchName: string,\n): string | undefined {\n\tif (parentName !== undefined) {\n\t\tconst match = branches.find((b) => b.name === parentName);\n\t\tif (!match) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t`createBranch: the policy for \"${branchName}\" sets parent \"${parentName}\", but no branch with that name exists. Existing branches: ${\n\t\t\t\t\tbranches.map((b) => b.name).join(\", \") || \"(none)\"\n\t\t\t\t}.`,\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchName,\n\t\t\t\t\t\tparent: parentName,\n\t\t\t\t\t\tavailable: branches.map((b) => b.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn match.id;\n\t}\n\treturn branches.find((b) => b.isDefault)?.id;\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { BucketAccessLevel, Config, NeonApi, NeonBranchSnapshot, NeonBucketSnapshot, NeonEndpointSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot } from "@neondatabase/config";
1
+ import { BucketAccessLevel, Config, NeonApi, NeonBranchSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonEndpointSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot } from "@neondatabase/config";
2
2
 
3
3
  //#region src/lib/pull-config.d.ts
4
4
  interface PullConfigOptions {
@@ -28,7 +28,13 @@ interface PulledPreview {
28
28
  slug: string;
29
29
  name: string;
30
30
  }>;
31
- aiGatewayEnabled: boolean;
31
+ /**
32
+ * Secret-free metadata for the credentials issued on the branch (Preview). Surfaced so
33
+ * `config status` can show issued credentials (id, scopes, last used) without ever
34
+ * exposing the one-time `api_token` / `s3_secret_access_key`. Empty when none / the
35
+ * credentials endpoint is unavailable for the project.
36
+ */
37
+ credentials: NeonCredentialMeta[];
32
38
  }
33
39
  interface PulledBranchConfig {
34
40
  project: {
@@ -54,8 +60,9 @@ interface PulledBranchConfig {
54
60
  */
55
61
  config: Config;
56
62
  /**
57
- * Live Preview-feature state, when the branch has any buckets/functions or an enabled
58
- * AI Gateway. Omitted entirely when there is nothing to report.
63
+ * Live Preview-feature state, when the branch has any buckets/functions or issued
64
+ * credentials. Omitted entirely when there is nothing to report. The AI Gateway is not
65
+ * included: it is always available (credential-gated), not per-branch state.
59
66
  */
60
67
  preview?: PulledPreview;
61
68
  }
@@ -63,7 +70,7 @@ declare function pullConfig(options: PullConfigOptions): Promise<PulledBranchCon
63
70
  declare function buildPulledBranchConfig(project: NeonProjectSnapshot, branch: NeonBranchSnapshot, branches: NeonBranchSnapshot[], endpoint: NeonEndpointSnapshot | undefined, previewState?: {
64
71
  buckets: NeonBucketSnapshot[];
65
72
  functions: NeonFunctionSnapshot[];
66
- aiGatewayEnabled: boolean;
73
+ credentials?: NeonCredentialMeta[];
67
74
  /** Whether a Neon Auth integration is enabled on the branch. */
68
75
  authEnabled?: boolean;
69
76
  /** Whether a Neon Data API integration is enabled on the branch. */
@@ -1 +1 @@
1
- {"version":3,"file":"pull-config.d.ts","names":[],"sources":["../../src/lib/pull-config.ts"],"mappings":";;;UAiBiB,iBAAA;;EAAA,SAAA,EAAA,MAAA;EAmBA;EAAa,QAAA,EAAA,MAAA;;QACpB,CAAA,EAAA,MAAA;;EACO,OAAA,CAAA,EAAA,MAAA;EAIA;EAAkB,GAAA,CAAA,EAf5B,OAe4B;;;AA2BX;AAGxB;;;;AAEG,UAtCc,aAAA,CAsCd;EAAO,OAAA,EArCA,KAqCA,CAAA;IAgGM,IAAA,EAAA,MAAA;IAAuB,MAAA,EArIC,iBAqID;;WAE9B,EAtIG,KAsIH,CAAA;IACE,IAAA,EAAA,MAAA;IACA,IAAA,EAAA,MAAA;;kBAGE,EAAA,OAAA;;AAOQ,UA9IJ,kBAAA,CA8II;;;;;;;;;;;;;;;;;;;;;;UAxHZ;;;;;YAKE;;iBAGW,UAAA,UACZ,oBACP,QAAQ;iBAgGK,uBAAA,UACN,6BACD,8BACE,gCACA;WAEA;aACE;;;;;;IAOV"}
1
+ {"version":3,"file":"pull-config.d.ts","names":[],"sources":["../../src/lib/pull-config.ts"],"mappings":";;;UAkBiB,iBAAA;;EAAA,SAAA,EAAA,MAAA;EAmBA;EAAa,QAAA,EAAA,MAAA;;QACpB,CAAA,EAAA,MAAA;;SAQI,CAAA,EAAA,MAAA;EAAkB;EAGf,GAAA,CAAA,EArBV,OAqBU;;;;AA4BO;AAGxB;;;AAEW,UA7CM,aAAA,CA6CN;SAAR,EA5CO,KA4CP,CAAA;IAAO,IAAA,EAAA,MAAA;IA4FM,MAAA,EAxIwB,iBAwID;EAAA,CAAA,CAAA;WAC7B,EAxIE,KAwIF,CAAA;IACD,IAAA,EAAA,MAAA;IACE,IAAA,EAAA,MAAA;;;;;;AAWU;;eA9IP;;UAGG,kBAAA;;;;;;;;;;;;;;;;;;;;;;UAsBR;;;;;;YAME;;iBAGW,UAAA,UACZ,oBACP,QAAQ;iBA4FK,uBAAA,UACN,6BACD,8BACE,gCACA;WAEA;aACE;gBACG;;;;;IAMb"}
@@ -8,17 +8,17 @@ async function pullConfig(options) {
8
8
  const branch = resolveBranch(options.branchId, branches);
9
9
  const endpoint = endpoints.find((ep) => ep.type === "read_write" && ep.branchId === branch.id);
10
10
  const probeDatabase = pickProbeDatabase(await api.listBranchDatabases(projectId, branch.id));
11
- const [buckets, functions, aiGatewayEnabled, auth, dataApi] = await Promise.all([
11
+ const [buckets, functions, credentials, auth, dataApi] = await Promise.all([
12
12
  degradeUnavailable(() => api.listBranchBuckets(projectId, branch.id), []),
13
13
  degradeUnavailable(() => api.listBranchFunctions(projectId, branch.id), []),
14
- degradeUnavailable(() => api.getAiGatewayEnabled(projectId, branch.id), false),
14
+ degradeUnavailable(() => api.listCredentials(projectId, branch.id), []),
15
15
  api.getNeonAuth(projectId, branch.id),
16
16
  probeDatabase ? api.getNeonDataApi(projectId, branch.id, probeDatabase) : Promise.resolve(null)
17
17
  ]);
18
18
  return buildPulledBranchConfig(project, branch, branches, endpoint, {
19
19
  buckets,
20
20
  functions,
21
- aiGatewayEnabled,
21
+ credentials,
22
22
  authEnabled: auth !== null,
23
23
  dataApiEnabled: dataApi !== null
24
24
  });
@@ -96,7 +96,8 @@ function buildPulledBranchConfig(project, branch, branches, endpoint, previewSta
96
96
  * the branch has no Preview features so the field can be omitted entirely.
97
97
  */
98
98
  function buildPulledPreview(state) {
99
- if (state.buckets.length === 0 && state.functions.length === 0 && !state.aiGatewayEnabled) return;
99
+ const credentials = state.credentials ?? [];
100
+ if (state.buckets.length === 0 && state.functions.length === 0 && credentials.length === 0) return;
100
101
  return {
101
102
  buckets: state.buckets.map((b) => ({
102
103
  name: b.name,
@@ -106,7 +107,7 @@ function buildPulledPreview(state) {
106
107
  slug: f.slug,
107
108
  name: f.name
108
109
  })),
109
- aiGatewayEnabled: state.aiGatewayEnabled
110
+ credentials
110
111
  };
111
112
  }
112
113
  function resolveBranch(branchId, branches) {
@@ -1 +1 @@
1
- {"version":3,"file":"pull-config.js","names":[],"sources":["../../src/lib/pull-config.ts"],"sourcesContent":["import {\n\ttype BranchTuning,\n\ttype BucketAccessLevel,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonBucketSnapshot,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonEndpointSnapshot,\n\ttype NeonFunctionSnapshot,\n\ttype NeonProjectSnapshot,\n\tPlatformError,\n} from \"@neondatabase/config\";\n\nexport interface PullConfigOptions {\n\t/** Neon project id (`<project>`). Required — the API addresses branches by project. */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). Required. Resolve names to ids before calling. */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n}\n\n/**\n * Live Preview-feature state read back from a branch. Surfaced alongside `config` rather\n * than inside it because functions cannot round-trip: the remote only knows the deployed\n * bundle, not the local `source` path a {@link FunctionConfig} requires, so a pulled\n * function is reported as `{ slug, name }` (no `source`).\n */\nexport interface PulledPreview {\n\tbuckets: Array<{ name: string; access: BucketAccessLevel }>;\n\tfunctions: Array<{ slug: string; name: string }>;\n\taiGatewayEnabled: boolean;\n}\n\nexport interface PulledBranchConfig {\n\tproject: {\n\t\tid: string;\n\t\tname: string;\n\t\tregion: string;\n\t\tpgVersion: number;\n\t\torgId?: string;\n\t};\n\tbranch: {\n\t\tid: string;\n\t\tname: string;\n\t\tparent?: string;\n\t\tisDefault: boolean;\n\t\tprotected: boolean;\n\t\texpiresAt?: string;\n\t};\n\t/**\n\t * The branch's live state expressed as a {@link Config}: static `auth` / `dataApi`\n\t * toggles plus a `branch` closure carrying the branch's lifecycle/compute tuning.\n\t * Preview functions/buckets are reported separately in {@link PulledBranchConfig.preview}\n\t * because functions cannot round-trip (the remote has no `source` path).\n\t */\n\tconfig: Config;\n\t/**\n\t * Live Preview-feature state, when the branch has any buckets/functions or an enabled\n\t * AI Gateway. Omitted entirely when there is nothing to report.\n\t */\n\tpreview?: PulledPreview;\n}\n\nexport async function pullConfig(\n\toptions: PullConfigOptions,\n): Promise<PulledBranchConfig> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\tconst project = await api.getProject(projectId);\n\tconst [branches, endpoints] = await Promise.all([\n\t\tapi.listBranches(projectId),\n\t\tapi.listEndpoints(projectId),\n\t]);\n\tconst branch = resolveBranch(options.branchId, branches);\n\tconst endpoint = endpoints.find(\n\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t);\n\n\t// Data API is enabled per branch + database, so resolve a database to probe.\n\tconst databases = await api.listBranchDatabases(projectId, branch.id);\n\tconst probeDatabase = pickProbeDatabase(databases);\n\n\t// Preview reads degrade to \"none / disabled\" when the feature isn't available for the\n\t// project/region. `pullConfig` mirrors the branch for env resolution (`neon dev`,\n\t// `neon env pull`) and `inspect` — an unavailable Preview feature should not break those\n\t// (env comes from auth/dataApi/postgres). `pushConfig` is the place that fails on an\n\t// unavailable feature, and only when the policy declares it.\n\tconst [buckets, functions, aiGatewayEnabled, auth, dataApi] =\n\t\tawait Promise.all([\n\t\t\tdegradeUnavailable(\n\t\t\t\t() => api.listBranchBuckets(projectId, branch.id),\n\t\t\t\t[],\n\t\t\t),\n\t\t\tdegradeUnavailable(\n\t\t\t\t() => api.listBranchFunctions(projectId, branch.id),\n\t\t\t\t[],\n\t\t\t),\n\t\t\tdegradeUnavailable(\n\t\t\t\t() => api.getAiGatewayEnabled(projectId, branch.id),\n\t\t\t\tfalse,\n\t\t\t),\n\t\t\tapi.getNeonAuth(projectId, branch.id),\n\t\t\tprobeDatabase\n\t\t\t\t? api.getNeonDataApi(projectId, branch.id, probeDatabase)\n\t\t\t\t: Promise.resolve(null),\n\t\t]);\n\n\treturn buildPulledBranchConfig(project, branch, branches, endpoint, {\n\t\tbuckets,\n\t\tfunctions,\n\t\taiGatewayEnabled,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t});\n}\n\n/**\n * Pick the database to probe for a Data API integration. Data API is enabled per\n * branch + database; for read-back we only need to know whether *any* database has it\n * on, so prefer the conventional default (`neondb`) and otherwise fall back to the first\n * database. Returns `undefined` when the branch has no databases.\n */\nfunction pickProbeDatabase(\n\tdatabases: NeonDatabaseSnapshot[],\n): string | undefined {\n\tif (databases.length === 0) return undefined;\n\tconst byName = databases.find((d) => d.name === \"neondb\");\n\tif (byName) return byName.name;\n\treturn databases[0].name;\n}\n\n/**\n * Run a Preview-feature read, returning `fallback` if the feature is unavailable for the\n * project/region (a {@link ErrorCode.FeatureUnavailable} from the adapter). Other errors\n * propagate. Used by `pullConfig` so a branch without a Preview feature still mirrors\n * cleanly for env resolution / `inspect`, rather than aborting on an unrelated capability.\n */\nasync function degradeUnavailable<T>(\n\tread: () => Promise<T>,\n\tfallback: T,\n): Promise<T> {\n\ttry {\n\t\treturn await read();\n\t} catch (err) {\n\t\tif (\n\t\t\terr instanceof PlatformError &&\n\t\t\terr.code === ErrorCode.FeatureUnavailable\n\t\t) {\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nfunction createApiFromOptions(options: PullConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pullConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nexport function buildPulledBranchConfig(\n\tproject: NeonProjectSnapshot,\n\tbranch: NeonBranchSnapshot,\n\tbranches: NeonBranchSnapshot[],\n\tendpoint: NeonEndpointSnapshot | undefined,\n\tpreviewState?: {\n\t\tbuckets: NeonBucketSnapshot[];\n\t\tfunctions: NeonFunctionSnapshot[];\n\t\taiGatewayEnabled: boolean;\n\t\t/** Whether a Neon Auth integration is enabled on the branch. */\n\t\tauthEnabled?: boolean;\n\t\t/** Whether a Neon Data API integration is enabled on the branch. */\n\t\tdataApiEnabled?: boolean;\n\t},\n): PulledBranchConfig {\n\tconst parent = branch.parentId\n\t\t? branches.find((b) => b.id === branch.parentId)\n\t\t: undefined;\n\t// Auth/Data API are static top-level toggles, so a config pulled from a branch with\n\t// them enabled round-trips through `resolveConfig` / `fetchEnv` and the matching\n\t// secrets get injected. Branch lifecycle/compute is per-branch tuning, so it goes in\n\t// the `branch` closure.\n\tconst tuning: BranchTuning = {};\n\tif (parent) tuning.parent = parent.name;\n\t// Deliberately NOT emitting `ttl` from `branch.expiresAt`: policy `ttl` is a\n\t// creation-time *duration*, while `expiresAt` is an absolute timestamp. Feeding the\n\t// timestamp through `parseDuration` (in `resolveConfig`) would throw, breaking\n\t// `fetchEnv` / `neon dev` / `neon env pull` for any branch that has a TTL. The expiry\n\t// is reported faithfully on `branch.expiresAt` above.\n\tif (branch.protected) tuning.protected = true;\n\tif (endpoint) {\n\t\tconst compute = endpointToComputeSettings(endpoint, project);\n\t\tif (compute) tuning.postgres = { computeSettings: compute };\n\t}\n\tconst config: Config = {\n\t\t...(previewState?.authEnabled ? { auth: true } : {}),\n\t\t...(previewState?.dataApiEnabled ? { dataApi: true } : {}),\n\t\t...(Object.keys(tuning).length > 0 ? { branch: () => tuning } : {}),\n\t};\n\tconst result: PulledBranchConfig = {\n\t\tproject: {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tregion: project.regionId,\n\t\t\tpgVersion: project.pgVersion,\n\t\t\t...(project.orgId ? { orgId: project.orgId } : {}),\n\t\t},\n\t\tbranch: {\n\t\t\tid: branch.id,\n\t\t\tname: branch.name,\n\t\t\t...(parent ? { parent: parent.name } : {}),\n\t\t\tisDefault: branch.isDefault,\n\t\t\tprotected: branch.protected,\n\t\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t\t},\n\t\tconfig,\n\t};\n\tconst preview = previewState ? buildPulledPreview(previewState) : undefined;\n\tif (preview) result.preview = preview;\n\treturn result;\n}\n\n/**\n * Reverse-engineer the {@link PulledPreview} from remote snapshots. Returns `undefined` when\n * the branch has no Preview features so the field can be omitted entirely.\n */\nfunction buildPulledPreview(state: {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\taiGatewayEnabled: boolean;\n}): PulledPreview | undefined {\n\tif (\n\t\tstate.buckets.length === 0 &&\n\t\tstate.functions.length === 0 &&\n\t\t!state.aiGatewayEnabled\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tbuckets: state.buckets.map((b) => ({\n\t\t\tname: b.name,\n\t\t\taccess: b.accessLevel,\n\t\t})),\n\t\tfunctions: state.functions.map((f) => ({\n\t\t\tslug: f.slug,\n\t\t\tname: f.name,\n\t\t})),\n\t\taiGatewayEnabled: state.aiGatewayEnabled,\n\t};\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pullConfig: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction endpointToComputeSettings(\n\tendpoint: NeonEndpointSnapshot,\n\tproject: NeonProjectSnapshot,\n): ComputeSettings | undefined {\n\tconst defaults = project.defaultEndpointSettings;\n\tconst out: ComputeSettings = {};\n\tif (\n\t\tendpoint.autoscalingLimitMinCu !== undefined &&\n\t\tendpoint.autoscalingLimitMinCu !== defaults?.autoscalingLimitMinCu\n\t) {\n\t\tout.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t}\n\tif (\n\t\tendpoint.autoscalingLimitMaxCu !== undefined &&\n\t\tendpoint.autoscalingLimitMaxCu !== defaults?.autoscalingLimitMaxCu\n\t) {\n\t\tout.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t}\n\tif (\n\t\tendpoint.suspendTimeout !== undefined &&\n\t\tendpoint.suspendTimeout !== defaults?.suspendTimeout\n\t) {\n\t\tout.suspendTimeout = endpoint.suspendTimeout;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pull-config.js","names":[],"sources":["../../src/lib/pull-config.ts"],"sourcesContent":["import {\n\ttype BranchTuning,\n\ttype BucketAccessLevel,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonBucketSnapshot,\n\ttype NeonCredentialMeta,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonEndpointSnapshot,\n\ttype NeonFunctionSnapshot,\n\ttype NeonProjectSnapshot,\n\tPlatformError,\n} from \"@neondatabase/config\";\n\nexport interface PullConfigOptions {\n\t/** Neon project id (`<project>`). Required — the API addresses branches by project. */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). Required. Resolve names to ids before calling. */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n}\n\n/**\n * Live Preview-feature state read back from a branch. Surfaced alongside `config` rather\n * than inside it because functions cannot round-trip: the remote only knows the deployed\n * bundle, not the local `source` path a {@link FunctionConfig} requires, so a pulled\n * function is reported as `{ slug, name }` (no `source`).\n */\nexport interface PulledPreview {\n\tbuckets: Array<{ name: string; access: BucketAccessLevel }>;\n\tfunctions: Array<{ slug: string; name: string }>;\n\t/**\n\t * Secret-free metadata for the credentials issued on the branch (Preview). Surfaced so\n\t * `config status` can show issued credentials (id, scopes, last used) without ever\n\t * exposing the one-time `api_token` / `s3_secret_access_key`. Empty when none / the\n\t * credentials endpoint is unavailable for the project.\n\t */\n\tcredentials: NeonCredentialMeta[];\n}\n\nexport interface PulledBranchConfig {\n\tproject: {\n\t\tid: string;\n\t\tname: string;\n\t\tregion: string;\n\t\tpgVersion: number;\n\t\torgId?: string;\n\t};\n\tbranch: {\n\t\tid: string;\n\t\tname: string;\n\t\tparent?: string;\n\t\tisDefault: boolean;\n\t\tprotected: boolean;\n\t\texpiresAt?: string;\n\t};\n\t/**\n\t * The branch's live state expressed as a {@link Config}: static `auth` / `dataApi`\n\t * toggles plus a `branch` closure carrying the branch's lifecycle/compute tuning.\n\t * Preview functions/buckets are reported separately in {@link PulledBranchConfig.preview}\n\t * because functions cannot round-trip (the remote has no `source` path).\n\t */\n\tconfig: Config;\n\t/**\n\t * Live Preview-feature state, when the branch has any buckets/functions or issued\n\t * credentials. Omitted entirely when there is nothing to report. The AI Gateway is not\n\t * included: it is always available (credential-gated), not per-branch state.\n\t */\n\tpreview?: PulledPreview;\n}\n\nexport async function pullConfig(\n\toptions: PullConfigOptions,\n): Promise<PulledBranchConfig> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\tconst project = await api.getProject(projectId);\n\tconst [branches, endpoints] = await Promise.all([\n\t\tapi.listBranches(projectId),\n\t\tapi.listEndpoints(projectId),\n\t]);\n\tconst branch = resolveBranch(options.branchId, branches);\n\tconst endpoint = endpoints.find(\n\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t);\n\n\t// Data API is enabled per branch + database, so resolve a database to probe.\n\tconst databases = await api.listBranchDatabases(projectId, branch.id);\n\tconst probeDatabase = pickProbeDatabase(databases);\n\n\t// Preview reads degrade to \"none / disabled\" when the feature isn't available for the\n\t// project/region. `pullConfig` mirrors the branch for env resolution (`neon dev`,\n\t// `neon env pull`) and `inspect` — an unavailable Preview feature should not break those\n\t// (env comes from auth/dataApi/postgres). `pushConfig` is the place that fails on an\n\t// unavailable feature, and only when the policy declares it.\n\tconst [buckets, functions, credentials, auth, dataApi] = await Promise.all([\n\t\tdegradeUnavailable(\n\t\t\t() => api.listBranchBuckets(projectId, branch.id),\n\t\t\t[],\n\t\t),\n\t\tdegradeUnavailable(\n\t\t\t() => api.listBranchFunctions(projectId, branch.id),\n\t\t\t[],\n\t\t),\n\t\tdegradeUnavailable(() => api.listCredentials(projectId, branch.id), []),\n\t\tapi.getNeonAuth(projectId, branch.id),\n\t\tprobeDatabase\n\t\t\t? api.getNeonDataApi(projectId, branch.id, probeDatabase)\n\t\t\t: Promise.resolve(null),\n\t]);\n\n\treturn buildPulledBranchConfig(project, branch, branches, endpoint, {\n\t\tbuckets,\n\t\tfunctions,\n\t\tcredentials,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t});\n}\n\n/**\n * Pick the database to probe for a Data API integration. Data API is enabled per\n * branch + database; for read-back we only need to know whether *any* database has it\n * on, so prefer the conventional default (`neondb`) and otherwise fall back to the first\n * database. Returns `undefined` when the branch has no databases.\n */\nfunction pickProbeDatabase(\n\tdatabases: NeonDatabaseSnapshot[],\n): string | undefined {\n\tif (databases.length === 0) return undefined;\n\tconst byName = databases.find((d) => d.name === \"neondb\");\n\tif (byName) return byName.name;\n\treturn databases[0].name;\n}\n\n/**\n * Run a Preview-feature read, returning `fallback` if the feature is unavailable for the\n * project/region (a {@link ErrorCode.FeatureUnavailable} from the adapter). Other errors\n * propagate. Used by `pullConfig` so a branch without a Preview feature still mirrors\n * cleanly for env resolution / `inspect`, rather than aborting on an unrelated capability.\n */\nasync function degradeUnavailable<T>(\n\tread: () => Promise<T>,\n\tfallback: T,\n): Promise<T> {\n\ttry {\n\t\treturn await read();\n\t} catch (err) {\n\t\tif (\n\t\t\terr instanceof PlatformError &&\n\t\t\terr.code === ErrorCode.FeatureUnavailable\n\t\t) {\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nfunction createApiFromOptions(options: PullConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pullConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nexport function buildPulledBranchConfig(\n\tproject: NeonProjectSnapshot,\n\tbranch: NeonBranchSnapshot,\n\tbranches: NeonBranchSnapshot[],\n\tendpoint: NeonEndpointSnapshot | undefined,\n\tpreviewState?: {\n\t\tbuckets: NeonBucketSnapshot[];\n\t\tfunctions: NeonFunctionSnapshot[];\n\t\tcredentials?: NeonCredentialMeta[];\n\t\t/** Whether a Neon Auth integration is enabled on the branch. */\n\t\tauthEnabled?: boolean;\n\t\t/** Whether a Neon Data API integration is enabled on the branch. */\n\t\tdataApiEnabled?: boolean;\n\t},\n): PulledBranchConfig {\n\tconst parent = branch.parentId\n\t\t? branches.find((b) => b.id === branch.parentId)\n\t\t: undefined;\n\t// Auth/Data API are static top-level toggles, so a config pulled from a branch with\n\t// them enabled round-trips through `resolveConfig` / `fetchEnv` and the matching\n\t// secrets get injected. Branch lifecycle/compute is per-branch tuning, so it goes in\n\t// the `branch` closure.\n\tconst tuning: BranchTuning = {};\n\tif (parent) tuning.parent = parent.name;\n\t// Deliberately NOT emitting `ttl` from `branch.expiresAt`: policy `ttl` is a\n\t// creation-time *duration*, while `expiresAt` is an absolute timestamp. Feeding the\n\t// timestamp through `parseDuration` (in `resolveConfig`) would throw, breaking\n\t// `fetchEnv` / `neon dev` / `neon env pull` for any branch that has a TTL. The expiry\n\t// is reported faithfully on `branch.expiresAt` above.\n\tif (branch.protected) tuning.protected = true;\n\tif (endpoint) {\n\t\tconst compute = endpointToComputeSettings(endpoint, project);\n\t\tif (compute) tuning.postgres = { computeSettings: compute };\n\t}\n\tconst config: Config = {\n\t\t...(previewState?.authEnabled ? { auth: true } : {}),\n\t\t...(previewState?.dataApiEnabled ? { dataApi: true } : {}),\n\t\t...(Object.keys(tuning).length > 0 ? { branch: () => tuning } : {}),\n\t};\n\tconst result: PulledBranchConfig = {\n\t\tproject: {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tregion: project.regionId,\n\t\t\tpgVersion: project.pgVersion,\n\t\t\t...(project.orgId ? { orgId: project.orgId } : {}),\n\t\t},\n\t\tbranch: {\n\t\t\tid: branch.id,\n\t\t\tname: branch.name,\n\t\t\t...(parent ? { parent: parent.name } : {}),\n\t\t\tisDefault: branch.isDefault,\n\t\t\tprotected: branch.protected,\n\t\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t\t},\n\t\tconfig,\n\t};\n\tconst preview = previewState ? buildPulledPreview(previewState) : undefined;\n\tif (preview) result.preview = preview;\n\treturn result;\n}\n\n/**\n * Reverse-engineer the {@link PulledPreview} from remote snapshots. Returns `undefined` when\n * the branch has no Preview features so the field can be omitted entirely.\n */\nfunction buildPulledPreview(state: {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\tcredentials?: NeonCredentialMeta[];\n}): PulledPreview | undefined {\n\tconst credentials = state.credentials ?? [];\n\tif (\n\t\tstate.buckets.length === 0 &&\n\t\tstate.functions.length === 0 &&\n\t\tcredentials.length === 0\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tbuckets: state.buckets.map((b) => ({\n\t\t\tname: b.name,\n\t\t\taccess: b.accessLevel,\n\t\t})),\n\t\tfunctions: state.functions.map((f) => ({\n\t\t\tslug: f.slug,\n\t\t\tname: f.name,\n\t\t})),\n\t\tcredentials,\n\t};\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pullConfig: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction endpointToComputeSettings(\n\tendpoint: NeonEndpointSnapshot,\n\tproject: NeonProjectSnapshot,\n): ComputeSettings | undefined {\n\tconst defaults = project.defaultEndpointSettings;\n\tconst out: ComputeSettings = {};\n\tif (\n\t\tendpoint.autoscalingLimitMinCu !== undefined &&\n\t\tendpoint.autoscalingLimitMinCu !== defaults?.autoscalingLimitMinCu\n\t) {\n\t\tout.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t}\n\tif (\n\t\tendpoint.autoscalingLimitMaxCu !== undefined &&\n\t\tendpoint.autoscalingLimitMaxCu !== defaults?.autoscalingLimitMaxCu\n\t) {\n\t\tout.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t}\n\tif (\n\t\tendpoint.suspendTimeout !== undefined &&\n\t\tendpoint.suspendTimeout !== defaults?.suspendTimeout\n\t) {\n\t\tout.suspendTimeout = endpoint.suspendTimeout;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n"],"mappings":"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"}
@@ -92,6 +92,15 @@ async function pushConfig(config, options) {
92
92
  });
93
93
  applied.push(change);
94
94
  }
95
+ await enrichFunctionInvocationUrls({
96
+ api,
97
+ projectId: remoteProject.id,
98
+ branchId: branch.id,
99
+ plan: diff.plan,
100
+ applied,
101
+ preview: remote.preview,
102
+ dryRun
103
+ });
95
104
  const result = {
96
105
  projectId: remoteProject.id,
97
106
  branchId: branch.id,
@@ -149,58 +158,32 @@ function synthesizeAppliedChange(step) {
149
158
  case "enable-auth": return {
150
159
  kind: "service",
151
160
  action: "create",
152
- identifier: "auth",
153
- details: {
154
- branchName: step.branchName,
155
- ...step.databaseName ? { databaseName: step.databaseName } : {}
156
- }
161
+ identifier: "auth"
157
162
  };
158
163
  case "enable-data-api": return {
159
164
  kind: "service",
160
165
  action: "create",
161
- identifier: "dataApi",
162
- details: {
163
- branchName: step.branchName,
164
- databaseName: step.databaseName
165
- }
166
+ identifier: "dataApi"
166
167
  };
167
168
  case "create-bucket": return {
168
169
  kind: "service",
169
170
  action: "create",
170
171
  identifier: `bucket:${step.bucketName}`,
171
172
  details: {
172
- branchName: step.branchName,
173
173
  bucketName: step.bucketName,
174
174
  accessLevel: step.accessLevel
175
175
  }
176
176
  };
177
- case "create-function": return {
178
- kind: "service",
179
- action: "create",
180
- identifier: `function:${step.fn.slug}`,
181
- details: {
182
- branchName: step.branchName,
183
- slug: step.fn.slug,
184
- name: step.fn.name
185
- }
186
- };
187
177
  case "deploy-function": return {
188
178
  kind: "service",
189
- action: "update",
179
+ action: step.functionExists ? "update" : "create",
190
180
  identifier: `function:${step.fn.slug}`,
191
181
  details: {
192
- branchName: step.branchName,
193
182
  slug: step.fn.slug,
194
183
  source: step.fn.source,
195
184
  runtime: step.fn.runtime
196
185
  }
197
186
  };
198
- case "enable-ai-gateway": return {
199
- kind: "service",
200
- action: "create",
201
- identifier: "aiGateway",
202
- details: { branchName: step.branchName }
203
- };
204
187
  }
205
188
  }
206
189
  function createApiFromOptions(options) {
@@ -240,21 +223,19 @@ async function resolveServiceState(args) {
240
223
  };
241
224
  }
242
225
  /**
243
- * Pre-fetch the current state of branch-scoped Preview features (buckets, functions, AI
244
- * Gateway) so the diff can be computed additively. Only called when the policy has a
245
- * `preview` block.
226
+ * Pre-fetch the current state of branch-scoped Preview features (buckets, functions) so the
227
+ * diff can be computed additively. Only called when the policy has a `preview` block.
228
+ *
229
+ * The AI Gateway is not probed: it is always available (credential-gated, not per-branch
230
+ * provisioned), so `preview.aiGateway` produces no plan step — it only drives the branch
231
+ * credential's `ai_gateway:invoke` scope and the gateway env vars (`@neondatabase/env`).
246
232
  */
247
233
  async function resolvePreviewState(args) {
248
234
  const { api, projectId, branchId, desired } = args;
249
- const [buckets, functions, aiGatewayEnabled] = await Promise.all([
250
- desired.buckets.length > 0 ? api.listBranchBuckets(projectId, branchId) : Promise.resolve([]),
251
- desired.functions.length > 0 ? api.listBranchFunctions(projectId, branchId) : Promise.resolve([]),
252
- desired.aiGatewayEnabled ? api.getAiGatewayEnabled(projectId, branchId) : Promise.resolve(false)
253
- ]);
235
+ const [buckets, functions] = await Promise.all([desired.buckets.length > 0 ? api.listBranchBuckets(projectId, branchId) : Promise.resolve([]), desired.functions.length > 0 ? api.listBranchFunctions(projectId, branchId) : Promise.resolve([])]);
254
236
  return {
255
237
  buckets,
256
- functions,
257
- aiGatewayEnabled
238
+ functions
258
239
  };
259
240
  }
260
241
  /**
@@ -318,22 +299,14 @@ async function applyStep(step, ctx) {
318
299
  return {
319
300
  kind: "service",
320
301
  action: "create",
321
- identifier: "auth",
322
- details: {
323
- branchName: step.branchName,
324
- ...step.databaseName ? { databaseName: step.databaseName } : {}
325
- }
302
+ identifier: "auth"
326
303
  };
327
304
  case "enable-data-api":
328
305
  await ctx.api.enableProjectBranchDataApi(ctx.remoteProjectId, step.branchId, step.databaseName);
329
306
  return {
330
307
  kind: "service",
331
308
  action: "create",
332
- identifier: "dataApi",
333
- details: {
334
- branchName: step.branchName,
335
- databaseName: step.databaseName
336
- }
309
+ identifier: "dataApi"
337
310
  };
338
311
  case "create-bucket":
339
312
  await ctx.api.createBranchBucket(ctx.remoteProjectId, step.branchId, {
@@ -345,26 +318,10 @@ async function applyStep(step, ctx) {
345
318
  action: "create",
346
319
  identifier: `bucket:${step.bucketName}`,
347
320
  details: {
348
- branchName: step.branchName,
349
321
  bucketName: step.bucketName,
350
322
  accessLevel: step.accessLevel
351
323
  }
352
324
  };
353
- case "create-function":
354
- await ctx.api.createBranchFunction(ctx.remoteProjectId, step.branchId, {
355
- slug: step.fn.slug,
356
- name: step.fn.name
357
- });
358
- return {
359
- kind: "service",
360
- action: "create",
361
- identifier: `function:${step.fn.slug}`,
362
- details: {
363
- branchName: step.branchName,
364
- slug: step.fn.slug,
365
- name: step.fn.name
366
- }
367
- };
368
325
  case "deploy-function": {
369
326
  const bundle = await ctx.bundleFunction(step.fn);
370
327
  const deployment = await ctx.api.deployBranchFunction(ctx.remoteProjectId, step.branchId, step.fn.slug, {
@@ -374,10 +331,9 @@ async function applyStep(step, ctx) {
374
331
  });
375
332
  return {
376
333
  kind: "service",
377
- action: "update",
334
+ action: step.functionExists ? "update" : "create",
378
335
  identifier: `function:${step.fn.slug}`,
379
336
  details: {
380
- branchName: step.branchName,
381
337
  slug: step.fn.slug,
382
338
  source: step.fn.source,
383
339
  runtime: step.fn.runtime,
@@ -385,16 +341,42 @@ async function applyStep(step, ctx) {
385
341
  }
386
342
  };
387
343
  }
388
- case "enable-ai-gateway":
389
- await ctx.api.enableAiGateway(ctx.remoteProjectId, step.branchId);
390
- return {
391
- kind: "service",
392
- action: "create",
393
- identifier: "aiGateway",
394
- details: { branchName: step.branchName }
395
- };
396
344
  }
397
345
  }
346
+ /**
347
+ * Add each deployed function's invocation URL to its applied-change `details` so callers
348
+ * (e.g. neonctl) can show users where to call the function right after a push.
349
+ *
350
+ * The URL is read from the preview snapshot already fetched for the diff, which lists every
351
+ * existing function with its `invocationUrl`. A function created by its *first* deployment in
352
+ * this push is not in that snapshot, so when one is present we re-list the branch's functions
353
+ * once to learn its freshly-minted URL. Skipped on dry-run (nothing was created) and
354
+ * best-effort otherwise: a failed re-list omits the URL rather than failing a push that has
355
+ * already applied.
356
+ */
357
+ async function enrichFunctionInvocationUrls(args) {
358
+ const { api, projectId, branchId, plan, applied, preview, dryRun } = args;
359
+ const deployedSlugs = plan.flatMap((step) => step.kind === "deploy-function" ? [step.fn.slug] : []);
360
+ if (deployedSlugs.length === 0) return;
361
+ const urlBySlug = new Map((preview?.functions ?? []).map((fn) => [fn.slug, fn.invocationUrl]));
362
+ if (deployedSlugs.some((slug) => !urlBySlug.has(slug)) && !dryRun) try {
363
+ for (const fn of await api.listBranchFunctions(projectId, branchId)) urlBySlug.set(fn.slug, fn.invocationUrl);
364
+ } catch {}
365
+ for (const change of applied) {
366
+ const slug = functionSlugFromIdentifier(change.identifier);
367
+ if (slug === void 0) continue;
368
+ const invocationUrl = urlBySlug.get(slug);
369
+ if (invocationUrl === void 0) continue;
370
+ change.details = {
371
+ ...change.details,
372
+ invocationUrl
373
+ };
374
+ }
375
+ }
376
+ /** Pull the function slug out of a `function:<slug>` applied-change identifier. */
377
+ function functionSlugFromIdentifier(identifier) {
378
+ return identifier.startsWith("function:") ? identifier.slice(9) : void 0;
379
+ }
398
380
  //#endregion
399
381
  export { pushConfig };
400
382
 
@@ -1 +1 @@
1
- {"version":3,"file":"push-config.js","names":[],"sources":["../../src/lib/push-config.ts"],"sourcesContent":["import {\n\ttype AppliedChange,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tdiffConfig,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype PlanStep,\n\tPlatformError,\n\tPushAbortedError,\n\tPushConflictError,\n\ttype PushResult,\n\ttype RemotePreviewState,\n\ttype RemoteServiceState,\n\ttype RemoteState,\n\ttype ResolvedFunctionConfig,\n\ttype ResolvedPreviewConfig,\n\tresolveConfig,\n} from \"@neondatabase/config\";\nimport type { FunctionBundler } from \"./function-bundle.js\";\n\n/**\n * Default function bundler (esbuild), loaded lazily so that `buildFunctionBundle`\n * — and the esbuild it pulls in — only enters the module graph when a deploy\n * actually needs it AND no custom `bundleFunction` was injected. A consumer that\n * injects its own bundler never triggers this import, so esbuild can be dropped\n * from their build entirely.\n */\nconst defaultBundleFunction: FunctionBundler = async (\n\tfn: ResolvedFunctionConfig,\n): Promise<Uint8Array> => {\n\tconst { buildFunctionBundle } = await import(\"./function-bundle.js\");\n\treturn buildFunctionBundle(fn);\n};\n\nexport interface PushConfigOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses every branch through\n\t * its project, so there is no way to push without it. `pushConfig` never creates a\n\t * project; resolve the id yourself (e.g. via neonctl) and pass it in.\n\t */\n\tprojectId: string;\n\t/**\n\t * Neon branch id (`br-…`). **Required.** `pushConfig` never creates a branch — it must\n\t * already exist on the project. Resolve names to ids before calling.\n\t */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. Ignored when `api` is supplied. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/**\n\t * Inject a custom NeonApi adapter. Primarily used by tests; production callers can rely\n\t * on the default real adapter built from `apiKey`.\n\t */\n\tapi?: NeonApi;\n\t/**\n\t * Whether to evaluate the policy as if the target branch **already exists** (the value of\n\t * `branch.exists` passed to the `defineConfig((branch) => …)` closure). Defaults to `true`.\n\t *\n\t * Set to `false` to evaluate the policy as a **branch creation** — used by\n\t * {@link createBranch} right after it provisions a new branch, so creation-time tuning\n\t * gated on `!branch.exists` (TTL, compute settings, `parent`) actually resolves instead of\n\t * hitting the \"existing branch, leave as-is\" path. Only affects policy evaluation; the\n\t * branch must still physically exist on Neon (`pushConfig` never creates one).\n\t */\n\tbranchExists?: boolean;\n\t/**\n\t * Auto-confirm overriding existing remote settings.\n\t *\n\t * When `true`, mutable drift on the selected branch (TTL, `protected` flag, compute\n\t * settings) is applied as actual mutations and the override-confirm prompt is\n\t * skipped. When `false` (default) the behaviour depends on whether `confirm` is\n\t * supplied:\n\t * - With `confirm`: the callback is asked whether to apply the override.\n\t * - Without `confirm`: drift is reported as a `PushConflictError` (legacy\n\t * non-interactive default — preserved so programmatic SDK callers don't\n\t * silently start mutating remote state).\n\t */\n\tupdateExisting?: boolean;\n\t/**\n\t * Auto-confirm pushing to a protected branch.\n\t *\n\t * When `true`, no protected-branch confirmation is asked. When `false` (default):\n\t * - With `confirm`: the callback is asked.\n\t * - Without `confirm`: the push proceeds (legacy SDK default).\n\t */\n\tallowProtectedBranch?: boolean;\n\t/**\n\t * Optional confirmation callback. Invoked once with a single context object before\n\t * any mutations run when the push needs confirmation: pushing to a protected\n\t * branch (unless `allowProtectedBranch` is `true`) and/or applying mutable drift\n\t * (unless `updateExisting` is `true`).\n\t *\n\t * Both prompts collapse into a single callback invocation when both apply, so the\n\t * CLI can render one combined \"are you sure?\" prompt.\n\t *\n\t * Resolves to `true` to proceed, `false` to abort with {@link PushAbortedError}.\n\t *\n\t * Never invoked on `dryRun`.\n\t */\n\tconfirm?: (context: PushConfirmContext) => boolean | Promise<boolean>;\n\t/**\n\t * Custom bundler for function source. Defaults to {@link buildFunctionBundle}\n\t * (esbuild). Inject your own to deploy functions without this package pulling\n\t * esbuild's native binary into your build — see {@link FunctionBundler}.\n\t */\n\tbundleFunction?: FunctionBundler;\n\t/**\n\t * When `true`, compute the full plan against the live remote state but **do not\n\t * execute any mutations**. The resulting `PushResult.applied` array records every\n\t * change that *would* run on a real push (with the same action / identifier / details\n\t * shape, so the existing CLI summary formatter just works), and conflicts are\n\t * reported instead of thrown.\n\t *\n\t * Used by `plan(config, branchId)` and any caller that wants a \"would this push do\n\t * something dangerous?\" check before invoking `pushConfig` for real.\n\t */\n\tdryRun?: boolean;\n}\n\n/**\n * Context handed to a {@link PushConfigOptions.confirm} callback. Both flags can be\n * `true` simultaneously when the push targets a protected branch *and* would override\n * existing settings — render a single combined prompt covering both reasons.\n */\nexport interface PushConfirmContext {\n\t/** Name of the target branch on Neon. */\n\tbranchName: string;\n\t/**\n\t * `true` when the target branch has the `protected` flag on Neon and the caller\n\t * did not pass `allowProtectedBranch: true`.\n\t */\n\tprotectedBranch: boolean;\n\t/**\n\t * `true` when the plan would override existing remote settings (TTL, `protected`\n\t * flag, compute settings on an existing endpoint) and the caller did not pass\n\t * `updateExisting: true`. Additive operations (enabling Neon Auth / Data API for\n\t * the first time) are **not** counted here — those are unambiguous and never\n\t * prompt.\n\t */\n\toverrideUpdates: boolean;\n}\n\n/**\n * Push a Neon branch policy to a specific project + branch.\n *\n * Filesystem- and env-agnostic: the caller supplies an already-validated `Config` object\n * (from `defineConfig` / `loadConfigFromFile`) and explicit `projectId` + `branch` in\n * `options`. `pushConfig` performs no `.neon` lookups and reads no `NEON_*` env vars except the API credential/host resolution documented on `apiKey`/`apiHost`.\n *\n * It will **not** create a project or branch — both must already exist on Neon.\n */\nexport async function pushConfig(\n\tconfig: Config,\n\toptions: PushConfigOptions,\n): Promise<PushResult> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\n\tconst dryRun = options.dryRun === true;\n\tconst updateExisting = options.updateExisting === true;\n\tconst allowProtectedBranch = options.allowProtectedBranch === true;\n\n\tconst remoteProject = await api.getProject(projectId);\n\n\tconst [branches, endpoints] = await Promise.all([\n\t\tapi.listBranches(remoteProject.id),\n\t\tapi.listEndpoints(remoteProject.id),\n\t]);\n\tconst branch = resolveRemoteBranch(options.branchId, branches);\n\tconst resolved = resolveConfig(config, {\n\t\tname: branch.name,\n\t\tid: branch.id,\n\t\texists: options.branchExists !== false,\n\t\t...(branch.parentId ? { parentId: branch.parentId } : {}),\n\t\tisDefault: branch.isDefault,\n\t\tisProtected: branch.protected,\n\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t});\n\tconst services = await resolveServiceState({\n\t\tapi,\n\t\tprojectId: remoteProject.id,\n\t\tbranch,\n\t\twantsAuth: resolved.authEnabled,\n\t\twantsDataApi: resolved.dataApiEnabled,\n\t});\n\tconst remote: RemoteState = {\n\t\tprojectId: remoteProject.id,\n\t\tbranch,\n\t\tendpoint: endpoints.find(\n\t\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t\t),\n\t\tservices,\n\t};\n\t// Only fetch Preview state when the policy actually uses it — and within that, only the\n\t// specific features the policy declares. So a policy that uses functions never probes\n\t// the AI Gateway, and `apply`/`plan` only fail on a Preview feature being unavailable\n\t// (404/503) when the policy actually asks for it.\n\tif (resolved.preview) {\n\t\tremote.preview = await resolvePreviewState({\n\t\t\tapi,\n\t\t\tprojectId: remoteProject.id,\n\t\t\tbranchId: branch.id,\n\t\t\tdesired: resolved.preview,\n\t\t});\n\t}\n\n\t// Always compute the plan with `updateExisting: true` so we can see what *would* be\n\t// overridden. The decision of whether to apply / prompt / fail is gated below using\n\t// the recorded steps.\n\tconst diff = diffConfig(resolved, remote, { updateExisting: true });\n\tconst overrideSteps = diff.plan.filter(isOverrideStep);\n\tconst needsOverrideConfirm = overrideSteps.length > 0 && !updateExisting;\n\tconst needsProtectedConfirm = branch.protected && !allowProtectedBranch;\n\n\tif (!dryRun && diff.conflicts.length > 0) {\n\t\tthrow new PushConflictError(diff.conflicts);\n\t}\n\n\tif (!dryRun && (needsOverrideConfirm || needsProtectedConfirm)) {\n\t\tif (options.confirm) {\n\t\t\tconst ok = await options.confirm({\n\t\t\t\tbranchName: branch.name,\n\t\t\t\tprotectedBranch: needsProtectedConfirm,\n\t\t\t\toverrideUpdates: needsOverrideConfirm,\n\t\t\t});\n\t\t\tif (!ok) {\n\t\t\t\tconst reasons: (\"protected-branch\" | \"override-updates\")[] = [];\n\t\t\t\tif (needsProtectedConfirm) reasons.push(\"protected-branch\");\n\t\t\t\tif (needsOverrideConfirm) reasons.push(\"override-updates\");\n\t\t\t\tthrow new PushAbortedError(branch.name, reasons);\n\t\t\t}\n\t\t} else if (needsOverrideConfirm) {\n\t\t\t// Legacy non-interactive fallback: surface the would-be drift as a\n\t\t\t// `PushConflictError` so programmatic callers that skipped both\n\t\t\t// `updateExisting` and `confirm` see the previous fail-fast behavior.\n\t\t\tconst legacy = diffConfig(resolved, remote, {\n\t\t\t\tupdateExisting: false,\n\t\t\t});\n\t\t\tthrow new PushConflictError(legacy.conflicts);\n\t\t}\n\t\t// Protected branch + no confirm callback: legacy default proceeds without\n\t\t// any extra check (no programmatic regression).\n\t}\n\n\tconst applied: AppliedChange[] = [\n\t\t{ kind: \"branch\", action: \"noop\", identifier: branch.name },\n\t];\n\n\tconst branchById = new Map(branches.map((b) => [b.id, b] as const));\n\tconst branchByName = new Map(branches.map((b) => [b.name, b] as const));\n\n\tfor (const step of diff.plan) {\n\t\tconst change = dryRun\n\t\t\t? synthesizeAppliedChange(step)\n\t\t\t: await applyStep(step, {\n\t\t\t\t\tapi,\n\t\t\t\t\tremoteProjectId: remoteProject.id,\n\t\t\t\t\tbranchById,\n\t\t\t\t\tbranchByName,\n\t\t\t\t\tbundleFunction:\n\t\t\t\t\t\toptions.bundleFunction ?? defaultBundleFunction,\n\t\t\t\t});\n\t\tapplied.push(change);\n\t}\n\n\tconst result: PushResult = {\n\t\tprojectId: remoteProject.id,\n\t\tbranchId: branch.id,\n\t\tbranchName: branch.name,\n\t\tdryRun,\n\t\tapplied,\n\t\tconflicts: diff.conflicts,\n\t};\n\tif (remoteProject.orgId) result.orgId = remoteProject.orgId;\n\treturn result;\n}\n\n/**\n * `update-*` plan steps mutate existing remote state. `enable-*` steps are additive (no\n * existing resource to override) and never trigger the override-confirm prompt.\n */\nfunction isOverrideStep(step: PlanStep): boolean {\n\treturn (\n\t\tstep.kind === \"update-branch-ttl\" ||\n\t\tstep.kind === \"update-branch-protected\" ||\n\t\tstep.kind === \"update-endpoint\"\n\t);\n}\n\n/**\n * Build an {@link AppliedChange} from a {@link PlanStep} without calling the Neon API.\n * Used by dry-run mode so callers see the same record shape they would on a live push,\n * just with no side effects. Identifiers are the branch names from the plan; any\n * sub-resource ids (`branchId`, `endpointId`) flow through unchanged when known.\n */\nfunction synthesizeAppliedChange(step: PlanStep): AppliedChange {\n\tswitch (step.kind) {\n\t\tcase \"update-branch-ttl\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: { field: \"ttl\", expiresAt: step.expiresAt },\n\t\t\t};\n\t\tcase \"update-branch-protected\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: { field: \"protected\", protected: step.protected },\n\t\t\t};\n\t\tcase \"update-endpoint\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: {\n\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\tendpointId: step.endpointId,\n\t\t\t\t\tsettings: step.settings,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"enable-auth\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"auth\",\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\t...(step.databaseName\n\t\t\t\t\t\t? { databaseName: step.databaseName }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"enable-data-api\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"dataApi\",\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tdatabaseName: step.databaseName,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"create-bucket\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `bucket:${step.bucketName}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tbucketName: step.bucketName,\n\t\t\t\t\taccessLevel: step.accessLevel,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"create-function\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tname: step.fn.name,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"deploy-function\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tsource: step.fn.source,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"enable-ai-gateway\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"aiGateway\",\n\t\t\t\tdetails: { branchName: step.branchName },\n\t\t\t};\n\t}\n}\n\nfunction createApiFromOptions(options: PushConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pushConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nfunction resolveRemoteBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst found = branches.find((b) => b.id === branchId);\n\tif (found) return found;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pushConfig: branch id ${JSON.stringify(branchId)} does not exist on the project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t\t\"Pass an existing branch id, or create the branch first with the neonctl CLI.\",\n\t\t].join(\" \"),\n\t\t{ details: { branchId, available: branches.map((b) => b.id) } },\n\t);\n}\n\n/**\n * Pre-fetch the current state of branch-scoped integrations on the selected branch.\n */\nasync function resolveServiceState(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranch: NeonBranchSnapshot;\n\twantsAuth: boolean;\n\twantsDataApi: boolean;\n}): Promise<RemoteServiceState> {\n\tconst { api, projectId, branch, wantsAuth, wantsDataApi } = args;\n\tif (!wantsAuth && !wantsDataApi) {\n\t\treturn {\n\t\t\tdatabaseName: \"neondb\",\n\t\t\tauthEnabled: false,\n\t\t\tdataApiEnabled: false,\n\t\t};\n\t}\n\n\tconst databaseName = await pickServiceDatabaseName(\n\t\tapi,\n\t\tprojectId,\n\t\tbranch.id,\n\t);\n\n\tconst [auth, dataApi] = await Promise.all([\n\t\twantsAuth\n\t\t\t? api.getNeonAuth(projectId, branch.id)\n\t\t\t: Promise.resolve(null),\n\t\twantsDataApi\n\t\t\t? api.getNeonDataApi(projectId, branch.id, databaseName)\n\t\t\t: Promise.resolve(null),\n\t]);\n\treturn {\n\t\tdatabaseName,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t};\n}\n\n/**\n * Pre-fetch the current state of branch-scoped Preview features (buckets, functions, AI\n * Gateway) so the diff can be computed additively. Only called when the policy has a\n * `preview` block.\n */\nasync function resolvePreviewState(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranchId: string;\n\tdesired: ResolvedPreviewConfig;\n}): Promise<RemotePreviewState> {\n\tconst { api, projectId, branchId, desired } = args;\n\t// Read only the Preview features the policy declares: undeclared features can never\n\t// produce a plan step (see diffConfig), so probing them is pure waste — and would make\n\t// `plan`/`apply` fail on a feature the user didn't ask for if it's unavailable in the\n\t// project/region. A declared-but-unavailable feature still throws (failing the push),\n\t// which is the intended signal to enable it first.\n\tconst [buckets, functions, aiGatewayEnabled] = await Promise.all([\n\t\tdesired.buckets.length > 0\n\t\t\t? api.listBranchBuckets(projectId, branchId)\n\t\t\t: Promise.resolve([]),\n\t\tdesired.functions.length > 0\n\t\t\t? api.listBranchFunctions(projectId, branchId)\n\t\t\t: Promise.resolve([]),\n\t\tdesired.aiGatewayEnabled\n\t\t\t? api.getAiGatewayEnabled(projectId, branchId)\n\t\t\t: Promise.resolve(false),\n\t]);\n\treturn { buckets, functions, aiGatewayEnabled };\n}\n\n/**\n * Resolve the database name for a Data API integration. Auto-pick when the branch has\n * exactly one database; otherwise fall back to Neon's default (`neondb`) so the call\n * stays useful even on branches with multiple databases — push doesn't have a way to\n * surface a \"pick one\" prompt the way `fetchEnv` does.\n */\nasync function pickServiceDatabaseName(\n\tapi: NeonApi,\n\tprojectId: string,\n\tbranchId: string,\n): Promise<string> {\n\tconst databases = await api.listBranchDatabases(projectId, branchId);\n\tif (databases.length === 1) return databases[0].name;\n\tconst neondb = databases.find((d) => d.name === \"neondb\");\n\tif (neondb) return neondb.name;\n\treturn databases[0]?.name ?? \"neondb\";\n}\n\ninterface ApplyContext {\n\tapi: NeonApi;\n\tremoteProjectId: string;\n\tbranchById: Map<string, NeonBranchSnapshot>;\n\tbranchByName: Map<string, NeonBranchSnapshot>;\n\tbundleFunction: FunctionBundler;\n}\n\nasync function applyStep(\n\tstep: PlanStep,\n\tctx: ApplyContext,\n): Promise<AppliedChange> {\n\tswitch (step.kind) {\n\t\tcase \"update-branch-ttl\": {\n\t\t\tconst updated = await ctx.api.updateBranch(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{\n\t\t\t\t\texpiresAt: step.expiresAt ?? null,\n\t\t\t\t},\n\t\t\t);\n\t\t\tctx.branchById.set(updated.id, updated);\n\t\t\tctx.branchByName.set(updated.name, updated);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: updated.name,\n\t\t\t\tdetails: { field: \"ttl\", expiresAt: step.expiresAt },\n\t\t\t};\n\t\t}\n\t\tcase \"update-branch-protected\": {\n\t\t\tconst updated = await ctx.api.updateBranch(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{ protected: step.protected },\n\t\t\t);\n\t\t\tctx.branchById.set(updated.id, updated);\n\t\t\tctx.branchByName.set(updated.name, updated);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: updated.name,\n\t\t\t\tdetails: { field: \"protected\", protected: step.protected },\n\t\t\t};\n\t\t}\n\t\tcase \"update-endpoint\": {\n\t\t\tconst updated = await ctx.api.updateEndpoint(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.endpointId,\n\t\t\t\tstep.settings,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: {\n\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\tendpointId: updated.id,\n\t\t\t\t\tsettings: step.settings,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"enable-auth\": {\n\t\t\tawait ctx.api.enableNeonAuth(ctx.remoteProjectId, step.branchId, {\n\t\t\t\t...(step.databaseName\n\t\t\t\t\t? { databaseName: step.databaseName }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"auth\",\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\t...(step.databaseName\n\t\t\t\t\t\t? { databaseName: step.databaseName }\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"enable-data-api\": {\n\t\t\tawait ctx.api.enableProjectBranchDataApi(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.databaseName,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"dataApi\",\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tdatabaseName: step.databaseName,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"create-bucket\": {\n\t\t\tawait ctx.api.createBranchBucket(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{ name: step.bucketName, accessLevel: step.accessLevel },\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `bucket:${step.bucketName}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tbucketName: step.bucketName,\n\t\t\t\t\taccessLevel: step.accessLevel,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"create-function\": {\n\t\t\tawait ctx.api.createBranchFunction(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{ slug: step.fn.slug, name: step.fn.name },\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tname: step.fn.name,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"deploy-function\": {\n\t\t\tconst bundle = await ctx.bundleFunction(step.fn);\n\t\t\tconst deployment = await ctx.api.deployBranchFunction(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.fn.slug,\n\t\t\t\t{\n\t\t\t\t\tbundle,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t\tenvironment: step.fn.env,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbranchName: step.branchName,\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tsource: step.fn.source,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t\tdeploymentId: deployment.id,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"enable-ai-gateway\": {\n\t\t\tawait ctx.api.enableAiGateway(ctx.remoteProjectId, step.branchId);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"aiGateway\",\n\t\t\t\tdetails: { branchName: step.branchName },\n\t\t\t};\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"push-config.js","names":[],"sources":["../../src/lib/push-config.ts"],"sourcesContent":["import {\n\ttype AppliedChange,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tdiffConfig,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype PlanStep,\n\tPlatformError,\n\tPushAbortedError,\n\tPushConflictError,\n\ttype PushResult,\n\ttype RemotePreviewState,\n\ttype RemoteServiceState,\n\ttype RemoteState,\n\ttype ResolvedFunctionConfig,\n\ttype ResolvedPreviewConfig,\n\tresolveConfig,\n} from \"@neondatabase/config\";\nimport type { FunctionBundler } from \"./function-bundle.js\";\n\n/**\n * Default function bundler (esbuild), loaded lazily so that `buildFunctionBundle`\n * — and the esbuild it pulls in — only enters the module graph when a deploy\n * actually needs it AND no custom `bundleFunction` was injected. A consumer that\n * injects its own bundler never triggers this import, so esbuild can be dropped\n * from their build entirely.\n */\nconst defaultBundleFunction: FunctionBundler = async (\n\tfn: ResolvedFunctionConfig,\n): Promise<Uint8Array> => {\n\tconst { buildFunctionBundle } = await import(\"./function-bundle.js\");\n\treturn buildFunctionBundle(fn);\n};\n\nexport interface PushConfigOptions {\n\t/**\n\t * Neon project id. **Required** — the management API addresses every branch through\n\t * its project, so there is no way to push without it. `pushConfig` never creates a\n\t * project; resolve the id yourself (e.g. via neonctl) and pass it in.\n\t */\n\tprojectId: string;\n\t/**\n\t * Neon branch id (`br-…`). **Required.** `pushConfig` never creates a branch — it must\n\t * already exist on the project. Resolve names to ids before calling.\n\t */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. Ignored when `api` is supplied. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/**\n\t * Inject a custom NeonApi adapter. Primarily used by tests; production callers can rely\n\t * on the default real adapter built from `apiKey`.\n\t */\n\tapi?: NeonApi;\n\t/**\n\t * Whether to evaluate the policy as if the target branch **already exists** (the value of\n\t * `branch.exists` passed to the `defineConfig((branch) => …)` closure). Defaults to `true`.\n\t *\n\t * Set to `false` to evaluate the policy as a **branch creation** — used by\n\t * {@link createBranch} right after it provisions a new branch, so creation-time tuning\n\t * gated on `!branch.exists` (TTL, compute settings, `parent`) actually resolves instead of\n\t * hitting the \"existing branch, leave as-is\" path. Only affects policy evaluation; the\n\t * branch must still physically exist on Neon (`pushConfig` never creates one).\n\t */\n\tbranchExists?: boolean;\n\t/**\n\t * Auto-confirm overriding existing remote settings.\n\t *\n\t * When `true`, mutable drift on the selected branch (TTL, `protected` flag, compute\n\t * settings) is applied as actual mutations and the override-confirm prompt is\n\t * skipped. When `false` (default) the behaviour depends on whether `confirm` is\n\t * supplied:\n\t * - With `confirm`: the callback is asked whether to apply the override.\n\t * - Without `confirm`: drift is reported as a `PushConflictError` (legacy\n\t * non-interactive default — preserved so programmatic SDK callers don't\n\t * silently start mutating remote state).\n\t */\n\tupdateExisting?: boolean;\n\t/**\n\t * Auto-confirm pushing to a protected branch.\n\t *\n\t * When `true`, no protected-branch confirmation is asked. When `false` (default):\n\t * - With `confirm`: the callback is asked.\n\t * - Without `confirm`: the push proceeds (legacy SDK default).\n\t */\n\tallowProtectedBranch?: boolean;\n\t/**\n\t * Optional confirmation callback. Invoked once with a single context object before\n\t * any mutations run when the push needs confirmation: pushing to a protected\n\t * branch (unless `allowProtectedBranch` is `true`) and/or applying mutable drift\n\t * (unless `updateExisting` is `true`).\n\t *\n\t * Both prompts collapse into a single callback invocation when both apply, so the\n\t * CLI can render one combined \"are you sure?\" prompt.\n\t *\n\t * Resolves to `true` to proceed, `false` to abort with {@link PushAbortedError}.\n\t *\n\t * Never invoked on `dryRun`.\n\t */\n\tconfirm?: (context: PushConfirmContext) => boolean | Promise<boolean>;\n\t/**\n\t * Custom bundler for function source. Defaults to {@link buildFunctionBundle}\n\t * (esbuild). Inject your own to deploy functions without this package pulling\n\t * esbuild's native binary into your build — see {@link FunctionBundler}.\n\t */\n\tbundleFunction?: FunctionBundler;\n\t/**\n\t * When `true`, compute the full plan against the live remote state but **do not\n\t * execute any mutations**. The resulting `PushResult.applied` array records every\n\t * change that *would* run on a real push (with the same action / identifier / details\n\t * shape, so the existing CLI summary formatter just works), and conflicts are\n\t * reported instead of thrown.\n\t *\n\t * Used by `plan(config, branchId)` and any caller that wants a \"would this push do\n\t * something dangerous?\" check before invoking `pushConfig` for real.\n\t */\n\tdryRun?: boolean;\n}\n\n/**\n * Context handed to a {@link PushConfigOptions.confirm} callback. Both flags can be\n * `true` simultaneously when the push targets a protected branch *and* would override\n * existing settings — render a single combined prompt covering both reasons.\n */\nexport interface PushConfirmContext {\n\t/** Name of the target branch on Neon. */\n\tbranchName: string;\n\t/**\n\t * `true` when the target branch has the `protected` flag on Neon and the caller\n\t * did not pass `allowProtectedBranch: true`.\n\t */\n\tprotectedBranch: boolean;\n\t/**\n\t * `true` when the plan would override existing remote settings (TTL, `protected`\n\t * flag, compute settings on an existing endpoint) and the caller did not pass\n\t * `updateExisting: true`. Additive operations (enabling Neon Auth / Data API for\n\t * the first time) are **not** counted here — those are unambiguous and never\n\t * prompt.\n\t */\n\toverrideUpdates: boolean;\n}\n\n/**\n * Push a Neon branch policy to a specific project + branch.\n *\n * Filesystem- and env-agnostic: the caller supplies an already-validated `Config` object\n * (from `defineConfig` / `loadConfigFromFile`) and explicit `projectId` + `branch` in\n * `options`. `pushConfig` performs no `.neon` lookups and reads no `NEON_*` env vars except the API credential/host resolution documented on `apiKey`/`apiHost`.\n *\n * It will **not** create a project or branch — both must already exist on Neon.\n */\nexport async function pushConfig(\n\tconfig: Config,\n\toptions: PushConfigOptions,\n): Promise<PushResult> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\n\tconst dryRun = options.dryRun === true;\n\tconst updateExisting = options.updateExisting === true;\n\tconst allowProtectedBranch = options.allowProtectedBranch === true;\n\n\tconst remoteProject = await api.getProject(projectId);\n\n\tconst [branches, endpoints] = await Promise.all([\n\t\tapi.listBranches(remoteProject.id),\n\t\tapi.listEndpoints(remoteProject.id),\n\t]);\n\tconst branch = resolveRemoteBranch(options.branchId, branches);\n\tconst resolved = resolveConfig(config, {\n\t\tname: branch.name,\n\t\tid: branch.id,\n\t\texists: options.branchExists !== false,\n\t\t...(branch.parentId ? { parentId: branch.parentId } : {}),\n\t\tisDefault: branch.isDefault,\n\t\tisProtected: branch.protected,\n\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t});\n\tconst services = await resolveServiceState({\n\t\tapi,\n\t\tprojectId: remoteProject.id,\n\t\tbranch,\n\t\twantsAuth: resolved.authEnabled,\n\t\twantsDataApi: resolved.dataApiEnabled,\n\t});\n\tconst remote: RemoteState = {\n\t\tprojectId: remoteProject.id,\n\t\tbranch,\n\t\tendpoint: endpoints.find(\n\t\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t\t),\n\t\tservices,\n\t};\n\t// Only fetch Preview state when the policy actually uses it — and within that, only the\n\t// specific features the policy declares. So a policy that uses functions never probes\n\t// the AI Gateway, and `apply`/`plan` only fail on a Preview feature being unavailable\n\t// (404/503) when the policy actually asks for it.\n\tif (resolved.preview) {\n\t\tremote.preview = await resolvePreviewState({\n\t\t\tapi,\n\t\t\tprojectId: remoteProject.id,\n\t\t\tbranchId: branch.id,\n\t\t\tdesired: resolved.preview,\n\t\t});\n\t}\n\n\t// Always compute the plan with `updateExisting: true` so we can see what *would* be\n\t// overridden. The decision of whether to apply / prompt / fail is gated below using\n\t// the recorded steps.\n\tconst diff = diffConfig(resolved, remote, { updateExisting: true });\n\tconst overrideSteps = diff.plan.filter(isOverrideStep);\n\tconst needsOverrideConfirm = overrideSteps.length > 0 && !updateExisting;\n\tconst needsProtectedConfirm = branch.protected && !allowProtectedBranch;\n\n\tif (!dryRun && diff.conflicts.length > 0) {\n\t\tthrow new PushConflictError(diff.conflicts);\n\t}\n\n\tif (!dryRun && (needsOverrideConfirm || needsProtectedConfirm)) {\n\t\tif (options.confirm) {\n\t\t\tconst ok = await options.confirm({\n\t\t\t\tbranchName: branch.name,\n\t\t\t\tprotectedBranch: needsProtectedConfirm,\n\t\t\t\toverrideUpdates: needsOverrideConfirm,\n\t\t\t});\n\t\t\tif (!ok) {\n\t\t\t\tconst reasons: (\"protected-branch\" | \"override-updates\")[] = [];\n\t\t\t\tif (needsProtectedConfirm) reasons.push(\"protected-branch\");\n\t\t\t\tif (needsOverrideConfirm) reasons.push(\"override-updates\");\n\t\t\t\tthrow new PushAbortedError(branch.name, reasons);\n\t\t\t}\n\t\t} else if (needsOverrideConfirm) {\n\t\t\t// Legacy non-interactive fallback: surface the would-be drift as a\n\t\t\t// `PushConflictError` so programmatic callers that skipped both\n\t\t\t// `updateExisting` and `confirm` see the previous fail-fast behavior.\n\t\t\tconst legacy = diffConfig(resolved, remote, {\n\t\t\t\tupdateExisting: false,\n\t\t\t});\n\t\t\tthrow new PushConflictError(legacy.conflicts);\n\t\t}\n\t\t// Protected branch + no confirm callback: legacy default proceeds without\n\t\t// any extra check (no programmatic regression).\n\t}\n\n\tconst applied: AppliedChange[] = [\n\t\t{ kind: \"branch\", action: \"noop\", identifier: branch.name },\n\t];\n\n\tconst branchById = new Map(branches.map((b) => [b.id, b] as const));\n\tconst branchByName = new Map(branches.map((b) => [b.name, b] as const));\n\n\tfor (const step of diff.plan) {\n\t\tconst change = dryRun\n\t\t\t? synthesizeAppliedChange(step)\n\t\t\t: await applyStep(step, {\n\t\t\t\t\tapi,\n\t\t\t\t\tremoteProjectId: remoteProject.id,\n\t\t\t\t\tbranchById,\n\t\t\t\t\tbranchByName,\n\t\t\t\t\tbundleFunction:\n\t\t\t\t\t\toptions.bundleFunction ?? defaultBundleFunction,\n\t\t\t\t});\n\t\tapplied.push(change);\n\t}\n\n\t// Surface each deployed function's invocation URL on its applied change so callers\n\t// (e.g. neonctl) can show users where to call it right after a push.\n\tawait enrichFunctionInvocationUrls({\n\t\tapi,\n\t\tprojectId: remoteProject.id,\n\t\tbranchId: branch.id,\n\t\tplan: diff.plan,\n\t\tapplied,\n\t\tpreview: remote.preview,\n\t\tdryRun,\n\t});\n\n\tconst result: PushResult = {\n\t\tprojectId: remoteProject.id,\n\t\tbranchId: branch.id,\n\t\tbranchName: branch.name,\n\t\tdryRun,\n\t\tapplied,\n\t\tconflicts: diff.conflicts,\n\t};\n\tif (remoteProject.orgId) result.orgId = remoteProject.orgId;\n\treturn result;\n}\n\n/**\n * `update-*` plan steps mutate existing remote state. `enable-*` steps are additive (no\n * existing resource to override) and never trigger the override-confirm prompt.\n */\nfunction isOverrideStep(step: PlanStep): boolean {\n\treturn (\n\t\tstep.kind === \"update-branch-ttl\" ||\n\t\tstep.kind === \"update-branch-protected\" ||\n\t\tstep.kind === \"update-endpoint\"\n\t);\n}\n\n/**\n * Build an {@link AppliedChange} from a {@link PlanStep} without calling the Neon API.\n * Used by dry-run mode so callers see the same record shape they would on a live push,\n * just with no side effects. Identifiers are the branch names from the plan; any\n * sub-resource ids (`branchId`, `endpointId`) flow through unchanged when known.\n */\nfunction synthesizeAppliedChange(step: PlanStep): AppliedChange {\n\tswitch (step.kind) {\n\t\tcase \"update-branch-ttl\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: { field: \"ttl\", expiresAt: step.expiresAt },\n\t\t\t};\n\t\tcase \"update-branch-protected\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: { field: \"protected\", protected: step.protected },\n\t\t\t};\n\t\tcase \"update-endpoint\":\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: {\n\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\tendpointId: step.endpointId,\n\t\t\t\t\tsettings: step.settings,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"enable-auth\":\n\t\t\t// Pure branch on/off toggle: the target branch is redundant (same on\n\t\t\t// every row) and the database is auto-derived, not policy-chosen — so\n\t\t\t// there is nothing meaningful to surface in the change summary.\n\t\t\treturn { kind: \"service\", action: \"create\", identifier: \"auth\" };\n\t\tcase \"enable-data-api\":\n\t\t\treturn { kind: \"service\", action: \"create\", identifier: \"dataApi\" };\n\t\tcase \"create-bucket\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `bucket:${step.bucketName}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbucketName: step.bucketName,\n\t\t\t\t\taccessLevel: step.accessLevel,\n\t\t\t\t},\n\t\t\t};\n\t\tcase \"deploy-function\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\t// The first deployment creates the function; a later one updates it.\n\t\t\t\taction: step.functionExists ? \"update\" : \"create\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tsource: step.fn.source,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t},\n\t\t\t};\n\t}\n}\n\nfunction createApiFromOptions(options: PushConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pushConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nfunction resolveRemoteBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst found = branches.find((b) => b.id === branchId);\n\tif (found) return found;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pushConfig: branch id ${JSON.stringify(branchId)} does not exist on the project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t\t\"Pass an existing branch id, or create the branch first with the neonctl CLI.\",\n\t\t].join(\" \"),\n\t\t{ details: { branchId, available: branches.map((b) => b.id) } },\n\t);\n}\n\n/**\n * Pre-fetch the current state of branch-scoped integrations on the selected branch.\n */\nasync function resolveServiceState(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranch: NeonBranchSnapshot;\n\twantsAuth: boolean;\n\twantsDataApi: boolean;\n}): Promise<RemoteServiceState> {\n\tconst { api, projectId, branch, wantsAuth, wantsDataApi } = args;\n\tif (!wantsAuth && !wantsDataApi) {\n\t\treturn {\n\t\t\tdatabaseName: \"neondb\",\n\t\t\tauthEnabled: false,\n\t\t\tdataApiEnabled: false,\n\t\t};\n\t}\n\n\tconst databaseName = await pickServiceDatabaseName(\n\t\tapi,\n\t\tprojectId,\n\t\tbranch.id,\n\t);\n\n\tconst [auth, dataApi] = await Promise.all([\n\t\twantsAuth\n\t\t\t? api.getNeonAuth(projectId, branch.id)\n\t\t\t: Promise.resolve(null),\n\t\twantsDataApi\n\t\t\t? api.getNeonDataApi(projectId, branch.id, databaseName)\n\t\t\t: Promise.resolve(null),\n\t]);\n\treturn {\n\t\tdatabaseName,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t};\n}\n\n/**\n * Pre-fetch the current state of branch-scoped Preview features (buckets, functions) so the\n * diff can be computed additively. Only called when the policy has a `preview` block.\n *\n * The AI Gateway is not probed: it is always available (credential-gated, not per-branch\n * provisioned), so `preview.aiGateway` produces no plan step — it only drives the branch\n * credential's `ai_gateway:invoke` scope and the gateway env vars (`@neondatabase/env`).\n */\nasync function resolvePreviewState(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranchId: string;\n\tdesired: ResolvedPreviewConfig;\n}): Promise<RemotePreviewState> {\n\tconst { api, projectId, branchId, desired } = args;\n\t// Read only the Preview features the policy declares: undeclared features can never\n\t// produce a plan step (see diffConfig), so probing them is pure waste — and would make\n\t// `plan`/`apply` fail on a feature the user didn't ask for if it's unavailable in the\n\t// project/region. A declared-but-unavailable feature still throws (failing the push),\n\t// which is the intended signal to enable it first.\n\tconst [buckets, functions] = await Promise.all([\n\t\tdesired.buckets.length > 0\n\t\t\t? api.listBranchBuckets(projectId, branchId)\n\t\t\t: Promise.resolve([]),\n\t\tdesired.functions.length > 0\n\t\t\t? api.listBranchFunctions(projectId, branchId)\n\t\t\t: Promise.resolve([]),\n\t]);\n\treturn { buckets, functions };\n}\n\n/**\n * Resolve the database name for a Data API integration. Auto-pick when the branch has\n * exactly one database; otherwise fall back to Neon's default (`neondb`) so the call\n * stays useful even on branches with multiple databases — push doesn't have a way to\n * surface a \"pick one\" prompt the way `fetchEnv` does.\n */\nasync function pickServiceDatabaseName(\n\tapi: NeonApi,\n\tprojectId: string,\n\tbranchId: string,\n): Promise<string> {\n\tconst databases = await api.listBranchDatabases(projectId, branchId);\n\tif (databases.length === 1) return databases[0].name;\n\tconst neondb = databases.find((d) => d.name === \"neondb\");\n\tif (neondb) return neondb.name;\n\treturn databases[0]?.name ?? \"neondb\";\n}\n\ninterface ApplyContext {\n\tapi: NeonApi;\n\tremoteProjectId: string;\n\tbranchById: Map<string, NeonBranchSnapshot>;\n\tbranchByName: Map<string, NeonBranchSnapshot>;\n\tbundleFunction: FunctionBundler;\n}\n\nasync function applyStep(\n\tstep: PlanStep,\n\tctx: ApplyContext,\n): Promise<AppliedChange> {\n\tswitch (step.kind) {\n\t\tcase \"update-branch-ttl\": {\n\t\t\tconst updated = await ctx.api.updateBranch(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{\n\t\t\t\t\texpiresAt: step.expiresAt ?? null,\n\t\t\t\t},\n\t\t\t);\n\t\t\tctx.branchById.set(updated.id, updated);\n\t\t\tctx.branchByName.set(updated.name, updated);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: updated.name,\n\t\t\t\tdetails: { field: \"ttl\", expiresAt: step.expiresAt },\n\t\t\t};\n\t\t}\n\t\tcase \"update-branch-protected\": {\n\t\t\tconst updated = await ctx.api.updateBranch(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{ protected: step.protected },\n\t\t\t);\n\t\t\tctx.branchById.set(updated.id, updated);\n\t\t\tctx.branchByName.set(updated.name, updated);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: updated.name,\n\t\t\t\tdetails: { field: \"protected\", protected: step.protected },\n\t\t\t};\n\t\t}\n\t\tcase \"update-endpoint\": {\n\t\t\tconst updated = await ctx.api.updateEndpoint(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.endpointId,\n\t\t\t\tstep.settings,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"branch\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: step.branchName,\n\t\t\t\tdetails: {\n\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\tendpointId: updated.id,\n\t\t\t\t\tsettings: step.settings,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"enable-auth\": {\n\t\t\tawait ctx.api.enableNeonAuth(ctx.remoteProjectId, step.branchId, {\n\t\t\t\t...(step.databaseName\n\t\t\t\t\t? { databaseName: step.databaseName }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"auth\",\n\t\t\t};\n\t\t}\n\t\tcase \"enable-data-api\": {\n\t\t\tawait ctx.api.enableProjectBranchDataApi(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.databaseName,\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: \"dataApi\",\n\t\t\t};\n\t\t}\n\t\tcase \"create-bucket\": {\n\t\t\tawait ctx.api.createBranchBucket(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\t{ name: step.bucketName, accessLevel: step.accessLevel },\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"create\",\n\t\t\t\tidentifier: `bucket:${step.bucketName}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tbucketName: step.bucketName,\n\t\t\t\t\taccessLevel: step.accessLevel,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\tcase \"deploy-function\": {\n\t\t\tconst bundle = await ctx.bundleFunction(step.fn);\n\t\t\t// Neon creates the function on its first deployment — there is no separate\n\t\t\t// create call — so a single deploy both creates (when absent) and ships code.\n\t\t\tconst deployment = await ctx.api.deployBranchFunction(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.fn.slug,\n\t\t\t\t{\n\t\t\t\t\tbundle,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t\tenvironment: step.fn.env,\n\t\t\t\t},\n\t\t\t);\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: step.functionExists ? \"update\" : \"create\",\n\t\t\t\tidentifier: `function:${step.fn.slug}`,\n\t\t\t\tdetails: {\n\t\t\t\t\tslug: step.fn.slug,\n\t\t\t\t\tsource: step.fn.source,\n\t\t\t\t\truntime: step.fn.runtime,\n\t\t\t\t\tdeploymentId: deployment.id,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n}\n\n/**\n * Add each deployed function's invocation URL to its applied-change `details` so callers\n * (e.g. neonctl) can show users where to call the function right after a push.\n *\n * The URL is read from the preview snapshot already fetched for the diff, which lists every\n * existing function with its `invocationUrl`. A function created by its *first* deployment in\n * this push is not in that snapshot, so when one is present we re-list the branch's functions\n * once to learn its freshly-minted URL. Skipped on dry-run (nothing was created) and\n * best-effort otherwise: a failed re-list omits the URL rather than failing a push that has\n * already applied.\n */\nasync function enrichFunctionInvocationUrls(args: {\n\tapi: NeonApi;\n\tprojectId: string;\n\tbranchId: string;\n\tplan: PlanStep[];\n\tapplied: AppliedChange[];\n\tpreview: RemotePreviewState | undefined;\n\tdryRun: boolean;\n}): Promise<void> {\n\tconst { api, projectId, branchId, plan, applied, preview, dryRun } = args;\n\tconst deployedSlugs = plan.flatMap((step) =>\n\t\tstep.kind === \"deploy-function\" ? [step.fn.slug] : [],\n\t);\n\tif (deployedSlugs.length === 0) return;\n\n\tconst urlBySlug = new Map<string, string>(\n\t\t(preview?.functions ?? []).map(\n\t\t\t(fn) => [fn.slug, fn.invocationUrl] as const,\n\t\t),\n\t);\n\n\t// A first-time deploy creates the function, so its URL isn't in the pre-fetch; re-list\n\t// once when any deployed slug is still missing a URL.\n\tconst hasMissingUrl = deployedSlugs.some((slug) => !urlBySlug.has(slug));\n\tif (hasMissingUrl && !dryRun) {\n\t\ttry {\n\t\t\tfor (const fn of await api.listBranchFunctions(\n\t\t\t\tprojectId,\n\t\t\t\tbranchId,\n\t\t\t)) {\n\t\t\t\turlBySlug.set(fn.slug, fn.invocationUrl);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Push already succeeded; surface what we can rather than failing here.\n\t\t}\n\t}\n\n\tfor (const change of applied) {\n\t\tconst slug = functionSlugFromIdentifier(change.identifier);\n\t\tif (slug === undefined) continue;\n\t\tconst invocationUrl = urlBySlug.get(slug);\n\t\tif (invocationUrl === undefined) continue;\n\t\tchange.details = { ...change.details, invocationUrl };\n\t}\n}\n\n/** Pull the function slug out of a `function:<slug>` applied-change identifier. */\nfunction functionSlugFromIdentifier(identifier: string): string | undefined {\n\tconst prefix = \"function:\";\n\treturn identifier.startsWith(prefix)\n\t\t? identifier.slice(prefix.length)\n\t\t: undefined;\n}\n"],"mappings":"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"}
package/dist/v1.d.ts CHANGED
@@ -2,5 +2,5 @@ import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
2
2
  import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
3
3
  import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
4
4
  import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
5
- import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, loadConfigFromFile } from "@neondatabase/config";
6
- export { type AppliedChange, type ApplyOptions, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, ErrorCode, type FunctionBundler, type LoadConfigOptions, MissingContextError, type NeonApi, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, loadConfigFromFile, plan, pullConfig, pushConfig };
5
+ import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "@neondatabase/config";
6
+ export { type AppliedChange, type ApplyOptions, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, ErrorCode, type FunctionBundler, type LoadConfigOptions, MissingContextError, type NeonApi, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
package/dist/v1.js CHANGED
@@ -2,5 +2,5 @@ import { buildFunctionBundle } from "./lib/function-bundle.js";
2
2
  import { pullConfig } from "./lib/pull-config.js";
3
3
  import { pushConfig } from "./lib/push-config.js";
4
4
  import { apply, createBranch, inspect, plan } from "./lib/operations.js";
5
- import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, loadConfigFromFile } from "@neondatabase/config";
6
- export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, loadConfigFromFile, plan, pullConfig, pushConfig };
5
+ import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "@neondatabase/config";
6
+ export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neondatabase/config-runtime",
3
- "version": "0.4.2",
3
+ "version": "0.7.0",
4
4
  "description": "Imperative runtime for @neondatabase/config: inspect/plan/apply a neon.ts policy against the Neon API and bundle + deploy Neon Functions. Pulls in esbuild; import this from CLIs and CI, not from neon.ts.",
5
5
  "keywords": [
6
6
  "neon",
@@ -52,7 +52,7 @@
52
52
  "dependencies": {
53
53
  "esbuild": "^0.25.0",
54
54
  "fflate": "^0.8.2",
55
- "@neondatabase/config": "0.4.2"
55
+ "@neondatabase/config": "0.7.0"
56
56
  },
57
57
  "engines": {
58
58
  "node": ">=22"