@neondatabase/config 1.5.1 → 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 +27 -38
- package/dist/index.d.ts +4 -3
- package/dist/index.js +2 -1
- package/dist/lib/authored-services.d.ts +36 -0
- package/dist/lib/authored-services.d.ts.map +1 -0
- package/dist/lib/authored-services.js +89 -0
- package/dist/lib/authored-services.js.map +1 -0
- package/dist/lib/define-config.d.ts +19 -14
- package/dist/lib/define-config.d.ts.map +1 -1
- package/dist/lib/define-config.js +34 -18
- 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/loader.js +6 -2
- package/dist/lib/loader.js.map +1 -1
- package/dist/lib/neon-api-real.d.ts.map +1 -1
- package/dist/lib/neon-api-real.js +74 -20
- 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 +58 -22
- package/dist/lib/schema.d.ts.map +1 -1
- package/dist/lib/schema.js +85 -44
- package/dist/lib/schema.js.map +1 -1
- package/dist/lib/types.d.ts +116 -41
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/v1.d.ts +56 -24
- package/dist/v1.d.ts.map +1 -1
- package/dist/v1.js +4 -2
- package/dist/v1.js.map +1 -1
- package/package.json +1 -1
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.";
|
package/dist/lib/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../../src/lib/errors.ts"],"sourcesContent":["import type { ConflictReport } from \"./types.js\";\n\n/**\n * Every code a {@link PlatformError} can carry. Stable identifiers — consumers can rely on\n * these for `instanceof PlatformError && err.code === ErrorCode.…` style checks instead of\n * matching on free-text messages.\n *\n * Grouped by source:\n * - `PLATFORM_INVALID_CONFIG` — `defineConfig` / `configSchema` rejected the input.\n * - `PLATFORM_MISSING_CONTEXT` — no project / branch context could be resolved.\n * - `PLATFORM_PUSH_CONFLICT` — local config conflicts with remote and the caller did not\n * opt in to apply.\n * - `PLATFORM_CONFIG_LOAD_FAILED` — `neon.ts` could not be found or evaluated.\n * - `PLATFORM_MISSING_API_KEY` — no `NEON_API_KEY` and no explicit `apiKey` was provided.\n * - `PLATFORM_MISSING_PARENT_BRANCH` — push tried to create a child of a non-existent\n * branch.\n * - `PLATFORM_PARTIAL_BRANCH_CREATE` — `createBranch` created the branch but failed to\n * apply its policy, so the branch exists without its declared settings.\n * - `PLATFORM_UNAUTHORIZED` / `PLATFORM_FORBIDDEN` / `PLATFORM_NOT_FOUND` /\n * `PLATFORM_CONFLICT` / `PLATFORM_RATE_LIMITED` / `PLATFORM_LOCKED` /\n * `PLATFORM_SERVER_ERROR` — wrappings of Neon HTTP failures.\n * - `PLATFORM_NETWORK_ERROR` — transport-level failure (no HTTP response at all).\n * - `PLATFORM_INTERNAL_ERROR` — invariant violations. Should never happen in production;\n * if you see one, please open an issue.\n */\nexport const ErrorCode = {\n\tInvalidConfig: \"PLATFORM_INVALID_CONFIG\",\n\tEnvNotInjected: \"PLATFORM_ENV_NOT_INJECTED\",\n\tMissingContext: \"PLATFORM_MISSING_CONTEXT\",\n\tPushConflict: \"PLATFORM_PUSH_CONFLICT\",\n\tPushAborted: \"PLATFORM_PUSH_ABORTED\",\n\tConfigLoadFailed: \"PLATFORM_CONFIG_LOAD_FAILED\",\n\tMissingApiKey: \"PLATFORM_MISSING_API_KEY\",\n\tAmbiguousBranchAuth: \"PLATFORM_AMBIGUOUS_BRANCH_AUTH\",\n\tBranchNotFound: \"PLATFORM_BRANCH_NOT_FOUND\",\n\tFeatureUnavailable: \"PLATFORM_FEATURE_UNAVAILABLE\",\n\tMissingParentBranch: \"PLATFORM_MISSING_PARENT_BRANCH\",\n\tPartialBranchCreate: \"PLATFORM_PARTIAL_BRANCH_CREATE\",\n\tUnauthorized: \"PLATFORM_UNAUTHORIZED\",\n\tForbidden: \"PLATFORM_FORBIDDEN\",\n\tNotFound: \"PLATFORM_NOT_FOUND\",\n\tConflict: \"PLATFORM_CONFLICT\",\n\tRateLimited: \"PLATFORM_RATE_LIMITED\",\n\tLocked: \"PLATFORM_LOCKED\",\n\tServerError: \"PLATFORM_SERVER_ERROR\",\n\tNetworkError: \"PLATFORM_NETWORK_ERROR\",\n\tInternalError: \"PLATFORM_INTERNAL_ERROR\",\n} as const;\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nconst ISSUE_URL = \"https://github.com/neondatabase/neon-pkgs/issues/new\";\n\n/**\n * Base class for all errors thrown by `@neon/config`. Always extend this so callers\n * can catch every package-thrown error with a single `instanceof` check.\n *\n * Optional `details` carries structured context that the CLI prints under `--debug` and\n * that programmatic consumers can read (e.g. `details.status` for HTTP wrappings,\n * `details.requestId` for Neon API failures).\n */\nexport class PlatformError extends Error {\n\toverride readonly name: string = \"PlatformError\";\n\treadonly code: string;\n\treadonly details: Readonly<Record<string, unknown>>;\n\n\tconstructor(\n\t\tcode: string,\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; details?: Record<string, unknown> },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.code = code;\n\t\tthis.details = Object.freeze({ ...(options?.details ?? {}) });\n\t}\n}\n\n/**\n * Structural check for a {@link PlatformError}.\n *\n * Prefer this over a bare `instanceof PlatformError` whenever the error may have crossed a\n * module-realm boundary. A `neon.ts` loaded through jiti imports its *own* copy of this\n * package, so a `PlatformError` thrown while evaluating it has a different class identity\n * than ours and fails `instanceof` — but its stable string `code` (always prefixed\n * `PLATFORM_`) survives the boundary intact, which is what we match on here.\n */\nexport function isPlatformError(value: unknown): value is PlatformError {\n\tif (value instanceof PlatformError) return true;\n\tif (typeof value !== \"object\" || value === null) return false;\n\tif (!(\"code\" in value)) return false;\n\tconst { code } = value;\n\treturn typeof code === \"string\" && code.startsWith(\"PLATFORM_\");\n}\n\n/**\n * Append a \"report-a-bug\" footer to an error message. Used only on truly unreachable\n * internal errors — never on user-facing validation / configuration errors where the user\n * is supposed to fix something on their end.\n */\nexport function bugReportFooter(): string {\n\treturn `\\nThis indicates a bug in @neon/config. Please file an issue: ${ISSUE_URL}`;\n}\n\n/**\n * Thrown by {@link defineConfig} when the user-provided configuration object is invalid.\n *\n * The class collects every validation failure rather than throwing on the first one so that\n * users get a complete picture of what is wrong with their `neon.ts`.\n */\nexport class ConfigValidationError extends PlatformError {\n\toverride readonly name = \"ConfigValidationError\";\n\treadonly issues: readonly string[];\n\n\tconstructor(issues: readonly string[]) {\n\t\tsuper(\n\t\t\t\"PLATFORM_INVALID_CONFIG\",\n\t\t\t`Invalid Neon config:\\n - ${issues.join(\"\\n - \")}`,\n\t\t);\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * Thrown when the package cannot resolve which Neon project to operate on.\n *\n * Per the package's read-only-filesystem contract, we never create a `.neon` context file;\n * callers must either pass `projectId`/`orgId` explicitly or rely on an existing context file\n * (`.neon/project.json` or neonctl's `.neon`).\n */\nexport class MissingContextError extends PlatformError {\n\toverride readonly name = \"MissingContextError\";\n\n\tconstructor(message: string) {\n\t\tsuper(\"PLATFORM_MISSING_CONTEXT\", message);\n\t}\n}\n\n/**\n * Thrown by {@link pushConfig} when it detects differences between the local config and\n * the remote project that the caller hasn't opted in to apply.\n *\n * The message lists every conflict with both the current and desired value plus a\n * per-conflict hint. Mutable branch drift is applied by passing `updateExisting: true`.\n */\nexport class PushConflictError extends PlatformError {\n\toverride readonly name = \"PushConflictError\";\n\treadonly conflicts: readonly ConflictReport[];\n\n\tconstructor(conflicts: readonly ConflictReport[]) {\n\t\tconst lines: string[] = [\n\t\t\t\"pushConfig refused to apply: local config conflicts with remote state.\",\n\t\t\t\"\",\n\t\t];\n\t\tfor (const c of conflicts) {\n\t\t\tlines.push(\n\t\t\t\t` - [${c.kind}:${c.identifier}] ${c.field}: ${c.reason}`,\n\t\t\t\t` current : ${formatValue(c.current)}`,\n\t\t\t\t` desired : ${formatValue(c.desired)}`,\n\t\t\t\t` fix : ${suggestFix(c)}`,\n\t\t\t);\n\t\t}\n\t\tconst hasOverrideable = conflicts.some(\n\t\t\t(c) =>\n\t\t\t\t!isImmutableConflict(c) &&\n\t\t\t\t(c.field !== \"customDomain\" ||\n\t\t\t\t\treasonAllowsUpdateExisting(c.reason)),\n\t\t);\n\t\tlines.push(\"\");\n\t\tif (hasOverrideable) {\n\t\t\tlines.push(\n\t\t\t\t\"For mutable conflicts, pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\",\n\t\t\t);\n\t\t}\n\n\t\tsuper(\"PLATFORM_PUSH_CONFLICT\", lines.join(\"\\n\"), {\n\t\t\tdetails: { conflicts: conflicts.map((c) => ({ ...c })) },\n\t\t});\n\t\tthis.conflicts = conflicts;\n\t}\n}\n\nfunction isImmutableConflict(_c: ConflictReport): boolean {\n\treturn false;\n}\n\n// Hostnames are user-controlled; matching /updateExisting/i classified\n// updateexisting.example.com as overrideable.\nfunction reasonAllowsUpdateExisting(reason: string): boolean {\n\treturn reason.includes(\"Pass `updateExisting: true`\");\n}\n\nfunction suggestFix(c: ConflictReport): string {\n\tif (c.field === \"customDomain\" && !reasonAllowsUpdateExisting(c.reason)) {\n\t\treturn \"delete the domain with `neon function domains delete`, or stop declaring it in neon.ts.\";\n\t}\n\tif (isImmutableConflict(c)) {\n\t\treturn \"immutable on Neon — recreate the project, or change your `neon.ts` to match the remote.\";\n\t}\n\tif (c.kind === \"branch\" && c.field === \"parent\") {\n\t\treturn \"create the parent branch on Neon first, or change the `parent` reference to an existing branch.\";\n\t}\n\treturn \"pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\";\n}\n\n/**\n * Thrown by {@link pushConfig} when the caller-supplied `confirm` callback declines a\n * push that requires confirmation (protected branch and/or mutable drift overriding\n * existing remote settings).\n *\n * The CLI maps this to a non-zero exit so users see \"aborted\" rather than a stack trace.\n */\nexport class PushAbortedError extends PlatformError {\n\toverride readonly name = \"PushAbortedError\";\n\treadonly branchName: string;\n\treadonly reasons: readonly (\"protected-branch\" | \"override-updates\")[];\n\n\tconstructor(\n\t\tbranchName: string,\n\t\treasons: readonly (\"protected-branch\" | \"override-updates\")[],\n\t) {\n\t\tsuper(\n\t\t\t\"PLATFORM_PUSH_ABORTED\",\n\t\t\t[\n\t\t\t\t`Aborted push to branch ${JSON.stringify(branchName)}.`,\n\t\t\t\treasons.length > 0\n\t\t\t\t\t? `Reason${reasons.length === 1 ? \"\" : \"s\"}: ${reasons.join(\", \")}.`\n\t\t\t\t\t: undefined,\n\t\t\t\t\"Re-run with `--update-existing` (override existing settings) or `--allow-protected-branch` (push to a protected branch) to skip the prompt.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\t{ details: { branchName, reasons: [...reasons] } },\n\t\t);\n\t\tthis.branchName = branchName;\n\t\tthis.reasons = reasons;\n\t}\n}\n\n/**\n * Thrown by `createBranch` when the branch was created but pushing its `neon.ts` policy onto\n * it failed — e.g. the API rejected a declared compute setting, or a service could not be\n * provisioned. The branch is **real**: it exists with its creation-time `parent` but without\n * the rest of its declared settings.\n *\n * The created branch's id/name are carried on the error so callers can keep it usable (pin it,\n * report it) instead of leaving a branch behind that no one knows diverges from the policy.\n * Nothing re-applies a policy to an *existing* branch implicitly, so recovering takes an\n * explicit apply with `updateExisting: true` (SDK) / `--update-existing` (CLI).\n */\nexport class PartialBranchCreateError extends PlatformError {\n\toverride readonly name = \"PartialBranchCreateError\";\n\treadonly branchId: string;\n\treadonly branchName: string;\n\t/**\n\t * Why the policy failed, taken from the underlying error — what Neon actually rejected.\n\t * Exposed separately from {@link message} so a caller that has already reported the created\n\t * branch can show just the reason.\n\t */\n\treadonly reason: string;\n\n\tconstructor(branchId: string, branchName: string, cause: unknown) {\n\t\tconst reason = messageOf(cause);\n\t\tsuper(\n\t\t\t\"PLATFORM_PARTIAL_BRANCH_CREATE\",\n\t\t\t[\n\t\t\t\t`Created branch ${JSON.stringify(branchName)} (${branchId}), but applying the neon.ts policy to it failed: ${reason}`,\n\t\t\t\t\"The branch exists and is usable, but its settings do not match the policy. Fix the cause above, then apply the policy to the existing branch with `updateExisting: true` (SDK) / `--update-existing` (CLI).\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause, details: { branchId, branchName } },\n\t\t);\n\t\tthis.branchId = branchId;\n\t\tthis.branchName = branchName;\n\t\tthis.reason = reason;\n\t}\n}\n\n/**\n * Structural check for a {@link PartialBranchCreateError}, matching on the stable `code` and\n * the branch fields rather than class identity — same realm-crossing rationale as\n * {@link isPlatformError}.\n */\nexport function isPartialBranchCreateError(\n\tvalue: unknown,\n): value is PartialBranchCreateError {\n\tif (value instanceof PartialBranchCreateError) return true;\n\tif (!isPlatformError(value)) return false;\n\tif (value.code !== ErrorCode.PartialBranchCreate) return false;\n\treturn (\n\t\t\"branchId\" in value &&\n\t\ttypeof value.branchId === \"string\" &&\n\t\t\"branchName\" in value &&\n\t\ttypeof value.branchName === \"string\" &&\n\t\t\"reason\" in value &&\n\t\ttypeof value.reason === \"string\"\n\t);\n}\n\n/**\n * Thrown when the SDK fails to find or load a `neon.ts` config file.\n */\nexport class ConfigLoadError extends PlatformError {\n\toverride readonly name = \"ConfigLoadError\";\n\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(\"PLATFORM_CONFIG_LOAD_FAILED\", message, options);\n\t}\n}\n\nfunction messageOf(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction formatValue(value: unknown): string {\n\tif (value === undefined) return \"<unset>\";\n\tif (typeof value === \"string\") return JSON.stringify(value);\n\tif (typeof value === \"object\" && value !== null)\n\t\treturn JSON.stringify(value);\n\treturn String(value);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/lib/errors.ts"],"sourcesContent":["import type { ConflictReport } from \"./types.js\";\n\n/**\n * Every code a {@link PlatformError} can carry. Stable identifiers — consumers can rely on\n * these for `instanceof PlatformError && err.code === ErrorCode.…` style checks instead of\n * matching on free-text messages.\n *\n * Grouped by source:\n * - `PLATFORM_INVALID_CONFIG` — `defineConfig` / `configSchema` rejected the input.\n * - `PLATFORM_MISSING_CONTEXT` — no project / branch context could be resolved.\n * - `PLATFORM_PUSH_CONFLICT` — local config conflicts with remote and the caller did not\n * opt in to apply.\n * - `PLATFORM_CONFIG_LOAD_FAILED` — `neon.ts` could not be found or evaluated.\n * - `PLATFORM_MISSING_API_KEY` — no `NEON_API_KEY` and no explicit `apiKey` was provided.\n * - `PLATFORM_MISSING_PARENT_BRANCH` — push tried to create a child of a non-existent\n * branch.\n * - `PLATFORM_PARTIAL_BRANCH_CREATE` — `createBranch` created the branch but failed to\n * apply its policy, so the branch exists without its declared settings.\n * - `PLATFORM_UNAUTHORIZED` / `PLATFORM_FORBIDDEN` / `PLATFORM_NOT_FOUND` /\n * `PLATFORM_CONFLICT` / `PLATFORM_RATE_LIMITED` / `PLATFORM_LOCKED` /\n * `PLATFORM_SERVER_ERROR` — wrappings of Neon HTTP failures.\n * - `PLATFORM_NETWORK_ERROR` — transport-level failure (no HTTP response at all).\n * - `PLATFORM_INTERNAL_ERROR` — invariant violations. Should never happen in production;\n * if you see one, please open an issue.\n */\nexport const ErrorCode = {\n\tInvalidConfig: \"PLATFORM_INVALID_CONFIG\",\n\tEnvNotInjected: \"PLATFORM_ENV_NOT_INJECTED\",\n\tMissingContext: \"PLATFORM_MISSING_CONTEXT\",\n\tPushConflict: \"PLATFORM_PUSH_CONFLICT\",\n\tPushAborted: \"PLATFORM_PUSH_ABORTED\",\n\tConfigLoadFailed: \"PLATFORM_CONFIG_LOAD_FAILED\",\n\tMissingApiKey: \"PLATFORM_MISSING_API_KEY\",\n\tAmbiguousBranchAuth: \"PLATFORM_AMBIGUOUS_BRANCH_AUTH\",\n\tBranchNotFound: \"PLATFORM_BRANCH_NOT_FOUND\",\n\tFeatureUnavailable: \"PLATFORM_FEATURE_UNAVAILABLE\",\n\tMissingParentBranch: \"PLATFORM_MISSING_PARENT_BRANCH\",\n\tPartialBranchCreate: \"PLATFORM_PARTIAL_BRANCH_CREATE\",\n\tUnauthorized: \"PLATFORM_UNAUTHORIZED\",\n\tForbidden: \"PLATFORM_FORBIDDEN\",\n\tNotFound: \"PLATFORM_NOT_FOUND\",\n\tConflict: \"PLATFORM_CONFLICT\",\n\tRateLimited: \"PLATFORM_RATE_LIMITED\",\n\tLocked: \"PLATFORM_LOCKED\",\n\tServerError: \"PLATFORM_SERVER_ERROR\",\n\tNetworkError: \"PLATFORM_NETWORK_ERROR\",\n\tInternalError: \"PLATFORM_INTERNAL_ERROR\",\n} as const;\nexport type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];\n\nconst ISSUE_URL = \"https://github.com/neondatabase/neon-pkgs/issues/new\";\n\n/**\n * Base class for all errors thrown by `@neon/config`. Always extend this so callers\n * can catch every package-thrown error with a single `instanceof` check.\n *\n * Optional `details` carries structured context that the CLI prints under `--debug` and\n * that programmatic consumers can read (e.g. `details.status` for HTTP wrappings,\n * `details.requestId` for Neon API failures).\n */\nexport class PlatformError extends Error {\n\toverride readonly name: string = \"PlatformError\";\n\treadonly code: string;\n\treadonly details: Readonly<Record<string, unknown>>;\n\n\tconstructor(\n\t\tcode: string,\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; details?: Record<string, unknown> },\n\t) {\n\t\tsuper(message, options);\n\t\tthis.code = code;\n\t\tthis.details = Object.freeze({ ...(options?.details ?? {}) });\n\t}\n}\n\n/**\n * Structural check for a {@link PlatformError}.\n *\n * Prefer this over a bare `instanceof PlatformError` whenever the error may have crossed a\n * module-realm boundary. A `neon.ts` loaded through jiti imports its *own* copy of this\n * package, so a `PlatformError` thrown while evaluating it has a different class identity\n * than ours and fails `instanceof` — but its stable string `code` (always prefixed\n * `PLATFORM_`) survives the boundary intact, which is what we match on here.\n */\nexport function isPlatformError(value: unknown): value is PlatformError {\n\tif (value instanceof PlatformError) return true;\n\tif (typeof value !== \"object\" || value === null) return false;\n\tif (!(\"code\" in value)) return false;\n\tconst { code } = value;\n\treturn typeof code === \"string\" && code.startsWith(\"PLATFORM_\");\n}\n\n/**\n * Append a \"report-a-bug\" footer to an error message. Used only on truly unreachable\n * internal errors — never on user-facing validation / configuration errors where the user\n * is supposed to fix something on their end.\n */\nexport function bugReportFooter(): string {\n\treturn `\\nThis indicates a bug in @neon/config. Please file an issue: ${ISSUE_URL}`;\n}\n\n/**\n * Thrown by {@link defineConfig} when the user-provided configuration object is invalid.\n *\n * The class collects every validation failure rather than throwing on the first one so that\n * users get a complete picture of what is wrong with their `neon.ts`.\n */\nexport class ConfigValidationError extends PlatformError {\n\toverride readonly name = \"ConfigValidationError\";\n\treadonly issues: readonly string[];\n\n\tconstructor(issues: readonly string[]) {\n\t\tsuper(\n\t\t\t\"PLATFORM_INVALID_CONFIG\",\n\t\t\t`Invalid Neon config:\\n - ${issues.join(\"\\n - \")}`,\n\t\t);\n\t\tthis.issues = issues;\n\t}\n}\n\n/**\n * Thrown when the package cannot resolve which Neon project to operate on.\n *\n * Per the package's read-only-filesystem contract, we never create a `.neon` context file;\n * callers must either pass `projectId`/`orgId` explicitly or rely on an existing context file\n * (`.neon/project.json` or neonctl's `.neon`).\n */\nexport class MissingContextError extends PlatformError {\n\toverride readonly name = \"MissingContextError\";\n\n\tconstructor(message: string) {\n\t\tsuper(\"PLATFORM_MISSING_CONTEXT\", message);\n\t}\n}\n\n/**\n * Thrown by {@link pushConfig} when it detects differences between the local config and\n * the remote project that the caller hasn't opted in to apply.\n *\n * The message lists every conflict with both the current and desired value plus a\n * per-conflict hint. Mutable branch drift is applied by passing `updateExisting: true`.\n */\nexport class PushConflictError extends PlatformError {\n\toverride readonly name = \"PushConflictError\";\n\treadonly conflicts: readonly ConflictReport[];\n\n\tconstructor(conflicts: readonly ConflictReport[]) {\n\t\tconst lines: string[] = [\n\t\t\t\"pushConfig refused to apply: local config conflicts with remote state.\",\n\t\t\t\"\",\n\t\t];\n\t\tfor (const c of conflicts) {\n\t\t\tlines.push(\n\t\t\t\t` - [${c.kind}:${c.identifier}] ${c.field}: ${c.reason}`,\n\t\t\t\t` current : ${formatValue(c.current)}`,\n\t\t\t\t` desired : ${formatValue(c.desired)}`,\n\t\t\t\t` fix : ${suggestFix(c)}`,\n\t\t\t);\n\t\t}\n\t\tconst hasOverrideable = conflicts.some(\n\t\t\t(c) =>\n\t\t\t\t!isImmutableConflict(c) &&\n\t\t\t\t(c.field !== \"customDomain\" ||\n\t\t\t\t\treasonAllowsUpdateExisting(c.reason)),\n\t\t);\n\t\tlines.push(\"\");\n\t\tif (hasOverrideable) {\n\t\t\tlines.push(\n\t\t\t\t\"For mutable conflicts, pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\",\n\t\t\t);\n\t\t}\n\n\t\tsuper(\"PLATFORM_PUSH_CONFLICT\", lines.join(\"\\n\"), {\n\t\t\tdetails: { conflicts: conflicts.map((c) => ({ ...c })) },\n\t\t});\n\t\tthis.conflicts = conflicts;\n\t}\n}\n\nfunction isImmutableConflict(_c: ConflictReport): boolean {\n\treturn false;\n}\n\n// Hostnames are user-controlled; matching /updateExisting/i classified\n// updateexisting.example.com as overrideable.\nfunction reasonAllowsUpdateExisting(reason: string): boolean {\n\treturn reason.includes(\"Pass `updateExisting: true`\");\n}\n\nfunction suggestFix(c: ConflictReport): string {\n\tif (c.field === \"customDomain\" && !reasonAllowsUpdateExisting(c.reason)) {\n\t\treturn \"delete the domain with `neon function domains delete`, or stop declaring it in neon.ts.\";\n\t}\n\tif (c.field === \"trigger\") {\n\t\treturn \"delete the remote trigger with `neon triggers delete`, or rename one of the two.\";\n\t}\n\tif (isImmutableConflict(c)) {\n\t\treturn \"immutable on Neon — recreate the project, or change your `neon.ts` to match the remote.\";\n\t}\n\tif (c.kind === \"branch\" && c.field === \"parent\") {\n\t\treturn \"create the parent branch on Neon first, or change the `parent` reference to an existing branch.\";\n\t}\n\treturn \"pass `updateExisting: true` (SDK) / `--update-existing` (CLI) to apply.\";\n}\n\n/**\n * Thrown by {@link pushConfig} when the caller-supplied `confirm` callback declines a\n * push that requires confirmation (protected branch and/or mutable drift overriding\n * existing remote settings).\n *\n * The CLI maps this to a non-zero exit so users see \"aborted\" rather than a stack trace.\n */\nexport class PushAbortedError extends PlatformError {\n\toverride readonly name = \"PushAbortedError\";\n\treadonly branchName: string;\n\treadonly reasons: readonly (\"protected-branch\" | \"override-updates\")[];\n\n\tconstructor(\n\t\tbranchName: string,\n\t\treasons: readonly (\"protected-branch\" | \"override-updates\")[],\n\t) {\n\t\tsuper(\n\t\t\t\"PLATFORM_PUSH_ABORTED\",\n\t\t\t[\n\t\t\t\t`Aborted push to branch ${JSON.stringify(branchName)}.`,\n\t\t\t\treasons.length > 0\n\t\t\t\t\t? `Reason${reasons.length === 1 ? \"\" : \"s\"}: ${reasons.join(\", \")}.`\n\t\t\t\t\t: undefined,\n\t\t\t\t\"Re-run with `--update-existing` (override existing settings) or `--allow-protected-branch` (push to a protected branch) to skip the prompt.\",\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\" \"),\n\t\t\t{ details: { branchName, reasons: [...reasons] } },\n\t\t);\n\t\tthis.branchName = branchName;\n\t\tthis.reasons = reasons;\n\t}\n}\n\n/**\n * Thrown by `createBranch` when the branch was created but pushing its `neon.ts` policy onto\n * it failed — e.g. the API rejected a declared compute setting, or a service could not be\n * provisioned. The branch is **real**: it exists with its creation-time `parent` but without\n * the rest of its declared settings.\n *\n * The created branch's id/name are carried on the error so callers can keep it usable (pin it,\n * report it) instead of leaving a branch behind that no one knows diverges from the policy.\n * Nothing re-applies a policy to an *existing* branch implicitly, so recovering takes an\n * explicit apply with `updateExisting: true` (SDK) / `--update-existing` (CLI).\n */\nexport class PartialBranchCreateError extends PlatformError {\n\toverride readonly name = \"PartialBranchCreateError\";\n\treadonly branchId: string;\n\treadonly branchName: string;\n\t/**\n\t * Why the policy failed, taken from the underlying error — what Neon actually rejected.\n\t * Exposed separately from {@link message} so a caller that has already reported the created\n\t * branch can show just the reason.\n\t */\n\treadonly reason: string;\n\n\tconstructor(branchId: string, branchName: string, cause: unknown) {\n\t\tconst reason = messageOf(cause);\n\t\tsuper(\n\t\t\t\"PLATFORM_PARTIAL_BRANCH_CREATE\",\n\t\t\t[\n\t\t\t\t`Created branch ${JSON.stringify(branchName)} (${branchId}), but applying the neon.ts policy to it failed: ${reason}`,\n\t\t\t\t\"The branch exists and is usable, but its settings do not match the policy. Fix the cause above, then apply the policy to the existing branch with `updateExisting: true` (SDK) / `--update-existing` (CLI).\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause, details: { branchId, branchName } },\n\t\t);\n\t\tthis.branchId = branchId;\n\t\tthis.branchName = branchName;\n\t\tthis.reason = reason;\n\t}\n}\n\n/**\n * Structural check for a {@link PartialBranchCreateError}, matching on the stable `code` and\n * the branch fields rather than class identity — same realm-crossing rationale as\n * {@link isPlatformError}.\n */\nexport function isPartialBranchCreateError(\n\tvalue: unknown,\n): value is PartialBranchCreateError {\n\tif (value instanceof PartialBranchCreateError) return true;\n\tif (!isPlatformError(value)) return false;\n\tif (value.code !== ErrorCode.PartialBranchCreate) return false;\n\treturn (\n\t\t\"branchId\" in value &&\n\t\ttypeof value.branchId === \"string\" &&\n\t\t\"branchName\" in value &&\n\t\ttypeof value.branchName === \"string\" &&\n\t\t\"reason\" in value &&\n\t\ttypeof value.reason === \"string\"\n\t);\n}\n\n/**\n * Thrown when the SDK fails to find or load a `neon.ts` config file.\n */\nexport class ConfigLoadError extends PlatformError {\n\toverride readonly name = \"ConfigLoadError\";\n\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(\"PLATFORM_CONFIG_LOAD_FAILED\", message, options);\n\t}\n}\n\nfunction messageOf(cause: unknown): string {\n\treturn cause instanceof Error ? cause.message : String(cause);\n}\n\nfunction formatValue(value: unknown): string {\n\tif (value === undefined) return \"<unset>\";\n\tif (typeof value === \"string\") return JSON.stringify(value);\n\tif (typeof value === \"object\" && value !== null)\n\t\treturn JSON.stringify(value);\n\treturn String(value);\n}\n"],"mappings":"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"}
|
package/dist/lib/loader.js
CHANGED
|
@@ -133,7 +133,7 @@ function safeIsFile(path) {
|
|
|
133
133
|
return false;
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
|
-
const UNSET_FUNCTION_ENV_ISSUE = /^preview\.functions\.[^.]+\.env\.(.+): Environment variable "\1" for function ".+" is undefined/;
|
|
136
|
+
const UNSET_FUNCTION_ENV_ISSUE = /^(?:preview\.)?functions\.[^.]+\.env\.(.+): Environment variable "\1" for function ".+" is undefined/;
|
|
137
137
|
function unsetFunctionEnvKeys(err) {
|
|
138
138
|
if (!isPlatformError(err) || err.code !== ErrorCode.InvalidConfig) return null;
|
|
139
139
|
const issues = platformErrorIssues(err);
|
|
@@ -180,7 +180,7 @@ async function withSerializedConfigLoad(run) {
|
|
|
180
180
|
return next;
|
|
181
181
|
}
|
|
182
182
|
function withoutFunctionEnvKeys(config, keys) {
|
|
183
|
-
const functions = config.preview?.functions;
|
|
183
|
+
const functions = config.functions ?? config.preview?.functions;
|
|
184
184
|
if (!functions) return config;
|
|
185
185
|
const nextFunctions = {};
|
|
186
186
|
for (const [slug, fn] of Object.entries(functions)) {
|
|
@@ -194,6 +194,10 @@ function withoutFunctionEnvKeys(config, keys) {
|
|
|
194
194
|
env
|
|
195
195
|
};
|
|
196
196
|
}
|
|
197
|
+
if (config.functions !== void 0) return Object.freeze({
|
|
198
|
+
...config,
|
|
199
|
+
functions: nextFunctions
|
|
200
|
+
});
|
|
197
201
|
return Object.freeze({
|
|
198
202
|
...config,
|
|
199
203
|
preview: {
|
package/dist/lib/loader.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loader.js","names":[],"sources":["../../src/lib/loader.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { defineConfig } from \"./define-config.js\";\nimport { ConfigLoadError, ErrorCode, isPlatformError } from \"./errors.js\";\nimport type { Config, FunctionDef } from \"./types.js\";\n\n/**\n * Default file names tried (in order) when {@link loadConfigFromFile} is called without an\n * explicit path. We accept `.ts` first because that's the documented format; `.mjs` and `.js`\n * fall out for free since jiti handles all of them.\n */\nexport const DEFAULT_CONFIG_FILENAMES = [\n\t\"neon.ts\",\n\t\"neon.mts\",\n\t\"neon.js\",\n\t\"neon.mjs\",\n] as const;\n\nexport interface LoadConfigOptions {\n\t/** Explicit absolute or cwd-relative path to a config file. Takes precedence over the search. */\n\tpath?: string;\n\t/** Starting directory for the upward search. Defaults to `process.cwd()`. */\n\tcwd?: string;\n\t/**\n\t * Hard ceiling for the upward walk — once `current === stopAt` the search returns\n\t * `null` even if no `.git` boundary was hit. Defaults to the OS home directory so\n\t * stray runs from outside any repo never leak into the user's `~` files.\n\t */\n\tstopAt?: string;\n\t/**\n\t * `\"error\"` (default) prevents deploy and dev from proceeding without\n\t * function secrets.\n\t * `\"omit\"` is for metadata-only reads because apply or deploy would lose the\n\t * omitted keys.\n\t */\n\tunsetFunctionEnv?: \"error\" | \"omit\";\n}\n\n/**\n * Load a `neon.ts` (or any other supported extension) and return the validated {@link Config}.\n *\n * Behavior:\n * - When `path` is set, that file is loaded directly. The file must exist and must default-export\n * a value produced by `defineConfig()`.\n * - When `path` is omitted, we walk up from `cwd` picking the **closest** file matching\n * {@link DEFAULT_CONFIG_FILENAMES}. The walk is monorepo-friendly: intermediate\n * `package.json` files do **not** stop it, so a single `neon.ts` lifted to the workspace\n * root keeps working when invoked from inside any sub-package. The walk terminates at the\n * first directory containing `.git`, at `stopAt`, or at the filesystem root.\n *\n * jiti is loaded lazily so that callers who pass an already-resolved `Config` to `pushConfig`\n * never pay the import cost.\n */\nexport async function loadConfigFromFile(\n\toptions: LoadConfigOptions = {},\n): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\t// The omit reload replaces process.env, which is process-global, including\n\t// across duplicate @neon/config copies. Queue loads on globalThis.\n\treturn await withSerializedConfigLoad(() =>\n\t\tloadConfigFromFileUnlocked(options),\n\t);\n}\n\nasync function loadConfigFromFileUnlocked(options: LoadConfigOptions): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tif (options.unsetFunctionEnv !== \"omit\") {\n\t\treturn await loadConfigOnce(options);\n\t}\n\n\ttry {\n\t\treturn await loadConfigOnce(options);\n\t} catch (err) {\n\t\tconst keys = unsetFunctionEnvKeys(err);\n\t\tif (keys === null) throw err;\n\t\t// The user's neon.ts may resolve another @neon/config copy, so the reload\n\t\t// input must satisfy its validation.\n\t\treturn await withPlaceholderFunctionEnv(async () => {\n\t\t\tconst loaded = await loadConfigOnce(options);\n\t\t\treturn {\n\t\t\t\t...loaded,\n\t\t\t\tconfig: withoutFunctionEnvKeys(loaded.config, new Set(keys)),\n\t\t\t};\n\t\t});\n\t}\n}\n\nasync function loadConfigOnce(options: LoadConfigOptions): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tconst resolvedPath = options.path\n\t\t? resolveExplicitPath(options.path, options.cwd)\n\t\t: findDefaultConfig(options.cwd, options.stopAt);\n\n\tif (!resolvedPath) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Could not find a Neon config file while walking up from ${resolve(options.cwd ?? process.cwd())}.`,\n\t\t\t\t`Looked for: ${DEFAULT_CONFIG_FILENAMES.join(\", \")} (stopping at the first directory with a \\`.git\\`).`,\n\t\t\t\t`Create one at your repository root (or anywhere on the path from cwd up to .git), or pass an explicit \\`configPath\\` (SDK) / \\`--config <path>\\` (CLI).`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\tlet mod: unknown;\n\ttry {\n\t\tmod = await importModule(\n\t\t\tresolvedPath,\n\t\t\toptions.unsetFunctionEnv === \"omit\",\n\t\t);\n\t} catch (cause) {\n\t\t// `defineConfig()` runs at module-eval time, so a config the user got *wrong*\n\t\t// (a bad function slug, an unknown key, an invalid duration, …) throws a\n\t\t// PlatformError from inside this import. That error already pinpoints the exact\n\t\t// field and reason, so surface it verbatim — burying it under the generic\n\t\t// \"this is usually a TypeScript syntax error\" hint sent users hunting for a\n\t\t// syntax bug that isn't there. The hint is reserved for genuine evaluation\n\t\t// failures (syntax errors, missing deps, thrown runtime exceptions).\n\t\tif (isPlatformError(cause)) {\n\t\t\tthrow cause;\n\t\t}\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Failed to evaluate ${resolvedPath}.`,\n\t\t\t\t`Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\t\"This is usually a TypeScript syntax error, a missing dependency, or a runtime exception inside the config file. Run the file directly (e.g. `npx tsx neon.ts`) to reproduce.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst exported = extractDefaultExport(mod);\n\tif (exported === undefined) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`${resolvedPath} loaded successfully but did not default-export a config.`,\n\t\t\t\t\"Add `export default defineConfig({ ... })` at the bottom of the file. (Named exports are ignored.)\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// Run through defineConfig to validate any function the user might have constructed manually.\n\tconst config = defineConfig(exported as Config);\n\treturn { config, resolvedPath };\n}\n\nfunction resolveExplicitPath(input: string, cwd?: string): string {\n\tconst base = resolve(cwd ?? process.cwd());\n\tconst abs = isAbsolute(input) ? input : resolve(base, input);\n\tif (!existsSync(abs)) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config file not found at ${abs}. The path was resolved from \\`${input}\\` against ${base}.`,\n\t\t);\n\t}\n\tconst s = statSync(abs);\n\tif (!s.isFile()) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config path ${abs} is a directory, not a file. Pass a path to the file itself (e.g. ./neon.ts).`,\n\t\t);\n\t}\n\treturn abs;\n}\n\nfunction findDefaultConfig(\n\tcwd: string | undefined,\n\tstopAt: string | undefined,\n): string | null {\n\tlet current = resolve(cwd ?? process.cwd());\n\tconst stop = resolve(stopAt ?? homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tfor (const name of DEFAULT_CONFIG_FILENAMES) {\n\t\t\tconst candidate = resolve(current, name);\n\t\t\tif (existsSync(candidate) && safeIsFile(candidate))\n\t\t\t\treturn candidate;\n\t\t}\n\n\t\t// `.git` is the canonical repo-root marker. `package.json` is deliberately *not*\n\t\t// a stop: monorepos lift `neon.ts` above sub-package package.jsons.\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\t\tif (current === stop) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nasync function importModule(\n\tabsPath: string,\n\tforceJiti = false,\n): Promise<unknown> {\n\tconst lower = absPath.toLowerCase();\n\tconst needsJiti =\n\t\tforceJiti ||\n\t\tlower.endsWith(\".ts\") ||\n\t\tlower.endsWith(\".mts\") ||\n\t\tlower.endsWith(\".cts\");\n\n\tif (!needsJiti) {\n\t\treturn import(pathToFileURL(absPath).href);\n\t}\n\n\tconst jitiModule: unknown = await import(\"jiti\");\n\tconst createJiti = extractCreateJiti(jitiModule);\n\tif (!createJiti) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t\"jiti is required to load TypeScript config files but could not be initialised.\",\n\t\t\t\t\"Reinstall the package dependencies (`pnpm install` / `npm install`) — jiti is a runtime dependency of @neon/config.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\tconst jiti = createJiti(pathToFileURL(absPath).href, {\n\t\tinteropDefault: true,\n\t\tmoduleCache: false,\n\t});\n\treturn jiti.import(absPath);\n}\n\nfunction extractCreateJiti(\n\tmod: unknown,\n): ((id: string, options?: unknown) => JitiInstance) | null {\n\tif (mod === null || typeof mod !== \"object\") return null;\n\tconst obj = mod as Record<string, unknown>;\n\tif (typeof obj.createJiti === \"function\") {\n\t\treturn obj.createJiti as (\n\t\t\tid: string,\n\t\t\toptions?: unknown,\n\t\t) => JitiInstance;\n\t}\n\tconst def = obj.default;\n\tif (def !== null && typeof def === \"object\") {\n\t\tconst defObj = def as Record<string, unknown>;\n\t\tif (typeof defObj.createJiti === \"function\") {\n\t\t\treturn defObj.createJiti as (\n\t\t\t\tid: string,\n\t\t\t\toptions?: unknown,\n\t\t\t) => JitiInstance;\n\t\t}\n\t}\n\treturn null;\n}\n\ninterface JitiInstance {\n\timport(id: string): Promise<unknown>;\n}\n\nfunction extractDefaultExport(mod: unknown): unknown {\n\tif (mod === null || typeof mod !== \"object\") return mod;\n\tconst obj = mod as Record<string, unknown>;\n\tif (\"default\" in obj && obj.default !== undefined) return obj.default;\n\t// No `default` export. If the module itself is a function, treat it as the config —\n\t// that lets tests and advanced users skip the wrapper.\n\t// Otherwise, return `undefined` so the caller surfaces a clear ConfigLoadError.\n\tif (typeof mod === \"function\") return mod;\n\treturn undefined;\n}\n\nfunction safeIsFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst UNSET_FUNCTION_ENV_ISSUE =\n\t/^preview\\.functions\\.[^.]+\\.env\\.(.+): Environment variable \"\\1\" for function \".+\" is undefined/;\n\nfunction unsetFunctionEnvKeys(err: unknown): string[] | null {\n\tif (!isPlatformError(err) || err.code !== ErrorCode.InvalidConfig) {\n\t\treturn null;\n\t}\n\tconst issues = platformErrorIssues(err);\n\tif (issues === null || issues.length === 0) return null;\n\tconst keys: string[] = [];\n\tfor (const issue of issues) {\n\t\tconst match = UNSET_FUNCTION_ENV_ISSUE.exec(issue);\n\t\tconst key = match?.[1];\n\t\tif (key === undefined) return null;\n\t\tkeys.push(key);\n\t}\n\treturn keys;\n}\n\nfunction platformErrorIssues(err: unknown): readonly string[] | null {\n\tif (typeof err !== \"object\" || err === null || !(\"issues\" in err)) {\n\t\treturn null;\n\t}\n\tconst { issues } = err;\n\tif (\n\t\t!Array.isArray(issues) ||\n\t\tissues.some((issue) => typeof issue !== \"string\")\n\t) {\n\t\treturn null;\n\t}\n\treturn issues;\n}\n\nasync function withPlaceholderFunctionEnv<T>(\n\trun: () => Promise<T>,\n): Promise<T> {\n\t// Validation reports config keys, not source process.env names, so the reload\n\t// must cover every unset lookup.\n\tconst original = process.env;\n\tconst proxy = new Proxy(original, {\n\t\tget(target, prop, receiver) {\n\t\t\tif (typeof prop === \"symbol\") {\n\t\t\t\treturn Reflect.get(target, prop, receiver);\n\t\t\t}\n\t\t\tconst value = Reflect.get(target, prop, receiver);\n\t\t\treturn value === undefined ? \"\" : value;\n\t\t},\n\t});\n\tprocess.env = proxy;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tprocess.env = original;\n\t}\n}\n\nconst CONFIG_LOAD_LOCK = Symbol.for(\"@neon/config loadConfigFromFile lock\");\n\ntype ConfigLoadLock = { chain: Promise<unknown> };\n\nfunction configLoadLock(): ConfigLoadLock {\n\tconst g = globalThis as typeof globalThis & {\n\t\t[CONFIG_LOAD_LOCK]?: ConfigLoadLock;\n\t};\n\tconst lock = g[CONFIG_LOAD_LOCK] ?? { chain: Promise.resolve() };\n\tg[CONFIG_LOAD_LOCK] = lock;\n\treturn lock;\n}\n\nasync function withSerializedConfigLoad<T>(run: () => Promise<T>): Promise<T> {\n\tconst lock = configLoadLock();\n\tconst next = lock.chain.then(run, run);\n\tlock.chain = next.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn next;\n}\n\nfunction withoutFunctionEnvKeys(\n\tconfig: Config,\n\tkeys: ReadonlySet<string>,\n): Config {\n\tconst functions = config.preview?.functions;\n\tif (!functions) return config;\n\tconst nextFunctions: Record<string, FunctionDef> = {};\n\tfor (const [slug, fn] of Object.entries(functions)) {\n\t\tif (!fn.env) {\n\t\t\tnextFunctions[slug] = fn;\n\t\t\tcontinue;\n\t\t}\n\t\tconst env = Object.fromEntries(\n\t\t\tObject.entries(fn.env).filter(([key]) => !keys.has(key)),\n\t\t);\n\t\tnextFunctions[slug] = { ...fn, env };\n\t}\n\treturn Object.freeze({\n\t\t...config,\n\t\tpreview: { ...config.preview, functions: nextFunctions },\n\t});\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loader.js","names":[],"sources":["../../src/lib/loader.ts"],"sourcesContent":["import { existsSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { defineConfig } from \"./define-config.js\";\nimport { ConfigLoadError, ErrorCode, isPlatformError } from \"./errors.js\";\nimport type { Config, FunctionDef } from \"./types.js\";\n\n/**\n * Default file names tried (in order) when {@link loadConfigFromFile} is called without an\n * explicit path. We accept `.ts` first because that's the documented format; `.mjs` and `.js`\n * fall out for free since jiti handles all of them.\n */\nexport const DEFAULT_CONFIG_FILENAMES = [\n\t\"neon.ts\",\n\t\"neon.mts\",\n\t\"neon.js\",\n\t\"neon.mjs\",\n] as const;\n\nexport interface LoadConfigOptions {\n\t/** Explicit absolute or cwd-relative path to a config file. Takes precedence over the search. */\n\tpath?: string;\n\t/** Starting directory for the upward search. Defaults to `process.cwd()`. */\n\tcwd?: string;\n\t/**\n\t * Hard ceiling for the upward walk — once `current === stopAt` the search returns\n\t * `null` even if no `.git` boundary was hit. Defaults to the OS home directory so\n\t * stray runs from outside any repo never leak into the user's `~` files.\n\t */\n\tstopAt?: string;\n\t/**\n\t * `\"error\"` (default) prevents deploy and dev from proceeding without\n\t * function secrets.\n\t * `\"omit\"` is for metadata-only reads because apply or deploy would lose the\n\t * omitted keys.\n\t */\n\tunsetFunctionEnv?: \"error\" | \"omit\";\n}\n\n/**\n * Load a `neon.ts` (or any other supported extension) and return the validated {@link Config}.\n *\n * Behavior:\n * - When `path` is set, that file is loaded directly. The file must exist and must default-export\n * a value produced by `defineConfig()`.\n * - When `path` is omitted, we walk up from `cwd` picking the **closest** file matching\n * {@link DEFAULT_CONFIG_FILENAMES}. The walk is monorepo-friendly: intermediate\n * `package.json` files do **not** stop it, so a single `neon.ts` lifted to the workspace\n * root keeps working when invoked from inside any sub-package. The walk terminates at the\n * first directory containing `.git`, at `stopAt`, or at the filesystem root.\n *\n * jiti is loaded lazily so that callers who pass an already-resolved `Config` to `pushConfig`\n * never pay the import cost.\n */\nexport async function loadConfigFromFile(\n\toptions: LoadConfigOptions = {},\n): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\t// The omit reload replaces process.env, which is process-global, including\n\t// across duplicate @neon/config copies. Queue loads on globalThis.\n\treturn await withSerializedConfigLoad(() =>\n\t\tloadConfigFromFileUnlocked(options),\n\t);\n}\n\nasync function loadConfigFromFileUnlocked(options: LoadConfigOptions): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tif (options.unsetFunctionEnv !== \"omit\") {\n\t\treturn await loadConfigOnce(options);\n\t}\n\n\ttry {\n\t\treturn await loadConfigOnce(options);\n\t} catch (err) {\n\t\tconst keys = unsetFunctionEnvKeys(err);\n\t\tif (keys === null) throw err;\n\t\t// The user's neon.ts may resolve another @neon/config copy, so the reload\n\t\t// input must satisfy its validation.\n\t\treturn await withPlaceholderFunctionEnv(async () => {\n\t\t\tconst loaded = await loadConfigOnce(options);\n\t\t\treturn {\n\t\t\t\t...loaded,\n\t\t\t\tconfig: withoutFunctionEnvKeys(loaded.config, new Set(keys)),\n\t\t\t};\n\t\t});\n\t}\n}\n\nasync function loadConfigOnce(options: LoadConfigOptions): Promise<{\n\tconfig: Config;\n\tresolvedPath: string;\n}> {\n\tconst resolvedPath = options.path\n\t\t? resolveExplicitPath(options.path, options.cwd)\n\t\t: findDefaultConfig(options.cwd, options.stopAt);\n\n\tif (!resolvedPath) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Could not find a Neon config file while walking up from ${resolve(options.cwd ?? process.cwd())}.`,\n\t\t\t\t`Looked for: ${DEFAULT_CONFIG_FILENAMES.join(\", \")} (stopping at the first directory with a \\`.git\\`).`,\n\t\t\t\t`Create one at your repository root (or anywhere on the path from cwd up to .git), or pass an explicit \\`configPath\\` (SDK) / \\`--config <path>\\` (CLI).`,\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\tlet mod: unknown;\n\ttry {\n\t\tmod = await importModule(\n\t\t\tresolvedPath,\n\t\t\toptions.unsetFunctionEnv === \"omit\",\n\t\t);\n\t} catch (cause) {\n\t\t// `defineConfig()` runs at module-eval time, so a config the user got *wrong*\n\t\t// (a bad function slug, an unknown key, an invalid duration, …) throws a\n\t\t// PlatformError from inside this import. That error already pinpoints the exact\n\t\t// field and reason, so surface it verbatim — burying it under the generic\n\t\t// \"this is usually a TypeScript syntax error\" hint sent users hunting for a\n\t\t// syntax bug that isn't there. The hint is reserved for genuine evaluation\n\t\t// failures (syntax errors, missing deps, thrown runtime exceptions).\n\t\tif (isPlatformError(cause)) {\n\t\t\tthrow cause;\n\t\t}\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`Failed to evaluate ${resolvedPath}.`,\n\t\t\t\t`Underlying error: ${cause instanceof Error ? cause.message : String(cause)}`,\n\t\t\t\t\"This is usually a TypeScript syntax error, a missing dependency, or a runtime exception inside the config file. Run the file directly (e.g. `npx tsx neon.ts`) to reproduce.\",\n\t\t\t].join(\"\\n\"),\n\t\t\t{ cause },\n\t\t);\n\t}\n\n\tconst exported = extractDefaultExport(mod);\n\tif (exported === undefined) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t`${resolvedPath} loaded successfully but did not default-export a config.`,\n\t\t\t\t\"Add `export default defineConfig({ ... })` at the bottom of the file. (Named exports are ignored.)\",\n\t\t\t].join(\"\\n\"),\n\t\t);\n\t}\n\n\t// Run through defineConfig to validate any function the user might have constructed manually.\n\tconst config = defineConfig(exported as Config);\n\treturn { config, resolvedPath };\n}\n\nfunction resolveExplicitPath(input: string, cwd?: string): string {\n\tconst base = resolve(cwd ?? process.cwd());\n\tconst abs = isAbsolute(input) ? input : resolve(base, input);\n\tif (!existsSync(abs)) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config file not found at ${abs}. The path was resolved from \\`${input}\\` against ${base}.`,\n\t\t);\n\t}\n\tconst s = statSync(abs);\n\tif (!s.isFile()) {\n\t\tthrow new ConfigLoadError(\n\t\t\t`Config path ${abs} is a directory, not a file. Pass a path to the file itself (e.g. ./neon.ts).`,\n\t\t);\n\t}\n\treturn abs;\n}\n\nfunction findDefaultConfig(\n\tcwd: string | undefined,\n\tstopAt: string | undefined,\n): string | null {\n\tlet current = resolve(cwd ?? process.cwd());\n\tconst stop = resolve(stopAt ?? homedir());\n\tlet lastSeen: string | null = null;\n\n\twhile (true) {\n\t\tfor (const name of DEFAULT_CONFIG_FILENAMES) {\n\t\t\tconst candidate = resolve(current, name);\n\t\t\tif (existsSync(candidate) && safeIsFile(candidate))\n\t\t\t\treturn candidate;\n\t\t}\n\n\t\t// `.git` is the canonical repo-root marker. `package.json` is deliberately *not*\n\t\t// a stop: monorepos lift `neon.ts` above sub-package package.jsons.\n\t\tif (existsSync(resolve(current, \".git\"))) return null;\n\t\tif (current === stop) return null;\n\n\t\tconst parent = dirname(current);\n\t\tif (parent === current || parent === lastSeen) return null;\n\t\tlastSeen = current;\n\t\tcurrent = parent;\n\t}\n}\n\nasync function importModule(\n\tabsPath: string,\n\tforceJiti = false,\n): Promise<unknown> {\n\tconst lower = absPath.toLowerCase();\n\tconst needsJiti =\n\t\tforceJiti ||\n\t\tlower.endsWith(\".ts\") ||\n\t\tlower.endsWith(\".mts\") ||\n\t\tlower.endsWith(\".cts\");\n\n\tif (!needsJiti) {\n\t\treturn import(pathToFileURL(absPath).href);\n\t}\n\n\tconst jitiModule: unknown = await import(\"jiti\");\n\tconst createJiti = extractCreateJiti(jitiModule);\n\tif (!createJiti) {\n\t\tthrow new ConfigLoadError(\n\t\t\t[\n\t\t\t\t\"jiti is required to load TypeScript config files but could not be initialised.\",\n\t\t\t\t\"Reinstall the package dependencies (`pnpm install` / `npm install`) — jiti is a runtime dependency of @neon/config.\",\n\t\t\t].join(\" \"),\n\t\t);\n\t}\n\tconst jiti = createJiti(pathToFileURL(absPath).href, {\n\t\tinteropDefault: true,\n\t\tmoduleCache: false,\n\t});\n\treturn jiti.import(absPath);\n}\n\nfunction extractCreateJiti(\n\tmod: unknown,\n): ((id: string, options?: unknown) => JitiInstance) | null {\n\tif (mod === null || typeof mod !== \"object\") return null;\n\tconst obj = mod as Record<string, unknown>;\n\tif (typeof obj.createJiti === \"function\") {\n\t\treturn obj.createJiti as (\n\t\t\tid: string,\n\t\t\toptions?: unknown,\n\t\t) => JitiInstance;\n\t}\n\tconst def = obj.default;\n\tif (def !== null && typeof def === \"object\") {\n\t\tconst defObj = def as Record<string, unknown>;\n\t\tif (typeof defObj.createJiti === \"function\") {\n\t\t\treturn defObj.createJiti as (\n\t\t\t\tid: string,\n\t\t\t\toptions?: unknown,\n\t\t\t) => JitiInstance;\n\t\t}\n\t}\n\treturn null;\n}\n\ninterface JitiInstance {\n\timport(id: string): Promise<unknown>;\n}\n\nfunction extractDefaultExport(mod: unknown): unknown {\n\tif (mod === null || typeof mod !== \"object\") return mod;\n\tconst obj = mod as Record<string, unknown>;\n\tif (\"default\" in obj && obj.default !== undefined) return obj.default;\n\t// No `default` export. If the module itself is a function, treat it as the config —\n\t// that lets tests and advanced users skip the wrapper.\n\t// Otherwise, return `undefined` so the caller surfaces a clear ConfigLoadError.\n\tif (typeof mod === \"function\") return mod;\n\treturn undefined;\n}\n\nfunction safeIsFile(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isFile();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nconst UNSET_FUNCTION_ENV_ISSUE =\n\t/^(?:preview\\.)?functions\\.[^.]+\\.env\\.(.+): Environment variable \"\\1\" for function \".+\" is undefined/;\n\nfunction unsetFunctionEnvKeys(err: unknown): string[] | null {\n\tif (!isPlatformError(err) || err.code !== ErrorCode.InvalidConfig) {\n\t\treturn null;\n\t}\n\tconst issues = platformErrorIssues(err);\n\tif (issues === null || issues.length === 0) return null;\n\tconst keys: string[] = [];\n\tfor (const issue of issues) {\n\t\tconst match = UNSET_FUNCTION_ENV_ISSUE.exec(issue);\n\t\tconst key = match?.[1];\n\t\tif (key === undefined) return null;\n\t\tkeys.push(key);\n\t}\n\treturn keys;\n}\n\nfunction platformErrorIssues(err: unknown): readonly string[] | null {\n\tif (typeof err !== \"object\" || err === null || !(\"issues\" in err)) {\n\t\treturn null;\n\t}\n\tconst { issues } = err;\n\tif (\n\t\t!Array.isArray(issues) ||\n\t\tissues.some((issue) => typeof issue !== \"string\")\n\t) {\n\t\treturn null;\n\t}\n\treturn issues;\n}\n\nasync function withPlaceholderFunctionEnv<T>(\n\trun: () => Promise<T>,\n): Promise<T> {\n\t// Validation reports config keys, not source process.env names, so the reload\n\t// must cover every unset lookup.\n\tconst original = process.env;\n\tconst proxy = new Proxy(original, {\n\t\tget(target, prop, receiver) {\n\t\t\tif (typeof prop === \"symbol\") {\n\t\t\t\treturn Reflect.get(target, prop, receiver);\n\t\t\t}\n\t\t\tconst value = Reflect.get(target, prop, receiver);\n\t\t\treturn value === undefined ? \"\" : value;\n\t\t},\n\t});\n\tprocess.env = proxy;\n\ttry {\n\t\treturn await run();\n\t} finally {\n\t\tprocess.env = original;\n\t}\n}\n\nconst CONFIG_LOAD_LOCK = Symbol.for(\"@neon/config loadConfigFromFile lock\");\n\ntype ConfigLoadLock = { chain: Promise<unknown> };\n\nfunction configLoadLock(): ConfigLoadLock {\n\tconst g = globalThis as typeof globalThis & {\n\t\t[CONFIG_LOAD_LOCK]?: ConfigLoadLock;\n\t};\n\tconst lock = g[CONFIG_LOAD_LOCK] ?? { chain: Promise.resolve() };\n\tg[CONFIG_LOAD_LOCK] = lock;\n\treturn lock;\n}\n\nasync function withSerializedConfigLoad<T>(run: () => Promise<T>): Promise<T> {\n\tconst lock = configLoadLock();\n\tconst next = lock.chain.then(run, run);\n\tlock.chain = next.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn next;\n}\n\nfunction withoutFunctionEnvKeys(\n\tconfig: Config,\n\tkeys: ReadonlySet<string>,\n): Config {\n\tconst functions = config.functions ?? config.preview?.functions;\n\tif (!functions) return config;\n\tconst nextFunctions: Record<string, FunctionDef> = {};\n\tfor (const [slug, fn] of Object.entries(functions)) {\n\t\tif (!fn.env) {\n\t\t\tnextFunctions[slug] = fn;\n\t\t\tcontinue;\n\t\t}\n\t\tconst env = Object.fromEntries(\n\t\t\tObject.entries(fn.env).filter(([key]) => !keys.has(key)),\n\t\t);\n\t\tnextFunctions[slug] = { ...fn, env };\n\t}\n\tif (config.functions !== undefined) {\n\t\treturn Object.freeze({\n\t\t\t...config,\n\t\t\tfunctions: nextFunctions,\n\t\t});\n\t}\n\treturn Object.freeze({\n\t\t...config,\n\t\tpreview: { ...config.preview, functions: nextFunctions },\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"neon-api-real.d.ts","names":[],"sources":["../../src/lib/neon-api-real.ts"],"mappings":";;;UA6UU,uBAAA;;EAAA,aAAA,CAAA,EAAA,MAAA;AAeV;AA4CC;AAeD;AAAmC;AAChB;AAAR;AACF,iBA7DO,iBAAA,CA6DP,OAAA,EAAA;EACE,MAAA,EAAA,MAAA;EAAR,OAAA,CAAA,EAAA,MAAA;EAAO;AA6BV;AAAwC;AAGjB;AAAjB;EACK,aAAA,CAAA,EAAA;IAAR,WAAA,CAAA,EAAA,MAAA;IAAO,cAAA,CAAA,EAAA,MAAA;IAu0CM,UAAA,CAAA,EAAA,MAAA;EAgGA,CAAA;AAuDhB,CAAA,CAAA,EAhjDI,OAgjDY;AA6DhB,UA5kDU,WAAA,CA4kDM;EAoBA,WAAA,EAAA,MAAA;EAAuB,cAAA,EAAA,MAAA;EAAQ,UAAA,EAAA,MAAA;AAAsB;AAAQ;AAwB7E;AAAkC;AAAM;AAAW;AAAO;;iBA3mDpC,2BACX,QAAQ,YACV,cACN,QAAQ;iBA6BW,iEAGhB;SAAiB;;KACpB,QAAQ;;;;;;;;;;;;iBAu0CK,2BAAA;;;;;iBAgGA,6BAAA;;;;;;;;;;;;iBAuDA,uBAAA;iBA6DA,uBAAA;;IAEZ;;;;;;;;;;;;iBAkBY,uBAAA,QAA+B,sBAAsB;;;;;;;;;;;iBAwB/C,YAAA,MAAkB,WAAW"}
|
|
@@ -125,8 +125,22 @@ const scheduleTriggerSchema = z.object({
|
|
|
125
125
|
inherited: z.boolean(),
|
|
126
126
|
next_run_at: z.string().nullable()
|
|
127
127
|
});
|
|
128
|
-
const
|
|
129
|
-
|
|
128
|
+
const storageObjectCreatedTriggerSchema = z.object({
|
|
129
|
+
type: z.literal("storage_object_created"),
|
|
130
|
+
trigger_id: z.string(),
|
|
131
|
+
function_slug: z.string(),
|
|
132
|
+
name: z.string(),
|
|
133
|
+
function_path: z.string(),
|
|
134
|
+
storage_object_created: z.object({
|
|
135
|
+
bucket_name: z.string(),
|
|
136
|
+
prefix: z.string().optional()
|
|
137
|
+
}),
|
|
138
|
+
enabled: z.boolean(),
|
|
139
|
+
inherited: z.boolean()
|
|
140
|
+
});
|
|
141
|
+
const triggerApiSchema = z.discriminatedUnion("type", [scheduleTriggerSchema, storageObjectCreatedTriggerSchema]);
|
|
142
|
+
const triggerResponseSchema = z.object({ trigger: triggerApiSchema });
|
|
143
|
+
const triggersListResponseSchema = z.object({ triggers: z.array(triggerApiSchema) });
|
|
130
144
|
const customDomainApiSchema = z.object({
|
|
131
145
|
domain: z.string(),
|
|
132
146
|
entity_type: z.string(),
|
|
@@ -703,14 +717,7 @@ var RealNeonApi = class {
|
|
|
703
717
|
async createBranchTrigger(projectId, branchId, input) {
|
|
704
718
|
try {
|
|
705
719
|
return await this.call(`createBranchTrigger(${projectId}/${branchId}/${input.name})`, async () => {
|
|
706
|
-
const data = await this.postJson(triggersPath(projectId, branchId),
|
|
707
|
-
type: "schedule",
|
|
708
|
-
name: input.name,
|
|
709
|
-
function_slug: input.functionSlug,
|
|
710
|
-
schedule: { cron: input.cron },
|
|
711
|
-
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
712
|
-
...input.enabled !== void 0 ? { enabled: input.enabled } : {}
|
|
713
|
-
});
|
|
720
|
+
const data = await this.postJson(triggersPath(projectId, branchId), createTriggerRequestBody(input));
|
|
714
721
|
return triggerToSnapshot(triggerResponseSchema.parse(data).trigger);
|
|
715
722
|
}, {
|
|
716
723
|
projectId,
|
|
@@ -723,14 +730,7 @@ var RealNeonApi = class {
|
|
|
723
730
|
async updateBranchTrigger(projectId, branchId, triggerId, input) {
|
|
724
731
|
try {
|
|
725
732
|
return await this.call(`updateBranchTrigger(${projectId}/${branchId}/${triggerId})`, async () => {
|
|
726
|
-
const data = await this.patchJson(`${triggersPath(projectId, branchId)}/${encodeURIComponent(triggerId)}`,
|
|
727
|
-
type: "schedule",
|
|
728
|
-
...input.name !== void 0 ? { name: input.name } : {},
|
|
729
|
-
...input.functionSlug !== void 0 ? { function_slug: input.functionSlug } : {},
|
|
730
|
-
...input.cron !== void 0 ? { schedule: { cron: input.cron } } : {},
|
|
731
|
-
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
732
|
-
...input.enabled !== void 0 ? { enabled: input.enabled } : {}
|
|
733
|
-
});
|
|
733
|
+
const data = await this.patchJson(`${triggersPath(projectId, branchId)}/${encodeURIComponent(triggerId)}`, updateTriggerRequestBody(input));
|
|
734
734
|
return triggerToSnapshot(triggerResponseSchema.parse(data).trigger);
|
|
735
735
|
}, {
|
|
736
736
|
projectId,
|
|
@@ -880,7 +880,8 @@ function customDomainToSnapshot(data) {
|
|
|
880
880
|
};
|
|
881
881
|
}
|
|
882
882
|
function triggerToSnapshot(data) {
|
|
883
|
-
return {
|
|
883
|
+
if (data.type === "schedule") return {
|
|
884
|
+
type: "schedule",
|
|
884
885
|
triggerId: data.trigger_id,
|
|
885
886
|
name: data.name,
|
|
886
887
|
functionSlug: data.function_slug,
|
|
@@ -890,6 +891,59 @@ function triggerToSnapshot(data) {
|
|
|
890
891
|
inherited: data.inherited,
|
|
891
892
|
nextRunAt: data.next_run_at
|
|
892
893
|
};
|
|
894
|
+
return {
|
|
895
|
+
type: "storage_object_created",
|
|
896
|
+
triggerId: data.trigger_id,
|
|
897
|
+
name: data.name,
|
|
898
|
+
functionSlug: data.function_slug,
|
|
899
|
+
functionPath: data.function_path,
|
|
900
|
+
bucketName: data.storage_object_created.bucket_name,
|
|
901
|
+
...data.storage_object_created.prefix !== void 0 ? { prefix: data.storage_object_created.prefix } : {},
|
|
902
|
+
enabled: data.enabled,
|
|
903
|
+
inherited: data.inherited
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
function createTriggerRequestBody(input) {
|
|
907
|
+
if (input.type === "storage_object_created") return {
|
|
908
|
+
type: "storage_object_created",
|
|
909
|
+
name: input.name,
|
|
910
|
+
function_slug: input.functionSlug,
|
|
911
|
+
storage_object_created: {
|
|
912
|
+
bucket_name: input.bucketName,
|
|
913
|
+
...input.prefix !== void 0 ? { prefix: input.prefix } : {}
|
|
914
|
+
},
|
|
915
|
+
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
916
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {}
|
|
917
|
+
};
|
|
918
|
+
return {
|
|
919
|
+
type: "schedule",
|
|
920
|
+
name: input.name,
|
|
921
|
+
function_slug: input.functionSlug,
|
|
922
|
+
schedule: { cron: input.cron },
|
|
923
|
+
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
924
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {}
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
function updateTriggerRequestBody(input) {
|
|
928
|
+
if (input.type === "storage_object_created") return {
|
|
929
|
+
type: "storage_object_created",
|
|
930
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
931
|
+
...input.functionSlug !== void 0 ? { function_slug: input.functionSlug } : {},
|
|
932
|
+
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
933
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {},
|
|
934
|
+
...input.bucketName !== void 0 ? { storage_object_created: {
|
|
935
|
+
bucket_name: input.bucketName,
|
|
936
|
+
...input.prefix !== void 0 ? { prefix: input.prefix } : {}
|
|
937
|
+
} } : {}
|
|
938
|
+
};
|
|
939
|
+
return {
|
|
940
|
+
type: "schedule",
|
|
941
|
+
...input.name !== void 0 ? { name: input.name } : {},
|
|
942
|
+
...input.functionSlug !== void 0 ? { function_slug: input.functionSlug } : {},
|
|
943
|
+
...input.cron !== void 0 ? { schedule: { cron: input.cron } } : {},
|
|
944
|
+
...input.functionPath !== void 0 ? { function_path: input.functionPath } : {},
|
|
945
|
+
...input.enabled !== void 0 ? { enabled: input.enabled } : {}
|
|
946
|
+
};
|
|
893
947
|
}
|
|
894
948
|
function createCredentialToSnapshot(data) {
|
|
895
949
|
const snapshot = {
|
|
@@ -1076,7 +1130,7 @@ function previewUnavailableError(err, featureLabel) {
|
|
|
1076
1130
|
return new PlatformError(ErrorCode.FeatureUnavailable, [
|
|
1077
1131
|
`${featureLabel} isn't available for this Neon project${apiContext}.`,
|
|
1078
1132
|
platformFeatureUnavailableHint(status, neonMessage),
|
|
1079
|
-
"If you don't need it, remove the corresponding feature from
|
|
1133
|
+
"If you don't need it, remove the corresponding feature from your neon.ts and re-run."
|
|
1080
1134
|
].join(" "), {
|
|
1081
1135
|
cause: err,
|
|
1082
1136
|
details: {
|