@neon/config-runtime 0.9.7 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/lib/operations.d.ts +14 -5
- package/dist/lib/operations.d.ts.map +1 -1
- package/dist/lib/operations.js +88 -18
- package/dist/lib/operations.js.map +1 -1
- package/dist/v1.d.ts +2 -2
- package/dist/v1.js +2 -2
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
|
|
|
2
2
|
import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
|
|
3
3
|
import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
|
|
4
4
|
import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
5
|
-
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
6
|
-
export { AppliedChange, ApplyOptions, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, ErrorCode, FunctionBundler, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
|
5
|
+
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
6
|
+
export { AppliedChange, ApplyOptions, Config, ConfigLoadError, ConfigOperationOptions, ConfigValidationError, ConflictReport, CreateBranchOptions, CreateBranchResult, ErrorCode, FunctionBundler, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PullConfigOptions, PulledBranchConfig, PulledPreview, PushAbortedError, PushConfigOptions, PushConfirmContext, PushConflictError, PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
package/dist/index.js
CHANGED
|
@@ -2,5 +2,5 @@ import { buildFunctionBundle } from "./lib/function-bundle.js";
|
|
|
2
2
|
import { pullConfig } from "./lib/pull-config.js";
|
|
3
3
|
import { pushConfig } from "./lib/push-config.js";
|
|
4
4
|
import { apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
5
|
-
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
6
|
-
export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
|
5
|
+
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "./v1.js";
|
|
6
|
+
export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
package/dist/lib/operations.d.ts
CHANGED
|
@@ -108,16 +108,25 @@ interface CreateBranchResult {
|
|
|
108
108
|
* 1. Evaluate the policy for the new branch with `exists: false` (so creation-time tuning —
|
|
109
109
|
* `parent`, `ttl`, compute settings, `protected` — resolves instead of the
|
|
110
110
|
* "existing branch, leave as-is" path most policies guard with `if (branch.exists)`).
|
|
111
|
-
* 2. Create the branch
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
111
|
+
* 2. Create the branch **with** that tuning: `parent`, `expires_at`, `protected`, and the
|
|
112
|
+
* compute settings all ride along on the single create call, which Neon validates as a
|
|
113
|
+
* whole. A setting it rejects therefore fails the creation outright, leaving nothing
|
|
114
|
+
* behind, rather than producing a branch that doesn't match the policy.
|
|
115
|
+
* 3. {@link pushConfig} the rest onto it with `branchExists: false` — the services (Neon
|
|
116
|
+
* Auth, Data API, buckets, functions), which have no create-time equivalent because they
|
|
117
|
+
* are provisioned against an existing branch id.
|
|
115
118
|
*
|
|
116
119
|
* This is why `apply` alone couldn't do it: `apply` operates on an *existing* branch
|
|
117
120
|
* (`exists: true`), so a policy keyed on `!branch.exists` never returns the creation tuning.
|
|
118
121
|
*
|
|
122
|
+
* `result.applied` covers both steps — the settings the create call carried are reported
|
|
123
|
+
* exactly like the ones a push applies (see {@link settingsAppliedAtCreate}), so folding them
|
|
124
|
+
* into the creation doesn't make them vanish from the summary.
|
|
125
|
+
*
|
|
119
126
|
* Throws {@link PlatformError} (`Conflict`) if a branch with `branchName` already exists, or
|
|
120
|
-
* (`BranchNotFound`) if the policy names a `parent` that isn't on the project
|
|
127
|
+
* (`BranchNotFound`) if the policy names a `parent` that isn't on the project — both before
|
|
128
|
+
* anything is created, as is a setting Neon rejects in step 2. Only step 3 can fail with a
|
|
129
|
+
* branch already created; that throws {@link PartialBranchCreateError} carrying its id/name.
|
|
121
130
|
*/
|
|
122
131
|
declare function createBranch(config: Config, options: CreateBranchOptions): Promise<CreateBranchResult>;
|
|
123
132
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.d.ts","names":[],"sources":["../../src/lib/operations.ts"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"operations.d.ts","names":[],"sources":["../../src/lib/operations.ts"],"mappings":";;;;;;;;;AAsBA;AAsBA;AAA8B;AAaZ,UAnCD,sBAAA,CAmCC;EAboB;AAAsB;AAsB5D;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,EA3Ef,iBA2EoB,CAAA,KAAA,CAAA;AAAA;AAClB;AACC;AACC;AAAR,UAxEc,YAAA,SAAqB,sBAwEnC,CAAA;EAAO;AAmBV;AAAoC;AAQ7B;EAEW,cAAA,CAAA,EAAA,OAAA;EAAe;EAOhB,oBAAA,CAAkB,EAAA,OAAA;EAmCb;AAAY;AACzB;AACC;AACC;EAAR,cAAA,CAAA,EArIe,eAqIf;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"}
|
package/dist/lib/operations.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { pullConfig } from "./pull-config.js";
|
|
2
2
|
import { pushConfig } from "./push-config.js";
|
|
3
|
-
import { ErrorCode, PlatformError, createNeonApiFromOptions, resolveConfig } from "@neon/config";
|
|
3
|
+
import { ErrorCode, PartialBranchCreateError, PlatformError, createNeonApiFromOptions, resolveConfig } from "@neon/config";
|
|
4
4
|
//#region src/lib/operations.ts
|
|
5
5
|
/**
|
|
6
6
|
* Read a branch's live Neon state as a plain object (project + branch metadata and the
|
|
@@ -67,16 +67,25 @@ async function apply(config, options) {
|
|
|
67
67
|
* 1. Evaluate the policy for the new branch with `exists: false` (so creation-time tuning —
|
|
68
68
|
* `parent`, `ttl`, compute settings, `protected` — resolves instead of the
|
|
69
69
|
* "existing branch, leave as-is" path most policies guard with `if (branch.exists)`).
|
|
70
|
-
* 2. Create the branch
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
70
|
+
* 2. Create the branch **with** that tuning: `parent`, `expires_at`, `protected`, and the
|
|
71
|
+
* compute settings all ride along on the single create call, which Neon validates as a
|
|
72
|
+
* whole. A setting it rejects therefore fails the creation outright, leaving nothing
|
|
73
|
+
* behind, rather than producing a branch that doesn't match the policy.
|
|
74
|
+
* 3. {@link pushConfig} the rest onto it with `branchExists: false` — the services (Neon
|
|
75
|
+
* Auth, Data API, buckets, functions), which have no create-time equivalent because they
|
|
76
|
+
* are provisioned against an existing branch id.
|
|
74
77
|
*
|
|
75
78
|
* This is why `apply` alone couldn't do it: `apply` operates on an *existing* branch
|
|
76
79
|
* (`exists: true`), so a policy keyed on `!branch.exists` never returns the creation tuning.
|
|
77
80
|
*
|
|
81
|
+
* `result.applied` covers both steps — the settings the create call carried are reported
|
|
82
|
+
* exactly like the ones a push applies (see {@link settingsAppliedAtCreate}), so folding them
|
|
83
|
+
* into the creation doesn't make them vanish from the summary.
|
|
84
|
+
*
|
|
78
85
|
* Throws {@link PlatformError} (`Conflict`) if a branch with `branchName` already exists, or
|
|
79
|
-
* (`BranchNotFound`) if the policy names a `parent` that isn't on the project
|
|
86
|
+
* (`BranchNotFound`) if the policy names a `parent` that isn't on the project — both before
|
|
87
|
+
* anything is created, as is a setting Neon rejects in step 2. Only step 3 can fail with a
|
|
88
|
+
* branch already created; that throws {@link PartialBranchCreateError} carrying its id/name.
|
|
80
89
|
*/
|
|
81
90
|
async function createBranch(config, options) {
|
|
82
91
|
const api = options.api ?? createNeonApiFromOptions("createBranch", options.apiKey ? { apiKey: options.apiKey } : {});
|
|
@@ -86,30 +95,91 @@ async function createBranch(config, options) {
|
|
|
86
95
|
projectId,
|
|
87
96
|
branchName
|
|
88
97
|
} });
|
|
89
|
-
const
|
|
98
|
+
const resolved = resolveConfig(config, {
|
|
90
99
|
name: branchName,
|
|
91
100
|
exists: false
|
|
92
|
-
})
|
|
101
|
+
});
|
|
102
|
+
const parentId = resolveParentBranchId(resolved.parent, branches, branchName);
|
|
103
|
+
const expiresAt = resolved.ttlSeconds !== void 0 ? new Date(Date.now() + resolved.ttlSeconds * 1e3).toISOString() : void 0;
|
|
104
|
+
const computeSettings = resolved.postgres?.computeSettings;
|
|
93
105
|
const { branch } = await api.createBranch(projectId, {
|
|
94
106
|
name: branchName,
|
|
95
|
-
...parentId ? { parentId } : {}
|
|
107
|
+
...parentId ? { parentId } : {},
|
|
108
|
+
...expiresAt ? { expiresAt } : {},
|
|
109
|
+
...resolved.protected !== void 0 ? { protected: resolved.protected } : {},
|
|
110
|
+
...computeSettings ? { computeSettings } : {}
|
|
96
111
|
});
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
allowProtectedBranch: true,
|
|
104
|
-
...options.bundleFunction ? { bundleFunction: options.bundleFunction } : {}
|
|
112
|
+
const appliedAtCreate = settingsAppliedAtCreate({
|
|
113
|
+
branchName: branch.name,
|
|
114
|
+
...resolved.parent !== void 0 ? { parentName: resolved.parent } : {},
|
|
115
|
+
...expiresAt !== void 0 ? { expiresAt } : {},
|
|
116
|
+
...resolved.protected !== void 0 ? { isProtected: resolved.protected } : {},
|
|
117
|
+
...computeSettings ? { computeSettings } : {}
|
|
105
118
|
});
|
|
119
|
+
let result;
|
|
120
|
+
try {
|
|
121
|
+
result = await pushConfig(config, {
|
|
122
|
+
projectId,
|
|
123
|
+
branchId: branch.id,
|
|
124
|
+
api,
|
|
125
|
+
branchExists: false,
|
|
126
|
+
updateExisting: true,
|
|
127
|
+
allowProtectedBranch: true,
|
|
128
|
+
...options.bundleFunction ? { bundleFunction: options.bundleFunction } : {}
|
|
129
|
+
});
|
|
130
|
+
} catch (cause) {
|
|
131
|
+
throw new PartialBranchCreateError(branch.id, branch.name, cause);
|
|
132
|
+
}
|
|
106
133
|
return {
|
|
107
134
|
branchId: branch.id,
|
|
108
135
|
branchName: branch.name,
|
|
109
|
-
result
|
|
136
|
+
result: {
|
|
137
|
+
...result,
|
|
138
|
+
applied: mergeCreateSettings(appliedAtCreate, result.applied)
|
|
139
|
+
}
|
|
110
140
|
};
|
|
111
141
|
}
|
|
112
142
|
/**
|
|
143
|
+
* Describe the policy settings the create call itself carried as {@link AppliedChange}s, keyed
|
|
144
|
+
* by field.
|
|
145
|
+
*
|
|
146
|
+
* Applying a setting *at creation* rather than in a follow-up call must not make it disappear
|
|
147
|
+
* from the report, so these are shaped exactly like the changes `pushConfig` returns for the
|
|
148
|
+
* same fields — same `kind`/`identifier`/`details`, so every renderer already knows how to
|
|
149
|
+
* print them and no caller needs to special-case a creation.
|
|
150
|
+
*/
|
|
151
|
+
const settingsAppliedAtCreate = (args) => {
|
|
152
|
+
const changes = /* @__PURE__ */ new Map();
|
|
153
|
+
const add = (field, details) => {
|
|
154
|
+
changes.set(field, {
|
|
155
|
+
kind: "branch",
|
|
156
|
+
action: "create",
|
|
157
|
+
identifier: args.branchName,
|
|
158
|
+
details: {
|
|
159
|
+
field,
|
|
160
|
+
...details
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
if (args.parentName !== void 0) add("parent", { parent: args.parentName });
|
|
165
|
+
if (args.expiresAt !== void 0) add("ttl", { expiresAt: args.expiresAt });
|
|
166
|
+
if (args.isProtected !== void 0) add("protected", { protected: args.isProtected });
|
|
167
|
+
if (args.computeSettings) add("computeSettings", { settings: args.computeSettings });
|
|
168
|
+
return changes;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Fold the create-time settings into what the push applied, reporting each field once.
|
|
172
|
+
*
|
|
173
|
+
* A field the create call already satisfied is a noop for the push, so the two normally don't
|
|
174
|
+
* overlap. They do when the create didn't take (an API that ignores a create-time field, or
|
|
175
|
+
* drift in the moments between the two calls) and the push applied it for real — the push
|
|
176
|
+
* change is the one that describes what actually happened, so it wins.
|
|
177
|
+
*/
|
|
178
|
+
const mergeCreateSettings = (atCreate, pushed) => {
|
|
179
|
+
const pushedFields = new Set(pushed.filter((c) => c.kind === "branch" && c.action !== "noop").map((c) => c.details?.field).filter((field) => typeof field === "string"));
|
|
180
|
+
return [...[...atCreate].filter(([field]) => !pushedFields.has(field)).map(([, change]) => change), ...pushed];
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
113
183
|
* Resolve the parent branch id for a new branch. A policy-declared `parent` (a branch name) is
|
|
114
184
|
* looked up by name; an unknown name is a hard error. With no `parent`, fall back to the
|
|
115
185
|
* project's default branch, or `undefined` (let the API pick the project default) when none is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.js","names":[],"sources":["../../src/lib/operations.ts"],"sourcesContent":["import {\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\tPlatformError,\n\ttype PushResult,\n\tresolveConfig,\n} from \"@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, branched from the policy's `parent` (falling back to the project's\n * default branch).\n * 3. {@link pushConfig} the policy onto it with `branchExists: false`, so TTL / compute /\n * `protected` and the services (Neon Auth, Data API, functions) are applied.\n *\n * This is why `apply` alone couldn't do it: `apply` operates on an *existing* branch\n * (`exists: true`), so a policy keyed on `!branch.exists` never returns the creation tuning.\n *\n * Throws {@link PlatformError} (`Conflict`) if a branch with `branchName` already exists, or\n * (`BranchNotFound`) if the policy names a `parent` that isn't on the project.\n */\nexport async function createBranch(\n\tconfig: Config,\n\toptions: CreateBranchOptions,\n): Promise<CreateBranchResult> {\n\tconst api =\n\t\toptions.api ??\n\t\tcreateNeonApiFromOptions(\n\t\t\t\"createBranch\",\n\t\t\toptions.apiKey ? { apiKey: options.apiKey } : {},\n\t\t);\n\tconst { projectId, branchName } = options;\n\n\tconst branches = await api.listBranches(projectId);\n\tif (branches.some((b) => b.name === branchName)) {\n\t\tthrow new PlatformError(\n\t\t\tErrorCode.Conflict,\n\t\t\t`createBranch: a branch named \"${branchName}\" already exists on project ${projectId}. Pick a different name or check it out instead of creating it.`,\n\t\t\t{ details: { projectId, branchName } },\n\t\t);\n\t}\n\n\t// Evaluate the policy as a creation so `parent` (only settable at create time) resolves.\n\tconst resolved = resolveConfig(config, {\n\t\tname: branchName,\n\t\texists: false,\n\t});\n\tconst parentId = resolveParentBranchId(\n\t\tresolved.parent,\n\t\tbranches,\n\t\tbranchName,\n\t);\n\n\tconst { branch } = await api.createBranch(projectId, {\n\t\tname: branchName,\n\t\t...(parentId ? { parentId } : {}),\n\t});\n\n\t// Reconcile the rest as a new branch (`branchExists: false`): TTL, compute settings,\n\t// `protected`, and the services/functions the policy declares are applied onto the\n\t// freshly created branch. `updateExisting`/`allowProtectedBranch` are safe here — there is\n\t// no pre-existing state a user would be surprised to see overridden.\n\tconst result = await pushConfig(config, {\n\t\tprojectId,\n\t\tbranchId: branch.id,\n\t\tapi,\n\t\tbranchExists: false,\n\t\tupdateExisting: true,\n\t\tallowProtectedBranch: true,\n\t\t...(options.bundleFunction\n\t\t\t? { bundleFunction: options.bundleFunction }\n\t\t\t: {}),\n\t});\n\n\treturn { branchId: branch.id, branchName: branch.name, result };\n}\n\n/**\n * Resolve the parent branch id for a new branch. A policy-declared `parent` (a branch name) is\n * looked up by name; an unknown name is a hard error. With no `parent`, fall back to the\n * project's default branch, or `undefined` (let the API pick the project default) when none is\n * marked default.\n */\nfunction resolveParentBranchId(\n\tparentName: string | undefined,\n\tbranches: NeonBranchSnapshot[],\n\tbranchName: string,\n): string | undefined {\n\tif (parentName !== undefined) {\n\t\tconst match = branches.find((b) => b.name === parentName);\n\t\tif (!match) {\n\t\t\tthrow new PlatformError(\n\t\t\t\tErrorCode.BranchNotFound,\n\t\t\t\t`createBranch: the policy for \"${branchName}\" sets parent \"${parentName}\", but no branch with that name exists. Existing branches: ${\n\t\t\t\t\tbranches.map((b) => b.name).join(\", \") || \"(none)\"\n\t\t\t\t}.`,\n\t\t\t\t{\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tbranchName,\n\t\t\t\t\t\tparent: parentName,\n\t\t\t\t\t\tavailable: branches.map((b) => b.name),\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\treturn match.id;\n\t}\n\treturn branches.find((b) => b.isDefault)?.id;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"operations.js","names":[],"sources":["../../src/lib/operations.ts"],"sourcesContent":["import {\n\ttype 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":";;;;;;;;;;AAkEA,eAAsB,QACrB,SAC8B;CAC9B,OAAO,WAAW;EACjB,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;EACnD,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;CACvD,CAAC;AACF;;;;;;;;;AAUA,eAAsB,KACrB,QACA,SACsB;CACtB,OAAO,WAAW,QAAQ;EACzB,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,QAAQ;EAER,gBAAgB;EAChB,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;EACnD,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;CACvD,CAAC;AACF;;;;;;;;;;;;AAaA,eAAsB,MACrB,QACA,SACsB;CACtB,OAAO,WAAW,QAAQ;EACzB,WAAW,QAAQ;EACnB,UAAU,QAAQ;EAClB,GAAI,QAAQ,MAAM,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;EAC1C,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;EACnD,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;EACtD,GAAI,QAAQ,iBAAiB,EAAE,gBAAgB,KAAK,IAAI,CAAC;EACzD,GAAI,QAAQ,uBAAuB,EAAE,sBAAsB,KAAK,IAAI,CAAC;EACrE,GAAI,QAAQ,iBACT,EAAE,gBAAgB,QAAQ,eAAe,IACzC,CAAC;CACL,CAAC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DA,eAAsB,aACrB,QACA,SAC8B;CAC9B,MAAM,MACL,QAAQ,OACR,yBACC,gBACA,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC,CAChD;CACD,MAAM,EAAE,WAAW,eAAe;CAElC,MAAM,WAAW,MAAM,IAAI,aAAa,SAAS;CACjD,IAAI,SAAS,MAAM,MAAM,EAAE,SAAS,UAAU,GAC7C,MAAM,IAAI,cACT,UAAU,UACV,iCAAiC,WAAW,8BAA8B,UAAU,kEACpF,EAAE,SAAS;EAAE;EAAW;CAAW,EAAE,CACtC;CAID,MAAM,WAAW,cAAc,QAAQ;EACtC,MAAM;EACN,QAAQ;CACT,CAAC;CACD,MAAM,WAAW,sBAChB,SAAS,QACT,UACA,UACD;CAOA,MAAM,YACL,SAAS,eAAe,KAAA,IACrB,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,aAAa,GAAI,CAAC,CAAC,YAAY,IAC9D,KAAA;CACJ,MAAM,kBAAkB,SAAS,UAAU;CAE3C,MAAM,EAAE,WAAW,MAAM,IAAI,aAAa,WAAW;EACpD,MAAM;EACN,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;EACjC,GAAI,SAAS,cAAc,KAAA,IACxB,EAAE,WAAW,SAAS,UAAU,IAChC,CAAC;EACJ,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;CAC9C,CAAC;CAED,MAAM,kBAAkB,wBAAwB;EAC/C,YAAY,OAAO;EACnB,GAAI,SAAS,WAAW,KAAA,IACrB,EAAE,YAAY,SAAS,OAAO,IAC9B,CAAC;EACJ,GAAI,cAAc,KAAA,IAAY,EAAE,UAAU,IAAI,CAAC;EAC/C,GAAI,SAAS,cAAc,KAAA,IACxB,EAAE,aAAa,SAAS,UAAU,IAClC,CAAC;EACJ,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;CAC9C,CAAC;CAaD,IAAI;CACJ,IAAI;EACH,SAAS,MAAM,WAAW,QAAQ;GACjC;GACA,UAAU,OAAO;GACjB;GACA,cAAc;GACd,gBAAgB;GAChB,sBAAsB;GACtB,GAAI,QAAQ,iBACT,EAAE,gBAAgB,QAAQ,eAAe,IACzC,CAAC;EACL,CAAC;CACF,SAAS,OAAO;EACf,MAAM,IAAI,yBAAyB,OAAO,IAAI,OAAO,MAAM,KAAK;CACjE;CAEA,OAAO;EACN,UAAU,OAAO;EACjB,YAAY,OAAO;EACnB,QAAQ;GACP,GAAG;GACH,SAAS,oBAAoB,iBAAiB,OAAO,OAAO;EAC7D;CACD;AACD;;;;;;;;;;AAWA,MAAM,2BAA2B,SAOC;CACjC,MAAM,0BAAU,IAAI,IAA2B;CAC/C,MAAM,OAAO,OAAe,YAA2C;EACtE,QAAQ,IAAI,OAAO;GAClB,MAAM;GACN,QAAQ;GACR,YAAY,KAAK;GACjB,SAAS;IAAE;IAAO,GAAG;GAAQ;EAC9B,CAAC;CACF;CACA,IAAI,KAAK,eAAe,KAAA,GACvB,IAAI,UAAU,EAAE,QAAQ,KAAK,WAAW,CAAC;CAE1C,IAAI,KAAK,cAAc,KAAA,GAAW,IAAI,OAAO,EAAE,WAAW,KAAK,UAAU,CAAC;CAC1E,IAAI,KAAK,gBAAgB,KAAA,GACxB,IAAI,aAAa,EAAE,WAAW,KAAK,YAAY,CAAC;CAEjD,IAAI,KAAK,iBACR,IAAI,mBAAmB,EAAE,UAAU,KAAK,gBAAgB,CAAC;CAE1D,OAAO;AACR;;;;;;;;;AAUA,MAAM,uBACL,UACA,WACqB;CACrB,MAAM,eAAe,IAAI,IACxB,OACE,QAAQ,MAAM,EAAE,SAAS,YAAY,EAAE,WAAW,MAAM,CAAC,CACzD,KAAK,MAAM,EAAE,SAAS,KAAK,CAAC,CAC5B,QAAQ,UAA2B,OAAO,UAAU,QAAQ,CAC/D;CAIA,OAAO,CAAC,GAHW,CAAC,GAAG,QAAQ,CAAC,CAC9B,QAAQ,CAAC,WAAW,CAAC,aAAa,IAAI,KAAK,CAAC,CAAC,CAC7C,KAAK,GAAG,YAAY,MACF,GAAG,GAAG,MAAM;AACjC;;;;;;;AAQA,SAAS,sBACR,YACA,UACA,YACqB;CACrB,IAAI,eAAe,KAAA,GAAW;EAC7B,MAAM,QAAQ,SAAS,MAAM,MAAM,EAAE,SAAS,UAAU;EACxD,IAAI,CAAC,OACJ,MAAM,IAAI,cACT,UAAU,gBACV,iCAAiC,WAAW,iBAAiB,WAAW,6DACvE,SAAS,KAAK,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,SAC1C,IACD,EACC,SAAS;GACR;GACA,QAAQ;GACR,WAAW,SAAS,KAAK,MAAM,EAAE,IAAI;EACtC,EACD,CACD;EAED,OAAO,MAAM;CACd;CACA,OAAO,SAAS,MAAM,MAAM,EAAE,SAAS,CAAC,EAAE;AAC3C"}
|
package/dist/v1.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import { FunctionBundler, buildFunctionBundle } from "./lib/function-bundle.js";
|
|
|
2
2
|
import { PullConfigOptions, PulledBranchConfig, PulledPreview, pullConfig } from "./lib/pull-config.js";
|
|
3
3
|
import { PushConfigOptions, PushConfirmContext, pushConfig } from "./lib/push-config.js";
|
|
4
4
|
import { ApplyOptions, ConfigOperationOptions, CreateBranchOptions, CreateBranchResult, apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
5
|
-
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
6
|
-
export { type AppliedChange, type ApplyOptions, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, ErrorCode, type FunctionBundler, type LoadConfigOptions, MissingContextError, type NeonApi, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
|
5
|
+
import { AppliedChange, Config, ConfigLoadError, ConfigValidationError, ConflictReport, ErrorCode, LoadConfigOptions, MissingContextError, NeonApi, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, PushResult, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
6
|
+
export { type AppliedChange, type ApplyOptions, type Config, ConfigLoadError, type ConfigOperationOptions, ConfigValidationError, type ConflictReport, type CreateBranchOptions, type CreateBranchResult, ErrorCode, type FunctionBundler, type LoadConfigOptions, MissingContextError, type NeonApi, PartialBranchCreateError, PlatformError, type PullConfigOptions, type PulledBranchConfig, type PulledPreview, PushAbortedError, type PushConfigOptions, type PushConfirmContext, PushConflictError, type PushResult, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
package/dist/v1.js
CHANGED
|
@@ -2,5 +2,5 @@ import { buildFunctionBundle } from "./lib/function-bundle.js";
|
|
|
2
2
|
import { pullConfig } from "./lib/pull-config.js";
|
|
3
3
|
import { pushConfig } from "./lib/push-config.js";
|
|
4
4
|
import { apply, createBranch, inspect, plan } from "./lib/operations.js";
|
|
5
|
-
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
6
|
-
export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
|
5
|
+
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, createNeonApiFromOptions, createRealNeonApi, defineConfig, isPartialBranchCreateError, isPlatformError, loadConfigFromFile } from "@neon/config";
|
|
6
|
+
export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, apply, buildFunctionBundle, createBranch, createNeonApiFromOptions, createRealNeonApi, defineConfig, inspect, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, plan, pullConfig, pushConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neon/config-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"esbuild": "^0.28.1",
|
|
54
54
|
"fflate": "^0.8.3",
|
|
55
|
-
"@neon/config": "0.
|
|
55
|
+
"@neon/config": "0.10.0"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
58
|
"node": ">=20.19.0"
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "tsc --noEmit && tsdown",
|
|
65
|
-
"test": "vitest --passWithNoTests",
|
|
65
|
+
"test": "pnpm --filter @neon/config-runtime... build && vitest --passWithNoTests",
|
|
66
66
|
"test:ci": "vitest run --passWithNoTests",
|
|
67
67
|
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
68
68
|
"tsc": "tsc"
|