@astrale-os/sdk 0.6.0-beta.21 → 0.6.0-beta.23

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.
Files changed (58) hide show
  1. package/README.md +0 -4
  2. package/dist/application/workflow/durable.d.ts +12 -8
  3. package/dist/deployment/adapter/adapter.d.ts +16 -5
  4. package/dist/deployment/adapter/define.d.ts +1 -1
  5. package/dist/deployment/adapter/define.js +40 -1
  6. package/dist/deployment/adapter/release/command.d.ts +33 -0
  7. package/dist/deployment/adapter/release/command.js +1 -0
  8. package/dist/deployment/adapter/release/context.d.ts +3 -4
  9. package/dist/deployment/adapter/release/index.d.ts +1 -0
  10. package/dist/deployment/adapter/release/list.d.ts +11 -10
  11. package/dist/deployment/address/configuration.d.ts +12 -0
  12. package/dist/deployment/address/configuration.js +11 -0
  13. package/dist/deployment/address/index.d.ts +3 -3
  14. package/dist/deployment/address/index.js +2 -3
  15. package/dist/deployment/address/label.d.ts +5 -5
  16. package/dist/deployment/address/label.js +5 -5
  17. package/dist/deployment/address/record.d.ts +21 -0
  18. package/dist/deployment/address/record.js +34 -0
  19. package/dist/deployment/address/summary.d.ts +17 -40
  20. package/dist/deployment/address/summary.js +1 -117
  21. package/dist/deployment/index.d.ts +1 -1
  22. package/dist/deployment/verify/index.d.ts +1 -1
  23. package/dist/deployment/verify/index.js +1 -1
  24. package/dist/deployment/verify/readiness/index.d.ts +7 -0
  25. package/dist/deployment/verify/readiness/index.js +17 -0
  26. package/dist/deployment/worker/address.d.ts +26 -0
  27. package/dist/deployment/worker/address.js +43 -0
  28. package/dist/deployment/worker/build.d.ts +60 -0
  29. package/dist/deployment/worker/build.js +172 -0
  30. package/dist/deployment/worker/configuration.d.ts +73 -0
  31. package/dist/deployment/worker/configuration.js +165 -0
  32. package/dist/deployment/worker/content.d.ts +38 -0
  33. package/dist/deployment/worker/content.js +169 -0
  34. package/dist/deployment/worker/index.d.ts +29 -0
  35. package/dist/deployment/worker/index.js +29 -0
  36. package/dist/deployment/worker/limits.d.ts +30 -0
  37. package/dist/deployment/worker/limits.js +30 -0
  38. package/dist/deployment/worker/record.d.ts +29 -0
  39. package/dist/deployment/worker/record.js +69 -0
  40. package/dist/deployment/worker/request.d.ts +118 -0
  41. package/dist/deployment/worker/request.js +280 -0
  42. package/dist/deployment/worker/response.d.ts +104 -0
  43. package/dist/deployment/worker/response.js +213 -0
  44. package/dist/execution/workflows/run.js +18 -6
  45. package/dist/tooling/cli/adapter-command.d.ts +35 -0
  46. package/dist/tooling/cli/adapter-command.js +66 -0
  47. package/dist/tooling/cli/arguments.d.ts +7 -1
  48. package/dist/tooling/cli/arguments.js +53 -1
  49. package/dist/tooling/cli/help.js +11 -8
  50. package/dist/tooling/cli/immutable-deployment.d.ts +2 -2
  51. package/dist/tooling/cli/index.d.ts +4 -2
  52. package/dist/tooling/cli/index.js +3 -2
  53. package/dist/tooling/cli/list.d.ts +5 -5
  54. package/dist/tooling/cli/list.js +16 -14
  55. package/dist/tooling/cli/orchestrate.js +18 -0
  56. package/dist/tooling/cli/publish/index.d.ts +4 -4
  57. package/dist/tooling/cli/publish/index.js +8 -9
  58. package/package.json +5 -1
@@ -0,0 +1,118 @@
1
+ import type { Digest } from '@astrale-os/kernel-protocol/artifact';
2
+ import type { WorkerBuildManifest, WorkerCode, WorkerModule } from './build.js';
3
+ import type { WorkerConfigurationV1 } from './configuration.js';
4
+ import type { WorkerContentV1 } from './content.js';
5
+ import type { WorkerRecord, WorkerRecordWireV1 } from './record.js';
6
+ export declare const WORKER_DEPLOYMENT_REQUEST_FORMAT = "astrale.worker.deployment-request";
7
+ export declare const WORKER_DEPLOYMENT_REQUEST_VERSION = 1;
8
+ /**
9
+ * `WorkerDeploymentRequestV1`: one Worker release to host as a deployment of the Service of its
10
+ * project and environment. Its host knows nothing of what the Worker serves: it recomputes the
11
+ * build digest from the uploaded bytes and the label from the Service name, the build and the
12
+ * configuration, and refuses a request that states another (`build-mismatch`,
13
+ * `address-mismatch`), so a deployer that predicted an address is never served elsewhere. Its
14
+ * `secrets` are bound to a new deployment, or rewritten in place on the reused one, and never
15
+ * stored; a host never journals the request.
16
+ */
17
+ export interface WorkerDeploymentRequestV1 {
18
+ readonly format: typeof WORKER_DEPLOYMENT_REQUEST_FORMAT;
19
+ readonly version: typeof WORKER_DEPLOYMENT_REQUEST_VERSION;
20
+ /**
21
+ * The Identity that owns the Service when this deploy creates it: the caller, or one it acts as.
22
+ * The caller when absent; an existing Service keeps its owner.
23
+ */
24
+ readonly owner?: string;
25
+ /** Any project name (`PROJECT_NAME`); a Domain deployer names its Domain's origin. */
26
+ readonly project: string;
27
+ readonly environment: string;
28
+ /** `<service>-<content>`, as `workerDeploymentAddress` derives it. */
29
+ readonly label: string;
30
+ /** The build digest of `worker`, as `workerBuildDigest` derives it. */
31
+ readonly buildDigest: string;
32
+ readonly configuration: WorkerConfigurationV1;
33
+ readonly worker: WorkerCodeWireV1;
34
+ /** Exactly when `configuration.record`: the record the deployment serves. */
35
+ readonly record?: WorkerRecordWireV1;
36
+ /**
37
+ * Local paths that must answer 200 with the deployment's `x-astrale-worker-version` before the
38
+ * deployment is activated; none when absent. A probe is not part of the configuration.
39
+ */
40
+ readonly readiness?: readonly string[];
41
+ /** The secret values, exactly the names `configuration.secrets` declares. */
42
+ readonly secrets: Readonly<Record<string, string>>;
43
+ /** Whether the Service serves the deployment once it is active; `true` when absent. */
44
+ readonly promote?: boolean;
45
+ }
46
+ /** The code of one Worker on the wire, each file in its content envelope. */
47
+ export interface WorkerCodeWireV1 {
48
+ readonly manifest: WorkerBuildManifest;
49
+ readonly mainModule: string;
50
+ readonly modules: readonly {
51
+ readonly name: string;
52
+ readonly kind: WorkerModule['kind'];
53
+ readonly content: WorkerContentV1;
54
+ }[];
55
+ readonly assets?: readonly {
56
+ readonly path: string;
57
+ readonly contentType?: string;
58
+ readonly content: WorkerContentV1;
59
+ }[];
60
+ }
61
+ /** One Worker deployment as its deployer describes it, before its address is derived. */
62
+ export interface WorkerDeploymentInput {
63
+ readonly owner?: string;
64
+ readonly project: string;
65
+ readonly environment: string;
66
+ readonly worker: WorkerCode;
67
+ readonly configuration: WorkerConfigurationV1;
68
+ readonly record?: WorkerRecord;
69
+ readonly readiness?: readonly string[];
70
+ readonly secrets?: Readonly<Record<string, string>>;
71
+ readonly promote?: boolean;
72
+ }
73
+ /** Where one Worker deployment is addressed: its Service, its digests and its label. */
74
+ export interface WorkerDeploymentAddress {
75
+ readonly service: string;
76
+ readonly buildDigest: Digest;
77
+ readonly configurationDigest: Digest;
78
+ readonly label: string;
79
+ }
80
+ /** One Worker deployment as a host admitted it: decoded, digested and addressed. */
81
+ export interface WorkerDeployment extends WorkerDeploymentAddress {
82
+ readonly owner?: string;
83
+ readonly project: string;
84
+ readonly environment: string;
85
+ readonly worker: WorkerCode;
86
+ readonly configuration: WorkerConfigurationV1;
87
+ readonly record?: WorkerRecord;
88
+ readonly readiness: readonly string[];
89
+ readonly secrets: Readonly<Record<string, string>>;
90
+ readonly promote: boolean;
91
+ }
92
+ /** Why a host refuses a Worker deployment request before reading or writing anything. */
93
+ export declare class WorkerDeploymentRefusal extends Error {
94
+ readonly reason: 'invalid-deployment' | 'build-mismatch' | 'address-mismatch';
95
+ readonly name = "WorkerDeploymentRefusal";
96
+ constructor(reason: 'invalid-deployment' | 'build-mismatch' | 'address-mismatch', message: string, options?: ErrorOptions);
97
+ }
98
+ /** The address one Worker deployment gets: its Service's name, its two digests and its label. */
99
+ export declare function workerDeploymentAddress(input: {
100
+ readonly project: string;
101
+ readonly environment: string;
102
+ readonly worker: WorkerCode;
103
+ readonly configuration: WorkerConfigurationV1;
104
+ }): WorkerDeploymentAddress;
105
+ /**
106
+ * Encode one Worker deployment as its request: its address derived, every file in its content
107
+ * envelope. A deployment its host would refuse as invalid is refused here, before any call.
108
+ */
109
+ export declare function encodeWorkerDeploymentRequest(input: WorkerDeploymentInput): Promise<WorkerDeploymentRequestV1>;
110
+ /**
111
+ * Decode and admit one Worker deployment request, as a host does before any graph or provider
112
+ * effect: exactly this request shape, every file decoded within its bounds and the Worker's, the
113
+ * configuration canonical, the record exactly when the configuration asks for one, the secrets
114
+ * exactly the names it declares. Then the build digest is recomputed from the decoded bytes
115
+ * (`build-mismatch` when the request states another) and the label from the Service name, that
116
+ * digest and the configuration (`address-mismatch`). Every refusal is a `WorkerDeploymentRefusal`.
117
+ */
118
+ export declare function decodeWorkerDeploymentRequest(input: unknown): Promise<WorkerDeployment>;
@@ -0,0 +1,280 @@
1
+ import { serviceName, workerDeploymentLabel } from './address.js';
2
+ import { acceptWorkerCode, workerBuildDigest } from './build.js';
3
+ import { acceptWorkerConfiguration, workerConfigurationDigest } from './configuration.js';
4
+ import { decodeWorkerContent, encodeWorkerContent } from './content.js';
5
+ import { WORKER_DEPLOYMENT_LIMITS } from './limits.js';
6
+ import { acceptWorkerRecord, decodeWorkerRecord, encodeWorkerRecord } from './record.js';
7
+ export const WORKER_DEPLOYMENT_REQUEST_FORMAT = 'astrale.worker.deployment-request';
8
+ export const WORKER_DEPLOYMENT_REQUEST_VERSION = 1;
9
+ /** Why a host refuses a Worker deployment request before reading or writing anything. */
10
+ export class WorkerDeploymentRefusal extends Error {
11
+ reason;
12
+ name = 'WorkerDeploymentRefusal';
13
+ constructor(reason, message, options) {
14
+ super(message, options);
15
+ this.reason = reason;
16
+ }
17
+ }
18
+ /** The address one Worker deployment gets: its Service's name, its two digests and its label. */
19
+ export function workerDeploymentAddress(input) {
20
+ const service = serviceName(input.project, input.environment);
21
+ const buildDigest = workerBuildDigest(input.worker);
22
+ const configurationDigest = workerConfigurationDigest(input.configuration);
23
+ return Object.freeze({
24
+ service,
25
+ buildDigest,
26
+ configurationDigest,
27
+ label: workerDeploymentLabel({ service, buildDigest, configurationDigest }),
28
+ });
29
+ }
30
+ /**
31
+ * Encode one Worker deployment as its request: its address derived, every file in its content
32
+ * envelope. A deployment its host would refuse as invalid is refused here, before any call.
33
+ */
34
+ export async function encodeWorkerDeploymentRequest(input) {
35
+ const deployment = admitDeployment(input);
36
+ const { worker } = deployment;
37
+ const modules = await Promise.all(worker.modules.map(async (module) => Object.freeze({
38
+ name: module.name,
39
+ kind: module.kind,
40
+ content: await encodeWorkerContent(module.bytes),
41
+ })));
42
+ const assets = await Promise.all((worker.assets ?? []).map(async (asset) => Object.freeze({
43
+ path: asset.path,
44
+ ...(asset.contentType === undefined ? {} : { contentType: asset.contentType }),
45
+ content: await encodeWorkerContent(asset.bytes),
46
+ })));
47
+ return Object.freeze({
48
+ format: WORKER_DEPLOYMENT_REQUEST_FORMAT,
49
+ version: WORKER_DEPLOYMENT_REQUEST_VERSION,
50
+ ...(deployment.owner === undefined ? {} : { owner: deployment.owner }),
51
+ project: deployment.project,
52
+ environment: deployment.environment,
53
+ label: deployment.label,
54
+ buildDigest: deployment.buildDigest,
55
+ configuration: deployment.configuration,
56
+ worker: Object.freeze({
57
+ manifest: worker.manifest,
58
+ mainModule: worker.mainModule,
59
+ modules: Object.freeze(modules),
60
+ ...(assets.length === 0 ? {} : { assets: Object.freeze(assets) }),
61
+ }),
62
+ ...(deployment.record === undefined ? {} : { record: encodeWorkerRecord(deployment.record) }),
63
+ ...(deployment.readiness.length === 0 ? {} : { readiness: deployment.readiness }),
64
+ secrets: deployment.secrets,
65
+ ...(input.promote === undefined ? {} : { promote: input.promote }),
66
+ });
67
+ }
68
+ /**
69
+ * Decode and admit one Worker deployment request, as a host does before any graph or provider
70
+ * effect: exactly this request shape, every file decoded within its bounds and the Worker's, the
71
+ * configuration canonical, the record exactly when the configuration asks for one, the secrets
72
+ * exactly the names it declares. Then the build digest is recomputed from the decoded bytes
73
+ * (`build-mismatch` when the request states another) and the label from the Service name, that
74
+ * digest and the configuration (`address-mismatch`). Every refusal is a `WorkerDeploymentRefusal`.
75
+ */
76
+ export async function decodeWorkerDeploymentRequest(input) {
77
+ let deployment;
78
+ let stated;
79
+ try {
80
+ const value = exact(input, [
81
+ 'format',
82
+ 'version',
83
+ 'project',
84
+ 'environment',
85
+ 'label',
86
+ 'buildDigest',
87
+ 'configuration',
88
+ 'worker',
89
+ 'secrets',
90
+ ], ['owner', 'record', 'readiness', 'promote'], 'request');
91
+ if (value.format !== WORKER_DEPLOYMENT_REQUEST_FORMAT ||
92
+ value.version !== WORKER_DEPLOYMENT_REQUEST_VERSION) {
93
+ throw new TypeError('The request is not a WorkerDeploymentRequestV1.');
94
+ }
95
+ stated = { buildDigest: value.buildDigest, label: value.label };
96
+ deployment = admitDeployment({
97
+ ...(value.owner === undefined ? {} : { owner: value.owner }),
98
+ project: value.project,
99
+ environment: value.environment,
100
+ worker: await decodeCode(value.worker),
101
+ configuration: value.configuration,
102
+ ...(value.record === undefined ? {} : { record: decodeWorkerRecord(value.record) }),
103
+ ...(value.readiness === undefined ? {} : { readiness: value.readiness }),
104
+ secrets: value.secrets,
105
+ ...(value.promote === undefined ? {} : { promote: value.promote }),
106
+ });
107
+ }
108
+ catch (cause) {
109
+ if (!(cause instanceof TypeError))
110
+ throw cause;
111
+ throw new WorkerDeploymentRefusal('invalid-deployment', cause.message, { cause });
112
+ }
113
+ if (stated.buildDigest !== deployment.buildDigest) {
114
+ throw new WorkerDeploymentRefusal('build-mismatch', `The uploaded Worker has build digest ${deployment.buildDigest}, not ${quoted(stated.buildDigest)}.`);
115
+ }
116
+ if (stated.label !== deployment.label) {
117
+ throw new WorkerDeploymentRefusal('address-mismatch', `This release of this Service is deployed at ${deployment.label}, not ${quoted(stated.label)}.`);
118
+ }
119
+ return deployment;
120
+ }
121
+ /** A stated value as a refusal names it: short printable text as written, anything else not. */
122
+ function quoted(value) {
123
+ return typeof value === 'string' && value.length <= 80 && !/[\p{Cc}]/u.test(value)
124
+ ? value
125
+ : 'the one stated';
126
+ }
127
+ /** Admit the decoded parts of one deployment and derive its address. */
128
+ function admitDeployment(input) {
129
+ if (input === null || typeof input !== 'object')
130
+ invalid('it is not an object');
131
+ const configuration = acceptWorkerConfiguration(input.configuration);
132
+ const worker = acceptWorkerCode(input.worker);
133
+ const secrets = acceptSecrets(input.secrets ?? {}, configuration.secrets);
134
+ if ((input.record !== undefined) !== configuration.record) {
135
+ invalid(configuration.record
136
+ ? 'its configuration serves a record it does not carry'
137
+ : 'it carries a record its configuration does not serve');
138
+ }
139
+ const record = input.record === undefined ? undefined : acceptWorkerRecord(input.record);
140
+ const readiness = acceptReadiness(input.readiness ?? []);
141
+ if (input.owner !== undefined && !isText(input.owner, 256))
142
+ invalid('its owner is invalid');
143
+ if (input.promote !== undefined && typeof input.promote !== 'boolean') {
144
+ invalid('its promote option is not a boolean');
145
+ }
146
+ const address = workerDeploymentAddress({
147
+ project: input.project,
148
+ environment: input.environment,
149
+ worker,
150
+ configuration,
151
+ });
152
+ return Object.freeze({
153
+ ...address,
154
+ ...(input.owner === undefined ? {} : { owner: input.owner }),
155
+ project: input.project,
156
+ environment: input.environment,
157
+ worker,
158
+ configuration,
159
+ ...(record === undefined ? {} : { record }),
160
+ readiness,
161
+ secrets,
162
+ promote: input.promote ?? true,
163
+ });
164
+ }
165
+ /** Exactly the declared secret names, each value bounded text. */
166
+ function acceptSecrets(input, declared) {
167
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
168
+ invalid('its secrets are not an object');
169
+ }
170
+ const names = Reflect.ownKeys(input);
171
+ if (names.length !== declared.length ||
172
+ names.some((name) => typeof name !== 'string' || !declared.includes(name))) {
173
+ invalid('its secrets are not exactly the names its configuration declares');
174
+ }
175
+ for (const name of declared) {
176
+ if (typeof input[name] !== 'string' ||
177
+ input[name].length > WORKER_DEPLOYMENT_LIMITS.valueLength) {
178
+ invalid(`secret ${name} is not text within its size limit`);
179
+ }
180
+ }
181
+ return Object.freeze(Object.fromEntries(declared.map((name) => [name, input[name]])));
182
+ }
183
+ /**
184
+ * Readiness paths: at most `readinessPaths` distinct canonical local paths, never a host, a query,
185
+ * credentials or a redirect destination.
186
+ */
187
+ function acceptReadiness(input) {
188
+ if (!Array.isArray(input) ||
189
+ input.length > WORKER_DEPLOYMENT_LIMITS.readinessPaths ||
190
+ new Set(input).size !== input.length) {
191
+ invalid('its readiness paths are invalid');
192
+ }
193
+ for (const path of input) {
194
+ if (typeof path !== 'string' ||
195
+ path.length > 1_024 ||
196
+ !/^\/(?:[A-Za-z0-9._~-]+\/)*[A-Za-z0-9._~-]*$/u.test(path) ||
197
+ path.split('/').some((part) => part === '.' || part === '..')) {
198
+ invalid('its readiness paths are not canonical local paths');
199
+ }
200
+ }
201
+ return Object.freeze([...input]);
202
+ }
203
+ /** Decode the code of one Worker, each file within its bounds and all within the Worker's. */
204
+ async function decodeCode(input) {
205
+ const value = exact(input, ['manifest', 'mainModule', 'modules'], ['assets'], 'worker');
206
+ const limits = WORKER_DEPLOYMENT_LIMITS;
207
+ if (!Array.isArray(value.modules) || value.modules.length > limits.modules) {
208
+ invalid('its modules are invalid');
209
+ }
210
+ if (value.assets !== undefined &&
211
+ (!Array.isArray(value.assets) || value.assets.length > limits.assets)) {
212
+ invalid('its assets are invalid');
213
+ }
214
+ let remaining = limits.workerSize;
215
+ const decode = async (content, size, base64Size) => {
216
+ const bytes = await decodeWorkerContent(content, {
217
+ size: Math.min(size, remaining),
218
+ base64Size,
219
+ });
220
+ remaining -= bytes.byteLength;
221
+ return bytes;
222
+ };
223
+ const modules = [];
224
+ for (const entry of value.modules) {
225
+ const module = exact(entry, ['name', 'kind', 'content'], [], 'module');
226
+ modules.push({
227
+ name: module.name,
228
+ kind: module.kind,
229
+ bytes: await decode(module.content, limits.moduleSize, limits.moduleBase64Size),
230
+ });
231
+ }
232
+ const assets = [];
233
+ for (const entry of (value.assets ?? [])) {
234
+ const asset = exact(entry, ['path', 'content'], ['contentType'], 'asset');
235
+ if (asset.contentType !== undefined && !isText(asset.contentType, 256)) {
236
+ invalid('an asset content type is invalid');
237
+ }
238
+ assets.push({
239
+ path: asset.path,
240
+ bytes: await decode(asset.content, limits.assetSize, limits.assetBase64Size),
241
+ ...(asset.contentType === undefined ? {} : { contentType: asset.contentType }),
242
+ });
243
+ }
244
+ return {
245
+ manifest: value.manifest,
246
+ mainModule: value.mainModule,
247
+ modules,
248
+ ...(assets.length === 0 ? {} : { assets }),
249
+ };
250
+ }
251
+ function isText(value, maximum) {
252
+ return (typeof value === 'string' &&
253
+ value.length > 0 &&
254
+ value.length <= maximum &&
255
+ !/[\p{Cc}]/u.test(value));
256
+ }
257
+ function exact(input, required, optional, label) {
258
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
259
+ invalid(`its ${label} is not an object`);
260
+ }
261
+ for (const key of Reflect.ownKeys(input)) {
262
+ if (typeof key !== 'string' || (!required.includes(key) && !optional.includes(key))) {
263
+ invalid(`its ${label} has an unknown member ${String(key)}`);
264
+ }
265
+ }
266
+ const value = input;
267
+ for (const key of required) {
268
+ if (!Object.hasOwn(value, key))
269
+ invalid(`its ${label} has no ${key}`);
270
+ }
271
+ for (const key of optional) {
272
+ if (Object.hasOwn(value, key) && value[key] === undefined) {
273
+ invalid(`its ${label} has an undefined ${key}`);
274
+ }
275
+ }
276
+ return value;
277
+ }
278
+ function invalid(reason) {
279
+ throw new TypeError(`Worker deployment is invalid: ${reason}.`);
280
+ }
@@ -0,0 +1,104 @@
1
+ import type { WorkerRecord, WorkerRecordWireV1 } from './record.js';
2
+ /** Where a Worker host addresses Services and deployments. */
3
+ export interface WorkerHostingV1 {
4
+ /**
5
+ * The DNS name every URL ends with: `https://<service>.<routingDomain>` serves a Service's
6
+ * current deployment, `https://<label>.<routingDomain>` one deployment.
7
+ */
8
+ readonly routingDomain: string;
9
+ }
10
+ /** What a host lists: the deployments of the caller's Services, optionally of one project or environment. */
11
+ export interface WorkerDeploymentListRequestV1 {
12
+ readonly project?: string;
13
+ readonly environment?: string;
14
+ }
15
+ /** The lifecycle of one deployment at its host. */
16
+ export type WorkerDeploymentState = 'deploying' | 'active' | 'retiring' | 'retired' | 'failed';
17
+ /**
18
+ * What a host knows of one deployment. It names a deployment by its label and URL, never by a
19
+ * provider version. A host keeps every deployment until it is retired: it has no preview, expiry or
20
+ * last call.
21
+ */
22
+ export interface WorkerDeploymentSummaryV1 {
23
+ /** The host's id of the deployment (a node id on Admin's Services). */
24
+ readonly id: string;
25
+ readonly url: string;
26
+ readonly label: string;
27
+ /** The host's id of the Service this deployment is a version of. */
28
+ readonly serviceId: string;
29
+ /** The name of that Service: the first part of the label. */
30
+ readonly service: string;
31
+ readonly project: string;
32
+ readonly environment: string;
33
+ /** Who owns the Service (an Identity node path on Admin's Services). */
34
+ readonly owner: string;
35
+ readonly state: WorkerDeploymentState;
36
+ /** Whether the Service's stable URL serves this deployment. */
37
+ readonly current: boolean;
38
+ readonly createdAt: string;
39
+ /** When its retirement began; present once it is `retiring`. */
40
+ readonly retiredAt?: string;
41
+ readonly buildDigest: string;
42
+ /** The digest of its `WorkerConfigurationV1`. */
43
+ readonly configuration: string;
44
+ /** The RFC 7638 thumbprint of its key, when its configuration asked for a signing identity. */
45
+ readonly signingKeyId?: string;
46
+ /** The record it serves, when its configuration asked for one. */
47
+ readonly record?: WorkerRecordWireV1;
48
+ }
49
+ /** One summary with its record decoded. */
50
+ export type WorkerDeploymentSummary = Omit<WorkerDeploymentSummaryV1, 'record'> & {
51
+ readonly record?: WorkerRecord;
52
+ };
53
+ /** Why a host refused to host one Worker release. A refusal changed nothing (see each reason). */
54
+ export type WorkerDeploymentRejectionReason = 'invalid-deployment' | 'address-mismatch' | 'build-mismatch' | 'service-owned-elsewhere' | 'deployment-conflict' | 'deployment-retired' | 'hosting-unavailable' | 'service-unavailable';
55
+ export declare const WORKER_DEPLOYMENT_REJECTIONS: readonly WorkerDeploymentRejectionReason[];
56
+ /**
57
+ * `WorkerDeploymentResponseV1`: a deployment `created` with the request's secrets `bound` to it,
58
+ * or `reused` with its declared secrets rewritten in place (`updated`) or left `unchanged`, or a
59
+ * refusal. A refusal changed nothing, except that a `service-unavailable` may follow effects (a
60
+ * creation that lost its lease after uploading, an in-place rewrite that wrote part of the
61
+ * values): rerunning the same deploy converges.
62
+ */
63
+ export type WorkerDeploymentResponseV1 = {
64
+ readonly kind: 'deployed';
65
+ readonly state: 'created' | 'reused';
66
+ readonly secrets: {
67
+ readonly state: 'bound' | 'updated' | 'unchanged';
68
+ };
69
+ readonly deployment: WorkerDeploymentSummaryV1;
70
+ } | WorkerDeploymentRejection;
71
+ export interface WorkerDeploymentRejection {
72
+ readonly kind: 'rejected';
73
+ readonly reason: WorkerDeploymentRejectionReason;
74
+ readonly message: string;
75
+ /** When a retry may succeed, for a refusal that only waits on another operation. */
76
+ readonly retryAfter?: string;
77
+ }
78
+ /** One decoded answer of a deploy. */
79
+ export type WorkerDeploymentResult = {
80
+ readonly kind: 'deployed';
81
+ readonly state: 'created' | 'reused';
82
+ readonly secrets: {
83
+ readonly state: 'bound' | 'updated' | 'unchanged';
84
+ };
85
+ readonly deployment: WorkerDeploymentSummary;
86
+ } | WorkerDeploymentRejection;
87
+ /**
88
+ * Admit the answer of a host's `hosting`: its routing domain. The `addressing: 'opaque'` an older
89
+ * Services answers beside it is admitted and dropped: every Service name is opaque.
90
+ */
91
+ export declare function decodeWorkerHosting(input: unknown): WorkerHostingV1;
92
+ /**
93
+ * Admit the answer of a deploy exactly: a deployment whose state reports the secret state it
94
+ * pairs with, or a refusal a host can give.
95
+ */
96
+ export declare function decodeWorkerDeploymentResponse(input: unknown): WorkerDeploymentResult;
97
+ /** Admit the answer of a host's `list`: the summaries of the caller's deployments, in its order. */
98
+ export declare function decodeWorkerDeploymentList(input: unknown): readonly WorkerDeploymentSummary[];
99
+ /**
100
+ * Admit one summary exactly: node ids and names a person can read, a label whose first part is
101
+ * the Service's name, the URL of that label, a known state, instants, digests, and a record that
102
+ * decodes.
103
+ */
104
+ export declare function decodeWorkerDeploymentSummary(input: unknown): WorkerDeploymentSummary;