@astrale-os/sdk 0.6.0-beta.15 → 0.6.0-beta.16

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 (40) hide show
  1. package/dist/deployment/adapter/adapter.d.ts +3 -2
  2. package/dist/deployment/adapter/index.d.ts +1 -0
  3. package/dist/deployment/adapter/index.js +1 -0
  4. package/dist/deployment/adapter/release/refusal.d.ts +10 -0
  5. package/dist/deployment/adapter/release/refusal.js +14 -0
  6. package/dist/tooling/cli/arguments.d.ts +7 -1
  7. package/dist/tooling/cli/arguments.js +67 -5
  8. package/dist/tooling/cli/commit.d.ts +12 -0
  9. package/dist/tooling/cli/commit.js +19 -0
  10. package/dist/tooling/cli/deploy-mode.d.ts +18 -6
  11. package/dist/tooling/cli/deploy-mode.js +28 -1
  12. package/dist/tooling/cli/diff/declared.d.ts +7 -0
  13. package/dist/tooling/cli/diff/declared.js +27 -0
  14. package/dist/tooling/cli/diff/index.d.ts +30 -0
  15. package/dist/tooling/cli/diff/index.js +58 -0
  16. package/dist/tooling/cli/diff/render.d.ts +15 -0
  17. package/dist/tooling/cli/diff/render.js +131 -0
  18. package/dist/tooling/cli/diff/report.d.ts +88 -0
  19. package/dist/tooling/cli/diff/report.js +154 -0
  20. package/dist/tooling/cli/help.js +98 -0
  21. package/dist/tooling/cli/immutable-deployment.d.ts +65 -9
  22. package/dist/tooling/cli/immutable-deployment.js +59 -19
  23. package/dist/tooling/cli/index.d.ts +8 -2
  24. package/dist/tooling/cli/index.js +6 -2
  25. package/dist/tooling/cli/orchestrate.js +104 -2
  26. package/dist/tooling/cli/publish/index.d.ts +74 -0
  27. package/dist/tooling/cli/publish/index.js +364 -0
  28. package/dist/tooling/cli/publish/report.d.ts +49 -0
  29. package/dist/tooling/cli/publish/report.js +15 -0
  30. package/dist/tooling/cli/registry/client.d.ts +127 -0
  31. package/dist/tooling/cli/registry/client.js +328 -0
  32. package/dist/tooling/cli/registry/executable.d.ts +20 -0
  33. package/dist/tooling/cli/registry/executable.js +79 -0
  34. package/dist/tooling/cli/registry/index.d.ts +6 -0
  35. package/dist/tooling/cli/registry/index.js +6 -0
  36. package/dist/tooling/cli/registry/process.d.ts +21 -0
  37. package/dist/tooling/cli/registry/process.js +84 -0
  38. package/dist/tooling/cli/yank.d.ts +34 -0
  39. package/dist/tooling/cli/yank.js +32 -0
  40. package/package.json +2 -2
@@ -0,0 +1,88 @@
1
+ import type { DomainSchema } from '@astrale-os/kernel-dsl/v1/schema';
2
+ import type { BumpLevel, Version } from '../../../platform/versioning/index.js';
3
+ import type { RegistryIndex, RegistryPublication } from '../registry/index.js';
4
+ /** One change since the baseline, with the level it alone requires. */
5
+ export interface DiffChangeV1 {
6
+ /**
7
+ * The compared member's Key (`issues.astrale.ai:class.Issue.property.priority`), or the Domain's
8
+ * origin for a change of the whole build or Schema.
9
+ */
10
+ readonly subject: string;
11
+ /**
12
+ * The compatibility engine's facet (`method.existence`, `property.required`, …), `schema` for a
13
+ * new Schema revision of equivalent meaning, `build` for new code under the same revision.
14
+ */
15
+ readonly facet: string;
16
+ readonly state: 'missing' | 'changed' | 'added';
17
+ readonly bump: BumpLevel;
18
+ /** What differs, such as `optional → required`, when the observation says. */
19
+ readonly detail?: string;
20
+ }
21
+ /**
22
+ * `astrale-domain diff --json` on stdout: the version the Project declares, checked against the
23
+ * highest published stable version up to it (CT30).
24
+ */
25
+ export interface DiffReportV1 {
26
+ readonly format: 'astrale.diff-report';
27
+ readonly version: 1;
28
+ readonly origin: string;
29
+ /** `package.json` `version`. */
30
+ readonly declared: string;
31
+ /** The declared version already names a Publication: the report is informational. */
32
+ readonly published: boolean;
33
+ /** `unregistered`: no Domain of this origin the caller may read; nothing is compared. */
34
+ readonly registry: 'read' | 'unregistered';
35
+ /** The highest published stable version up to the declared one, yanked ones included. */
36
+ readonly baseline?: string;
37
+ /** The level the changes since the baseline require. */
38
+ readonly level?: BumpLevel;
39
+ /** The level after the 0.x rule. */
40
+ readonly effective?: BumpLevel;
41
+ /** The smallest version the changes admit, a pre-release such as `2.0.0-0`. */
42
+ readonly minimum?: string;
43
+ /** The stable form of the required version, such as `2.0.0`. */
44
+ readonly next?: string;
45
+ /** For a refused pre-release, the first pre-release of the required version on its channel. */
46
+ readonly suggestion?: string;
47
+ /**
48
+ * The declared version reaches the minimum, or there is nothing to compare. It judges an
49
+ * unpublished version: a published stable one is its own baseline and is never accepted, so a
50
+ * consumer decides on `published || accepted`, the exit status.
51
+ */
52
+ readonly accepted: boolean;
53
+ /** Every change since the baseline, ordered by subject Key then observation; empty for none. */
54
+ readonly changes: readonly DiffChangeV1[];
55
+ }
56
+ export interface DiffInput {
57
+ readonly origin: string;
58
+ readonly declared: Version;
59
+ /**
60
+ * Which published versions can be the baseline (AM-23): `diff` compares with the highest stable
61
+ * version up to the declared one (`<=`, the default); `publish`, which never runs for a published
62
+ * version, with the highest one below it (`<`).
63
+ */
64
+ readonly bound?: '<' | '<=';
65
+ /** The Schema revision the Project builds now. */
66
+ readonly revision: string;
67
+ /** The caller's view of the registry; `undefined` when it holds no readable Domain of `origin`. */
68
+ readonly index: RegistryIndex | undefined;
69
+ /** The Schema the Project builds now. */
70
+ readonly current: () => DomainSchema;
71
+ /** The build digest of every distinct bundler; read only when the Schema revision is unchanged. */
72
+ readonly builds: () => Promise<readonly string[]>;
73
+ /** The Schema a Publication names; read only when its revision differs from the Project's. */
74
+ readonly published: (publication: RegistryPublication) => Promise<DomainSchema>;
75
+ }
76
+ /**
77
+ * Compare the Project with the highest published stable version up to the declared one
78
+ * (`baselineVersion(index, declared, '<=')`, or below it for `publish`, AM-23) and judge the declared version against the
79
+ * level its changes require (tech Versioning [.21507], [.106102]). The judgement always receives a
80
+ * level beside its baseline (AM-56): the compatibility engine's when the Schema revision changed,
81
+ * otherwise patch, the smallest level, which `diff` picks itself when nothing changed at all.
82
+ */
83
+ export declare function diffReport(input: DiffInput): Promise<DiffReportV1>;
84
+ /**
85
+ * Decode the bundle a Publication stores and admit it only as that Publication's Schema: the
86
+ * Domain's origin and the Publication's Schema revision.
87
+ */
88
+ export declare function publishedSchema(bytes: Uint8Array, publication: RegistryPublication, origin: string): DomainSchema;
@@ -0,0 +1,154 @@
1
+ import * as bundle from '@astrale-os/kernel-dsl/v1/bundle/transport';
2
+ import { requiredBump } from '@astrale-os/kernel-dsl/v1/schema/compatibility';
3
+ import { revision } from '@astrale-os/kernel-dsl/v1/schema/revision';
4
+ import { baselineVersion, judgeVersion } from '../../../platform/versioning/index.js';
5
+ /**
6
+ * Compare the Project with the highest published stable version up to the declared one
7
+ * (`baselineVersion(index, declared, '<=')`, or below it for `publish`, AM-23) and judge the declared version against the
8
+ * level its changes require (tech Versioning [.21507], [.106102]). The judgement always receives a
9
+ * level beside its baseline (AM-56): the compatibility engine's when the Schema revision changed,
10
+ * otherwise patch, the smallest level, which `diff` picks itself when nothing changed at all.
11
+ */
12
+ export async function diffReport(input) {
13
+ const identity = {
14
+ format: 'astrale.diff-report',
15
+ version: 1,
16
+ origin: input.origin,
17
+ declared: input.declared,
18
+ };
19
+ if (input.index === undefined) {
20
+ return Object.freeze({
21
+ ...identity,
22
+ published: false,
23
+ registry: 'unregistered',
24
+ accepted: true,
25
+ changes: Object.freeze([]),
26
+ });
27
+ }
28
+ const publications = input.index.publications;
29
+ const published = publications.some(({ version }) => version === input.declared);
30
+ // An index entry that is not a canonical version is never a baseline: `baselineVersion` keeps
31
+ // only canonical stable versions (AM-56), so the registry's strings need no admission here.
32
+ const index = publications.map(({ version, yanked }) => ({
33
+ version: version,
34
+ yanked,
35
+ }));
36
+ const baseline = baselineVersion(index, input.declared, input.bound ?? '<=');
37
+ if (baseline === undefined) {
38
+ // Without a stable version before it there is nothing to check (tech [.106102]).
39
+ const verdict = judgeVersion({ declared: input.declared });
40
+ return Object.freeze({
41
+ ...identity,
42
+ published,
43
+ registry: 'read',
44
+ accepted: verdict.accepted,
45
+ changes: Object.freeze([]),
46
+ });
47
+ }
48
+ const publication = publications.find(({ version }) => version === baseline);
49
+ const { level, changes } = await changesSince(publication, input);
50
+ const verdict = judgeVersion({ declared: input.declared, previous: baseline, level });
51
+ return Object.freeze({
52
+ ...identity,
53
+ published,
54
+ registry: 'read',
55
+ baseline,
56
+ level,
57
+ effective: verdict.effective,
58
+ minimum: verdict.minimum,
59
+ next: verdict.next,
60
+ ...(verdict.suggestion === undefined ? {} : { suggestion: verdict.suggestion }),
61
+ accepted: verdict.accepted,
62
+ changes,
63
+ });
64
+ }
65
+ /**
66
+ * Decode the bundle a Publication stores and admit it only as that Publication's Schema: the
67
+ * Domain's origin and the Publication's Schema revision.
68
+ */
69
+ export function publishedSchema(bytes, publication, origin) {
70
+ const root = bundle.decode(bytes).root;
71
+ if (root.origin !== origin || revision(root) !== publication.schemaRevision) {
72
+ throw new TypeError(`The stored bundle of ${origin}@${publication.version} is not that Publication's Schema ${publication.schemaRevision}.`);
73
+ }
74
+ return root;
75
+ }
76
+ async function changesSince(publication, input) {
77
+ if (publication.schemaRevision !== input.revision) {
78
+ const required = requiredBump(await input.published(publication), input.current());
79
+ const changes = required.changes.map(change);
80
+ return {
81
+ level: required.level,
82
+ changes: Object.freeze(changes.length > 0
83
+ ? changes
84
+ : // A new revision whose meaning the engine finds equivalent, such as descriptions.
85
+ [
86
+ Object.freeze({
87
+ subject: input.origin,
88
+ facet: 'schema',
89
+ state: 'changed',
90
+ bump: 'patch',
91
+ detail: 'same meaning',
92
+ }),
93
+ ]),
94
+ };
95
+ }
96
+ const builds = await input.builds();
97
+ if (builds.includes(publication.buildDigest)) {
98
+ // No change at all: same build digest and no row of [.21507] (AM-56).
99
+ return { level: 'patch', changes: Object.freeze([]) };
100
+ }
101
+ // Code only, same Schema revision ([.21507], first row).
102
+ return {
103
+ level: 'patch',
104
+ changes: Object.freeze([
105
+ Object.freeze({
106
+ subject: input.origin,
107
+ facet: 'build',
108
+ state: 'changed',
109
+ bump: 'patch',
110
+ detail: `${publication.buildDigest} → ${builds.length === 0 ? 'none' : builds.join(', ')}`,
111
+ }),
112
+ ]),
113
+ };
114
+ }
115
+ function change({ subject, observation, bump }) {
116
+ const detail = describe(observation);
117
+ return Object.freeze({
118
+ subject: subject.key,
119
+ facet: observation.facet,
120
+ // `requiredBump` lists only observations that are not equal.
121
+ state: observation.state,
122
+ bump,
123
+ ...(detail === undefined ? {} : { detail }),
124
+ });
125
+ }
126
+ const DETAILED_DIFFERENCES = 3;
127
+ const VALUE_LENGTH = 40;
128
+ /** The observation's differences in a few words: `optional → required`, `input/type: "string" → "number"`. */
129
+ function describe(observation) {
130
+ const differences = observation.differences;
131
+ if (differences.length === 0)
132
+ return undefined;
133
+ const [first] = differences;
134
+ if (differences.length === 1 &&
135
+ first.path === '/required' &&
136
+ typeof first.before === 'boolean' &&
137
+ typeof first.after === 'boolean') {
138
+ return first.after ? 'optional → required' : 'required → optional';
139
+ }
140
+ const described = differences.slice(0, DETAILED_DIFFERENCES).map((difference) => {
141
+ const path = difference.path === '' ? '/' : difference.path.slice(1);
142
+ if (difference.before === undefined)
143
+ return `${path}: added ${value(difference.after)}`;
144
+ if (difference.after === undefined)
145
+ return `${path}: removed ${value(difference.before)}`;
146
+ return `${path}: ${value(difference.before)} → ${value(difference.after)}`;
147
+ });
148
+ const more = differences.length - described.length;
149
+ return more > 0 ? `${described.join('; ')}; ${more} more` : described.join('; ');
150
+ }
151
+ function value(input) {
152
+ const text = JSON.stringify(input) ?? String(input);
153
+ return text.length > VALUE_LENGTH ? `${text.slice(0, VALUE_LENGTH - 1)}…` : text;
154
+ }
@@ -22,11 +22,14 @@ Commands:
22
22
  dev [environment] Deprecated: use deploy, then astrale domain install.
23
23
  build Build, verify and print the build digest.
24
24
  deploy <environment> Deploy one Environment; never installs.
25
+ diff Show the changes since the published version and the minimum.
25
26
  list List the deployments of the Project's Environments.
27
+ publish <environment> Deploy, verify and publish the package.json version; never installs.
26
28
  test Run Project tests, optionally against one Environment.
27
29
  lint Check Domain source and architecture.
28
30
  package Prepare public declarations for publication.
29
31
  upgrade Update Astrale dependencies through pnpm (default: beta).
32
+ yank <version> Take a published version out of resolution, or put it back.
30
33
 
31
34
  Options:
32
35
  -h, --help Show help.
@@ -187,6 +190,79 @@ Behavior:
187
190
  takes.
188
191
  Exit status: 0 listed, 1 a host could not be listed (with --json, nothing on
189
192
  stdout), 2 usage error.
193
+ `,
194
+ diff: `Show the changes since the last published version and the version they require.
195
+
196
+ Usage:
197
+ astrale-domain diff [--as <identity>] [--json]
198
+
199
+ Options:
200
+ --as <identity> Select the Astrale identity that reads the Domain registry.
201
+ --json Print one DiffReportV1 on stdout; the summary goes to stderr.
202
+ -h, --help Show help for diff.
203
+
204
+ Behavior:
205
+ Reads the declared version from package.json and compares the Domain with the
206
+ highest published stable version up to it, yanked versions included. The level
207
+ the changes require is a floor: removing or changing a member is major, adding
208
+ one minor, anything else (code, descriptions) patch; below 1.0.0 a breaking
209
+ change raises the minor. Callable authorization is outside the floor: raise the
210
+ version yourself when you narrow it.
211
+ When package.json still names a published version, as on a feature branch, the
212
+ report is informational and exits 0. Otherwise the declared version must reach
213
+ the minimum: a version that is too low exits 1 and names what requires more, as
214
+ on a release PR, where Release-As: 2.0.0 or a feat!: commit raises the number.
215
+ Nothing to compare (no published stable version, or a Domain absent from the
216
+ registry or not readable by the caller) exits 0.
217
+ Reads the registry through the Astrale CLI (domain versions) with the caller's
218
+ own credential, and the stored bundle of the compared version only when its
219
+ Schema differs. Builds the Domain when the Schema is unchanged, to compare build
220
+ digests. Deploys, publishes and installs nothing.
221
+ Exit status: 0 accepted, published or nothing to compare; 1 too low or failed
222
+ (with --json, a failure prints nothing on stdout); 2 usage error.
223
+ `,
224
+ publish: `Deploy one Environment, verify it, and publish the version package.json declares.
225
+
226
+ Usage:
227
+ astrale-domain publish <environment> [--allow-dirty] [--as <identity>] [--json]
228
+
229
+ Arguments:
230
+ environment Required adapter environment key, usually production.
231
+
232
+ Options:
233
+ --allow-dirty Publish from a working tree with changes, or outside git; the
234
+ Publication is marked dirty.
235
+ --as <identity> Select the Astrale identity that reads and writes the Domain
236
+ registry and deploys.
237
+ --json Print one PublishReportV1 on stdout; progress goes to stderr.
238
+ -h, --help Show help for publish.
239
+
240
+ Behavior:
241
+ Names one immutable deployment's release with the version in package.json, in
242
+ this order, deploying only once the registry allows the version:
243
+ 1. Before any effect: refuses an Environment that does not deploy immutable
244
+ deployments, a working tree with changes or no git commit (unless
245
+ --allow-dirty), and warns when no remote branch holds the commit; then builds,
246
+ freezes the configuration, places the deployment and seals its release.
247
+ 2. Reads the version from package.json and the registry through the Astrale CLI.
248
+ A version already published with this release succeeds without effect; with
249
+ another release it is refused: a published version never changes.
250
+ 3. Compares with the highest stable version below it, yanked versions included,
251
+ as diff does, and refuses a version that is too low.
252
+ 4. Deploys the release, or reuses its deployment, as deploy does.
253
+ 5. Verifies that the deployment serves the sealed release, within 60 seconds.
254
+ 6. Asks Admin for the Publication, which Admin creates under the Domain's lock. A
255
+ version published meanwhile with another release is refused, and this
256
+ deployment stays a preview. On a platform dispatch namespace the published
257
+ deployment is then kept from expiry; Admin keeps those of its Services itself.
258
+ A failure leaves at most a deployment without a version, and says whether a rerun
259
+ can help: when it can, rerun publish, which reuses it; a refusal Admin repeats
260
+ (a missing domain_admin right, a release it will not name) names its cause.
261
+ Admin unavailable or the Domain absent from the registry: nothing is deployed.
262
+ It never installs the Domain: install the URL it prints with
263
+ ${installCommand()}.
264
+ Exit status: 0 published or already published; 1 refused or failed (with --json,
265
+ a failure prints nothing on stdout); 2 usage error.
190
266
  `,
191
267
  test: `Run Project tests locally or against one selected Environment.
192
268
 
@@ -214,6 +290,28 @@ Options:
214
290
  --fix Apply supported fixes.
215
291
  --format <format> Select stylish or json output (default: stylish).
216
292
  -h, --help Show help for lint.
293
+ `,
294
+ yank: `Take one published version of the Domain out of resolution, or put it back.
295
+
296
+ Usage:
297
+ astrale-domain yank <version> [--undo] [--json]
298
+
299
+ Arguments:
300
+ version One published version of this Project's Domain, e.g. 1.5.0.
301
+
302
+ Options:
303
+ --undo Let line references choose the version again.
304
+ --json Print one YankReportV1 on stdout.
305
+ -h, --help Show help for yank.
306
+
307
+ Behavior:
308
+ Changes the version in the Admin registry through the Astrale CLI, with its configured
309
+ Admin target and identity; yanking requires administering the Domain. A line reference
310
+ such as @1.5 never chooses a yanked version, and an exact reference installs it with a
311
+ warning. Installations keep running and the deployment keeps serving: yanking only
312
+ changes resolution. A version already in the requested state answers unchanged, so a
313
+ rerun is safe. Exit status: 0 done, 1 refused or failed, 2 usage error; a refused or
314
+ failed run prints nothing on stdout.
217
315
  `,
218
316
  package: `Prepare public TypeScript declarations for package publication.
219
317
 
@@ -1,15 +1,71 @@
1
- import type { Adapter, Build, LegacyAdapter, ResolvedRuntimeReference } from '../../deployment/index.js';
1
+ import type { DeploymentCommitV1, FrozenConfigurationV1 } from '../../deployment/address/index.js';
2
+ import type { Adapter, Build, Bundle, LegacyAdapter, Placement, Release, ResolvedRuntimeReference } from '../../deployment/index.js';
2
3
  import type { DeployResultV1 } from './deploy-result.js';
3
4
  /**
4
- * Deploy one canonical Environment (CT33) as the immutable deployment of one sealed release.
5
+ * Everything the deploy of one canonical Environment derives before its only effectful call: the
6
+ * Bundle, the Environment's current secrets, the frozen configuration, where the host places the
7
+ * deployment, its label, the sealed release it serves and the commit it is built from. Planning has
8
+ * no effect at the host: `placement` only reads, and nothing is created, reused or written.
9
+ */
10
+ export interface ImmutableDeploymentPlan {
11
+ readonly projectDir: string;
12
+ readonly environment: string;
13
+ readonly adapter: Adapter<object>;
14
+ readonly parameters: object;
15
+ readonly identity?: string;
16
+ readonly bundle: Bundle;
17
+ readonly secrets: Readonly<Record<string, string>>;
18
+ readonly configuration: FrozenConfigurationV1;
19
+ readonly placement: Placement;
20
+ /** `<line>-<content>`: the deployment's DNS label and provider script. */
21
+ readonly label: string;
22
+ /** The `DomainRelease` v4 the deployment serves at its URL, `release.url`. */
23
+ readonly release: Release;
24
+ /** The commit this deploy runs from, which a deployment it creates declares. */
25
+ readonly commit: DeploymentCommitV1 | null;
26
+ }
27
+ /**
28
+ * Plan the immutable deployment of one canonical Environment (CT33), without any effect.
29
+ *
30
+ * The SDK bundles exactly as `astrale-domain build` does, asks the adapter to freeze the
31
+ * Environment's configuration and say where its host places deployments, derives the deployment
32
+ * label and URL from them (S3), seals the `DomainRelease` v4 the deployment serves at that URL
33
+ * (S4a), and reads the commit it is built from. `deploy` deploys the plan at once; `publish`
34
+ * checks the registry against the sealed release first, and deploys only when it may name it.
35
+ */
36
+ export declare function planImmutableDeployment(input: {
37
+ readonly projectDir: string;
38
+ readonly environment: string;
39
+ /** A canonical Environment's adapter (CT33), which implements adapter interface v2. */
40
+ readonly adapter: Adapter<object> | LegacyAdapter<object>;
41
+ readonly parameters: object;
42
+ readonly build: Build;
43
+ readonly runtime: ResolvedRuntimeReference;
44
+ readonly identity?: string;
45
+ /** The commit provenance the caller already read (`publish` checks it first); read otherwise. */
46
+ readonly commit?: DeploymentCommitV1 | null;
47
+ readonly signal: AbortSignal;
48
+ }): Promise<ImmutableDeploymentPlan>;
49
+ /**
50
+ * The commit the Project's runtime module is built from, as a deployment declares it (CT18
51
+ * `commit`): its sha, whether the tree holds changes it does not, and the last release tag before
52
+ * it. `null` outside git.
53
+ */
54
+ export declare function runtimeCommit(projectDir: string, runtime: ResolvedRuntimeReference, signal: AbortSignal): Promise<DeploymentCommitV1 | null>;
55
+ /**
56
+ * Create, or reuse, the immutable deployment a plan describes: the adapter's `deployRelease` is
57
+ * its only effectful call. The SDK makes no Kernel call, the deploy calls no instance and installs
58
+ * nothing: `astrale domain install <url>` installs the URL it prints.
5
59
  *
6
- * The SDK derives everything before the adapter's only effectful call: it bundles exactly as
7
- * `astrale-domain build` does, asks the adapter to freeze the Environment's configuration and say
8
- * where its host places deployments, derives the deployment label and URL from them (S3), seals
9
- * the `DomainRelease` v4 the deployment serves at that URL (S4a), and reads the commit it is built
10
- * from. `deployRelease` then creates that deployment or reuses it. The SDK makes no Kernel call,
11
- * the deploy calls no instance and installs nothing: `astrale domain install <url>` installs the
12
- * URL it prints.
60
+ * Returns the deploy result, or nothing once a failure is reported on stderr.
61
+ */
62
+ export declare function deployPlannedRelease(plan: ImmutableDeploymentPlan, input: {
63
+ readonly signal: AbortSignal;
64
+ readonly report: (line: string) => void;
65
+ }): Promise<DeployResultV1 | undefined>;
66
+ /**
67
+ * Deploy one canonical Environment (CT33) as the immutable deployment of one sealed release: plan
68
+ * it without effect, then make the adapter's one effectful call.
13
69
  *
14
70
  * Returns the deploy result, or nothing once a failure is reported on stderr.
15
71
  */
@@ -7,19 +7,15 @@ import { deployResult } from './deploy-result.js';
7
7
  import { error } from './log.js';
8
8
  import { environmentSecrets } from './secrets.js';
9
9
  /**
10
- * Deploy one canonical Environment (CT33) as the immutable deployment of one sealed release.
10
+ * Plan the immutable deployment of one canonical Environment (CT33), without any effect.
11
11
  *
12
- * The SDK derives everything before the adapter's only effectful call: it bundles exactly as
13
- * `astrale-domain build` does, asks the adapter to freeze the Environment's configuration and say
14
- * where its host places deployments, derives the deployment label and URL from them (S3), seals
15
- * the `DomainRelease` v4 the deployment serves at that URL (S4a), and reads the commit it is built
16
- * from. `deployRelease` then creates that deployment or reuses it. The SDK makes no Kernel call,
17
- * the deploy calls no instance and installs nothing: `astrale domain install <url>` installs the
18
- * URL it prints.
19
- *
20
- * Returns the deploy result, or nothing once a failure is reported on stderr.
12
+ * The SDK bundles exactly as `astrale-domain build` does, asks the adapter to freeze the
13
+ * Environment's configuration and say where its host places deployments, derives the deployment
14
+ * label and URL from them (S3), seals the `DomainRelease` v4 the deployment serves at that URL
15
+ * (S4a), and reads the commit it is built from. `deploy` deploys the plan at once; `publish`
16
+ * checks the registry against the sealed release first, and deploys only when it may name it.
21
17
  */
22
- export async function deployImmutable(input) {
18
+ export async function planImmutableDeployment(input) {
23
19
  const { parameters, environment, build, signal } = input;
24
20
  const adapter = releaseAdapter(input.adapter);
25
21
  const identity = input.identity === undefined ? {} : { identity: input.identity };
@@ -39,25 +35,59 @@ export async function deployImmutable(input) {
39
35
  const origin = build.schema.compiled.root.origin;
40
36
  const label = deploymentLabel(deploymentLine({ origin, environment, addressing: placement.addressing }), deploymentContent(bundle.digest, configuration));
41
37
  const release = assemble(build, bundle.digest, deploymentUrl(label, placement.routingDomain));
42
- const commit = await deploymentCommit({
43
- projectDir: input.projectDir,
44
- // The configuration sits in the Project directory, and names its runtime relative to it.
45
- runtimeModule: resolve(input.projectDir, input.runtime.reference.path),
46
- signal,
47
- });
48
- const deployed = await adapter.deployRelease(parameters, {
38
+ const commit = input.commit !== undefined
39
+ ? input.commit
40
+ : await runtimeCommit(input.projectDir, input.runtime, signal);
41
+ return Object.freeze({
49
42
  projectDir: input.projectDir,
50
43
  environment,
44
+ adapter,
45
+ parameters,
51
46
  ...identity,
52
- secrets,
53
47
  bundle,
48
+ secrets,
54
49
  configuration,
55
50
  placement,
56
51
  label,
57
52
  release,
58
53
  commit,
54
+ });
55
+ }
56
+ /**
57
+ * The commit the Project's runtime module is built from, as a deployment declares it (CT18
58
+ * `commit`): its sha, whether the tree holds changes it does not, and the last release tag before
59
+ * it. `null` outside git.
60
+ */
61
+ export function runtimeCommit(projectDir, runtime, signal) {
62
+ return deploymentCommit({
63
+ projectDir,
64
+ // The configuration sits in the Project directory, and names its runtime relative to it.
65
+ runtimeModule: resolve(projectDir, runtime.reference.path),
59
66
  signal,
60
67
  });
68
+ }
69
+ /**
70
+ * Create, or reuse, the immutable deployment a plan describes: the adapter's `deployRelease` is
71
+ * its only effectful call. The SDK makes no Kernel call, the deploy calls no instance and installs
72
+ * nothing: `astrale domain install <url>` installs the URL it prints.
73
+ *
74
+ * Returns the deploy result, or nothing once a failure is reported on stderr.
75
+ */
76
+ export async function deployPlannedRelease(plan, input) {
77
+ const { adapter, environment, label, release } = plan;
78
+ const deployed = await adapter.deployRelease(plan.parameters, {
79
+ projectDir: plan.projectDir,
80
+ environment,
81
+ ...(plan.identity === undefined ? {} : { identity: plan.identity }),
82
+ secrets: plan.secrets,
83
+ bundle: plan.bundle,
84
+ configuration: plan.configuration,
85
+ placement: plan.placement,
86
+ label,
87
+ release,
88
+ commit: plan.commit,
89
+ signal: input.signal,
90
+ });
61
91
  if (deployed.status !== 'ready') {
62
92
  error(`Deployment failed during ${deployed.phase}: ${deployed.error.message}`);
63
93
  return undefined;
@@ -74,6 +104,16 @@ export async function deployImmutable(input) {
74
104
  input.report('Ready: deployment verified');
75
105
  return result;
76
106
  }
107
+ /**
108
+ * Deploy one canonical Environment (CT33) as the immutable deployment of one sealed release: plan
109
+ * it without effect, then make the adapter's one effectful call.
110
+ *
111
+ * Returns the deploy result, or nothing once a failure is reported on stderr.
112
+ */
113
+ export async function deployImmutable(input) {
114
+ const plan = await planImmutableDeployment(input);
115
+ return deployPlannedRelease(plan, { signal: input.signal, report: input.report });
116
+ }
77
117
  /**
78
118
  * What the deploy did to the secrets of the one deployment it made or reused: `bound` to the new
79
119
  * deployment, or `updated` or left `unchanged` in place on the reused one. No other deployment's
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * `@astrale-os/sdk/cli` — the `astrale-domain` CLI
3
- * (dev | build | deploy | list | test | lint | package), its JSON results, and the explicit dotenv
4
- * boundary. `dev` is deprecated and lives in `legacy/development/`.
3
+ * (dev | build | deploy | diff | list | publish | test | lint | package | yank), its JSON results,
4
+ * the deploy mode each command selects for an Environment (CT33), and the explicit dotenv boundary.
5
+ * `dev` is deprecated and lives in `legacy/development/`.
5
6
  *
6
7
  * Node-only: the CLI imports `node:fs`/`node:module`/`node:url` and runs under
7
8
  * Bun (it imports the project's `astrale.config.ts` directly). This subpath is
@@ -13,5 +14,10 @@
13
14
  export { run } from './run.js';
14
15
  export type { BuildResultV1 } from './build.js';
15
16
  export type { DeployResultV1, DeploymentSecretsState } from './deploy-result.js';
17
+ export type { DiffChangeV1, DiffReportV1 } from './diff/index.js';
16
18
  export type { ListedDeploymentV1, ListResultV1, UnlistedEnvironmentV1 } from './list.js';
19
+ export type { PublishReportV1 } from './publish/index.js';
20
+ export type { YankReportV1 } from './yank.js';
21
+ export { selectDeployMode } from './deploy-mode.js';
22
+ export type { CloudflareDirectParameter, DeployMode, DeployModeCommand, DeployRefusal, LegacyDirectReason, } from './deploy-mode.js';
17
23
  export { loadDeclaredSecrets } from './dotenv.js';
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * `@astrale-os/sdk/cli` — the `astrale-domain` CLI
3
- * (dev | build | deploy | list | test | lint | package), its JSON results, and the explicit dotenv
4
- * boundary. `dev` is deprecated and lives in `legacy/development/`.
3
+ * (dev | build | deploy | diff | list | publish | test | lint | package | yank), its JSON results,
4
+ * the deploy mode each command selects for an Environment (CT33), and the explicit dotenv boundary.
5
+ * `dev` is deprecated and lives in `legacy/development/`.
5
6
  *
6
7
  * Node-only: the CLI imports `node:fs`/`node:module`/`node:url` and runs under
7
8
  * Bun (it imports the project's `astrale.config.ts` directly). This subpath is
@@ -11,4 +12,7 @@
11
12
  * The bin lives at `./bin`.
12
13
  */
13
14
  export { run } from './run.js';
15
+ // The domain-publish Action asks the selection `publish` makes before it runs it, to skip an
16
+ // Environment that is not canonical with a notice instead of failing on its refusal.
17
+ export { selectDeployMode } from './deploy-mode.js';
14
18
  export { loadDeclaredSecrets } from './dotenv.js';