@neondatabase/config 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -7
- package/dist/index.d.ts +2 -2
- package/dist/lib/define-config.d.ts +32 -15
- package/dist/lib/define-config.d.ts.map +1 -1
- package/dist/lib/define-config.js +79 -58
- package/dist/lib/define-config.js.map +1 -1
- package/dist/lib/errors.d.ts +1 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +1 -0
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/neon-api-real.d.ts +43 -2
- package/dist/lib/neon-api-real.d.ts.map +1 -1
- package/dist/lib/neon-api-real.js +85 -19
- package/dist/lib/neon-api-real.js.map +1 -1
- package/dist/lib/neon-api.d.ts +1 -2
- package/dist/lib/neon-api.d.ts.map +1 -1
- package/dist/lib/schema.d.ts +61 -50
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +52 -72
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +129 -75
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/v1.d.ts +48 -55
- package/dist/v1.d.ts.map +1 -1
- package/dist/v1.js +9 -7
- package/dist/v1.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,17 +16,30 @@ npm install @neondatabase/config
|
|
|
16
16
|
// neon.ts
|
|
17
17
|
import { defineConfig } from "@neondatabase/config/v1";
|
|
18
18
|
|
|
19
|
-
export default defineConfig(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export default defineConfig({
|
|
20
|
+
// Static: what *exists* on every branch. GA service toggles drive the typed env.
|
|
21
|
+
auth: true,
|
|
22
|
+
dataApi: false,
|
|
23
|
+
// Beta (Preview) features, keyed by slug / name.
|
|
24
|
+
preview: {
|
|
25
|
+
functions: {
|
|
26
|
+
hello: { name: "Hello", source: "./functions/hello.ts", dev: { port: 8787 } },
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
// Dynamic: per-branch tuning only. Cannot add/remove services or functions.
|
|
30
|
+
branch: (branch) => ({
|
|
31
|
+
protected: branch.name === "main",
|
|
32
|
+
...(branch.name === "main" ? {} : { parent: "main", ttl: "7d" }),
|
|
33
|
+
}),
|
|
24
34
|
});
|
|
25
35
|
```
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
A policy is split into a **static** existential set and a **dynamic** `branch` closure:
|
|
28
38
|
|
|
29
|
-
`
|
|
39
|
+
- **Static top-level** — `auth` / `dataApi` (GA service toggles) and the beta `preview` block (`aiGateway`, `functions` keyed by slug, `buckets` keyed by name). Because this is static, the secret set is known at the type level, so `parseEnv` / `fetchEnv` from `@neondatabase/env` return an exact `NeonEnv`.
|
|
40
|
+
- **`branch` closure** — receives a **read-only descriptor** (`BranchTarget`) of the branch being evaluated (`name`, `id`, `exists`, `isDefault`, `isProtected`, `parentId`, `expiresAt`) and returns per-branch *tuning*: `parent`, `ttl`, `protected`, `postgres.computeSettings`, and per-function `runtime`. Function memory is fixed at `2048` MiB for now and is not user-configurable. It runs both against existing branches and during pre-create evaluation (`exists: false`). It **cannot** change which services or functions exist — that is what keeps the static secret set sound.
|
|
41
|
+
|
|
42
|
+
Service toggles accept `true` / `{}` / `{ enabled: true }` (enabled) and `false` / `{ enabled: false }` (disabled). Function slugs (record keys) must match `^[a-z0-9]{1,20}$`.
|
|
30
43
|
|
|
31
44
|
## Functions
|
|
32
45
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppliedChange,
|
|
1
|
+
import { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, Config, ConflictReport, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceToggle, ServiceToggleInput } from "./lib/types.js";
|
|
2
2
|
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError } from "./lib/errors.js";
|
|
3
3
|
import { CreateBranchInput, CreateBucketInput, CreateProjectInput, DeployFunctionInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBucketSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, UpdateBranchInput } from "./lib/neon-api.js";
|
|
4
4
|
import { createNeonApiFromOptions, resolveApiKey } from "./lib/auth.js";
|
|
@@ -7,4 +7,4 @@ import { DiffOptions, DiffResult, PlanStep, RemotePreviewState, RemoteServiceSta
|
|
|
7
7
|
import { LoadConfigOptions, loadConfigFromFile } from "./lib/loader.js";
|
|
8
8
|
import { createRealNeonApi } from "./lib/neon-api-real.js";
|
|
9
9
|
import { errors, schemas } from "./v1.js";
|
|
10
|
-
export { AppliedChange,
|
|
10
|
+
export { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateBranchInput, CreateBucketInput, CreateProjectInput, DeployFunctionInput, DiffOptions, DiffResult, ErrorCode, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionTuning, GetConnectionUriInput, LoadConfigOptions, MissingContextError, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBucketSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, PlanStep, PlatformError, PostgresConfig, PreviewInput, PreviewTuning, PushAbortedError, PushConflictError, PushResult, RemotePreviewState, RemoteServiceState, RemoteState, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedFunctionConfig, ResolvedPreviewConfig, ServiceToggle, ServiceToggleInput, UpdateBranchInput, createNeonApiFromOptions, createRealNeonApi, defineConfig, diffConfig, errors, loadConfigFromFile, resolveApiKey, resolveConfig, schemas };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BranchTarget, Config, ResolvedBranchConfig } from "./types.js";
|
|
1
|
+
import { BranchTarget, BranchTuningFn, Config, PreviewInput, ResolvedBranchConfig, ServiceToggleInput } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/define-config.d.ts
|
|
4
4
|
|
|
@@ -9,27 +9,44 @@ import { BranchTarget, Config, ResolvedBranchConfig } from "./types.js";
|
|
|
9
9
|
* ```ts
|
|
10
10
|
* import { defineConfig } from "@neondatabase/config/v1";
|
|
11
11
|
*
|
|
12
|
-
* export default defineConfig(
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* export default defineConfig({
|
|
13
|
+
* auth: true,
|
|
14
|
+
* preview: {
|
|
15
|
+
* functions: {
|
|
16
|
+
* hello: { name: "Hello", source: "./functions/hello.ts", dev: { port: 8787 } },
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* branch: (branch) => ({ protected: branch.name === "main" }),
|
|
17
20
|
* });
|
|
18
21
|
* ```
|
|
19
22
|
*
|
|
20
|
-
* The
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Neon) and during pre-create evaluation (`exists: false`, `id` undefined).
|
|
23
|
+
* The policy is split into a **static** existential set (top-level `auth` / `dataApi`
|
|
24
|
+
* toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The
|
|
25
|
+
* static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`
|
|
26
|
+
* are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function
|
|
27
|
+
* deploy settings), never change what exists.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
+
* The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch
|
|
30
|
+
* being decided for (not a live handle); switch on its facts (`branch.name`,
|
|
31
|
+
* `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two
|
|
32
|
+
* modes: against an existing branch (fields populated from Neon) and during pre-create
|
|
33
|
+
* evaluation (`exists: false`, `id` undefined).
|
|
34
|
+
*
|
|
35
|
+
* Pure: no I/O, no side effects. The static parts are validated here; the closure's output
|
|
36
|
+
* is validated every time it is evaluated so errors point at the concrete branch target.
|
|
29
37
|
*/
|
|
30
|
-
declare function defineConfig<const
|
|
38
|
+
declare function defineConfig<const Auth extends ServiceToggleInput | undefined = undefined, const DataApi extends ServiceToggleInput | undefined = undefined, const Preview extends PreviewInput | undefined = undefined>(input: {
|
|
39
|
+
auth?: Auth;
|
|
40
|
+
dataApi?: DataApi;
|
|
41
|
+
preview?: Preview;
|
|
42
|
+
branch?: BranchTuningFn<Preview>;
|
|
43
|
+
}): Config<Auth, DataApi, Preview>;
|
|
31
44
|
/**
|
|
32
45
|
* Evaluate a branch policy for a specific branch target and return a normalized config.
|
|
46
|
+
*
|
|
47
|
+
* Merges the static existential set (services + preview functions/buckets) with the
|
|
48
|
+
* per-branch tuning returned by the `branch` closure into the same {@link
|
|
49
|
+
* ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.
|
|
33
50
|
*/
|
|
34
51
|
declare function resolveConfig(config: Config, branch: BranchTarget): ResolvedBranchConfig;
|
|
35
52
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.d.ts","names":[],"sources":["../../src/lib/define-config.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","names":[],"sources":["../../src/lib/define-config.ts"],"mappings":";;;;;;AA2DA;;;;;;;;;;;;;;AASU;AA4BV;;;;;AAGuB;AA6GvB;;;;;;;;;;iBArJgB,gCACI,kEACG,kEACA;SAEf;YACG;YACA;WACD,eAAe;IACrB,OAAO,MAAM,SAAS;;;;;;;;iBA4BV,aAAA,SACP,gBACA,eACN;;;;;;iBA6Ga,eAAA"}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { ConfigValidationError } from "./errors.js";
|
|
2
2
|
import { parseDuration } from "./duration.js";
|
|
3
|
-
import {
|
|
3
|
+
import { branchTuningSchema, configInputSchema, formatZodIssues } from "./schema.js";
|
|
4
4
|
//#region src/lib/define-config.ts
|
|
5
5
|
/** Default deploy parameters applied to functions that omit them in `neon.ts`. */
|
|
6
6
|
const DEFAULT_FUNCTION_RUNTIME = "nodejs24";
|
|
7
|
-
const DEFAULT_FUNCTION_MEMORY_MIB = 512;
|
|
8
7
|
const REGION_PREFIX = /^(aws|azure|gcp)-/;
|
|
9
8
|
/**
|
|
10
9
|
* Validate and freeze a Neon Platform branch policy.
|
|
@@ -13,88 +12,110 @@ const REGION_PREFIX = /^(aws|azure|gcp)-/;
|
|
|
13
12
|
* ```ts
|
|
14
13
|
* import { defineConfig } from "@neondatabase/config/v1";
|
|
15
14
|
*
|
|
16
|
-
* export default defineConfig(
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
15
|
+
* export default defineConfig({
|
|
16
|
+
* auth: true,
|
|
17
|
+
* preview: {
|
|
18
|
+
* functions: {
|
|
19
|
+
* hello: { name: "Hello", source: "./functions/hello.ts", dev: { port: 8787 } },
|
|
20
|
+
* },
|
|
21
|
+
* },
|
|
22
|
+
* branch: (branch) => ({ protected: branch.name === "main" }),
|
|
21
23
|
* });
|
|
22
24
|
* ```
|
|
23
25
|
*
|
|
24
|
-
* The
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* Neon) and during pre-create evaluation (`exists: false`, `id` undefined).
|
|
26
|
+
* The policy is split into a **static** existential set (top-level `auth` / `dataApi`
|
|
27
|
+
* toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The
|
|
28
|
+
* static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`
|
|
29
|
+
* are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function
|
|
30
|
+
* deploy settings), never change what exists.
|
|
30
31
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch
|
|
33
|
+
* being decided for (not a live handle); switch on its facts (`branch.name`,
|
|
34
|
+
* `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two
|
|
35
|
+
* modes: against an existing branch (fields populated from Neon) and during pre-create
|
|
36
|
+
* evaluation (`exists: false`, `id` undefined).
|
|
37
|
+
*
|
|
38
|
+
* Pure: no I/O, no side effects. The static parts are validated here; the closure's output
|
|
39
|
+
* is validated every time it is evaluated so errors point at the concrete branch target.
|
|
33
40
|
*/
|
|
34
41
|
function defineConfig(input) {
|
|
35
|
-
if (typeof input
|
|
36
|
-
|
|
42
|
+
if (typeof input === "function") throw new ConfigValidationError(["defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.", "The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure."]);
|
|
43
|
+
if (input === null || typeof input !== "object") throw new ConfigValidationError(["defineConfig expects a configuration object: `export default defineConfig({ … })`."]);
|
|
44
|
+
const parsed = configInputSchema.safeParse(input);
|
|
45
|
+
if (!parsed.success) throw new ConfigValidationError(formatZodIssues(parsed.error));
|
|
46
|
+
return Object.freeze({ ...input });
|
|
37
47
|
}
|
|
38
48
|
/**
|
|
39
49
|
* Evaluate a branch policy for a specific branch target and return a normalized config.
|
|
50
|
+
*
|
|
51
|
+
* Merges the static existential set (services + preview functions/buckets) with the
|
|
52
|
+
* per-branch tuning returned by the `branch` closure into the same {@link
|
|
53
|
+
* ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.
|
|
40
54
|
*/
|
|
41
55
|
function resolveConfig(config, branch) {
|
|
56
|
+
const tuning = evaluateBranchTuning(config.branch, branch);
|
|
57
|
+
const resolved = {
|
|
58
|
+
authEnabled: isServiceEnabled(config.auth),
|
|
59
|
+
dataApiEnabled: isServiceEnabled(config.dataApi)
|
|
60
|
+
};
|
|
61
|
+
if (tuning.parent !== void 0) resolved.parent = tuning.parent;
|
|
62
|
+
if (tuning.ttl !== void 0) {
|
|
63
|
+
const parsedTtl = parseDuration(tuning.ttl);
|
|
64
|
+
if (!("error" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;
|
|
65
|
+
}
|
|
66
|
+
if (tuning.protected !== void 0) resolved.protected = tuning.protected;
|
|
67
|
+
if (tuning.postgres?.computeSettings) resolved.postgres = { computeSettings: { ...tuning.postgres.computeSettings } };
|
|
68
|
+
const preview = resolvePreviewConfig(config.preview, tuning);
|
|
69
|
+
if (preview) resolved.preview = preview;
|
|
70
|
+
return resolved;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run the `branch` closure (when present) for the target and validate its output. The
|
|
74
|
+
* closure is optional — a fully static policy resolves with empty tuning.
|
|
75
|
+
*/
|
|
76
|
+
function evaluateBranchTuning(branchFn, target) {
|
|
77
|
+
if (!branchFn) return {};
|
|
42
78
|
let raw;
|
|
43
79
|
try {
|
|
44
|
-
raw =
|
|
80
|
+
raw = branchFn(Object.freeze({ ...target }));
|
|
45
81
|
} catch (cause) {
|
|
46
|
-
throw new ConfigValidationError([`
|
|
82
|
+
throw new ConfigValidationError([`Branch policy threw while evaluating branch "${target.name}".`, cause?.message ?? String(cause)]);
|
|
47
83
|
}
|
|
48
|
-
const parsed =
|
|
84
|
+
const parsed = branchTuningSchema.safeParse(raw ?? {});
|
|
49
85
|
if (!parsed.success) throw new ConfigValidationError(formatZodIssues(parsed.error));
|
|
50
|
-
|
|
51
|
-
const issues = [];
|
|
52
|
-
let ttlSeconds;
|
|
53
|
-
if (cfg.ttl !== void 0) {
|
|
54
|
-
const parsedTtl = parseDuration(cfg.ttl);
|
|
55
|
-
if ("error" in parsedTtl) issues.push(`ttl: ${parsedTtl.error}`);
|
|
56
|
-
else ttlSeconds = parsedTtl.seconds;
|
|
57
|
-
}
|
|
58
|
-
if (issues.length > 0) throw new ConfigValidationError(issues);
|
|
59
|
-
const resolved = {
|
|
60
|
-
authEnabled: isServiceEnabled(cfg.auth),
|
|
61
|
-
dataApiEnabled: isServiceEnabled(cfg.dataApi)
|
|
62
|
-
};
|
|
63
|
-
if (cfg.parent !== void 0) resolved.parent = cfg.parent;
|
|
64
|
-
if (ttlSeconds !== void 0) resolved.ttlSeconds = ttlSeconds;
|
|
65
|
-
if (cfg.protected !== void 0) resolved.protected = cfg.protected;
|
|
66
|
-
if (cfg.postgres) resolved.postgres = { ...cfg.postgres.computeSettings ? { computeSettings: { ...cfg.postgres.computeSettings } } : {} };
|
|
67
|
-
if (cfg.preview) resolved.preview = resolvePreviewConfig(cfg.preview);
|
|
68
|
-
return resolved;
|
|
86
|
+
return parsed.data;
|
|
69
87
|
}
|
|
70
|
-
function isServiceEnabled(
|
|
71
|
-
|
|
88
|
+
function isServiceEnabled(toggle) {
|
|
89
|
+
if (toggle === void 0) return false;
|
|
90
|
+
if (typeof toggle === "boolean") return toggle;
|
|
91
|
+
return toggle.enabled !== false;
|
|
72
92
|
}
|
|
73
93
|
/**
|
|
74
|
-
* Normalize
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
94
|
+
* Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a
|
|
95
|
+
* {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`
|
|
96
|
+
* block so the field can be omitted entirely. Function slugs / bucket names come from the
|
|
97
|
+
* record keys.
|
|
78
98
|
*/
|
|
79
|
-
function resolvePreviewConfig(preview) {
|
|
99
|
+
function resolvePreviewConfig(preview, tuning) {
|
|
100
|
+
if (!preview) return void 0;
|
|
101
|
+
const fnTuning = tuning.preview?.functions ?? {};
|
|
80
102
|
return {
|
|
81
|
-
functions: (preview.functions ??
|
|
82
|
-
buckets: (preview.buckets ??
|
|
83
|
-
name
|
|
84
|
-
access:
|
|
103
|
+
functions: Object.entries(preview.functions ?? {}).map(([slug, def]) => resolveFunctionConfig(slug, def, fnTuning[slug] ?? {})),
|
|
104
|
+
buckets: Object.entries(preview.buckets ?? {}).map(([name, def]) => ({
|
|
105
|
+
name,
|
|
106
|
+
access: def.access ?? "private"
|
|
85
107
|
})),
|
|
86
108
|
aiGatewayEnabled: isServiceEnabled(preview.aiGateway)
|
|
87
109
|
};
|
|
88
110
|
}
|
|
89
|
-
function resolveFunctionConfig(
|
|
111
|
+
function resolveFunctionConfig(slug, def, tuning) {
|
|
90
112
|
return {
|
|
91
|
-
slug
|
|
92
|
-
name:
|
|
93
|
-
source:
|
|
94
|
-
env: { ...
|
|
95
|
-
runtime:
|
|
96
|
-
|
|
97
|
-
...fn.dev ? { dev: fn.dev } : {}
|
|
113
|
+
slug,
|
|
114
|
+
name: def.name,
|
|
115
|
+
source: def.source,
|
|
116
|
+
env: { ...def.env ?? {} },
|
|
117
|
+
runtime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,
|
|
118
|
+
...def.dev ? { dev: def.dev } : {}
|
|
98
119
|
};
|
|
99
120
|
}
|
|
100
121
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import { parseDuration } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport {
|
|
1
|
+
{"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import { parseDuration } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tConfig,\n\tFunctionDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedFunctionConfig,\n\tResolvedPreviewConfig,\n\tServiceToggleInput,\n} from \"./types.js\";\n\n/** Default deploy parameters applied to functions that omit them in `neon.ts`. */\nconst DEFAULT_FUNCTION_RUNTIME = \"nodejs24\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Validate and freeze a Neon Platform branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neondatabase/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * preview: {\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\n * },\n * },\n * branch: (branch) => ({ protected: branch.name === \"main\" }),\n * });\n * ```\n *\n * The policy is split into a **static** existential set (top-level `auth` / `dataApi`\n * toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The\n * static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`\n * are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function\n * deploy settings), never change what exists.\n *\n * The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch\n * being decided for (not a live handle); switch on its facts (`branch.name`,\n * `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two\n * modes: against an existing branch (fields populated from Neon) and during pre-create\n * evaluation (`exists: false`, `id` undefined).\n *\n * Pure: no I/O, no side effects. The static parts are validated here; the closure's output\n * is validated every time it is evaluated so errors point at the concrete branch target.\n */\nexport function defineConfig<\n\tconst Auth extends ServiceToggleInput | undefined = undefined,\n\tconst DataApi extends ServiceToggleInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n>(input: {\n\tauth?: Auth;\n\tdataApi?: DataApi;\n\tpreview?: Preview;\n\tbranch?: BranchTuningFn<Preview>;\n}): Config<Auth, DataApi, Preview> {\n\tif (typeof input === \"function\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure.\",\n\t\t]);\n\t}\n\tif (input === null || typeof input !== \"object\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig expects a configuration object: `export default defineConfig({ … })`.\",\n\t\t]);\n\t}\n\n\tconst parsed = configInputSchema.safeParse(input);\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\n\treturn Object.freeze({ ...input }) as Config<Auth, DataApi, Preview>;\n}\n\n/**\n * Evaluate a branch policy for a specific branch target and return a normalized config.\n *\n * Merges the static existential set (services + preview functions/buckets) with the\n * per-branch tuning returned by the `branch` closure into the same {@link\n * ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.\n */\nexport function resolveConfig(\n\tconfig: Config,\n\tbranch: BranchTarget,\n): ResolvedBranchConfig {\n\tconst tuning = evaluateBranchTuning(config.branch, branch);\n\n\tconst resolved: ResolvedBranchConfig = {\n\t\tauthEnabled: isServiceEnabled(config.auth),\n\t\tdataApiEnabled: isServiceEnabled(config.dataApi),\n\t};\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseDuration`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseDuration(tuning.ttl);\n\t\tif (!(\"error\" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;\n\t}\n\tif (tuning.protected !== undefined) resolved.protected = tuning.protected;\n\tif (tuning.postgres?.computeSettings) {\n\t\tresolved.postgres = {\n\t\t\tcomputeSettings: { ...tuning.postgres.computeSettings },\n\t\t};\n\t}\n\n\tconst preview = resolvePreviewConfig(config.preview, tuning);\n\tif (preview) resolved.preview = preview;\n\n\treturn resolved;\n}\n\n/**\n * Run the `branch` closure (when present) for the target and validate its output. The\n * closure is optional — a fully static policy resolves with empty tuning.\n */\nfunction evaluateBranchTuning(\n\tbranchFn: BranchTuningFn | undefined,\n\ttarget: BranchTarget,\n): BranchTuning {\n\tif (!branchFn) return {};\n\tlet raw: unknown;\n\ttry {\n\t\traw = branchFn(Object.freeze({ ...target }));\n\t} catch (cause) {\n\t\tthrow new ConfigValidationError([\n\t\t\t`Branch policy threw while evaluating branch \"${target.name}\".`,\n\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t]);\n\t}\n\tconst parsed = branchTuningSchema.safeParse(raw ?? {});\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\treturn parsed.data as BranchTuning;\n}\n\nfunction isServiceEnabled(toggle: ServiceToggleInput | undefined): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/**\n * Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a\n * {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`\n * block so the field can be omitted entirely. Function slugs / bucket names come from the\n * record keys.\n */\nfunction resolvePreviewConfig(\n\tpreview: PreviewInput | undefined,\n\ttuning: BranchTuning,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = tuning.preview?.functions ?? {};\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}),\n\t);\n\tconst buckets = Object.entries(preview.buckets ?? {}).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\taccess: def.access ?? \"private\",\n\t\t}),\n\t);\n\treturn {\n\t\tfunctions,\n\t\tbuckets,\n\t\taiGatewayEnabled: isServiceEnabled(preview.aiGateway),\n\t};\n}\n\nfunction resolveFunctionConfig(\n\tslug: string,\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n): ResolvedFunctionConfig {\n\treturn {\n\t\tslug,\n\t\tname: def.name,\n\t\tsource: def.source,\n\t\tenv: { ...(def.env ?? {}) },\n\t\truntime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t};\n}\n\n/**\n * Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes\n * `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the\n * NeonApi adapter.\n */\nexport function normalizeRegion(region: string): string {\n\tif (REGION_PREFIX.test(region)) return region;\n\treturn `aws-${region}`;\n}\n"],"mappings":"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"}
|
package/dist/lib/errors.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ declare const ErrorCode: {
|
|
|
33
33
|
readonly MissingApiKey: "PLATFORM_MISSING_API_KEY";
|
|
34
34
|
readonly AmbiguousBranchAuth: "PLATFORM_AMBIGUOUS_BRANCH_AUTH";
|
|
35
35
|
readonly BranchNotFound: "PLATFORM_BRANCH_NOT_FOUND";
|
|
36
|
+
readonly FeatureUnavailable: "PLATFORM_FEATURE_UNAVAILABLE";
|
|
36
37
|
readonly MissingParentBranch: "PLATFORM_MISSING_PARENT_BRANCH";
|
|
37
38
|
readonly Unauthorized: "PLATFORM_UNAUTHORIZED";
|
|
38
39
|
readonly Forbidden: "PLATFORM_FORBIDDEN";
|
package/dist/lib/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/lib/errors.ts"],"mappings":";;;;;;AAuBA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/lib/errors.ts"],"mappings":";;;;;;AAuBA;AAsBA;;;;AAAiE;AAYjE;;;;;;AAAwC;AAqBxC;AAUA;AAoBA;AAeA;;;AAIiC,cAxGpB,SAwGoB,EAAA;WAJM,aAAA,EAAA,yBAAA;EAAa,SAAA,cAAA,EAAA,2BAAA;EAqDvC,SAAA,cAAiB,EAAA,0BAAqB;EA8BtC,SAAA,YAAgB,EAAA,wBAAqB;;;;;;;;;;;;;;;;;;KAjKtC,SAAA,WAAoB,wBAAwB;;;;;;;;;cAY3C,aAAA,SAAsB,KAAA;;;oBAGhB,SAAS;;;cAKa;;;;;;;;iBAazB,eAAA,CAAA;;;;;;;cAUH,qBAAA,SAA8B,aAAa;;;;;;;;;;;;cAoB3C,mBAAA,SAA4B,aAAa;;;;;;;;;;;cAezC,iBAAA,SAA0B,aAAA;;+BAET;kCAEG;;;;;;;;;cAiDpB,gBAAA,SAAyB,aAAa;;;;;;;;;cA8BtC,eAAA,SAAwB,aAAa"}
|
package/dist/lib/errors.js
CHANGED
|
@@ -30,6 +30,7 @@ const ErrorCode = {
|
|
|
30
30
|
MissingApiKey: "PLATFORM_MISSING_API_KEY",
|
|
31
31
|
AmbiguousBranchAuth: "PLATFORM_AMBIGUOUS_BRANCH_AUTH",
|
|
32
32
|
BranchNotFound: "PLATFORM_BRANCH_NOT_FOUND",
|
|
33
|
+
FeatureUnavailable: "PLATFORM_FEATURE_UNAVAILABLE",
|
|
33
34
|
MissingParentBranch: "PLATFORM_MISSING_PARENT_BRANCH",
|
|
34
35
|
Unauthorized: "PLATFORM_UNAUTHORIZED",
|
|
35
36
|
Forbidden: "PLATFORM_FORBIDDEN",
|
package/dist/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/lib/errors.ts"],"sourcesContent":["import type { ConflictReport } from \"./types.js\";\n\n/**\n * Every code a {@link PlatformError} can carry. Stable identifiers — consumers can rely on\n * these for `instanceof PlatformError && err.code === ErrorCode.…` style checks instead of\n * matching on free-text messages.\n *\n * Grouped by source:\n * - `PLATFORM_INVALID_CONFIG` — `defineConfig` / `configSchema` rejected the input.\n * - `PLATFORM_MISSING_CONTEXT` — no project / branch context could be resolved.\n * - `PLATFORM_PUSH_CONFLICT` — local config conflicts with remote and the caller did not\n * opt in to apply.\n * - `PLATFORM_CONFIG_LOAD_FAILED` — `neon.ts` could not be found or evaluated.\n * - `PLATFORM_MISSING_API_KEY` — no `NEON_API_KEY` and no explicit `apiKey` was provided.\n * - `PLATFORM_MISSING_PARENT_BRANCH` — push tried to create a child of a non-existent\n * branch.\n * - `PLATFORM_UNAUTHORIZED` / `PLATFORM_FORBIDDEN` / `PLATFORM_NOT_FOUND` /\n * `PLATFORM_CONFLICT` / `PLATFORM_RATE_LIMITED` / `PLATFORM_LOCKED` /\n * `PLATFORM_SERVER_ERROR` — wrappings of Neon HTTP failures.\n * - `PLATFORM_NETWORK_ERROR` — transport-level failure (no HTTP response at all).\n * - `PLATFORM_INTERNAL_ERROR` — invariant violations. Should never happen in production;\n * if you see one, please open an issue.\n */\nexport const ErrorCode = {\n\tInvalidConfig: \"PLATFORM_INVALID_CONFIG\",\n\tEnvNotInjected: \"PLATFORM_ENV_NOT_INJECTED\",\n\tMissingContext: \"PLATFORM_MISSING_CONTEXT\",\n\tPushConflict: \"PLATFORM_PUSH_CONFLICT\",\n\tPushAborted: \"PLATFORM_PUSH_ABORTED\",\n\tConfigLoadFailed: \"PLATFORM_CONFIG_LOAD_FAILED\",\n\tMissingApiKey: \"PLATFORM_MISSING_API_KEY\",\n\tAmbiguousBranchAuth: \"PLATFORM_AMBIGUOUS_BRANCH_AUTH\",\n\tBranchNotFound: \"PLATFORM_BRANCH_NOT_FOUND\",\n\tMissingParentBranch: \"PLATFORM_MISSING_PARENT_BRANCH\",\n\tUnauthorized: \"PLATFORM_UNAUTHORIZED\",\n\tForbidden: \"PLATFORM_FORBIDDEN\",\n\tNotFound: \"PLATFORM_NOT_FOUND\",\n\tConflict: \"PLATFORM_CONFLICT\",\n\tRateLimited: \"PLATFORM_RATE_LIMITED\",\n\tLocked: \"PLATFORM_LOCKED\",\n\tServerError: \"PLATFORM_SERVER_ERROR\",\n\tNetworkError: \"PLATFORM_NETWORK_ERROR\",\n\tInternalError: \"PLATFORM_INTERNAL_ERROR\",\n} as const;\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nconst ISSUE_URL = \"https://github.com/neondatabase/neon-pkgs/issues/new\";\n\n/**\n * Base class for all errors thrown by `@neondatabase/config`. Always extend this so callers\n * can catch every package-thrown error with a single `instanceof` check.\n *\n * Optional `details` carries structured context that the CLI prints under `--debug` and\n * that programmatic consumers can read (e.g. `details.status` for HTTP wrappings,\n * `details.requestId` for Neon API failures).\n */\nexport class PlatformError extends Error {\n\toverride readonly name: string = \"PlatformError\";\n\treadonly code: string;\n\treadonly details: Readonly<Record<string, unknown>>;\n\n\tconstructor(\n\t\tcode: string,\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; details?: Record<string, unknown> },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.code = code;\n\t\tthis.details = Object.freeze({ ...(options?.details ?? {}) });\n\t}\n}\n\n/**\n * Append a \"report-a-bug\" footer to an error message. Used only on truly unreachable\n * internal errors — never on user-facing validation / configuration errors where the user\n * is supposed to fix something on their end.\n */\nexport function bugReportFooter(): string {\n\treturn `\\nThis indicates a bug in @neondatabase/config. Please file an issue: ${ISSUE_URL}`;\n}\n\n/**\n * Thrown by {@link defineConfig} when the user-provided configuration object is invalid.\n *\n * The class collects every validation failure rather than throwing on the first one so that\n * users get a complete picture of what is wrong with their `neon.ts`.\n */\nexport class ConfigValidationError extends PlatformError {\n\toverride readonly name = \"ConfigValidationError\";\n\treadonly issues: readonly string[];\n\n\tconstructor(issues: readonly string[]) {\n\t\tsuper(\n\t\t\t\"PLATFORM_INVALID_CONFIG\",\n\t\t\t`Invalid Neon platform config:\\n - ${issues.join(\"\\n - \")}`,\n\t\t);\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * Thrown when the package cannot resolve which Neon project to operate on.\n *\n * Per the package's read-only-filesystem contract, we never create a `.neon` context file;\n * callers must either pass `projectId`/`orgId` explicitly or rely on an existing context file\n * (`.neon/project.json` or neonctl's `.neon`).\n */\nexport class MissingContextError extends PlatformError {\n\toverride readonly name = \"MissingContextError\";\n\n\tconstructor(message: string) {\n\t\tsuper(\"PLATFORM_MISSING_CONTEXT\", message);\n\t}\n}\n\n/**\n * Thrown by {@link pushConfig} when it detects differences between the local config and\n * the remote project that the caller hasn't opted in to apply.\n *\n * The message lists every conflict with both the current and desired value plus a\n * per-conflict hint. Mutable branch drift is applied by passing `updateExisting: true`.\n */\nexport class PushConflictError extends PlatformError {\n\toverride readonly name = \"PushConflictError\";\n\treadonly conflicts: readonly ConflictReport[];\n\n\tconstructor(conflicts: readonly ConflictReport[]) {\n\t\tconst lines: string[] = [\n\t\t\t\"pushConfig refused to apply: local config conflicts with remote state.\",\n\t\t\t\"\",\n\t\t];\n\t\tfor (const c of conflicts) {\n\t\t\tlines.push(\n\t\t\t\t` - [${c.kind}:${c.identifier}] ${c.field}: ${c.reason}`,\n\t\t\t\t` current : ${formatValue(c.current)}`,\n\t\t\t\t` desired : ${formatValue(c.desired)}`,\n\t\t\t\t` fix : ${suggestFix(c)}`,\n\t\t\t);\n\t\t}\n\t\tconst hasMutable = conflicts.some((c) => !isImmutableConflict(c));\n\t\tlines.push(\"\");\n\t\tif (hasMutable) {\n\t\t\tlines.push(\n\t\t\t\t\"For mutable conflicts, pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\",\n\t\t\t);\n\t\t}\n\n\t\tsuper(\"PLATFORM_PUSH_CONFLICT\", lines.join(\"\\n\"), {\n\t\t\tdetails: { conflicts: conflicts.map((c) => ({ ...c })) },\n\t\t});\n\t\tthis.conflicts = conflicts;\n\t}\n}\n\nfunction isImmutableConflict(_c: ConflictReport): boolean {\n\treturn false;\n}\n\nfunction suggestFix(c: ConflictReport): string {\n\tif (isImmutableConflict(c)) {\n\t\treturn \"immutable on Neon — recreate the project, or change your `neon.ts` to match the remote.\";\n\t}\n\tif (c.kind === \"branch\" && c.field === \"parent\") {\n\t\treturn \"create the parent branch on Neon first, or change the `parent` reference to an existing branch.\";\n\t}\n\treturn \"pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\";\n}\n\n/**\n * Thrown by {@link pushConfig} when the caller-supplied `confirm` callback declines a\n * push that requires confirmation (protected branch and/or mutable drift overriding\n * existing remote settings).\n *\n * The CLI maps this to a non-zero exit so users see \"aborted\" rather than a stack trace.\n */\nexport class PushAbortedError extends PlatformError {\n\toverride readonly name = \"PushAbortedError\";\n\treadonly branchName: string;\n\treadonly reasons: readonly (\"protected-branch\" | \"override-updates\")[];\n\n\tconstructor(\n\t\tbranchName: string,\n\t\treasons: readonly (\"protected-branch\" | \"override-updates\")[],\n\t) {\n\t\tsuper(\n\t\t\t\"PLATFORM_PUSH_ABORTED\",\n\t\t\t[\n\t\t\t\t`Aborted push to branch ${JSON.stringify(branchName)}.`,\n\t\t\t\treasons.length > 0\n\t\t\t\t\t? `Reason${reasons.length === 1 ? \"\" : \"s\"}: ${reasons.join(\", \")}.`\n\t\t\t\t\t: undefined,\n\t\t\t\t\"Re-run with `--update-existing` (override existing settings) or `--allow-protected-branch` (push to a protected branch) to skip the prompt.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\t{ details: { branchName, reasons: [...reasons] } },\n\t\t);\n\t\tthis.branchName = branchName;\n\t\tthis.reasons = reasons;\n\t}\n}\n\n/**\n * Thrown when the SDK fails to find or load a `neon.ts` config file.\n */\nexport class ConfigLoadError extends PlatformError {\n\toverride readonly name = \"ConfigLoadError\";\n\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(\"PLATFORM_CONFIG_LOAD_FAILED\", message, options);\n\t}\n}\n\nfunction formatValue(value: unknown): string {\n\tif (value === undefined) return \"<unset>\";\n\tif (typeof value === \"string\") return JSON.stringify(value);\n\tif (typeof value === \"object\" && value !== null)\n\t\treturn JSON.stringify(value);\n\treturn String(value);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/lib/errors.ts"],"sourcesContent":["import type { ConflictReport } from \"./types.js\";\n\n/**\n * Every code a {@link PlatformError} can carry. Stable identifiers — consumers can rely on\n * these for `instanceof PlatformError && err.code === ErrorCode.…` style checks instead of\n * matching on free-text messages.\n *\n * Grouped by source:\n * - `PLATFORM_INVALID_CONFIG` — `defineConfig` / `configSchema` rejected the input.\n * - `PLATFORM_MISSING_CONTEXT` — no project / branch context could be resolved.\n * - `PLATFORM_PUSH_CONFLICT` — local config conflicts with remote and the caller did not\n * opt in to apply.\n * - `PLATFORM_CONFIG_LOAD_FAILED` — `neon.ts` could not be found or evaluated.\n * - `PLATFORM_MISSING_API_KEY` — no `NEON_API_KEY` and no explicit `apiKey` was provided.\n * - `PLATFORM_MISSING_PARENT_BRANCH` — push tried to create a child of a non-existent\n * branch.\n * - `PLATFORM_UNAUTHORIZED` / `PLATFORM_FORBIDDEN` / `PLATFORM_NOT_FOUND` /\n * `PLATFORM_CONFLICT` / `PLATFORM_RATE_LIMITED` / `PLATFORM_LOCKED` /\n * `PLATFORM_SERVER_ERROR` — wrappings of Neon HTTP failures.\n * - `PLATFORM_NETWORK_ERROR` — transport-level failure (no HTTP response at all).\n * - `PLATFORM_INTERNAL_ERROR` — invariant violations. Should never happen in production;\n * if you see one, please open an issue.\n */\nexport const ErrorCode = {\n\tInvalidConfig: \"PLATFORM_INVALID_CONFIG\",\n\tEnvNotInjected: \"PLATFORM_ENV_NOT_INJECTED\",\n\tMissingContext: \"PLATFORM_MISSING_CONTEXT\",\n\tPushConflict: \"PLATFORM_PUSH_CONFLICT\",\n\tPushAborted: \"PLATFORM_PUSH_ABORTED\",\n\tConfigLoadFailed: \"PLATFORM_CONFIG_LOAD_FAILED\",\n\tMissingApiKey: \"PLATFORM_MISSING_API_KEY\",\n\tAmbiguousBranchAuth: \"PLATFORM_AMBIGUOUS_BRANCH_AUTH\",\n\tBranchNotFound: \"PLATFORM_BRANCH_NOT_FOUND\",\n\tFeatureUnavailable: \"PLATFORM_FEATURE_UNAVAILABLE\",\n\tMissingParentBranch: \"PLATFORM_MISSING_PARENT_BRANCH\",\n\tUnauthorized: \"PLATFORM_UNAUTHORIZED\",\n\tForbidden: \"PLATFORM_FORBIDDEN\",\n\tNotFound: \"PLATFORM_NOT_FOUND\",\n\tConflict: \"PLATFORM_CONFLICT\",\n\tRateLimited: \"PLATFORM_RATE_LIMITED\",\n\tLocked: \"PLATFORM_LOCKED\",\n\tServerError: \"PLATFORM_SERVER_ERROR\",\n\tNetworkError: \"PLATFORM_NETWORK_ERROR\",\n\tInternalError: \"PLATFORM_INTERNAL_ERROR\",\n} as const;\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nconst ISSUE_URL = \"https://github.com/neondatabase/neon-pkgs/issues/new\";\n\n/**\n * Base class for all errors thrown by `@neondatabase/config`. Always extend this so callers\n * can catch every package-thrown error with a single `instanceof` check.\n *\n * Optional `details` carries structured context that the CLI prints under `--debug` and\n * that programmatic consumers can read (e.g. `details.status` for HTTP wrappings,\n * `details.requestId` for Neon API failures).\n */\nexport class PlatformError extends Error {\n\toverride readonly name: string = \"PlatformError\";\n\treadonly code: string;\n\treadonly details: Readonly<Record<string, unknown>>;\n\n\tconstructor(\n\t\tcode: string,\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; details?: Record<string, unknown> },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.code = code;\n\t\tthis.details = Object.freeze({ ...(options?.details ?? {}) });\n\t}\n}\n\n/**\n * Append a \"report-a-bug\" footer to an error message. Used only on truly unreachable\n * internal errors — never on user-facing validation / configuration errors where the user\n * is supposed to fix something on their end.\n */\nexport function bugReportFooter(): string {\n\treturn `\\nThis indicates a bug in @neondatabase/config. Please file an issue: ${ISSUE_URL}`;\n}\n\n/**\n * Thrown by {@link defineConfig} when the user-provided configuration object is invalid.\n *\n * The class collects every validation failure rather than throwing on the first one so that\n * users get a complete picture of what is wrong with their `neon.ts`.\n */\nexport class ConfigValidationError extends PlatformError {\n\toverride readonly name = \"ConfigValidationError\";\n\treadonly issues: readonly string[];\n\n\tconstructor(issues: readonly string[]) {\n\t\tsuper(\n\t\t\t\"PLATFORM_INVALID_CONFIG\",\n\t\t\t`Invalid Neon platform config:\\n - ${issues.join(\"\\n - \")}`,\n\t\t);\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * Thrown when the package cannot resolve which Neon project to operate on.\n *\n * Per the package's read-only-filesystem contract, we never create a `.neon` context file;\n * callers must either pass `projectId`/`orgId` explicitly or rely on an existing context file\n * (`.neon/project.json` or neonctl's `.neon`).\n */\nexport class MissingContextError extends PlatformError {\n\toverride readonly name = \"MissingContextError\";\n\n\tconstructor(message: string) {\n\t\tsuper(\"PLATFORM_MISSING_CONTEXT\", message);\n\t}\n}\n\n/**\n * Thrown by {@link pushConfig} when it detects differences between the local config and\n * the remote project that the caller hasn't opted in to apply.\n *\n * The message lists every conflict with both the current and desired value plus a\n * per-conflict hint. Mutable branch drift is applied by passing `updateExisting: true`.\n */\nexport class PushConflictError extends PlatformError {\n\toverride readonly name = \"PushConflictError\";\n\treadonly conflicts: readonly ConflictReport[];\n\n\tconstructor(conflicts: readonly ConflictReport[]) {\n\t\tconst lines: string[] = [\n\t\t\t\"pushConfig refused to apply: local config conflicts with remote state.\",\n\t\t\t\"\",\n\t\t];\n\t\tfor (const c of conflicts) {\n\t\t\tlines.push(\n\t\t\t\t` - [${c.kind}:${c.identifier}] ${c.field}: ${c.reason}`,\n\t\t\t\t` current : ${formatValue(c.current)}`,\n\t\t\t\t` desired : ${formatValue(c.desired)}`,\n\t\t\t\t` fix : ${suggestFix(c)}`,\n\t\t\t);\n\t\t}\n\t\tconst hasMutable = conflicts.some((c) => !isImmutableConflict(c));\n\t\tlines.push(\"\");\n\t\tif (hasMutable) {\n\t\t\tlines.push(\n\t\t\t\t\"For mutable conflicts, pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\",\n\t\t\t);\n\t\t}\n\n\t\tsuper(\"PLATFORM_PUSH_CONFLICT\", lines.join(\"\\n\"), {\n\t\t\tdetails: { conflicts: conflicts.map((c) => ({ ...c })) },\n\t\t});\n\t\tthis.conflicts = conflicts;\n\t}\n}\n\nfunction isImmutableConflict(_c: ConflictReport): boolean {\n\treturn false;\n}\n\nfunction suggestFix(c: ConflictReport): string {\n\tif (isImmutableConflict(c)) {\n\t\treturn \"immutable on Neon — recreate the project, or change your `neon.ts` to match the remote.\";\n\t}\n\tif (c.kind === \"branch\" && c.field === \"parent\") {\n\t\treturn \"create the parent branch on Neon first, or change the `parent` reference to an existing branch.\";\n\t}\n\treturn \"pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\";\n}\n\n/**\n * Thrown by {@link pushConfig} when the caller-supplied `confirm` callback declines a\n * push that requires confirmation (protected branch and/or mutable drift overriding\n * existing remote settings).\n *\n * The CLI maps this to a non-zero exit so users see \"aborted\" rather than a stack trace.\n */\nexport class PushAbortedError extends PlatformError {\n\toverride readonly name = \"PushAbortedError\";\n\treadonly branchName: string;\n\treadonly reasons: readonly (\"protected-branch\" | \"override-updates\")[];\n\n\tconstructor(\n\t\tbranchName: string,\n\t\treasons: readonly (\"protected-branch\" | \"override-updates\")[],\n\t) {\n\t\tsuper(\n\t\t\t\"PLATFORM_PUSH_ABORTED\",\n\t\t\t[\n\t\t\t\t`Aborted push to branch ${JSON.stringify(branchName)}.`,\n\t\t\t\treasons.length > 0\n\t\t\t\t\t? `Reason${reasons.length === 1 ? \"\" : \"s\"}: ${reasons.join(\", \")}.`\n\t\t\t\t\t: undefined,\n\t\t\t\t\"Re-run with `--update-existing` (override existing settings) or `--allow-protected-branch` (push to a protected branch) to skip the prompt.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\t{ details: { branchName, reasons: [...reasons] } },\n\t\t);\n\t\tthis.branchName = branchName;\n\t\tthis.reasons = reasons;\n\t}\n}\n\n/**\n * Thrown when the SDK fails to find or load a `neon.ts` config file.\n */\nexport class ConfigLoadError extends PlatformError {\n\toverride readonly name = \"ConfigLoadError\";\n\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(\"PLATFORM_CONFIG_LOAD_FAILED\", message, options);\n\t}\n}\n\nfunction formatValue(value: unknown): string {\n\tif (value === undefined) return \"<unset>\";\n\tif (typeof value === \"string\") return JSON.stringify(value);\n\tif (typeof value === \"object\" && value !== null)\n\t\treturn JSON.stringify(value);\n\treturn String(value);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NeonApi } from "./neon-api.js";
|
|
1
|
+
import { DeployFunctionInput, NeonApi } from "./neon-api.js";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/neon-api-real.d.ts
|
|
4
4
|
interface CreateNeonAuthRestInput {
|
|
@@ -37,9 +37,50 @@ interface RetryConfig {
|
|
|
37
37
|
* Exported only for tests; production callers go through the wrapped {@link NeonApi}.
|
|
38
38
|
*/
|
|
39
39
|
declare function retryOnLocked<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* Whether an error from a Preview-feature read means the feature simply isn't available
|
|
42
|
+
* for this project/branch/region (as opposed to a real, transient failure). Neon signals
|
|
43
|
+
* this a few ways: a 404 "this route does not exist" (the route isn't deployed at all), or
|
|
44
|
+
* a 503/4xx whose message says the platform feature is "not available" / "not enabled".
|
|
45
|
+
*
|
|
46
|
+
* Callers do **not** swallow this into an empty result — touching a Preview feature that
|
|
47
|
+
* isn't available is surfaced as a {@link previewUnavailableError} so `plan` / `status` /
|
|
48
|
+
* `pull` (and `neon dev`) fail clearly instead of, say, planning to create resources the
|
|
49
|
+
* API will refuse to create.
|
|
50
|
+
*/
|
|
51
|
+
declare function isPreviewFeatureUnavailable(err: unknown): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Convert a Preview-feature error into a clear {@link PlatformError} when the feature is
|
|
54
|
+
* unavailable for the project; otherwise pass the original error through unchanged so a
|
|
55
|
+
* genuine failure (auth, transient 5xx, …) keeps its specific code and message.
|
|
56
|
+
*/
|
|
57
|
+
declare function previewUnavailableError(err: unknown, featureLabel: string): unknown;
|
|
40
58
|
declare function createNeonAuthRestInput(input: {
|
|
41
59
|
databaseName?: string;
|
|
42
60
|
}): CreateNeonAuthRestInput;
|
|
61
|
+
/**
|
|
62
|
+
* Build the `multipart/form-data` body for a function deployment, matching the public
|
|
63
|
+
* `FunctionDeployRequest` schema (`POST .../functions/{slug}/deployments`):
|
|
64
|
+
*
|
|
65
|
+
* - `zip` — the bundle as a binary part (named `bundle.zip`).
|
|
66
|
+
* - `runtime` — the function runtime.
|
|
67
|
+
* - `environment` — a single JSON-encoded string→string map (multipart can't carry a typed
|
|
68
|
+
* object part), omitted entirely when there are no env vars.
|
|
69
|
+
*
|
|
70
|
+
* Pure (no I/O) so it can be unit-tested against the spec without stubbing `fetch`.
|
|
71
|
+
*/
|
|
72
|
+
declare function buildFunctionDeployForm(input: DeployFunctionInput): FormData;
|
|
73
|
+
/**
|
|
74
|
+
* Read a response body as JSON, tolerating non-JSON. Some Neon routes return a plain-text
|
|
75
|
+
* body (e.g. a 404 `"this route does not exist"` for a Preview feature not enabled in the
|
|
76
|
+
* project/region). Parsing that with `JSON.parse` used to throw a cryptic
|
|
77
|
+
* `SyntaxError: Unexpected token …`, which — because parsing happens before the `res.ok`
|
|
78
|
+
* check in {@link request} — masked the real HTTP status. We instead return the raw text
|
|
79
|
+
* wrapped as `{ message }` so the status-based error path in `request` / `wrapNeonError`
|
|
80
|
+
* runs and produces a proper {@link PlatformError} (e.g. `NotFound`), and a non-error body
|
|
81
|
+
* that simply isn't JSON degrades to text rather than crashing.
|
|
82
|
+
*/
|
|
83
|
+
declare function readJsonBody(res: Response): Promise<unknown>;
|
|
43
84
|
//#endregion
|
|
44
|
-
export { createNeonAuthRestInput, createRealNeonApi, retryOnLocked };
|
|
85
|
+
export { buildFunctionDeployForm, createNeonAuthRestInput, createRealNeonApi, isPreviewFeatureUnavailable, previewUnavailableError, readJsonBody, retryOnLocked };
|
|
45
86
|
//# sourceMappingURL=neon-api-real.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;UAoFU,uBAAA;;EAAA,aAAA,CAAA,EAAA,MAAA;AAeV;AAyCC;AAeD;;;;AAES,iBA1DO,iBAAA,CA0DP,OAAA,EAAA;QACE,EAAA,MAAA;SAAR,CAAA,EAAA,MAAA;EAAO;AAwzBV
|
|
1
|
+
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;UAoFU,uBAAA;;EAAA,aAAA,CAAA,EAAA,MAAA;AAeV;AAyCC;AAeD;;;;AAES,iBA1DO,iBAAA,CA0DP,OAAA,EAAA;QACE,EAAA,MAAA;SAAR,CAAA,EAAA,MAAA;EAAO;AAwzBV;AAsBA;AAmCA;AAoBA;EAAuC,aAAA,CAAA,EAAA;IAAQ,WAAA,CAAA,EAAA,MAAA;IAAsB,cAAA,CAAA,EAAA,MAAA;IAAQ,UAAA,CAAA,EAAA,MAAA;EAwBvD,CAAA;CAAY,CAAA,EA38B9B,OA28B8B;UA76BxB,WAAA,CA66B8B;aAAW,EAAA,MAAA;EAAO,cAAA,EAAA,MAAA;;;;;;;;;;iBAh6BpC,2BACX,QAAQ,YACV,cACN,QAAQ;;;;;;;;;;;;iBAwzBK,2BAAA;;;;;;iBAsBA,uBAAA;iBAmCA,uBAAA;;IAEZ;;;;;;;;;;;;iBAkBY,uBAAA,QAA+B,sBAAsB;;;;;;;;;;;iBAwB/C,YAAA,MAAkB,WAAW"}
|