@neon/config-runtime 1.0.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/lib/function-bundle.d.ts +9 -42
- package/dist/lib/function-bundle.d.ts.map +1 -1
- package/dist/lib/function-bundle.js +29 -46
- package/dist/lib/function-bundle.js.map +1 -1
- package/dist/lib/function-source.d.ts +16 -0
- package/dist/lib/function-source.d.ts.map +1 -0
- package/dist/lib/function-source.js +87 -0
- package/dist/lib/function-source.js.map +1 -0
- package/dist/lib/function-zip.d.ts +7 -0
- package/dist/lib/function-zip.d.ts.map +1 -0
- package/dist/lib/function-zip.js +25 -0
- package/dist/lib/function-zip.js.map +1 -0
- package/dist/lib/native-packages.js +1 -1
- package/dist/lib/operations.d.ts +2 -6
- package/dist/lib/operations.d.ts.map +1 -1
- package/dist/lib/operations.js.map +1 -1
- package/dist/lib/push-config.d.ts +1 -5
- package/dist/lib/push-config.d.ts.map +1 -1
- package/dist/lib/push-config.js +14 -13
- package/dist/lib/push-config.js.map +1 -1
- package/dist/v1.d.ts +4 -2
- package/dist/v1.js +3 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -17,7 +17,9 @@ npm install @neon/config-runtime
|
|
|
17
17
|
- `inspect` / `plan` / `apply` — read the current branch state, diff a policy against it, and apply the changes.
|
|
18
18
|
- `pullConfig` / `pushConfig` — lower-level pull/push primitives.
|
|
19
19
|
- `createBranch` — create a branch to target.
|
|
20
|
-
- `buildFunctionBundle` —
|
|
20
|
+
- `buildFunctionBundle` — ZIP a function for deploy, honoring `bundler`. Omit / `"esbuild"`: a file `source` is the entry; a directory is searched for `index.ts`, then `index.js`, then `index.mjs`. `"none"`: zip `source` as-is. An inline function: zip the file map it returns. On the esbuild path, `externalPackages` are passed to esbuild's `external`, and unless an entry sets `includeFiles: false` the package's real files are staged into the archive. Reports a bundled-in native dependency that was never declared, as a warning.
|
|
21
|
+
- `bundleAsIs` — read a prebuilt directory or `index.mjs` / `index.js` file into a file map without loading esbuild. The packaged CLI uses this for `bundler: "none"` / `--no-bundle` so it never pulls esbuild into its snapshot. `buildFunctionBundle` calls it for `"none"`; call it directly when you need the file map (for example to write files for `neon dev`) rather than a ZIP.
|
|
22
|
+
- `resolveEsbuildEntry` — resolve a file or directory `source` to the esbuild entry path.
|
|
21
23
|
- `traceNativePackages` — install the packages a function stages for the runtime target (`RUNTIME_TARGET`: linux-arm64, glibc) into a throwaway directory, trace the files they reach, and return them keyed by archive path with the `node_modules` layout preserved. Called by `buildFunctionBundle`; exported for a CLI that supplies its own bundler.
|
|
22
24
|
- `findUndeclaredNativePackages` / `describeNativeFinding` — advisory detection of a native package that was bundled in without being declared, and the report text for it. Evidence only: it cannot prove the code path is reached, so it must never fail a deploy.
|
|
23
25
|
- `assertZipWithinLimits` — check a finished archive against the build service's size limits (`DEFAULT_ARCHIVE_LIMITS`), with an error naming the largest files.
|
|
@@ -27,3 +29,5 @@ import { inspect, plan, apply } from "@neon/config-runtime/v1";
|
|
|
27
29
|
```
|
|
28
30
|
|
|
29
31
|
See [`@neon/config`](../config) for authoring the `neon.ts` policy these operate on.
|
|
32
|
+
`apply` / `pushConfig` treat explicit `dataApi: false` as a Data API delete (override).
|
|
33
|
+
Omitting `dataApi` leaves an existing integration alone.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ArchiveLimits, DEFAULT_ARCHIVE_LIMITS, NativeTraceDeps, NativeTraceResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, assertZipWithinLimits, enforceLimits, traceNativePackages } from "./lib/native-packages.js";
|
|
2
2
|
import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
|
|
3
|
+
import { bundleAsIs, resolveEsbuildEntry } from "./lib/function-source.js";
|
|
4
|
+
import { zipFunctionBundle } from "./lib/function-zip.js";
|
|
3
5
|
import { NativeEvidence, NativeFinding, describeNativeFinding, findUndeclaredNativePackages } from "./lib/native-detect.js";
|
|
4
6
|
import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
|
|
5
7
|
import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
|
|
6
8
|
import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
7
|
-
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
8
|
-
export { AppliedChange, ApplyOptions, ArchiveLimits, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, DEFAULT_ARCHIVE_LIMITS, ErrorCode, FunctionBundler, LoadConfigOptions, MissingContextError, NativeEvidence, NativeFinding, NativeTraceDeps, NativeTraceResult, NeonApi, PartialBranchCreateError, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, traceNativePackages };
|
|
9
|
+
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, FunctionBundle, FunctionBundlerInput, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
10
|
+
export { AppliedChange, ApplyOptions, ArchiveLimits, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, DEFAULT_ARCHIVE_LIMITS, ErrorCode, FunctionBundle, FunctionBundler, FunctionBundlerInput, LoadConfigOptions, MissingContextError, NativeEvidence, NativeFinding, NativeTraceDeps, NativeTraceResult, NeonApi, PartialBranchCreateError, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { bundleAsIs, resolveEsbuildEntry } from "./lib/function-source.js";
|
|
1
2
|
import { DEFAULT_ARCHIVE_LIMITS, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, assertZipWithinLimits, enforceLimits, traceNativePackages } from "./lib/native-packages.js";
|
|
3
|
+
import { zipFunctionBundle } from "./lib/function-zip.js";
|
|
2
4
|
import { describeNativeFinding, findUndeclaredNativePackages } from "./lib/native-detect.js";
|
|
3
5
|
import { buildFunctionBundle } from "./lib/function-bundle.js";
|
|
4
6
|
import { pullConfig } from "./lib/pull-config.js";
|
|
5
7
|
import { pushConfig } from "./lib/push-config.js";
|
|
6
8
|
import { apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
7
9
|
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
8
|
-
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, traceNativePackages };
|
|
10
|
+
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle };
|
|
@@ -2,15 +2,7 @@ import { NativeTraceDeps } from "./native-packages.js";
|
|
|
2
2
|
import { ResolvedFunctionConfig } from "@neon/config";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/function-bundle.d.ts
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Builds the deployable ZIP bundle for a single function. The default
|
|
8
|
-
* implementation ({@link buildFunctionBundle}) shells out to esbuild, but
|
|
9
|
-
* `pushConfig` / `apply` accept a custom bundler so a consumer that can't ship
|
|
10
|
-
* esbuild's native binary (e.g. a single-file CLI) can supply its own — a WASM
|
|
11
|
-
* build, an esbuild binary on PATH, etc. — without this package dragging esbuild
|
|
12
|
-
* into their bundle.
|
|
13
|
-
*/
|
|
5
|
+
/** Deploy-level ZIP. `@neon/config`'s FunctionBundler returns a file map. */
|
|
14
6
|
type FunctionBundler = (fn: ResolvedFunctionConfig) => Promise<Uint8Array>;
|
|
15
7
|
/**
|
|
16
8
|
* Prepended to the ESM bundle. Bundled dependencies are frequently CommonJS, but an ESM
|
|
@@ -20,41 +12,16 @@ type FunctionBundler = (fn: ResolvedFunctionConfig) => Promise<Uint8Array>;
|
|
|
20
12
|
* so CJS and ESM dependencies coexist in the single `index.mjs`.
|
|
21
13
|
*/
|
|
22
14
|
declare 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);";
|
|
23
|
-
|
|
24
|
-
* Build the deployable bundle (a ZIP archive of the esbuild-bundled source) for a function.
|
|
25
|
-
*
|
|
26
|
-
* This is the **imperative shell** step of function deploys, and the reason it lives in
|
|
27
|
-
* `@neon/config-runtime` rather than `@neon/config`: it pulls in `esbuild`
|
|
28
|
-
* (a native binary) and `fflate`. Keeping it out of `@neon/config` means a `neon.ts`
|
|
29
|
-
* that only imports `defineConfig` never drags esbuild into the user's dependency tree or
|
|
30
|
-
* bundle. Deploy-side consumers (the neonctl CLI, CI) import this package and get esbuild as
|
|
31
|
-
* a normal, auto-installed dependency.
|
|
32
|
-
*
|
|
33
|
-
* esbuild and fflate are loaded with a dynamic `import()` (not a static top-level import) so
|
|
34
|
-
* that nothing in this package's static graph names esbuild until a deploy actually runs —
|
|
35
|
-
* a second layer of protection on top of the package split.
|
|
36
|
-
*
|
|
37
|
-
* Mirrors: `esbuild <source> --bundle --outfile=index.mjs --minify --format=esm
|
|
38
|
-
* --platform=node --banner:js=<createRequire shim>`, then zips the emitted files into the
|
|
39
|
-
* archive the Functions deploy endpoint expects. Dependencies are bundled into the entry
|
|
40
|
-
* (Node built-ins stay external); see {@link ESM_CJS_INTEROP_BANNER} for why the banner.
|
|
41
|
-
*
|
|
42
|
-
* No source map is emitted: the Functions runtime does not run Node with source-map support,
|
|
43
|
-
* so an uploaded `index.mjs.map` is never consumed (a thrown error's stack still points into
|
|
44
|
-
* the minified bundle). Generating it only inflated the deployed archive, so it is omitted.
|
|
45
|
-
*/
|
|
46
|
-
declare function buildFunctionBundle(fn: ResolvedFunctionConfig, options?: {
|
|
15
|
+
type FunctionBundleOptions = {
|
|
47
16
|
nativeDeps?: Partial<NativeTraceDeps>;
|
|
48
|
-
/**
|
|
49
|
-
* Where advisory findings go — the undeclared-native-dependency report, and a package
|
|
50
|
-
* whose version could not be pinned.
|
|
51
|
-
*
|
|
52
|
-
* Defaults to discarding them, because a library has no business writing to the
|
|
53
|
-
* console. Pass a handler: these are the only signal that a function will fail at
|
|
54
|
-
* invoke, and the CLI wires this to its logger for exactly that reason.
|
|
55
|
-
*/
|
|
17
|
+
/** Callers own advisory output because this library has no output sink. */
|
|
56
18
|
onWarning?: (message: string) => void;
|
|
57
|
-
}
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Honors `fn.bundler`. The packaged CLI never calls this — it injects its own
|
|
22
|
+
* bundler so esbuild stays out of the pkg graph.
|
|
23
|
+
*/
|
|
24
|
+
declare function buildFunctionBundle(fn: ResolvedFunctionConfig, options?: FunctionBundleOptions): Promise<Uint8Array>;
|
|
58
25
|
//#endregion
|
|
59
26
|
export { ESM_CJS_INTEROP_BANNER, FunctionBundler, buildFunctionBundle };
|
|
60
27
|
//# sourceMappingURL=function-bundle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function-bundle.d.ts","names":[],"sources":["../../src/lib/function-bundle.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"function-bundle.d.ts","names":[],"sources":["../../src/lib/function-bundle.ts"],"mappings":";;;;;KAmBY,eAAA,QACP,2BACA,QAAQ;AAFb;AAA2B;AACtB;AACQ;AAAR;AAAO;AASZ;AAGK,cAHQ,sBAAA,GAGa,mOAAA;AAAA,KAArB,qBAAA,GAAqB;EACJ,UAAA,CAAA,EAAR,OAAQ,CAAA,eAAA,CAAA;EAAR;EAAO,SAAA,CAAA,EAAA,CAAA,OAAA,EAAA,MAAA,EAAA,GAAA,IAAA;AASrB,CAAA;AAAyC;AACpC;AACK;AACC;AAAR,iBAHmB,mBAAA,CAGnB,EAAA,EAFE,sBAEF,EAAA,OAAA,CAAA,EADO,qBACP,CAAA,EAAA,OAAA,CAAQ,UAAR,CAAA"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { bundleAsIs, resolveEsbuildEntry } from "./function-source.js";
|
|
2
|
+
import { traceNativePackages } from "./native-packages.js";
|
|
3
|
+
import { zipEntries, zipFunctionBundle } from "./function-zip.js";
|
|
2
4
|
import { describeNativeFinding, findUndeclaredNativePackages } from "./native-detect.js";
|
|
3
5
|
import { ErrorCode, PlatformError, packagesToStage } from "@neon/config";
|
|
4
6
|
import { basename, dirname } from "node:path";
|
|
@@ -12,37 +14,30 @@ import { basename, dirname } from "node:path";
|
|
|
12
14
|
*/
|
|
13
15
|
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);";
|
|
14
16
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* This is the **imperative shell** step of function deploys, and the reason it lives in
|
|
18
|
-
* `@neon/config-runtime` rather than `@neon/config`: it pulls in `esbuild`
|
|
19
|
-
* (a native binary) and `fflate`. Keeping it out of `@neon/config` means a `neon.ts`
|
|
20
|
-
* that only imports `defineConfig` never drags esbuild into the user's dependency tree or
|
|
21
|
-
* bundle. Deploy-side consumers (the neonctl CLI, CI) import this package and get esbuild as
|
|
22
|
-
* a normal, auto-installed dependency.
|
|
23
|
-
*
|
|
24
|
-
* esbuild and fflate are loaded with a dynamic `import()` (not a static top-level import) so
|
|
25
|
-
* that nothing in this package's static graph names esbuild until a deploy actually runs —
|
|
26
|
-
* a second layer of protection on top of the package split.
|
|
27
|
-
*
|
|
28
|
-
* Mirrors: `esbuild <source> --bundle --outfile=index.mjs --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.
|
|
32
|
-
*
|
|
33
|
-
* No source map is emitted: the Functions runtime does not run Node with source-map support,
|
|
34
|
-
* so an uploaded `index.mjs.map` is never consumed (a thrown error's stack still points into
|
|
35
|
-
* the minified bundle). Generating it only inflated the deployed archive, so it is omitted.
|
|
17
|
+
* Honors `fn.bundler`. The packaged CLI never calls this — it injects its own
|
|
18
|
+
* bundler so esbuild stays out of the pkg graph.
|
|
36
19
|
*/
|
|
37
20
|
async function buildFunctionBundle(fn, options = {}) {
|
|
21
|
+
const { bundler } = fn;
|
|
22
|
+
if (bundler === void 0 || bundler === "esbuild") return bundleWithEsbuild(fn, options);
|
|
23
|
+
const bundle = bundler === "none" ? await bundleAsIs(fn) : await bundler(fn);
|
|
24
|
+
return zipFunctionBundle(fn.slug, bundle);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Source maps are omitted because Functions does not enable them and they only
|
|
28
|
+
* enlarge archives.
|
|
29
|
+
*/
|
|
30
|
+
const bundleWithEsbuild = async (fn, options = {}) => {
|
|
31
|
+
const entry = await resolveEsbuildEntry(fn.source);
|
|
38
32
|
const esbuild = await loadEsbuild();
|
|
39
33
|
const externalPackages = fn.externalPackages ?? [];
|
|
40
34
|
const staged = packagesToStage(externalPackages);
|
|
41
35
|
const onWarning = options.onWarning ?? (() => {});
|
|
36
|
+
const projectDir = dirname(entry);
|
|
42
37
|
let result;
|
|
43
38
|
try {
|
|
44
39
|
result = await esbuild.build({
|
|
45
|
-
entryPoints: [
|
|
40
|
+
entryPoints: [entry],
|
|
46
41
|
bundle: true,
|
|
47
42
|
write: false,
|
|
48
43
|
outfile: "index.mjs",
|
|
@@ -55,47 +50,35 @@ async function buildFunctionBundle(fn, options = {}) {
|
|
|
55
50
|
logLevel: "silent"
|
|
56
51
|
});
|
|
57
52
|
} catch (cause) {
|
|
58
|
-
|
|
53
|
+
const detail = cause instanceof Error ? cause.message : String(cause);
|
|
54
|
+
throw new PlatformError(ErrorCode.InvalidConfig, [`Failed to bundle function "${fn.slug}" from ${entry}.`, detail].join(" "), { cause });
|
|
59
55
|
}
|
|
60
56
|
const entries = {};
|
|
61
57
|
for (const file of result.outputFiles ?? []) entries[basename(file.path)] = file.contents;
|
|
62
58
|
for (const finding of findUndeclaredNativePackages({
|
|
63
59
|
metafile: result.metafile,
|
|
64
60
|
declared: externalPackages.map((pkg) => pkg.name),
|
|
65
|
-
projectDir
|
|
61
|
+
projectDir
|
|
66
62
|
})) onWarning(describeNativeFinding(fn.slug, finding));
|
|
67
|
-
if (staged.length === 0) return
|
|
63
|
+
if (staged.length === 0) return zipEntries(entries);
|
|
68
64
|
const traced = await traceNativePackages({
|
|
69
65
|
slug: fn.slug,
|
|
70
66
|
packages: staged,
|
|
71
|
-
projectDir
|
|
67
|
+
projectDir,
|
|
72
68
|
...options.nativeDeps ? { deps: options.nativeDeps } : {}
|
|
73
69
|
});
|
|
74
70
|
for (const warning of traced.warnings) onWarning(warning);
|
|
75
71
|
Object.assign(entries, traced.entries);
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
async function zipBundle(entries) {
|
|
82
|
-
const { zipSync } = await loadFflate();
|
|
83
|
-
return zipSync(entries, { level: 6 });
|
|
84
|
-
}
|
|
85
|
-
async function loadEsbuild() {
|
|
72
|
+
return zipFunctionBundle(fn.slug, entries);
|
|
73
|
+
};
|
|
74
|
+
const loadEsbuild = async () => {
|
|
75
|
+
const name = ["es", "build"].join("");
|
|
86
76
|
try {
|
|
87
|
-
return await import(
|
|
77
|
+
return await import(name);
|
|
88
78
|
} catch (cause) {
|
|
89
79
|
throw new PlatformError(ErrorCode.InvalidConfig, ["Deploying Neon Functions requires `esbuild`, which could not be loaded.", "It is a dependency of @neon/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`)."].join(" "), { cause });
|
|
90
80
|
}
|
|
91
|
-
}
|
|
92
|
-
async function loadFflate() {
|
|
93
|
-
try {
|
|
94
|
-
return await import("fflate");
|
|
95
|
-
} catch (cause) {
|
|
96
|
-
throw new PlatformError(ErrorCode.InvalidConfig, ["Deploying Neon Functions requires `fflate`, which could not be loaded.", "It is a dependency of @neon/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`)."].join(" "), { cause });
|
|
97
|
-
}
|
|
98
|
-
}
|
|
81
|
+
};
|
|
99
82
|
//#endregion
|
|
100
83
|
export { ESM_CJS_INTEROP_BANNER, buildFunctionBundle };
|
|
101
84
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"function-bundle.js","names":[],"sources":["../../src/lib/function-bundle.ts"],"sourcesContent":["import { basename, dirname } from \"node:path\";\nimport {\n\tErrorCode,\n\tPlatformError,\n\tpackagesToStage,\n\ttype ResolvedFunctionConfig,\n} from \"@neon/config\";\nimport {\n\tdescribeNativeFinding,\n\tfindUndeclaredNativePackages,\n} from \"./native-detect.js\";\nimport {\n\tassertZipWithinLimits,\n\tenforceLimits,\n\ttype NativeTraceDeps,\n\ttraceNativePackages,\n} from \"./native-packages.js\";\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 */\nexport const 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 * `@neon/config-runtime` rather than `@neon/config`: it pulls in `esbuild`\n * (a native binary) and `fflate`. Keeping it out of `@neon/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 --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 *\n * No source map is emitted: the Functions runtime does not run Node with source-map support,\n * so an uploaded `index.mjs.map` is never consumed (a thrown error's stack still points into\n * the minified bundle). Generating it only inflated the deployed archive, so it is omitted.\n */\nexport async function buildFunctionBundle(\n\tfn: ResolvedFunctionConfig,\n\toptions: {\n\t\tnativeDeps?: Partial<NativeTraceDeps>;\n\t\t/**\n\t\t * Where advisory findings go — the undeclared-native-dependency report, and a package\n\t\t * whose version could not be pinned.\n\t\t *\n\t\t * Defaults to discarding them, because a library has no business writing to the\n\t\t * console. Pass a handler: these are the only signal that a function will fail at\n\t\t * invoke, and the CLI wires this to its logger for exactly that reason.\n\t\t */\n\t\tonWarning?: (message: string) => void;\n\t} = {},\n): Promise<Uint8Array> {\n\tconst esbuild = await loadEsbuild();\n\tconst externalPackages = fn.externalPackages ?? [];\n\tconst staged = packagesToStage(externalPackages);\n\tconst onWarning = options.onWarning ?? (() => {});\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`: the Functions runtime imports the archive's entry by the\n\t\t\t// conventional `index.{js,mjs}` name, and `.mjs` makes Node load the ESM output\n\t\t\t// directly. (With `write: false` and no outfile, esbuild would label the buffer\n\t\t\t// `<stdout>`.) No `sourcemap` — see the doc comment above for why.\n\t\t\toutfile: \"index.mjs\",\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\t// Packages the policy declared unbundleable. Their imports survive into the\n\t\t\t// bundle instead of being followed; whether they then resolve at runtime depends\n\t\t\t// on `includeFiles`, which is acted on below. Both modes are external here.\n\t\t\texternal: externalPackages.map((pkg) => pkg.name),\n\t\t\t// Reveals which packages were bundled in, so an undeclared native dependency can\n\t\t\t// be reported. Costs nothing here — esbuild is already running.\n\t\t\tmetafile: true,\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`) so the bundle layout is stable regardless of cwd.\n\t\tentries[basename(file.path)] = file.contents;\n\t}\n\n\t// Only reachable on a successful build, which is the point: `sharp` and its kind bundle\n\t// cleanly and fail at invoke, so this is exactly the case nothing else catches. A build\n\t// that throws has already failed loudly with esbuild's own diagnostic.\n\tfor (const finding of findUndeclaredNativePackages({\n\t\tmetafile: result.metafile,\n\t\tdeclared: externalPackages.map((pkg) => pkg.name),\n\t\tprojectDir: dirname(fn.source),\n\t})) {\n\t\tonWarning(describeNativeFinding(fn.slug, finding));\n\t}\n\n\t// The pre-existing path, byte for byte: no install, no trace, no size check, and an\n\t// archive of exactly the esbuild output. A policy that declares nothing to stage — or\n\t// sets `includeFiles: false` on everything — deploys the archive it always did.\n\tif (staged.length === 0) return zipBundle(entries);\n\n\tconst traced = await traceNativePackages({\n\t\tslug: fn.slug,\n\t\tpackages: staged,\n\t\t// The user's project, located from their entry — where their resolved versions are\n\t\t// read from. Only versions: the files themselves come from the staging install.\n\t\tprojectDir: dirname(fn.source),\n\t\t...(options.nativeDeps ? { deps: options.nativeDeps } : {}),\n\t});\n\tfor (const warning of traced.warnings) onWarning(warning);\n\t// Keys carry their `node_modules/...` path, so the archive reproduces the tree layout a\n\t// `.node` addon needs to find its sibling libraries. Do not flatten these.\n\tObject.assign(entries, traced.entries);\n\n\t// Re-checked against the final archive: the staged files were measured without the\n\t// bundle, so the entry count and uncompressed total are only complete now.\n\tenforceLimits(fn.slug, entries);\n\tconst zip = await zipBundle(entries);\n\tassertZipWithinLimits(fn.slug, zip, entries);\n\treturn zip;\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 @neon/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 @neon/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":";;;;;;;;;;;;AAqCA,MAAa,yBACZ;;;;;;;;;;;;;;;;;;;;;;;;AAyBD,eAAsB,oBACrB,IACA,UAWI,CAAC,GACiB;CACtB,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;CACjD,MAAM,SAAS,gBAAgB,gBAAgB;CAC/C,MAAM,YAAY,QAAQ,oBAAoB,CAAC;CAE/C,IAAI;CACJ,IAAI;EACH,SAAS,MAAM,QAAQ,MAAM;GAC5B,aAAa,CAAC,GAAG,MAAM;GACvB,QAAQ;GACR,OAAO;GAKP,SAAS;GACT,QAAQ;GACR,QAAQ;GACR,UAAU;GAKV,QAAQ,EAAE,IAAI,uBAAuB;GAIrC,UAAU,iBAAiB,KAAK,QAAQ,IAAI,IAAI;GAGhD,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,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;CAMrC,KAAK,MAAM,WAAW,6BAA6B;EAClD,UAAU,OAAO;EACjB,UAAU,iBAAiB,KAAK,QAAQ,IAAI,IAAI;EAChD,YAAY,QAAQ,GAAG,MAAM;CAC9B,CAAC,GACA,UAAU,sBAAsB,GAAG,MAAM,OAAO,CAAC;CAMlD,IAAI,OAAO,WAAW,GAAG,OAAO,UAAU,OAAO;CAEjD,MAAM,SAAS,MAAM,oBAAoB;EACxC,MAAM,GAAG;EACT,UAAU;EAGV,YAAY,QAAQ,GAAG,MAAM;EAC7B,GAAI,QAAQ,aAAa,EAAE,MAAM,QAAQ,WAAW,IAAI,CAAC;CAC1D,CAAC;CACD,KAAK,MAAM,WAAW,OAAO,UAAU,UAAU,OAAO;CAGxD,OAAO,OAAO,SAAS,OAAO,OAAO;CAIrC,cAAc,GAAG,MAAM,OAAO;CAC9B,MAAM,MAAM,MAAM,UAAU,OAAO;CACnC,sBAAsB,GAAG,MAAM,KAAK,OAAO;CAC3C,OAAO;AACR;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,4GACD,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,4GACD,CAAC,CAAC,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, dirname } from \"node:path\";\nimport {\n\tErrorCode,\n\tPlatformError,\n\tpackagesToStage,\n\ttype ResolvedFunctionConfig,\n} from \"@neon/config\";\nimport { bundleAsIs, resolveEsbuildEntry } from \"./function-source.js\";\nimport { zipEntries, zipFunctionBundle } from \"./function-zip.js\";\nimport {\n\tdescribeNativeFinding,\n\tfindUndeclaredNativePackages,\n} from \"./native-detect.js\";\nimport {\n\ttype NativeTraceDeps,\n\ttraceNativePackages,\n} from \"./native-packages.js\";\n\n/** Deploy-level ZIP. `@neon/config`'s FunctionBundler returns a file map. */\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 */\nexport const 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\ntype FunctionBundleOptions = {\n\tnativeDeps?: Partial<NativeTraceDeps>;\n\t/** Callers own advisory output because this library has no output sink. */\n\tonWarning?: (message: string) => void;\n};\n\n/**\n * Honors `fn.bundler`. The packaged CLI never calls this — it injects its own\n * bundler so esbuild stays out of the pkg graph.\n */\nexport async function buildFunctionBundle(\n\tfn: ResolvedFunctionConfig,\n\toptions: FunctionBundleOptions = {},\n): Promise<Uint8Array> {\n\tconst { bundler } = fn;\n\tif (bundler === undefined || bundler === \"esbuild\") {\n\t\treturn bundleWithEsbuild(fn, options);\n\t}\n\tconst bundle =\n\t\tbundler === \"none\" ? await bundleAsIs(fn) : await bundler(fn);\n\treturn zipFunctionBundle(fn.slug, bundle);\n}\n\n/**\n * Source maps are omitted because Functions does not enable them and they only\n * enlarge archives.\n */\nconst bundleWithEsbuild = async (\n\tfn: ResolvedFunctionConfig,\n\toptions: FunctionBundleOptions = {},\n): Promise<Uint8Array> => {\n\tconst entry = await resolveEsbuildEntry(fn.source);\n\tconst esbuild = await loadEsbuild();\n\tconst externalPackages = fn.externalPackages ?? [];\n\tconst staged = packagesToStage(externalPackages);\n\tconst onWarning = options.onWarning ?? (() => {});\n\tconst projectDir = dirname(entry);\n\n\tlet result: Awaited<ReturnType<typeof esbuild.build>>;\n\ttry {\n\t\tresult = await esbuild.build({\n\t\t\tentryPoints: [entry],\n\t\t\tbundle: true,\n\t\t\twrite: false,\n\t\t\t// Emit `index.mjs`: the Functions runtime imports the archive's entry by the\n\t\t\t// conventional `index.{js,mjs}` name, and `.mjs` makes Node load the ESM output\n\t\t\t// directly. (With `write: false` and no outfile, esbuild would label the buffer\n\t\t\t// `<stdout>`.) No `sourcemap` — see the doc comment above for why.\n\t\t\toutfile: \"index.mjs\",\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\t// Packages the policy declared unbundleable. Their imports survive into the\n\t\t\t// bundle instead of being followed; whether they then resolve at runtime depends\n\t\t\t// on `includeFiles`, which is acted on below. Both modes are external here.\n\t\t\texternal: externalPackages.map((pkg) => pkg.name),\n\t\t\t// Reveals which packages were bundled in, so an undeclared native dependency can\n\t\t\t// be reported. Costs nothing here — esbuild is already running.\n\t\t\tmetafile: true,\n\t\t\tlogLevel: \"silent\",\n\t\t});\n\t} catch (cause) {\n\t\tconst detail = cause instanceof Error ? cause.message : String(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 ${entry}.`,\n\t\t\t\tdetail,\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`) so the bundle layout is stable regardless of cwd.\n\t\tentries[basename(file.path)] = file.contents;\n\t}\n\n\t// Only reachable on a successful build, which is the point: `sharp` and its kind bundle\n\t// cleanly and fail at invoke, so this is exactly the case nothing else catches. A build\n\t// that throws has already failed loudly with esbuild's own diagnostic.\n\tfor (const finding of findUndeclaredNativePackages({\n\t\tmetafile: result.metafile,\n\t\tdeclared: externalPackages.map((pkg) => pkg.name),\n\t\tprojectDir,\n\t})) {\n\t\tonWarning(describeNativeFinding(fn.slug, finding));\n\t}\n\n\t// The pre-existing path, byte for byte: no install, no trace, no size check, and an\n\t// archive of exactly the esbuild output. A policy that declares nothing to stage — or\n\t// sets `includeFiles: false` on everything — deploys the archive it always did.\n\tif (staged.length === 0) return zipEntries(entries);\n\n\tconst traced = await traceNativePackages({\n\t\tslug: fn.slug,\n\t\tpackages: staged,\n\t\t// The user's project, located from their entry — where their resolved versions are\n\t\t// read from. Only versions: the files themselves come from the staging install.\n\t\tprojectDir,\n\t\t...(options.nativeDeps ? { deps: options.nativeDeps } : {}),\n\t});\n\tfor (const warning of traced.warnings) onWarning(warning);\n\t// Keys carry their `node_modules/...` path, so the archive reproduces the tree layout a\n\t// `.node` addon needs to find its sibling libraries. Do not flatten these.\n\tObject.assign(entries, traced.entries);\n\n\t// Re-checked against the final archive: the staged files were measured without the\n\t// bundle, so the entry count and uncompressed total are only complete now.\n\treturn zipFunctionBundle(fn.slug, entries);\n};\n\ntype EsbuildModule = {\n\tbuild: (opts: Record<string, unknown>) => Promise<{\n\t\toutputFiles?: readonly { path: string; contents: Uint8Array }[];\n\t\tmetafile?: { inputs?: Record<string, unknown> };\n\t}>;\n};\n\nconst loadEsbuild = async (): Promise<EsbuildModule> => {\n\t// A literal module name would let Rollup and pkg snapshot esbuild's native binary.\n\tconst name = [\"es\", \"build\"].join(\"\");\n\ttry {\n\t\treturn (await import(name)) as EsbuildModule;\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 @neon/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":";;;;;;;;;;;;;;AA8BA,MAAa,yBACZ;;;;;AAYD,eAAsB,oBACrB,IACA,UAAiC,CAAC,GACZ;CACtB,MAAM,EAAE,YAAY;CACpB,IAAI,YAAY,KAAA,KAAa,YAAY,WACxC,OAAO,kBAAkB,IAAI,OAAO;CAErC,MAAM,SACL,YAAY,SAAS,MAAM,WAAW,EAAE,IAAI,MAAM,QAAQ,EAAE;CAC7D,OAAO,kBAAkB,GAAG,MAAM,MAAM;AACzC;;;;;AAMA,MAAM,oBAAoB,OACzB,IACA,UAAiC,CAAC,MACT;CACzB,MAAM,QAAQ,MAAM,oBAAoB,GAAG,MAAM;CACjD,MAAM,UAAU,MAAM,YAAY;CAClC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;CACjD,MAAM,SAAS,gBAAgB,gBAAgB;CAC/C,MAAM,YAAY,QAAQ,oBAAoB,CAAC;CAC/C,MAAM,aAAa,QAAQ,KAAK;CAEhC,IAAI;CACJ,IAAI;EACH,SAAS,MAAM,QAAQ,MAAM;GAC5B,aAAa,CAAC,KAAK;GACnB,QAAQ;GACR,OAAO;GAKP,SAAS;GACT,QAAQ;GACR,QAAQ;GACR,UAAU;GAKV,QAAQ,EAAE,IAAI,uBAAuB;GAIrC,UAAU,iBAAiB,KAAK,QAAQ,IAAI,IAAI;GAGhD,UAAU;GACV,UAAU;EACX,CAAC;CACF,SAAS,OAAO;EACf,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACpE,MAAM,IAAI,cACT,UAAU,eACV,CACC,8BAA8B,GAAG,KAAK,SAAS,MAAM,IACrD,MACD,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;CAMrC,KAAK,MAAM,WAAW,6BAA6B;EAClD,UAAU,OAAO;EACjB,UAAU,iBAAiB,KAAK,QAAQ,IAAI,IAAI;EAChD;CACD,CAAC,GACA,UAAU,sBAAsB,GAAG,MAAM,OAAO,CAAC;CAMlD,IAAI,OAAO,WAAW,GAAG,OAAO,WAAW,OAAO;CAElD,MAAM,SAAS,MAAM,oBAAoB;EACxC,MAAM,GAAG;EACT,UAAU;EAGV;EACA,GAAI,QAAQ,aAAa,EAAE,MAAM,QAAQ,WAAW,IAAI,CAAC;CAC1D,CAAC;CACD,KAAK,MAAM,WAAW,OAAO,UAAU,UAAU,OAAO;CAGxD,OAAO,OAAO,SAAS,OAAO,OAAO;CAIrC,OAAO,kBAAkB,GAAG,MAAM,OAAO;AAC1C;AASA,MAAM,cAAc,YAAoC;CAEvD,MAAM,OAAO,CAAC,MAAM,OAAO,CAAC,CAAC,KAAK,EAAE;CACpC,IAAI;EACH,OAAQ,MAAM,OAAO;CACtB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,2EACA,4GACD,CAAC,CAAC,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FunctionBundle, ResolvedFunctionConfig } from "@neon/config";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/function-source.d.ts
|
|
4
|
+
type UnbundledVia = "none" | "no-bundle";
|
|
5
|
+
/** A file is the entry. A directory is searched in {@link FUNCTION_SOURCE_ENTRIES} order; only real files count. */
|
|
6
|
+
declare function resolveEsbuildEntry(source: string): Promise<string>;
|
|
7
|
+
/**
|
|
8
|
+
* `"none"` / `--no-bundle`: zip `source` without esbuild. The archive root (or
|
|
9
|
+
* the file's basename) must be `index.mjs` or `index.js`. TypeScript cannot ship unbundled.
|
|
10
|
+
*/
|
|
11
|
+
declare function bundleAsIs(fn: ResolvedFunctionConfig, options?: {
|
|
12
|
+
via?: UnbundledVia;
|
|
13
|
+
}): Promise<FunctionBundle>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { UnbundledVia, bundleAsIs, resolveEsbuildEntry };
|
|
16
|
+
//# sourceMappingURL=function-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-source.d.ts","names":[],"sources":["../../src/lib/function-source.ts"],"mappings":";;;KAaY,YAAA;;AAAA,iBAGU,mBAAA,CAHE,MAAA,EAAA,MAAA,CAAA,EAGmC,OAHnC,CAAA,MAAA,CAAA;AAGxB;AAyCA;AAAgC;AAC3B;AACa,iBAFI,UAAA,CAEJ,EAAA,EADb,sBACa,EAAA,OACf,CADe,EAAA;EACP,GAAA,CAAA,EADO,YACP;AAAR,CAAA,CAAA,EAAA,OAAA,CAAQ,cAAR,CAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { ErrorCode, FUNCTION_ARCHIVE_ENTRIES, FUNCTION_SOURCE_ENTRIES, PlatformError, isFunctionArchiveEntry, pickFunctionSourceEntry } from "@neon/config";
|
|
2
|
+
import { basename, isAbsolute, join, relative, sep } from "node:path";
|
|
3
|
+
import { readFile, readdir, realpath, stat } from "node:fs/promises";
|
|
4
|
+
//#region src/lib/function-source.ts
|
|
5
|
+
/** A file is the entry. A directory is searched in {@link FUNCTION_SOURCE_ENTRIES} order; only real files count. */
|
|
6
|
+
async function resolveEsbuildEntry(source) {
|
|
7
|
+
let sourceStat;
|
|
8
|
+
try {
|
|
9
|
+
sourceStat = await stat(source);
|
|
10
|
+
} catch (cause) {
|
|
11
|
+
throw new PlatformError(ErrorCode.InvalidConfig, `Function source ${source} does not exist.`, { cause });
|
|
12
|
+
}
|
|
13
|
+
if (sourceStat.isFile()) return source;
|
|
14
|
+
if (!sourceStat.isDirectory()) throw new PlatformError(ErrorCode.InvalidConfig, `Function source ${source} is neither a file nor a directory.`);
|
|
15
|
+
const present = [];
|
|
16
|
+
await Promise.all(FUNCTION_SOURCE_ENTRIES.map(async (name) => {
|
|
17
|
+
const candidate = join(source, name);
|
|
18
|
+
if ((await stat(candidate).catch(() => null))?.isFile()) present.push(name);
|
|
19
|
+
}));
|
|
20
|
+
const picked = pickFunctionSourceEntry(present);
|
|
21
|
+
if (picked === void 0) throw new PlatformError(ErrorCode.InvalidConfig, `No entry file found in ${source}. Expected one of: ${FUNCTION_SOURCE_ENTRIES.join(", ")}.`);
|
|
22
|
+
return join(source, picked);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* `"none"` / `--no-bundle`: zip `source` without esbuild. The archive root (or
|
|
26
|
+
* the file's basename) must be `index.mjs` or `index.js`. TypeScript cannot ship unbundled.
|
|
27
|
+
*/
|
|
28
|
+
async function bundleAsIs(fn, options = {}) {
|
|
29
|
+
const via = options.via ?? "none";
|
|
30
|
+
let sourceStat;
|
|
31
|
+
try {
|
|
32
|
+
sourceStat = await stat(fn.source);
|
|
33
|
+
} catch (cause) {
|
|
34
|
+
throw new PlatformError(ErrorCode.InvalidConfig, `Function "${fn.slug}" bundler is "none" but its source ${fn.source} does not exist.`, { cause });
|
|
35
|
+
}
|
|
36
|
+
if (sourceStat.isDirectory()) {
|
|
37
|
+
const entries = {};
|
|
38
|
+
await collectDirectory(fn.source, fn.source, entries, await realpath(fn.source), []);
|
|
39
|
+
if (FUNCTION_ARCHIVE_ENTRIES.some((name) => name in entries)) return entries;
|
|
40
|
+
if ("index.ts" in entries) throw new PlatformError(ErrorCode.InvalidConfig, typescriptUnbundledMessage(fn, via));
|
|
41
|
+
throw new PlatformError(ErrorCode.InvalidConfig, `Function "${fn.slug}" source directory ${fn.source} has no entry module at its root (expected one of: ${FUNCTION_ARCHIVE_ENTRIES.join(", ")}). The Functions runtime imports the archive by that name.`);
|
|
42
|
+
}
|
|
43
|
+
if (!sourceStat.isFile()) throw new PlatformError(ErrorCode.InvalidConfig, `Function "${fn.slug}" source ${fn.source} is neither a file nor a directory.`);
|
|
44
|
+
const name = basename(fn.source);
|
|
45
|
+
if (name === "index.ts") throw new PlatformError(ErrorCode.InvalidConfig, typescriptUnbundledMessage(fn, via));
|
|
46
|
+
if (!isFunctionArchiveEntry(name)) throw new PlatformError(ErrorCode.InvalidConfig, via === "no-bundle" ? `${fn.source} must be named index.mjs or index.js to ship with --no-bundle (got "${name}"). Omit --no-bundle to esbuild it, or rename the file.` : `Function "${fn.slug}" bundler is "none" but ${fn.source} is named "${name}". The Functions runtime imports index.mjs or index.js at the archive root.`);
|
|
47
|
+
return { [name]: new Uint8Array(await readFile(fn.source)) };
|
|
48
|
+
}
|
|
49
|
+
const typescriptUnbundledMessage = (fn, via) => via === "no-bundle" ? `TypeScript must be bundled. ${fn.source} has no index.mjs or index.js; omit --no-bundle to esbuild it, or emit one of those files.` : `Function "${fn.slug}" bundler is "none" but ${fn.source} is TypeScript. Use the default esbuild bundler, or emit index.mjs / index.js.`;
|
|
50
|
+
async function collectDirectory(root, dir, entries, rootReal, stack) {
|
|
51
|
+
const dirReal = await realpath(dir);
|
|
52
|
+
if (stack.includes(dirReal)) return;
|
|
53
|
+
const nextStack = [...stack, dirReal];
|
|
54
|
+
const dirents = await readdir(dir, { withFileTypes: true });
|
|
55
|
+
await Promise.all(dirents.map(async (dirent) => {
|
|
56
|
+
const abs = join(dir, dirent.name);
|
|
57
|
+
if (dirent.isDirectory()) {
|
|
58
|
+
await collectDirectory(root, abs, entries, rootReal, nextStack);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (dirent.isSymbolicLink()) {
|
|
62
|
+
await assertSymlinkInsideSource(abs, root, rootReal);
|
|
63
|
+
const target = await stat(abs).catch(() => null);
|
|
64
|
+
if (!target) return;
|
|
65
|
+
if (target.isDirectory()) {
|
|
66
|
+
await collectDirectory(root, abs, entries, rootReal, nextStack);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const key = relative(root, abs).split(sep).join("/");
|
|
71
|
+
entries[key] = new Uint8Array(await readFile(abs));
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
const assertSymlinkInsideSource = async (abs, root, rootReal) => {
|
|
75
|
+
let targetReal;
|
|
76
|
+
try {
|
|
77
|
+
targetReal = await realpath(abs);
|
|
78
|
+
} catch (cause) {
|
|
79
|
+
throw new PlatformError(ErrorCode.InvalidConfig, `Function source ${root} contains a dangling symlink: ${abs}.`, { cause });
|
|
80
|
+
}
|
|
81
|
+
const escaped = relative(rootReal, targetReal);
|
|
82
|
+
if (escaped === ".." || escaped.startsWith(`..${sep}`) || isAbsolute(escaped)) throw new PlatformError(ErrorCode.InvalidConfig, `Function source ${root} contains a symlink that points outside the directory: ${abs}.`);
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
export { bundleAsIs, resolveEsbuildEntry };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=function-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-source.js","names":[],"sources":["../../src/lib/function-source.ts"],"sourcesContent":["import { readdir, readFile, realpath, stat } from \"node:fs/promises\";\nimport { basename, isAbsolute, join, relative, sep } from \"node:path\";\nimport {\n\tErrorCode,\n\tFUNCTION_ARCHIVE_ENTRIES,\n\tFUNCTION_SOURCE_ENTRIES,\n\ttype FunctionBundle,\n\tisFunctionArchiveEntry,\n\tPlatformError,\n\tpickFunctionSourceEntry,\n\ttype ResolvedFunctionConfig,\n} from \"@neon/config\";\n\nexport type UnbundledVia = \"none\" | \"no-bundle\";\n\n/** A file is the entry. A directory is searched in {@link FUNCTION_SOURCE_ENTRIES} order; only real files count. */\nexport async function resolveEsbuildEntry(source: string): Promise<string> {\n\tlet sourceStat: Awaited<ReturnType<typeof stat>>;\n\ttry {\n\t\tsourceStat = await stat(source);\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function source ${source} does not exist.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\tif (sourceStat.isFile()) return source;\n\tif (!sourceStat.isDirectory()) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function source ${source} is neither a file nor a directory.`,\n\t\t);\n\t}\n\n\tconst present: string[] = [];\n\tawait Promise.all(\n\t\tFUNCTION_SOURCE_ENTRIES.map(async (name) => {\n\t\t\tconst candidate = join(source, name);\n\t\t\tconst candidateStat = await stat(candidate).catch(() => null);\n\t\t\tif (candidateStat?.isFile()) present.push(name);\n\t\t}),\n\t);\n\tconst picked = pickFunctionSourceEntry(present);\n\tif (picked === undefined) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`No entry file found in ${source}. Expected one of: ${FUNCTION_SOURCE_ENTRIES.join(\", \")}.`,\n\t\t);\n\t}\n\treturn join(source, picked);\n}\n\n/**\n * `\"none\"` / `--no-bundle`: zip `source` without esbuild. The archive root (or\n * the file's basename) must be `index.mjs` or `index.js`. TypeScript cannot ship unbundled.\n */\nexport async function bundleAsIs(\n\tfn: ResolvedFunctionConfig,\n\toptions: { via?: UnbundledVia } = {},\n): Promise<FunctionBundle> {\n\tconst via = options.via ?? \"none\";\n\tlet sourceStat: Awaited<ReturnType<typeof stat>>;\n\ttry {\n\t\tsourceStat = await stat(fn.source);\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function \"${fn.slug}\" bundler is \"none\" but its source ${fn.source} does not exist.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tif (sourceStat.isDirectory()) {\n\t\tconst entries: FunctionBundle = {};\n\t\tawait collectDirectory(\n\t\t\tfn.source,\n\t\t\tfn.source,\n\t\t\tentries,\n\t\t\tawait realpath(fn.source),\n\t\t\t[],\n\t\t);\n\t\tif (FUNCTION_ARCHIVE_ENTRIES.some((name) => name in entries)) {\n\t\t\treturn entries;\n\t\t}\n\t\tif (\"index.ts\" in entries) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.InvalidConfig,\n\t\t\t\ttypescriptUnbundledMessage(fn, via),\n\t\t\t);\n\t\t}\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function \"${fn.slug}\" source directory ${fn.source} has no entry module at its root ` +\n\t\t\t\t`(expected one of: ${FUNCTION_ARCHIVE_ENTRIES.join(\", \")}). The Functions runtime imports the archive by that name.`,\n\t\t);\n\t}\n\n\tif (!sourceStat.isFile()) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function \"${fn.slug}\" source ${fn.source} is neither a file nor a directory.`,\n\t\t);\n\t}\n\n\tconst name = basename(fn.source);\n\tif (name === \"index.ts\") {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\ttypescriptUnbundledMessage(fn, via),\n\t\t);\n\t}\n\tif (!isFunctionArchiveEntry(name)) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\tvia === \"no-bundle\"\n\t\t\t\t? `${fn.source} must be named index.mjs or index.js to ship with --no-bundle (got \"${name}\"). Omit --no-bundle to esbuild it, or rename the file.`\n\t\t\t\t: `Function \"${fn.slug}\" bundler is \"none\" but ${fn.source} is named \"${name}\". ` +\n\t\t\t\t\t\t`The Functions runtime imports index.mjs or index.js at the archive root.`,\n\t\t);\n\t}\n\treturn { [name]: new Uint8Array(await readFile(fn.source)) };\n}\n\nconst typescriptUnbundledMessage = (\n\tfn: ResolvedFunctionConfig,\n\tvia: UnbundledVia,\n): string =>\n\tvia === \"no-bundle\"\n\t\t? `TypeScript must be bundled. ${fn.source} has no index.mjs or index.js; omit --no-bundle to esbuild it, or emit one of those files.`\n\t\t: `Function \"${fn.slug}\" bundler is \"none\" but ${fn.source} is TypeScript. ` +\n\t\t\t`Use the default esbuild bundler, or emit index.mjs / index.js.`;\n\nasync function collectDirectory(\n\troot: string,\n\tdir: string,\n\tentries: FunctionBundle,\n\trootReal: string,\n\tstack: readonly string[],\n): Promise<void> {\n\tconst dirReal = await realpath(dir);\n\tif (stack.includes(dirReal)) return;\n\tconst nextStack = [...stack, dirReal];\n\tconst dirents = await readdir(dir, { withFileTypes: true });\n\tawait Promise.all(\n\t\tdirents.map(async (dirent) => {\n\t\t\tconst abs = join(dir, dirent.name);\n\t\t\tif (dirent.isDirectory()) {\n\t\t\t\tawait collectDirectory(root, abs, entries, rootReal, nextStack);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (dirent.isSymbolicLink()) {\n\t\t\t\tawait assertSymlinkInsideSource(abs, root, rootReal);\n\t\t\t\tconst target = await stat(abs).catch(() => null);\n\t\t\t\tif (!target) return;\n\t\t\t\tif (target.isDirectory()) {\n\t\t\t\t\tawait collectDirectory(\n\t\t\t\t\t\troot,\n\t\t\t\t\t\tabs,\n\t\t\t\t\t\tentries,\n\t\t\t\t\t\trootReal,\n\t\t\t\t\t\tnextStack,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst key = relative(root, abs).split(sep).join(\"/\");\n\t\t\tentries[key] = new Uint8Array(await readFile(abs));\n\t\t}),\n\t);\n}\n\nconst assertSymlinkInsideSource = async (\n\tabs: string,\n\troot: string,\n\trootReal: string,\n): Promise<void> => {\n\tlet targetReal: string;\n\ttry {\n\t\ttargetReal = await realpath(abs);\n\t} catch (cause) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function source ${root} contains a dangling symlink: ${abs}.`,\n\t\t\t{ cause },\n\t\t);\n\t}\n\tconst escaped = relative(rootReal, targetReal);\n\tif (\n\t\tescaped === \"..\" ||\n\t\tescaped.startsWith(`..${sep}`) ||\n\t\tisAbsolute(escaped)\n\t) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.InvalidConfig,\n\t\t\t`Function source ${root} contains a symlink that points outside the directory: ${abs}.`,\n\t\t);\n\t}\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/lib/function-zip.d.ts
|
|
2
|
+
/** No size check — the esbuild path without staged native files stayed unlimited. */
|
|
3
|
+
declare function zipEntries(entries: Record<string, Uint8Array>): Promise<Uint8Array>;
|
|
4
|
+
declare function zipFunctionBundle(slug: string, entries: Record<string, Uint8Array>): Promise<Uint8Array>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { zipEntries, zipFunctionBundle };
|
|
7
|
+
//# sourceMappingURL=function-zip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-zip.d.ts","names":[],"sources":["../../src/lib/function-zip.ts"],"mappings":";;AAIsB,iBAAA,UAAA,CAAU,OAAA,EACtB,MADsB,CAAA,MAAA,EACP,UADO,CAAA,CAAA,EAE7B,OAF6B,CAErB,UAFqB,CAAA;AAAA,iBAOV,iBAAA,CAPU,IAAA,EAAA,MAAA,EAAA,OAAA,EAStB,MATsB,CAAA,MAAA,EASP,UATO,CAAA,CAAA,EAU7B,OAV6B,CAUrB,UAVqB,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { assertZipWithinLimits, enforceLimits } from "./native-packages.js";
|
|
2
|
+
import { ErrorCode, PlatformError } from "@neon/config";
|
|
3
|
+
//#region src/lib/function-zip.ts
|
|
4
|
+
/** No size check — the esbuild path without staged native files stayed unlimited. */
|
|
5
|
+
async function zipEntries(entries) {
|
|
6
|
+
const { zipSync } = await loadFflate();
|
|
7
|
+
return zipSync(entries, { level: 6 });
|
|
8
|
+
}
|
|
9
|
+
async function zipFunctionBundle(slug, entries) {
|
|
10
|
+
enforceLimits(slug, entries);
|
|
11
|
+
const zip = await zipEntries(entries);
|
|
12
|
+
assertZipWithinLimits(slug, zip, entries);
|
|
13
|
+
return zip;
|
|
14
|
+
}
|
|
15
|
+
async function loadFflate() {
|
|
16
|
+
try {
|
|
17
|
+
return await import("fflate");
|
|
18
|
+
} catch (cause) {
|
|
19
|
+
throw new PlatformError(ErrorCode.InvalidConfig, ["Deploying Neon Functions requires `fflate`, which could not be loaded.", "It is a dependency of @neon/config-runtime — reinstall your dependencies (`pnpm install` / `npm install`)."].join(" "), { cause });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { zipEntries, zipFunctionBundle };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=function-zip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"function-zip.js","names":[],"sources":["../../src/lib/function-zip.ts"],"sourcesContent":["import { ErrorCode, PlatformError } from \"@neon/config\";\nimport { assertZipWithinLimits, enforceLimits } from \"./native-packages.js\";\n\n/** No size check — the esbuild path without staged native files stayed unlimited. */\nexport async function zipEntries(\n\tentries: Record<string, Uint8Array>,\n): Promise<Uint8Array> {\n\tconst { zipSync } = await loadFflate();\n\treturn zipSync(entries, { level: 6 });\n}\n\nexport async function zipFunctionBundle(\n\tslug: string,\n\tentries: Record<string, Uint8Array>,\n): Promise<Uint8Array> {\n\tenforceLimits(slug, entries);\n\tconst zip = await zipEntries(entries);\n\tassertZipWithinLimits(slug, zip, entries);\n\treturn zip;\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 @neon/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":";;;;AAIA,eAAsB,WACrB,SACsB;CACtB,MAAM,EAAE,YAAY,MAAM,WAAW;CACrC,OAAO,QAAQ,SAAS,EAAE,OAAO,EAAE,CAAC;AACrC;AAEA,eAAsB,kBACrB,MACA,SACsB;CACtB,cAAc,MAAM,OAAO;CAC3B,MAAM,MAAM,MAAM,WAAW,OAAO;CACpC,sBAAsB,MAAM,KAAK,OAAO;CACxC,OAAO;AACR;AAEA,eAAe,aAA+C;CAC7D,IAAI;EACH,OAAO,MAAM,OAAO;CACrB,SAAS,OAAO;EACf,MAAM,IAAI,cACT,UAAU,eACV,CACC,0EACA,4GACD,CAAC,CAAC,KAAK,GAAG,GACV,EAAE,MAAM,CACT;CACD;AACD"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { installedPackageVersion, readPackageManifest } from "./resolve-package.js";
|
|
2
2
|
import { ErrorCode, PlatformError, externalPackageRoot } from "@neon/config";
|
|
3
3
|
import { join, resolve, sep } from "node:path";
|
|
4
|
-
import { lstatSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
4
|
import { spawn } from "node:child_process";
|
|
5
|
+
import { lstatSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
//#region src/lib/native-packages.ts
|
|
8
8
|
/**
|
package/dist/lib/operations.d.ts
CHANGED
|
@@ -39,11 +39,7 @@ interface ApplyOptions extends ConfigOperationOptions {
|
|
|
39
39
|
updateExisting?: boolean;
|
|
40
40
|
/** Auto-confirm applying to a branch marked `protected` on Neon. */
|
|
41
41
|
allowProtectedBranch?: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Custom function bundler. Defaults to esbuild (`buildFunctionBundle`); inject
|
|
44
|
-
* your own to deploy functions without pulling esbuild's native binary into
|
|
45
|
-
* your build. See {@link FunctionBundler}.
|
|
46
|
-
*/
|
|
42
|
+
/** Inject to deploy without this package loading esbuild. */
|
|
47
43
|
bundleFunction?: FunctionBundler;
|
|
48
44
|
}
|
|
49
45
|
/**
|
|
@@ -87,7 +83,7 @@ interface CreateBranchOptions {
|
|
|
87
83
|
apiKey?: string;
|
|
88
84
|
/** Inject a custom NeonApi adapter (primarily for tests). */
|
|
89
85
|
api?: NeonApi;
|
|
90
|
-
/**
|
|
86
|
+
/** Inject to deploy without this package loading esbuild. */
|
|
91
87
|
bundleFunction?: FunctionBundler;
|
|
92
88
|
}
|
|
93
89
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.d.ts","names":[],"sources":["../../src/lib/operations.ts"],"mappings":";;;;;;;;;AAsBA;AAsBA;AAA8B;
|
|
1
|
+
{"version":3,"file":"operations.d.ts","names":[],"sources":["../../src/lib/operations.ts"],"mappings":";;;;;;;;;AAsBA;AAsBA;AAA8B;AASZ,UA/BD,sBAAA,CA+BC;EAToB;AAAsB;AAkB5D;AAA6B;EACnB,SAAA,EAAA,MAAA;EACC;AAAR;AAAO;AAkBV;EAA0B,QAAA,EAAA,MAAA;EACjB;EACC,MAAA,CAAA,EAAA,MAAA;EACC;EAAR,OAAA,CAAA,EAAA,MAAA;EAAO;EAwBY,GAAA,CAAA,EAvEf,iBAuEoB,CAAA,KAAA,CAAA;AAAA;AAClB;AACC;AACC;AAAR,UApEc,YAAA,SAAqB,sBAoEnC,CAAA;EAAO;AAmBV;AAAoC;AAQ7B;EAEW,cAAA,CAAA,EAAA,OAAA;EAAe;EAOhB,oBAAA,CAAkB,EAAA,OAAA;EAmCb;EAAY,cAAA,CAAA,EAlIhB,eAkIgB;AACzB;AACC;AACC;AAAR;AAAO;;;iBA5HY,OAAA,UACZ,yBACP,QAAQ;;;;;;;;;iBAkBW,IAAA,SACb,iBACC,yBACP,QAAQ;;;;;;;;;;;;iBAwBW,KAAA,SACb,iBACC,eACP,QAAQ;;;;;UAmBM,mBAAA;;;;;;;;QAQV;;mBAEW;;;;;;UAOD,kBAAA;;;;UAIR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+Ba,YAAA,SACb,iBACC,sBACP,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.js","names":[],"sources":["../../src/lib/operations.ts"],"sourcesContent":["import {\n\ttype AppliedChange,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\tPartialBranchCreateError,\n\tPlatformError,\n\ttype PushResult,\n\tresolveConfig,\n} from \"@neon/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 **with** that tuning: `parent`, `expires_at`, `protected`, and the\n * compute settings all ride along on the single create call, which Neon validates as a\n * whole. A setting it rejects therefore fails the creation outright, leaving nothing\n * behind, rather than producing a branch that doesn't match the policy.\n * 3. {@link pushConfig} the rest onto it with `branchExists: false` — the services (Neon\n * Auth, Data API, buckets, functions), which have no create-time equivalent because they\n * are provisioned against an existing branch id.\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 * `result.applied` covers both steps — the settings the create call carried are reported\n * exactly like the ones a push applies (see {@link settingsAppliedAtCreate}), so folding them\n * into the creation doesn't make them vanish from the summary.\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 — both before\n * anything is created, as is a setting Neon rejects in step 2. Only step 3 can fail with a\n * branch already created; that throws {@link PartialBranchCreateError} carrying its id/name.\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\t// Everything the policy can express *in the create call itself* goes into it. Neon\n\t// validates the whole request, so a value it rejects (a plan-gated suspend timeout, an\n\t// out-of-range autoscaling limit) fails with **no branch created** instead of leaving one\n\t// behind that silently diverges from the policy. Only the services below genuinely need an\n\t// existing branch id, which is what keeps them a second step.\n\tconst expiresAt =\n\t\tresolved.ttlSeconds !== undefined\n\t\t\t? new Date(Date.now() + resolved.ttlSeconds * 1000).toISOString()\n\t\t\t: undefined;\n\tconst computeSettings = resolved.postgres?.computeSettings;\n\n\tconst { branch } = await api.createBranch(projectId, {\n\t\tname: branchName,\n\t\t...(parentId ? { parentId } : {}),\n\t\t...(expiresAt ? { expiresAt } : {}),\n\t\t...(resolved.protected !== undefined\n\t\t\t? { protected: resolved.protected }\n\t\t\t: {}),\n\t\t...(computeSettings ? { computeSettings } : {}),\n\t});\n\n\tconst appliedAtCreate = settingsAppliedAtCreate({\n\t\tbranchName: branch.name,\n\t\t...(resolved.parent !== undefined\n\t\t\t? { parentName: resolved.parent }\n\t\t\t: {}),\n\t\t...(expiresAt !== undefined ? { expiresAt } : {}),\n\t\t...(resolved.protected !== undefined\n\t\t\t? { isProtected: resolved.protected }\n\t\t\t: {}),\n\t\t...(computeSettings ? { computeSettings } : {}),\n\t});\n\n\t// Reconcile what the create call could not express as a new branch (`branchExists: false`):\n\t// the services/functions the policy declares. The settings above are already in place, so\n\t// push sees them satisfied and applies nothing for them.\n\t// `updateExisting`/`allowProtectedBranch` are safe here — there is no pre-existing state a\n\t// user would be surprised to see overridden.\n\t//\n\t// The branch is already created at this point, so a failure here (a service that can't be\n\t// provisioned, a function that fails to deploy, a transient API error) cannot be undone by\n\t// throwing: the branch would linger without its declared services, invisible to the caller.\n\t// Re-throw as PartialBranchCreateError so the id/name survive and the caller can keep the\n\t// branch usable while reporting that it diverges from the policy.\n\tlet result: PushResult;\n\ttry {\n\t\tresult = await pushConfig(config, {\n\t\t\tprojectId,\n\t\t\tbranchId: branch.id,\n\t\t\tapi,\n\t\t\tbranchExists: false,\n\t\t\tupdateExisting: true,\n\t\t\tallowProtectedBranch: true,\n\t\t\t...(options.bundleFunction\n\t\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t\t: {}),\n\t\t});\n\t} catch (cause) {\n\t\tthrow new PartialBranchCreateError(branch.id, branch.name, cause);\n\t}\n\n\treturn {\n\t\tbranchId: branch.id,\n\t\tbranchName: branch.name,\n\t\tresult: {\n\t\t\t...result,\n\t\t\tapplied: mergeCreateSettings(appliedAtCreate, result.applied),\n\t\t},\n\t};\n}\n\n/**\n * Describe the policy settings the create call itself carried as {@link AppliedChange}s, keyed\n * by field.\n *\n * Applying a setting *at creation* rather than in a follow-up call must not make it disappear\n * from the report, so these are shaped exactly like the changes `pushConfig` returns for the\n * same fields — same `kind`/`identifier`/`details`, so every renderer already knows how to\n * print them and no caller needs to special-case a creation.\n */\nconst settingsAppliedAtCreate = (args: {\n\tbranchName: string;\n\t/** The parent the *policy* named, omitted when the branch fell back to the project default. */\n\tparentName?: string;\n\texpiresAt?: string;\n\tisProtected?: boolean;\n\tcomputeSettings?: ComputeSettings;\n}): Map<string, AppliedChange> => {\n\tconst changes = new Map<string, AppliedChange>();\n\tconst add = (field: string, details: Record<string, unknown>): void => {\n\t\tchanges.set(field, {\n\t\t\tkind: \"branch\",\n\t\t\taction: \"create\",\n\t\t\tidentifier: args.branchName,\n\t\t\tdetails: { field, ...details },\n\t\t});\n\t};\n\tif (args.parentName !== undefined) {\n\t\tadd(\"parent\", { parent: args.parentName });\n\t}\n\tif (args.expiresAt !== undefined) add(\"ttl\", { expiresAt: args.expiresAt });\n\tif (args.isProtected !== undefined) {\n\t\tadd(\"protected\", { protected: args.isProtected });\n\t}\n\tif (args.computeSettings) {\n\t\tadd(\"computeSettings\", { settings: args.computeSettings });\n\t}\n\treturn changes;\n};\n\n/**\n * Fold the create-time settings into what the push applied, reporting each field once.\n *\n * A field the create call already satisfied is a noop for the push, so the two normally don't\n * overlap. They do when the create didn't take (an API that ignores a create-time field, or\n * drift in the moments between the two calls) and the push applied it for real — the push\n * change is the one that describes what actually happened, so it wins.\n */\nconst mergeCreateSettings = (\n\tatCreate: Map<string, AppliedChange>,\n\tpushed: AppliedChange[],\n): AppliedChange[] => {\n\tconst pushedFields = new Set(\n\t\tpushed\n\t\t\t.filter((c) => c.kind === \"branch\" && c.action !== \"noop\")\n\t\t\t.map((c) => c.details?.field)\n\t\t\t.filter((field): field is string => typeof field === \"string\"),\n\t);\n\tconst fromCreate = [...atCreate]\n\t\t.filter(([field]) => !pushedFields.has(field))\n\t\t.map(([, change]) => change);\n\treturn [...fromCreate, ...pushed];\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 AppliedChange,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\tPartialBranchCreateError,\n\tPlatformError,\n\ttype PushResult,\n\tresolveConfig,\n} from \"@neon/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/** Inject to deploy without this package loading esbuild. */\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/** Inject to deploy without this package loading esbuild. */\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 **with** that tuning: `parent`, `expires_at`, `protected`, and the\n * compute settings all ride along on the single create call, which Neon validates as a\n * whole. A setting it rejects therefore fails the creation outright, leaving nothing\n * behind, rather than producing a branch that doesn't match the policy.\n * 3. {@link pushConfig} the rest onto it with `branchExists: false` — the services (Neon\n * Auth, Data API, buckets, functions), which have no create-time equivalent because they\n * are provisioned against an existing branch id.\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 * `result.applied` covers both steps — the settings the create call carried are reported\n * exactly like the ones a push applies (see {@link settingsAppliedAtCreate}), so folding them\n * into the creation doesn't make them vanish from the summary.\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 — both before\n * anything is created, as is a setting Neon rejects in step 2. Only step 3 can fail with a\n * branch already created; that throws {@link PartialBranchCreateError} carrying its id/name.\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\t// Everything the policy can express *in the create call itself* goes into it. Neon\n\t// validates the whole request, so a value it rejects (a plan-gated suspend timeout, an\n\t// out-of-range autoscaling limit) fails with **no branch created** instead of leaving one\n\t// behind that silently diverges from the policy. Only the services below genuinely need an\n\t// existing branch id, which is what keeps them a second step.\n\tconst expiresAt =\n\t\tresolved.ttlSeconds !== undefined\n\t\t\t? new Date(Date.now() + resolved.ttlSeconds * 1000).toISOString()\n\t\t\t: undefined;\n\tconst computeSettings = resolved.postgres?.computeSettings;\n\n\tconst { branch } = await api.createBranch(projectId, {\n\t\tname: branchName,\n\t\t...(parentId ? { parentId } : {}),\n\t\t...(expiresAt ? { expiresAt } : {}),\n\t\t...(resolved.protected !== undefined\n\t\t\t? { protected: resolved.protected }\n\t\t\t: {}),\n\t\t...(computeSettings ? { computeSettings } : {}),\n\t});\n\n\tconst appliedAtCreate = settingsAppliedAtCreate({\n\t\tbranchName: branch.name,\n\t\t...(resolved.parent !== undefined\n\t\t\t? { parentName: resolved.parent }\n\t\t\t: {}),\n\t\t...(expiresAt !== undefined ? { expiresAt } : {}),\n\t\t...(resolved.protected !== undefined\n\t\t\t? { isProtected: resolved.protected }\n\t\t\t: {}),\n\t\t...(computeSettings ? { computeSettings } : {}),\n\t});\n\n\t// Reconcile what the create call could not express as a new branch (`branchExists: false`):\n\t// the services/functions the policy declares. The settings above are already in place, so\n\t// push sees them satisfied and applies nothing for them.\n\t// `updateExisting`/`allowProtectedBranch` are safe here — there is no pre-existing state a\n\t// user would be surprised to see overridden.\n\t//\n\t// The branch is already created at this point, so a failure here (a service that can't be\n\t// provisioned, a function that fails to deploy, a transient API error) cannot be undone by\n\t// throwing: the branch would linger without its declared services, invisible to the caller.\n\t// Re-throw as PartialBranchCreateError so the id/name survive and the caller can keep the\n\t// branch usable while reporting that it diverges from the policy.\n\tlet result: PushResult;\n\ttry {\n\t\tresult = await pushConfig(config, {\n\t\t\tprojectId,\n\t\t\tbranchId: branch.id,\n\t\t\tapi,\n\t\t\tbranchExists: false,\n\t\t\tupdateExisting: true,\n\t\t\tallowProtectedBranch: true,\n\t\t\t...(options.bundleFunction\n\t\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t\t: {}),\n\t\t});\n\t} catch (cause) {\n\t\tthrow new PartialBranchCreateError(branch.id, branch.name, cause);\n\t}\n\n\treturn {\n\t\tbranchId: branch.id,\n\t\tbranchName: branch.name,\n\t\tresult: {\n\t\t\t...result,\n\t\t\tapplied: mergeCreateSettings(appliedAtCreate, result.applied),\n\t\t},\n\t};\n}\n\n/**\n * Describe the policy settings the create call itself carried as {@link AppliedChange}s, keyed\n * by field.\n *\n * Applying a setting *at creation* rather than in a follow-up call must not make it disappear\n * from the report, so these are shaped exactly like the changes `pushConfig` returns for the\n * same fields — same `kind`/`identifier`/`details`, so every renderer already knows how to\n * print them and no caller needs to special-case a creation.\n */\nconst settingsAppliedAtCreate = (args: {\n\tbranchName: string;\n\t/** The parent the *policy* named, omitted when the branch fell back to the project default. */\n\tparentName?: string;\n\texpiresAt?: string;\n\tisProtected?: boolean;\n\tcomputeSettings?: ComputeSettings;\n}): Map<string, AppliedChange> => {\n\tconst changes = new Map<string, AppliedChange>();\n\tconst add = (field: string, details: Record<string, unknown>): void => {\n\t\tchanges.set(field, {\n\t\t\tkind: \"branch\",\n\t\t\taction: \"create\",\n\t\t\tidentifier: args.branchName,\n\t\t\tdetails: { field, ...details },\n\t\t});\n\t};\n\tif (args.parentName !== undefined) {\n\t\tadd(\"parent\", { parent: args.parentName });\n\t}\n\tif (args.expiresAt !== undefined) add(\"ttl\", { expiresAt: args.expiresAt });\n\tif (args.isProtected !== undefined) {\n\t\tadd(\"protected\", { protected: args.isProtected });\n\t}\n\tif (args.computeSettings) {\n\t\tadd(\"computeSettings\", { settings: args.computeSettings });\n\t}\n\treturn changes;\n};\n\n/**\n * Fold the create-time settings into what the push applied, reporting each field once.\n *\n * A field the create call already satisfied is a noop for the push, so the two normally don't\n * overlap. They do when the create didn't take (an API that ignores a create-time field, or\n * drift in the moments between the two calls) and the push applied it for real — the push\n * change is the one that describes what actually happened, so it wins.\n */\nconst mergeCreateSettings = (\n\tatCreate: Map<string, AppliedChange>,\n\tpushed: AppliedChange[],\n): AppliedChange[] => {\n\tconst pushedFields = new Set(\n\t\tpushed\n\t\t\t.filter((c) => c.kind === \"branch\" && c.action !== \"noop\")\n\t\t\t.map((c) => c.details?.field)\n\t\t\t.filter((field): field is string => typeof field === \"string\"),\n\t);\n\tconst fromCreate = [...atCreate]\n\t\t.filter(([field]) => !pushedFields.has(field))\n\t\t.map(([, change]) => change);\n\treturn [...fromCreate, ...pushed];\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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"}
|
|
@@ -69,11 +69,7 @@ interface PushConfigOptions {
|
|
|
69
69
|
* Never invoked on `dryRun`.
|
|
70
70
|
*/
|
|
71
71
|
confirm?: (context: PushConfirmContext) => boolean | Promise<boolean>;
|
|
72
|
-
/**
|
|
73
|
-
* Custom bundler for function source. Defaults to {@link buildFunctionBundle}
|
|
74
|
-
* (esbuild). Inject your own to deploy functions without this package pulling
|
|
75
|
-
* esbuild's native binary into your build — see {@link FunctionBundler}.
|
|
76
|
-
*/
|
|
72
|
+
/** Inject to deploy without this package loading esbuild. */
|
|
77
73
|
bundleFunction?: FunctionBundler;
|
|
78
74
|
/**
|
|
79
75
|
* When `true`, compute the full plan against the live remote state but **do not
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push-config.d.ts","names":[],"sources":["../../src/lib/push-config.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"push-config.d.ts","names":[],"sources":["../../src/lib/push-config.ts"],"mappings":";;;;UA8BiB,iBAAA;;AAAjB;AAAkC;AAoB3B;AA8Cc;EAAiC,SAAA,EAAA,MAAA;EAEpC;AAAe;AAmBjC;AA2BA;EAAgC,QAAA,EAAA,MAAA;EACvB;EACC,MAAA,CAAA,EAAA,MAAA;EACC;EAAR,OAAA,CAAA,EAAA,MAAA;EAAO;;;;QAjGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBA8Cc,iCAAiC;;mBAEpC;;;;;;;;;;;;;;;;;;UAmBD,kBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2BK,UAAA,SACb,iBACC,oBACP,QAAQ"}
|
package/dist/lib/push-config.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
1
|
import { ErrorCode, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, diffConfig, resolveConfig } from "@neon/config";
|
|
2
2
|
//#region src/lib/push-config.ts
|
|
3
|
-
/**
|
|
4
|
-
* Default function bundler (esbuild), loaded lazily so that `buildFunctionBundle`
|
|
5
|
-
* — and the esbuild it pulls in — only enters the module graph when a deploy
|
|
6
|
-
* actually needs it AND no custom `bundleFunction` was injected. A consumer that
|
|
7
|
-
* injects its own bundler never triggers this import, so esbuild can be dropped
|
|
8
|
-
* from their build entirely.
|
|
9
|
-
*/
|
|
10
3
|
const makeDefaultBundleFunction = (onWarning) => async (fn) => {
|
|
11
4
|
const { buildFunctionBundle } = await import("./function-bundle.js");
|
|
12
5
|
return buildFunctionBundle(fn, { onWarning });
|
|
@@ -44,7 +37,7 @@ async function pushConfig(config, options) {
|
|
|
44
37
|
projectId: remoteProject.id,
|
|
45
38
|
branch,
|
|
46
39
|
wantsAuth: resolved.authEnabled,
|
|
47
|
-
wantsDataApi: resolved.dataApiEnabled
|
|
40
|
+
wantsDataApi: resolved.dataApiEnabled || resolved.dataApiPolicy === "disabled"
|
|
48
41
|
});
|
|
49
42
|
const remote = {
|
|
50
43
|
projectId: remoteProject.id,
|
|
@@ -119,12 +112,8 @@ async function pushConfig(config, options) {
|
|
|
119
112
|
if (remoteProject.orgId) result.orgId = remoteProject.orgId;
|
|
120
113
|
return result;
|
|
121
114
|
}
|
|
122
|
-
/**
|
|
123
|
-
* `update-*` plan steps mutate existing remote state. `enable-*` steps are additive (no
|
|
124
|
-
* existing resource to override) and never trigger the override-confirm prompt.
|
|
125
|
-
*/
|
|
126
115
|
function isOverrideStep(step) {
|
|
127
|
-
return step.kind === "update-branch-ttl" || step.kind === "update-branch-protected" || step.kind === "update-endpoint" || step.kind === "update-data-api";
|
|
116
|
+
return step.kind === "update-branch-ttl" || step.kind === "update-branch-protected" || step.kind === "update-endpoint" || step.kind === "update-data-api" || step.kind === "disable-data-api";
|
|
128
117
|
}
|
|
129
118
|
/**
|
|
130
119
|
* Build an {@link AppliedChange} from a {@link PlanStep} without calling the Neon API.
|
|
@@ -181,6 +170,11 @@ function synthesizeAppliedChange(step) {
|
|
|
181
170
|
settings: step.settings
|
|
182
171
|
}
|
|
183
172
|
};
|
|
173
|
+
case "disable-data-api": return {
|
|
174
|
+
kind: "service",
|
|
175
|
+
action: "delete",
|
|
176
|
+
identifier: "dataApi"
|
|
177
|
+
};
|
|
184
178
|
case "create-bucket": return {
|
|
185
179
|
kind: "service",
|
|
186
180
|
action: "create",
|
|
@@ -337,6 +331,13 @@ async function applyStep(step, ctx) {
|
|
|
337
331
|
settings: step.settings
|
|
338
332
|
}
|
|
339
333
|
};
|
|
334
|
+
case "disable-data-api":
|
|
335
|
+
await ctx.api.deleteProjectBranchDataApi(ctx.remoteProjectId, step.branchId, step.databaseName);
|
|
336
|
+
return {
|
|
337
|
+
kind: "service",
|
|
338
|
+
action: "delete",
|
|
339
|
+
identifier: "dataApi"
|
|
340
|
+
};
|
|
340
341
|
case "create-bucket":
|
|
341
342
|
await ctx.api.createBranchBucket(ctx.remoteProjectId, step.branchId, {
|
|
342
343
|
name: step.bucketName,
|
|
@@ -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 \"@neon/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 makeDefaultBundleFunction =\n\t(onWarning: (message: string) => void): FunctionBundler =>\n\tasync (fn: ResolvedFunctionConfig): Promise<Uint8Array> => {\n\t\tconst { buildFunctionBundle } = await import(\"./function-bundle.js\");\n\t\treturn buildFunctionBundle(fn, { onWarning });\n\t};\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: (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\t// Collected from the built-in bundler and returned on the result. A caller that injects\n\t// its own `bundleFunction` reports for itself, so this stays empty for them.\n\tconst warnings: string[] = [];\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 ??\n\t\t\t\t\t\tmakeDefaultBundleFunction((message) => {\n\t\t\t\t\t\t\twarnings.push(message);\n\t\t\t\t\t\t}),\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\twarnings,\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\tstep.kind === \"update-data-api\"\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 \"update-data-api\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: \"dataApi\",\n\t\t\t\tdetails: { field: \"settings\", settings: step.settings },\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\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\tconst result: RemoteServiceState = {\n\t\tdatabaseName,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t};\n\t// Carry the current Data API settings (when reported) so the diff can detect settings\n\t// drift and plan an update. `null` distinguishes \"enabled but not reported\" from \"absent\".\n\tif (dataApi) result.dataApiSettings = dataApi.settings ?? null;\n\treturn result;\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 (`@neon/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\tstep.input,\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 \"update-data-api\": {\n\t\t\tawait ctx.api.updateProjectBranchDataApi(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.databaseName,\n\t\t\t\tstep.settings,\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: \"dataApi\",\n\t\t\t\tdetails: { field: \"settings\", settings: step.settings },\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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"}
|
|
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 \"@neon/config\";\nimport type { FunctionBundler } from \"./function-bundle.js\";\n\n// Eager loading would evaluate esbuild for callers that provide their own bundler.\nconst makeDefaultBundleFunction =\n\t(onWarning: (message: string) => void): FunctionBundler =>\n\tasync (fn: ResolvedFunctionConfig): Promise<Uint8Array> => {\n\t\tconst { buildFunctionBundle } = await import(\"./function-bundle.js\");\n\t\treturn buildFunctionBundle(fn, { onWarning });\n\t};\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: (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/** Inject to deploy without this package loading esbuild. */\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\t// Collected from the built-in bundler and returned on the result. A caller that injects\n\t// its own `bundleFunction` reports for itself, so this stays empty for them.\n\tconst warnings: string[] = [];\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:\n\t\t\tresolved.dataApiEnabled || resolved.dataApiPolicy === \"disabled\",\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 ??\n\t\t\t\t\t\tmakeDefaultBundleFunction((message) => {\n\t\t\t\t\t\t\twarnings.push(message);\n\t\t\t\t\t\t}),\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\twarnings,\n\t};\n\tif (remoteProject.orgId) result.orgId = remoteProject.orgId;\n\treturn result;\n}\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\tstep.kind === \"update-data-api\" ||\n\t\tstep.kind === \"disable-data-api\"\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 \"update-data-api\":\n\t\t\treturn {\n\t\t\t\tkind: \"service\",\n\t\t\t\taction: \"update\",\n\t\t\t\tidentifier: \"dataApi\",\n\t\t\t\tdetails: { field: \"settings\", settings: step.settings },\n\t\t\t};\n\t\tcase \"disable-data-api\":\n\t\t\treturn { kind: \"service\", action: \"delete\", 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\tconst result: RemoteServiceState = {\n\t\tdatabaseName,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t};\n\t// Carry the current Data API settings (when reported) so the diff can detect settings\n\t// drift and plan an update. `null` distinguishes \"enabled but not reported\" from \"absent\".\n\tif (dataApi) result.dataApiSettings = dataApi.settings ?? null;\n\treturn result;\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 (`@neon/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\tstep.input,\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 \"update-data-api\": {\n\t\t\tawait ctx.api.updateProjectBranchDataApi(\n\t\t\t\tctx.remoteProjectId,\n\t\t\t\tstep.branchId,\n\t\t\t\tstep.databaseName,\n\t\t\t\tstep.settings,\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: \"dataApi\",\n\t\t\t\tdetails: { field: \"settings\", settings: step.settings },\n\t\t\t};\n\t\t}\n\t\tcase \"disable-data-api\": {\n\t\t\tawait ctx.api.deleteProjectBranchDataApi(\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: \"delete\",\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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { ArchiveLimits, DEFAULT_ARCHIVE_LIMITS, NativeTraceDeps, NativeTraceResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, assertZipWithinLimits, enforceLimits, traceNativePackages } from "./lib/native-packages.js";
|
|
2
2
|
import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
|
|
3
|
+
import { bundleAsIs, resolveEsbuildEntry } from "./lib/function-source.js";
|
|
4
|
+
import { zipFunctionBundle } from "./lib/function-zip.js";
|
|
3
5
|
import { NativeEvidence, NativeFinding, describeNativeFinding, findUndeclaredNativePackages } from "./lib/native-detect.js";
|
|
4
6
|
import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
|
|
5
7
|
import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
|
|
6
8
|
import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
7
|
-
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
8
|
-
export { type AppliedChange, type ApplyOptions, type ArchiveLimits, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, DEFAULT_ARCHIVE_LIMITS, ErrorCode, type FunctionBundler, type LoadConfigOptions, MissingContextError, type NativeEvidence, type NativeFinding, type NativeTraceDeps, type NativeTraceResult, type NeonApi, PartialBranchCreateError, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, traceNativePackages };
|
|
9
|
+
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, FunctionBundle, FunctionBundlerInput, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
10
|
+
export { type AppliedChange, type ApplyOptions, type ArchiveLimits, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, DEFAULT_ARCHIVE_LIMITS, ErrorCode, type FunctionBundle, type FunctionBundler, type FunctionBundlerInput, type LoadConfigOptions, MissingContextError, type NativeEvidence, type NativeFinding, type NativeTraceDeps, type NativeTraceResult, type NeonApi, PartialBranchCreateError, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle };
|
package/dist/v1.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { bundleAsIs, resolveEsbuildEntry } from "./lib/function-source.js";
|
|
1
2
|
import { DEFAULT_ARCHIVE_LIMITS, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, assertZipWithinLimits, enforceLimits, traceNativePackages } from "./lib/native-packages.js";
|
|
3
|
+
import { zipFunctionBundle } from "./lib/function-zip.js";
|
|
2
4
|
import { describeNativeFinding, findUndeclaredNativePackages } from "./lib/native-detect.js";
|
|
3
5
|
import { buildFunctionBundle } from "./lib/function-bundle.js";
|
|
4
6
|
import { pullConfig } from "./lib/pull-config.js";
|
|
5
7
|
import { pushConfig } from "./lib/push-config.js";
|
|
6
8
|
import { apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
7
9
|
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
8
|
-
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, traceNativePackages };
|
|
10
|
+
export { ConfigLoadError, ConfigValidationError, DEFAULT_ARCHIVE_LIMITS, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, RUNTIME_TARGET, RUNTIME_TARGET_LABEL, apply, assertZipWithinLimits, buildFunctionBundle, bundleAsIs, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, describeNativeFinding, enforceLimits, findUndeclaredNativePackages, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig, resolveEsbuildEntry, traceNativePackages, zipFunctionBundle };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neon/config-runtime",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Imperative runtime for @neon/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",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"typescript": "^5.9.0",
|
|
49
49
|
"vitest": "^3.0.9",
|
|
50
50
|
"@neon/e2e-harness": "0.0.0",
|
|
51
|
-
"@neon/sdk": "3.
|
|
51
|
+
"@neon/sdk": "3.1.0"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@vercel/nft": "^1.10.2",
|
|
55
55
|
"esbuild": "^0.28.1",
|
|
56
56
|
"fflate": "^0.8.3",
|
|
57
|
-
"@neon/config": "1.0
|
|
57
|
+
"@neon/config": "1.2.0"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=20.19.0"
|