@neon/config 0.0.0 → 0.8.1
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/README.md +146 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +10 -0
- package/dist/lib/auth.d.ts +67 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +107 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/credentials.d.ts +37 -0
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +30 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/define-config.d.ts +123 -0
- package/dist/lib/define-config.d.ts.map +1 -0
- package/dist/lib/define-config.js +168 -0
- package/dist/lib/define-config.js.map +1 -0
- package/dist/lib/diff.d.ts +120 -0
- package/dist/lib/diff.d.ts.map +1 -0
- package/dist/lib/diff.js +284 -0
- package/dist/lib/diff.js.map +1 -0
- package/dist/lib/duration.d.ts +68 -0
- package/dist/lib/duration.d.ts.map +1 -0
- package/dist/lib/duration.js +111 -0
- package/dist/lib/duration.js.map +1 -0
- package/dist/lib/errors.d.ts +140 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +185 -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 +120 -0
- package/dist/lib/loader.js.map +1 -0
- package/dist/lib/neon-api-real.d.ts +92 -0
- package/dist/lib/neon-api-real.d.ts.map +1 -0
- package/dist/lib/neon-api-real.js +957 -0
- package/dist/lib/neon-api-real.js.map +1 -0
- package/dist/lib/neon-api.d.ts +373 -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 +215 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +284 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/types.d.ts +546 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +18 -0
- package/dist/lib/types.js.map +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 +211 -0
- package/dist/v1.d.ts.map +1 -0
- package/dist/v1.js +82 -0
- package/dist/v1.js.map +1 -0
- package/package.json +57 -18
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { BranchTarget, BranchTuningFn, BucketDef, Config, DataApiInput, FunctionDef, PreviewInput, ResolvedBranchConfig, ServiceEnabled, ServiceToggleInput } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/lib/define-config.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is
|
|
7
|
+
* on (see {@link ServiceEnabled}) and not the explicit `authProvider: "external"` variant
|
|
8
|
+
* (so the default / `"neon"` provider). This is the case that requires top-level Neon Auth.
|
|
9
|
+
*/
|
|
10
|
+
type DataApiUsesNeonAuth<DataApi> = ServiceEnabled<DataApi> extends true ? [DataApi] extends [{
|
|
11
|
+
authProvider: "external";
|
|
12
|
+
}] ? false : true : false;
|
|
13
|
+
/**
|
|
14
|
+
* Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data
|
|
15
|
+
* API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the
|
|
16
|
+
* offending value against this string literal — `Type 'true' is not assignable to type
|
|
17
|
+
* '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque
|
|
18
|
+
* `Type 'true' is not assignable to type 'never'` an intersection guard produces.
|
|
19
|
+
*
|
|
20
|
+
* It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API
|
|
21
|
+
* *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).
|
|
22
|
+
*/
|
|
23
|
+
type NeonAuthRequiredHint = "`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`";
|
|
24
|
+
/**
|
|
25
|
+
* Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`
|
|
26
|
+
* field** rather than an intersected requirement on `auth`.
|
|
27
|
+
*
|
|
28
|
+
* - A Neon-Auth Data API (`authProvider: "neon"`, the default) with top-level `auth` enabled,
|
|
29
|
+
* or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the
|
|
30
|
+
* `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the
|
|
31
|
+
* returned {@link Config}).
|
|
32
|
+
* - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to
|
|
33
|
+
* the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)
|
|
34
|
+
* right on the `dataApi` value.
|
|
35
|
+
*
|
|
36
|
+
* The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for
|
|
37
|
+
* non-typed (plain-JS) callers, so the behavior is identical — only the type-level message
|
|
38
|
+
* changes.
|
|
39
|
+
*/
|
|
40
|
+
type DataApiField<Auth, DataApi> = DataApiUsesNeonAuth<DataApi> extends true ? ServiceEnabled<Auth> extends true ? DataApi & DataApiInput : NeonAuthRequiredHint : DataApi & DataApiInput;
|
|
41
|
+
/**
|
|
42
|
+
* Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.
|
|
43
|
+
*
|
|
44
|
+
* {@link PreviewInput} types those records with a string index signature
|
|
45
|
+
* (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers
|
|
46
|
+
* `const Preview`, every authored slug becomes a **named** property on the inferred literal
|
|
47
|
+
* (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature
|
|
48
|
+
* when the editor computes the contextual type of that slug's value — so the rest of
|
|
49
|
+
* {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as
|
|
50
|
+
* completions inside `hello: { … }` / `uploads: { … }`.
|
|
51
|
+
*
|
|
52
|
+
* Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via
|
|
53
|
+
* a mapped type over the already-inferred keys) puts those members back onto the contextual
|
|
54
|
+
* type without going through an index signature, which restores autocomplete. Intersected with
|
|
55
|
+
* `Preview & PreviewInput` it neither widens what is accepted (the values were already
|
|
56
|
+
* `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for
|
|
57
|
+
* `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.
|
|
58
|
+
*/
|
|
59
|
+
type PreviewAutocomplete<Preview> = (Preview extends {
|
|
60
|
+
functions: infer F;
|
|
61
|
+
} ? {
|
|
62
|
+
functions: { [Slug in keyof F]: FunctionDef };
|
|
63
|
+
} : unknown) & (Preview extends {
|
|
64
|
+
buckets: infer B;
|
|
65
|
+
} ? {
|
|
66
|
+
buckets: { [Name in keyof B]: BucketDef };
|
|
67
|
+
} : unknown);
|
|
68
|
+
/**
|
|
69
|
+
* Validate and freeze a Neon Platform branch policy.
|
|
70
|
+
*
|
|
71
|
+
* Used at the top of `neon.ts`:
|
|
72
|
+
* ```ts
|
|
73
|
+
* import { defineConfig } from "@neon/config/v1";
|
|
74
|
+
*
|
|
75
|
+
* export default defineConfig({
|
|
76
|
+
* auth: true,
|
|
77
|
+
* preview: {
|
|
78
|
+
* functions: {
|
|
79
|
+
* hello: { name: "Hello", source: "./functions/hello.ts", dev: { port: 8787 } },
|
|
80
|
+
* },
|
|
81
|
+
* },
|
|
82
|
+
* branch: (branch) => ({ protected: branch.name === "main" }),
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* The policy is split into a **static** existential set (top-level `auth` / `dataApi`
|
|
87
|
+
* toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The
|
|
88
|
+
* static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`
|
|
89
|
+
* are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function
|
|
90
|
+
* deploy settings), never change what exists.
|
|
91
|
+
*
|
|
92
|
+
* The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch
|
|
93
|
+
* being decided for (not a live handle); switch on its facts (`branch.name`,
|
|
94
|
+
* `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two
|
|
95
|
+
* modes: against an existing branch (fields populated from Neon) and during pre-create
|
|
96
|
+
* evaluation (`exists: false`, `id` undefined).
|
|
97
|
+
*
|
|
98
|
+
* Pure: no I/O, no side effects. The static parts are validated here; the closure's output
|
|
99
|
+
* is validated every time it is evaluated so errors point at the concrete branch target.
|
|
100
|
+
*/
|
|
101
|
+
declare function defineConfig<const Auth extends ServiceToggleInput | undefined = undefined, const DataApi extends DataApiInput | undefined = undefined, const Preview extends PreviewInput | undefined = undefined>(input: {
|
|
102
|
+
auth?: Auth & ServiceToggleInput;
|
|
103
|
+
dataApi?: DataApiField<Auth, DataApi>;
|
|
104
|
+
preview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;
|
|
105
|
+
branch?: BranchTuningFn<Preview>;
|
|
106
|
+
}): Config<Auth, DataApi, Preview>;
|
|
107
|
+
/**
|
|
108
|
+
* Evaluate a branch policy for a specific branch target and return a normalized config.
|
|
109
|
+
*
|
|
110
|
+
* Merges the static existential set (services + preview functions/buckets) with the
|
|
111
|
+
* per-branch tuning returned by the `branch` closure into the same {@link
|
|
112
|
+
* ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.
|
|
113
|
+
*/
|
|
114
|
+
declare function resolveConfig(config: Config, branch: BranchTarget): ResolvedBranchConfig;
|
|
115
|
+
/**
|
|
116
|
+
* Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes
|
|
117
|
+
* `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the
|
|
118
|
+
* NeonApi adapter.
|
|
119
|
+
*/
|
|
120
|
+
declare function normalizeRegion(region: string): string;
|
|
121
|
+
//#endregion
|
|
122
|
+
export { DataApiField, NeonAuthRequiredHint, defineConfig, normalizeRegion, resolveConfig };
|
|
123
|
+
//# sourceMappingURL=define-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-config.d.ts","names":[],"sources":["../../src/lib/define-config.ts"],"mappings":";;;;;;AAwBoB;;;KAYf,mBAA+B,CAAA,OAAA,CAAA,GAAA,cAAA,CAAe,OAAf,CAAA,SAAA,IAAA,GAAA,CAChC,OADgC,CAAA,SAAA,CAAA;cAChC,EAAA,UAAA;AAAO,CAAA,CAAA,GAAA,KAAA,GAAA,IAAA,GAAA,KAAA;AAiBX;AAqBA;;;;;;;;;AAKI,KA1BQ,oBAAA,GA0BR,+QAAA;;AAAsB;AAAC;;;;;;;;AAwBkB;AAoC7C;;;;;AAWQ,KA5EI,YA4EJ,CAAA,IAAA,EAAA,OAAA,CAAA,GA3EP,mBA2EO,CA3Ea,OA2Eb,CAAA,SAAA,IAAA,GA1EJ,cA0EI,CA1EW,IA0EX,CAAA,SAAA,IAAA,GAzEH,OAyEG,GAzEO,YAyEP,GAxEH,oBAwEG,GAvEJ,OAuEI,GAvEM,YAuEN;;;;;;;;;;;;;;;AAUE;AA4BV;;;KAzFK,mBA2FI,CAAA,OAAA,CAAA,GAAA,CA3F4B,OA2F5B,SAAA;WACN,EAAA,KAAA,EAAA;AAAoB,CAAA,GAAA;EAgKP,SAAA,EAAA,iBA3PiB,IAAI;gBAEnC;;;4BAC8B,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoCpB,gCACI,kEACG,4DACA;SAQf,OAAO;YAIJ,aAAa,MAAM;YAInB,UAAU,eAAe,oBAAoB;WAC9C,eAAe;IACrB,OAAO,MAAM,SAAS;;;;;;;;iBA4BV,aAAA,SACP,gBACA,eACN;;;;;;iBAgKa,eAAA"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { ConfigValidationError } from "./errors.js";
|
|
2
|
+
import { parseBranchTtl } from "./duration.js";
|
|
3
|
+
import { branchTuningSchema, configInputSchema, formatZodIssues } from "./schema.js";
|
|
4
|
+
//#region src/lib/define-config.ts
|
|
5
|
+
/** Default deploy parameters applied to functions that omit them in `neon.ts`. */
|
|
6
|
+
const DEFAULT_FUNCTION_RUNTIME = "nodejs24";
|
|
7
|
+
const REGION_PREFIX = /^(aws|azure|gcp)-/;
|
|
8
|
+
/**
|
|
9
|
+
* Validate and freeze a Neon Platform branch policy.
|
|
10
|
+
*
|
|
11
|
+
* Used at the top of `neon.ts`:
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { defineConfig } from "@neon/config/v1";
|
|
14
|
+
*
|
|
15
|
+
* export default defineConfig({
|
|
16
|
+
* auth: true,
|
|
17
|
+
* preview: {
|
|
18
|
+
* functions: {
|
|
19
|
+
* hello: { name: "Hello", source: "./functions/hello.ts", dev: { port: 8787 } },
|
|
20
|
+
* },
|
|
21
|
+
* },
|
|
22
|
+
* branch: (branch) => ({ protected: branch.name === "main" }),
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The policy is split into a **static** existential set (top-level `auth` / `dataApi`
|
|
27
|
+
* toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The
|
|
28
|
+
* static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`
|
|
29
|
+
* are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function
|
|
30
|
+
* deploy settings), never change what exists.
|
|
31
|
+
*
|
|
32
|
+
* The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch
|
|
33
|
+
* being decided for (not a live handle); switch on its facts (`branch.name`,
|
|
34
|
+
* `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two
|
|
35
|
+
* modes: against an existing branch (fields populated from Neon) and during pre-create
|
|
36
|
+
* evaluation (`exists: false`, `id` undefined).
|
|
37
|
+
*
|
|
38
|
+
* Pure: no I/O, no side effects. The static parts are validated here; the closure's output
|
|
39
|
+
* is validated every time it is evaluated so errors point at the concrete branch target.
|
|
40
|
+
*/
|
|
41
|
+
function defineConfig(input) {
|
|
42
|
+
if (typeof input === "function") throw new ConfigValidationError(["defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.", "The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure."]);
|
|
43
|
+
if (input === null || typeof input !== "object") throw new ConfigValidationError(["defineConfig expects a configuration object: `export default defineConfig({ … })`."]);
|
|
44
|
+
const parsed = configInputSchema.safeParse(input);
|
|
45
|
+
if (!parsed.success) throw new ConfigValidationError(formatZodIssues(parsed.error));
|
|
46
|
+
return Object.freeze({ ...input });
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Evaluate a branch policy for a specific branch target and return a normalized config.
|
|
50
|
+
*
|
|
51
|
+
* Merges the static existential set (services + preview functions/buckets) with the
|
|
52
|
+
* per-branch tuning returned by the `branch` closure into the same {@link
|
|
53
|
+
* ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.
|
|
54
|
+
*/
|
|
55
|
+
function resolveConfig(config, branch) {
|
|
56
|
+
const tuning = evaluateBranchTuning(config.branch, branch);
|
|
57
|
+
const resolved = {
|
|
58
|
+
authEnabled: isServiceEnabled(config.auth),
|
|
59
|
+
dataApiEnabled: isDataApiEnabled(config.dataApi)
|
|
60
|
+
};
|
|
61
|
+
const dataApi = resolveDataApi(config.dataApi);
|
|
62
|
+
if (dataApi) resolved.dataApi = dataApi;
|
|
63
|
+
if (tuning.parent !== void 0) resolved.parent = tuning.parent;
|
|
64
|
+
if (tuning.ttl !== void 0) {
|
|
65
|
+
const parsedTtl = parseBranchTtl(tuning.ttl);
|
|
66
|
+
if (!("error" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;
|
|
67
|
+
}
|
|
68
|
+
if (tuning.protected !== void 0) resolved.protected = tuning.protected;
|
|
69
|
+
if (tuning.postgres?.computeSettings) resolved.postgres = { computeSettings: { ...tuning.postgres.computeSettings } };
|
|
70
|
+
const preview = resolvePreviewConfig(config.preview, tuning);
|
|
71
|
+
if (preview) resolved.preview = preview;
|
|
72
|
+
return resolved;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Run the `branch` closure (when present) for the target and validate its output. The
|
|
76
|
+
* closure is optional — a fully static policy resolves with empty tuning.
|
|
77
|
+
*/
|
|
78
|
+
function evaluateBranchTuning(branchFn, target) {
|
|
79
|
+
if (!branchFn) return {};
|
|
80
|
+
let raw;
|
|
81
|
+
try {
|
|
82
|
+
raw = branchFn(Object.freeze({ ...target }));
|
|
83
|
+
} catch (cause) {
|
|
84
|
+
throw new ConfigValidationError([`Branch policy threw while evaluating branch "${target.name}".`, cause?.message ?? String(cause)]);
|
|
85
|
+
}
|
|
86
|
+
const parsed = branchTuningSchema.safeParse(raw ?? {});
|
|
87
|
+
if (!parsed.success) throw new ConfigValidationError(formatZodIssues(parsed.error));
|
|
88
|
+
return parsed.data;
|
|
89
|
+
}
|
|
90
|
+
function isServiceEnabled(toggle) {
|
|
91
|
+
if (toggle === void 0) return false;
|
|
92
|
+
if (typeof toggle === "boolean") return toggle;
|
|
93
|
+
return toggle.enabled !== false;
|
|
94
|
+
}
|
|
95
|
+
/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */
|
|
96
|
+
function isDataApiEnabled(input) {
|
|
97
|
+
if (input === void 0) return false;
|
|
98
|
+
if (typeof input === "boolean") return input;
|
|
99
|
+
return input.enabled !== false;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when
|
|
103
|
+
* the Data API is not enabled. `authProvider` defaults to `"neon"`; the external-IdP wiring
|
|
104
|
+
* is carried through only for the `"external"` provider; `settings` is copied with its
|
|
105
|
+
* `undefined` entries dropped so diffing only considers fields the policy actually set.
|
|
106
|
+
*/
|
|
107
|
+
function resolveDataApi(input) {
|
|
108
|
+
if (!isDataApiEnabled(input)) return void 0;
|
|
109
|
+
if (typeof input !== "object") return { authProvider: "neon" };
|
|
110
|
+
const authProvider = input.authProvider ?? "neon";
|
|
111
|
+
const resolved = { authProvider };
|
|
112
|
+
if (authProvider === "external") {
|
|
113
|
+
if (input.jwksUrl !== void 0) resolved.jwksUrl = input.jwksUrl;
|
|
114
|
+
if (input.providerName !== void 0) resolved.providerName = input.providerName;
|
|
115
|
+
if (input.jwtAudience !== void 0) resolved.jwtAudience = input.jwtAudience;
|
|
116
|
+
}
|
|
117
|
+
const settings = normalizeDataApiSettings(input.settings);
|
|
118
|
+
if (settings) resolved.settings = settings;
|
|
119
|
+
return resolved;
|
|
120
|
+
}
|
|
121
|
+
/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */
|
|
122
|
+
function normalizeDataApiSettings(settings) {
|
|
123
|
+
if (!settings) return void 0;
|
|
124
|
+
const out = {};
|
|
125
|
+
for (const [key, value] of Object.entries(settings)) if (value !== void 0) out[key] = value;
|
|
126
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a
|
|
130
|
+
* {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`
|
|
131
|
+
* block so the field can be omitted entirely. Function slugs / bucket names come from the
|
|
132
|
+
* record keys.
|
|
133
|
+
*/
|
|
134
|
+
function resolvePreviewConfig(preview, tuning) {
|
|
135
|
+
if (!preview) return void 0;
|
|
136
|
+
const fnTuning = tuning.preview?.functions ?? {};
|
|
137
|
+
return {
|
|
138
|
+
functions: Object.entries(preview.functions ?? {}).map(([slug, def]) => resolveFunctionConfig(slug, def, fnTuning[slug] ?? {})),
|
|
139
|
+
buckets: Object.entries(preview.buckets ?? {}).map(([name, def]) => ({
|
|
140
|
+
name,
|
|
141
|
+
access: def.access ?? "private"
|
|
142
|
+
})),
|
|
143
|
+
aiGatewayEnabled: isServiceEnabled(preview.aiGateway)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function resolveFunctionConfig(slug, def, tuning) {
|
|
147
|
+
return {
|
|
148
|
+
slug,
|
|
149
|
+
name: def.name,
|
|
150
|
+
source: def.source,
|
|
151
|
+
env: { ...def.env ?? {} },
|
|
152
|
+
runtime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,
|
|
153
|
+
...def.dev ? { dev: def.dev } : {}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes
|
|
158
|
+
* `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the
|
|
159
|
+
* NeonApi adapter.
|
|
160
|
+
*/
|
|
161
|
+
function normalizeRegion(region) {
|
|
162
|
+
if (REGION_PREFIX.test(region)) return region;
|
|
163
|
+
return `aws-${region}`;
|
|
164
|
+
}
|
|
165
|
+
//#endregion
|
|
166
|
+
export { defineConfig, normalizeRegion, resolveConfig };
|
|
167
|
+
|
|
168
|
+
//# sourceMappingURL=define-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"define-config.js","names":[],"sources":["../../src/lib/define-config.ts"],"sourcesContent":["import { parseBranchTtl } from \"./duration.js\";\nimport { ConfigValidationError } from \"./errors.js\";\nimport {\n\tbranchTuningSchema,\n\tconfigInputSchema,\n\tformatZodIssues,\n} from \"./schema.js\";\nimport type {\n\tBranchTarget,\n\tBranchTuning,\n\tBranchTuningFn,\n\tBucketDef,\n\tConfig,\n\tDataApiInput,\n\tDataApiSettings,\n\tFunctionDef,\n\tFunctionTuning,\n\tPreviewInput,\n\tResolvedBranchConfig,\n\tResolvedDataApiConfig,\n\tResolvedFunctionConfig,\n\tResolvedPreviewConfig,\n\tServiceEnabled,\n\tServiceToggleInput,\n} from \"./types.js\";\n\n/** Default deploy parameters applied to functions that omit them in `neon.ts`. */\nconst DEFAULT_FUNCTION_RUNTIME = \"nodejs24\" as const;\n\nconst REGION_PREFIX = /^(aws|azure|gcp)-/;\n\n/**\n * Whether a `dataApi` toggle is **enabled and verified by Neon Auth** at the type level: it is\n * on (see {@link ServiceEnabled}) and not the explicit `authProvider: \"external\"` variant\n * (so the default / `\"neon\"` provider). This is the case that requires top-level Neon Auth.\n */\ntype DataApiUsesNeonAuth<DataApi> = ServiceEnabled<DataApi> extends true\n\t? [DataApi] extends [{ authProvider: \"external\" }]\n\t\t? false\n\t\t: true\n\t: false;\n\n/**\n * Human-readable hint surfaced as the **expected type** of `dataApi` when a Neon-Auth Data\n * API is declared without Neon Auth enabled (see {@link DataApiField}). TypeScript prints the\n * offending value against this string literal — `Type 'true' is not assignable to type\n * '…requires `auth: true`…'` — which points straight at the fix, instead of the opaque\n * `Type 'true' is not assignable to type 'never'` an intersection guard produces.\n *\n * It documents **both** fixes: enabling Neon Auth (`auth: true`), and running the Data API\n * *without* Neon Auth by verifying a third-party IdP (`authProvider: 'external'` + `jwksUrl`).\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type NeonAuthRequiredHint =\n\t\"`dataApi` with Neon Auth (the default `authProvider: 'neon'`) requires Neon Auth, so add `auth: true`. To enable the Data API WITHOUT Neon Auth, verify a third-party IdP instead: `dataApi: { authProvider: 'external', jwksUrl: 'https://your-idp/.well-known/jwks.json' }`\";\n\n/**\n * Static cross-field guard for {@link defineConfig}, expressed as the **type of the `dataApi`\n * field** rather than an intersected requirement on `auth`.\n *\n * - A Neon-Auth Data API (`authProvider: \"neon\"`, the default) with top-level `auth` enabled,\n * or any external Data API: the field keeps its normal `DataApi & DataApiInput` type (the\n * `& DataApiInput` preserves member autocomplete; the `const DataApi` still types the\n * returned {@link Config}).\n * - A Neon-Auth Data API **without** `auth` enabled: the field's expected type collapses to\n * the {@link NeonAuthRequiredHint} message, so the author sees the rule (and the two fixes)\n * right on the `dataApi` value.\n *\n * The runtime `superRefine` in {@link configInputSchema} enforces the same invariant for\n * non-typed (plain-JS) callers, so the behavior is identical — only the type-level message\n * changes.\n */\n// Exported (type-only) for the type tests in `define-config.test-d.ts`; intentionally not\n// re-exported from `v1.ts` / `index.ts`, so it stays an internal implementation detail.\nexport type DataApiField<Auth, DataApi> =\n\tDataApiUsesNeonAuth<DataApi> extends true\n\t\t? ServiceEnabled<Auth> extends true\n\t\t\t? DataApi & DataApiInput\n\t\t\t: NeonAuthRequiredHint\n\t\t: DataApi & DataApiInput;\n\n/**\n * Autocomplete bridge for the nested `preview.functions` / `preview.buckets` slug objects.\n *\n * {@link PreviewInput} types those records with a string index signature\n * (`Record<string, FunctionDef>` / `Record<string, BucketDef>`). When `defineConfig` infers\n * `const Preview`, every authored slug becomes a **named** property on the inferred literal\n * (e.g. `{ hello: { name; source } }`), and a named property **shadows** the index signature\n * when the editor computes the contextual type of that slug's value — so the rest of\n * {@link FunctionDef} / {@link BucketDef} (`env`, `dev`, `access`, …) never surfaces as\n * completions inside `hello: { … }` / `uploads: { … }`.\n *\n * Re-declaring each inferred slug's value as `FunctionDef` / `BucketDef` (a *named* member, via\n * a mapped type over the already-inferred keys) puts those members back onto the contextual\n * type without going through an index signature, which restores autocomplete. Intersected with\n * `Preview & PreviewInput` it neither widens what is accepted (the values were already\n * `FunctionDef` / `BucketDef`) nor perturbs the inferred `const Preview` — so slug inference for\n * `BranchTuningFn<Preview>` and the returned {@link Config} is unchanged.\n */\ntype PreviewAutocomplete<Preview> = (Preview extends { functions: infer F }\n\t? { functions: { [Slug in keyof F]: FunctionDef } }\n\t: unknown) &\n\t(Preview extends { buckets: infer B }\n\t\t? { buckets: { [Name in keyof B]: BucketDef } }\n\t\t: unknown);\n\n/**\n * Validate and freeze a Neon Platform branch policy.\n *\n * Used at the top of `neon.ts`:\n * ```ts\n * import { defineConfig } from \"@neon/config/v1\";\n *\n * export default defineConfig({\n * auth: true,\n * preview: {\n * functions: {\n * hello: { name: \"Hello\", source: \"./functions/hello.ts\", dev: { port: 8787 } },\n * },\n * },\n * branch: (branch) => ({ protected: branch.name === \"main\" }),\n * });\n * ```\n *\n * The policy is split into a **static** existential set (top-level `auth` / `dataApi`\n * toggles and the beta `preview` block) and a **dynamic** per-branch `branch` closure. The\n * static half determines which secrets exist — so `NeonEnv<typeof config>` and `parseEnv`\n * are exact — while the closure can only *tune* a branch (lifecycle, compute, per-function\n * deploy settings), never change what exists.\n *\n * The `branch` callback receives a read-only {@link BranchTarget} descriptor of the branch\n * being decided for (not a live handle); switch on its facts (`branch.name`,\n * `branch.isDefault`, `branch.exists`, …) and **return** the desired tuning. It runs in two\n * modes: against an existing branch (fields populated from Neon) and during pre-create\n * evaluation (`exists: false`, `id` undefined).\n *\n * Pure: no I/O, no side effects. The static parts are validated here; the closure's output\n * is validated every time it is evaluated so errors point at the concrete branch target.\n */\nexport function defineConfig<\n\tconst Auth extends ServiceToggleInput | undefined = undefined,\n\tconst DataApi extends DataApiInput | undefined = undefined,\n\tconst Preview extends PreviewInput | undefined = undefined,\n>(input: {\n\t// Each field is intersected with its concrete interface (not just typed as the bare\n\t// generic). The generic alone — e.g. `preview?: Preview` — gives editors no members to\n\t// complete against in the object-literal position (they see `{} | undefined`), so you\n\t// lose hints for `aiGateway` / `functions` / `buckets`. `& PreviewInput` restores the\n\t// full shape for autocomplete while still inferring the `const` literal that types the\n\t// `branch` closure's slugs (BranchTuningFn<Preview>) and the returned Config.\n\tauth?: Auth & ServiceToggleInput;\n\t// The `dataApi` field carries the Neon-Auth cross-field guard at the type level (see\n\t// `DataApiField`): a Neon-Auth Data API without `auth` enabled surfaces a readable hint\n\t// as the field's expected type instead of collapsing the value to `never`.\n\tdataApi?: DataApiField<Auth, DataApi>;\n\t// `& PreviewInput` restores top-level member hints (aiGateway/functions/buckets);\n\t// `& PreviewAutocomplete<Preview>` restores hints *inside* each function/bucket slug\n\t// object (see `PreviewAutocomplete`), which the bare index signature otherwise hides.\n\tpreview?: Preview & PreviewInput & PreviewAutocomplete<Preview>;\n\tbranch?: BranchTuningFn<Preview>;\n}): Config<Auth, DataApi, Preview> {\n\tif (typeof input === \"function\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig now expects an object, not a function: `export default defineConfig({ auth: true, preview: { … }, branch: (branch) => ({ … }) })`.\",\n\t\t\t\"The static services/preview set moved to the top level; per-branch logic moved into the `branch` closure.\",\n\t\t]);\n\t}\n\tif (input === null || typeof input !== \"object\") {\n\t\tthrow new ConfigValidationError([\n\t\t\t\"defineConfig expects a configuration object: `export default defineConfig({ … })`.\",\n\t\t]);\n\t}\n\n\tconst parsed = configInputSchema.safeParse(input);\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\n\treturn Object.freeze({ ...input }) as Config<Auth, DataApi, Preview>;\n}\n\n/**\n * Evaluate a branch policy for a specific branch target and return a normalized config.\n *\n * Merges the static existential set (services + preview functions/buckets) with the\n * per-branch tuning returned by the `branch` closure into the same {@link\n * ResolvedBranchConfig} the rest of the runtime (diff / push / fetchEnv) consumes.\n */\nexport function resolveConfig(\n\tconfig: Config,\n\tbranch: BranchTarget,\n): ResolvedBranchConfig {\n\tconst tuning = evaluateBranchTuning(config.branch, branch);\n\n\tconst resolved: ResolvedBranchConfig = {\n\t\tauthEnabled: isServiceEnabled(config.auth),\n\t\tdataApiEnabled: isDataApiEnabled(config.dataApi),\n\t};\n\tconst dataApi = resolveDataApi(config.dataApi);\n\tif (dataApi) resolved.dataApi = dataApi;\n\tif (tuning.parent !== undefined) resolved.parent = tuning.parent;\n\tif (tuning.ttl !== undefined) {\n\t\t// `branchTuningSchema` already validated `ttl` with the same `parseBranchTtl`, so\n\t\t// this only converts the validated value to seconds — it cannot fail here.\n\t\tconst parsedTtl = parseBranchTtl(tuning.ttl);\n\t\tif (!(\"error\" in parsedTtl)) resolved.ttlSeconds = parsedTtl.seconds;\n\t}\n\tif (tuning.protected !== undefined) resolved.protected = tuning.protected;\n\tif (tuning.postgres?.computeSettings) {\n\t\tresolved.postgres = {\n\t\t\tcomputeSettings: { ...tuning.postgres.computeSettings },\n\t\t};\n\t}\n\n\tconst preview = resolvePreviewConfig(config.preview, tuning);\n\tif (preview) resolved.preview = preview;\n\n\treturn resolved;\n}\n\n/**\n * Run the `branch` closure (when present) for the target and validate its output. The\n * closure is optional — a fully static policy resolves with empty tuning.\n */\nfunction evaluateBranchTuning(\n\tbranchFn: BranchTuningFn | undefined,\n\ttarget: BranchTarget,\n): BranchTuning {\n\tif (!branchFn) return {};\n\tlet raw: unknown;\n\ttry {\n\t\traw = branchFn(Object.freeze({ ...target }));\n\t} catch (cause) {\n\t\tthrow new ConfigValidationError([\n\t\t\t`Branch policy threw while evaluating branch \"${target.name}\".`,\n\t\t\t(cause as Error)?.message ?? String(cause),\n\t\t]);\n\t}\n\tconst parsed = branchTuningSchema.safeParse(raw ?? {});\n\tif (!parsed.success) {\n\t\tthrow new ConfigValidationError(formatZodIssues(parsed.error));\n\t}\n\treturn parsed.data as BranchTuning;\n}\n\nfunction isServiceEnabled(toggle: ServiceToggleInput | undefined): boolean {\n\tif (toggle === undefined) return false;\n\tif (typeof toggle === \"boolean\") return toggle;\n\treturn toggle.enabled !== false;\n}\n\n/** Whether a {@link DataApiInput} is enabled (present object/`true` unless `enabled: false`). */\nfunction isDataApiEnabled(input: DataApiInput | undefined): boolean {\n\tif (input === undefined) return false;\n\tif (typeof input === \"boolean\") return input;\n\treturn input.enabled !== false;\n}\n\n/**\n * Normalize a {@link DataApiInput} into a {@link ResolvedDataApiConfig}, or `undefined` when\n * the Data API is not enabled. `authProvider` defaults to `\"neon\"`; the external-IdP wiring\n * is carried through only for the `\"external\"` provider; `settings` is copied with its\n * `undefined` entries dropped so diffing only considers fields the policy actually set.\n */\nfunction resolveDataApi(\n\tinput: DataApiInput | undefined,\n): ResolvedDataApiConfig | undefined {\n\tif (!isDataApiEnabled(input)) return undefined;\n\tif (typeof input !== \"object\") {\n\t\t// Bare `true`: enabled with Neon Auth and all-default settings.\n\t\treturn { authProvider: \"neon\" };\n\t}\n\tconst authProvider = input.authProvider ?? \"neon\";\n\tconst resolved: ResolvedDataApiConfig = { authProvider };\n\tif (authProvider === \"external\") {\n\t\tif (input.jwksUrl !== undefined) resolved.jwksUrl = input.jwksUrl;\n\t\tif (input.providerName !== undefined)\n\t\t\tresolved.providerName = input.providerName;\n\t\tif (input.jwtAudience !== undefined)\n\t\t\tresolved.jwtAudience = input.jwtAudience;\n\t}\n\tconst settings = normalizeDataApiSettings(input.settings);\n\tif (settings) resolved.settings = settings;\n\treturn resolved;\n}\n\n/** Copy a {@link DataApiSettings}, dropping `undefined` entries; `undefined` when empty. */\nfunction normalizeDataApiSettings(\n\tsettings: DataApiSettings | undefined,\n): DataApiSettings | undefined {\n\tif (!settings) return undefined;\n\tconst out: DataApiSettings = {};\n\tfor (const [key, value] of Object.entries(settings)) {\n\t\tif (value !== undefined) {\n\t\t\t(out as Record<string, unknown>)[key] = value;\n\t\t}\n\t}\n\treturn Object.keys(out).length > 0 ? out : undefined;\n}\n\n/**\n * Normalize the static {@link PreviewInput} (merged with per-branch function tuning) into a\n * {@link ResolvedPreviewConfig}. Returns `undefined` when the policy declares no `preview`\n * block so the field can be omitted entirely. Function slugs / bucket names come from the\n * record keys.\n */\nfunction resolvePreviewConfig(\n\tpreview: PreviewInput | undefined,\n\ttuning: BranchTuning,\n): ResolvedPreviewConfig | undefined {\n\tif (!preview) return undefined;\n\tconst fnTuning = tuning.preview?.functions ?? {};\n\tconst functions: ResolvedFunctionConfig[] = Object.entries(\n\t\tpreview.functions ?? {},\n\t).map(([slug, def]) =>\n\t\tresolveFunctionConfig(slug, def, fnTuning[slug] ?? {}),\n\t);\n\tconst buckets = Object.entries(preview.buckets ?? {}).map(\n\t\t([name, def]) => ({\n\t\t\tname,\n\t\t\taccess: def.access ?? \"private\",\n\t\t}),\n\t);\n\treturn {\n\t\tfunctions,\n\t\tbuckets,\n\t\taiGatewayEnabled: isServiceEnabled(preview.aiGateway),\n\t};\n}\n\nfunction resolveFunctionConfig(\n\tslug: string,\n\tdef: FunctionDef,\n\ttuning: FunctionTuning,\n): ResolvedFunctionConfig {\n\treturn {\n\t\tslug,\n\t\tname: def.name,\n\t\tsource: def.source,\n\t\tenv: { ...(def.env ?? {}) },\n\t\truntime: tuning.runtime ?? DEFAULT_FUNCTION_RUNTIME,\n\t\t// Passed through untouched (no defaults); only `neon dev` reads it.\n\t\t...(def.dev ? { dev: def.dev } : {}),\n\t};\n}\n\n/**\n * Normalize a region identifier to Neon's `<cloud>-<region>` format. When the user writes\n * `us-east-1` we assume `aws-us-east-1`. Pure helper used by both the validator and the\n * NeonApi adapter.\n */\nexport function normalizeRegion(region: string): string {\n\tif (REGION_PREFIX.test(region)) return region;\n\treturn `aws-${region}`;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { BucketAccessLevel, ComputeSettings, ConflictReport, DataApiSettings, ResolvedBranchConfig, ResolvedFunctionConfig } from "./types.js";
|
|
2
|
+
import { EnableDataApiInput, 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
|
+
/** Create-time auth wiring + initial settings from the policy. */
|
|
41
|
+
input?: EnableDataApiInput;
|
|
42
|
+
} | {
|
|
43
|
+
/**
|
|
44
|
+
* Reconcile the runtime settings of an already-enabled Data API integration.
|
|
45
|
+
* Only `settings` are mutable post-create, so this is the lone Data API
|
|
46
|
+
* *update* step — and it is an override (requires `updateExisting`).
|
|
47
|
+
*/
|
|
48
|
+
kind: "update-data-api";
|
|
49
|
+
projectId: string;
|
|
50
|
+
branchId: string;
|
|
51
|
+
branchName: string;
|
|
52
|
+
databaseName: string;
|
|
53
|
+
settings: DataApiSettings;
|
|
54
|
+
} | {
|
|
55
|
+
kind: "create-bucket";
|
|
56
|
+
projectId: string;
|
|
57
|
+
branchId: string;
|
|
58
|
+
branchName: string;
|
|
59
|
+
bucketName: string;
|
|
60
|
+
accessLevel: BucketAccessLevel;
|
|
61
|
+
} | {
|
|
62
|
+
/**
|
|
63
|
+
* Deploy code to a function. Planned for every desired function — deployments are
|
|
64
|
+
* versioned and the newest becomes active, so a push ships the current source each
|
|
65
|
+
* time. Neon has no separate "create function" endpoint: the first deployment to a
|
|
66
|
+
* slug creates the function. `functionExists` therefore only drives whether this
|
|
67
|
+
* surfaces as a `create` (first deploy) or an `update` (re-deploy).
|
|
68
|
+
*/
|
|
69
|
+
kind: "deploy-function";
|
|
70
|
+
projectId: string;
|
|
71
|
+
branchId: string;
|
|
72
|
+
branchName: string;
|
|
73
|
+
fn: ResolvedFunctionConfig;
|
|
74
|
+
/** Whether the function already existed remotely when the plan was computed. */
|
|
75
|
+
functionExists: boolean;
|
|
76
|
+
};
|
|
77
|
+
interface RemoteServiceState {
|
|
78
|
+
databaseName: string;
|
|
79
|
+
authEnabled: boolean;
|
|
80
|
+
dataApiEnabled: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Current Data API runtime settings, when the integration is enabled and the API reports
|
|
83
|
+
* them (SubZero only). `null`/absent means "not reported" — settings drift can't be
|
|
84
|
+
* computed, so no update step is planned.
|
|
85
|
+
*/
|
|
86
|
+
dataApiSettings?: DataApiSettings | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Snapshot of the branch's current Preview-feature state. Absent (`undefined`) when the
|
|
90
|
+
* policy has no `preview` block — `pushConfig` only fetches this when needed.
|
|
91
|
+
*/
|
|
92
|
+
interface RemotePreviewState {
|
|
93
|
+
buckets: NeonBucketSnapshot[];
|
|
94
|
+
functions: NeonFunctionSnapshot[];
|
|
95
|
+
}
|
|
96
|
+
interface RemoteState {
|
|
97
|
+
projectId: string;
|
|
98
|
+
branch: NeonBranchSnapshot;
|
|
99
|
+
endpoint?: NeonEndpointSnapshot;
|
|
100
|
+
services: RemoteServiceState;
|
|
101
|
+
preview?: RemotePreviewState;
|
|
102
|
+
}
|
|
103
|
+
interface DiffOptions {
|
|
104
|
+
/**
|
|
105
|
+
* Apply mutable drift on the selected branch as plan steps instead of conflicts.
|
|
106
|
+
* Default: `false`.
|
|
107
|
+
*/
|
|
108
|
+
updateExisting: boolean;
|
|
109
|
+
}
|
|
110
|
+
interface DiffResult {
|
|
111
|
+
plan: PlanStep[];
|
|
112
|
+
conflicts: ConflictReport[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Diff desired branch policy against the selected remote branch. Pure function.
|
|
116
|
+
*/
|
|
117
|
+
declare function diffConfig(config: ResolvedBranchConfig, remote: RemoteState, options: DiffOptions): DiffResult;
|
|
118
|
+
//#endregion
|
|
119
|
+
export { DiffOptions, DiffResult, PlanStep, RemotePreviewState, RemoteServiceState, RemoteState, diffConfig };
|
|
120
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","names":[],"sources":["../../src/lib/diff.ts"],"mappings":";;;;;;;AAqBA;;AAoBa,KApBD,QAAA,GAoBC;MAgBF,EAAA,mBAAA;WAaE,EAAA,MAAA;UAQG,EAAA,MAAA;YAcT,EAAA,MAAA;EAAsB,SAAA,EAAA,MAAA,GAAA,IAAA;AAK7B,CAAA,GAAiB;EAgBA,IAAA,EAAA,yBAAkB;EAAA,SAAA,EAAA,MAAA;UACzB,EAAA,MAAA;YACE,EAAA,MAAA;EAAoB,SAAA,EAAA,OAAA;AAGhC,CAAA,GAAiB;EAAW,IAAA,EAAA,iBAAA;WAEnB,EAAA,MAAA;YACG,EAAA,MAAA;YACD,EAAA,MAAA;UACA,EAlFE,eAkFF;AAAkB,CAAA,GAAA;EAGZ,IAAA,EAAA,aAAW;EAQX,SAAA,EAAA,MAAU;EAAA,QAAA,EAAA,MAAA;YACpB,EAAA,MAAA;cACK,CAAA,EAAA,MAAA;AAAc,CAAA,GAAA;EAMV,IAAA,EAAA,iBAAU;EAAA,SAAA,EAAA,MAAA;UACjB,EAAA,MAAA;YACA,EAAA,MAAA;cACC,EAAA,MAAA;;EACG,KAAA,CAAA,EAzFF,kBAyFE;;;;;;;;;;;;YA5EA;;;;;;;eAQG;;;;;;;;;;;;;MAcT;;;;UAKU,kBAAA;;;;;;;;;oBASE;;;;;;UAOF,kBAAA;WACP;aACE;;UAGK,WAAA;;UAER;aACG;YACD;YACA;;UAGM,WAAA;;;;;;;UAQA,UAAA;QACV;aACK;;;;;iBAMI,UAAA,SACP,8BACA,sBACC,cACP"}
|