@neon/config-runtime 1.10.5 → 1.11.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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BucketAccessLevel, Config, NeonApi, NeonBranchSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonEndpointSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRealtimeSnapshot } from "@neon/config";
|
|
1
|
+
import { BucketAccessLevel, Config, DataApiInput, NeonApi, NeonBranchSnapshot, NeonBucketSnapshot, NeonCredentialMeta, NeonDataApiSnapshot, NeonEndpointSnapshot, NeonFunctionSnapshot, NeonProjectSnapshot, NeonRealtimeSnapshot } from "@neon/config";
|
|
2
2
|
|
|
3
3
|
// src/lib/pull-config.d.ts
|
|
4
4
|
interface PullConfigOptions {
|
|
@@ -77,6 +77,14 @@ interface PulledBranchConfig {
|
|
|
77
77
|
preview?: PulledPreview;
|
|
78
78
|
}
|
|
79
79
|
declare function pullConfig(options: PullConfigOptions): Promise<PulledBranchConfig>;
|
|
80
|
+
/**
|
|
81
|
+
* The `dataApi` a pulled policy declares for a live Data API. The provider is fixed when the
|
|
82
|
+
* Data API is created, so a bare `true` (Neon Auth) is only right when that is what the
|
|
83
|
+
* branch has; writing it over an external-IdP Data API would make the next apply enable Neon
|
|
84
|
+
* Auth. When the API does not report the provider (older Data APIs), the branch's Neon Auth
|
|
85
|
+
* state is the best available answer: with Neon Auth off, the Data API cannot be using it.
|
|
86
|
+
*/
|
|
87
|
+
declare function pulledDataApiConfig(dataApi: NeonDataApiSnapshot, authEnabled: boolean): DataApiInput;
|
|
80
88
|
declare function buildPulledBranchConfig(project: NeonProjectSnapshot, branch: NeonBranchSnapshot, branches: NeonBranchSnapshot[], endpoint: NeonEndpointSnapshot | undefined, previewState?: {
|
|
81
89
|
buckets: NeonBucketSnapshot[];
|
|
82
90
|
functions: NeonFunctionSnapshot[];
|
|
@@ -85,8 +93,14 @@ declare function buildPulledBranchConfig(project: NeonProjectSnapshot, branch: N
|
|
|
85
93
|
authEnabled?: boolean;
|
|
86
94
|
/** Whether a Neon Data API integration is enabled on the branch. */
|
|
87
95
|
dataApiEnabled?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The live Data API, when enabled. Its auth provider decides the shape of the emitted
|
|
98
|
+
* `dataApi` (see {@link pulledDataApiConfig}); with only `dataApiEnabled` the policy
|
|
99
|
+
* falls back to a bare `true`.
|
|
100
|
+
*/
|
|
101
|
+
dataApi?: NeonDataApiSnapshot | null;
|
|
88
102
|
/** Realtime state when the adapter supports the branch endpoint. */
|
|
89
103
|
realtime?: NeonRealtimeSnapshot | null;
|
|
90
104
|
}): PulledBranchConfig;
|
|
91
|
-
export { PullConfigOptions, PulledBranchConfig, PulledPreview, buildPulledBranchConfig, pullConfig };
|
|
105
|
+
export { PullConfigOptions, PulledBranchConfig, PulledPreview, buildPulledBranchConfig, pullConfig, pulledDataApiConfig };
|
|
92
106
|
//# sourceMappingURL=pull-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-config.d.ts","names":["BucketAccessLevel","Config","NeonApi","NeonBranchSnapshot","NeonBucketSnapshot","NeonCredentialMeta","NeonEndpointSnapshot","NeonFunctionSnapshot","NeonProjectSnapshot","NeonRealtimeSnapshot","PullConfigOptions","PulledPreview","Array","PulledBranchConfig","pullConfig","Promise","buildPulledBranchConfig","BucketAccessLevel","Config","NeonApi","NeonBranchSnapshot","NeonBucketSnapshot","NeonCredentialMeta","NeonEndpointSnapshot","NeonFunctionSnapshot","NeonProjectSnapshot","NeonRealtimeSnapshot","PullConfigOptions","projectId","branchId","apiKey","apiHost","api","realtime","PulledPreview","buckets","Array","name","access","functions","slug","credentials","PulledBranchConfig","project","id","region","pgVersion","orgId","branch","parent","isDefault","protected","expiresAt","config","preview","pullConfig","options","Promise","buildPulledBranchConfig","branches","endpoint","previewState","
|
|
1
|
+
{"version":3,"file":"pull-config.d.ts","names":["BucketAccessLevel","Config","DataApiInput","NeonApi","NeonBranchSnapshot","NeonBucketSnapshot","NeonCredentialMeta","NeonDataApiSnapshot","NeonEndpointSnapshot","NeonFunctionSnapshot","NeonProjectSnapshot","NeonRealtimeSnapshot","PullConfigOptions","PulledPreview","Array","PulledBranchConfig","pullConfig","Promise","pulledDataApiConfig","buildPulledBranchConfig","BucketAccessLevel","Config","DataApiInput","NeonApi","NeonBranchSnapshot","NeonBucketSnapshot","NeonCredentialMeta","NeonDataApiSnapshot","NeonEndpointSnapshot","NeonFunctionSnapshot","NeonProjectSnapshot","NeonRealtimeSnapshot","PullConfigOptions","projectId","branchId","apiKey","apiHost","api","realtime","PulledPreview","buckets","Array","name","access","functions","slug","credentials","PulledBranchConfig","project","id","region","pgVersion","orgId","branch","parent","isDefault","protected","expiresAt","config","preview","pullConfig","options","Promise","pulledDataApiConfig","dataApi","authEnabled","buildPulledBranchConfig","branches","endpoint","previewState","dataApiEnabled"],"sources":["../../src/lib/pull-config.ts"],"mappings":";;;A,UAsBiBY,iBAAAA,C;;EAAAA,SAAAA,EAAAA,MAAAA;EAqBAC;EAAaqB,QAAA,EAAA,MAAA;EACUlC;EAA9Bc,MAAAA,CAAAA,EAAAA,MAAAA;EACEA;EAOER,OAAAA,CAAAA,EAAAA,MAAAA;EAAkB;EAGfS,GAAAA,CAAAA,EAvBVZ,OAuBUY;EAAkB;EA8B1Bd,QAAAA,CAAAA,EAAAA,OAAAA;AAMEY;AAAa;AAGxB;AAAgC;AACtBD;AACCG;AAARE;AAAO,UArDOJ,aAAAA,CAqDP;EAkGMK,OAAAA,EAtJNJ,KAsJMI,CAAAA;IAAmBwB,IAAA,EAAA,MAAA;IACzBnC,MAAAA,EAvJ8BP,iBAuJ9BO;EAEPL,CAAAA,CAAAA;EAAY0C,SAAA,EAxJH9B,KAwJG,CAAA;IAkFCK,IAAAA,EAAAA,MAAAA;IAAuBuB,IAAA,EAAA,MAAA;EAC7BhC,CAAAA,CAAAA;EACDN;AACEA;AACAI;AAEAH;AACEI;AACGH;EAUJC,WAAAA,EArPED,kBAqPFC,EAAAA;AAECI;AAEVI,UAtPcA,kBAAAA,CAsPdA;EAAkBiC,OAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;E,QAxNZ/C,M;;;;;;E,UAMEY,a;;A,iBAGWG,UAAAA,C,SACZJ,iB,GACPK,O,CAAQF,kB;;;;;;;;A,iBAkGKG,mBAAAA,C,SACNX,mB,yBAEPL,Y;A,iBAkFaiB,uBAAAA,C,SACNT,mB,UACDN,kB,YACEA,kB,cACAI,oB;E,SAEAH,kB;E,WACEI,oB;E,cACGH,kB;;;;;;;;;;E,UAUJC,mB;;E,WAECI,oB;A,IAEVI,kB"}
|
package/dist/lib/pull-config.js
CHANGED
|
@@ -31,10 +31,32 @@ async function pullConfig(options) {
|
|
|
31
31
|
credentials,
|
|
32
32
|
authEnabled: auth !== null,
|
|
33
33
|
dataApiEnabled: dataApi !== null,
|
|
34
|
+
dataApi,
|
|
34
35
|
realtime
|
|
35
36
|
});
|
|
36
37
|
}
|
|
37
38
|
/**
|
|
39
|
+
* The `dataApi` a pulled policy declares for a live Data API. The provider is fixed when the
|
|
40
|
+
* Data API is created, so a bare `true` (Neon Auth) is only right when that is what the
|
|
41
|
+
* branch has; writing it over an external-IdP Data API would make the next apply enable Neon
|
|
42
|
+
* Auth. When the API does not report the provider (older Data APIs), the branch's Neon Auth
|
|
43
|
+
* state is the best available answer: with Neon Auth off, the Data API cannot be using it.
|
|
44
|
+
*/
|
|
45
|
+
function pulledDataApiConfig(dataApi, authEnabled) {
|
|
46
|
+
const external = () => {
|
|
47
|
+
const config = { authProvider: "external" };
|
|
48
|
+
if (dataApi.jwksUrl !== void 0) config.jwksUrl = dataApi.jwksUrl;
|
|
49
|
+
if (dataApi.providerName !== void 0) config.providerName = dataApi.providerName;
|
|
50
|
+
if (dataApi.jwtAudience !== void 0) config.jwtAudience = dataApi.jwtAudience;
|
|
51
|
+
return config;
|
|
52
|
+
};
|
|
53
|
+
switch (dataApi.authProvider) {
|
|
54
|
+
case "neon_auth": return true;
|
|
55
|
+
case "external": return external();
|
|
56
|
+
default: return authEnabled ? true : external();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
38
60
|
* Starts a read now and keeps an unread rejection from crashing the process; the caller
|
|
39
61
|
* awaits it (and sees the error) when its turn comes. Also turns a synchronous throw from
|
|
40
62
|
* a custom adapter into a rejection.
|
|
@@ -92,9 +114,11 @@ function buildPulledBranchConfig(project, branch, branches, endpoint, previewSta
|
|
|
92
114
|
const policyPreview = {};
|
|
93
115
|
if (previewState?.realtime?.enabled) policyPreview.realtime = true;
|
|
94
116
|
if (Object.keys(bucketDefs).length > 0) policyPreview.buckets = bucketDefs;
|
|
117
|
+
const authEnabled = previewState?.authEnabled === true;
|
|
118
|
+
const dataApi = previewState?.dataApi ? pulledDataApiConfig(previewState.dataApi, authEnabled) : previewState?.dataApiEnabled ? true : void 0;
|
|
95
119
|
const config = {
|
|
96
|
-
...
|
|
97
|
-
...
|
|
120
|
+
...authEnabled ? { auth: true } : {},
|
|
121
|
+
...dataApi !== void 0 ? { dataApi } : {},
|
|
98
122
|
...Object.keys(policyPreview).length > 0 ? { preview: policyPreview } : {},
|
|
99
123
|
...Object.keys(tuning).length > 0 ? { branch: () => tuning } : {}
|
|
100
124
|
};
|
|
@@ -155,6 +179,6 @@ function endpointToComputeSettings(endpoint, project) {
|
|
|
155
179
|
if (endpoint.suspendTimeout !== void 0 && endpoint.suspendTimeout !== defaults?.suspendTimeout) out.suspendTimeout = endpoint.suspendTimeout;
|
|
156
180
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
157
181
|
}
|
|
158
|
-
export { buildPulledBranchConfig, pullConfig };
|
|
182
|
+
export { buildPulledBranchConfig, pullConfig, pulledDataApiConfig };
|
|
159
183
|
|
|
160
184
|
//# sourceMappingURL=pull-config.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull-config.js","names":[],"sources":["../../src/lib/pull-config.ts"],"sourcesContent":["import {\n\ttype BranchTuning,\n\ttype BucketAccessLevel,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonBucketSnapshot,\n\ttype NeonCredentialMeta,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonEndpointSnapshot,\n\ttype NeonFunctionSnapshot,\n\ttype NeonProjectSnapshot,\n\ttype NeonRealtimeSnapshot,\n\tPlatformError,\n} from \"@neon/config\";\n\nexport interface PullConfigOptions {\n\t/** Neon project id (`<project>`). Required — the API addresses branches by project. */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). Required. Resolve names to ids before calling. */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n\t/** Read the branch's Realtime state. Defaults to `true`. */\n\trealtime?: boolean;\n}\n\n/**\n * Live Preview-feature state read back from a branch. Surfaced alongside `config` rather\n * than inside it because functions cannot round-trip: the remote only knows the deployed\n * bundle, not the local `source` path a {@link FunctionConfig} requires, so a pulled\n * function is reported as `{ slug, name }` (no `source`).\n */\nexport interface PulledPreview {\n\tbuckets: Array<{ name: string; access: BucketAccessLevel }>;\n\tfunctions: Array<{ slug: string; name: string }>;\n\t/**\n\t * Secret-free metadata for the credentials issued on the branch (Preview). Surfaced so\n\t * `config status` can show issued credentials (id, scopes, last used) without ever\n\t * exposing the one-time `api_token` / `s3_secret_access_key`. Empty when none / the\n\t * credentials endpoint is unavailable for the project.\n\t */\n\tcredentials: NeonCredentialMeta[];\n}\n\nexport interface PulledBranchConfig {\n\tproject: {\n\t\tid: string;\n\t\tname: string;\n\t\tregion: string;\n\t\tpgVersion: number;\n\t\torgId?: string;\n\t};\n\tbranch: {\n\t\tid: string;\n\t\tname: string;\n\t\tparent?: string;\n\t\tisDefault: boolean;\n\t\tprotected: boolean;\n\t\texpiresAt?: string;\n\t};\n\t/**\n\t * The branch's live state expressed as a {@link Config}: static `auth` / `dataApi`\n\t * toggles, Realtime under `preview.realtime`, any object-storage `preview.buckets`, plus\n\t * a `branch` closure carrying the\n\t * branch's lifecycle/compute tuning. Buckets are included here (not just in\n\t * {@link PulledBranchConfig.preview}) because they round-trip cleanly — name + access\n\t * level is all a {@link Config} needs — so the no-policy env path (`neon dev` /\n\t * `neon env pull`) can resolve the branch's storage credentials without a local\n\t * `neon.ts`. Preview **functions** are reported only in\n\t * {@link PulledBranchConfig.preview}: they cannot round-trip (the remote has no local\n\t * `source` path). The AI Gateway is omitted from both — it has no branch-level \"enabled\"\n\t * state to read back (it is always available, credential-gated), so `pullConfig` cannot\n\t * tell whether a policy would enable it.\n\t */\n\tconfig: Config;\n\t/**\n\t * Live Preview-feature state, when the branch has any buckets/functions or issued\n\t * credentials. Omitted entirely when there is nothing to report. The AI Gateway is not\n\t * included: it is always available (credential-gated), not per-branch state.\n\t */\n\tpreview?: PulledPreview;\n}\n\nexport async function pullConfig(\n\toptions: PullConfigOptions,\n): Promise<PulledBranchConfig> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\tconst branchId = options.branchId;\n\tconst getRealtime =\n\t\toptions.realtime === false\n\t\t\t? undefined\n\t\t\t: api.getProjectBranchRealtime?.bind(api);\n\n\t// The first three stages need only the project and branch ids, so they start together.\n\t// They are awaited in the order they used to run, so the first error reported is the same\n\t// one as before (project, then branches/endpoints, then the branch lookup, then databases).\n\tconst projectRead = started(() => api.getProject(projectId));\n\tconst listsRead = started(() =>\n\t\tPromise.all([\n\t\t\tstarted(() => api.listBranches(projectId)),\n\t\t\tstarted(() => api.listEndpoints(projectId)),\n\t\t]),\n\t);\n\t// Data API is enabled per branch + database, so resolve a database to probe.\n\tconst databasesRead = started(() =>\n\t\tapi.listBranchDatabases(projectId, branchId),\n\t);\n\t// Preview reads degrade to \"none / disabled\" when the feature isn't available for the\n\t// project/region. `pullConfig` mirrors the branch for env resolution (`neon dev`,\n\t// `neon env pull`) and `inspect` — an unavailable Preview feature should not break those\n\t// (env comes from auth/dataApi/postgres). `pushConfig` is the place that fails on an\n\t// unavailable feature, and only when the policy declares it.\n\t// The group starts together once databases resolve (the Data API probe needs one), as\n\t// before, so which member's error comes first is unchanged.\n\tconst previewRead = started(() =>\n\t\tdatabasesRead.then((databases) => {\n\t\t\tconst probeDatabase = pickProbeDatabase(databases);\n\t\t\treturn Promise.all([\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listBranchBuckets(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listBranchFunctions(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listCredentials(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() => api.getNeonAuth(projectId, branchId)),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tprobeDatabase\n\t\t\t\t\t\t? api.getNeonDataApi(projectId, branchId, probeDatabase)\n\t\t\t\t\t\t: Promise.resolve(null),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tgetRealtime\n\t\t\t\t\t\t? degradeUnavailable(\n\t\t\t\t\t\t\t\t() => getRealtime(projectId, branchId),\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: Promise.resolve(null),\n\t\t\t\t),\n\t\t\t]);\n\t\t}),\n\t);\n\n\tconst project = await projectRead;\n\tconst [branches, endpoints] = await listsRead;\n\tconst branch = resolveBranch(branchId, branches);\n\tconst endpoint = endpoints.find(\n\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t);\n\tawait databasesRead;\n\tconst [buckets, functions, credentials, auth, dataApi, realtime] =\n\t\tawait previewRead;\n\n\treturn buildPulledBranchConfig(project, branch, branches, endpoint, {\n\t\tbuckets,\n\t\tfunctions,\n\t\tcredentials,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t\trealtime,\n\t});\n}\n\n/**\n * Starts a read now and keeps an unread rejection from crashing the process; the caller\n * awaits it (and sees the error) when its turn comes. Also turns a synchronous throw from\n * a custom adapter into a rejection.\n */\nfunction started<T>(read: () => Promise<T>): Promise<T> {\n\tconst promise = Promise.resolve().then(read);\n\tpromise.catch(() => undefined);\n\treturn promise;\n}\n\n/**\n * Pick the database to probe for a Data API integration. Data API is enabled per\n * branch + database; for read-back we only need to know whether *any* database has it\n * on, so prefer the conventional default (`neondb`) and otherwise fall back to the first\n * database. Returns `undefined` when the branch has no databases.\n */\nfunction pickProbeDatabase(\n\tdatabases: NeonDatabaseSnapshot[],\n): string | undefined {\n\tif (databases.length === 0) return undefined;\n\tconst byName = databases.find((d) => d.name === \"neondb\");\n\tif (byName) return byName.name;\n\treturn databases[0].name;\n}\n\n/**\n * Run a Preview-feature read, returning `fallback` if the feature is unavailable for the\n * project/region. Most adapters report {@link ErrorCode.FeatureUnavailable}; API hosts where\n * a route is not deployed can instead return a generic 404. The project and branch are\n * validated independently by `pullConfig`, so that route-level 404 is also safe to degrade.\n * Other errors propagate. This lets a branch without a Preview feature still mirror cleanly\n * for env resolution / `inspect`, rather than aborting on an unrelated capability.\n */\nasync function degradeUnavailable<T>(\n\tread: () => Promise<T>,\n\tfallback: T,\n): Promise<T> {\n\ttry {\n\t\treturn await read();\n\t} catch (err) {\n\t\tif (\n\t\t\terr instanceof PlatformError &&\n\t\t\t(err.code === ErrorCode.FeatureUnavailable ||\n\t\t\t\t(err.code === ErrorCode.NotFound && err.details.status === 404))\n\t\t) {\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nfunction createApiFromOptions(options: PullConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pullConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nexport function buildPulledBranchConfig(\n\tproject: NeonProjectSnapshot,\n\tbranch: NeonBranchSnapshot,\n\tbranches: NeonBranchSnapshot[],\n\tendpoint: NeonEndpointSnapshot | undefined,\n\tpreviewState?: {\n\t\tbuckets: NeonBucketSnapshot[];\n\t\tfunctions: NeonFunctionSnapshot[];\n\t\tcredentials?: NeonCredentialMeta[];\n\t\t/** Whether a Neon Auth integration is enabled on the branch. */\n\t\tauthEnabled?: boolean;\n\t\t/** Whether a Neon Data API integration is enabled on the branch. */\n\t\tdataApiEnabled?: boolean;\n\t\t/** Realtime state when the adapter supports the branch endpoint. */\n\t\trealtime?: NeonRealtimeSnapshot | null;\n\t},\n): PulledBranchConfig {\n\tconst parent = branch.parentId\n\t\t? branches.find((b) => b.id === branch.parentId)\n\t\t: undefined;\n\t// Auth and Data API are static top-level toggles, while Realtime is static under\n\t// `preview.realtime`. A config pulled from a branch with them enabled round-trips through\n\t// `resolveConfig` / `fetchEnv` and the matching secrets get injected. Branch\n\t// lifecycle/compute is per-branch tuning, so it goes in the `branch` closure.\n\tconst tuning: BranchTuning = {};\n\tif (parent) tuning.parent = parent.name;\n\t// Deliberately NOT emitting `ttl` from `branch.expiresAt`: policy `ttl` is a\n\t// creation-time *duration*, while `expiresAt` is an absolute timestamp. Feeding the\n\t// timestamp through `parseDuration` (in `resolveConfig`) would throw, breaking\n\t// `fetchEnv` / `neon dev` / `neon env pull` for any branch that has a TTL. The expiry\n\t// is reported faithfully on `branch.expiresAt` above.\n\tif (branch.protected) tuning.protected = true;\n\tif (endpoint) {\n\t\tconst compute = endpointToComputeSettings(endpoint, project);\n\t\tif (compute) tuning.postgres = { computeSettings: compute };\n\t}\n\t// Buckets round-trip cleanly (name + access level), so they belong in the config the\n\t// env path resolves from — this is what lets `neon dev` / `neon env pull` inject the\n\t// branch's object-storage credentials (AWS_*) with no local neon.ts. Functions are\n\t// deliberately NOT emitted here (they can't round-trip: the remote has no `source`\n\t// path) and neither is the AI Gateway (no branch-level enabled state to detect, only a\n\t// credential) — both would otherwise turn a read-back into a bogus policy declaration.\n\tconst bucketDefs: Record<string, { access: BucketAccessLevel }> = {};\n\tfor (const b of previewState?.buckets ?? []) {\n\t\tbucketDefs[b.name] = { access: b.accessLevel };\n\t}\n\tconst policyPreview: NonNullable<Config[\"preview\"]> = {};\n\tif (previewState?.realtime?.enabled) policyPreview.realtime = true;\n\tif (Object.keys(bucketDefs).length > 0) policyPreview.buckets = bucketDefs;\n\tconst config: Config = {\n\t\t...(previewState?.authEnabled ? { auth: true } : {}),\n\t\t...(previewState?.dataApiEnabled ? { dataApi: true } : {}),\n\t\t...(Object.keys(policyPreview).length > 0\n\t\t\t? { preview: policyPreview }\n\t\t\t: {}),\n\t\t...(Object.keys(tuning).length > 0 ? { branch: () => tuning } : {}),\n\t};\n\tconst result: PulledBranchConfig = {\n\t\tproject: {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tregion: project.regionId,\n\t\t\tpgVersion: project.pgVersion,\n\t\t\t...(project.orgId ? { orgId: project.orgId } : {}),\n\t\t},\n\t\tbranch: {\n\t\t\tid: branch.id,\n\t\t\tname: branch.name,\n\t\t\t...(parent ? { parent: parent.name } : {}),\n\t\t\tisDefault: branch.isDefault,\n\t\t\tprotected: branch.protected,\n\t\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t\t},\n\t\tconfig,\n\t};\n\tconst preview = previewState ? buildPulledPreview(previewState) : undefined;\n\tif (preview) result.preview = preview;\n\treturn result;\n}\n\n/**\n * Reverse-engineer the {@link PulledPreview} from remote snapshots. Returns `undefined` when\n * the branch has no Preview features so the field can be omitted entirely.\n */\nfunction buildPulledPreview(state: {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\tcredentials?: NeonCredentialMeta[];\n}): PulledPreview | undefined {\n\tconst credentials = state.credentials ?? [];\n\tif (\n\t\tstate.buckets.length === 0 &&\n\t\tstate.functions.length === 0 &&\n\t\tcredentials.length === 0\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tbuckets: state.buckets.map((b) => ({\n\t\t\tname: b.name,\n\t\t\taccess: b.accessLevel,\n\t\t})),\n\t\tfunctions: state.functions.map((f) => ({\n\t\t\tslug: f.slug,\n\t\t\tname: f.name,\n\t\t})),\n\t\tcredentials,\n\t};\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pullConfig: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction endpointToComputeSettings(\n\tendpoint: NeonEndpointSnapshot,\n\tproject: NeonProjectSnapshot,\n): ComputeSettings | undefined {\n\tconst defaults = project.defaultEndpointSettings;\n\tconst out: ComputeSettings = {};\n\tif (\n\t\tendpoint.autoscalingLimitMinCu !== undefined &&\n\t\tendpoint.autoscalingLimitMinCu !== defaults?.autoscalingLimitMinCu\n\t) {\n\t\tout.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t}\n\tif (\n\t\tendpoint.autoscalingLimitMaxCu !== undefined &&\n\t\tendpoint.autoscalingLimitMaxCu !== defaults?.autoscalingLimitMaxCu\n\t) {\n\t\tout.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t}\n\tif (\n\t\tendpoint.suspendTimeout !== undefined &&\n\t\tendpoint.suspendTimeout !== defaults?.suspendTimeout\n\t) {\n\t\tout.suspendTimeout = endpoint.suspendTimeout;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pull-config.js","names":[],"sources":["../../src/lib/pull-config.ts"],"sourcesContent":["import {\n\ttype BranchTuning,\n\ttype BucketAccessLevel,\n\ttype ComputeSettings,\n\ttype Config,\n\tcreateNeonApiFromOptions,\n\ttype DataApiExternalAuthConfig,\n\ttype DataApiInput,\n\tErrorCode,\n\ttype NeonApi,\n\ttype NeonBranchSnapshot,\n\ttype NeonBucketSnapshot,\n\ttype NeonCredentialMeta,\n\ttype NeonDataApiSnapshot,\n\ttype NeonDatabaseSnapshot,\n\ttype NeonEndpointSnapshot,\n\ttype NeonFunctionSnapshot,\n\ttype NeonProjectSnapshot,\n\ttype NeonRealtimeSnapshot,\n\tPlatformError,\n} from \"@neon/config\";\n\nexport interface PullConfigOptions {\n\t/** Neon project id (`<project>`). Required — the API addresses branches by project. */\n\tprojectId: string;\n\t/** Neon branch id (`br-…`). Required. Resolve names to ids before calling. */\n\tbranchId: string;\n\t/** Neon API key. Falls back to `NEON_API_KEY` / neonctl credentials. */\n\tapiKey?: string;\n\t/** Neon API base URL. Falls back to `NEON_API_HOST`, then production. */\n\tapiHost?: string;\n\t/** Inject a custom NeonApi adapter (primarily for tests). */\n\tapi?: NeonApi;\n\t/** Read the branch's Realtime state. Defaults to `true`. */\n\trealtime?: boolean;\n}\n\n/**\n * Live Preview-feature state read back from a branch. Surfaced alongside `config` rather\n * than inside it because functions cannot round-trip: the remote only knows the deployed\n * bundle, not the local `source` path a {@link FunctionConfig} requires, so a pulled\n * function is reported as `{ slug, name }` (no `source`).\n */\nexport interface PulledPreview {\n\tbuckets: Array<{ name: string; access: BucketAccessLevel }>;\n\tfunctions: Array<{ slug: string; name: string }>;\n\t/**\n\t * Secret-free metadata for the credentials issued on the branch (Preview). Surfaced so\n\t * `config status` can show issued credentials (id, scopes, last used) without ever\n\t * exposing the one-time `api_token` / `s3_secret_access_key`. Empty when none / the\n\t * credentials endpoint is unavailable for the project.\n\t */\n\tcredentials: NeonCredentialMeta[];\n}\n\nexport interface PulledBranchConfig {\n\tproject: {\n\t\tid: string;\n\t\tname: string;\n\t\tregion: string;\n\t\tpgVersion: number;\n\t\torgId?: string;\n\t};\n\tbranch: {\n\t\tid: string;\n\t\tname: string;\n\t\tparent?: string;\n\t\tisDefault: boolean;\n\t\tprotected: boolean;\n\t\texpiresAt?: string;\n\t};\n\t/**\n\t * The branch's live state expressed as a {@link Config}: static `auth` / `dataApi`\n\t * toggles, Realtime under `preview.realtime`, any object-storage `preview.buckets`, plus\n\t * a `branch` closure carrying the\n\t * branch's lifecycle/compute tuning. Buckets are included here (not just in\n\t * {@link PulledBranchConfig.preview}) because they round-trip cleanly — name + access\n\t * level is all a {@link Config} needs — so the no-policy env path (`neon dev` /\n\t * `neon env pull`) can resolve the branch's storage credentials without a local\n\t * `neon.ts`. Preview **functions** are reported only in\n\t * {@link PulledBranchConfig.preview}: they cannot round-trip (the remote has no local\n\t * `source` path). The AI Gateway is omitted from both — it has no branch-level \"enabled\"\n\t * state to read back (it is always available, credential-gated), so `pullConfig` cannot\n\t * tell whether a policy would enable it.\n\t */\n\tconfig: Config;\n\t/**\n\t * Live Preview-feature state, when the branch has any buckets/functions or issued\n\t * credentials. Omitted entirely when there is nothing to report. The AI Gateway is not\n\t * included: it is always available (credential-gated), not per-branch state.\n\t */\n\tpreview?: PulledPreview;\n}\n\nexport async function pullConfig(\n\toptions: PullConfigOptions,\n): Promise<PulledBranchConfig> {\n\tconst api = options.api ?? createApiFromOptions(options);\n\tconst projectId = options.projectId;\n\tconst branchId = options.branchId;\n\tconst getRealtime =\n\t\toptions.realtime === false\n\t\t\t? undefined\n\t\t\t: api.getProjectBranchRealtime?.bind(api);\n\n\t// The first three stages need only the project and branch ids, so they start together.\n\t// They are awaited in the order they used to run, so the first error reported is the same\n\t// one as before (project, then branches/endpoints, then the branch lookup, then databases).\n\tconst projectRead = started(() => api.getProject(projectId));\n\tconst listsRead = started(() =>\n\t\tPromise.all([\n\t\t\tstarted(() => api.listBranches(projectId)),\n\t\t\tstarted(() => api.listEndpoints(projectId)),\n\t\t]),\n\t);\n\t// Data API is enabled per branch + database, so resolve a database to probe.\n\tconst databasesRead = started(() =>\n\t\tapi.listBranchDatabases(projectId, branchId),\n\t);\n\t// Preview reads degrade to \"none / disabled\" when the feature isn't available for the\n\t// project/region. `pullConfig` mirrors the branch for env resolution (`neon dev`,\n\t// `neon env pull`) and `inspect` — an unavailable Preview feature should not break those\n\t// (env comes from auth/dataApi/postgres). `pushConfig` is the place that fails on an\n\t// unavailable feature, and only when the policy declares it.\n\t// The group starts together once databases resolve (the Data API probe needs one), as\n\t// before, so which member's error comes first is unchanged.\n\tconst previewRead = started(() =>\n\t\tdatabasesRead.then((databases) => {\n\t\t\tconst probeDatabase = pickProbeDatabase(databases);\n\t\t\treturn Promise.all([\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listBranchBuckets(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listBranchFunctions(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tdegradeUnavailable(\n\t\t\t\t\t\t() => api.listCredentials(projectId, branchId),\n\t\t\t\t\t\t[],\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\tstarted(() => api.getNeonAuth(projectId, branchId)),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tprobeDatabase\n\t\t\t\t\t\t? api.getNeonDataApi(projectId, branchId, probeDatabase)\n\t\t\t\t\t\t: Promise.resolve(null),\n\t\t\t\t),\n\t\t\t\tstarted(() =>\n\t\t\t\t\tgetRealtime\n\t\t\t\t\t\t? degradeUnavailable(\n\t\t\t\t\t\t\t\t() => getRealtime(projectId, branchId),\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: Promise.resolve(null),\n\t\t\t\t),\n\t\t\t]);\n\t\t}),\n\t);\n\n\tconst project = await projectRead;\n\tconst [branches, endpoints] = await listsRead;\n\tconst branch = resolveBranch(branchId, branches);\n\tconst endpoint = endpoints.find(\n\t\t(ep) => ep.type === \"read_write\" && ep.branchId === branch.id,\n\t);\n\tawait databasesRead;\n\tconst [buckets, functions, credentials, auth, dataApi, realtime] =\n\t\tawait previewRead;\n\n\treturn buildPulledBranchConfig(project, branch, branches, endpoint, {\n\t\tbuckets,\n\t\tfunctions,\n\t\tcredentials,\n\t\tauthEnabled: auth !== null,\n\t\tdataApiEnabled: dataApi !== null,\n\t\tdataApi,\n\t\trealtime,\n\t});\n}\n\n/**\n * The `dataApi` a pulled policy declares for a live Data API. The provider is fixed when the\n * Data API is created, so a bare `true` (Neon Auth) is only right when that is what the\n * branch has; writing it over an external-IdP Data API would make the next apply enable Neon\n * Auth. When the API does not report the provider (older Data APIs), the branch's Neon Auth\n * state is the best available answer: with Neon Auth off, the Data API cannot be using it.\n */\nexport function pulledDataApiConfig(\n\tdataApi: NeonDataApiSnapshot,\n\tauthEnabled: boolean,\n): DataApiInput {\n\tconst external = (): DataApiExternalAuthConfig => {\n\t\tconst config: DataApiExternalAuthConfig = { authProvider: \"external\" };\n\t\tif (dataApi.jwksUrl !== undefined) config.jwksUrl = dataApi.jwksUrl;\n\t\tif (dataApi.providerName !== undefined)\n\t\t\tconfig.providerName = dataApi.providerName;\n\t\tif (dataApi.jwtAudience !== undefined)\n\t\t\tconfig.jwtAudience = dataApi.jwtAudience;\n\t\treturn config;\n\t};\n\tswitch (dataApi.authProvider) {\n\t\tcase \"neon_auth\":\n\t\t\treturn true;\n\t\tcase \"external\":\n\t\t\treturn external();\n\t\tdefault:\n\t\t\t// \"none\" has no policy spelling; `external` without a JWKS declares the Data API\n\t\t\t// while leaving Neon Auth alone, which is the one thing a provider-less Data API\n\t\t\t// and an unknown one have in common.\n\t\t\treturn authEnabled ? true : external();\n\t}\n}\n\n/**\n * Starts a read now and keeps an unread rejection from crashing the process; the caller\n * awaits it (and sees the error) when its turn comes. Also turns a synchronous throw from\n * a custom adapter into a rejection.\n */\nfunction started<T>(read: () => Promise<T>): Promise<T> {\n\tconst promise = Promise.resolve().then(read);\n\tpromise.catch(() => undefined);\n\treturn promise;\n}\n\n/**\n * Pick the database to probe for a Data API integration. Data API is enabled per\n * branch + database; for read-back we only need to know whether *any* database has it\n * on, so prefer the conventional default (`neondb`) and otherwise fall back to the first\n * database. Returns `undefined` when the branch has no databases.\n */\nfunction pickProbeDatabase(\n\tdatabases: NeonDatabaseSnapshot[],\n): string | undefined {\n\tif (databases.length === 0) return undefined;\n\tconst byName = databases.find((d) => d.name === \"neondb\");\n\tif (byName) return byName.name;\n\treturn databases[0].name;\n}\n\n/**\n * Run a Preview-feature read, returning `fallback` if the feature is unavailable for the\n * project/region. Most adapters report {@link ErrorCode.FeatureUnavailable}; API hosts where\n * a route is not deployed can instead return a generic 404. The project and branch are\n * validated independently by `pullConfig`, so that route-level 404 is also safe to degrade.\n * Other errors propagate. This lets a branch without a Preview feature still mirror cleanly\n * for env resolution / `inspect`, rather than aborting on an unrelated capability.\n */\nasync function degradeUnavailable<T>(\n\tread: () => Promise<T>,\n\tfallback: T,\n): Promise<T> {\n\ttry {\n\t\treturn await read();\n\t} catch (err) {\n\t\tif (\n\t\t\terr instanceof PlatformError &&\n\t\t\t(err.code === ErrorCode.FeatureUnavailable ||\n\t\t\t\t(err.code === ErrorCode.NotFound && err.details.status === 404))\n\t\t) {\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\nfunction createApiFromOptions(options: PullConfigOptions): NeonApi {\n\treturn createNeonApiFromOptions(\"pullConfig\", {\n\t\t...(options.apiKey ? { apiKey: options.apiKey } : {}),\n\t\t...(options.apiHost ? { apiHost: options.apiHost } : {}),\n\t});\n}\n\nexport function buildPulledBranchConfig(\n\tproject: NeonProjectSnapshot,\n\tbranch: NeonBranchSnapshot,\n\tbranches: NeonBranchSnapshot[],\n\tendpoint: NeonEndpointSnapshot | undefined,\n\tpreviewState?: {\n\t\tbuckets: NeonBucketSnapshot[];\n\t\tfunctions: NeonFunctionSnapshot[];\n\t\tcredentials?: NeonCredentialMeta[];\n\t\t/** Whether a Neon Auth integration is enabled on the branch. */\n\t\tauthEnabled?: boolean;\n\t\t/** Whether a Neon Data API integration is enabled on the branch. */\n\t\tdataApiEnabled?: boolean;\n\t\t/**\n\t\t * The live Data API, when enabled. Its auth provider decides the shape of the emitted\n\t\t * `dataApi` (see {@link pulledDataApiConfig}); with only `dataApiEnabled` the policy\n\t\t * falls back to a bare `true`.\n\t\t */\n\t\tdataApi?: NeonDataApiSnapshot | null;\n\t\t/** Realtime state when the adapter supports the branch endpoint. */\n\t\trealtime?: NeonRealtimeSnapshot | null;\n\t},\n): PulledBranchConfig {\n\tconst parent = branch.parentId\n\t\t? branches.find((b) => b.id === branch.parentId)\n\t\t: undefined;\n\t// Auth and Data API are static top-level toggles, while Realtime is static under\n\t// `preview.realtime`. A config pulled from a branch with them enabled round-trips through\n\t// `resolveConfig` / `fetchEnv` and the matching secrets get injected. Branch\n\t// lifecycle/compute is per-branch tuning, so it goes in the `branch` closure.\n\tconst tuning: BranchTuning = {};\n\tif (parent) tuning.parent = parent.name;\n\t// Deliberately NOT emitting `ttl` from `branch.expiresAt`: policy `ttl` is a\n\t// creation-time *duration*, while `expiresAt` is an absolute timestamp. Feeding the\n\t// timestamp through `parseDuration` (in `resolveConfig`) would throw, breaking\n\t// `fetchEnv` / `neon dev` / `neon env pull` for any branch that has a TTL. The expiry\n\t// is reported faithfully on `branch.expiresAt` above.\n\tif (branch.protected) tuning.protected = true;\n\tif (endpoint) {\n\t\tconst compute = endpointToComputeSettings(endpoint, project);\n\t\tif (compute) tuning.postgres = { computeSettings: compute };\n\t}\n\t// Buckets round-trip cleanly (name + access level), so they belong in the config the\n\t// env path resolves from — this is what lets `neon dev` / `neon env pull` inject the\n\t// branch's object-storage credentials (AWS_*) with no local neon.ts. Functions are\n\t// deliberately NOT emitted here (they can't round-trip: the remote has no `source`\n\t// path) and neither is the AI Gateway (no branch-level enabled state to detect, only a\n\t// credential) — both would otherwise turn a read-back into a bogus policy declaration.\n\tconst bucketDefs: Record<string, { access: BucketAccessLevel }> = {};\n\tfor (const b of previewState?.buckets ?? []) {\n\t\tbucketDefs[b.name] = { access: b.accessLevel };\n\t}\n\tconst policyPreview: NonNullable<Config[\"preview\"]> = {};\n\tif (previewState?.realtime?.enabled) policyPreview.realtime = true;\n\tif (Object.keys(bucketDefs).length > 0) policyPreview.buckets = bucketDefs;\n\tconst authEnabled = previewState?.authEnabled === true;\n\tconst dataApi = previewState?.dataApi\n\t\t? pulledDataApiConfig(previewState.dataApi, authEnabled)\n\t\t: previewState?.dataApiEnabled\n\t\t\t? true\n\t\t\t: undefined;\n\tconst config: Config = {\n\t\t...(authEnabled ? { auth: true } : {}),\n\t\t...(dataApi !== undefined ? { dataApi } : {}),\n\t\t...(Object.keys(policyPreview).length > 0\n\t\t\t? { preview: policyPreview }\n\t\t\t: {}),\n\t\t...(Object.keys(tuning).length > 0 ? { branch: () => tuning } : {}),\n\t};\n\tconst result: PulledBranchConfig = {\n\t\tproject: {\n\t\t\tid: project.id,\n\t\t\tname: project.name,\n\t\t\tregion: project.regionId,\n\t\t\tpgVersion: project.pgVersion,\n\t\t\t...(project.orgId ? { orgId: project.orgId } : {}),\n\t\t},\n\t\tbranch: {\n\t\t\tid: branch.id,\n\t\t\tname: branch.name,\n\t\t\t...(parent ? { parent: parent.name } : {}),\n\t\t\tisDefault: branch.isDefault,\n\t\t\tprotected: branch.protected,\n\t\t\t...(branch.expiresAt ? { expiresAt: branch.expiresAt } : {}),\n\t\t},\n\t\tconfig,\n\t};\n\tconst preview = previewState ? buildPulledPreview(previewState) : undefined;\n\tif (preview) result.preview = preview;\n\treturn result;\n}\n\n/**\n * Reverse-engineer the {@link PulledPreview} from remote snapshots. Returns `undefined` when\n * the branch has no Preview features so the field can be omitted entirely.\n */\nfunction buildPulledPreview(state: {\n\tbuckets: NeonBucketSnapshot[];\n\tfunctions: NeonFunctionSnapshot[];\n\tcredentials?: NeonCredentialMeta[];\n}): PulledPreview | undefined {\n\tconst credentials = state.credentials ?? [];\n\tif (\n\t\tstate.buckets.length === 0 &&\n\t\tstate.functions.length === 0 &&\n\t\tcredentials.length === 0\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tbuckets: state.buckets.map((b) => ({\n\t\t\tname: b.name,\n\t\t\taccess: b.accessLevel,\n\t\t})),\n\t\tfunctions: state.functions.map((f) => ({\n\t\t\tslug: f.slug,\n\t\t\tname: f.name,\n\t\t})),\n\t\tcredentials,\n\t};\n}\n\nfunction resolveBranch(\n\tbranchId: string,\n\tbranches: NeonBranchSnapshot[],\n): NeonBranchSnapshot {\n\tconst match = branches.find((b) => b.id === branchId);\n\tif (match) return match;\n\tthrow new PlatformError(\n\t\tErrorCode.BranchNotFound,\n\t\t[\n\t\t\t`pullConfig: branch id ${JSON.stringify(branchId)} not found on project.`,\n\t\t\t`Available branches: ${branches.map((b) => `${b.name} (${b.id})`).join(\", \") || \"(none)\"}.`,\n\t\t].join(\" \"),\n\t\t{\n\t\t\tdetails: {\n\t\t\t\tbranchId,\n\t\t\t\tavailable: branches.map((b) => b.id),\n\t\t\t},\n\t\t},\n\t);\n}\n\nfunction endpointToComputeSettings(\n\tendpoint: NeonEndpointSnapshot,\n\tproject: NeonProjectSnapshot,\n): ComputeSettings | undefined {\n\tconst defaults = project.defaultEndpointSettings;\n\tconst out: ComputeSettings = {};\n\tif (\n\t\tendpoint.autoscalingLimitMinCu !== undefined &&\n\t\tendpoint.autoscalingLimitMinCu !== defaults?.autoscalingLimitMinCu\n\t) {\n\t\tout.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;\n\t}\n\tif (\n\t\tendpoint.autoscalingLimitMaxCu !== undefined &&\n\t\tendpoint.autoscalingLimitMaxCu !== defaults?.autoscalingLimitMaxCu\n\t) {\n\t\tout.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;\n\t}\n\tif (\n\t\tendpoint.suspendTimeout !== undefined &&\n\t\tendpoint.suspendTimeout !== defaults?.suspendTimeout\n\t) {\n\t\tout.suspendTimeout = endpoint.suspendTimeout;\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neon/config-runtime",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "Imperative runtime for @neon/config: inspect/plan/apply a neon.ts policy against the Neon API and bundle + deploy Neon Functions. Pulls in esbuild; import this from CLIs and CI, not from neon.ts.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -48,13 +48,13 @@
|
|
|
48
48
|
"typescript": "^5.9.0",
|
|
49
49
|
"vitest": "^3.0.9",
|
|
50
50
|
"@neon/e2e-harness": "0.0.0",
|
|
51
|
-
"@neon/sdk": "7.
|
|
51
|
+
"@neon/sdk": "7.7.0"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@vercel/nft": "^1.10.2",
|
|
55
55
|
"esbuild": "^0.28.1",
|
|
56
56
|
"fflate": "^0.8.3",
|
|
57
|
-
"@neon/config": "1.
|
|
57
|
+
"@neon/config": "1.10.0"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=20.19.0"
|