@neondatabase/config 0.0.0 → 0.1.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 +111 -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 +109 -0
  40. package/dist/lib/schema.d.ts.map +1 -0
  41. package/dist/lib/schema.js +199 -0
  42. package/dist/lib/schema.js.map +1 -0
  43. package/dist/lib/types.d.ts +259 -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 +132 -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
@@ -0,0 +1,199 @@
1
+ import { parseDuration, parseSuspendTimeout } from "./duration.js";
2
+ import { isWildcardPattern, validatePattern } from "./patterns.js";
3
+ import { z } from "zod";
4
+ //#region src/lib/schema.ts
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
+ const computeSettingsSchema = z.strictObject({
18
+ autoscalingLimitMinCu: z.union([
19
+ z.literal(.25),
20
+ z.literal(.5),
21
+ z.literal(1),
22
+ z.literal(2),
23
+ z.literal(4),
24
+ z.literal(8)
25
+ ]).optional(),
26
+ autoscalingLimitMaxCu: z.union([
27
+ z.literal(.25),
28
+ z.literal(.5),
29
+ z.literal(1),
30
+ z.literal(2),
31
+ z.literal(4),
32
+ z.literal(8)
33
+ ]).optional(),
34
+ suspendTimeout: z.union([
35
+ z.literal(false),
36
+ z.string(),
37
+ z.number()
38
+ ]).optional().superRefine((value, ctx) => {
39
+ if (value === void 0) return;
40
+ const result = parseSuspendTimeout(value);
41
+ if ("error" in result) ctx.addIssue({
42
+ code: "custom",
43
+ message: result.error
44
+ });
45
+ })
46
+ }).superRefine((settings, ctx) => {
47
+ const { autoscalingLimitMinCu: min, autoscalingLimitMaxCu: max } = settings;
48
+ if (min !== void 0 && max !== void 0 && min > max) ctx.addIssue({
49
+ code: "custom",
50
+ path: ["autoscalingLimitMinCu"],
51
+ message: `autoscalingLimitMinCu (${min}) must be <= autoscalingLimitMaxCu (${max})`
52
+ });
53
+ });
54
+ const serviceToggleSchema = z.strictObject({ enabled: z.boolean().optional() });
55
+ const postgresConfigSchema = z.strictObject({ computeSettings: computeSettingsSchema.optional() });
56
+ /**
57
+ * Branch-unique function slug. Mirrors the Neon Functions API path-segment rule
58
+ * (`platform/internal/platform/functions/name.go`): lowercase DNS label, 1–40 chars.
59
+ */
60
+ const functionSlugSchema = z.string().regex(/^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$/, "function slug must be a lowercase DNS label (1-40 chars, letters/digits/hyphens, no leading/trailing hyphen)");
61
+ /**
62
+ * Per-function environment map. Every value must be a defined string: a `process.env.X`
63
+ * that is unset surfaces as `undefined` and is rejected here (rather than silently
64
+ * shipping `undefined` into the deployment).
65
+ */
66
+ const functionEnvSchema = z.record(z.string(), z.string());
67
+ const functionConfigSchema = z.strictObject({
68
+ slug: functionSlugSchema,
69
+ name: z.string().min(1).max(255),
70
+ source: z.string().min(1),
71
+ env: functionEnvSchema.optional(),
72
+ runtime: z.literal("nodejs24").optional(),
73
+ memoryMib: z.union([
74
+ z.literal(256),
75
+ z.literal(512),
76
+ z.literal(1024),
77
+ z.literal(2048),
78
+ z.literal(4096),
79
+ z.literal(8192)
80
+ ]).optional()
81
+ });
82
+ const bucketConfigSchema = z.strictObject({
83
+ name: z.string().min(1).max(255),
84
+ access: z.union([z.literal("private"), z.literal("public_read")]).optional()
85
+ });
86
+ const previewConfigSchema = z.strictObject({
87
+ functions: z.array(functionConfigSchema).optional(),
88
+ buckets: z.array(bucketConfigSchema).optional(),
89
+ aiGateway: serviceToggleSchema.optional()
90
+ }).superRefine((preview, ctx) => {
91
+ assertUnique({
92
+ ctx,
93
+ path: ["functions"],
94
+ items: preview.functions ?? [],
95
+ key: (fn) => fn.slug,
96
+ label: "function slug"
97
+ });
98
+ assertUnique({
99
+ ctx,
100
+ path: ["buckets"],
101
+ items: preview.buckets ?? [],
102
+ key: (bucket) => bucket.name,
103
+ label: "bucket name"
104
+ });
105
+ });
106
+ /**
107
+ * Flag duplicate keys within a Preview collection so a typo in two function slugs (or two
108
+ * buckets) surfaces as a config error rather than the second silently clobbering the first
109
+ * at apply time.
110
+ */
111
+ function assertUnique(args) {
112
+ const { ctx, path, items, key, label } = args;
113
+ const seen = /* @__PURE__ */ new Set();
114
+ items.forEach((item, index) => {
115
+ const value = key(item);
116
+ if (seen.has(value)) ctx.addIssue({
117
+ code: "custom",
118
+ path: [...path, index],
119
+ message: `duplicate ${label}: ${JSON.stringify(value)}`
120
+ });
121
+ seen.add(value);
122
+ });
123
+ }
124
+ const branchConfigSchema = z.strictObject({
125
+ parent: z.string().optional(),
126
+ protected: z.boolean().optional(),
127
+ ttl: z.union([z.string(), z.number()]).optional().superRefine((value, ctx) => {
128
+ if (value === void 0) return;
129
+ const result = parseDuration(value);
130
+ if ("error" in result) ctx.addIssue({
131
+ code: "custom",
132
+ message: result.error
133
+ });
134
+ }),
135
+ postgres: postgresConfigSchema.optional(),
136
+ auth: serviceToggleSchema.optional(),
137
+ dataApi: serviceToggleSchema.optional(),
138
+ preview: previewConfigSchema.optional()
139
+ }).superRefine((cfg, ctx) => {
140
+ validateParentReference({
141
+ ctx,
142
+ path: ["parent"],
143
+ parent: cfg.parent
144
+ });
145
+ });
146
+ function validateParentReference(args) {
147
+ const { ctx, path, parent } = args;
148
+ if (parent === void 0) return;
149
+ const patternCheck = validatePattern(parent);
150
+ if ("error" in patternCheck) ctx.addIssue({
151
+ code: "custom",
152
+ path,
153
+ message: patternCheck.error
154
+ });
155
+ else if (isWildcardPattern(parent)) ctx.addIssue({
156
+ code: "custom",
157
+ path,
158
+ message: `parent must be a concrete branch name (no wildcards), got "${parent}"`
159
+ });
160
+ }
161
+ const configSchema = z.function({
162
+ input: [z.unknown()],
163
+ output: z.unknown()
164
+ });
165
+ /**
166
+ * Convert the structured {@link z.ZodError} produced by `configSchema.safeParse` into the
167
+ * `string[]` shape used by {@link import("./errors.js").ConfigValidationError}.
168
+ *
169
+ * Issue paths are rendered as dot-separated property accesses (`postgres.computeSettings`)
170
+ * and unknown-key issues from `strictObject` are normalised so the message contains the
171
+ * substring "unknown key" — keeping pre-zod assertions in test suites and downstream tools
172
+ * stable.
173
+ */
174
+ function formatZodIssues(error) {
175
+ return error.issues.map((issue) => {
176
+ const path = renderPath(issue.path);
177
+ const message = normaliseIssueMessage(issue);
178
+ return path ? `${path}: ${message}` : message;
179
+ });
180
+ }
181
+ function renderPath(path) {
182
+ let out = "";
183
+ for (const segment of path) if (typeof segment === "number") out += `[${segment}]`;
184
+ else if (out === "") out += String(segment);
185
+ else out += `.${String(segment)}`;
186
+ return out;
187
+ }
188
+ function normaliseIssueMessage(issue) {
189
+ if (issue.code === "unrecognized_keys") {
190
+ const keys = issue.keys ?? [];
191
+ const formatted = keys.map((k) => JSON.stringify(k)).join(", ");
192
+ return `unknown key${keys.length === 1 ? "" : "s"}: ${formatted}`;
193
+ }
194
+ return issue.message;
195
+ }
196
+ //#endregion
197
+ export { branchConfigSchema, bucketConfigSchema, computeSettingsSchema, configSchema, formatZodIssues, functionConfigSchema, postgresConfigSchema, previewConfigSchema, serviceToggleSchema };
198
+
199
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../src/lib/schema.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { parseDuration, parseSuspendTimeout } from \"./duration.js\";\nimport { isWildcardPattern, validatePattern } from \"./patterns.js\";\n\n/**\n * Zod schema for {@link import(\"./types.js\").ComputeSettings}.\n *\n * - CU values must be one of: 0.25, 0.5, 1, 2, 4, 8\n * - `suspendTimeout` can be:\n * - `false` (never suspend)\n * - duration string like \"5m\", \"1h\" (must be 60s-604800s when parsed)\n * - number in seconds (60-604800, or -1/0 for special values)\n * - `undefined` (use platform default)\n *\n * Cross-field invariants (min <= max) are enforced via `superRefine`.\n */\nexport const computeSettingsSchema = z\n\t.strictObject({\n\t\tautoscalingLimitMinCu: z\n\t\t\t.union([\n\t\t\t\tz.literal(0.25),\n\t\t\t\tz.literal(0.5),\n\t\t\t\tz.literal(1),\n\t\t\t\tz.literal(2),\n\t\t\t\tz.literal(4),\n\t\t\t\tz.literal(8),\n\t\t\t])\n\t\t\t.optional(),\n\t\tautoscalingLimitMaxCu: z\n\t\t\t.union([\n\t\t\t\tz.literal(0.25),\n\t\t\t\tz.literal(0.5),\n\t\t\t\tz.literal(1),\n\t\t\t\tz.literal(2),\n\t\t\t\tz.literal(4),\n\t\t\t\tz.literal(8),\n\t\t\t])\n\t\t\t.optional(),\n\t\tsuspendTimeout: z\n\t\t\t.union([z.literal(false), z.string(), z.number()])\n\t\t\t.optional()\n\t\t\t.superRefine((value, ctx) => {\n\t\t\t\tif (value === undefined) return; // undefined is valid (use platform default)\n\t\t\t\tconst result = parseSuspendTimeout(value);\n\t\t\t\tif (\"error\" in result) {\n\t\t\t\t\tctx.addIssue({\n\t\t\t\t\t\tcode: \"custom\",\n\t\t\t\t\t\tmessage: result.error,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}),\n\t})\n\t.superRefine((settings, ctx) => {\n\t\tconst { autoscalingLimitMinCu: min, autoscalingLimitMaxCu: max } =\n\t\t\tsettings;\n\t\tif (min !== undefined && max !== undefined && min > max) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: \"custom\",\n\t\t\t\tpath: [\"autoscalingLimitMinCu\"],\n\t\t\t\tmessage: `autoscalingLimitMinCu (${min}) must be <= autoscalingLimitMaxCu (${max})`,\n\t\t\t});\n\t\t}\n\t});\n\nexport const serviceToggleSchema = z.strictObject({\n\tenabled: z.boolean().optional(),\n});\n\nexport const postgresConfigSchema = z.strictObject({\n\tcomputeSettings: computeSettingsSchema.optional(),\n});\n\n/**\n * Branch-unique function slug. Mirrors the Neon Functions API path-segment rule\n * (`platform/internal/platform/functions/name.go`): lowercase DNS label, 1–40 chars.\n */\nconst functionSlugSchema = z\n\t.string()\n\t.regex(\n\t\t/^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$/,\n\t\t\"function slug must be a lowercase DNS label (1-40 chars, letters/digits/hyphens, no leading/trailing hyphen)\",\n\t);\n\n/**\n * Per-function environment map. Every value must be a defined string: a `process.env.X`\n * that is unset surfaces as `undefined` and is rejected here (rather than silently\n * shipping `undefined` into the deployment).\n */\nconst functionEnvSchema = z.record(z.string(), z.string());\n\nexport const functionConfigSchema = z.strictObject({\n\tslug: functionSlugSchema,\n\tname: z.string().min(1).max(255),\n\tsource: z.string().min(1),\n\tenv: functionEnvSchema.optional(),\n\truntime: z.literal(\"nodejs24\").optional(),\n\tmemoryMib: z\n\t\t.union([\n\t\t\tz.literal(256),\n\t\t\tz.literal(512),\n\t\t\tz.literal(1024),\n\t\t\tz.literal(2048),\n\t\t\tz.literal(4096),\n\t\t\tz.literal(8192),\n\t\t])\n\t\t.optional(),\n});\n\nexport const bucketConfigSchema = z.strictObject({\n\tname: z.string().min(1).max(255),\n\taccess: z\n\t\t.union([z.literal(\"private\"), z.literal(\"public_read\")])\n\t\t.optional(),\n});\n\nexport const previewConfigSchema = z\n\t.strictObject({\n\t\tfunctions: z.array(functionConfigSchema).optional(),\n\t\tbuckets: z.array(bucketConfigSchema).optional(),\n\t\taiGateway: serviceToggleSchema.optional(),\n\t})\n\t.superRefine((preview, ctx) => {\n\t\tassertUnique({\n\t\t\tctx,\n\t\t\tpath: [\"functions\"],\n\t\t\titems: preview.functions ?? [],\n\t\t\tkey: (fn) => fn.slug,\n\t\t\tlabel: \"function slug\",\n\t\t});\n\t\tassertUnique({\n\t\t\tctx,\n\t\t\tpath: [\"buckets\"],\n\t\t\titems: preview.buckets ?? [],\n\t\t\tkey: (bucket) => bucket.name,\n\t\t\tlabel: \"bucket name\",\n\t\t});\n\t});\n\n/**\n * Flag duplicate keys within a Preview collection so a typo in two function slugs (or two\n * buckets) surfaces as a config error rather than the second silently clobbering the first\n * at apply time.\n */\nfunction assertUnique<T>(args: {\n\tctx: z.RefinementCtx;\n\tpath: (string | number)[];\n\titems: T[];\n\tkey: (item: T) => string;\n\tlabel: string;\n}): void {\n\tconst { ctx, path, items, key, label } = args;\n\tconst seen = new Set<string>();\n\titems.forEach((item, index) => {\n\t\tconst value = key(item);\n\t\tif (seen.has(value)) {\n\t\t\tctx.addIssue({\n\t\t\t\tcode: \"custom\",\n\t\t\t\tpath: [...path, index],\n\t\t\t\tmessage: `duplicate ${label}: ${JSON.stringify(value)}`,\n\t\t\t});\n\t\t}\n\t\tseen.add(value);\n\t});\n}\n\nexport const branchConfigSchema = z\n\t.strictObject({\n\t\tparent: z.string().optional(),\n\t\tprotected: z.boolean().optional(),\n\t\tttl: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.optional()\n\t\t\t.superRefine((value, ctx) => {\n\t\t\t\tif (value === undefined) return;\n\t\t\t\tconst result = parseDuration(value);\n\t\t\t\tif (\"error\" in result) {\n\t\t\t\t\tctx.addIssue({ code: \"custom\", message: result.error });\n\t\t\t\t}\n\t\t\t}),\n\t\tpostgres: postgresConfigSchema.optional(),\n\t\tauth: serviceToggleSchema.optional(),\n\t\tdataApi: serviceToggleSchema.optional(),\n\t\tpreview: previewConfigSchema.optional(),\n\t})\n\t.superRefine((cfg, ctx) => {\n\t\tvalidateParentReference({\n\t\t\tctx,\n\t\t\tpath: [\"parent\"],\n\t\t\tparent: cfg.parent,\n\t\t});\n\t});\n\nfunction validateParentReference(args: {\n\tctx: z.RefinementCtx;\n\tpath: (string | number)[];\n\tparent: string | undefined;\n}): void {\n\tconst { ctx, path, parent } = args;\n\tif (parent === undefined) return;\n\n\tconst patternCheck = validatePattern(parent);\n\tif (\"error\" in patternCheck) {\n\t\tctx.addIssue({ code: \"custom\", path, message: patternCheck.error });\n\t} else if (isWildcardPattern(parent)) {\n\t\tctx.addIssue({\n\t\t\tcode: \"custom\",\n\t\t\tpath,\n\t\t\tmessage: `parent must be a concrete branch name (no wildcards), got \"${parent}\"`,\n\t\t});\n\t}\n}\n\nexport const configSchema = z.function({\n\tinput: [z.unknown()],\n\toutput: z.unknown(),\n});\n\n/**\n * Convert the structured {@link z.ZodError} produced by `configSchema.safeParse` into the\n * `string[]` shape used by {@link import(\"./errors.js\").ConfigValidationError}.\n *\n * Issue paths are rendered as dot-separated property accesses (`postgres.computeSettings`)\n * and unknown-key issues from `strictObject` are normalised so the message contains the\n * substring \"unknown key\" — keeping pre-zod assertions in test suites and downstream tools\n * stable.\n */\nexport function formatZodIssues(error: z.ZodError): string[] {\n\treturn error.issues.map((issue) => {\n\t\tconst path = renderPath(issue.path);\n\t\tconst message = normaliseIssueMessage(issue);\n\t\treturn path ? `${path}: ${message}` : message;\n\t});\n}\n\nfunction renderPath(path: ReadonlyArray<PropertyKey>): string {\n\tlet out = \"\";\n\tfor (const segment of path) {\n\t\tif (typeof segment === \"number\") out += `[${segment}]`;\n\t\telse if (out === \"\") out += String(segment);\n\t\telse out += `.${String(segment)}`;\n\t}\n\treturn out;\n}\n\nfunction normaliseIssueMessage(issue: z.core.$ZodIssue): string {\n\tif (issue.code === \"unrecognized_keys\") {\n\t\tconst keys = (issue as z.core.$ZodIssueUnrecognizedKeys).keys ?? [];\n\t\tconst formatted = keys.map((k) => JSON.stringify(k)).join(\", \");\n\t\treturn `unknown key${keys.length === 1 ? \"\" : \"s\"}: ${formatted}`;\n\t}\n\treturn issue.message;\n}\n"],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ //#region src/lib/types.d.ts
2
+ /**
3
+ * Valid Neon Compute Unit values.
4
+ * Most plans support 0.25, 0.5, 1, 2, 4, 8. Higher values may be available on Business plans.
5
+ */
6
+ type ComputeUnit = 0.25 | 0.5 | 1 | 2 | 4 | 8;
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
+ 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
+ * Read-only descriptor of the branch a {@link Config} policy is being evaluated for — the
44
+ * `branch` argument passed to your `defineConfig((branch) => …)` callback. It describes
45
+ * **which** branch this invocation decides for; it is not a live branch handle and must not
46
+ * be mutated. Switch on its fields and return the desired {@link BranchConfig}.
47
+ */
48
+ interface BranchTarget {
49
+ /** Branch name being evaluated. For `branch dev`, this is the generated branch name. */
50
+ name: string;
51
+ /** Neon branch id when the branch already exists. Undefined during pre-create eval. */
52
+ id?: string;
53
+ /** Whether this branch already exists on Neon. */
54
+ exists: boolean;
55
+ /** Parent branch id from Neon when known. */
56
+ parentId?: string;
57
+ /** Whether Neon marks this branch as the project default. */
58
+ isDefault?: boolean;
59
+ /** Whether Neon currently marks this branch protected. */
60
+ isProtected?: boolean;
61
+ /** Current expiration timestamp from Neon, when set. */
62
+ expiresAt?: string;
63
+ }
64
+ interface ServiceToggle {
65
+ /** Defaults to `true` when the service namespace is present. Set `false` to opt out. */
66
+ enabled?: boolean;
67
+ }
68
+ interface PostgresConfig {
69
+ computeSettings?: ComputeSettings;
70
+ }
71
+ /**
72
+ * Supported function runtimes. Mirrors the Neon Functions deploy API `runtime` enum.
73
+ * Only `nodejs24` exists today; kept as a union so adding runtimes later is a
74
+ * non-breaking, type-checked change.
75
+ */
76
+ type FunctionRuntime = "nodejs24";
77
+ /**
78
+ * Memory sizes (MiB) accepted by the Neon Functions deploy API. Mirrors the
79
+ * `memory_mib` enum in the spec.
80
+ */
81
+ type FunctionMemoryMib = 256 | 512 | 1024 | 2048 | 4096 | 8192;
82
+ /**
83
+ * A single Neon Function deployed to a branch (Preview feature).
84
+ *
85
+ * A function is invoked like a Cloudflare/Vercel handler — its source module
86
+ * `export default { fetch }` or `export async function handler(req): Response`. The
87
+ * `source` path is bundled (esbuild) and uploaded as a deployment; the newest
88
+ * deployment becomes active.
89
+ */
90
+ interface FunctionConfig {
91
+ /**
92
+ * Branch-unique, lowercase DNS-label used as the path segment in the function's
93
+ * invocation URL. Immutable once created. 1–40 chars, `^[a-z0-9]([a-z0-9-]{0,38}[a-z0-9])?$`.
94
+ * @example "hello-world"
95
+ */
96
+ slug: string;
97
+ /** Free-form display name. @example "Hello World" */
98
+ name: string;
99
+ /**
100
+ * Path to the function's entry module, **relative to `neon.ts`** (or absolute). The
101
+ * module's default export (`{ fetch }`) or `handler` export is the function entry. This
102
+ * path is resolved against the loaded `neon.ts` location and bundled with esbuild at
103
+ * deploy time.
104
+ *
105
+ * We require a string path rather than an imported handler because a JS function value
106
+ * carries no reference back to its source file, so esbuild has nothing to bundle from.
107
+ * @example "./functions/hello-world.ts"
108
+ */
109
+ source: string;
110
+ /**
111
+ * Environment variables injected into the deployed function. Every value must be a
112
+ * defined string — a `process.env.X` that is `undefined` (unset) errors at validation
113
+ * time rather than silently shipping `undefined`.
114
+ * @example { RESEND_API_KEY: process.env.RESEND_API_KEY }
115
+ */
116
+ env?: Record<string, string>;
117
+ /** Runtime to execute the function with. Defaults to `"nodejs24"`. */
118
+ runtime?: FunctionRuntime;
119
+ /** Memory allotted to each invocation, in MiB. Defaults to `512`. */
120
+ memoryMib?: FunctionMemoryMib;
121
+ }
122
+ /** Anonymous-access level for a branchable object-storage bucket. */
123
+ type BucketAccessLevel = "private" | "public_read";
124
+ /**
125
+ * A branchable object-storage bucket on a branch (Preview feature).
126
+ */
127
+ interface BucketConfig {
128
+ /** Bucket name, unique within a branch. 1–255 chars. */
129
+ name: string;
130
+ /**
131
+ * Anonymous access level. `private` (default) requires authenticated reads/writes;
132
+ * `public_read` allows anonymous GetObject/HeadObject.
133
+ */
134
+ access?: BucketAccessLevel;
135
+ }
136
+ /**
137
+ * Branch-scoped Preview features. Grouped under `preview` to signal they are backed by
138
+ * Neon `x-stability-level: beta` endpoints and may change before GA.
139
+ */
140
+ interface PreviewConfig {
141
+ /** Functions to deploy on the branch. */
142
+ functions?: FunctionConfig[];
143
+ /** Object-storage buckets to create on the branch. */
144
+ buckets?: BucketConfig[];
145
+ /** Enable/disable the AI Gateway on the branch (toggle, like auth / dataApi). */
146
+ aiGateway?: ServiceToggle;
147
+ }
148
+ interface BranchConfigBase {
149
+ /** Parent branch name used when creating a new branch. Not a Postgres setting. */
150
+ parent?: string;
151
+ /** Time-to-live applied when creating a new branch, or reconciled on existing branches. */
152
+ ttl?: string | number;
153
+ /** Whether the selected branch should be protected. Undefined means "leave as-is". */
154
+ protected?: boolean;
155
+ postgres?: PostgresConfig;
156
+ /**
157
+ * Branch-scoped Preview features (functions, object-storage buckets, AI Gateway).
158
+ * Backed by Neon `x-stability-level: beta` endpoints — see {@link PreviewConfig}.
159
+ */
160
+ preview?: PreviewConfig;
161
+ }
162
+ type BranchServiceConfig = {
163
+ auth?: never;
164
+ dataApi?: never;
165
+ } | {
166
+ auth: ServiceToggle;
167
+ dataApi?: never;
168
+ } | {
169
+ auth?: never;
170
+ dataApi: ServiceToggle;
171
+ } | {
172
+ auth: ServiceToggle;
173
+ dataApi: ServiceToggle;
174
+ };
175
+ type BranchConfig = BranchConfigBase & BranchServiceConfig;
176
+ type Config = (branch: BranchTarget) => BranchConfig;
177
+ /**
178
+ * A function with all deploy defaults applied. `resolveConfig` fills in `runtime` and
179
+ * `memoryMib` so downstream diff/apply never has to re-derive them.
180
+ */
181
+ interface ResolvedFunctionConfig {
182
+ slug: string;
183
+ name: string;
184
+ source: string;
185
+ env: Record<string, string>;
186
+ runtime: FunctionRuntime;
187
+ memoryMib: FunctionMemoryMib;
188
+ }
189
+ /** A bucket with its access level defaulted to `private`. */
190
+ interface ResolvedBucketConfig {
191
+ name: string;
192
+ access: BucketAccessLevel;
193
+ }
194
+ /**
195
+ * Normalized {@link PreviewConfig}. Only present on {@link ResolvedBranchConfig} when the
196
+ * policy returned a `preview` block. `aiGatewayEnabled` follows the same
197
+ * "present-and-not-`false`" semantics as `authEnabled` / `dataApiEnabled`.
198
+ */
199
+ interface ResolvedPreviewConfig {
200
+ functions: ResolvedFunctionConfig[];
201
+ buckets: ResolvedBucketConfig[];
202
+ aiGatewayEnabled: boolean;
203
+ }
204
+ interface ResolvedBranchConfig {
205
+ parent?: string;
206
+ ttlSeconds?: number;
207
+ protected?: boolean;
208
+ postgres?: PostgresConfig;
209
+ authEnabled: boolean;
210
+ dataApiEnabled: boolean;
211
+ preview?: ResolvedPreviewConfig;
212
+ }
213
+ /**
214
+ * One concrete change `pushConfig` made (or, in dry-run, would make) on the remote.
215
+ */
216
+ interface AppliedChange {
217
+ /**
218
+ * `service` covers branch-scoped integrations driven by the branch policy (e.g.
219
+ * Neon Auth, Data API).
220
+ */
221
+ kind: "branch" | "service";
222
+ action: "create" | "update" | "noop";
223
+ identifier: string;
224
+ details?: Record<string, unknown>;
225
+ }
226
+ /**
227
+ * A diff entry that conflicts with the desired config. `pushConfig` throws
228
+ * {@link PushConflictError} on the first call when conflicts exist; pass
229
+ * `updateExisting: true` to apply mutable drift (settings, `protected`, TTL, project
230
+ * rename). Immutable fields (region, Postgres major version) are always conflicts —
231
+ * recreate the project to change them.
232
+ */
233
+ interface ConflictReport {
234
+ kind: "branch";
235
+ identifier: string;
236
+ field: string;
237
+ current: unknown;
238
+ desired: unknown;
239
+ reason: string;
240
+ }
241
+ /**
242
+ * Result of a `pushConfig` invocation.
243
+ */
244
+ interface PushResult {
245
+ projectId: string;
246
+ orgId?: string;
247
+ branchId: string;
248
+ branchName: string;
249
+ /**
250
+ * `true` when `pushConfig` was called with `{ dryRun: true }`. `applied` then records
251
+ * what **would** be applied on a real push; no API mutations were performed.
252
+ */
253
+ dryRun: boolean;
254
+ applied: AppliedChange[];
255
+ conflicts: ConflictReport[];
256
+ }
257
+ //#endregion
258
+ export { AppliedChange, BranchConfig, BranchTarget, BucketAccessLevel, BucketConfig, ComputeSettings, ComputeUnit, Config, ConflictReport, FunctionConfig, FunctionMemoryMib, FunctionRuntime, PostgresConfig, PreviewConfig, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceToggle };
259
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/lib/types.ts"],"mappings":";;AAIA;AASA;;AAMyB,KAfb,WAAA,GAea,IAAA,GAAA,GAAA,GAAA,CAAA,GAAA,CAAA,GAAA,CAAA,GAAA,CAAA;;AAMW;AAuBpC;AAiBA;AAKA;AASA;AAMA;AAUiB,UAlFA,eAAA,CAkFc;EAAA;;;;AA8BD;EAIlB,qBAAiB,CAAA,EA9GJ,WA8GI;EAKZ;AAcjB;;;;uBAMa,CAAA,EAjIY,WAiIZ;EAAa;AACzB;;;;AAcuB;AAAA;;;;;;AAOwB;EAEpC,cAAA,CAAY,EAAA,KAAA,GAAA,IAAA,GAAA,IAAA,GAAA,MAAA,GAAA,MAAA;;;;AAAyC;AAEjE;;;AAA+C,UApI9B,YAAA,CAoI8B;EAAY;EAM1C,IAAA,EAAA,MAAA;EAAsB;KAIjC,EAAA,MAAA;;QAEM,EAAA,OAAA;EAAiB;EAIZ,QAAA,CAAA,EAAA,MAAA;EAUA;EAAqB,SAAA,CAAA,EAAA,OAAA;;aAE5B,CAAA,EAAA,OAAA;EAAoB;EAIb,SAAA,CAAA,EAAA,MAAA;;AAIL,UAvJK,aAAA,CAuJL;;EAGoB,OAAA,CAAA,EAAA,OAAA;AAMhC;AAkBiB,UA7KA,cAAA,CA6Kc;EAYd,eAAU,CAAA,EAxLR,eAwLQ;;;;AAWD;;;KA3Ld,eAAA;;;;;KAMA,iBAAA;;;;;;;;;UAUK,cAAA;;;;;;;;;;;;;;;;;;;;;;;;;;QA0BV;;YAEI;;cAEE;;;KAID,iBAAA;;;;UAKK,YAAA;;;;;;;WAOP;;;;;;UAOO,aAAA;;cAEJ;;YAEF;;cAEE;;UAGH,gBAAA;;;;;;;aAOE;;;;;YAKD;;KAGN,mBAAA;;;;QAEM;;;;WACiB;;QACjB;WAAwB;;KAEvB,YAAA,GAAe,mBAAmB;KAElC,MAAA,YAAkB,iBAAiB;;;;;UAM9B,sBAAA;;;;OAIX;WACI;aACE;;;UAIK,oBAAA;;UAER;;;;;;;UAQQ,qBAAA;aACL;WACF;;;UAIO,oBAAA;;;;aAIL;;;YAGD;;;;;UAMM,aAAA;;;;;;;;YAQN;;;;;;;;;UAUM,cAAA;;;;;;;;;;;UAYA,UAAA;;;;;;;;;;WAUP;aACE"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { PlatformError } from "./errors.js";
2
+
3
+ //#region src/lib/wrap-neon-error.d.ts
4
+
5
+ /**
6
+ * Context the wrapper attaches to every PlatformError so consumers can debug without
7
+ * digging into the raw axios stack.
8
+ */
9
+ interface NeonErrorContext {
10
+ /** Short label of the operation that failed, e.g. `getProject(proj-foo)` or `createBranch`. */
11
+ op: string;
12
+ /** Optional project id when the operation is project-scoped. */
13
+ projectId?: string;
14
+ }
15
+ /**
16
+ * Turn a raw error from `@neondatabase/api-client` (axios under the hood) into a typed
17
+ * {@link PlatformError} whose message includes:
18
+ *
19
+ * 1. What operation was attempted (`op`, e.g. `getProject(proj-foo)`).
20
+ * 2. Why it failed in human terms (e.g. "API key is unauthorized").
21
+ * 3. The exact Neon API error message + request id (when present) for support tickets.
22
+ * 4. A concrete next action ("Generate a new key at …", "Pass `projectId`", …).
23
+ *
24
+ * Non-axios errors are passed through unchanged (a regular `Error` already has a useful
25
+ * stack trace; wrapping it would lose information without adding value).
26
+ */
27
+ declare function wrapNeonError(err: unknown, context: NeonErrorContext): PlatformError | unknown;
28
+ //#endregion
29
+ export { NeonErrorContext, wrapNeonError };
30
+ //# sourceMappingURL=wrap-neon-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-neon-error.d.ts","names":[],"sources":["../../src/lib/wrap-neon-error.ts"],"mappings":";;;;;;AAMA;AAmBA;AAA6B,UAnBZ,gBAAA,CAmBY;;MAG1B,MAAA;EAAa;;;;;;;;;;;;;;;iBAHA,aAAA,wBAEN,mBACP"}
@@ -0,0 +1,139 @@
1
+ import { ErrorCode, PlatformError } from "./errors.js";
2
+ //#region src/lib/wrap-neon-error.ts
3
+ /**
4
+ * Turn a raw error from `@neondatabase/api-client` (axios under the hood) into a typed
5
+ * {@link PlatformError} whose message includes:
6
+ *
7
+ * 1. What operation was attempted (`op`, e.g. `getProject(proj-foo)`).
8
+ * 2. Why it failed in human terms (e.g. "API key is unauthorized").
9
+ * 3. The exact Neon API error message + request id (when present) for support tickets.
10
+ * 4. A concrete next action ("Generate a new key at …", "Pass `projectId`", …).
11
+ *
12
+ * Non-axios errors are passed through unchanged (a regular `Error` already has a useful
13
+ * stack trace; wrapping it would lose information without adding value).
14
+ */
15
+ function wrapNeonError(err, context) {
16
+ if (err instanceof PlatformError) return err;
17
+ const httpInfo = extractHttpInfo(err);
18
+ if (!httpInfo) {
19
+ const networkInfo = extractNetworkInfo(err);
20
+ if (networkInfo) return new PlatformError(ErrorCode.NetworkError, `Could not reach the Neon API while running ${context.op}: ${networkInfo.message}. Check your network connection and that https://console.neon.tech is reachable.`, {
21
+ cause: err,
22
+ details: {
23
+ op: context.op,
24
+ ...networkInfo
25
+ }
26
+ });
27
+ return err;
28
+ }
29
+ const apiSummary = httpInfo.neonMessage ? `Neon API said: "${httpInfo.neonMessage}"` : `HTTP ${httpInfo.status}`;
30
+ const requestIdSuffix = httpInfo.requestId ? ` (request id ${httpInfo.requestId})` : "";
31
+ const apiSummaryWithRequestId = `${apiSummary}${requestIdSuffix}.`;
32
+ switch (httpInfo.status) {
33
+ case 401: return new PlatformError(ErrorCode.Unauthorized, [
34
+ `${context.op} failed: the Bearer token sent to the Neon API was rejected.`,
35
+ apiSummaryWithRequestId,
36
+ "Either (a) generate or rotate an API key at https://console.neon.tech/app/settings/api-keys and set NEON_API_KEY / pass --api-key, or (b) re-run `npx neonctl auth` to refresh the OAuth token in `~/.config/neonctl/credentials.json` (OAuth tokens expire)."
37
+ ].join(" "), {
38
+ cause: err,
39
+ details: httpDetails(context, httpInfo)
40
+ });
41
+ case 403: return new PlatformError(ErrorCode.Forbidden, [
42
+ `${context.op} failed: this API key is not allowed to perform that operation.`,
43
+ apiSummaryWithRequestId,
44
+ "Project-scoped keys can only operate on their own project; switch to an organisation/user-scoped key or pass `projectId` for an operation that doesn't need listing."
45
+ ].join(" "), {
46
+ cause: err,
47
+ details: httpDetails(context, httpInfo)
48
+ });
49
+ case 404: return new PlatformError(ErrorCode.NotFound, [
50
+ `${context.op} failed: resource not found on Neon.`,
51
+ apiSummaryWithRequestId,
52
+ context.projectId ? `Verify that project '${context.projectId}' exists in this account and that the API key has access to it.` : "Verify that the resource id is correct and that the API key has access to it."
53
+ ].join(" "), {
54
+ cause: err,
55
+ details: httpDetails(context, httpInfo)
56
+ });
57
+ case 409: return new PlatformError(ErrorCode.Conflict, [
58
+ `${context.op} failed: a conflicting resource already exists on Neon.`,
59
+ apiSummaryWithRequestId,
60
+ "This is often a name collision (e.g. a branch with the same name already exists). Pull first to compare against the remote, or rename in your `neon.ts`."
61
+ ].join(" "), {
62
+ cause: err,
63
+ details: httpDetails(context, httpInfo)
64
+ });
65
+ case 423: return new PlatformError(ErrorCode.Locked, [
66
+ `${context.op} failed: the resource is still being modified by a previous operation, and our built-in retries did not drain it in time.`,
67
+ apiSummaryWithRequestId,
68
+ "Wait a few seconds and re-run, or raise `retryOnLocked.maxAttempts` when constructing the real Neon adapter."
69
+ ].join(" "), {
70
+ cause: err,
71
+ details: httpDetails(context, httpInfo)
72
+ });
73
+ case 429: return new PlatformError(ErrorCode.RateLimited, [
74
+ `${context.op} failed: rate-limited by the Neon API.`,
75
+ apiSummaryWithRequestId,
76
+ "Back off and retry; if this happens repeatedly, contact Neon support with the request id above."
77
+ ].join(" "), {
78
+ cause: err,
79
+ details: httpDetails(context, httpInfo)
80
+ });
81
+ }
82
+ if (httpInfo.status >= 500) return new PlatformError(ErrorCode.ServerError, [
83
+ `${context.op} failed: the Neon API returned a server error (HTTP ${httpInfo.status}).`,
84
+ apiSummaryWithRequestId,
85
+ "This is most likely transient. Retry shortly; if it persists, file an issue with the request id above and check https://neonstatus.com."
86
+ ].join(" "), {
87
+ cause: err,
88
+ details: httpDetails(context, httpInfo)
89
+ });
90
+ return new PlatformError(ErrorCode.ServerError, `${context.op} failed: HTTP ${httpInfo.status}. ${apiSummary}${requestIdSuffix}.`, {
91
+ cause: err,
92
+ details: httpDetails(context, httpInfo)
93
+ });
94
+ }
95
+ function extractHttpInfo(err) {
96
+ if (err === null || typeof err !== "object") return null;
97
+ const response = err.response;
98
+ if (response === null || typeof response !== "object") return null;
99
+ const status = response.status;
100
+ if (typeof status !== "number") return null;
101
+ const data = response.data;
102
+ const out = { status };
103
+ if (data !== null && typeof data === "object") {
104
+ const dataObj = data;
105
+ if (typeof dataObj.message === "string" && dataObj.message !== "") out.neonMessage = dataObj.message;
106
+ if (typeof dataObj.code === "string" && dataObj.code !== "") out.neonCode = dataObj.code;
107
+ if (typeof dataObj.request_id === "string" && dataObj.request_id !== "") out.requestId = dataObj.request_id;
108
+ }
109
+ return out;
110
+ }
111
+ function extractNetworkInfo(err) {
112
+ if (err === null || typeof err !== "object") return null;
113
+ const code = err.code;
114
+ const message = err.message;
115
+ if (typeof code === "string" && /^(ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|EPIPE|EHOSTUNREACH|ENETUNREACH)$/.test(code)) return {
116
+ message: typeof message === "string" ? message : code,
117
+ code
118
+ };
119
+ if (code === "ECONNABORTED") return {
120
+ message: typeof message === "string" ? message : "timeout",
121
+ code
122
+ };
123
+ return null;
124
+ }
125
+ function httpDetails(context, info) {
126
+ const out = {
127
+ op: context.op,
128
+ status: info.status
129
+ };
130
+ if (context.projectId) out.projectId = context.projectId;
131
+ if (info.neonMessage) out.neonMessage = info.neonMessage;
132
+ if (info.neonCode) out.neonCode = info.neonCode;
133
+ if (info.requestId) out.requestId = info.requestId;
134
+ return out;
135
+ }
136
+ //#endregion
137
+ export { wrapNeonError };
138
+
139
+ //# sourceMappingURL=wrap-neon-error.js.map