@neondatabase/config 0.0.0 → 0.2.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/LICENSE.md +178 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/lib/auth.d.ts +63 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +93 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/define-config.d.ts +43 -0
- package/dist/lib/define-config.d.ts.map +1 -0
- package/dist/lib/define-config.js +112 -0
- package/dist/lib/define-config.js.map +1 -0
- package/dist/lib/diff.d.ts +109 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +205 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/duration.d.ts +46 -0
- package/dist/lib/duration.d.ts.map +1 -0
- package/dist/lib/duration.js +96 -0
- package/dist/lib/duration.js.map +1 -0
- package/dist/lib/errors.d.ts +129 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +168 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/loader.d.ts +44 -0
- package/dist/lib/loader.d.ts.map +1 -0
- package/dist/lib/loader.js +119 -0
- package/dist/lib/loader.js.map +1 -0
- package/dist/lib/neon-api-real.d.ts +45 -0
- package/dist/lib/neon-api-real.d.ts.map +1 -0
- package/dist/lib/neon-api-real.js +582 -0
- package/dist/lib/neon-api-real.js.map +1 -0
- package/dist/lib/neon-api.d.ts +262 -0
- package/dist/lib/neon-api.d.ts.map +1 -0
- package/dist/lib/neon-api.js +1 -0
- package/dist/lib/patterns.d.ts +43 -0
- package/dist/lib/patterns.d.ts.map +1 -0
- package/dist/lib/patterns.js +76 -0
- package/dist/lib/patterns.js.map +1 -0
- package/dist/lib/schema.d.ts +130 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +218 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/types.d.ts +289 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/wrap-neon-error.d.ts +30 -0
- package/dist/lib/wrap-neon-error.d.ts.map +1 -0
- package/dist/lib/wrap-neon-error.js +139 -0
- package/dist/lib/wrap-neon-error.js.map +1 -0
- package/dist/v1.d.ts +153 -0
- package/dist/v1.d.ts.map +1 -0
- package/dist/v1.js +69 -0
- package/dist/v1.js.map +1 -0
- package/package.json +67 -17
- package/.env.example +0 -5
- package/e2e/errors.e2e.test.ts +0 -52
- package/e2e/helpers.ts +0 -205
- package/e2e/load-env.ts +0 -29
- package/e2e/setup.ts +0 -24
- package/src/index.ts +0 -5
- package/src/lib/auth.test.ts +0 -166
- package/src/lib/auth.ts +0 -124
- package/src/lib/define-config.test.ts +0 -161
- package/src/lib/define-config.ts +0 -152
- package/src/lib/diff.test.ts +0 -142
- package/src/lib/diff.ts +0 -391
- package/src/lib/duration.test.ts +0 -105
- package/src/lib/duration.ts +0 -147
- package/src/lib/errors.test.ts +0 -26
- package/src/lib/errors.ts +0 -220
- package/src/lib/fake-neon-api.ts +0 -782
- package/src/lib/loader.test.ts +0 -35
- package/src/lib/loader.ts +0 -215
- package/src/lib/neon-api-real.test.ts +0 -72
- package/src/lib/neon-api-real.ts +0 -1123
- package/src/lib/neon-api.ts +0 -356
- package/src/lib/patterns.test.ts +0 -80
- package/src/lib/patterns.ts +0 -98
- package/src/lib/schema.test.ts +0 -88
- package/src/lib/schema.ts +0 -252
- package/src/lib/test-utils.ts +0 -83
- package/src/lib/types.ts +0 -268
- package/src/lib/wrap-neon-error.test.ts +0 -145
- package/src/lib/wrap-neon-error.ts +0 -204
- package/src/v1.test.ts +0 -33
- package/src/v1.ts +0 -148
- package/tsconfig.json +0 -4
- package/tsdown.config.ts +0 -19
- package/vitest.config.ts +0 -19
- package/vitest.e2e.config.ts +0 -29
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { BucketAccessLevel, ComputeSettings, ConflictReport, ResolvedBranchConfig, ResolvedFunctionConfig } from "./types.js";
|
|
2
|
+
import { NeonBranchSnapshot, NeonBucketSnapshot, NeonEndpointSnapshot, NeonFunctionSnapshot } from "./neon-api.js";
|
|
3
|
+
|
|
4
|
+
//#region src/lib/diff.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A planned action to perform a single mutation against the Neon API. The diff engine
|
|
8
|
+
* produces a list of these for `pushConfig` to execute (or report).
|
|
9
|
+
*/
|
|
10
|
+
type PlanStep = {
|
|
11
|
+
kind: "update-branch-ttl";
|
|
12
|
+
projectId: string;
|
|
13
|
+
branchId: string;
|
|
14
|
+
branchName: string;
|
|
15
|
+
expiresAt: string | null;
|
|
16
|
+
} | {
|
|
17
|
+
kind: "update-branch-protected";
|
|
18
|
+
projectId: string;
|
|
19
|
+
branchId: string;
|
|
20
|
+
branchName: string;
|
|
21
|
+
protected: boolean;
|
|
22
|
+
} | {
|
|
23
|
+
kind: "update-endpoint";
|
|
24
|
+
projectId: string;
|
|
25
|
+
branchName: string;
|
|
26
|
+
endpointId: string;
|
|
27
|
+
settings: ComputeSettings;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "enable-auth";
|
|
30
|
+
projectId: string;
|
|
31
|
+
branchId: string;
|
|
32
|
+
branchName: string;
|
|
33
|
+
databaseName?: string;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "enable-data-api";
|
|
36
|
+
projectId: string;
|
|
37
|
+
branchId: string;
|
|
38
|
+
branchName: string;
|
|
39
|
+
databaseName: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: "create-bucket";
|
|
42
|
+
projectId: string;
|
|
43
|
+
branchId: string;
|
|
44
|
+
branchName: string;
|
|
45
|
+
bucketName: string;
|
|
46
|
+
accessLevel: BucketAccessLevel;
|
|
47
|
+
} | {
|
|
48
|
+
kind: "create-function";
|
|
49
|
+
projectId: string;
|
|
50
|
+
branchId: string;
|
|
51
|
+
branchName: string;
|
|
52
|
+
fn: ResolvedFunctionConfig;
|
|
53
|
+
} | {
|
|
54
|
+
/**
|
|
55
|
+
* Deploy (or re-deploy) code to a function. Always planned for every desired
|
|
56
|
+
* function — deployments are versioned and the newest becomes active, so a push
|
|
57
|
+
* ships the current source each time. `functionExists` tells `pushConfig` whether
|
|
58
|
+
* it must create the function first (covered by a preceding `create-function` step).
|
|
59
|
+
*/
|
|
60
|
+
kind: "deploy-function";
|
|
61
|
+
projectId: string;
|
|
62
|
+
branchId: string;
|
|
63
|
+
branchName: string;
|
|
64
|
+
fn: ResolvedFunctionConfig;
|
|
65
|
+
} | {
|
|
66
|
+
kind: "enable-ai-gateway";
|
|
67
|
+
projectId: string;
|
|
68
|
+
branchId: string;
|
|
69
|
+
branchName: string;
|
|
70
|
+
};
|
|
71
|
+
interface RemoteServiceState {
|
|
72
|
+
databaseName: string;
|
|
73
|
+
authEnabled: boolean;
|
|
74
|
+
dataApiEnabled: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Snapshot of the branch's current Preview-feature state. Absent (`undefined`) when the
|
|
78
|
+
* policy has no `preview` block — `pushConfig` only fetches this when needed.
|
|
79
|
+
*/
|
|
80
|
+
interface RemotePreviewState {
|
|
81
|
+
buckets: NeonBucketSnapshot[];
|
|
82
|
+
functions: NeonFunctionSnapshot[];
|
|
83
|
+
aiGatewayEnabled: boolean;
|
|
84
|
+
}
|
|
85
|
+
interface RemoteState {
|
|
86
|
+
projectId: string;
|
|
87
|
+
branch: NeonBranchSnapshot;
|
|
88
|
+
endpoint?: NeonEndpointSnapshot;
|
|
89
|
+
services: RemoteServiceState;
|
|
90
|
+
preview?: RemotePreviewState;
|
|
91
|
+
}
|
|
92
|
+
interface DiffOptions {
|
|
93
|
+
/**
|
|
94
|
+
* Apply mutable drift on the selected branch as plan steps instead of conflicts.
|
|
95
|
+
* Default: `false`.
|
|
96
|
+
*/
|
|
97
|
+
updateExisting: boolean;
|
|
98
|
+
}
|
|
99
|
+
interface DiffResult {
|
|
100
|
+
plan: PlanStep[];
|
|
101
|
+
conflicts: ConflictReport[];
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Diff desired branch policy against the selected remote branch. Pure function.
|
|
105
|
+
*/
|
|
106
|
+
declare function diffConfig(config: ResolvedBranchConfig, remote: RemoteState, options: DiffOptions): DiffResult;
|
|
107
|
+
//#endregion
|
|
108
|
+
export { DiffOptions, DiffResult, PlanStep, RemotePreviewState, RemoteServiceState, RemoteState, diffConfig };
|
|
109
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","names":[],"sources":["../../src/lib/diff.ts"],"mappings":";;;;;;;AAkBA;;AAoBa,KApBD,QAAA,GAoBC;MAsBG,EAAA,mBAAA;WAOT,EAAA,MAAA;UAaA,EAAA,MAAA;EAAsB,UAAA,EAAA,MAAA;EASZ,SAAA,EAAA,MAAA,GAAA,IAAkB;AAUnC,CAAA,GAAiB;EAAkB,IAAA,EAAA,yBAAA;WACzB,EAAA,MAAA;UACE,EAAA,MAAA;EAAoB,UAAA,EAAA,MAAA;EAIf,SAAA,EAAA,OAAW;CAAA,GAAA;MAEnB,EAAA,iBAAA;WACG,EAAA,MAAA;YACD,EAAA,MAAA;YACA,EAAA,MAAA;EAAkB,QAAA,EAxEhB,eAwEgB;AAG7B,CAAA,GAAiB;EAQA,IAAA,EAAA,aAAU;EAAA,SAAA,EAAA,MAAA;UACpB,EAAA,MAAA;YACK,EAAA,MAAA;EAAc,YAAA,CAAA,EAAA,MAAA;AAM1B,CAAA,GAAgB;EAAU,IAAA,EAAA,iBAAA;WACjB,EAAA,MAAA;UACA,EAAA,MAAA;YACC,EAAA,MAAA;cACP,EAAA,MAAA;AAAU,CAAA,GAAA;;;;;;eAzEG;;;;;;MAOT;;;;;;;;;;;;MAaA;;;;;;;UASU,kBAAA;;;;;;;;;UAUA,kBAAA;WACP;aACE;;;UAIK,WAAA;;UAER;aACG;YACD;YACA;;UAGM,WAAA;;;;;;;UAQA,UAAA;QACV;aACK;;;;;iBAMI,UAAA,SACP,8BACA,sBACC,cACP"}
|
package/dist/lib/diff.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
//#region src/lib/diff.ts
|
|
2
|
+
/**
|
|
3
|
+
* Diff desired branch policy against the selected remote branch. Pure function.
|
|
4
|
+
*/
|
|
5
|
+
function diffConfig(config, remote, options) {
|
|
6
|
+
const conflicts = [];
|
|
7
|
+
const plan = [];
|
|
8
|
+
diffBranchConfig({
|
|
9
|
+
config,
|
|
10
|
+
remote,
|
|
11
|
+
options,
|
|
12
|
+
plan,
|
|
13
|
+
conflicts
|
|
14
|
+
});
|
|
15
|
+
diffServices({
|
|
16
|
+
config,
|
|
17
|
+
remote,
|
|
18
|
+
plan
|
|
19
|
+
});
|
|
20
|
+
diffPreview({
|
|
21
|
+
config,
|
|
22
|
+
remote,
|
|
23
|
+
plan
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
plan,
|
|
27
|
+
conflicts
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Plan Preview features (functions, buckets, AI Gateway). Like {@link diffServices}, this
|
|
32
|
+
* is **additive**: it creates desired buckets/functions and enables the AI Gateway, but
|
|
33
|
+
* never deletes buckets/functions or disables the gateway. Teardown is destructive, so it
|
|
34
|
+
* stays explicit/manual — matching the existing auth / dataApi behaviour.
|
|
35
|
+
*
|
|
36
|
+
* Functions are always (re-)deployed: deployments are versioned and the newest becomes
|
|
37
|
+
* active, so each push ships the current source. A `create-function` step precedes the
|
|
38
|
+
* `deploy-function` step when the function does not yet exist remotely.
|
|
39
|
+
*/
|
|
40
|
+
function diffPreview(args) {
|
|
41
|
+
const { config, remote, plan } = args;
|
|
42
|
+
const preview = config.preview;
|
|
43
|
+
if (!preview) return;
|
|
44
|
+
const state = remote.preview ?? {
|
|
45
|
+
buckets: [],
|
|
46
|
+
functions: [],
|
|
47
|
+
aiGatewayEnabled: false
|
|
48
|
+
};
|
|
49
|
+
for (const bucket of preview.buckets) {
|
|
50
|
+
if (state.buckets.some((b) => b.name === bucket.name)) continue;
|
|
51
|
+
plan.push({
|
|
52
|
+
kind: "create-bucket",
|
|
53
|
+
projectId: remote.projectId,
|
|
54
|
+
branchId: remote.branch.id,
|
|
55
|
+
branchName: remote.branch.name,
|
|
56
|
+
bucketName: bucket.name,
|
|
57
|
+
accessLevel: bucket.access
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
for (const fn of preview.functions) {
|
|
61
|
+
if (!state.functions.some((f) => f.slug === fn.slug)) plan.push({
|
|
62
|
+
kind: "create-function",
|
|
63
|
+
projectId: remote.projectId,
|
|
64
|
+
branchId: remote.branch.id,
|
|
65
|
+
branchName: remote.branch.name,
|
|
66
|
+
fn
|
|
67
|
+
});
|
|
68
|
+
plan.push({
|
|
69
|
+
kind: "deploy-function",
|
|
70
|
+
projectId: remote.projectId,
|
|
71
|
+
branchId: remote.branch.id,
|
|
72
|
+
branchName: remote.branch.name,
|
|
73
|
+
fn
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (preview.aiGatewayEnabled && !state.aiGatewayEnabled) plan.push({
|
|
77
|
+
kind: "enable-ai-gateway",
|
|
78
|
+
projectId: remote.projectId,
|
|
79
|
+
branchId: remote.branch.id,
|
|
80
|
+
branchName: remote.branch.name
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Plan additive branch-scoped integrations. Disabling remains explicit/manual because
|
|
85
|
+
* teardown is destructive.
|
|
86
|
+
*/
|
|
87
|
+
function diffServices(args) {
|
|
88
|
+
const { config, remote, plan } = args;
|
|
89
|
+
const state = remote.services;
|
|
90
|
+
if (config.authEnabled && !state.authEnabled) {
|
|
91
|
+
const step = {
|
|
92
|
+
kind: "enable-auth",
|
|
93
|
+
projectId: remote.projectId,
|
|
94
|
+
branchId: remote.branch.id,
|
|
95
|
+
branchName: remote.branch.name
|
|
96
|
+
};
|
|
97
|
+
if (state.databaseName) step.databaseName = state.databaseName;
|
|
98
|
+
plan.push(step);
|
|
99
|
+
}
|
|
100
|
+
if (config.dataApiEnabled && !state.dataApiEnabled) plan.push({
|
|
101
|
+
kind: "enable-data-api",
|
|
102
|
+
projectId: remote.projectId,
|
|
103
|
+
branchId: remote.branch.id,
|
|
104
|
+
branchName: remote.branch.name,
|
|
105
|
+
databaseName: state.databaseName
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function diffBranchConfig(args) {
|
|
109
|
+
const { config, remote, options, plan, conflicts } = args;
|
|
110
|
+
const branchName = remote.branch.name;
|
|
111
|
+
const computeSettings = config.postgres?.computeSettings;
|
|
112
|
+
if (computeSettings) {
|
|
113
|
+
const endpoint = remote.endpoint;
|
|
114
|
+
if (!endpoint) conflicts.push({
|
|
115
|
+
kind: "branch",
|
|
116
|
+
identifier: branchName,
|
|
117
|
+
field: "endpoint",
|
|
118
|
+
current: void 0,
|
|
119
|
+
desired: computeSettings,
|
|
120
|
+
reason: "Branch has no read-write endpoint; cannot apply compute settings."
|
|
121
|
+
});
|
|
122
|
+
else {
|
|
123
|
+
const drift = computeDriftBetween(computeSettings, endpoint);
|
|
124
|
+
if (drift) if (options.updateExisting) plan.push({
|
|
125
|
+
kind: "update-endpoint",
|
|
126
|
+
projectId: remote.projectId,
|
|
127
|
+
branchName,
|
|
128
|
+
endpointId: endpoint.id,
|
|
129
|
+
settings: computeSettings
|
|
130
|
+
});
|
|
131
|
+
else conflicts.push({
|
|
132
|
+
kind: "branch",
|
|
133
|
+
identifier: branchName,
|
|
134
|
+
field: "computeSettings",
|
|
135
|
+
current: drift.current,
|
|
136
|
+
desired: drift.desired,
|
|
137
|
+
reason: "Existing branch has different compute settings. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply."
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (config.protected !== void 0 && config.protected !== remote.branch.protected) if (options.updateExisting) plan.push({
|
|
142
|
+
kind: "update-branch-protected",
|
|
143
|
+
projectId: remote.projectId,
|
|
144
|
+
branchId: remote.branch.id,
|
|
145
|
+
branchName,
|
|
146
|
+
protected: config.protected
|
|
147
|
+
});
|
|
148
|
+
else conflicts.push({
|
|
149
|
+
kind: "branch",
|
|
150
|
+
identifier: branchName,
|
|
151
|
+
field: "protected",
|
|
152
|
+
current: remote.branch.protected,
|
|
153
|
+
desired: config.protected,
|
|
154
|
+
reason: "Existing branch has a different `protected` flag. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply."
|
|
155
|
+
});
|
|
156
|
+
if (config.ttlSeconds !== void 0) {
|
|
157
|
+
const current = remote.branch.expiresAt ? Math.max(0, Math.round((Date.parse(remote.branch.expiresAt) - Date.now()) / 1e3)) : void 0;
|
|
158
|
+
if (current === void 0 || Math.abs(current - config.ttlSeconds) > 30) {
|
|
159
|
+
const expiresAt = new Date(Date.now() + config.ttlSeconds * 1e3).toISOString();
|
|
160
|
+
if (options.updateExisting) plan.push({
|
|
161
|
+
kind: "update-branch-ttl",
|
|
162
|
+
projectId: remote.projectId,
|
|
163
|
+
branchId: remote.branch.id,
|
|
164
|
+
branchName,
|
|
165
|
+
expiresAt
|
|
166
|
+
});
|
|
167
|
+
else conflicts.push({
|
|
168
|
+
kind: "branch",
|
|
169
|
+
identifier: branchName,
|
|
170
|
+
field: "ttl",
|
|
171
|
+
current: remote.branch.expiresAt,
|
|
172
|
+
desired: expiresAt,
|
|
173
|
+
reason: "Existing branch has a different TTL. Pass `updateExisting: true` (SDK) or `--update-existing` (CLI) to apply."
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function computeDriftBetween(desired, endpoint) {
|
|
179
|
+
const currentDrift = {};
|
|
180
|
+
const desiredDrift = {};
|
|
181
|
+
let drift = false;
|
|
182
|
+
if (desired.autoscalingLimitMinCu !== void 0 && desired.autoscalingLimitMinCu !== endpoint.autoscalingLimitMinCu) {
|
|
183
|
+
currentDrift.autoscalingLimitMinCu = endpoint.autoscalingLimitMinCu;
|
|
184
|
+
desiredDrift.autoscalingLimitMinCu = desired.autoscalingLimitMinCu;
|
|
185
|
+
drift = true;
|
|
186
|
+
}
|
|
187
|
+
if (desired.autoscalingLimitMaxCu !== void 0 && desired.autoscalingLimitMaxCu !== endpoint.autoscalingLimitMaxCu) {
|
|
188
|
+
currentDrift.autoscalingLimitMaxCu = endpoint.autoscalingLimitMaxCu;
|
|
189
|
+
desiredDrift.autoscalingLimitMaxCu = desired.autoscalingLimitMaxCu;
|
|
190
|
+
drift = true;
|
|
191
|
+
}
|
|
192
|
+
if (desired.suspendTimeout !== void 0 && desired.suspendTimeout !== endpoint.suspendTimeout) {
|
|
193
|
+
currentDrift.suspendTimeout = endpoint.suspendTimeout;
|
|
194
|
+
desiredDrift.suspendTimeout = desired.suspendTimeout;
|
|
195
|
+
drift = true;
|
|
196
|
+
}
|
|
197
|
+
return drift ? {
|
|
198
|
+
current: currentDrift,
|
|
199
|
+
desired: desiredDrift
|
|
200
|
+
} : null;
|
|
201
|
+
}
|
|
202
|
+
//#endregion
|
|
203
|
+
export { diffConfig };
|
|
204
|
+
|
|
205
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","names":[],"sources":["../../src/lib/diff.ts"],"sourcesContent":["import type {\n\tNeonBranchSnapshot,\n\tNeonBucketSnapshot,\n\tNeonEndpointSnapshot,\n\tNeonFunctionSnapshot,\n} from \"./neon-api.js\";\nimport type {\n\tBucketAccessLevel,\n\tComputeSettings,\n\tConflictReport,\n\tResolvedBranchConfig,\n\tResolvedFunctionConfig,\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 }\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\tkind: \"create-function\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t\t\tfn: ResolvedFunctionConfig;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * Deploy (or re-deploy) code to a function. Always planned for every desired\n\t\t\t * function — deployments are versioned and the newest becomes active, so a push\n\t\t\t * ships the current source each time. `functionExists` tells `pushConfig` whether\n\t\t\t * it must create the function first (covered by a preceding `create-function` step).\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 }\n\t| {\n\t\t\tkind: \"enable-ai-gateway\";\n\t\t\tprojectId: string;\n\t\t\tbranchId: string;\n\t\t\tbranchName: string;\n\t };\n\nexport interface RemoteServiceState {\n\tdatabaseName: string;\n\tauthEnabled: boolean;\n\tdataApiEnabled: boolean;\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\taiGatewayEnabled: boolean;\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, plan });\n\tdiffPreview({ config, remote, plan });\n\treturn { plan, conflicts };\n}\n\n/**\n * Plan Preview features (functions, buckets, AI Gateway). Like {@link diffServices}, this\n * is **additive**: it creates desired buckets/functions and enables the AI Gateway, but\n * never deletes buckets/functions or disables the gateway. Teardown is destructive, so it\n * stays explicit/manual — matching the existing auth / dataApi behaviour.\n *\n * Functions are always (re-)deployed: deployments are versioned and the newest becomes\n * active, so each push ships the current source. A `create-function` step precedes the\n * `deploy-function` step when the function does not yet exist remotely.\n */\nfunction diffPreview(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\tplan: PlanStep[];\n}): void {\n\tconst { config, remote, plan } = 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\taiGatewayEnabled: false,\n\t};\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\tif (!exists) {\n\t\t\tplan.push({\n\t\t\t\tkind: \"create-function\",\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\tfn,\n\t\t\t});\n\t\t}\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});\n\t}\n\n\tif (preview.aiGatewayEnabled && !state.aiGatewayEnabled) {\n\t\tplan.push({\n\t\t\tkind: \"enable-ai-gateway\",\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}\n}\n\n/**\n * Plan additive branch-scoped integrations. Disabling remains explicit/manual because\n * teardown is destructive.\n */\nfunction diffServices(args: {\n\tconfig: ResolvedBranchConfig;\n\tremote: RemoteState;\n\tplan: PlanStep[];\n}): void {\n\tconst { config, remote, plan } = 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\tif (config.dataApiEnabled && !state.dataApiEnabled) {\n\t\tplan.push({\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}\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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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//#region src/lib/duration.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Parse a TTL value into whole seconds.
|
|
4
|
+
*
|
|
5
|
+
* Accepted formats:
|
|
6
|
+
* - a positive finite number → interpreted as seconds (must be an integer)
|
|
7
|
+
* - a positive integer string ("3600") → seconds
|
|
8
|
+
* - `<number><unit>` where unit is one of `s`, `m`, `h`, `d`, `w` (e.g. `30s`, `5m`, `1h`, `7d`, `2w`)
|
|
9
|
+
*
|
|
10
|
+
* Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.
|
|
11
|
+
*/
|
|
12
|
+
declare function parseDuration(input: string | number): {
|
|
13
|
+
seconds: number;
|
|
14
|
+
} | {
|
|
15
|
+
error: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Render a TTL in seconds back to the canonical "<n><unit>" form. Used for round-trip
|
|
19
|
+
* serialization when {@link pullConfig} emits a TTL value (it always falls back to seconds
|
|
20
|
+
* when no clean unit boundary matches).
|
|
21
|
+
*/
|
|
22
|
+
declare function formatDurationSeconds(totalSeconds: number): string;
|
|
23
|
+
/**
|
|
24
|
+
* Parse a suspend timeout value into seconds for the Neon API.
|
|
25
|
+
*
|
|
26
|
+
* Accepted formats:
|
|
27
|
+
* - `false` → -1 (never suspend)
|
|
28
|
+
* - `undefined` → 0 (use platform default)
|
|
29
|
+
* - duration string → parsed seconds ("5m", "1h", "7d")
|
|
30
|
+
* - number → validated seconds (must be 60-604800 or -1/0)
|
|
31
|
+
*
|
|
32
|
+
* Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.
|
|
33
|
+
*/
|
|
34
|
+
declare function parseSuspendTimeout(input: false | string | number | undefined): {
|
|
35
|
+
seconds: number;
|
|
36
|
+
} | {
|
|
37
|
+
error: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Format a suspend timeout value from API seconds back to the user-facing type.
|
|
41
|
+
* Returns `false` for -1 (never suspend), `undefined` for 0 (default), or a duration string.
|
|
42
|
+
*/
|
|
43
|
+
declare function formatSuspendTimeout(seconds: number): false | string | undefined;
|
|
44
|
+
//#endregion
|
|
45
|
+
export { formatDurationSeconds, formatSuspendTimeout, parseDuration, parseSuspendTimeout };
|
|
46
|
+
//# sourceMappingURL=duration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","names":[],"sources":["../../src/lib/duration.ts"],"mappings":";;AAUA;AAoDA;AA+BA;AA+CA;;;;;;iBAlIgB,aAAA;;;;;;;;;;iBAoDA,qBAAA;;;;;;;;;;;;iBA+BA,mBAAA;;;;;;;;;iBA+CA,oBAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
//#region src/lib/duration.ts
|
|
2
|
+
/**
|
|
3
|
+
* Parse a TTL value into whole seconds.
|
|
4
|
+
*
|
|
5
|
+
* Accepted formats:
|
|
6
|
+
* - a positive finite number → interpreted as seconds (must be an integer)
|
|
7
|
+
* - a positive integer string ("3600") → seconds
|
|
8
|
+
* - `<number><unit>` where unit is one of `s`, `m`, `h`, `d`, `w` (e.g. `30s`, `5m`, `1h`, `7d`, `2w`)
|
|
9
|
+
*
|
|
10
|
+
* Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.
|
|
11
|
+
*/
|
|
12
|
+
function parseDuration(input) {
|
|
13
|
+
if (typeof input === "number") {
|
|
14
|
+
if (!Number.isFinite(input)) return { error: `not a finite number: ${input}` };
|
|
15
|
+
if (!Number.isInteger(input)) return { error: `must be an integer when passed as number: ${input}` };
|
|
16
|
+
if (input <= 0) return { error: `must be > 0, got ${input}` };
|
|
17
|
+
return { seconds: input };
|
|
18
|
+
}
|
|
19
|
+
const trimmed = input.trim();
|
|
20
|
+
if (trimmed === "") return { error: "duration string is empty" };
|
|
21
|
+
const numericMatch = /^(\d+)$/.exec(trimmed);
|
|
22
|
+
if (numericMatch) {
|
|
23
|
+
const n = Number(numericMatch[1]);
|
|
24
|
+
if (n <= 0) return { error: `must be > 0, got "${trimmed}"` };
|
|
25
|
+
return { seconds: n };
|
|
26
|
+
}
|
|
27
|
+
const unitMatch = /^(\d+)([smhdw])$/i.exec(trimmed);
|
|
28
|
+
if (!unitMatch) return { error: `invalid duration "${input}"; expected a number followed by one of: s, m, h, d, w (e.g. "30s", "1h", "7d")` };
|
|
29
|
+
const value = Number(unitMatch[1]);
|
|
30
|
+
const unit = unitMatch[2].toLowerCase();
|
|
31
|
+
if (value <= 0) return { error: `must be > 0, got "${trimmed}"` };
|
|
32
|
+
return { seconds: value * UNIT_SECONDS[unit] };
|
|
33
|
+
}
|
|
34
|
+
const UNIT_SECONDS = {
|
|
35
|
+
s: 1,
|
|
36
|
+
m: 60,
|
|
37
|
+
h: 3600,
|
|
38
|
+
d: 1440 * 60,
|
|
39
|
+
w: 10080 * 60
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Render a TTL in seconds back to the canonical "<n><unit>" form. Used for round-trip
|
|
43
|
+
* serialization when {@link pullConfig} emits a TTL value (it always falls back to seconds
|
|
44
|
+
* when no clean unit boundary matches).
|
|
45
|
+
*/
|
|
46
|
+
function formatDurationSeconds(totalSeconds) {
|
|
47
|
+
if (!Number.isFinite(totalSeconds) || totalSeconds <= 0) throw new RangeError(`formatDurationSeconds expected a positive finite number, got ${totalSeconds}`);
|
|
48
|
+
const candidates = [
|
|
49
|
+
["w", UNIT_SECONDS.w],
|
|
50
|
+
["d", UNIT_SECONDS.d],
|
|
51
|
+
["h", UNIT_SECONDS.h],
|
|
52
|
+
["m", UNIT_SECONDS.m]
|
|
53
|
+
];
|
|
54
|
+
for (const [unit, perUnit] of candidates) if (totalSeconds % perUnit === 0) return `${totalSeconds / perUnit}${unit}`;
|
|
55
|
+
return `${totalSeconds}s`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse a suspend timeout value into seconds for the Neon API.
|
|
59
|
+
*
|
|
60
|
+
* Accepted formats:
|
|
61
|
+
* - `false` → -1 (never suspend)
|
|
62
|
+
* - `undefined` → 0 (use platform default)
|
|
63
|
+
* - duration string → parsed seconds ("5m", "1h", "7d")
|
|
64
|
+
* - number → validated seconds (must be 60-604800 or -1/0)
|
|
65
|
+
*
|
|
66
|
+
* Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.
|
|
67
|
+
*/
|
|
68
|
+
function parseSuspendTimeout(input) {
|
|
69
|
+
if (input === false) return { seconds: -1 };
|
|
70
|
+
if (input === void 0) return { seconds: 0 };
|
|
71
|
+
if (typeof input === "number") {
|
|
72
|
+
if (!Number.isFinite(input)) return { error: `not a finite number: ${input}` };
|
|
73
|
+
if (!Number.isInteger(input)) return { error: `must be an integer: ${input}` };
|
|
74
|
+
if (input === -1 || input === 0) return { seconds: input };
|
|
75
|
+
if (input < 60 || input > 604800) return { error: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), got ${input}` };
|
|
76
|
+
return { seconds: input };
|
|
77
|
+
}
|
|
78
|
+
const result = parseDuration(input);
|
|
79
|
+
if ("error" in result) return result;
|
|
80
|
+
const { seconds } = result;
|
|
81
|
+
if (seconds < 60 || seconds > 604800) return { error: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), "${input}" = ${seconds}s` };
|
|
82
|
+
return { seconds };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Format a suspend timeout value from API seconds back to the user-facing type.
|
|
86
|
+
* Returns `false` for -1 (never suspend), `undefined` for 0 (default), or a duration string.
|
|
87
|
+
*/
|
|
88
|
+
function formatSuspendTimeout(seconds) {
|
|
89
|
+
if (seconds === -1) return false;
|
|
90
|
+
if (seconds === 0) return void 0;
|
|
91
|
+
return formatDurationSeconds(seconds);
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
export { formatDurationSeconds, formatSuspendTimeout, parseDuration, parseSuspendTimeout };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","names":[],"sources":["../../src/lib/duration.ts"],"sourcesContent":["/**\n * Parse a TTL value into whole seconds.\n *\n * Accepted formats:\n * - a positive finite number → interpreted as seconds (must be an integer)\n * - a positive integer string (\"3600\") → seconds\n * - `<number><unit>` where unit is one of `s`, `m`, `h`, `d`, `w` (e.g. `30s`, `5m`, `1h`, `7d`, `2w`)\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseDuration(\n\tinput: string | number,\n): { seconds: number } | { error: string } {\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn {\n\t\t\t\terror: `must be an integer when passed as number: ${input}`,\n\t\t\t};\n\t\tif (input <= 0) return { error: `must be > 0, got ${input}` };\n\t\treturn { seconds: input };\n\t}\n\n\tconst trimmed = input.trim();\n\tif (trimmed === \"\") return { error: \"duration string is empty\" };\n\n\tconst numericMatch = /^(\\d+)$/.exec(trimmed);\n\tif (numericMatch) {\n\t\tconst n = Number(numericMatch[1]);\n\t\tif (n <= 0) return { error: `must be > 0, got \"${trimmed}\"` };\n\t\treturn { seconds: n };\n\t}\n\n\tconst unitMatch = /^(\\d+)([smhdw])$/i.exec(trimmed);\n\tif (!unitMatch) {\n\t\treturn {\n\t\t\terror: `invalid duration \"${input}\"; expected a number followed by one of: s, m, h, d, w (e.g. \"30s\", \"1h\", \"7d\")`,\n\t\t};\n\t}\n\n\tconst value = Number(unitMatch[1]);\n\tconst unit = unitMatch[2].toLowerCase() as \"s\" | \"m\" | \"h\" | \"d\" | \"w\";\n\tif (value <= 0) return { error: `must be > 0, got \"${trimmed}\"` };\n\n\tconst seconds = value * UNIT_SECONDS[unit];\n\treturn { seconds };\n}\n\nconst UNIT_SECONDS = {\n\ts: 1,\n\tm: 60,\n\th: 60 * 60,\n\td: 24 * 60 * 60,\n\tw: 7 * 24 * 60 * 60,\n} as const;\n\n/**\n * Render a TTL in seconds back to the canonical \"<n><unit>\" form. Used for round-trip\n * serialization when {@link pullConfig} emits a TTL value (it always falls back to seconds\n * when no clean unit boundary matches).\n */\nexport function formatDurationSeconds(totalSeconds: number): string {\n\tif (!Number.isFinite(totalSeconds) || totalSeconds <= 0) {\n\t\tthrow new RangeError(\n\t\t\t`formatDurationSeconds expected a positive finite number, got ${totalSeconds}`,\n\t\t);\n\t}\n\tconst candidates = [\n\t\t[\"w\", UNIT_SECONDS.w],\n\t\t[\"d\", UNIT_SECONDS.d],\n\t\t[\"h\", UNIT_SECONDS.h],\n\t\t[\"m\", UNIT_SECONDS.m],\n\t] as const;\n\tfor (const [unit, perUnit] of candidates) {\n\t\tif (totalSeconds % perUnit === 0) {\n\t\t\treturn `${totalSeconds / perUnit}${unit}`;\n\t\t}\n\t}\n\treturn `${totalSeconds}s`;\n}\n\n/**\n * Parse a suspend timeout value into seconds for the Neon API.\n *\n * Accepted formats:\n * - `false` → -1 (never suspend)\n * - `undefined` → 0 (use platform default)\n * - duration string → parsed seconds (\"5m\", \"1h\", \"7d\")\n * - number → validated seconds (must be 60-604800 or -1/0)\n *\n * Returns `{ seconds }` on success or `{ error }` on failure. Pure function — never throws.\n */\nexport function parseSuspendTimeout(\n\tinput: false | string | number | undefined,\n): { seconds: number } | { error: string } {\n\t// false means \"never suspend\"\n\tif (input === false) return { seconds: -1 };\n\n\t// undefined means \"use platform default\"\n\tif (input === undefined) return { seconds: 0 };\n\n\t// If it's a number, validate the range\n\tif (typeof input === \"number\") {\n\t\tif (!Number.isFinite(input))\n\t\t\treturn { error: `not a finite number: ${input}` };\n\t\tif (!Number.isInteger(input))\n\t\t\treturn { error: `must be an integer: ${input}` };\n\n\t\t// Allow special values: -1 (never), 0 (default)\n\t\tif (input === -1 || input === 0) return { seconds: input };\n\n\t\t// Validate range for custom timeout: 60s (1 min) to 604800s (1 week)\n\t\tif (input < 60 || input > 604_800) {\n\t\t\treturn {\n\t\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), got ${input}`,\n\t\t\t};\n\t\t}\n\t\treturn { seconds: input };\n\t}\n\n\t// Parse duration string\n\tconst result = parseDuration(input);\n\tif (\"error\" in result) return result;\n\n\t// Validate the parsed duration is in the valid range\n\tconst { seconds } = result;\n\tif (seconds < 60 || seconds > 604_800) {\n\t\treturn {\n\t\t\terror: `suspend timeout must be between 60 and 604800 seconds (1 minute to 1 week), \"${input}\" = ${seconds}s`,\n\t\t};\n\t}\n\n\treturn { seconds };\n}\n\n/**\n * Format a suspend timeout value from API seconds back to the user-facing type.\n * Returns `false` for -1 (never suspend), `undefined` for 0 (default), or a duration string.\n */\nexport function formatSuspendTimeout(\n\tseconds: number,\n): false | string | undefined {\n\tif (seconds === -1) return false; // never suspend\n\tif (seconds === 0) return undefined; // platform default\n\treturn formatDurationSeconds(seconds);\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,cACf,OAC0C;CAC1C,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,CAAC,OAAO,SAAS,KAAK,GACzB,OAAO,EAAE,OAAO,wBAAwB,QAAQ;EACjD,IAAI,CAAC,OAAO,UAAU,KAAK,GAC1B,OAAO,EACN,OAAO,6CAA6C,QACrD;EACD,IAAI,SAAS,GAAG,OAAO,EAAE,OAAO,oBAAoB,QAAQ;EAC5D,OAAO,EAAE,SAAS,MAAM;CACzB;CAEA,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,YAAY,IAAI,OAAO,EAAE,OAAO,2BAA2B;CAE/D,MAAM,eAAe,UAAU,KAAK,OAAO;CAC3C,IAAI,cAAc;EACjB,MAAM,IAAI,OAAO,aAAa,EAAE;EAChC,IAAI,KAAK,GAAG,OAAO,EAAE,OAAO,qBAAqB,QAAQ,GAAG;EAC5D,OAAO,EAAE,SAAS,EAAE;CACrB;CAEA,MAAM,YAAY,oBAAoB,KAAK,OAAO;CAClD,IAAI,CAAC,WACJ,OAAO,EACN,OAAO,qBAAqB,MAAM,iFACnC;CAGD,MAAM,QAAQ,OAAO,UAAU,EAAE;CACjC,MAAM,OAAO,UAAU,GAAG,YAAY;CACtC,IAAI,SAAS,GAAG,OAAO,EAAE,OAAO,qBAAqB,QAAQ,GAAG;CAGhE,OAAO,EAAE,SADO,QAAQ,aAAa,MACpB;AAClB;AAEA,MAAM,eAAe;CACpB,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG,OAAU;CACb,GAAG,QAAc;AAClB;;;;;;AAOA,SAAgB,sBAAsB,cAA8B;CACnE,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,gBAAgB,GACrD,MAAM,IAAI,WACT,gEAAgE,cACjE;CAED,MAAM,aAAa;EAClB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;EACpB,CAAC,KAAK,aAAa,CAAC;CACrB;CACA,KAAK,MAAM,CAAC,MAAM,YAAY,YAC7B,IAAI,eAAe,YAAY,GAC9B,OAAO,GAAG,eAAe,UAAU;CAGrC,OAAO,GAAG,aAAa;AACxB;;;;;;;;;;;;AAaA,SAAgB,oBACf,OAC0C;CAE1C,IAAI,UAAU,OAAO,OAAO,EAAE,SAAS,GAAG;CAG1C,IAAI,UAAU,KAAA,GAAW,OAAO,EAAE,SAAS,EAAE;CAG7C,IAAI,OAAO,UAAU,UAAU;EAC9B,IAAI,CAAC,OAAO,SAAS,KAAK,GACzB,OAAO,EAAE,OAAO,wBAAwB,QAAQ;EACjD,IAAI,CAAC,OAAO,UAAU,KAAK,GAC1B,OAAO,EAAE,OAAO,uBAAuB,QAAQ;EAGhD,IAAI,UAAU,MAAM,UAAU,GAAG,OAAO,EAAE,SAAS,MAAM;EAGzD,IAAI,QAAQ,MAAM,QAAQ,QACzB,OAAO,EACN,OAAO,mFAAmF,QAC3F;EAED,OAAO,EAAE,SAAS,MAAM;CACzB;CAGA,MAAM,SAAS,cAAc,KAAK;CAClC,IAAI,WAAW,QAAQ,OAAO;CAG9B,MAAM,EAAE,YAAY;CACpB,IAAI,UAAU,MAAM,UAAU,QAC7B,OAAO,EACN,OAAO,gFAAgF,MAAM,MAAM,QAAQ,GAC5G;CAGD,OAAO,EAAE,QAAQ;AAClB;;;;;AAMA,SAAgB,qBACf,SAC6B;CAC7B,IAAI,YAAY,IAAI,OAAO;CAC3B,IAAI,YAAY,GAAG,OAAO,KAAA;CAC1B,OAAO,sBAAsB,OAAO;AACrC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ConflictReport } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/errors.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Every code a {@link PlatformError} can carry. Stable identifiers — consumers can rely on
|
|
7
|
+
* these for `instanceof PlatformError && err.code === ErrorCode.…` style checks instead of
|
|
8
|
+
* matching on free-text messages.
|
|
9
|
+
*
|
|
10
|
+
* Grouped by source:
|
|
11
|
+
* - `PLATFORM_INVALID_CONFIG` — `defineConfig` / `configSchema` rejected the input.
|
|
12
|
+
* - `PLATFORM_MISSING_CONTEXT` — no project / branch context could be resolved.
|
|
13
|
+
* - `PLATFORM_PUSH_CONFLICT` — local config conflicts with remote and the caller did not
|
|
14
|
+
* opt in to apply.
|
|
15
|
+
* - `PLATFORM_CONFIG_LOAD_FAILED` — `neon.ts` could not be found or evaluated.
|
|
16
|
+
* - `PLATFORM_MISSING_API_KEY` — no `NEON_API_KEY` and no explicit `apiKey` was provided.
|
|
17
|
+
* - `PLATFORM_MISSING_PARENT_BRANCH` — push tried to create a child of a non-existent
|
|
18
|
+
* branch.
|
|
19
|
+
* - `PLATFORM_UNAUTHORIZED` / `PLATFORM_FORBIDDEN` / `PLATFORM_NOT_FOUND` /
|
|
20
|
+
* `PLATFORM_CONFLICT` / `PLATFORM_RATE_LIMITED` / `PLATFORM_LOCKED` /
|
|
21
|
+
* `PLATFORM_SERVER_ERROR` — wrappings of Neon HTTP failures.
|
|
22
|
+
* - `PLATFORM_NETWORK_ERROR` — transport-level failure (no HTTP response at all).
|
|
23
|
+
* - `PLATFORM_INTERNAL_ERROR` — invariant violations. Should never happen in production;
|
|
24
|
+
* if you see one, please open an issue.
|
|
25
|
+
*/
|
|
26
|
+
declare const ErrorCode: {
|
|
27
|
+
readonly InvalidConfig: "PLATFORM_INVALID_CONFIG";
|
|
28
|
+
readonly EnvNotInjected: "PLATFORM_ENV_NOT_INJECTED";
|
|
29
|
+
readonly MissingContext: "PLATFORM_MISSING_CONTEXT";
|
|
30
|
+
readonly PushConflict: "PLATFORM_PUSH_CONFLICT";
|
|
31
|
+
readonly PushAborted: "PLATFORM_PUSH_ABORTED";
|
|
32
|
+
readonly ConfigLoadFailed: "PLATFORM_CONFIG_LOAD_FAILED";
|
|
33
|
+
readonly MissingApiKey: "PLATFORM_MISSING_API_KEY";
|
|
34
|
+
readonly AmbiguousBranchAuth: "PLATFORM_AMBIGUOUS_BRANCH_AUTH";
|
|
35
|
+
readonly BranchNotFound: "PLATFORM_BRANCH_NOT_FOUND";
|
|
36
|
+
readonly MissingParentBranch: "PLATFORM_MISSING_PARENT_BRANCH";
|
|
37
|
+
readonly Unauthorized: "PLATFORM_UNAUTHORIZED";
|
|
38
|
+
readonly Forbidden: "PLATFORM_FORBIDDEN";
|
|
39
|
+
readonly NotFound: "PLATFORM_NOT_FOUND";
|
|
40
|
+
readonly Conflict: "PLATFORM_CONFLICT";
|
|
41
|
+
readonly RateLimited: "PLATFORM_RATE_LIMITED";
|
|
42
|
+
readonly Locked: "PLATFORM_LOCKED";
|
|
43
|
+
readonly ServerError: "PLATFORM_SERVER_ERROR";
|
|
44
|
+
readonly NetworkError: "PLATFORM_NETWORK_ERROR";
|
|
45
|
+
readonly InternalError: "PLATFORM_INTERNAL_ERROR";
|
|
46
|
+
};
|
|
47
|
+
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
48
|
+
/**
|
|
49
|
+
* Base class for all errors thrown by `@neondatabase/config`. Always extend this so callers
|
|
50
|
+
* can catch every package-thrown error with a single `instanceof` check.
|
|
51
|
+
*
|
|
52
|
+
* Optional `details` carries structured context that the CLI prints under `--debug` and
|
|
53
|
+
* that programmatic consumers can read (e.g. `details.status` for HTTP wrappings,
|
|
54
|
+
* `details.requestId` for Neon API failures).
|
|
55
|
+
*/
|
|
56
|
+
declare class PlatformError extends Error {
|
|
57
|
+
readonly name: string;
|
|
58
|
+
readonly code: string;
|
|
59
|
+
readonly details: Readonly<Record<string, unknown>>;
|
|
60
|
+
constructor(code: string, message: string, options?: {
|
|
61
|
+
cause?: unknown;
|
|
62
|
+
details?: Record<string, unknown>;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Append a "report-a-bug" footer to an error message. Used only on truly unreachable
|
|
67
|
+
* internal errors — never on user-facing validation / configuration errors where the user
|
|
68
|
+
* is supposed to fix something on their end.
|
|
69
|
+
*/
|
|
70
|
+
declare function bugReportFooter(): string;
|
|
71
|
+
/**
|
|
72
|
+
* Thrown by {@link defineConfig} when the user-provided configuration object is invalid.
|
|
73
|
+
*
|
|
74
|
+
* The class collects every validation failure rather than throwing on the first one so that
|
|
75
|
+
* users get a complete picture of what is wrong with their `neon.ts`.
|
|
76
|
+
*/
|
|
77
|
+
declare class ConfigValidationError extends PlatformError {
|
|
78
|
+
readonly name = "ConfigValidationError";
|
|
79
|
+
readonly issues: readonly string[];
|
|
80
|
+
constructor(issues: readonly string[]);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Thrown when the package cannot resolve which Neon project to operate on.
|
|
84
|
+
*
|
|
85
|
+
* Per the package's read-only-filesystem contract, we never create a `.neon` context file;
|
|
86
|
+
* callers must either pass `projectId`/`orgId` explicitly or rely on an existing context file
|
|
87
|
+
* (`.neon/project.json` or neonctl's `.neon`).
|
|
88
|
+
*/
|
|
89
|
+
declare class MissingContextError extends PlatformError {
|
|
90
|
+
readonly name = "MissingContextError";
|
|
91
|
+
constructor(message: string);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Thrown by {@link pushConfig} when it detects differences between the local config and
|
|
95
|
+
* the remote project that the caller hasn't opted in to apply.
|
|
96
|
+
*
|
|
97
|
+
* The message lists every conflict with both the current and desired value plus a
|
|
98
|
+
* per-conflict hint. Mutable branch drift is applied by passing `updateExisting: true`.
|
|
99
|
+
*/
|
|
100
|
+
declare class PushConflictError extends PlatformError {
|
|
101
|
+
readonly name = "PushConflictError";
|
|
102
|
+
readonly conflicts: readonly ConflictReport[];
|
|
103
|
+
constructor(conflicts: readonly ConflictReport[]);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Thrown by {@link pushConfig} when the caller-supplied `confirm` callback declines a
|
|
107
|
+
* push that requires confirmation (protected branch and/or mutable drift overriding
|
|
108
|
+
* existing remote settings).
|
|
109
|
+
*
|
|
110
|
+
* The CLI maps this to a non-zero exit so users see "aborted" rather than a stack trace.
|
|
111
|
+
*/
|
|
112
|
+
declare class PushAbortedError extends PlatformError {
|
|
113
|
+
readonly name = "PushAbortedError";
|
|
114
|
+
readonly branchName: string;
|
|
115
|
+
readonly reasons: readonly ("protected-branch" | "override-updates")[];
|
|
116
|
+
constructor(branchName: string, reasons: readonly ("protected-branch" | "override-updates")[]);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Thrown when the SDK fails to find or load a `neon.ts` config file.
|
|
120
|
+
*/
|
|
121
|
+
declare class ConfigLoadError extends PlatformError {
|
|
122
|
+
readonly name = "ConfigLoadError";
|
|
123
|
+
constructor(message: string, options?: {
|
|
124
|
+
cause?: unknown;
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
export { ConfigLoadError, ConfigValidationError, ErrorCode, MissingContextError, PlatformError, PushAbortedError, PushConflictError, bugReportFooter };
|
|
129
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","names":[],"sources":["../../src/lib/errors.ts"],"mappings":";;;;;;AAuBA;AAqBA;;;;AAAiE;AAYjE;;;;;;AAAwC;AAqBxC;AAUA;AAoBA;AAeA;;;AAIiC,cAvGpB,SAuGoB,EAAA;WAJM,aAAA,EAAA,yBAAA;EAAa,SAAA,cAAA,EAAA,2BAAA;EAqDvC,SAAA,cAAiB,EAAA,0BAAqB;EA8BtC,SAAA,YAAgB,EAAA,wBAAqB;;;;;;;;;;;;;;;;;KAjKtC,SAAA,WAAoB,wBAAwB;;;;;;;;;cAY3C,aAAA,SAAsB,KAAA;;;oBAGhB,SAAS;;;cAKa;;;;;;;;iBAazB,eAAA,CAAA;;;;;;;cAUH,qBAAA,SAA8B,aAAa;;;;;;;;;;;;cAoB3C,mBAAA,SAA4B,aAAa;;;;;;;;;;;cAezC,iBAAA,SAA0B,aAAA;;+BAET;kCAEG;;;;;;;;;cAiDpB,gBAAA,SAAyB,aAAa;;;;;;;;;cA8BtC,eAAA,SAAwB,aAAa"}
|