@neondatabase/config 1.6.0 → 1.7.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 +4 -4
- package/dist/index.d.ts +3 -3
- package/dist/lib/define-config.d.ts +3 -2
- package/dist/lib/define-config.d.ts.map +1 -1
- package/dist/lib/define-config.js +23 -6
- package/dist/lib/define-config.js.map +1 -1
- package/dist/lib/diff.d.ts +3 -3
- package/dist/lib/diff.d.ts.map +1 -1
- package/dist/lib/diff.js +41 -23
- package/dist/lib/diff.js.map +1 -1
- 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.map +1 -1
- package/dist/lib/neon-api-real.js +73 -19
- package/dist/lib/neon-api-real.js.map +1 -1
- package/dist/lib/neon-api.d.ts +34 -7
- package/dist/lib/neon-api.d.ts.map +1 -1
- package/dist/lib/schema.d.ts +17 -38
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +43 -42
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +50 -13
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/v1.d.ts +17 -38
- package/dist/v1.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,12 +26,12 @@ export default defineConfig({
|
|
|
26
26
|
hello: {
|
|
27
27
|
name: "Hello",
|
|
28
28
|
source: "./functions/hello.ts",
|
|
29
|
-
triggers: [
|
|
30
|
-
{ type: "schedule", name: "hourly", cron: "0 * * * *" },
|
|
31
|
-
],
|
|
32
29
|
customDomains: ["docs.example.com"], // default branch only
|
|
33
30
|
},
|
|
34
31
|
},
|
|
32
|
+
triggers: {
|
|
33
|
+
hourly: { type: "schedule", function: "hello", cron: "0 * * * *" },
|
|
34
|
+
},
|
|
35
35
|
// Dynamic: per-branch tuning only. Cannot add/remove services or functions.
|
|
36
36
|
branch: (branch) => ({
|
|
37
37
|
protected: branch.name === "main",
|
|
@@ -42,7 +42,7 @@ export default defineConfig({
|
|
|
42
42
|
|
|
43
43
|
A policy is split into a **static** existential set and a **dynamic** `branch` closure:
|
|
44
44
|
|
|
45
|
-
- **Static top-level** — `auth` / `dataApi` / `aiGateway` / `functions` (keyed by slug) / `buckets` (keyed by name).
|
|
45
|
+
- **Static top-level** — `auth` / `dataApi` / `aiGateway` / `functions` (keyed by slug) / `buckets` (keyed by name) / `triggers` (keyed by name; each value names the function, and for `storage_object_created` the bucket, it binds). `aiGateway`, `functions`, and `buckets` still work under deprecated `preview`; `neon config apply` / `deploy` warn that those keys can be lifted. Because this is static, the secret set is known at the type level, so `parseEnv` / `fetchEnv` from `@neon/env` return an exact `NeonEnv`. Function source, env, and triggers apply on every branch. `customDomains` is default-branch-only ([details](#default-branch-only-fields)).
|
|
46
46
|
- **`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`, per-function `runtime` (`functions` on the returned object, or deprecated `preview.functions`), and per-function `customDomains`. 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.
|
|
47
47
|
|
|
48
48
|
Service toggles accept `true` / `{}` / `{ enabled: true }` (enabled) and `false` / `{ enabled: false }` (disabled). Function slugs (record keys) must match `^[a-z0-9]{1,20}$`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, ExternalPackageDef, ExternalPackageEntry, FunctionBundle, FunctionBundler, FunctionBundlerInput, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionScheduleTriggerDef, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedFunctionScheduleTrigger, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput } from "./lib/types.js";
|
|
1
|
+
import { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConflictReport, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DurationString, DurationUnit, ExternalPackageDef, ExternalPackageEntry, FunctionBundle, FunctionBundler, FunctionBundlerInput, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionScheduleTriggerDef, FunctionStorageObjectCreatedTriggerDef, FunctionTriggerDef, FunctionTuning, PostgresConfig, PreviewInput, PreviewTuning, PushResult, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedFunctionScheduleTrigger, ResolvedFunctionStorageObjectCreatedTrigger, ResolvedFunctionTrigger, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput } from "./lib/types.js";
|
|
2
2
|
import { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PartialBranchCreateError, PlatformError, PushAbortedError, PushConflictError, isPartialBranchCreateError, isPlatformError } from "./lib/errors.js";
|
|
3
|
-
import { CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CreateTriggerInput, DeployFunctionInput, EnableDataApiInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialReveal, NeonCredentialSecret, NeonCustomDomainSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, NeonTriggerSnapshot, UpdateBranchInput, UpdateTriggerInput } from "./lib/neon-api.js";
|
|
3
|
+
import { CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CreateTriggerInput, DeployFunctionInput, EnableDataApiInput, GetConnectionUriInput, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialReveal, NeonCredentialSecret, NeonCustomDomainSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, NeonScheduleTriggerSnapshot, NeonStorageObjectCreatedTriggerSnapshot, NeonTriggerSnapshot, UpdateBranchInput, UpdateTriggerInput } from "./lib/neon-api.js";
|
|
4
4
|
import { createNeonApiFromOptions } from "./lib/auth.js";
|
|
5
5
|
import { authoredAiGateway, authoredBuckets, authoredFunctions, deprecatedPreviewAuthoring, previewGaWarningForConfig, previewGaWarningMessage } from "./lib/authored-services.js";
|
|
6
6
|
import { CredentialFeatureFlags, credentialScopesSatisfied, deriveCredentialScopes } from "./lib/credentials.js";
|
|
@@ -11,4 +11,4 @@ import { FUNCTION_ARCHIVE_ENTRIES, FUNCTION_SOURCE_ENTRIES, FunctionArchiveEntry
|
|
|
11
11
|
import { LoadConfigOptions, loadConfigFromFile } from "./lib/loader.js";
|
|
12
12
|
import { createRealNeonApi } from "./lib/neon-api-real.js";
|
|
13
13
|
import { errors, schemas } from "./v1.js";
|
|
14
|
-
export { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CreateTriggerInput, CredentialFeatureFlags, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DeployFunctionInput, DiffOptions, DiffResult, DurationString, DurationUnit, EnableDataApiInput, ErrorCode, ExternalPackageDef, ExternalPackageEntry, FUNCTION_ARCHIVE_ENTRIES, FUNCTION_SOURCE_ENTRIES, FunctionArchiveEntry, FunctionBundle, FunctionBundler, FunctionBundlerInput, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionScheduleTriggerDef, FunctionSourceEntry, FunctionTuning, GetConnectionUriInput, LoadConfigOptions, MissingContextError, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialReveal, NeonCredentialSecret, NeonCustomDomainSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, NeonTriggerSnapshot, PartialBranchCreateError, PlanStep, PlatformError, PostgresConfig, PreviewInput, PreviewTuning, PushAbortedError, PushConflictError, PushResult, RemotePreviewState, RemoteServiceState, RemoteState, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedFunctionScheduleTrigger, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput, UpdateBranchInput, UpdateTriggerInput, authoredAiGateway, authoredBuckets, authoredFunctions, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deprecatedPreviewAuthoring, deriveCredentialScopes, diffConfig, errors, externalPackageRoot, isFunctionArchiveEntry, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, packagesToStage, pickFunctionSourceEntry, previewGaWarningForConfig, previewGaWarningMessage, resolveConfig, schemas };
|
|
14
|
+
export { AppliedChange, BranchTarget, BranchTuning, BranchTuningFn, BucketAccessLevel, BucketDef, ComputeSettings, ComputeUnit, Config, ConfigLoadError, ConfigValidationError, ConflictReport, CreateBranchInput, CreateBucketInput, CreateCredentialInput, CreateProjectInput, CreateTriggerInput, CredentialFeatureFlags, CredentialPrincipalType, CredentialScope, DATA_API_AUTH_PROVIDERS, DataApiAuthProvider, DataApiConfig, DataApiExternalAuthConfig, DataApiInput, DataApiNeonAuthConfig, DataApiSettings, DeployFunctionInput, DiffOptions, DiffResult, DurationString, DurationUnit, EnableDataApiInput, ErrorCode, ExternalPackageDef, ExternalPackageEntry, FUNCTION_ARCHIVE_ENTRIES, FUNCTION_SOURCE_ENTRIES, FunctionArchiveEntry, FunctionBundle, FunctionBundler, FunctionBundlerInput, FunctionDef, FunctionDevConfig, FunctionRuntime, FunctionScheduleTriggerDef, FunctionSourceEntry, FunctionStorageObjectCreatedTriggerDef, FunctionTriggerDef, FunctionTuning, GetConnectionUriInput, LoadConfigOptions, MissingContextError, NeonApi, NeonAuthSnapshot, NeonBranchSnapshot, NeonBranchStorageSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonCredentialReveal, NeonCredentialSecret, NeonCustomDomainSnapshot, NeonDataApiSnapshot, NeonDatabaseSnapshot, NeonEndpointSnapshot, NeonFunctionDeploymentSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRoleSnapshot, NeonScheduleTriggerSnapshot, NeonStorageObjectCreatedTriggerSnapshot, NeonTriggerSnapshot, PartialBranchCreateError, PlanStep, PlatformError, PostgresConfig, PreviewInput, PreviewTuning, PushAbortedError, PushConflictError, PushResult, RemotePreviewState, RemoteServiceState, RemoteState, ResolvedBranchConfig, ResolvedBucketConfig, ResolvedDataApiConfig, ResolvedExternalPackage, ResolvedFunctionConfig, ResolvedFunctionScheduleTrigger, ResolvedFunctionStorageObjectCreatedTrigger, ResolvedFunctionTrigger, ResolvedPreviewConfig, ServiceEnabled, ServiceToggle, ServiceToggleInput, UpdateBranchInput, UpdateTriggerInput, authoredAiGateway, authoredBuckets, authoredFunctions, createNeonApiFromOptions, createRealNeonApi, credentialScopesSatisfied, defineConfig, deprecatedPreviewAuthoring, deriveCredentialScopes, diffConfig, errors, externalPackageRoot, isFunctionArchiveEntry, isPartialBranchCreateError, isPlatformError, loadConfigFromFile, packagesToStage, pickFunctionSourceEntry, previewGaWarningForConfig, previewGaWarningMessage, resolveConfig, schemas };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BranchTarget, BranchTuningFn, BucketDef, Config, DataApiInput, FunctionDef, PreviewInput, ResolvedBranchConfig, ServiceEnabled, ServiceToggleInput } from "./types.js";
|
|
1
|
+
import { BranchTarget, BranchTuningFn, BucketDef, Config, DataApiInput, FunctionDef, FunctionTriggerDef, PreviewInput, ResolvedBranchConfig, ServiceEnabled, ServiceToggleInput } from "./types.js";
|
|
2
2
|
|
|
3
3
|
//#region src/lib/define-config.d.ts
|
|
4
4
|
|
|
@@ -84,7 +84,7 @@ type BucketsAutocomplete<Buckets> = Buckets extends Record<string, unknown> ? {
|
|
|
84
84
|
* ```
|
|
85
85
|
*
|
|
86
86
|
* The policy is split into a **static** existential set (top-level `auth` / `dataApi` /
|
|
87
|
-
* `aiGateway` / `functions` / `buckets`, plus the deprecated `preview` aliases) and a
|
|
87
|
+
* `aiGateway` / `functions` / `buckets` / `triggers`, plus the deprecated `preview` aliases) and a
|
|
88
88
|
* **dynamic** per-branch `branch` closure. The static half determines which secrets exist —
|
|
89
89
|
* so `NeonEnv<typeof config>` and `parseEnv` are exact — while the closure can only *tune*
|
|
90
90
|
* a branch (lifecycle, compute, per-function deploy settings), never change what exists.
|
|
@@ -104,6 +104,7 @@ declare function defineConfig<const Auth extends ServiceToggleInput | undefined
|
|
|
104
104
|
aiGateway?: AiGateway & ServiceToggleInput;
|
|
105
105
|
functions?: Functions & Record<string, FunctionDef> & FunctionsAutocomplete<Functions>;
|
|
106
106
|
buckets?: Buckets & Record<string, BucketDef> & BucketsAutocomplete<Buckets>;
|
|
107
|
+
triggers?: Record<string, FunctionTriggerDef>;
|
|
107
108
|
/** @deprecated Use top-level `aiGateway`, `functions`, and `buckets`. */
|
|
108
109
|
preview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;
|
|
109
110
|
branch?: BranchTuningFn<Preview, Functions>;
|
|
@@ -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":";;;;;;AAgCoB;AAcI;AACR;AAAf,KADI,mBACJ,CAAA,OAAA,CAAA,GAAA,cAAA,CAAe,OAAf,CAAA,SAAA,IAAA,GAAA,CACI,OADJ,CAAA,SAAA,CAAA;EACI,YAAA,EAAA,UAAA;AAAO,CAAA,CAAA,GAAA,KAAA,GAAA,IAAA,GAAA,KAAA;AAiBZ;AAqBA;AAAwB;AACH;AAApB;AACkB;AAAf;AACC;AAAU;AACV;AACD,KA1BQ,oBAAA,GA0BR,+QAAA;AAAU;AAAY;AAAC;AAoBH;AAAa;AACJ;AAAI;AAEnC;AAC8B;AAAI;AAAS;AAAA;AAGnB;AACzB;AAAkB;AACE;AAAY,KAlCrB,YAkCqB,CAAA,IAAA,EAAA,OAAA,CAAA,GAjChC,mBAiCgC,CAjCZ,OAiCY,CAAA,SAAA,IAAA,GAhC7B,cAgC6B,CAhCd,IAgCc,CAAA,SAAA,IAAA,GA/B5B,OA+B4B,GA/BlB,YA+BkB,GA9B5B,oBA8B4B,GA7B7B,OA6B6B,GA7BnB,YA6BmB;AAAW;AAAA;AAGpB;AACvB;AAAgB;AACI;AAAU;AAAS;AAkCxC;AAA4B;AACR;AACG;AACA;AACiB;AAAf;AACa;AAAf;AACE;AAQjB,KA9DH,mBA8DG,CAAA,OAAA,CAAA,GAAA,CA9D6B,OA8D7B,SAAA;EAAO,SAAA,EAAA,KAAA,EAAA;AAIS,CAAA,GAAA;EAAM,SAAA,EAAA,WAAnB,MAjEsB,CAiEtB,GAjE0B,WAiE1B,EACE;AAAY,CAAA,GAAA,OAAA,CAAA,GAAA,CAhEvB,OAgEuB,SAAA;EACZ,OAAA,EAAA,KAAA,EAAA;AACI,CAAA,GAAA;EAAf,OAAA,EAAA,WACsB,MAlEQ,CAkER,GAlEY,SAkEZ,EAAtB;AACS,CAAA,GAAA,OAAA,CAAA;AACM,KAjEZ,qBAiEY,CAAA,SAAA,CAAA,GAhEhB,SAgEgB,SAhEE,MAgEF,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,WAAf,MA/DmB,SA+DnB,GA/D+B,WA+D/B,EACoB,GAAA,OAAA;AAApB,KA7DG,mBA6DH,CAAA,OAAA,CAAA,GA5DD,OA4DC,SA5De,MA4Df,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,WACyB,MA5DN,OA4DM,GA5DI,SA4DJ,EAAf,GAAA,OAAA;AAKD;AAAU;AAAmC;AAApB;AACX;AAAS;AAAxB;AACC;AAAM;AAAS;AAAS;AAAW;AAAS;AAAnD;AAAM;AAmCV;AAA6B;AACpB;AACA;AACN;AAAoB;AAuPvB;;;;;;;;;;iBA9TgB,gCACI,kEACG,4DACA,8DACE,eAAe,4DACjB,eAAe,4DACb;SAQjB,OAAO;YAIJ,aAAa,MAAM;cACjB,YAAY;cACZ,YACX,eAAe,eACf,sBAAsB;YACb,UACT,eAAe,aACf,oBAAoB;aACV,eAAe;;YAKhB,UAAU,eAAe,oBAAoB;WAC9C,eAAe,SAAS;IAC9B,OAAO,MAAM,SAAS,SAAS,WAAW,SAAS;;;;;;;;iBAmCvC,aAAA,SACP,gBACA,eACN;;;;;;iBAuPa,eAAA"}
|
|
@@ -26,7 +26,7 @@ const REGION_PREFIX = /^(aws|azure|gcp)-/;
|
|
|
26
26
|
* ```
|
|
27
27
|
*
|
|
28
28
|
* The policy is split into a **static** existential set (top-level `auth` / `dataApi` /
|
|
29
|
-
* `aiGateway` / `functions` / `buckets`, plus the deprecated `preview` aliases) and a
|
|
29
|
+
* `aiGateway` / `functions` / `buckets` / `triggers`, plus the deprecated `preview` aliases) and a
|
|
30
30
|
* **dynamic** per-branch `branch` closure. The static half determines which secrets exist —
|
|
31
31
|
* so `NeonEnv<typeof config>` and `parseEnv` are exact — while the closure can only *tune*
|
|
32
32
|
* a branch (lifecycle, compute, per-function deploy settings), never change what exists.
|
|
@@ -72,6 +72,8 @@ function resolveConfig(config, branch) {
|
|
|
72
72
|
if (tuning.postgres?.computeSettings) resolved.postgres = { computeSettings: { ...tuning.postgres.computeSettings } };
|
|
73
73
|
const preview = resolvePreviewConfig(mergeAuthoredPreview(config), tuning, branch);
|
|
74
74
|
if (preview) resolved.preview = preview;
|
|
75
|
+
const triggers = resolveTriggers(config.triggers);
|
|
76
|
+
if (triggers.length > 0) resolved.triggers = triggers;
|
|
75
77
|
return resolved;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
@@ -159,15 +161,30 @@ function resolveFunctionConfig(slug, def, tuning, branch) {
|
|
|
159
161
|
bundler: def.bundler ?? DEFAULT_FUNCTION_BUNDLER,
|
|
160
162
|
...def.externalPackages ? { externalPackages: def.externalPackages.map(normalizeExternalPackage) } : {},
|
|
161
163
|
...def.dev ? { dev: def.dev } : {},
|
|
162
|
-
...
|
|
164
|
+
...customDomains !== void 0 ? { customDomains } : {}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function resolveTriggers(triggers) {
|
|
168
|
+
if (!triggers) return [];
|
|
169
|
+
return Object.entries(triggers).map(([name, trigger]) => {
|
|
170
|
+
if (trigger.type === "schedule") return {
|
|
163
171
|
type: "schedule",
|
|
164
|
-
name
|
|
172
|
+
name,
|
|
173
|
+
functionSlug: trigger.function,
|
|
165
174
|
cron: trigger.cron,
|
|
166
175
|
functionPath: trigger.functionPath ?? "/",
|
|
167
176
|
enabled: trigger.enabled ?? true
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
|
|
177
|
+
};
|
|
178
|
+
return {
|
|
179
|
+
type: "storage_object_created",
|
|
180
|
+
name,
|
|
181
|
+
functionSlug: trigger.function,
|
|
182
|
+
bucketName: trigger.bucket,
|
|
183
|
+
...trigger.prefix !== void 0 ? { prefix: trigger.prefix } : {},
|
|
184
|
+
functionPath: trigger.functionPath ?? "/",
|
|
185
|
+
enabled: trigger.enabled ?? true
|
|
186
|
+
};
|
|
187
|
+
});
|
|
171
188
|
}
|
|
172
189
|
function resolveFunctionCustomDomains(def, tuning, branch) {
|
|
173
190
|
if (tuning.customDomains !== void 0) return tuning.customDomains.map(normalizeCustomDomain);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import {\n\tauthoredFunctionTuning,\n\tmergeAuthoredPreview,\n} from \"./authored-services.js\";\nimport { normalizeCustomDomain } from \"./custom-domain.js\";\nimport { parseBranchTtl } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport { normalizeExternalPackage } from \"./external-packages.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tBucketDef,\n\tConfig,\n\tDataApiInput,\n\tDataApiSettings,\n\tFunctionDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedPreviewConfig,\n\tServiceEnabled,\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 DEFAULT_FUNCTION_BUNDLER = \"esbuild\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is\n * on (see {@link ServiceEnabled}) and not the explicit `authProvider: \"external\"` variant\n * (so the default / `\"neon\"` provider). This is the case that requires top-level Neon Auth.\n */\ntype DataApiUsesNeonAuth<DataApi> =\n\tServiceEnabled<DataApi> extends true\n\t\t? [DataApi] extends [{ authProvider: \"external\" }]\n\t\t\t? false\n\t\t\t: true\n\t\t: false;\n\n/**\n * Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data\n * API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the\n * offending value against this string literal — `Type 'true' is not assignable to type\n * '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque\n * `Type 'true' is not assignable to type 'never'` an intersection guard produces.\n *\n * It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API\n * *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type NeonAuthRequiredHint =\n\t\"`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`\";\n\n/**\n * Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`\n * field** rather than an intersected requirement on `auth`.\n *\n * - A Neon-Auth Data API (`authProvider: \"neon\"`, the default) with top-level `auth` enabled,\n * or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the\n * `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the\n * returned {@link Config}).\n * - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to\n * the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)\n * right on the `dataApi` value.\n *\n * The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for\n * non-typed (plain-JS) callers, so the behavior is identical — only the type-level message\n * changes.\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type DataApiField<Auth, DataApi> =\n\tDataApiUsesNeonAuth<DataApi> extends true\n\t\t? ServiceEnabled<Auth> extends true\n\t\t\t? DataApi & DataApiInput\n\t\t\t: NeonAuthRequiredHint\n\t\t: DataApi & DataApiInput;\n\n/**\n * Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.\n *\n * {@link PreviewInput} types those records with a string index signature\n * (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers\n * `const Preview`, every authored slug becomes a **named** property on the inferred literal\n * (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature\n * when the editor computes the contextual type of that slug's value — so the rest of\n * {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as\n * completions inside `hello: { … }` / `uploads: { … }`.\n *\n * Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via\n * a mapped type over the already-inferred keys) puts those members back onto the contextual\n * type without going through an index signature, which restores autocomplete. Intersected with\n * `Preview & PreviewInput` it neither widens what is accepted (the values were already\n * `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for\n * `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.\n */\ntype PreviewAutocomplete<Preview> = (Preview extends { functions: infer F }\n\t? { functions: { [Slug in keyof F]: FunctionDef } }\n\t: unknown) &\n\t(Preview extends { buckets: infer B }\n\t\t? { buckets: { [Name in keyof B]: BucketDef } }\n\t\t: unknown);\n\ntype FunctionsAutocomplete<Functions> =\n\tFunctions extends Record<string, unknown>\n\t\t? { [Slug in keyof Functions]: FunctionDef }\n\t\t: unknown;\n\ntype BucketsAutocomplete<Buckets> =\n\tBuckets extends Record<string, unknown>\n\t\t? { [Name in keyof Buckets]: BucketDef }\n\t\t: unknown;\n\n/**\n * Validate and freeze a Neon branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neon/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\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 * `aiGateway` / `functions` / `buckets`, plus the deprecated `preview` aliases) and a\n * **dynamic** per-branch `branch` closure. The static half determines which secrets exist —\n * so `NeonEnv<typeof config>` and `parseEnv` are exact — while the closure can only *tune*\n * a branch (lifecycle, compute, per-function 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 DataApiInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n\tconst Functions extends Record<string, FunctionDef> | undefined = undefined,\n\tconst Buckets extends Record<string, BucketDef> | undefined = undefined,\n\tconst AiGateway extends ServiceToggleInput | undefined = undefined,\n>(input: {\n\t// Each field is intersected with its concrete interface (not just typed as the bare\n\t// generic). The generic alone — e.g. `preview?: Preview` — gives editors no members to\n\t// complete against in the object-literal position (they see `{} | undefined`), so you\n\t// lose hints for `aiGateway` / `functions` / `buckets`. `& PreviewInput` restores the\n\t// full shape for autocomplete while still inferring the `const` literal that types the\n\t// `branch` closure's slugs (BranchTuningFn<Preview>) and the returned Config.\n\tauth?: Auth & ServiceToggleInput;\n\t// The `dataApi` field carries the Neon-Auth cross-field guard at the type level (see\n\t// `DataApiField`): a Neon-Auth Data API without `auth` enabled surfaces a readable hint\n\t// as the field's expected type instead of collapsing the value to `never`.\n\tdataApi?: DataApiField<Auth, DataApi>;\n\taiGateway?: AiGateway & ServiceToggleInput;\n\tfunctions?: Functions &\n\t\tRecord<string, FunctionDef> &\n\t\tFunctionsAutocomplete<Functions>;\n\tbuckets?: Buckets &\n\t\tRecord<string, BucketDef> &\n\t\tBucketsAutocomplete<Buckets>;\n\t// `& PreviewInput` restores top-level member hints (aiGateway/functions/buckets);\n\t// `& PreviewAutocomplete<Preview>` restores hints *inside* each function/bucket slug\n\t// object (see `PreviewAutocomplete`), which the bare index signature otherwise hides.\n\t/** @deprecated Use top-level `aiGateway`, `functions`, and `buckets`. */\n\tpreview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;\n\tbranch?: BranchTuningFn<Preview, Functions>;\n}): Config<Auth, DataApi, Preview, Functions, Buckets, AiGateway> {\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, functions: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services 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<\n\t\tAuth,\n\t\tDataApi,\n\t\tPreview,\n\t\tFunctions,\n\t\tBuckets,\n\t\tAiGateway\n\t>;\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 + 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: isDataApiEnabled(config.dataApi),\n\t\tdataApiPolicy:\n\t\t\tconfig.dataApi === undefined\n\t\t\t\t? \"omitted\"\n\t\t\t\t: isDataApiEnabled(config.dataApi)\n\t\t\t\t\t? \"enabled\"\n\t\t\t\t\t: \"disabled\",\n\t};\n\tconst dataApi = resolveDataApi(config.dataApi);\n\tif (dataApi) resolved.dataApi = dataApi;\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseBranchTtl`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseBranchTtl(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(\n\t\tmergeAuthoredPreview(config),\n\t\ttuning,\n\t\tbranch,\n\t);\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/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */\nfunction isDataApiEnabled(input: DataApiInput | undefined): boolean {\n\tif (input === undefined) return false;\n\tif (typeof input === \"boolean\") return input;\n\treturn input.enabled !== false;\n}\n\n/**\n * Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when\n * the Data API is not enabled. `authProvider` defaults to `\"neon\"`; the external-IdP wiring\n * is carried through only for the `\"external\"` provider; `settings` is copied with its\n * `undefined` entries dropped so diffing only considers fields the policy actually set.\n */\nfunction resolveDataApi(\n\tinput: DataApiInput | undefined,\n): ResolvedDataApiConfig | undefined {\n\tif (!isDataApiEnabled(input)) return undefined;\n\tif (typeof input !== \"object\") {\n\t\t// Bare `true`: enabled with Neon Auth and all-default settings.\n\t\treturn { authProvider: \"neon\" };\n\t}\n\tconst authProvider = input.authProvider ?? \"neon\";\n\tconst resolved: ResolvedDataApiConfig = { authProvider };\n\tif (authProvider === \"external\") {\n\t\tif (input.jwksUrl !== undefined) resolved.jwksUrl = input.jwksUrl;\n\t\tif (input.providerName !== undefined)\n\t\t\tresolved.providerName = input.providerName;\n\t\tif (input.jwtAudience !== undefined)\n\t\t\tresolved.jwtAudience = input.jwtAudience;\n\t}\n\tconst settings = normalizeDataApiSettings(input.settings);\n\tif (settings) resolved.settings = settings;\n\treturn resolved;\n}\n\n/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */\nfunction normalizeDataApiSettings(\n\tsettings: DataApiSettings | undefined,\n): DataApiSettings | undefined {\n\tif (!settings) return undefined;\n\tconst out: DataApiSettings = {};\n\tfor (const [key, value] of Object.entries(settings)) {\n\t\tif (value !== undefined) {\n\t\t\t(out as Record<string, unknown>)[key] = value;\n\t\t}\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\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\tbranch: BranchTarget,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = authoredFunctionTuning(tuning);\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}, branch),\n\t);\n\tassertUniqueResolvedCustomDomains(functions);\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\tbranch: BranchTarget,\n): ResolvedFunctionConfig {\n\tconst customDomains = resolveFunctionCustomDomains(def, tuning, branch);\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\tbundler: def.bundler ?? DEFAULT_FUNCTION_BUNDLER,\n\t\t// Normalized only when declared, so a policy without it resolves unchanged and takes\n\t\t// the pre-existing bundling path. Both bundlers read it; `neon dev` mirrors it so a\n\t\t// local run leaves the same packages unbundled as a deploy.\n\t\t...(def.externalPackages\n\t\t\t? {\n\t\t\t\t\texternalPackages: def.externalPackages.map(\n\t\t\t\t\t\tnormalizeExternalPackage,\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: {}),\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t\t...(def.triggers\n\t\t\t? {\n\t\t\t\t\ttriggers: def.triggers.map((trigger) => ({\n\t\t\t\t\t\ttype: \"schedule\" as const,\n\t\t\t\t\t\tname: trigger.name,\n\t\t\t\t\t\tcron: trigger.cron,\n\t\t\t\t\t\tfunctionPath: trigger.functionPath ?? \"/\",\n\t\t\t\t\t\tenabled: trigger.enabled ?? true,\n\t\t\t\t\t})),\n\t\t\t\t}\n\t\t\t: {}),\n\t\t...(customDomains !== undefined ? { customDomains } : {}),\n\t};\n}\n\nfunction resolveFunctionCustomDomains(\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n\tbranch: BranchTarget,\n): string[] | undefined {\n\tif (tuning.customDomains !== undefined) {\n\t\treturn tuning.customDomains.map(normalizeCustomDomain);\n\t}\n\t// Hostnames are globally unique, so a child checkout 409s if it inherits this list.\n\tif (def.customDomains !== undefined && branch.isDefault === true) {\n\t\treturn def.customDomains.map(normalizeCustomDomain);\n\t}\n\treturn undefined;\n}\n\nfunction assertUniqueResolvedCustomDomains(\n\tfunctions: ResolvedFunctionConfig[],\n): void {\n\tconst byDomain = new Map<string, string>();\n\tfor (const fn of functions) {\n\t\tfor (const domain of fn.customDomains ?? []) {\n\t\t\tconst prior = byDomain.get(domain);\n\t\t\tif (prior !== undefined) {\n\t\t\t\tthrow new ConfigValidationError([\n\t\t\t\t\t`custom domain \"${domain}\" is used by both function \"${prior}\" and \"${fn.slug}\"`,\n\t\t\t\t]);\n\t\t\t}\n\t\t\tbyDomain.set(domain, fn.slug);\n\t\t}\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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"}
|
|
1
|
+
{"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import {\n\tauthoredFunctionTuning,\n\tmergeAuthoredPreview,\n} from \"./authored-services.js\";\nimport { normalizeCustomDomain } from \"./custom-domain.js\";\nimport { parseBranchTtl } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport { normalizeExternalPackage } from \"./external-packages.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tBucketDef,\n\tConfig,\n\tDataApiInput,\n\tDataApiSettings,\n\tFunctionDef,\n\tFunctionTriggerDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedFunctionTrigger,\n\tResolvedPreviewConfig,\n\tServiceEnabled,\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 DEFAULT_FUNCTION_BUNDLER = \"esbuild\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is\n * on (see {@link ServiceEnabled}) and not the explicit `authProvider: \"external\"` variant\n * (so the default / `\"neon\"` provider). This is the case that requires top-level Neon Auth.\n */\ntype DataApiUsesNeonAuth<DataApi> =\n\tServiceEnabled<DataApi> extends true\n\t\t? [DataApi] extends [{ authProvider: \"external\" }]\n\t\t\t? false\n\t\t\t: true\n\t\t: false;\n\n/**\n * Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data\n * API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the\n * offending value against this string literal — `Type 'true' is not assignable to type\n * '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque\n * `Type 'true' is not assignable to type 'never'` an intersection guard produces.\n *\n * It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API\n * *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type NeonAuthRequiredHint =\n\t\"`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`\";\n\n/**\n * Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`\n * field** rather than an intersected requirement on `auth`.\n *\n * - A Neon-Auth Data API (`authProvider: \"neon\"`, the default) with top-level `auth` enabled,\n * or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the\n * `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the\n * returned {@link Config}).\n * - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to\n * the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)\n * right on the `dataApi` value.\n *\n * The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for\n * non-typed (plain-JS) callers, so the behavior is identical — only the type-level message\n * changes.\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type DataApiField<Auth, DataApi> =\n\tDataApiUsesNeonAuth<DataApi> extends true\n\t\t? ServiceEnabled<Auth> extends true\n\t\t\t? DataApi & DataApiInput\n\t\t\t: NeonAuthRequiredHint\n\t\t: DataApi & DataApiInput;\n\n/**\n * Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.\n *\n * {@link PreviewInput} types those records with a string index signature\n * (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers\n * `const Preview`, every authored slug becomes a **named** property on the inferred literal\n * (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature\n * when the editor computes the contextual type of that slug's value — so the rest of\n * {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as\n * completions inside `hello: { … }` / `uploads: { … }`.\n *\n * Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via\n * a mapped type over the already-inferred keys) puts those members back onto the contextual\n * type without going through an index signature, which restores autocomplete. Intersected with\n * `Preview & PreviewInput` it neither widens what is accepted (the values were already\n * `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for\n * `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.\n */\ntype PreviewAutocomplete<Preview> = (Preview extends { functions: infer F }\n\t? { functions: { [Slug in keyof F]: FunctionDef } }\n\t: unknown) &\n\t(Preview extends { buckets: infer B }\n\t\t? { buckets: { [Name in keyof B]: BucketDef } }\n\t\t: unknown);\n\ntype FunctionsAutocomplete<Functions> =\n\tFunctions extends Record<string, unknown>\n\t\t? { [Slug in keyof Functions]: FunctionDef }\n\t\t: unknown;\n\ntype BucketsAutocomplete<Buckets> =\n\tBuckets extends Record<string, unknown>\n\t\t? { [Name in keyof Buckets]: BucketDef }\n\t\t: unknown;\n\n/**\n * Validate and freeze a Neon branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neon/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\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 * `aiGateway` / `functions` / `buckets` / `triggers`, plus the deprecated `preview` aliases) and a\n * **dynamic** per-branch `branch` closure. The static half determines which secrets exist —\n * so `NeonEnv<typeof config>` and `parseEnv` are exact — while the closure can only *tune*\n * a branch (lifecycle, compute, per-function 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 DataApiInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n\tconst Functions extends Record<string, FunctionDef> | undefined = undefined,\n\tconst Buckets extends Record<string, BucketDef> | undefined = undefined,\n\tconst AiGateway extends ServiceToggleInput | undefined = undefined,\n>(input: {\n\t// Each field is intersected with its concrete interface (not just typed as the bare\n\t// generic). The generic alone — e.g. `preview?: Preview` — gives editors no members to\n\t// complete against in the object-literal position (they see `{} | undefined`), so you\n\t// lose hints for `aiGateway` / `functions` / `buckets`. `& PreviewInput` restores the\n\t// full shape for autocomplete while still inferring the `const` literal that types the\n\t// `branch` closure's slugs (BranchTuningFn<Preview>) and the returned Config.\n\tauth?: Auth & ServiceToggleInput;\n\t// The `dataApi` field carries the Neon-Auth cross-field guard at the type level (see\n\t// `DataApiField`): a Neon-Auth Data API without `auth` enabled surfaces a readable hint\n\t// as the field's expected type instead of collapsing the value to `never`.\n\tdataApi?: DataApiField<Auth, DataApi>;\n\taiGateway?: AiGateway & ServiceToggleInput;\n\tfunctions?: Functions &\n\t\tRecord<string, FunctionDef> &\n\t\tFunctionsAutocomplete<Functions>;\n\tbuckets?: Buckets &\n\t\tRecord<string, BucketDef> &\n\t\tBucketsAutocomplete<Buckets>;\n\ttriggers?: Record<string, FunctionTriggerDef>;\n\t// `& PreviewInput` restores top-level member hints (aiGateway/functions/buckets);\n\t// `& PreviewAutocomplete<Preview>` restores hints *inside* each function/bucket slug\n\t// object (see `PreviewAutocomplete`), which the bare index signature otherwise hides.\n\t/** @deprecated Use top-level `aiGateway`, `functions`, and `buckets`. */\n\tpreview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;\n\tbranch?: BranchTuningFn<Preview, Functions>;\n}): Config<Auth, DataApi, Preview, Functions, Buckets, AiGateway> {\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, functions: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services 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<\n\t\tAuth,\n\t\tDataApi,\n\t\tPreview,\n\t\tFunctions,\n\t\tBuckets,\n\t\tAiGateway\n\t>;\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 + 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: isDataApiEnabled(config.dataApi),\n\t\tdataApiPolicy:\n\t\t\tconfig.dataApi === undefined\n\t\t\t\t? \"omitted\"\n\t\t\t\t: isDataApiEnabled(config.dataApi)\n\t\t\t\t\t? \"enabled\"\n\t\t\t\t\t: \"disabled\",\n\t};\n\tconst dataApi = resolveDataApi(config.dataApi);\n\tif (dataApi) resolved.dataApi = dataApi;\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseBranchTtl`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseBranchTtl(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(\n\t\tmergeAuthoredPreview(config),\n\t\ttuning,\n\t\tbranch,\n\t);\n\tif (preview) resolved.preview = preview;\n\tconst triggers = resolveTriggers(config.triggers);\n\tif (triggers.length > 0) resolved.triggers = triggers;\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/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */\nfunction isDataApiEnabled(input: DataApiInput | undefined): boolean {\n\tif (input === undefined) return false;\n\tif (typeof input === \"boolean\") return input;\n\treturn input.enabled !== false;\n}\n\n/**\n * Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when\n * the Data API is not enabled. `authProvider` defaults to `\"neon\"`; the external-IdP wiring\n * is carried through only for the `\"external\"` provider; `settings` is copied with its\n * `undefined` entries dropped so diffing only considers fields the policy actually set.\n */\nfunction resolveDataApi(\n\tinput: DataApiInput | undefined,\n): ResolvedDataApiConfig | undefined {\n\tif (!isDataApiEnabled(input)) return undefined;\n\tif (typeof input !== \"object\") {\n\t\t// Bare `true`: enabled with Neon Auth and all-default settings.\n\t\treturn { authProvider: \"neon\" };\n\t}\n\tconst authProvider = input.authProvider ?? \"neon\";\n\tconst resolved: ResolvedDataApiConfig = { authProvider };\n\tif (authProvider === \"external\") {\n\t\tif (input.jwksUrl !== undefined) resolved.jwksUrl = input.jwksUrl;\n\t\tif (input.providerName !== undefined)\n\t\t\tresolved.providerName = input.providerName;\n\t\tif (input.jwtAudience !== undefined)\n\t\t\tresolved.jwtAudience = input.jwtAudience;\n\t}\n\tconst settings = normalizeDataApiSettings(input.settings);\n\tif (settings) resolved.settings = settings;\n\treturn resolved;\n}\n\n/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */\nfunction normalizeDataApiSettings(\n\tsettings: DataApiSettings | undefined,\n): DataApiSettings | undefined {\n\tif (!settings) return undefined;\n\tconst out: DataApiSettings = {};\n\tfor (const [key, value] of Object.entries(settings)) {\n\t\tif (value !== undefined) {\n\t\t\t(out as Record<string, unknown>)[key] = value;\n\t\t}\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\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\tbranch: BranchTarget,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = authoredFunctionTuning(tuning);\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}, branch),\n\t);\n\tassertUniqueResolvedCustomDomains(functions);\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\tbranch: BranchTarget,\n): ResolvedFunctionConfig {\n\tconst customDomains = resolveFunctionCustomDomains(def, tuning, branch);\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\tbundler: def.bundler ?? DEFAULT_FUNCTION_BUNDLER,\n\t\t// Normalized only when declared, so a policy without it resolves unchanged and takes\n\t\t// the pre-existing bundling path. Both bundlers read it; `neon dev` mirrors it so a\n\t\t// local run leaves the same packages unbundled as a deploy.\n\t\t...(def.externalPackages\n\t\t\t? {\n\t\t\t\t\texternalPackages: def.externalPackages.map(\n\t\t\t\t\t\tnormalizeExternalPackage,\n\t\t\t\t\t),\n\t\t\t\t}\n\t\t\t: {}),\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t\t...(customDomains !== undefined ? { customDomains } : {}),\n\t};\n}\n\nfunction resolveTriggers(\n\ttriggers: Record<string, FunctionTriggerDef> | undefined,\n): ResolvedFunctionTrigger[] {\n\tif (!triggers) return [];\n\treturn Object.entries(triggers).map(([name, trigger]) => {\n\t\tif (trigger.type === \"schedule\") {\n\t\t\treturn {\n\t\t\t\ttype: \"schedule\" as const,\n\t\t\t\tname,\n\t\t\t\tfunctionSlug: trigger.function,\n\t\t\t\tcron: trigger.cron,\n\t\t\t\tfunctionPath: trigger.functionPath ?? \"/\",\n\t\t\t\tenabled: trigger.enabled ?? true,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttype: \"storage_object_created\" as const,\n\t\t\tname,\n\t\t\tfunctionSlug: trigger.function,\n\t\t\tbucketName: trigger.bucket,\n\t\t\t...(trigger.prefix !== undefined ? { prefix: trigger.prefix } : {}),\n\t\t\tfunctionPath: trigger.functionPath ?? \"/\",\n\t\t\tenabled: trigger.enabled ?? true,\n\t\t};\n\t});\n}\n\nfunction resolveFunctionCustomDomains(\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n\tbranch: BranchTarget,\n): string[] | undefined {\n\tif (tuning.customDomains !== undefined) {\n\t\treturn tuning.customDomains.map(normalizeCustomDomain);\n\t}\n\t// Hostnames are globally unique, so a child checkout 409s if it inherits this list.\n\tif (def.customDomains !== undefined && branch.isDefault === true) {\n\t\treturn def.customDomains.map(normalizeCustomDomain);\n\t}\n\treturn undefined;\n}\n\nfunction assertUniqueResolvedCustomDomains(\n\tfunctions: ResolvedFunctionConfig[],\n): void {\n\tconst byDomain = new Map<string, string>();\n\tfor (const fn of functions) {\n\t\tfor (const domain of fn.customDomains ?? []) {\n\t\t\tconst prior = byDomain.get(domain);\n\t\t\tif (prior !== undefined) {\n\t\t\t\tthrow new ConfigValidationError([\n\t\t\t\t\t`custom domain \"${domain}\" is used by both function \"${prior}\" and \"${fn.slug}\"`,\n\t\t\t\t]);\n\t\t\t}\n\t\t\tbyDomain.set(domain, fn.slug);\n\t\t}\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/diff.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BucketAccessLevel, ComputeSettings, ConflictReport, DataApiSettings, ResolvedBranchConfig, ResolvedFunctionConfig,
|
|
1
|
+
import { BucketAccessLevel, ComputeSettings, ConflictReport, DataApiSettings, ResolvedBranchConfig, ResolvedFunctionConfig, ResolvedFunctionTrigger } from "./types.js";
|
|
2
2
|
import { EnableDataApiInput, NeonBranchSnapshot, NeonBucketSnapshot, NeonCustomDomainSnapshot, NeonEndpointSnapshot, NeonFunctionSnapshot, NeonTriggerSnapshot } from "./neon-api.js";
|
|
3
3
|
|
|
4
4
|
//#region src/lib/diff.d.ts
|
|
@@ -80,7 +80,7 @@ type PlanStep = {
|
|
|
80
80
|
branchId: string;
|
|
81
81
|
branchName: string;
|
|
82
82
|
functionSlug: string;
|
|
83
|
-
trigger:
|
|
83
|
+
trigger: ResolvedFunctionTrigger;
|
|
84
84
|
} | {
|
|
85
85
|
kind: "update-trigger";
|
|
86
86
|
projectId: string;
|
|
@@ -88,7 +88,7 @@ type PlanStep = {
|
|
|
88
88
|
branchName: string;
|
|
89
89
|
triggerId: string;
|
|
90
90
|
functionSlug: string;
|
|
91
|
-
trigger:
|
|
91
|
+
trigger: ResolvedFunctionTrigger;
|
|
92
92
|
} | {
|
|
93
93
|
kind: "register-custom-domain";
|
|
94
94
|
projectId: string;
|
package/dist/lib/diff.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","names":[],"sources":["../../src/lib/diff.ts"],"mappings":";;;;;;;AAwBA;AAAoB;AAoBP,KApBD,QAAA,GAoBC;EAgBF,IAAA,EAAA,mBAAA;EAQE,SAAA,EAAA,MAAA;EAeG,QAAA,EAAA,MAAA;EAcT,UAAA,EAAA,MAAA;EAUK,SAAA,EAAA,MAAA,GAAA,IAAA;AASA,CAAA,GAAA;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","names":[],"sources":["../../src/lib/diff.ts"],"mappings":";;;;;;;AAwBA;AAAoB;AAoBP,KApBD,QAAA,GAoBC;EAgBF,IAAA,EAAA,mBAAA;EAQE,SAAA,EAAA,MAAA;EAeG,QAAA,EAAA,MAAA;EAcT,UAAA,EAAA,MAAA;EAUK,SAAA,EAAA,MAAA,GAAA,IAAA;AASA,CAAA,GAAA;EAAuB,IAAA,EAAA,yBAAA;EAoBlB,SAAA,EAAA,MAAA;EAgBA,QAAA,EAAA,MAAA;EAAkB,UAAA,EAAA,MAAA;EACzB,SAAA,EAAA,OAAA;AACE,CAAA,GAAA;EACD,IAAA,EAAA,iBAAA;EAEM,SAAA,EAAA,MAAA;EAAwB,UAAA,EAAA,MAAA;EAGxB,UAAA,EAAA,MAAW;EAAA,QAAA,EApHf,eAoHe;AAEnB,CAAA,GAAA;EACG,IAAA,EAAA,aAAA;EACD,SAAA,EAAA,MAAA;EACA,QAAA,EAAA,MAAA;EAAkB,UAAA,EAAA,MAAA;EAGZ,YAAA,CAAA,EAAW,MAAA;AAQ5B,CAAA,GAAiB;EAAU,IAAA,EAAA,iBAAA;EACpB,SAAA,EAAA,MAAA;EACK,QAAA,EAAA,MAAA;EAAc,UAAA,EAAA,MAAA;EAMV,YAAA,EAAU,MAAA;EAAA;EACjB,KAAA,CAAA,EA7HE,kBA6HF;AACA,CAAA,GAAA;EACC,IAAA,EAAA,iBAAA;EACP,SAAA,EAAA,MAAA;EAAU,QAAA,EAAA,MAAA;;;YAxHA;;;;;;;;;;;;;eAeG;;;;;;;;;;;;;MAcT;;;;;;;;;WAUK;;;;;;;;WASA;;;;;;;;;;;;;;;;;UAoBK,kBAAA;;;;;;;;;oBASE;;;;;;UAOF,kBAAA;WACP;aACE;YACD;;kBAEM;;UAGA,WAAA;;UAER;aACG;YACD;YACA;;UAGM,WAAA;;;;;;;UAQA,UAAA;QACV;aACK;;;;;iBAMI,UAAA,SACP,8BACA,sBACC,cACP"}
|
package/dist/lib/diff.js
CHANGED
|
@@ -76,29 +76,6 @@ function diffPreview(args) {
|
|
|
76
76
|
fn,
|
|
77
77
|
functionExists: exists
|
|
78
78
|
});
|
|
79
|
-
for (const trigger of fn.triggers ?? []) {
|
|
80
|
-
const remoteTrigger = state.triggers.find((t) => t.functionSlug === fn.slug && t.name === trigger.name);
|
|
81
|
-
if (!remoteTrigger) {
|
|
82
|
-
plan.push({
|
|
83
|
-
kind: "create-trigger",
|
|
84
|
-
projectId: remote.projectId,
|
|
85
|
-
branchId: remote.branch.id,
|
|
86
|
-
branchName: remote.branch.name,
|
|
87
|
-
functionSlug: fn.slug,
|
|
88
|
-
trigger
|
|
89
|
-
});
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (remoteTrigger.cron !== trigger.cron || remoteTrigger.enabled !== trigger.enabled || remoteTrigger.functionPath !== trigger.functionPath) plan.push({
|
|
93
|
-
kind: "update-trigger",
|
|
94
|
-
projectId: remote.projectId,
|
|
95
|
-
branchId: remote.branch.id,
|
|
96
|
-
branchName: remote.branch.name,
|
|
97
|
-
triggerId: remoteTrigger.triggerId,
|
|
98
|
-
functionSlug: fn.slug,
|
|
99
|
-
trigger
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
79
|
diffFunctionCustomDomains({
|
|
103
80
|
fn,
|
|
104
81
|
customDomains,
|
|
@@ -108,6 +85,47 @@ function diffPreview(args) {
|
|
|
108
85
|
conflicts
|
|
109
86
|
});
|
|
110
87
|
}
|
|
88
|
+
for (const trigger of config.triggers ?? []) {
|
|
89
|
+
const remoteTrigger = state.triggers.find((t) => t.name === trigger.name);
|
|
90
|
+
if (!remoteTrigger) {
|
|
91
|
+
plan.push({
|
|
92
|
+
kind: "create-trigger",
|
|
93
|
+
projectId: remote.projectId,
|
|
94
|
+
branchId: remote.branch.id,
|
|
95
|
+
branchName: remote.branch.name,
|
|
96
|
+
functionSlug: trigger.functionSlug,
|
|
97
|
+
trigger
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (remoteTrigger.type !== trigger.type) {
|
|
102
|
+
conflicts.push({
|
|
103
|
+
kind: "branch",
|
|
104
|
+
identifier: trigger.name,
|
|
105
|
+
field: "trigger",
|
|
106
|
+
current: remoteTrigger.type,
|
|
107
|
+
desired: trigger.type,
|
|
108
|
+
reason: `trigger "${trigger.name}" exists as type ${remoteTrigger.type}; neon.ts declares ${trigger.type}. Delete the remote trigger with \`neon triggers delete\` and apply again.`
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (triggerNeedsUpdate(trigger, remoteTrigger)) plan.push({
|
|
113
|
+
kind: "update-trigger",
|
|
114
|
+
projectId: remote.projectId,
|
|
115
|
+
branchId: remote.branch.id,
|
|
116
|
+
branchName: remote.branch.name,
|
|
117
|
+
triggerId: remoteTrigger.triggerId,
|
|
118
|
+
functionSlug: trigger.functionSlug,
|
|
119
|
+
trigger
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function triggerNeedsUpdate(desired, remote) {
|
|
124
|
+
if (remote.functionSlug !== desired.functionSlug) return true;
|
|
125
|
+
if (remote.functionPath !== desired.functionPath) return true;
|
|
126
|
+
if (remote.enabled !== desired.enabled) return true;
|
|
127
|
+
if (desired.type === "schedule") return remote.type !== "schedule" || remote.cron !== desired.cron;
|
|
128
|
+
return remote.type !== "storage_object_created" || remote.bucketName !== desired.bucketName || remote.prefix !== desired.prefix;
|
|
111
129
|
}
|
|
112
130
|
const FUNCTION_CUSTOM_DOMAIN_ENTITY = "function";
|
|
113
131
|
function diffFunctionCustomDomains(args) {
|
package/dist/lib/diff.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.js","names":[],"sources":["../../src/lib/diff.ts"],"sourcesContent":["import type {\n\tEnableDataApiInput,\n\tNeonBranchSnapshot,\n\tNeonBucketSnapshot,\n\tNeonCustomDomainSnapshot,\n\tNeonEndpointSnapshot,\n\tNeonFunctionSnapshot,\n\tNeonTriggerSnapshot,\n} from \"./neon-api.js\";\nimport type {\n\tBucketAccessLevel,\n\tComputeSettings,\n\tConflictReport,\n\tDataApiSettings,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedFunctionScheduleTrigger,\n} from \"./types.js\";\n\n/**\n * A planned action to perform a single mutation against the Neon API. The diff engine\n * produces a list of these for `pushConfig` to execute (or report).\n */\nexport type PlanStep =\n\t| {\n\t\t\tkind: \"update-branch-ttl\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\texpiresAt: string | null;\n\t }\n\t| {\n\t\t\tkind: \"update-branch-protected\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tprotected: boolean;\n\t }\n\t| {\n\t\t\tkind: \"update-endpoint\";\n\t\t\tprojectId: string;\n\t\t\tbranchName: string;\n\t\t\tendpointId: string;\n\t\t\tsettings: ComputeSettings;\n\t }\n\t| {\n\t\t\tkind: \"enable-auth\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName?: string;\n\t }\n\t| {\n\t\t\tkind: \"enable-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t\t\t/** Create-time auth wiring + initial settings from the policy. */\n\t\t\tinput?: EnableDataApiInput;\n\t }\n\t| {\n\t\t\tkind: \"update-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t\t\tsettings: DataApiSettings;\n\t }\n\t| {\n\t\t\tkind: \"disable-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t }\n\t| {\n\t\t\tkind: \"create-bucket\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tbucketName: string;\n\t\t\taccessLevel: BucketAccessLevel;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * Deploy code to a function. Planned for every desired function — deployments are\n\t\t\t * versioned and the newest becomes active, so a push ships the current source each\n\t\t\t * time. Neon has no separate \"create function\" endpoint: the first deployment to a\n\t\t\t * slug creates the function. `functionExists` therefore only drives whether this\n\t\t\t * surfaces as a `create` (first deploy) or an `update` (re-deploy).\n\t\t\t */\n\t\t\tkind: \"deploy-function\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfn: ResolvedFunctionConfig;\n\t\t\t/** Whether the function already existed remotely when the plan was computed. */\n\t\t\tfunctionExists: boolean;\n\t }\n\t| {\n\t\t\tkind: \"create-trigger\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\ttrigger: ResolvedFunctionScheduleTrigger;\n\t }\n\t| {\n\t\t\tkind: \"update-trigger\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\ttriggerId: string;\n\t\t\tfunctionSlug: string;\n\t\t\ttrigger: ResolvedFunctionScheduleTrigger;\n\t }\n\t| {\n\t\t\tkind: \"register-custom-domain\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\tdomain: string;\n\t }\n\t| {\n\t\t\tkind: \"retarget-custom-domain\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\tpreviousSlug: string;\n\t\t\tdomain: string;\n\t };\n\nexport interface RemoteServiceState {\n\tdatabaseName: string;\n\tauthEnabled: boolean;\n\tdataApiEnabled: boolean;\n\t/**\n\t * Current Data API runtime settings, when the integration is enabled and the API reports\n\t * them (SubZero only). `null`/absent means \"not reported\" — settings drift can't be\n\t * computed, so no update step is planned.\n\t */\n\tdataApiSettings?: DataApiSettings | null;\n}\n\n/**\n * Snapshot of the branch's current Preview-feature state. Absent (`undefined`) when the\n * policy has no `preview` block — `pushConfig` only fetches this when needed.\n */\nexport interface RemotePreviewState {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\ttriggers: NeonTriggerSnapshot[];\n\t/** Absent on pre-feature snapshots; treated as none. */\n\tcustomDomains?: NeonCustomDomainSnapshot[];\n}\n\nexport interface RemoteState {\n\tprojectId: string;\n\tbranch: NeonBranchSnapshot;\n\tendpoint?: NeonEndpointSnapshot;\n\tservices: RemoteServiceState;\n\tpreview?: RemotePreviewState;\n}\n\nexport interface DiffOptions {\n\t/**\n\t * Apply mutable drift on the selected branch as plan steps instead of conflicts.\n\t * Default: `false`.\n\t */\n\tupdateExisting: boolean;\n}\n\nexport interface DiffResult {\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}\n\n/**\n * Diff desired branch policy against the selected remote branch. Pure function.\n */\nexport function diffConfig(\n\tconfig: ResolvedBranchConfig,\n\tremote: RemoteState,\n\toptions: DiffOptions,\n): DiffResult {\n\tconst conflicts: ConflictReport[] = [];\n\tconst plan: PlanStep[] = [];\n\tdiffBranchConfig({ config, remote, options, plan, conflicts });\n\tdiffServices({ config, remote, options, plan, conflicts });\n\tdiffPreview({ config, remote, options, plan, conflicts });\n\treturn { plan, conflicts };\n}\n\n/**\n * Data API teardown requires explicit `dataApi: false` so omission remains non-destructive.\n *\n * The AI Gateway is intentionally NOT planned here: it is always available on a branch\n * (credential-gated, not per-branch provisioned), so `preview.aiGateway` produces no plan\n * step — it only drives the branch credential's `ai_gateway:invoke` scope and the gateway\n * env vars (`@neon/env`). There is nothing to create, and nothing to probe.\n *\n * Functions are always (re-)deployed: deployments are versioned and the newest becomes\n * active, so each push ships the current source. There is no separate create step — Neon\n * creates the function on its first deployment — so a single `deploy-function` step is\n * emitted per desired function, carrying `functionExists` so the apply can report it as a\n * create (first deploy) or an update (re-deploy).\n */\nfunction diffPreview(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst preview = config.preview;\n\tif (!preview) return;\n\t// `remote.preview` is only fetched when the policy has a preview block; treat a missing\n\t// snapshot as \"nothing exists yet\" so the diff is still well-defined.\n\tconst state: RemotePreviewState = remote.preview ?? {\n\t\tbuckets: [],\n\t\tfunctions: [],\n\t\ttriggers: [],\n\t};\n\tconst customDomains = state.customDomains ?? [];\n\n\tfor (const bucket of preview.buckets) {\n\t\tif (state.buckets.some((b) => b.name === bucket.name)) continue;\n\t\tplan.push({\n\t\t\tkind: \"create-bucket\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tbucketName: bucket.name,\n\t\t\taccessLevel: bucket.access,\n\t\t});\n\t}\n\n\tfor (const fn of preview.functions) {\n\t\tconst exists = state.functions.some((f) => f.slug === fn.slug);\n\t\tplan.push({\n\t\t\tkind: \"deploy-function\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tfn,\n\t\t\tfunctionExists: exists,\n\t\t});\n\t\tfor (const trigger of fn.triggers ?? []) {\n\t\t\tconst remoteTrigger = state.triggers.find(\n\t\t\t\t(t) => t.functionSlug === fn.slug && t.name === trigger.name,\n\t\t\t);\n\t\t\tif (!remoteTrigger) {\n\t\t\t\tplan.push({\n\t\t\t\t\tkind: \"create-trigger\",\n\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\t\ttrigger,\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tremoteTrigger.cron !== trigger.cron ||\n\t\t\t\tremoteTrigger.enabled !== trigger.enabled ||\n\t\t\t\tremoteTrigger.functionPath !== trigger.functionPath\n\t\t\t) {\n\t\t\t\tplan.push({\n\t\t\t\t\tkind: \"update-trigger\",\n\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\t\ttriggerId: remoteTrigger.triggerId,\n\t\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\t\ttrigger,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tdiffFunctionCustomDomains({\n\t\t\tfn,\n\t\t\tcustomDomains,\n\t\t\tremote,\n\t\t\toptions,\n\t\t\tplan,\n\t\t\tconflicts,\n\t\t});\n\t}\n}\n\nconst FUNCTION_CUSTOM_DOMAIN_ENTITY = \"function\";\n\nfunction diffFunctionCustomDomains(args: {\n\tfn: ResolvedFunctionConfig;\n\tcustomDomains: NeonCustomDomainSnapshot[];\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { fn, customDomains, remote, options, plan, conflicts } = args;\n\tfor (const domain of fn.customDomains ?? []) {\n\t\tconst existing = customDomains.find((d) => d.domain === domain);\n\t\tif (!existing) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"register-custom-domain\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\tdomain,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (existing.entityType !== FUNCTION_CUSTOM_DOMAIN_ENTITY) {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: remote.branch.name,\n\t\t\t\tfield: \"customDomain\",\n\t\t\t\tcurrent: {\n\t\t\t\t\tentityType: existing.entityType,\n\t\t\t\t\tentityId: existing.entityId,\n\t\t\t\t},\n\t\t\t\tdesired: {\n\t\t\t\t\tentityType: FUNCTION_CUSTOM_DOMAIN_ENTITY,\n\t\t\t\t\tentityId: fn.slug,\n\t\t\t\t},\n\t\t\t\treason: `custom domain \"${domain}\" is registered to entity type \"${existing.entityType}\", which neon.ts cannot retarget. Delete it with \\`neon function domains delete\\` or stop declaring it.`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (existing.entityId === fn.slug) continue;\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"retarget-custom-domain\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\tpreviousSlug: existing.entityId,\n\t\t\t\tdomain,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconflicts.push({\n\t\t\tkind: \"branch\",\n\t\t\tidentifier: remote.branch.name,\n\t\t\tfield: \"customDomain\",\n\t\t\tcurrent: existing.entityId,\n\t\t\tdesired: fn.slug,\n\t\t\treason: `custom domain \"${domain}\" is registered to function \"${existing.entityId}\". Pass \\`updateExisting: true\\` (SDK) or \\`--update-existing\\` (CLI) to retarget it to \"${fn.slug}\".`,\n\t\t});\n\t}\n}\n\n/**\n * Plan branch-scoped integrations. Enabling is additive (no existing resource to override).\n * The Data API is the one integration that also has a reconcilable *update*: its runtime\n * `settings` can drift once enabled, and reconciling them is an override (gated on\n * `updateExisting`, like compute/TTL/protected). The auth provider / JWKS wiring is fixed at\n * enable time, so it is never updated here. Disabling stays explicit/manual (destructive).\n */\nfunction diffServices(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst state = remote.services;\n\tif (config.authEnabled && !state.authEnabled) {\n\t\tconst step: PlanStep = {\n\t\t\tkind: \"enable-auth\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t};\n\t\tif (state.databaseName) step.databaseName = state.databaseName;\n\t\tplan.push(step);\n\t}\n\tconst dataApiPolicy =\n\t\tconfig.dataApiPolicy ?? (config.dataApiEnabled ? \"enabled\" : \"omitted\");\n\tif (dataApiPolicy === \"enabled\") {\n\t\tdiffDataApi({ config, remote, options, plan, conflicts });\n\t} else if (dataApiPolicy === \"disabled\" && state.dataApiEnabled) {\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"disable-data-api\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tdatabaseName: state.databaseName,\n\t\t\t});\n\t\t} else {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: remote.branch.name,\n\t\t\t\tfield: \"dataApi\",\n\t\t\t\tcurrent: true,\n\t\t\t\tdesired: false,\n\t\t\t\treason: \"Existing Data API would be deleted. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Plan the Data API: a first-time **enable** (carrying the create-time auth wiring +\n * settings), or — when it already exists — a **settings update** if the policy's settings\n * drift from the remote. The update is an override: applied as a plan step under\n * `updateExisting`, otherwise reported as a conflict.\n */\nfunction diffDataApi(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst state = remote.services;\n\tconst desired = config.dataApi;\n\n\tif (!state.dataApiEnabled) {\n\t\tconst step: PlanStep = {\n\t\t\tkind: \"enable-data-api\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tdatabaseName: state.databaseName,\n\t\t};\n\t\tconst input = desired ? enableInputFromResolved(desired) : undefined;\n\t\tif (input) step.input = input;\n\t\tplan.push(step);\n\t\treturn;\n\t}\n\n\t// Already enabled: the only reconcilable change is its runtime settings.\n\tconst desiredSettings = desired?.settings;\n\tif (!desiredSettings) return;\n\tif (!dataApiSettingsDiffer(desiredSettings, state.dataApiSettings)) return;\n\n\tif (options.updateExisting) {\n\t\tplan.push({\n\t\t\tkind: \"update-data-api\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tdatabaseName: state.databaseName,\n\t\t\tsettings: desiredSettings,\n\t\t});\n\t} else {\n\t\tconflicts.push({\n\t\t\tkind: \"branch\",\n\t\t\tidentifier: remote.branch.name,\n\t\t\tfield: \"dataApi.settings\",\n\t\t\tcurrent: state.dataApiSettings ?? undefined,\n\t\t\tdesired: desiredSettings,\n\t\t\treason: \"Existing Data API has different settings. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t});\n\t}\n}\n\n/** Build the create-time {@link EnableDataApiInput} from a resolved Data API config. */\nfunction enableInputFromResolved(\n\tresolved: ResolvedDataApiConfig,\n): EnableDataApiInput {\n\tconst input: EnableDataApiInput = { authProvider: resolved.authProvider };\n\tif (resolved.jwksUrl !== undefined) input.jwksUrl = resolved.jwksUrl;\n\tif (resolved.providerName !== undefined)\n\t\tinput.providerName = resolved.providerName;\n\tif (resolved.jwtAudience !== undefined)\n\t\tinput.jwtAudience = resolved.jwtAudience;\n\tif (resolved.settings) input.settings = resolved.settings;\n\treturn input;\n}\n\n/** The camelCase keys of {@link DataApiSettings}, used to compare desired vs remote settings. */\nconst DATA_API_SETTING_KEYS = [\n\t\"dbAggregatesEnabled\",\n\t\"dbAnonRole\",\n\t\"dbExtraSearchPath\",\n\t\"dbMaxRows\",\n\t\"dbSchemas\",\n\t\"jwtRoleClaimKey\",\n\t\"jwtCacheMaxLifetime\",\n\t\"openapiMode\",\n\t\"serverCorsAllowedOrigins\",\n\t\"serverTimingEnabled\",\n] as const satisfies ReadonlyArray<keyof DataApiSettings>;\n\n/**\n * Whether the policy's Data API `settings` differ from the remote ones. Only the keys the\n * policy actually set are compared (so unset fields never count as drift). When the remote\n * settings are not reported (`null`/absent — non-SubZero), drift can't be computed and this\n * returns `false` so no spurious update is planned.\n */\nfunction dataApiSettingsDiffer(\n\tdesired: DataApiSettings,\n\tcurrent: DataApiSettings | null | undefined,\n): boolean {\n\tif (!current) return false;\n\tfor (const key of DATA_API_SETTING_KEYS) {\n\t\tif (desired[key] === undefined) continue;\n\t\tif (JSON.stringify(desired[key]) !== JSON.stringify(current[key])) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\ninterface BranchConfigArgs {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}\n\nfunction diffBranchConfig(args: BranchConfigArgs): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst branchName = remote.branch.name;\n\tconst computeSettings = config.postgres?.computeSettings;\n\n\tif (computeSettings) {\n\t\tconst endpoint = remote.endpoint;\n\t\tif (!endpoint) {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: branchName,\n\t\t\t\tfield: \"endpoint\",\n\t\t\t\tcurrent: undefined,\n\t\t\t\tdesired: computeSettings,\n\t\t\t\treason: \"Branch has no read-write endpoint; cannot apply compute settings.\",\n\t\t\t});\n\t\t} else {\n\t\t\tconst drift = computeDriftBetween(computeSettings, endpoint);\n\t\t\tif (drift) {\n\t\t\t\tif (options.updateExisting) {\n\t\t\t\t\tplan.push({\n\t\t\t\t\t\tkind: \"update-endpoint\",\n\t\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\t\tbranchName,\n\t\t\t\t\t\tendpointId: endpoint.id,\n\t\t\t\t\t\tsettings: computeSettings,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tkind: \"branch\",\n\t\t\t\t\t\tidentifier: branchName,\n\t\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\t\tcurrent: drift.current,\n\t\t\t\t\t\tdesired: drift.desired,\n\t\t\t\t\t\treason: \"Existing branch has different compute settings. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (\n\t\tconfig.protected !== undefined &&\n\t\tconfig.protected !== remote.branch.protected\n\t) {\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"update-branch-protected\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName,\n\t\t\t\tprotected: config.protected,\n\t\t\t});\n\t\t} else {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: branchName,\n\t\t\t\tfield: \"protected\",\n\t\t\t\tcurrent: remote.branch.protected,\n\t\t\t\tdesired: config.protected,\n\t\t\t\treason: \"Existing branch has a different `protected` flag. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (config.ttlSeconds !== undefined) {\n\t\tconst current = remote.branch.expiresAt\n\t\t\t? Math.max(\n\t\t\t\t\t0,\n\t\t\t\t\tMath.round(\n\t\t\t\t\t\t(Date.parse(remote.branch.expiresAt) - Date.now()) /\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t: undefined;\n\t\tif (\n\t\t\tcurrent === undefined ||\n\t\t\tMath.abs(current - config.ttlSeconds) > 30\n\t\t) {\n\t\t\tconst expiresAt = new Date(\n\t\t\t\tDate.now() + config.ttlSeconds * 1000,\n\t\t\t).toISOString();\n\t\t\tif (options.updateExisting) {\n\t\t\t\tplan.push({\n\t\t\t\t\tkind: \"update-branch-ttl\",\n\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\t\tbranchName,\n\t\t\t\t\texpiresAt,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconflicts.push({\n\t\t\t\t\tkind: \"branch\",\n\t\t\t\t\tidentifier: branchName,\n\t\t\t\t\tfield: \"ttl\",\n\t\t\t\t\tcurrent: remote.branch.expiresAt,\n\t\t\t\t\tdesired: expiresAt,\n\t\t\t\t\treason: \"Existing branch has a different TTL. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction computeDriftBetween(\n\tdesired: ComputeSettings,\n\tendpoint: NeonEndpointSnapshot,\n): {\n\tcurrent: Partial<ComputeSettings>;\n\tdesired: Partial<ComputeSettings>;\n} | null {\n\tconst currentDrift: Partial<ComputeSettings> = {};\n\tconst desiredDrift: Partial<ComputeSettings> = {};\n\tlet drift = false;\n\n\tif (\n\t\tdesired.autoscalingLimitMinCu !== undefined &&\n\t\tdesired.autoscalingLimitMinCu !== endpoint.autoscalingLimitMinCu\n\t) {\n\t\tcurrentDrift.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t\tdesiredDrift.autoscalingLimitMinCu = desired.autoscalingLimitMinCu;\n\t\tdrift = true;\n\t}\n\tif (\n\t\tdesired.autoscalingLimitMaxCu !== undefined &&\n\t\tdesired.autoscalingLimitMaxCu !== endpoint.autoscalingLimitMaxCu\n\t) {\n\t\tcurrentDrift.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t\tdesiredDrift.autoscalingLimitMaxCu = desired.autoscalingLimitMaxCu;\n\t\tdrift = true;\n\t}\n\tif (\n\t\tdesired.suspendTimeout !== undefined &&\n\t\tdesired.suspendTimeout !== endpoint.suspendTimeout\n\t) {\n\t\tcurrentDrift.suspendTimeout = endpoint.suspendTimeout;\n\t\tdesiredDrift.suspendTimeout = desired.suspendTimeout;\n\t\tdrift = true;\n\t}\n\treturn drift ? { current: currentDrift, desired: desiredDrift } : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff.js","names":[],"sources":["../../src/lib/diff.ts"],"sourcesContent":["import type {\n\tEnableDataApiInput,\n\tNeonBranchSnapshot,\n\tNeonBucketSnapshot,\n\tNeonCustomDomainSnapshot,\n\tNeonEndpointSnapshot,\n\tNeonFunctionSnapshot,\n\tNeonTriggerSnapshot,\n} from \"./neon-api.js\";\nimport type {\n\tBucketAccessLevel,\n\tComputeSettings,\n\tConflictReport,\n\tDataApiSettings,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedFunctionTrigger,\n} from \"./types.js\";\n\n/**\n * A planned action to perform a single mutation against the Neon API. The diff engine\n * produces a list of these for `pushConfig` to execute (or report).\n */\nexport type PlanStep =\n\t| {\n\t\t\tkind: \"update-branch-ttl\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\texpiresAt: string | null;\n\t }\n\t| {\n\t\t\tkind: \"update-branch-protected\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tprotected: boolean;\n\t }\n\t| {\n\t\t\tkind: \"update-endpoint\";\n\t\t\tprojectId: string;\n\t\t\tbranchName: string;\n\t\t\tendpointId: string;\n\t\t\tsettings: ComputeSettings;\n\t }\n\t| {\n\t\t\tkind: \"enable-auth\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName?: string;\n\t }\n\t| {\n\t\t\tkind: \"enable-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t\t\t/** Create-time auth wiring + initial settings from the policy. */\n\t\t\tinput?: EnableDataApiInput;\n\t }\n\t| {\n\t\t\tkind: \"update-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t\t\tsettings: DataApiSettings;\n\t }\n\t| {\n\t\t\tkind: \"disable-data-api\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tdatabaseName: string;\n\t }\n\t| {\n\t\t\tkind: \"create-bucket\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tbucketName: string;\n\t\t\taccessLevel: BucketAccessLevel;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * Deploy code to a function. Planned for every desired function — deployments are\n\t\t\t * versioned and the newest becomes active, so a push ships the current source each\n\t\t\t * time. Neon has no separate \"create function\" endpoint: the first deployment to a\n\t\t\t * slug creates the function. `functionExists` therefore only drives whether this\n\t\t\t * surfaces as a `create` (first deploy) or an `update` (re-deploy).\n\t\t\t */\n\t\t\tkind: \"deploy-function\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfn: ResolvedFunctionConfig;\n\t\t\t/** Whether the function already existed remotely when the plan was computed. */\n\t\t\tfunctionExists: boolean;\n\t }\n\t| {\n\t\t\tkind: \"create-trigger\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\ttrigger: ResolvedFunctionTrigger;\n\t }\n\t| {\n\t\t\tkind: \"update-trigger\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\ttriggerId: string;\n\t\t\tfunctionSlug: string;\n\t\t\ttrigger: ResolvedFunctionTrigger;\n\t }\n\t| {\n\t\t\tkind: \"register-custom-domain\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\tdomain: string;\n\t }\n\t| {\n\t\t\tkind: \"retarget-custom-domain\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfunctionSlug: string;\n\t\t\tpreviousSlug: string;\n\t\t\tdomain: string;\n\t };\n\nexport interface RemoteServiceState {\n\tdatabaseName: string;\n\tauthEnabled: boolean;\n\tdataApiEnabled: boolean;\n\t/**\n\t * Current Data API runtime settings, when the integration is enabled and the API reports\n\t * them (SubZero only). `null`/absent means \"not reported\" — settings drift can't be\n\t * computed, so no update step is planned.\n\t */\n\tdataApiSettings?: DataApiSettings | null;\n}\n\n/**\n * Snapshot of the branch's current Preview-feature state. Absent (`undefined`) when the\n * policy has no `preview` block — `pushConfig` only fetches this when needed.\n */\nexport interface RemotePreviewState {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\ttriggers: NeonTriggerSnapshot[];\n\t/** Absent on pre-feature snapshots; treated as none. */\n\tcustomDomains?: NeonCustomDomainSnapshot[];\n}\n\nexport interface RemoteState {\n\tprojectId: string;\n\tbranch: NeonBranchSnapshot;\n\tendpoint?: NeonEndpointSnapshot;\n\tservices: RemoteServiceState;\n\tpreview?: RemotePreviewState;\n}\n\nexport interface DiffOptions {\n\t/**\n\t * Apply mutable drift on the selected branch as plan steps instead of conflicts.\n\t * Default: `false`.\n\t */\n\tupdateExisting: boolean;\n}\n\nexport interface DiffResult {\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}\n\n/**\n * Diff desired branch policy against the selected remote branch. Pure function.\n */\nexport function diffConfig(\n\tconfig: ResolvedBranchConfig,\n\tremote: RemoteState,\n\toptions: DiffOptions,\n): DiffResult {\n\tconst conflicts: ConflictReport[] = [];\n\tconst plan: PlanStep[] = [];\n\tdiffBranchConfig({ config, remote, options, plan, conflicts });\n\tdiffServices({ config, remote, options, plan, conflicts });\n\tdiffPreview({ config, remote, options, plan, conflicts });\n\treturn { plan, conflicts };\n}\n\n/**\n * Data API teardown requires explicit `dataApi: false` so omission remains non-destructive.\n *\n * The AI Gateway is intentionally NOT planned here: it is always available on a branch\n * (credential-gated, not per-branch provisioned), so `preview.aiGateway` produces no plan\n * step — it only drives the branch credential's `ai_gateway:invoke` scope and the gateway\n * env vars (`@neon/env`). There is nothing to create, and nothing to probe.\n *\n * Functions are always (re-)deployed: deployments are versioned and the newest becomes\n * active, so each push ships the current source. There is no separate create step — Neon\n * creates the function on its first deployment — so a single `deploy-function` step is\n * emitted per desired function, carrying `functionExists` so the apply can report it as a\n * create (first deploy) or an update (re-deploy).\n */\nfunction diffPreview(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst preview = config.preview;\n\tif (!preview) return;\n\t// `remote.preview` is only fetched when the policy has a preview block; treat a missing\n\t// snapshot as \"nothing exists yet\" so the diff is still well-defined.\n\tconst state: RemotePreviewState = remote.preview ?? {\n\t\tbuckets: [],\n\t\tfunctions: [],\n\t\ttriggers: [],\n\t};\n\tconst customDomains = state.customDomains ?? [];\n\n\tfor (const bucket of preview.buckets) {\n\t\tif (state.buckets.some((b) => b.name === bucket.name)) continue;\n\t\tplan.push({\n\t\t\tkind: \"create-bucket\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tbucketName: bucket.name,\n\t\t\taccessLevel: bucket.access,\n\t\t});\n\t}\n\n\tfor (const fn of preview.functions) {\n\t\tconst exists = state.functions.some((f) => f.slug === fn.slug);\n\t\tplan.push({\n\t\t\tkind: \"deploy-function\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tfn,\n\t\t\tfunctionExists: exists,\n\t\t});\n\t\tdiffFunctionCustomDomains({\n\t\t\tfn,\n\t\t\tcustomDomains,\n\t\t\tremote,\n\t\t\toptions,\n\t\t\tplan,\n\t\t\tconflicts,\n\t\t});\n\t}\n\n\tfor (const trigger of config.triggers ?? []) {\n\t\tconst remoteTrigger = state.triggers.find(\n\t\t\t(t) => t.name === trigger.name,\n\t\t);\n\t\tif (!remoteTrigger) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"create-trigger\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tfunctionSlug: trigger.functionSlug,\n\t\t\t\ttrigger,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (remoteTrigger.type !== trigger.type) {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: trigger.name,\n\t\t\t\tfield: \"trigger\",\n\t\t\t\tcurrent: remoteTrigger.type,\n\t\t\t\tdesired: trigger.type,\n\t\t\t\treason: `trigger \"${trigger.name}\" exists as type ${remoteTrigger.type}; neon.ts declares ${trigger.type}. Delete the remote trigger with \\`neon triggers delete\\` and apply again.`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (triggerNeedsUpdate(trigger, remoteTrigger)) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"update-trigger\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\ttriggerId: remoteTrigger.triggerId,\n\t\t\t\tfunctionSlug: trigger.functionSlug,\n\t\t\t\ttrigger,\n\t\t\t});\n\t\t}\n\t}\n}\n\nfunction triggerNeedsUpdate(\n\tdesired: ResolvedFunctionTrigger,\n\tremote: NeonTriggerSnapshot,\n): boolean {\n\tif (remote.functionSlug !== desired.functionSlug) return true;\n\tif (remote.functionPath !== desired.functionPath) return true;\n\tif (remote.enabled !== desired.enabled) return true;\n\tif (desired.type === \"schedule\") {\n\t\treturn remote.type !== \"schedule\" || remote.cron !== desired.cron;\n\t}\n\treturn (\n\t\tremote.type !== \"storage_object_created\" ||\n\t\tremote.bucketName !== desired.bucketName ||\n\t\tremote.prefix !== desired.prefix\n\t);\n}\n\nconst FUNCTION_CUSTOM_DOMAIN_ENTITY = \"function\";\n\nfunction diffFunctionCustomDomains(args: {\n\tfn: ResolvedFunctionConfig;\n\tcustomDomains: NeonCustomDomainSnapshot[];\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { fn, customDomains, remote, options, plan, conflicts } = args;\n\tfor (const domain of fn.customDomains ?? []) {\n\t\tconst existing = customDomains.find((d) => d.domain === domain);\n\t\tif (!existing) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"register-custom-domain\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\tdomain,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (existing.entityType !== FUNCTION_CUSTOM_DOMAIN_ENTITY) {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: remote.branch.name,\n\t\t\t\tfield: \"customDomain\",\n\t\t\t\tcurrent: {\n\t\t\t\t\tentityType: existing.entityType,\n\t\t\t\t\tentityId: existing.entityId,\n\t\t\t\t},\n\t\t\t\tdesired: {\n\t\t\t\t\tentityType: FUNCTION_CUSTOM_DOMAIN_ENTITY,\n\t\t\t\t\tentityId: fn.slug,\n\t\t\t\t},\n\t\t\t\treason: `custom domain \"${domain}\" is registered to entity type \"${existing.entityType}\", which neon.ts cannot retarget. Delete it with \\`neon function domains delete\\` or stop declaring it.`,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tif (existing.entityId === fn.slug) continue;\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"retarget-custom-domain\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tfunctionSlug: fn.slug,\n\t\t\t\tpreviousSlug: existing.entityId,\n\t\t\t\tdomain,\n\t\t\t});\n\t\t\tcontinue;\n\t\t}\n\t\tconflicts.push({\n\t\t\tkind: \"branch\",\n\t\t\tidentifier: remote.branch.name,\n\t\t\tfield: \"customDomain\",\n\t\t\tcurrent: existing.entityId,\n\t\t\tdesired: fn.slug,\n\t\t\treason: `custom domain \"${domain}\" is registered to function \"${existing.entityId}\". Pass \\`updateExisting: true\\` (SDK) or \\`--update-existing\\` (CLI) to retarget it to \"${fn.slug}\".`,\n\t\t});\n\t}\n}\n\n/**\n * Plan branch-scoped integrations. Enabling is additive (no existing resource to override).\n * The Data API is the one integration that also has a reconcilable *update*: its runtime\n * `settings` can drift once enabled, and reconciling them is an override (gated on\n * `updateExisting`, like compute/TTL/protected). The auth provider / JWKS wiring is fixed at\n * enable time, so it is never updated here. Disabling stays explicit/manual (destructive).\n */\nfunction diffServices(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst state = remote.services;\n\tif (config.authEnabled && !state.authEnabled) {\n\t\tconst step: PlanStep = {\n\t\t\tkind: \"enable-auth\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t};\n\t\tif (state.databaseName) step.databaseName = state.databaseName;\n\t\tplan.push(step);\n\t}\n\tconst dataApiPolicy =\n\t\tconfig.dataApiPolicy ?? (config.dataApiEnabled ? \"enabled\" : \"omitted\");\n\tif (dataApiPolicy === \"enabled\") {\n\t\tdiffDataApi({ config, remote, options, plan, conflicts });\n\t} else if (dataApiPolicy === \"disabled\" && state.dataApiEnabled) {\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"disable-data-api\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName: remote.branch.name,\n\t\t\t\tdatabaseName: state.databaseName,\n\t\t\t});\n\t\t} else {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: remote.branch.name,\n\t\t\t\tfield: \"dataApi\",\n\t\t\t\tcurrent: true,\n\t\t\t\tdesired: false,\n\t\t\t\treason: \"Existing Data API would be deleted. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t});\n\t\t}\n\t}\n}\n\n/**\n * Plan the Data API: a first-time **enable** (carrying the create-time auth wiring +\n * settings), or — when it already exists — a **settings update** if the policy's settings\n * drift from the remote. The update is an override: applied as a plan step under\n * `updateExisting`, otherwise reported as a conflict.\n */\nfunction diffDataApi(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst state = remote.services;\n\tconst desired = config.dataApi;\n\n\tif (!state.dataApiEnabled) {\n\t\tconst step: PlanStep = {\n\t\t\tkind: \"enable-data-api\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tdatabaseName: state.databaseName,\n\t\t};\n\t\tconst input = desired ? enableInputFromResolved(desired) : undefined;\n\t\tif (input) step.input = input;\n\t\tplan.push(step);\n\t\treturn;\n\t}\n\n\t// Already enabled: the only reconcilable change is its runtime settings.\n\tconst desiredSettings = desired?.settings;\n\tif (!desiredSettings) return;\n\tif (!dataApiSettingsDiffer(desiredSettings, state.dataApiSettings)) return;\n\n\tif (options.updateExisting) {\n\t\tplan.push({\n\t\t\tkind: \"update-data-api\",\n\t\t\tprojectId: remote.projectId,\n\t\t\tbranchId: remote.branch.id,\n\t\t\tbranchName: remote.branch.name,\n\t\t\tdatabaseName: state.databaseName,\n\t\t\tsettings: desiredSettings,\n\t\t});\n\t} else {\n\t\tconflicts.push({\n\t\t\tkind: \"branch\",\n\t\t\tidentifier: remote.branch.name,\n\t\t\tfield: \"dataApi.settings\",\n\t\t\tcurrent: state.dataApiSettings ?? undefined,\n\t\t\tdesired: desiredSettings,\n\t\t\treason: \"Existing Data API has different settings. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t});\n\t}\n}\n\n/** Build the create-time {@link EnableDataApiInput} from a resolved Data API config. */\nfunction enableInputFromResolved(\n\tresolved: ResolvedDataApiConfig,\n): EnableDataApiInput {\n\tconst input: EnableDataApiInput = { authProvider: resolved.authProvider };\n\tif (resolved.jwksUrl !== undefined) input.jwksUrl = resolved.jwksUrl;\n\tif (resolved.providerName !== undefined)\n\t\tinput.providerName = resolved.providerName;\n\tif (resolved.jwtAudience !== undefined)\n\t\tinput.jwtAudience = resolved.jwtAudience;\n\tif (resolved.settings) input.settings = resolved.settings;\n\treturn input;\n}\n\n/** The camelCase keys of {@link DataApiSettings}, used to compare desired vs remote settings. */\nconst DATA_API_SETTING_KEYS = [\n\t\"dbAggregatesEnabled\",\n\t\"dbAnonRole\",\n\t\"dbExtraSearchPath\",\n\t\"dbMaxRows\",\n\t\"dbSchemas\",\n\t\"jwtRoleClaimKey\",\n\t\"jwtCacheMaxLifetime\",\n\t\"openapiMode\",\n\t\"serverCorsAllowedOrigins\",\n\t\"serverTimingEnabled\",\n] as const satisfies ReadonlyArray<keyof DataApiSettings>;\n\n/**\n * Whether the policy's Data API `settings` differ from the remote ones. Only the keys the\n * policy actually set are compared (so unset fields never count as drift). When the remote\n * settings are not reported (`null`/absent — non-SubZero), drift can't be computed and this\n * returns `false` so no spurious update is planned.\n */\nfunction dataApiSettingsDiffer(\n\tdesired: DataApiSettings,\n\tcurrent: DataApiSettings | null | undefined,\n): boolean {\n\tif (!current) return false;\n\tfor (const key of DATA_API_SETTING_KEYS) {\n\t\tif (desired[key] === undefined) continue;\n\t\tif (JSON.stringify(desired[key]) !== JSON.stringify(current[key])) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\ninterface BranchConfigArgs {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\toptions: DiffOptions;\n\tplan: PlanStep[];\n\tconflicts: ConflictReport[];\n}\n\nfunction diffBranchConfig(args: BranchConfigArgs): void {\n\tconst { config, remote, options, plan, conflicts } = args;\n\tconst branchName = remote.branch.name;\n\tconst computeSettings = config.postgres?.computeSettings;\n\n\tif (computeSettings) {\n\t\tconst endpoint = remote.endpoint;\n\t\tif (!endpoint) {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: branchName,\n\t\t\t\tfield: \"endpoint\",\n\t\t\t\tcurrent: undefined,\n\t\t\t\tdesired: computeSettings,\n\t\t\t\treason: \"Branch has no read-write endpoint; cannot apply compute settings.\",\n\t\t\t});\n\t\t} else {\n\t\t\tconst drift = computeDriftBetween(computeSettings, endpoint);\n\t\t\tif (drift) {\n\t\t\t\tif (options.updateExisting) {\n\t\t\t\t\tplan.push({\n\t\t\t\t\t\tkind: \"update-endpoint\",\n\t\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\t\tbranchName,\n\t\t\t\t\t\tendpointId: endpoint.id,\n\t\t\t\t\t\tsettings: computeSettings,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\tkind: \"branch\",\n\t\t\t\t\t\tidentifier: branchName,\n\t\t\t\t\t\tfield: \"computeSettings\",\n\t\t\t\t\t\tcurrent: drift.current,\n\t\t\t\t\t\tdesired: drift.desired,\n\t\t\t\t\t\treason: \"Existing branch has different compute settings. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif (\n\t\tconfig.protected !== undefined &&\n\t\tconfig.protected !== remote.branch.protected\n\t) {\n\t\tif (options.updateExisting) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"update-branch-protected\",\n\t\t\t\tprojectId: remote.projectId,\n\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\tbranchName,\n\t\t\t\tprotected: config.protected,\n\t\t\t});\n\t\t} else {\n\t\t\tconflicts.push({\n\t\t\t\tkind: \"branch\",\n\t\t\t\tidentifier: branchName,\n\t\t\t\tfield: \"protected\",\n\t\t\t\tcurrent: remote.branch.protected,\n\t\t\t\tdesired: config.protected,\n\t\t\t\treason: \"Existing branch has a different `protected` flag. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t});\n\t\t}\n\t}\n\n\tif (config.ttlSeconds !== undefined) {\n\t\tconst current = remote.branch.expiresAt\n\t\t\t? Math.max(\n\t\t\t\t\t0,\n\t\t\t\t\tMath.round(\n\t\t\t\t\t\t(Date.parse(remote.branch.expiresAt) - Date.now()) /\n\t\t\t\t\t\t\t1000,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t: undefined;\n\t\tif (\n\t\t\tcurrent === undefined ||\n\t\t\tMath.abs(current - config.ttlSeconds) > 30\n\t\t) {\n\t\t\tconst expiresAt = new Date(\n\t\t\t\tDate.now() + config.ttlSeconds * 1000,\n\t\t\t).toISOString();\n\t\t\tif (options.updateExisting) {\n\t\t\t\tplan.push({\n\t\t\t\t\tkind: \"update-branch-ttl\",\n\t\t\t\t\tprojectId: remote.projectId,\n\t\t\t\t\tbranchId: remote.branch.id,\n\t\t\t\t\tbranchName,\n\t\t\t\t\texpiresAt,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tconflicts.push({\n\t\t\t\t\tkind: \"branch\",\n\t\t\t\t\tidentifier: branchName,\n\t\t\t\t\tfield: \"ttl\",\n\t\t\t\t\tcurrent: remote.branch.expiresAt,\n\t\t\t\t\tdesired: expiresAt,\n\t\t\t\t\treason: \"Existing branch has a different TTL. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply.\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction computeDriftBetween(\n\tdesired: ComputeSettings,\n\tendpoint: NeonEndpointSnapshot,\n): {\n\tcurrent: Partial<ComputeSettings>;\n\tdesired: Partial<ComputeSettings>;\n} | null {\n\tconst currentDrift: Partial<ComputeSettings> = {};\n\tconst desiredDrift: Partial<ComputeSettings> = {};\n\tlet drift = false;\n\n\tif (\n\t\tdesired.autoscalingLimitMinCu !== undefined &&\n\t\tdesired.autoscalingLimitMinCu !== endpoint.autoscalingLimitMinCu\n\t) {\n\t\tcurrentDrift.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t\tdesiredDrift.autoscalingLimitMinCu = desired.autoscalingLimitMinCu;\n\t\tdrift = true;\n\t}\n\tif (\n\t\tdesired.autoscalingLimitMaxCu !== undefined &&\n\t\tdesired.autoscalingLimitMaxCu !== endpoint.autoscalingLimitMaxCu\n\t) {\n\t\tcurrentDrift.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t\tdesiredDrift.autoscalingLimitMaxCu = desired.autoscalingLimitMaxCu;\n\t\tdrift = true;\n\t}\n\tif (\n\t\tdesired.suspendTimeout !== undefined &&\n\t\tdesired.suspendTimeout !== endpoint.suspendTimeout\n\t) {\n\t\tcurrentDrift.suspendTimeout = endpoint.suspendTimeout;\n\t\tdesiredDrift.suspendTimeout = desired.suspendTimeout;\n\t\tdrift = true;\n\t}\n\treturn drift ? { current: currentDrift, desired: desiredDrift } : null;\n}\n"],"mappings":"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"}
|
package/dist/lib/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/lib/errors.ts"],"mappings":";;;;;;AAyBA;AAuBA;AAAqB;AAAW;AAAwB;AAAS;AAYjE;AAA2B;AAGC;AAAT;AAKsB;AARN;AAAK;AAyBxC;AAaA;AAUA;AAoBA;AAeA;AAA+B;AAED;AAEG;AAJM,cAtH1B,SAsH0B,EAAA;EAAa,SAAA,aAAA,EAAA,yBAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/lib/errors.ts"],"mappings":";;;;;;AAyBA;AAuBA;AAAqB;AAAW;AAAwB;AAAS;AAYjE;AAA2B;AAGC;AAAT;AAKsB;AARN;AAAK;AAyBxC;AAaA;AAUA;AAoBA;AAeA;AAA+B;AAED;AAEG;AAJM,cAtH1B,SAsH0B,EAAA;EAAa,SAAA,aAAA,EAAA,yBAAA;EAsEvC,SAAA,cAAiB,EAAA,2BAAqB;EAsCtC,SAAA,cAAA,EAAA,0BAA8C;EAgC3C,SAAA,YAAA,EAAA,wBAEJ;EAiBC,SAAA,WAAgB,EAAA,uBAAqB;;;;;;;;;;;;;;;;;;KA9PtC,SAAA,WAAoB,wBAAwB;;;;;;;;;cAY3C,aAAA,SAAsB,KAAA;;;oBAGhB,SAAS;;;cAKa;;;;;;;;;;;;iBAiBzB,eAAA,2BAA0C;;;;;;iBAa1C,eAAA,CAAA;;;;;;;cAUH,qBAAA,SAA8B,aAAa;;;;;;;;;;;;cAoB3C,mBAAA,SAA4B,aAAa;;;;;;;;;;;cAezC,iBAAA,SAA0B,aAAA;;+BAET;kCAEG;;;;;;;;;cAkEpB,gBAAA,SAAyB,aAAa;;;;;;;;;;;;;;;;;cAsCtC,wBAAA,SAAiC,aAAa;;;;;;;;;;;;;;;;;iBAgC3C,0BAAA,2BAEJ;;;;cAiBC,eAAA,SAAwB,aAAa"}
|
package/dist/lib/errors.js
CHANGED
|
@@ -143,6 +143,7 @@ function reasonAllowsUpdateExisting(reason) {
|
|
|
143
143
|
}
|
|
144
144
|
function suggestFix(c) {
|
|
145
145
|
if (c.field === "customDomain" && !reasonAllowsUpdateExisting(c.reason)) return "delete the domain with `neon function domains delete`, or stop declaring it in neon.ts.";
|
|
146
|
+
if (c.field === "trigger") return "delete the remote trigger with `neon triggers delete`, or rename one of the two.";
|
|
146
147
|
if (isImmutableConflict(c)) return "immutable on Neon — recreate the project, or change your `neon.ts` to match the remote.";
|
|
147
148
|
if (c.kind === "branch" && c.field === "parent") return "create the parent branch on Neon first, or change the `parent` reference to an existing branch.";
|
|
148
149
|
return "pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.";
|