@fabrika/provider-contract 0.9.0-dev.1 → 0.9.0-dev.2
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/package.json +6 -5
- package/types/src/authoring.d.ts +27 -0
- package/types/src/control.d.ts +482 -0
- package/types/src/index.d.ts +5 -0
- package/types/src/installation.d.ts +37 -0
- package/types/src/json.d.ts +17 -0
- package/types/src/schema.d.ts +17 -0
- package/types/src/steps.d.ts +23 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabrika/provider-contract",
|
|
3
|
-
"version": "0.9.0-dev.
|
|
3
|
+
"version": "0.9.0-dev.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,17 +12,18 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"src",
|
|
15
|
-
"!src/**/__tests__/**"
|
|
15
|
+
"!src/**/__tests__/**",
|
|
16
|
+
"types"
|
|
16
17
|
],
|
|
17
18
|
"exports": {
|
|
18
19
|
".": {
|
|
19
20
|
"bun": "./src/index.ts",
|
|
20
|
-
"types": "./src/index.ts",
|
|
21
|
+
"types": "./types/src/index.d.ts",
|
|
21
22
|
"default": "./src/index.ts"
|
|
22
23
|
},
|
|
23
24
|
"./installation": {
|
|
24
25
|
"bun": "./src/installation.ts",
|
|
25
|
-
"types": "./src/installation.ts",
|
|
26
|
+
"types": "./types/src/installation.d.ts",
|
|
26
27
|
"default": "./src/installation.ts"
|
|
27
28
|
}
|
|
28
29
|
},
|
|
@@ -31,6 +32,6 @@
|
|
|
31
32
|
"test": "bun test"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@fabrika/auth-core": "0.9.0-dev.
|
|
35
|
+
"@fabrika/auth-core": "0.9.0-dev.2"
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AppSchema } from '@fabrika/auth-core';
|
|
2
|
+
export declare const APP_PROVIDER: unique symbol;
|
|
3
|
+
export interface ProviderAuthoredApp<Provider extends string = string> {
|
|
4
|
+
readonly [APP_PROVIDER]: Provider;
|
|
5
|
+
}
|
|
6
|
+
export declare const authoredAppProvider: (value: unknown) => string | undefined;
|
|
7
|
+
export declare const isProviderAuthoredApp: <Provider extends string>(value: unknown, provider: Provider) => value is ProviderAuthoredApp<Provider>;
|
|
8
|
+
/** Provider-neutral build inputs and environment requirements declared by an app. */
|
|
9
|
+
export interface AppPipeline {
|
|
10
|
+
/** Source directory relative to the app config file. Defaults to `.`. */
|
|
11
|
+
workerDir?: string;
|
|
12
|
+
/** Shell command that builds the deployable artifact. */
|
|
13
|
+
build?: string;
|
|
14
|
+
/** Secret names that must resolve before deployment. */
|
|
15
|
+
secrets?: string[];
|
|
16
|
+
/** Non-secret variable names that must resolve before deployment. */
|
|
17
|
+
vars?: string[];
|
|
18
|
+
}
|
|
19
|
+
/** App metadata shared by every provider-specific authoring config. */
|
|
20
|
+
export interface AppConfigBase {
|
|
21
|
+
/** Stable app id, unique within one control plane. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** The app-owned authorization vocabulary reconciled into IAM. */
|
|
24
|
+
schema?: AppSchema;
|
|
25
|
+
/** Provider-neutral build and environment requirements. */
|
|
26
|
+
pipeline?: AppPipeline;
|
|
27
|
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import type { AppSchema, ContractArtifact } from '@fabrika/auth-core';
|
|
2
|
+
import type { JsonValue, ProviderEnvelope } from './json';
|
|
3
|
+
/** Events whose persistence and presentation belong to the control plane. */
|
|
4
|
+
export interface ProviderRunEvents {
|
|
5
|
+
log(line: string): void;
|
|
6
|
+
/** Atomically persist the external operation id and, when supplied, its credential-free checkpoint. */
|
|
7
|
+
externalId(id: string, state?: JsonValue): Promise<void>;
|
|
8
|
+
/** Replace the credential-free checkpoint while this run remains active. */
|
|
9
|
+
checkpoint(state: JsonValue): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* A step boundary: Control checks its lease and reads the run's cancellation request. Rejects with a
|
|
12
|
+
* `ProviderInvocationAbort` after aborting the run's signal. A long step calls it at its own boundaries.
|
|
13
|
+
*/
|
|
14
|
+
boundary(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
/** Complete platform-owned runtime state. A null value removes the key from persistent targets. */
|
|
17
|
+
export type ProviderManagedEnvironment = Readonly<Record<string, string | null>>;
|
|
18
|
+
/** Provider-neutral coordinates for obtaining one app revision. */
|
|
19
|
+
export interface ProviderSource {
|
|
20
|
+
readonly repoUrl: string;
|
|
21
|
+
readonly ref: string;
|
|
22
|
+
readonly workerDir?: string;
|
|
23
|
+
readonly buildCommand?: string;
|
|
24
|
+
readonly configPath?: string;
|
|
25
|
+
}
|
|
26
|
+
/** App data needed by a control provider. */
|
|
27
|
+
export interface ProviderApp {
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly source: ProviderSource;
|
|
30
|
+
}
|
|
31
|
+
/** One provider-owned placement boundary shared by zero or more app environments. */
|
|
32
|
+
export interface ProviderDeploymentNamespace {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly env: string;
|
|
35
|
+
readonly exclusiveAppId?: string;
|
|
36
|
+
readonly target: ProviderEnvelope;
|
|
37
|
+
}
|
|
38
|
+
/** A framework release as npm publishes it; the only form one may take before it reaches a generated build command. */
|
|
39
|
+
export declare const FRAMEWORK_RELEASE_PATTERN: RegExp;
|
|
40
|
+
/**
|
|
41
|
+
* The access Control approved for one application environment (ADR-0050): the contract the proxy enforces and the
|
|
42
|
+
* IAM vocabulary compiled from it. A provider publishes a contract application's endpoint table and reconciles its
|
|
43
|
+
* IAM schema from this, never from the artifact or a runner's report.
|
|
44
|
+
*/
|
|
45
|
+
export interface ProviderApprovedAccess {
|
|
46
|
+
/** The contract in this platform release's model, upcast from the application's own release's artifact (ADR-0062). */
|
|
47
|
+
readonly contract: ContractArtifact;
|
|
48
|
+
/**
|
|
49
|
+
* The digest the application's own framework release computes for its contract, which it compares with
|
|
50
|
+
* `FABRIKA_CONTRACT_DIGEST` at startup. It is `contractDigest(contract)` only when the application is on this
|
|
51
|
+
* release's artifact format, so a provider never derives it from `contract`.
|
|
52
|
+
*/
|
|
53
|
+
readonly contractDigest: string;
|
|
54
|
+
readonly schema: AppSchema;
|
|
55
|
+
/**
|
|
56
|
+
* The `@fabrika/check` release whose reader read the application (`FRAMEWORK_RELEASE_PATTERN`): the newest patch of
|
|
57
|
+
* the framework minor it pins (ADR-0062). Its build checks with that release.
|
|
58
|
+
*/
|
|
59
|
+
readonly checker: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `dev`: the environment follows a workspace through a platform-owned launcher, which its provider starts instead of the
|
|
63
|
+
* serving entry (ADR-0043). The kind is Control's registry, never the application's.
|
|
64
|
+
*/
|
|
65
|
+
export type ProviderEnvironmentKind = 'standard' | 'dev';
|
|
66
|
+
/** Persisted deploy data for one app environment. */
|
|
67
|
+
export interface ProviderEnvironment {
|
|
68
|
+
readonly appId: string;
|
|
69
|
+
readonly env: string;
|
|
70
|
+
/** Absent means `standard`. */
|
|
71
|
+
readonly kind?: ProviderEnvironmentKind;
|
|
72
|
+
readonly domain?: string;
|
|
73
|
+
/** Canonical externally reachable HTTP(S) origin; independent of provider domain routing. */
|
|
74
|
+
readonly publicOrigin?: string;
|
|
75
|
+
/**
|
|
76
|
+
* The exact origins whose pages may call the environment's cross-origin endpoints, as the operator listed them in
|
|
77
|
+
* Control's registry (ADR-0022 "Cross-origin endpoints"). Absent when it lists none. A provider writes them into the
|
|
78
|
+
* application's proxy manifest entry as `crossOrigins`.
|
|
79
|
+
*/
|
|
80
|
+
readonly crossOrigins?: readonly string[];
|
|
81
|
+
readonly namespace?: ProviderDeploymentNamespace;
|
|
82
|
+
readonly target: ProviderEnvelope;
|
|
83
|
+
readonly artifact: ProviderEnvelope;
|
|
84
|
+
/**
|
|
85
|
+
* The environment's approved contract, from Control's `approvedSnapshot`. Absent when nothing is approved: then a
|
|
86
|
+
* provider publishes no endpoint table and reconciles no IAM schema, whatever the artifact claims (ADR-0050).
|
|
87
|
+
*/
|
|
88
|
+
readonly access?: ProviderApprovedAccess;
|
|
89
|
+
}
|
|
90
|
+
/** Raw registration data passed to provider-owned validation and normalization. */
|
|
91
|
+
export interface ProviderRegistrationInput {
|
|
92
|
+
readonly app: ProviderApp;
|
|
93
|
+
readonly environment: ProviderEnvironment;
|
|
94
|
+
}
|
|
95
|
+
/** Canonical registration returned after provider-owned validation and normalization. */
|
|
96
|
+
export type ProviderRegistration = ProviderRegistrationInput;
|
|
97
|
+
/** Provider-neutral input for resolving one requested source ref to an immutable Git object id. */
|
|
98
|
+
export interface ProviderSourceResolutionInput {
|
|
99
|
+
readonly runId: string;
|
|
100
|
+
readonly app: ProviderApp;
|
|
101
|
+
readonly environment: ProviderEnvironment;
|
|
102
|
+
/** An immutable commit already recorded by the trigger, which resolution must verify exactly. */
|
|
103
|
+
readonly expectedCommitSha?: string;
|
|
104
|
+
readonly sourceBinding?: ProviderSourceBinding;
|
|
105
|
+
readonly signal: AbortSignal;
|
|
106
|
+
}
|
|
107
|
+
/** The exact source revision a provider resolved before starting deploy work. */
|
|
108
|
+
export interface ProviderSourceResolution {
|
|
109
|
+
readonly commitSha: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Credential-free selector of the GitHub connection and installation that read a private source
|
|
113
|
+
* (ADR-0029). Absent means a public source; an installation id alone never selects a credential.
|
|
114
|
+
*/
|
|
115
|
+
export interface ProviderSourceBinding {
|
|
116
|
+
readonly connectionId: string;
|
|
117
|
+
readonly installationId: number;
|
|
118
|
+
}
|
|
119
|
+
/** How private GitHub sources reach a provider (ADR-0029): one keyed connection's App and installation. */
|
|
120
|
+
export interface ProviderSourceCapabilities {
|
|
121
|
+
readonly binding: 'connection';
|
|
122
|
+
}
|
|
123
|
+
/** One control-plane deploy invocation. */
|
|
124
|
+
export interface ProviderDeployInput {
|
|
125
|
+
readonly runId: string;
|
|
126
|
+
readonly app: ProviderApp;
|
|
127
|
+
readonly environment: ProviderEnvironment;
|
|
128
|
+
readonly secrets: Readonly<Record<string, string>>;
|
|
129
|
+
readonly vars: Readonly<Record<string, string>>;
|
|
130
|
+
/** Platform-owned runtime configuration, separate from operator-managed application vars. */
|
|
131
|
+
readonly managedEnvironment: ProviderManagedEnvironment;
|
|
132
|
+
/**
|
|
133
|
+
* Every origin IAM may hand this app a session at (ADR-0022). App-wide, not per-environment:
|
|
134
|
+
* IAM's registry is keyed by app id, so the set is assembled from every environment of the app.
|
|
135
|
+
* Absent when the app has no public origin at all, which leaves IAM's registry untouched.
|
|
136
|
+
*/
|
|
137
|
+
readonly returnOrigins?: readonly string[];
|
|
138
|
+
readonly dryRun: boolean;
|
|
139
|
+
readonly sourceBinding?: ProviderSourceBinding;
|
|
140
|
+
/** Optional short-lived, run-scoped Operations artifact destination. */
|
|
141
|
+
readonly artifactUpload?: {
|
|
142
|
+
readonly url: string;
|
|
143
|
+
readonly bearer: string;
|
|
144
|
+
readonly appId: string;
|
|
145
|
+
readonly environment: string;
|
|
146
|
+
readonly serviceKey: string;
|
|
147
|
+
readonly release: string;
|
|
148
|
+
readonly runId: string;
|
|
149
|
+
};
|
|
150
|
+
readonly signal: AbortSignal;
|
|
151
|
+
readonly events: ProviderRunEvents;
|
|
152
|
+
}
|
|
153
|
+
/** A terminal provider operation result. */
|
|
154
|
+
export interface ProviderTerminalOutcome {
|
|
155
|
+
readonly state: 'succeeded' | 'failed';
|
|
156
|
+
readonly exitCode?: number;
|
|
157
|
+
/** Whether provider-local release artifacts reached Operations. */
|
|
158
|
+
readonly artifactState?: 'complete' | 'incomplete' | 'not_applicable';
|
|
159
|
+
}
|
|
160
|
+
/** Running means external settlement is unresolved, including an ambiguous submission response. */
|
|
161
|
+
export type ProviderDeployOutcome = ProviderTerminalOutcome | {
|
|
162
|
+
readonly state: 'running';
|
|
163
|
+
};
|
|
164
|
+
/** Settled does not imply rollback: when activation won before cancellation, `outcome` is `succeeded`. */
|
|
165
|
+
export type ProviderCancelOutcome = {
|
|
166
|
+
readonly state: 'pending';
|
|
167
|
+
} | {
|
|
168
|
+
readonly state: 'settled';
|
|
169
|
+
readonly outcome?: 'succeeded' | 'failed';
|
|
170
|
+
};
|
|
171
|
+
/** Losing invocation ownership must never be interpreted as permission to cancel external work. */
|
|
172
|
+
export declare class ProviderInvocationAbort extends Error {
|
|
173
|
+
readonly kind: 'lease_lost' | 'cancel_requested';
|
|
174
|
+
constructor(kind: 'lease_lost' | 'cancel_requested');
|
|
175
|
+
}
|
|
176
|
+
export declare const isLeaseLoss: (reason: unknown) => boolean;
|
|
177
|
+
/** A signal that aborts with `signal`'s reason only when the lease is lost, never on a cancel. */
|
|
178
|
+
export declare const followLeaseLoss: (signal: AbortSignal) => AbortSignal;
|
|
179
|
+
/** Resolve after `ms`, or reject with the signal's reason once it aborts. */
|
|
180
|
+
export declare const abortableWait: (ms: number, signal: AbortSignal) => Promise<void>;
|
|
181
|
+
/** A platform-owned run that can be cancelled or reconciled. */
|
|
182
|
+
export interface ProviderRunReference {
|
|
183
|
+
readonly runId: string;
|
|
184
|
+
readonly externalId: string;
|
|
185
|
+
readonly environment: ProviderEnvironment;
|
|
186
|
+
/** Credential-free provider-owned checkpoint recorded by the run's last owner. */
|
|
187
|
+
readonly providerState?: JsonValue;
|
|
188
|
+
/** When the run started (`started_at`, else `created_at`), in epoch milliseconds; bounds how long a provider polls. */
|
|
189
|
+
readonly startedAtMs?: number;
|
|
190
|
+
}
|
|
191
|
+
export interface ProviderCancelInput extends ProviderRunReference {
|
|
192
|
+
readonly signal: AbortSignal;
|
|
193
|
+
/** See `ProviderDeployInput.returnOrigins`; a cancel that settles an activated deploy projects the same set. */
|
|
194
|
+
readonly returnOrigins?: readonly string[];
|
|
195
|
+
}
|
|
196
|
+
export interface ProviderReconcileInput extends ProviderRunReference {
|
|
197
|
+
readonly signal: AbortSignal;
|
|
198
|
+
/** See `ProviderDeployInput.returnOrigins`; a provider that settles a deploy here projects the same set. */
|
|
199
|
+
readonly returnOrigins?: readonly string[];
|
|
200
|
+
/** Append a line to the run's log. */
|
|
201
|
+
log?(line: string): Promise<void>;
|
|
202
|
+
}
|
|
203
|
+
/** The latest state reported while reconciling a platform-owned run. */
|
|
204
|
+
export interface ProviderReconcileOutcome {
|
|
205
|
+
readonly state: 'running' | 'succeeded' | 'failed';
|
|
206
|
+
readonly exitCode?: number;
|
|
207
|
+
/** See `ProviderTerminalOutcome.artifactState`. */
|
|
208
|
+
readonly artifactState?: 'complete' | 'incomplete' | 'not_applicable';
|
|
209
|
+
/** The settled run was a preview: core projects it as a dry run. */
|
|
210
|
+
readonly dryRun?: boolean;
|
|
211
|
+
}
|
|
212
|
+
/** Write one secret value to provider-managed storage. */
|
|
213
|
+
export interface ProviderSecretPutInput {
|
|
214
|
+
readonly environment: ProviderEnvironment;
|
|
215
|
+
readonly name: string;
|
|
216
|
+
readonly value: string;
|
|
217
|
+
}
|
|
218
|
+
/** Remove one secret value from provider-managed storage. */
|
|
219
|
+
export interface ProviderSecretDeleteInput {
|
|
220
|
+
readonly environment: ProviderEnvironment;
|
|
221
|
+
readonly name: string;
|
|
222
|
+
}
|
|
223
|
+
/** Opaque reference persisted by core instead of the provider-managed secret value. */
|
|
224
|
+
export interface ProviderSecretPutResult {
|
|
225
|
+
readonly valueRef: string;
|
|
226
|
+
}
|
|
227
|
+
/** Optional edit-time secret storage owned by a provider. */
|
|
228
|
+
export interface ProviderManagedSecrets {
|
|
229
|
+
put(input: ProviderSecretPutInput): Promise<ProviderSecretPutResult>;
|
|
230
|
+
delete(input: ProviderSecretDeleteInput): Promise<void>;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* A namespace lifecycle failure an operator can act on.
|
|
234
|
+
*
|
|
235
|
+
* `code` is a STABLE identifier — a platform error code, or one this provider defines for a lifecycle
|
|
236
|
+
* invariant — so core can record and a console can render the CLASS of a failure instead of matching
|
|
237
|
+
* prose. `message` is the provider's own summary; `detail` carries the upstream's words VERBATIM.
|
|
238
|
+
* Neither is safe to persist as-is: core redacts both before they reach a row or a log, because only
|
|
239
|
+
* core knows what a credential looks like in this installation.
|
|
240
|
+
*/
|
|
241
|
+
export declare class ProviderNamespaceError extends Error {
|
|
242
|
+
readonly code: string;
|
|
243
|
+
readonly retryable: boolean;
|
|
244
|
+
readonly detail?: string | undefined;
|
|
245
|
+
constructor(message: string, code: string, retryable: boolean, detail?: string | undefined);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* A provider-managed secret write the provider refuses on the app's own terms — a value naming a
|
|
249
|
+
* service the app does not own, say. `message` carries the secret's name and the offending host,
|
|
250
|
+
* never the value, so core may hand it to the caller as a 4xx.
|
|
251
|
+
*/
|
|
252
|
+
export declare class ProviderSecretError extends Error {
|
|
253
|
+
readonly code: string;
|
|
254
|
+
constructor(message: string, code: string);
|
|
255
|
+
}
|
|
256
|
+
/** Persist a canonical namespace target immediately after one external mutation succeeds. */
|
|
257
|
+
export interface ProviderNamespaceEvents {
|
|
258
|
+
checkpoint(namespace: ProviderDeploymentNamespace): Promise<void>;
|
|
259
|
+
/** A step boundary: Control checks the namespace lease and, for a deploy, the run's cancellation request. */
|
|
260
|
+
boundary(): Promise<void>;
|
|
261
|
+
}
|
|
262
|
+
/** One app environment assigned to a namespace, as its proxy routes it. */
|
|
263
|
+
export interface ProviderNamespaceMember {
|
|
264
|
+
readonly appId: string;
|
|
265
|
+
readonly env: string;
|
|
266
|
+
readonly domain?: string;
|
|
267
|
+
/** See `ProviderEnvironment.crossOrigins`: the namespace proxy's entry for this member lists them. */
|
|
268
|
+
readonly crossOrigins?: readonly string[];
|
|
269
|
+
readonly artifact: ProviderEnvelope;
|
|
270
|
+
/**
|
|
271
|
+
* The secret the namespace proxy authenticates itself to this application environment with (ADR-0022 rule 5).
|
|
272
|
+
* Control generates it once and keeps it in its vault; absent when Control has no vault.
|
|
273
|
+
*/
|
|
274
|
+
readonly proxySecret?: string;
|
|
275
|
+
/**
|
|
276
|
+
* The origin of this environment's active Operations ingest DSN, never the DSN itself: a contract application's
|
|
277
|
+
* proxy admits it in `connect-src`, as the framework does. Absent while the environment has no active ingest.
|
|
278
|
+
*/
|
|
279
|
+
readonly operationsOrigin?: string;
|
|
280
|
+
/** See `ProviderEnvironment.access`: the proxy table of a contract application comes only from here. */
|
|
281
|
+
readonly access?: ProviderApprovedAccess;
|
|
282
|
+
}
|
|
283
|
+
/** One idempotent namespace lifecycle invocation. */
|
|
284
|
+
export interface ProviderNamespaceMutationInput {
|
|
285
|
+
readonly namespace: ProviderDeploymentNamespace;
|
|
286
|
+
/** Every app environment assigned to the namespace, read under its lease. */
|
|
287
|
+
readonly members: readonly ProviderNamespaceMember[];
|
|
288
|
+
readonly signal: AbortSignal;
|
|
289
|
+
readonly events: ProviderNamespaceEvents;
|
|
290
|
+
}
|
|
291
|
+
/** `settled` is false while the rebuild is still in flight; core then hands it to a `reconcile` job. */
|
|
292
|
+
export interface ProviderNamespaceRepublishResult {
|
|
293
|
+
readonly namespace: ProviderDeploymentNamespace;
|
|
294
|
+
readonly settled: boolean;
|
|
295
|
+
}
|
|
296
|
+
/** Rebuild a ready namespace's entry point from its current members. */
|
|
297
|
+
export interface ProviderNamespaceRepublishInput extends ProviderNamespaceMutationInput {
|
|
298
|
+
/** `started`: return once the rebuild is submitted. `active`: return once it is live. */
|
|
299
|
+
readonly wait: 'started' | 'active';
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The `code` of a `ProviderNamespaceError` an update plan throws when the requested target changes
|
|
303
|
+
* something the provider cannot move in place. Core answers it as a conflict that names the supported path.
|
|
304
|
+
*/
|
|
305
|
+
export declare const PROVIDER_NAMESPACE_IMMUTABLE = "namespaceImmutable";
|
|
306
|
+
/**
|
|
307
|
+
* The `code` of a `ProviderNamespaceError` an update apply throws when the live change MAY have been made
|
|
308
|
+
* and is not confirmed. Any other failure means nothing changed. Applying the same plan again must converge.
|
|
309
|
+
*/
|
|
310
|
+
export declare const PROVIDER_NAMESPACE_UPDATE_UNCONFIRMED = "namespaceUpdateUnconfirmed";
|
|
311
|
+
/** One provider-described change, safe to audit: names and values only, never a credential. */
|
|
312
|
+
export interface ProviderNamespaceChange {
|
|
313
|
+
readonly field: string;
|
|
314
|
+
readonly from: string | null;
|
|
315
|
+
readonly to: string;
|
|
316
|
+
}
|
|
317
|
+
/** The validated result of merging a requested target over the stored one. `change` is null for a no-op. */
|
|
318
|
+
export interface ProviderNamespaceUpdatePlan {
|
|
319
|
+
readonly namespace: ProviderDeploymentNamespace;
|
|
320
|
+
readonly change: ProviderNamespaceChange | null;
|
|
321
|
+
}
|
|
322
|
+
/** Apply one plan. `next` is the planned namespace, with the same coordinates as `namespace`. */
|
|
323
|
+
export interface ProviderNamespaceUpdateInput {
|
|
324
|
+
readonly namespace: ProviderDeploymentNamespace;
|
|
325
|
+
readonly next: ProviderDeploymentNamespace;
|
|
326
|
+
/** The namespace an earlier, unconfirmed update planned; the live resources may already match it. */
|
|
327
|
+
readonly unconfirmed?: ProviderDeploymentNamespace;
|
|
328
|
+
readonly signal: AbortSignal;
|
|
329
|
+
}
|
|
330
|
+
/** In-place change of a settled namespace. */
|
|
331
|
+
export interface ProviderNamespaceUpdates {
|
|
332
|
+
/**
|
|
333
|
+
* Merge `requested` — whose payload may name only what changes — over the stored `namespace` and
|
|
334
|
+
* validate the result. Pure. A change that cannot be made in place throws `PROVIDER_NAMESPACE_IMMUTABLE`.
|
|
335
|
+
*/
|
|
336
|
+
plan(namespace: ProviderDeploymentNamespace, requested: ProviderEnvelope): ProviderNamespaceUpdatePlan;
|
|
337
|
+
/** Move the live resources to `next` and return the target core stores. */
|
|
338
|
+
apply(input: ProviderNamespaceUpdateInput): Promise<ProviderDeploymentNamespace>;
|
|
339
|
+
}
|
|
340
|
+
/** One registration whose provider coordinates do not exist until namespace services are prepared. */
|
|
341
|
+
export interface ProviderRegistrationPreparationInput {
|
|
342
|
+
readonly registration: ProviderRegistration;
|
|
343
|
+
readonly signal: AbortSignal;
|
|
344
|
+
}
|
|
345
|
+
/** One provider-defined operator choice. Core does not interpret its id or description. */
|
|
346
|
+
export interface ProviderNamespacePreset {
|
|
347
|
+
readonly id: string;
|
|
348
|
+
readonly label: string;
|
|
349
|
+
readonly description: string;
|
|
350
|
+
readonly requiresExclusiveApp: boolean;
|
|
351
|
+
}
|
|
352
|
+
/** Provider-neutral coordinates plus opaque provider options for a namespace preview. */
|
|
353
|
+
export interface ProviderNamespacePlanInput {
|
|
354
|
+
readonly id: string;
|
|
355
|
+
readonly env: string;
|
|
356
|
+
readonly preset: string;
|
|
357
|
+
readonly exclusiveAppId?: string;
|
|
358
|
+
readonly options?: JsonValue;
|
|
359
|
+
}
|
|
360
|
+
/** One safe, human-readable provider fact. Values must never contain credentials. */
|
|
361
|
+
export interface ProviderNamespaceFact {
|
|
362
|
+
readonly label: string;
|
|
363
|
+
readonly value: string;
|
|
364
|
+
/**
|
|
365
|
+
* Marks a fact that NAMES A LIVE PROVIDER RESOURCE, not a policy choice. Core deletes no provider
|
|
366
|
+
* resource, so removal has to tell the operator what it stopped tracking — and only the provider
|
|
367
|
+
* knows which of its facts is a real thing with an id. Absent means "not a resource"; a fact is
|
|
368
|
+
* marked only when its value identifies something that outlives the namespace row.
|
|
369
|
+
*/
|
|
370
|
+
readonly resource?: true;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* One public host this namespace's own entry point answers on, with the listener port that publishes it.
|
|
374
|
+
*
|
|
375
|
+
* Only the provider knows how a host comes to exist — Zerops generates one per published HTTP port — and
|
|
376
|
+
* core knows which of them an application environment already claims, so the provider names them and core
|
|
377
|
+
* marks them. A namespace whose domains are bound out of band names none: the provider cannot see them.
|
|
378
|
+
*/
|
|
379
|
+
export interface ProviderNamespaceHost {
|
|
380
|
+
readonly host: string;
|
|
381
|
+
readonly port: number;
|
|
382
|
+
}
|
|
383
|
+
/** Provider-owned operator copy for a planned or persisted namespace. */
|
|
384
|
+
export interface ProviderNamespacePresentation {
|
|
385
|
+
readonly preset: string;
|
|
386
|
+
readonly title: string;
|
|
387
|
+
readonly facts: readonly ProviderNamespaceFact[];
|
|
388
|
+
readonly instructions: readonly string[];
|
|
389
|
+
/** The hosts this namespace can serve, so an operator can name one as an environment's domain. */
|
|
390
|
+
readonly hosts?: readonly ProviderNamespaceHost[];
|
|
391
|
+
}
|
|
392
|
+
/** A mutation-free preview that can be submitted unchanged to namespace creation. */
|
|
393
|
+
export interface ProviderNamespacePlan {
|
|
394
|
+
readonly namespace: ProviderDeploymentNamespace;
|
|
395
|
+
readonly presentation: ProviderNamespacePresentation;
|
|
396
|
+
}
|
|
397
|
+
/** Optional operator surface for providers that offer opinionated namespace presets. */
|
|
398
|
+
export interface ProviderNamespaceOperator {
|
|
399
|
+
readonly presets: readonly ProviderNamespacePreset[];
|
|
400
|
+
plan(input: ProviderNamespacePlanInput): ProviderNamespacePlan;
|
|
401
|
+
present(namespace: ProviderDeploymentNamespace): ProviderNamespacePresentation;
|
|
402
|
+
}
|
|
403
|
+
/** Optional placement lifecycle owned by providers that use deployment namespaces. */
|
|
404
|
+
export interface ProviderNamespaceCapabilities {
|
|
405
|
+
normalize(namespace: ProviderDeploymentNamespace): ProviderDeploymentNamespace;
|
|
406
|
+
/** Namespace-owned resources that must be reserved before provisioning starts. */
|
|
407
|
+
namespaceResourceClaims(namespace: ProviderDeploymentNamespace): readonly string[];
|
|
408
|
+
/** App-owned resources derived from one canonical normalized registration. */
|
|
409
|
+
registrationResourceClaims(registration: ProviderRegistration): readonly string[];
|
|
410
|
+
/**
|
|
411
|
+
* Materialize app-owned provider resources and return their canonical coordinates.
|
|
412
|
+
* Core acquires every registration resource claim before invoking this mutation.
|
|
413
|
+
*/
|
|
414
|
+
prepareRegistration?(input: ProviderRegistrationPreparationInput): Promise<ProviderRegistration>;
|
|
415
|
+
provision(input: ProviderNamespaceMutationInput): Promise<ProviderDeploymentNamespace>;
|
|
416
|
+
reconcile(input: ProviderNamespaceMutationInput): Promise<ProviderDeploymentNamespace>;
|
|
417
|
+
/** Absent means the namespace has no entry point that follows its members. */
|
|
418
|
+
republish?(input: ProviderNamespaceRepublishInput): Promise<ProviderNamespaceRepublishResult>;
|
|
419
|
+
/** Absent means the provider changes no namespace in place. */
|
|
420
|
+
readonly updates?: ProviderNamespaceUpdates;
|
|
421
|
+
readonly operator?: ProviderNamespaceOperator;
|
|
422
|
+
}
|
|
423
|
+
/** One call on the service of a `dev` environment, between its bootstrap runs. */
|
|
424
|
+
export interface ProviderDevEnvironmentInput {
|
|
425
|
+
readonly environment: ProviderEnvironment;
|
|
426
|
+
readonly signal: AbortSignal;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The service of a `dev` environment outside a run (ADR-0049): Control stops it after an idle period and starts it on
|
|
430
|
+
* the next commit. Files the launcher wrote survive both.
|
|
431
|
+
*/
|
|
432
|
+
export interface ProviderDevEnvironments {
|
|
433
|
+
stop(input: ProviderDevEnvironmentInput): Promise<void>;
|
|
434
|
+
start(input: ProviderDevEnvironmentInput): Promise<void>;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* The control-plane capability bundle selected statically by one composition root.
|
|
438
|
+
*
|
|
439
|
+
* Core owns no provider registry. The selected provider validates registration envelopes and owns
|
|
440
|
+
* every provider-specific deploy lifecycle capability.
|
|
441
|
+
*/
|
|
442
|
+
export interface ControlProvider {
|
|
443
|
+
readonly id: string;
|
|
444
|
+
/** Absent means core stores the registration as submitted. */
|
|
445
|
+
normalizeRegistration?(input: ProviderRegistrationInput): ProviderRegistration;
|
|
446
|
+
resolveSource?(input: ProviderSourceResolutionInput): Promise<ProviderSourceResolution>;
|
|
447
|
+
/**
|
|
448
|
+
* Mutation-free check of a candidate's compatibility with its placement. Control calls it before publishing the
|
|
449
|
+
* snapshot, under the namespace lease, on every publication attempt. The candidate is not yet approved access.
|
|
450
|
+
*/
|
|
451
|
+
validateAccess?(input: {
|
|
452
|
+
readonly environment: ProviderEnvironment;
|
|
453
|
+
readonly access: ProviderApprovedAccess;
|
|
454
|
+
}): void;
|
|
455
|
+
deploy(input: ProviderDeployInput): Promise<ProviderDeployOutcome>;
|
|
456
|
+
cancel?(input: ProviderCancelInput): Promise<ProviderCancelOutcome>;
|
|
457
|
+
reconcile?(input: ProviderReconcileInput): Promise<ProviderReconcileOutcome>;
|
|
458
|
+
readonly secrets?: ProviderManagedSecrets;
|
|
459
|
+
readonly namespaces?: ProviderNamespaceCapabilities;
|
|
460
|
+
/** Absent means the provider runs no `dev` environment, and Control refuses one. */
|
|
461
|
+
readonly devEnvironments?: ProviderDevEnvironments;
|
|
462
|
+
/** Absent means the provider deploys public sources only. */
|
|
463
|
+
readonly source?: ProviderSourceCapabilities;
|
|
464
|
+
/**
|
|
465
|
+
* The variable names this artifact declares, so core can refuse one it would silently ignore
|
|
466
|
+
* ([ADR-0035](../../../docs/decisions/0035-the-platform-owns-the-application-iam-issuer.md)).
|
|
467
|
+
*
|
|
468
|
+
* OPTIONAL, and `undefined` is the honest answer rather than an empty set: a provider whose artifact
|
|
469
|
+
* names a config path in the repository cannot know what that config declares, and guessing would
|
|
470
|
+
* refuse a variable that works. A provider that CAN answer — one whose artifact is the compiled
|
|
471
|
+
* manifest — constrains what an operator may set.
|
|
472
|
+
*/
|
|
473
|
+
declaredVariables?(input: {
|
|
474
|
+
readonly artifact: ProviderEnvelope;
|
|
475
|
+
}): readonly string[] | undefined;
|
|
476
|
+
/**
|
|
477
|
+
* The repository-relative directory this provider executes the application from, normalized as its execution
|
|
478
|
+
* normalizes it (`''` for the repository root), or null for a root execution refuses. Control reads the run's
|
|
479
|
+
* access snapshot there (ADR-0050). Absent means the repository root.
|
|
480
|
+
*/
|
|
481
|
+
applicationRoot?(source: ProviderSource): string | null;
|
|
482
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The platform commands a provider MAY offer. Every one of them is optional — a provider declares the
|
|
3
|
+
* subset it implements and the CLI prints its usage for anything else.
|
|
4
|
+
*
|
|
5
|
+
* `install` is the newest and the narrowest in support: it BRINGS UP an installation that does not
|
|
6
|
+
* exist yet, generating and placing every credential it needs. Only Zerops declares it, because only
|
|
7
|
+
* there is a from-scratch bring-up a sequence of API calls; on Cloudflare the same ground is covered by
|
|
8
|
+
* `init` plus the workflow it scaffolds.
|
|
9
|
+
*
|
|
10
|
+
* `admin` admits the FIRST HUMAN to an installation that already runs. It is a verb of its own rather
|
|
11
|
+
* than a step of `install`, because it is the one command that is useful long after the bring-up: an
|
|
12
|
+
* installation whose only administrator left needs it, and a bring-up that got as far as the console
|
|
13
|
+
* does not need to be repeated to use it.
|
|
14
|
+
*
|
|
15
|
+
* `upgrade` rolls a RUNNING installation onto a newer published fabrika release. It is separate from
|
|
16
|
+
* `deploy` because the two run in different places: `deploy` runs inside the operator's own pipeline
|
|
17
|
+
* and brings an installation to the checkout it was handed, while `upgrade` runs on the operator's
|
|
18
|
+
* machine and moves the pin that pipeline reads. A provider whose operator edits no pin does not
|
|
19
|
+
* declare it.
|
|
20
|
+
*
|
|
21
|
+
* `guide` prints the operator's procedure for the whole bring-up, with the traps the per-command usage
|
|
22
|
+
* cannot show. It ships with the provider package, so an agent reads the guide of the release it runs.
|
|
23
|
+
*/
|
|
24
|
+
export type InstallationCommand = 'init' | 'install' | 'plan' | 'deploy' | 'admin' | 'upgrade' | 'guide';
|
|
25
|
+
export declare const isInstallationCommand: (value: unknown) => value is InstallationCommand;
|
|
26
|
+
export interface InstallationCli {
|
|
27
|
+
readonly provider: string;
|
|
28
|
+
readonly commands: readonly InstallationCommand[];
|
|
29
|
+
readonly usage: string;
|
|
30
|
+
run(command: InstallationCommand, argv: readonly string[]): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
export interface InstallationCliModule {
|
|
33
|
+
readonly installationCli: InstallationCli;
|
|
34
|
+
}
|
|
35
|
+
export declare const isInstallationCli: (value: unknown) => value is InstallationCli;
|
|
36
|
+
export declare const supportsInstallationCommand: (installation: InstallationCli, command: InstallationCommand) => boolean;
|
|
37
|
+
export declare const installationCliFromModule: (value: unknown, expectedProvider?: string) => InstallationCli;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** A value that can cross the provider persistence boundary without provider-specific types. */
|
|
2
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
/** Persisted provider data. The provider owns the payload schema and its version. */
|
|
6
|
+
export interface ProviderEnvelope {
|
|
7
|
+
readonly provider: string;
|
|
8
|
+
readonly version: number;
|
|
9
|
+
readonly payload: JsonValue;
|
|
10
|
+
}
|
|
11
|
+
/** Converts one provider-owned value to and from its versioned JSON payload. */
|
|
12
|
+
export interface ProviderCodec<T> {
|
|
13
|
+
readonly version: number;
|
|
14
|
+
encode(value: T): JsonValue;
|
|
15
|
+
decode(payload: JsonValue): T;
|
|
16
|
+
}
|
|
17
|
+
export declare const decodeEnvelope: <T>(kind: string, provider: string, envelope: ProviderEnvelope, codec: ProviderCodec<T>) => T;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AppSchema } from '@fabrika/auth-core';
|
|
2
|
+
/** Inputs needed to reconcile one app's authorization vocabulary into IAM. */
|
|
3
|
+
export interface SchemaReconcileInput {
|
|
4
|
+
url: string;
|
|
5
|
+
app: string;
|
|
6
|
+
schema: AppSchema;
|
|
7
|
+
/**
|
|
8
|
+
* Every origin IAM may hand this app a session at, as known by the control plane. Omitted leaves
|
|
9
|
+
* IAM's registry alone; a supplied set replaces it. It travels beside the schema and never inside
|
|
10
|
+
* it — an app declares its vocabulary, the control plane declares where the app lives.
|
|
11
|
+
*/
|
|
12
|
+
returnOrigins?: readonly string[];
|
|
13
|
+
adminKey?: string;
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
/** Provider-neutral port for authorization schema reconciliation. */
|
|
17
|
+
export type SchemaReconciler = (input: SchemaReconcileInput) => Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** What happened to one provider deploy step. */
|
|
2
|
+
export interface DeployStepResult {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly status: 'succeeded' | 'failed' | 'skipped';
|
|
5
|
+
readonly error?: string;
|
|
6
|
+
readonly startedAt?: number;
|
|
7
|
+
readonly finishedAt?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface RunStepsOptions {
|
|
10
|
+
readonly appId: string;
|
|
11
|
+
readonly env: string;
|
|
12
|
+
readonly dryRun: boolean;
|
|
13
|
+
readonly signal: AbortSignal;
|
|
14
|
+
readonly log: (line: string) => void;
|
|
15
|
+
}
|
|
16
|
+
/** Run a provider's steps in order. The first failure or a cancellation skips every later step. */
|
|
17
|
+
export declare const runSteps: <TStep extends {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly description: string;
|
|
20
|
+
}>(steps: readonly TStep[], options: RunStepsOptions, runOne: (step: TStep) => Promise<void>) => Promise<{
|
|
21
|
+
readonly status: "succeeded" | "failed";
|
|
22
|
+
readonly steps: readonly DeployStepResult[];
|
|
23
|
+
}>;
|