@neondatabase/config 0.0.0 → 0.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.
Files changed (90) hide show
  1. package/LICENSE.md +178 -0
  2. package/dist/index.d.ts +10 -0
  3. package/dist/index.js +8 -0
  4. package/dist/lib/auth.d.ts +63 -0
  5. package/dist/lib/auth.d.ts.map +1 -0
  6. package/dist/lib/auth.js +93 -0
  7. package/dist/lib/auth.js.map +1 -0
  8. package/dist/lib/define-config.d.ts +43 -0
  9. package/dist/lib/define-config.d.ts.map +1 -0
  10. package/dist/lib/define-config.js +112 -0
  11. package/dist/lib/define-config.js.map +1 -0
  12. package/dist/lib/diff.d.ts +109 -0
  13. package/dist/lib/diff.d.ts.map +1 -0
  14. package/dist/lib/diff.js +205 -0
  15. package/dist/lib/diff.js.map +1 -0
  16. package/dist/lib/duration.d.ts +46 -0
  17. package/dist/lib/duration.d.ts.map +1 -0
  18. package/dist/lib/duration.js +96 -0
  19. package/dist/lib/duration.js.map +1 -0
  20. package/dist/lib/errors.d.ts +129 -0
  21. package/dist/lib/errors.d.ts.map +1 -0
  22. package/dist/lib/errors.js +168 -0
  23. package/dist/lib/errors.js.map +1 -0
  24. package/dist/lib/loader.d.ts +44 -0
  25. package/dist/lib/loader.d.ts.map +1 -0
  26. package/dist/lib/loader.js +119 -0
  27. package/dist/lib/loader.js.map +1 -0
  28. package/dist/lib/neon-api-real.d.ts +45 -0
  29. package/dist/lib/neon-api-real.d.ts.map +1 -0
  30. package/dist/lib/neon-api-real.js +582 -0
  31. package/dist/lib/neon-api-real.js.map +1 -0
  32. package/dist/lib/neon-api.d.ts +262 -0
  33. package/dist/lib/neon-api.d.ts.map +1 -0
  34. package/dist/lib/neon-api.js +1 -0
  35. package/dist/lib/patterns.d.ts +43 -0
  36. package/dist/lib/patterns.d.ts.map +1 -0
  37. package/dist/lib/patterns.js +76 -0
  38. package/dist/lib/patterns.js.map +1 -0
  39. package/dist/lib/schema.d.ts +130 -0
  40. package/dist/lib/schema.d.ts.map +1 -0
  41. package/dist/lib/schema.js +218 -0
  42. package/dist/lib/schema.js.map +1 -0
  43. package/dist/lib/types.d.ts +289 -0
  44. package/dist/lib/types.d.ts.map +1 -0
  45. package/dist/lib/types.js +1 -0
  46. package/dist/lib/wrap-neon-error.d.ts +30 -0
  47. package/dist/lib/wrap-neon-error.d.ts.map +1 -0
  48. package/dist/lib/wrap-neon-error.js +139 -0
  49. package/dist/lib/wrap-neon-error.js.map +1 -0
  50. package/dist/v1.d.ts +153 -0
  51. package/dist/v1.d.ts.map +1 -0
  52. package/dist/v1.js +69 -0
  53. package/dist/v1.js.map +1 -0
  54. package/package.json +67 -17
  55. package/.env.example +0 -5
  56. package/e2e/errors.e2e.test.ts +0 -52
  57. package/e2e/helpers.ts +0 -205
  58. package/e2e/load-env.ts +0 -29
  59. package/e2e/setup.ts +0 -24
  60. package/src/index.ts +0 -5
  61. package/src/lib/auth.test.ts +0 -166
  62. package/src/lib/auth.ts +0 -124
  63. package/src/lib/define-config.test.ts +0 -161
  64. package/src/lib/define-config.ts +0 -152
  65. package/src/lib/diff.test.ts +0 -142
  66. package/src/lib/diff.ts +0 -391
  67. package/src/lib/duration.test.ts +0 -105
  68. package/src/lib/duration.ts +0 -147
  69. package/src/lib/errors.test.ts +0 -26
  70. package/src/lib/errors.ts +0 -220
  71. package/src/lib/fake-neon-api.ts +0 -782
  72. package/src/lib/loader.test.ts +0 -35
  73. package/src/lib/loader.ts +0 -215
  74. package/src/lib/neon-api-real.test.ts +0 -72
  75. package/src/lib/neon-api-real.ts +0 -1123
  76. package/src/lib/neon-api.ts +0 -356
  77. package/src/lib/patterns.test.ts +0 -80
  78. package/src/lib/patterns.ts +0 -98
  79. package/src/lib/schema.test.ts +0 -88
  80. package/src/lib/schema.ts +0 -252
  81. package/src/lib/test-utils.ts +0 -83
  82. package/src/lib/types.ts +0 -268
  83. package/src/lib/wrap-neon-error.test.ts +0 -145
  84. package/src/lib/wrap-neon-error.ts +0 -204
  85. package/src/v1.test.ts +0 -33
  86. package/src/v1.ts +0 -148
  87. package/tsconfig.json +0 -4
  88. package/tsdown.config.ts +0 -19
  89. package/vitest.config.ts +0 -19
  90. package/vitest.e2e.config.ts +0 -29
package/src/lib/schema.ts DELETED
@@ -1,252 +0,0 @@
1
- import { z } from "zod";
2
- import { parseDuration, parseSuspendTimeout } from "./duration.js";
3
- import { isWildcardPattern, validatePattern } from "./patterns.js";
4
-
5
- /**
6
- * Zod schema for {@link import("./types.js").ComputeSettings}.
7
- *
8
- * - CU values must be one of: 0.25, 0.5, 1, 2, 4, 8
9
- * - `suspendTimeout` can be:
10
- * - `false` (never suspend)
11
- * - duration string like "5m", "1h" (must be 60s-604800s when parsed)
12
- * - number in seconds (60-604800, or -1/0 for special values)
13
- * - `undefined` (use platform default)
14
- *
15
- * Cross-field invariants (min <= max) are enforced via `superRefine`.
16
- */
17
- export const computeSettingsSchema = z
18
- .strictObject({
19
- autoscalingLimitMinCu: z
20
- .union([
21
- z.literal(0.25),
22
- z.literal(0.5),
23
- z.literal(1),
24
- z.literal(2),
25
- z.literal(4),
26
- z.literal(8),
27
- ])
28
- .optional(),
29
- autoscalingLimitMaxCu: z
30
- .union([
31
- z.literal(0.25),
32
- z.literal(0.5),
33
- z.literal(1),
34
- z.literal(2),
35
- z.literal(4),
36
- z.literal(8),
37
- ])
38
- .optional(),
39
- suspendTimeout: z
40
- .union([z.literal(false), z.string(), z.number()])
41
- .optional()
42
- .superRefine((value, ctx) => {
43
- if (value === undefined) return; // undefined is valid (use platform default)
44
- const result = parseSuspendTimeout(value);
45
- if ("error" in result) {
46
- ctx.addIssue({
47
- code: "custom",
48
- message: result.error,
49
- });
50
- }
51
- }),
52
- })
53
- .superRefine((settings, ctx) => {
54
- const { autoscalingLimitMinCu: min, autoscalingLimitMaxCu: max } =
55
- settings;
56
- if (min !== undefined && max !== undefined && min > max) {
57
- ctx.addIssue({
58
- code: "custom",
59
- path: ["autoscalingLimitMinCu"],
60
- message: `autoscalingLimitMinCu (${min}) must be <= autoscalingLimitMaxCu (${max})`,
61
- });
62
- }
63
- });
64
-
65
- export const serviceToggleSchema = z.strictObject({
66
- enabled: z.boolean().optional(),
67
- });
68
-
69
- export const postgresConfigSchema = z.strictObject({
70
- computeSettings: computeSettingsSchema.optional(),
71
- });
72
-
73
- /**
74
- * Branch-unique function slug. Mirrors the Neon Functions API path-segment rule
75
- * (`platform/internal/platform/functions/name.go`): lowercase DNS label, 1–40 chars.
76
- */
77
- const functionSlugSchema = z
78
- .string()
79
- .regex(
80
- /^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$/,
81
- "function slug must be a lowercase DNS label (1-40 chars, letters/digits/hyphens, no leading/trailing hyphen)",
82
- );
83
-
84
- /**
85
- * Per-function environment map. Every value must be a defined string: a `process.env.X`
86
- * that is unset surfaces as `undefined` and is rejected here (rather than silently
87
- * shipping `undefined` into the deployment).
88
- */
89
- const functionEnvSchema = z.record(z.string(), z.string());
90
-
91
- export const functionConfigSchema = z.strictObject({
92
- slug: functionSlugSchema,
93
- name: z.string().min(1).max(255),
94
- source: z.string().min(1),
95
- env: functionEnvSchema.optional(),
96
- runtime: z.literal("nodejs24").optional(),
97
- memoryMib: z
98
- .union([
99
- z.literal(256),
100
- z.literal(512),
101
- z.literal(1024),
102
- z.literal(2048),
103
- z.literal(4096),
104
- z.literal(8192),
105
- ])
106
- .optional(),
107
- });
108
-
109
- export const bucketConfigSchema = z.strictObject({
110
- name: z.string().min(1).max(255),
111
- access: z
112
- .union([z.literal("private"), z.literal("public_read")])
113
- .optional(),
114
- });
115
-
116
- export const previewConfigSchema = z
117
- .strictObject({
118
- functions: z.array(functionConfigSchema).optional(),
119
- buckets: z.array(bucketConfigSchema).optional(),
120
- aiGateway: serviceToggleSchema.optional(),
121
- })
122
- .superRefine((preview, ctx) => {
123
- assertUnique({
124
- ctx,
125
- path: ["functions"],
126
- items: preview.functions ?? [],
127
- key: (fn) => fn.slug,
128
- label: "function slug",
129
- });
130
- assertUnique({
131
- ctx,
132
- path: ["buckets"],
133
- items: preview.buckets ?? [],
134
- key: (bucket) => bucket.name,
135
- label: "bucket name",
136
- });
137
- });
138
-
139
- /**
140
- * Flag duplicate keys within a Preview collection so a typo in two function slugs (or two
141
- * buckets) surfaces as a config error rather than the second silently clobbering the first
142
- * at apply time.
143
- */
144
- function assertUnique<T>(args: {
145
- ctx: z.RefinementCtx;
146
- path: (string | number)[];
147
- items: T[];
148
- key: (item: T) => string;
149
- label: string;
150
- }): void {
151
- const { ctx, path, items, key, label } = args;
152
- const seen = new Set<string>();
153
- items.forEach((item, index) => {
154
- const value = key(item);
155
- if (seen.has(value)) {
156
- ctx.addIssue({
157
- code: "custom",
158
- path: [...path, index],
159
- message: `duplicate ${label}: ${JSON.stringify(value)}`,
160
- });
161
- }
162
- seen.add(value);
163
- });
164
- }
165
-
166
- export const branchConfigSchema = z
167
- .strictObject({
168
- parent: z.string().optional(),
169
- protected: z.boolean().optional(),
170
- ttl: z
171
- .union([z.string(), z.number()])
172
- .optional()
173
- .superRefine((value, ctx) => {
174
- if (value === undefined) return;
175
- const result = parseDuration(value);
176
- if ("error" in result) {
177
- ctx.addIssue({ code: "custom", message: result.error });
178
- }
179
- }),
180
- postgres: postgresConfigSchema.optional(),
181
- auth: serviceToggleSchema.optional(),
182
- dataApi: serviceToggleSchema.optional(),
183
- preview: previewConfigSchema.optional(),
184
- })
185
- .superRefine((cfg, ctx) => {
186
- validateParentReference({
187
- ctx,
188
- path: ["parent"],
189
- parent: cfg.parent,
190
- });
191
- });
192
-
193
- function validateParentReference(args: {
194
- ctx: z.RefinementCtx;
195
- path: (string | number)[];
196
- parent: string | undefined;
197
- }): void {
198
- const { ctx, path, parent } = args;
199
- if (parent === undefined) return;
200
-
201
- const patternCheck = validatePattern(parent);
202
- if ("error" in patternCheck) {
203
- ctx.addIssue({ code: "custom", path, message: patternCheck.error });
204
- } else if (isWildcardPattern(parent)) {
205
- ctx.addIssue({
206
- code: "custom",
207
- path,
208
- message: `parent must be a concrete branch name (no wildcards), got "${parent}"`,
209
- });
210
- }
211
- }
212
-
213
- export const configSchema = z.function({
214
- input: [z.unknown()],
215
- output: z.unknown(),
216
- });
217
-
218
- /**
219
- * Convert the structured {@link z.ZodError} produced by `configSchema.safeParse` into the
220
- * `string[]` shape used by {@link import("./errors.js").ConfigValidationError}.
221
- *
222
- * Issue paths are rendered as dot-separated property accesses (`postgres.computeSettings`)
223
- * and unknown-key issues from `strictObject` are normalised so the message contains the
224
- * substring "unknown key" — keeping pre-zod assertions in test suites and downstream tools
225
- * stable.
226
- */
227
- export function formatZodIssues(error: z.ZodError): string[] {
228
- return error.issues.map((issue) => {
229
- const path = renderPath(issue.path);
230
- const message = normaliseIssueMessage(issue);
231
- return path ? `${path}: ${message}` : message;
232
- });
233
- }
234
-
235
- function renderPath(path: ReadonlyArray<PropertyKey>): string {
236
- let out = "";
237
- for (const segment of path) {
238
- if (typeof segment === "number") out += `[${segment}]`;
239
- else if (out === "") out += String(segment);
240
- else out += `.${String(segment)}`;
241
- }
242
- return out;
243
- }
244
-
245
- function normaliseIssueMessage(issue: z.core.$ZodIssue): string {
246
- if (issue.code === "unrecognized_keys") {
247
- const keys = (issue as z.core.$ZodIssueUnrecognizedKeys).keys ?? [];
248
- const formatted = keys.map((k) => JSON.stringify(k)).join(", ");
249
- return `unknown key${keys.length === 1 ? "" : "s"}: ${formatted}`;
250
- }
251
- return issue.message;
252
- }
@@ -1,83 +0,0 @@
1
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { dirname, join } from "node:path";
4
- import { vi } from "vitest";
5
-
6
- /**
7
- * Every Neon env var (and adjacent OS env var) the platform package reads at runtime.
8
- * Used by {@link stubCleanNeonEnv} to give each test a deterministic, empty starting
9
- * env regardless of the developer's local `~/.config/neonctl`, exported `NEON_*` vars,
10
- * or anything else carried over from the parent shell.
11
- */
12
- const NEON_AND_RELATED_ENV_KEYS = [
13
- "NEON_API_KEY",
14
- "NEON_PROJECT_ID",
15
- "NEON_BRANCH_ID",
16
- "NEON_ORG_ID",
17
- "NEON_AUTH_BASE_URL",
18
- "NEON_DATA_API_URL",
19
- "DATABASE_URL",
20
- "DATABASE_URL_UNPOOLED",
21
- "NEONCTL_CONFIG_DIR",
22
- "HOME",
23
- "USERPROFILE",
24
- ] as const;
25
-
26
- /**
27
- * Stub every Neon-related env var to `undefined` (so they look unset to the code under
28
- * test). Tests can override individual keys with `vi.stubEnv(key, value)` after calling
29
- * this. `vitest.config.ts` sets `unstubEnvs: true` so each test's stubs are auto-reset.
30
- *
31
- * Call this from a `beforeEach` in any test file that touches `process.env`-driven
32
- * resolution (api key / context / connection-string env vars).
33
- */
34
- export function stubCleanNeonEnv(): void {
35
- for (const key of NEON_AND_RELATED_ENV_KEYS) {
36
- // vitest 3.x: passing `undefined` deletes the env var (rather than setting it
37
- // to the literal string "undefined") — exactly what we want, because empty
38
- // strings would still trip code that reads `env.HOME` to build paths.
39
- vi.stubEnv(key, undefined);
40
- }
41
- }
42
-
43
- /**
44
- * Build a transient project tree under the OS temp directory.
45
- *
46
- * Returns the absolute root path plus a `cleanup()` that removes the tree. Tests should
47
- * call `cleanup()` from an `afterEach`/`afterAll` hook.
48
- *
49
- * `files` is a flat map of relative paths → contents; intermediate directories are created
50
- * automatically. Directories themselves can be created by passing `null` as the value.
51
- *
52
- * A `.git/HEAD` marker is seeded at the root by default so the platform's upward walkers
53
- * (which stop at `.git`) don't escape the synthetic repo and read the developer's real
54
- * `~/.neon`. Pass an explicit `.git` entry in `files` to override or position it elsewhere
55
- * (e.g. for tests that exercise the boundary behaviour itself).
56
- */
57
- export function makeTempRepo(files: Record<string, string | null>): {
58
- root: string;
59
- cleanup: () => void;
60
- } {
61
- const root = mkdtempSync(join(tmpdir(), "neon-ts-test-"));
62
- const callerSpecifiesGit = Object.keys(files).some(
63
- (p) => p === ".git" || p.startsWith(".git/"),
64
- );
65
- const entries: Array<[string, string | null]> = callerSpecifiesGit
66
- ? Object.entries(files)
67
- : [[".git/HEAD", "ref: refs/heads/main\n"], ...Object.entries(files)];
68
- for (const [relPath, contents] of entries) {
69
- const abs = join(root, relPath);
70
- mkdirSync(dirname(abs), { recursive: true });
71
- if (contents !== null) {
72
- writeFileSync(abs, contents, "utf-8");
73
- } else {
74
- mkdirSync(abs, { recursive: true });
75
- }
76
- }
77
- return {
78
- root,
79
- cleanup: () => {
80
- rmSync(root, { recursive: true, force: true });
81
- },
82
- };
83
- }
package/src/lib/types.ts DELETED
@@ -1,268 +0,0 @@
1
- /**
2
- * Valid Neon Compute Unit values.
3
- * Most plans support 0.25, 0.5, 1, 2, 4, 8. Higher values may be available on Business plans.
4
- */
5
- export type ComputeUnit = 0.25 | 0.5 | 1 | 2 | 4 | 8;
6
-
7
- /**
8
- * Compute settings applied to the read/write endpoint of a branch.
9
- *
10
- * Mirrors the subset of {@link https://api-docs.neon.tech/reference/getting-started-with-neon-api Neon endpoint}
11
- * fields that we expose as IaC primitives. Anything left undefined falls back to the project's
12
- * `default_endpoint_settings` (which themselves fall back to Neon platform defaults).
13
- */
14
- export interface ComputeSettings {
15
- /**
16
- * Minimum number of Compute Units. Set to 0.25 for true scale-to-zero.
17
- * @example 0.25 // scale-to-zero
18
- * @example 1 // always-on with 1 CU minimum
19
- */
20
- autoscalingLimitMinCu?: ComputeUnit;
21
- /**
22
- * Maximum number of Compute Units for autoscaling.
23
- * @example 2
24
- * @example 8
25
- */
26
- autoscalingLimitMaxCu?: ComputeUnit;
27
- /**
28
- * How long to wait before suspending an idle compute.
29
- *
30
- * - `false` — never suspend (always-on compute)
31
- * - `"5m"` — duration string (supports "30s", "5m", "1h", "7d", etc)
32
- * - `300` — custom timeout in seconds (60-604800)
33
- * - `undefined` — use Neon platform default (currently 300s / 5 minutes)
34
- *
35
- * @example false // never suspend
36
- * @example "5m" // 5 minutes
37
- * @example "1h" // 1 hour
38
- * @example 300 // 5 minutes in seconds
39
- */
40
- suspendTimeout?: false | "5m" | "1h" | string | number;
41
- }
42
-
43
- /**
44
- * Read-only descriptor of the branch a {@link Config} policy is being evaluated for — the
45
- * `branch` argument passed to your `defineConfig((branch) => …)` callback. It describes
46
- * **which** branch this invocation decides for; it is not a live branch handle and must not
47
- * be mutated. Switch on its fields and return the desired {@link BranchConfig}.
48
- */
49
- export interface BranchTarget {
50
- /** Branch name being evaluated. For `branch dev`, this is the generated branch name. */
51
- name: string;
52
- /** Neon branch id when the branch already exists. Undefined during pre-create eval. */
53
- id?: string;
54
- /** Whether this branch already exists on Neon. */
55
- exists: boolean;
56
- /** Parent branch id from Neon when known. */
57
- parentId?: string;
58
- /** Whether Neon marks this branch as the project default. */
59
- isDefault?: boolean;
60
- /** Whether Neon currently marks this branch protected. */
61
- isProtected?: boolean;
62
- /** Current expiration timestamp from Neon, when set. */
63
- expiresAt?: string;
64
- }
65
-
66
- export interface ServiceToggle {
67
- /** Defaults to `true` when the service namespace is present. Set `false` to opt out. */
68
- enabled?: boolean;
69
- }
70
-
71
- export interface PostgresConfig {
72
- computeSettings?: ComputeSettings;
73
- }
74
-
75
- /**
76
- * Supported function runtimes. Mirrors the Neon Functions deploy API `runtime` enum.
77
- * Only `nodejs24` exists today; kept as a union so adding runtimes later is a
78
- * non-breaking, type-checked change.
79
- */
80
- export type FunctionRuntime = "nodejs24";
81
-
82
- /**
83
- * Memory sizes (MiB) accepted by the Neon Functions deploy API. Mirrors the
84
- * `memory_mib` enum in the spec.
85
- */
86
- export type FunctionMemoryMib = 256 | 512 | 1024 | 2048 | 4096 | 8192;
87
-
88
- /**
89
- * A single Neon Function deployed to a branch (Preview feature).
90
- *
91
- * A function is invoked like a Cloudflare/Vercel handler — its source module
92
- * `export default { fetch }` or `export async function handler(req): Response`. The
93
- * `source` path is bundled (esbuild) and uploaded as a deployment; the newest
94
- * deployment becomes active.
95
- */
96
- export interface FunctionConfig {
97
- /**
98
- * Branch-unique, lowercase DNS-label used as the path segment in the function's
99
- * invocation URL. Immutable once created. 1–40 chars, `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`.
100
- * @example "hello-world"
101
- */
102
- slug: string;
103
- /** Free-form display name. @example "Hello World" */
104
- name: string;
105
- /**
106
- * Path to the function's entry module, **relative to `neon.ts`** (or absolute). The
107
- * module's default export (`{ fetch }`) or `handler` export is the function entry. This
108
- * path is resolved against the loaded `neon.ts` location and bundled with esbuild at
109
- * deploy time.
110
- *
111
- * We require a string path rather than an imported handler because a JS function value
112
- * carries no reference back to its source file, so esbuild has nothing to bundle from.
113
- * @example "./functions/hello-world.ts"
114
- */
115
- source: string;
116
- /**
117
- * Environment variables injected into the deployed function. Every value must be a
118
- * defined string — a `process.env.X` that is `undefined` (unset) errors at validation
119
- * time rather than silently shipping `undefined`.
120
- * @example { RESEND_API_KEY: process.env.RESEND_API_KEY }
121
- */
122
- env?: Record<string, string>;
123
- /** Runtime to execute the function with. Defaults to `"nodejs24"`. */
124
- runtime?: FunctionRuntime;
125
- /** Memory allotted to each invocation, in MiB. Defaults to `512`. */
126
- memoryMib?: FunctionMemoryMib;
127
- }
128
-
129
- /** Anonymous-access level for a branchable object-storage bucket. */
130
- export type BucketAccessLevel = "private" | "public_read";
131
-
132
- /**
133
- * A branchable object-storage bucket on a branch (Preview feature).
134
- */
135
- export interface BucketConfig {
136
- /** Bucket name, unique within a branch. 1–255 chars. */
137
- name: string;
138
- /**
139
- * Anonymous access level. `private` (default) requires authenticated reads/writes;
140
- * `public_read` allows anonymous GetObject/HeadObject.
141
- */
142
- access?: BucketAccessLevel;
143
- }
144
-
145
- /**
146
- * Branch-scoped Preview features. Grouped under `preview` to signal they are backed by
147
- * Neon `x-stability-level: beta` endpoints and may change before GA.
148
- */
149
- export interface PreviewConfig {
150
- /** Functions to deploy on the branch. */
151
- functions?: FunctionConfig[];
152
- /** Object-storage buckets to create on the branch. */
153
- buckets?: BucketConfig[];
154
- /** Enable/disable the AI Gateway on the branch (toggle, like auth / dataApi). */
155
- aiGateway?: ServiceToggle;
156
- }
157
-
158
- interface BranchConfigBase {
159
- /** Parent branch name used when creating a new branch. Not a Postgres setting. */
160
- parent?: string;
161
- /** Time-to-live applied when creating a new branch, or reconciled on existing branches. */
162
- ttl?: string | number;
163
- /** Whether the selected branch should be protected. Undefined means "leave as-is". */
164
- protected?: boolean;
165
- postgres?: PostgresConfig;
166
- /**
167
- * Branch-scoped Preview features (functions, object-storage buckets, AI Gateway).
168
- * Backed by Neon `x-stability-level: beta` endpoints — see {@link PreviewConfig}.
169
- */
170
- preview?: PreviewConfig;
171
- }
172
-
173
- type BranchServiceConfig =
174
- | { auth?: never; dataApi?: never }
175
- | { auth: ServiceToggle; dataApi?: never }
176
- | { auth?: never; dataApi: ServiceToggle }
177
- | { auth: ServiceToggle; dataApi: ServiceToggle };
178
-
179
- export type BranchConfig = BranchConfigBase & BranchServiceConfig;
180
-
181
- export type Config = (branch: BranchTarget) => BranchConfig;
182
-
183
- /**
184
- * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` and
185
- * `memoryMib` so downstream diff/apply never has to re-derive them.
186
- */
187
- export interface ResolvedFunctionConfig {
188
- slug: string;
189
- name: string;
190
- source: string;
191
- env: Record<string, string>;
192
- runtime: FunctionRuntime;
193
- memoryMib: FunctionMemoryMib;
194
- }
195
-
196
- /** A bucket with its access level defaulted to `private`. */
197
- export interface ResolvedBucketConfig {
198
- name: string;
199
- access: BucketAccessLevel;
200
- }
201
-
202
- /**
203
- * Normalized {@link PreviewConfig}. Only present on {@link ResolvedBranchConfig} when the
204
- * policy returned a `preview` block. `aiGatewayEnabled` follows the same
205
- * "present-and-not-`false`" semantics as `authEnabled` / `dataApiEnabled`.
206
- */
207
- export interface ResolvedPreviewConfig {
208
- functions: ResolvedFunctionConfig[];
209
- buckets: ResolvedBucketConfig[];
210
- aiGatewayEnabled: boolean;
211
- }
212
-
213
- export interface ResolvedBranchConfig {
214
- parent?: string;
215
- ttlSeconds?: number;
216
- protected?: boolean;
217
- postgres?: PostgresConfig;
218
- authEnabled: boolean;
219
- dataApiEnabled: boolean;
220
- preview?: ResolvedPreviewConfig;
221
- }
222
-
223
- /**
224
- * One concrete change `pushConfig` made (or, in dry-run, would make) on the remote.
225
- */
226
- export interface AppliedChange {
227
- /**
228
- * `service` covers branch-scoped integrations driven by the branch policy (e.g.
229
- * Neon Auth, Data API).
230
- */
231
- kind: "branch" | "service";
232
- action: "create" | "update" | "noop";
233
- identifier: string;
234
- details?: Record<string, unknown>;
235
- }
236
-
237
- /**
238
- * A diff entry that conflicts with the desired config. `pushConfig` throws
239
- * {@link PushConflictError} on the first call when conflicts exist; pass
240
- * `updateExisting: true` to apply mutable drift (settings, `protected`, TTL, project
241
- * rename). Immutable fields (region, Postgres major version) are always conflicts —
242
- * recreate the project to change them.
243
- */
244
- export interface ConflictReport {
245
- kind: "branch";
246
- identifier: string;
247
- field: string;
248
- current: unknown;
249
- desired: unknown;
250
- reason: string;
251
- }
252
-
253
- /**
254
- * Result of a `pushConfig` invocation.
255
- */
256
- export interface PushResult {
257
- projectId: string;
258
- orgId?: string;
259
- branchId: string;
260
- branchName: string;
261
- /**
262
- * `true` when `pushConfig` was called with `{ dryRun: true }`. `applied` then records
263
- * what **would** be applied on a real push; no API mutations were performed.
264
- */
265
- dryRun: boolean;
266
- applied: AppliedChange[];
267
- conflicts: ConflictReport[];
268
- }