@astrale-os/sdk 0.6.0-beta.13 → 0.6.0-beta.14

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 (135) hide show
  1. package/README.md +4 -0
  2. package/dist/deployment/adapter/adapter.d.ts +1 -6
  3. package/dist/deployment/adapter/legacy/adapter.d.ts +0 -10
  4. package/dist/deployment/adapter/legacy/deploy/context.d.ts +0 -2
  5. package/dist/deployment/adapter/legacy/deploy/result.d.ts +2 -3
  6. package/dist/deployment/adapter/legacy/index.d.ts +10 -8
  7. package/dist/deployment/adapter/legacy/prepare/context.d.ts +0 -2
  8. package/dist/deployment/adapter/release/context.d.ts +16 -2
  9. package/dist/deployment/address/index.d.ts +1 -1
  10. package/dist/deployment/address/index.js +1 -0
  11. package/dist/deployment/address/summary.d.ts +30 -0
  12. package/dist/deployment/address/summary.js +117 -1
  13. package/dist/deployment/build/index.d.ts +1 -1
  14. package/dist/deployment/build/index.js +1 -1
  15. package/dist/deployment/build/manifest.d.ts +5 -0
  16. package/dist/deployment/build/manifest.js +10 -0
  17. package/dist/deployment/index.d.ts +1 -1
  18. package/dist/deployment/release/legacy/index.d.ts +15 -10
  19. package/dist/deployment/release/legacy/index.js +15 -10
  20. package/dist/deployment/verify/readiness/deployment.js +3 -13
  21. package/dist/deployment/verify/readiness/fetch.d.ts +13 -0
  22. package/dist/deployment/verify/readiness/fetch.js +26 -0
  23. package/dist/deployment/verify/readiness/identity.d.ts +3 -6
  24. package/dist/deployment/verify/readiness/identity.js +3 -3
  25. package/dist/deployment/verify/readiness/index.d.ts +12 -8
  26. package/dist/deployment/verify/readiness/index.js +15 -4
  27. package/dist/deployment/verify/readiness/legacy/index.d.ts +2 -0
  28. package/dist/deployment/verify/readiness/legacy/index.js +2 -0
  29. package/dist/deployment/verify/readiness/legacy/publication.d.ts +12 -0
  30. package/dist/deployment/verify/readiness/legacy/publication.js +23 -0
  31. package/dist/deployment/verify/readiness/release.d.ts +8 -0
  32. package/dist/deployment/verify/readiness/release.js +22 -0
  33. package/dist/execution/identity/authentication.d.ts +7 -0
  34. package/dist/execution/identity/authentication.js +7 -1
  35. package/dist/execution/identity/delivery.d.ts +11 -4
  36. package/dist/execution/identity/delivery.js +47 -20
  37. package/dist/execution/identity/identity.d.ts +7 -3
  38. package/dist/execution/identity/identity.js +2 -2
  39. package/dist/execution/invocation/admission/authentication.d.ts +1 -2
  40. package/dist/execution/invocation/admission/authentication.js +1 -2
  41. package/dist/execution/invocation/admission/authority.d.ts +10 -4
  42. package/dist/execution/invocation/admission/authority.js +5 -1
  43. package/dist/execution/invocation/invocation.js +51 -14
  44. package/dist/execution/runtime/index.d.ts +1 -0
  45. package/dist/execution/runtime/index.js +1 -0
  46. package/dist/execution/runtime/load.d.ts +3 -3
  47. package/dist/execution/runtime/load.js +3 -2
  48. package/dist/execution/runtime/release.d.ts +22 -0
  49. package/dist/execution/runtime/release.js +14 -0
  50. package/dist/execution/serve.d.ts +2 -2
  51. package/dist/execution/service/create.js +9 -7
  52. package/dist/execution/service/release.d.ts +2 -2
  53. package/dist/execution/service/release.js +3 -2
  54. package/dist/execution/service/service.d.ts +9 -5
  55. package/dist/execution/service/serving/delivery.d.ts +6 -0
  56. package/dist/execution/service/{publication → serving}/delivery.js +5 -1
  57. package/dist/execution/service/{publication → serving}/index.d.ts +1 -1
  58. package/dist/execution/service/serving/installation.d.ts +4 -0
  59. package/dist/execution/service/serving/installation.js +9 -0
  60. package/dist/execution/service/serving/legacy/index.d.ts +15 -0
  61. package/dist/execution/service/serving/legacy/index.js +19 -0
  62. package/dist/execution/service/serving/source.d.ts +18 -0
  63. package/dist/execution/service/serving/source.js +19 -0
  64. package/dist/project/define.js +57 -28
  65. package/dist/project/install-command.d.ts +10 -0
  66. package/dist/project/install-command.js +13 -0
  67. package/dist/project/project.d.ts +4 -2
  68. package/dist/tooling/cli/arguments.d.ts +0 -2
  69. package/dist/tooling/cli/arguments.js +22 -15
  70. package/dist/tooling/cli/commit.d.ts +23 -0
  71. package/dist/tooling/cli/commit.js +216 -0
  72. package/dist/tooling/cli/deploy-mode.d.ts +19 -12
  73. package/dist/tooling/cli/deploy-mode.js +14 -32
  74. package/dist/tooling/cli/deploy-result.d.ts +10 -7
  75. package/dist/tooling/cli/deploy-result.js +4 -3
  76. package/dist/tooling/cli/development/candidate.d.ts +2 -3
  77. package/dist/tooling/cli/development/candidate.js +2 -9
  78. package/dist/tooling/cli/development/deployment.d.ts +2 -8
  79. package/dist/tooling/cli/development/deployment.js +2 -21
  80. package/dist/tooling/cli/development/develop.d.ts +4 -3
  81. package/dist/tooling/cli/development/develop.js +8 -112
  82. package/dist/tooling/cli/development/report.d.ts +1 -3
  83. package/dist/tooling/cli/development/report.js +1 -4
  84. package/dist/tooling/cli/help.js +28 -26
  85. package/dist/tooling/cli/immutable-deployment.d.ts +27 -0
  86. package/dist/tooling/cli/immutable-deployment.js +95 -0
  87. package/dist/tooling/cli/index.d.ts +2 -4
  88. package/dist/tooling/cli/index.js +2 -3
  89. package/dist/tooling/cli/legacy/deploy-result.d.ts +5 -3
  90. package/dist/tooling/cli/legacy/deploy-result.js +5 -3
  91. package/dist/tooling/cli/legacy/stable-target-adapter.d.ts +5 -3
  92. package/dist/tooling/cli/legacy/stable-target-deploy.d.ts +42 -0
  93. package/dist/tooling/cli/legacy/stable-target-deploy.js +104 -0
  94. package/dist/tooling/cli/orchestrate.js +46 -97
  95. package/dist/tooling/cli/secrets.d.ts +9 -0
  96. package/dist/tooling/cli/secrets.js +19 -0
  97. package/package.json +1 -1
  98. package/dist/deployment/verify/readiness/publication.d.ts +0 -11
  99. package/dist/deployment/verify/readiness/publication.js +0 -41
  100. package/dist/execution/service/publication/delivery.d.ts +0 -2
  101. package/dist/execution/service/publication/installation.d.ts +0 -3
  102. package/dist/execution/service/publication/installation.js +0 -8
  103. package/dist/execution/service/publication/source.d.ts +0 -3
  104. package/dist/execution/service/publication/source.js +0 -11
  105. package/dist/tooling/cli/astrale/authority.d.ts +0 -5
  106. package/dist/tooling/cli/astrale/authority.js +0 -11
  107. package/dist/tooling/cli/astrale/executable.d.ts +0 -2
  108. package/dist/tooling/cli/astrale/executable.js +0 -47
  109. package/dist/tooling/cli/astrale/failure.d.ts +0 -6
  110. package/dist/tooling/cli/astrale/failure.js +0 -48
  111. package/dist/tooling/cli/astrale/instance.d.ts +0 -11
  112. package/dist/tooling/cli/astrale/instance.js +0 -52
  113. package/dist/tooling/cli/astrale/process.d.ts +0 -13
  114. package/dist/tooling/cli/astrale/process.js +0 -94
  115. package/dist/tooling/cli/astrale/version.d.ts +0 -18
  116. package/dist/tooling/cli/astrale/version.js +0 -55
  117. package/dist/tooling/cli/development/release-evidence.d.ts +0 -4
  118. package/dist/tooling/cli/development/release-evidence.js +0 -10
  119. package/dist/tooling/cli/development/session-file.d.ts +0 -53
  120. package/dist/tooling/cli/development/session-file.js +0 -337
  121. package/dist/tooling/cli/development/target-lock.d.ts +0 -16
  122. package/dist/tooling/cli/development/target-lock.js +0 -68
  123. package/dist/tooling/cli/development/view.d.ts +0 -28
  124. package/dist/tooling/cli/development/view.js +0 -151
  125. package/dist/tooling/cli/environment-release.d.ts +0 -24
  126. package/dist/tooling/cli/environment-release.js +0 -46
  127. package/dist/tooling/cli/evidence.d.ts +0 -14
  128. package/dist/tooling/cli/evidence.js +0 -53
  129. package/dist/tooling/cli/installation/index.d.ts +0 -2
  130. package/dist/tooling/cli/installation/index.js +0 -1
  131. package/dist/tooling/cli/installation/observation.d.ts +0 -10
  132. package/dist/tooling/cli/installation/observation.js +0 -44
  133. package/dist/tooling/cli/installation/reconcile.d.ts +0 -32
  134. package/dist/tooling/cli/installation/reconcile.js +0 -122
  135. /package/dist/execution/service/{publication → serving}/index.js +0 -0
@@ -0,0 +1,216 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { isVersion } from '../../platform/versioning/version.js';
5
+ /** A commit as `DeploymentCommitV1` holds it: the full SHA-1 git prints, in lower case. */
6
+ const SHA = /^[0-9a-f]{40}$/u;
7
+ /** `git describe --long` output: `<tag>-<commits since the tag>-g<abbreviated commit>`. */
8
+ const DESCRIBED = /^(?<tag>.+)-(?<distance>\d+)-g[0-9a-f]+$/u;
9
+ /** A tag prefix this module can hand to `git describe --match` without escaping a glob. */
10
+ const LITERAL_PREFIX = /^[^*?[\]\\\s]*$/u;
11
+ /** The configuration files release-please reads at a repository root, the dotted name first. */
12
+ const RELEASE_PLEASE_CONFIGS = ['.release-please-config.json', 'release-please-config.json'];
13
+ const GIT_TIMEOUT_MS = 30_000;
14
+ /**
15
+ * The commit provenance a canonical deploy declares in its deployment record (CT18 `commit`): the
16
+ * commit of the repository the Project lives in, whether the working tree holds changes that
17
+ * commit does not, and the last release of the Domain before it, as `git describe` finds it.
18
+ *
19
+ * - `dirty` covers tracked changes and untracked files, over the whole working tree; ignored files
20
+ * never count. Unlike `git describe --dirty`, an untracked file counts, since a build can import
21
+ * it.
22
+ * - `base` is the nearest release tag of the package the Domain is released from: the
23
+ * release-please package that holds the Domain's runtime module (see {@link releaseTagPrefixes}).
24
+ * The tag prefix is stripped, and a tag whose rest is not a canonical SemVer version is left
25
+ * out, so the record never carries a base a deployment name would refuse (AM-57). A Domain no
26
+ * release line holds declares no base: it is never given another package's version.
27
+ * - Outside a git repository, without git, or in a repository whose commits are not SHA-1, the
28
+ * deploy declares no commit (`null`); a deploy never fails for want of provenance.
29
+ */
30
+ export async function deploymentCommit(input) {
31
+ const head = await git(input.projectDir, input.signal, ['rev-parse', '--verify', 'HEAD']);
32
+ if (head === undefined || !SHA.test(head))
33
+ return null;
34
+ const status = await git(input.projectDir, input.signal, [
35
+ 'status',
36
+ '--porcelain',
37
+ '--untracked-files=normal',
38
+ ]);
39
+ if (status === undefined)
40
+ return null;
41
+ const prefixes = await releaseTagPrefixes(input);
42
+ const base = prefixes === undefined ? undefined : await describedBase(input, prefixes);
43
+ return Object.freeze({
44
+ sha: head,
45
+ dirty: status.length > 0,
46
+ ...(base === undefined ? {} : { base }),
47
+ });
48
+ }
49
+ /**
50
+ * The prefixes of the release tags of the package the Domain is released from, or nothing when
51
+ * no release line holds the Domain.
52
+ *
53
+ * In a repository with a release-please configuration at its root, that package is the configured
54
+ * package whose path is the nearest one holding the runtime module's directory, unless one of its
55
+ * `exclude-paths` holds it. Its tags are named as release-please names them: `<component>`, its
56
+ * `tag-separator` (`-`) and `v` before the version, without the component when
57
+ * `include-component-in-tag` is false and without the `v` when `include-v-in-tag` is false; the
58
+ * component is the package's `component`, else its unscoped `package-name`, else the unscoped name
59
+ * in its `package.json`. Root-level options are the packages' defaults, as in release-please.
60
+ *
61
+ * Without a configuration, the tags are those `npm version` writes (`v<version>`) and
62
+ * `<name>-v<version>`, where the name is the unscoped one in the Project's `package.json`.
63
+ */
64
+ async function releaseTagPrefixes(input) {
65
+ const root = await git(input.projectDir, input.signal, ['rev-parse', '--show-toplevel']);
66
+ if (root === undefined)
67
+ return undefined;
68
+ const config = await releasePleaseConfig(root);
69
+ if (config === undefined) {
70
+ const name = unscoped(await packageName(input.projectDir));
71
+ return literal(['v', ...(name === undefined ? [] : [`${name}-v`])]);
72
+ }
73
+ // Git names the module's directory relative to its repository, symbolic links resolved.
74
+ const located = await git(dirname(input.runtimeModule), input.signal, [
75
+ 'rev-parse',
76
+ '--show-toplevel',
77
+ '--show-prefix',
78
+ ]);
79
+ const [moduleRoot, prefix = ''] = located?.split('\n') ?? [];
80
+ if (moduleRoot !== root)
81
+ return undefined;
82
+ const path = prefix.replace(/\/$/u, '');
83
+ const found = releasePackage(config, path);
84
+ if (found === undefined)
85
+ return undefined;
86
+ const option = (key) => found.options[key] ?? config[key];
87
+ const v = option('include-v-in-tag') === false ? '' : 'v';
88
+ if (option('include-component-in-tag') === false)
89
+ return literal([v]);
90
+ const separator = option('tag-separator') ?? '-';
91
+ const component = option('component') ??
92
+ unscoped(option('package-name')) ??
93
+ unscoped(await packageName(join(root, found.path)));
94
+ if (typeof component !== 'string' || component.length === 0 || typeof separator !== 'string') {
95
+ return undefined;
96
+ }
97
+ return literal([`${component}${separator}${v}`]);
98
+ }
99
+ /**
100
+ * The configured package that holds `path` (relative to the repository root, `''` for the root),
101
+ * the nearest one first, or nothing when none holds it or its `exclude-paths` do.
102
+ */
103
+ function releasePackage(config, path) {
104
+ const packages = config.packages;
105
+ if (packages === null || typeof packages !== 'object' || Array.isArray(packages))
106
+ return undefined;
107
+ let found;
108
+ for (const [key, options] of Object.entries(packages)) {
109
+ if (options === null || typeof options !== 'object' || Array.isArray(options))
110
+ continue;
111
+ const packagePath = key === '.' ? '' : key.replace(/^\.\//u, '').replace(/\/$/u, '');
112
+ if (!holds(packagePath, path))
113
+ continue;
114
+ if (found === undefined || packagePath.length > found.path.length) {
115
+ found = { path: packagePath, options: options };
116
+ }
117
+ }
118
+ if (found === undefined)
119
+ return undefined;
120
+ const excluded = found.options['exclude-paths'] ?? config['exclude-paths'];
121
+ if (Array.isArray(excluded)) {
122
+ for (const exclusion of excluded) {
123
+ if (typeof exclusion !== 'string')
124
+ continue;
125
+ const relative = exclusion.replace(/^\.\//u, '').replace(/\/$/u, '');
126
+ if (holds(found.path === '' ? relative : `${found.path}/${relative}`, path))
127
+ return undefined;
128
+ }
129
+ }
130
+ return found;
131
+ }
132
+ /** Whether the directory `outer` holds `inner`, both relative to the repository root. */
133
+ function holds(outer, inner) {
134
+ return outer === '' || inner === outer || inner.startsWith(`${outer}/`);
135
+ }
136
+ /** The canonical version and distance of the nearest tag with one of these prefixes, or nothing. */
137
+ async function describedBase(input, prefixes) {
138
+ const described = await git(input.projectDir, input.signal, [
139
+ 'describe',
140
+ '--tags',
141
+ '--long',
142
+ '--abbrev=40',
143
+ ...prefixes.flatMap((prefix) => ['--match', `${prefix}[0-9]*`]),
144
+ 'HEAD',
145
+ ]);
146
+ const match = described === undefined ? null : DESCRIBED.exec(described);
147
+ if (match?.groups === undefined)
148
+ return undefined;
149
+ const tag = match.groups.tag;
150
+ const distance = Number(match.groups.distance);
151
+ const version = prefixes
152
+ .filter((prefix) => tag.startsWith(prefix))
153
+ .map((prefix) => tag.slice(prefix.length))
154
+ .find((rest) => isVersion(rest));
155
+ if (version === undefined || !Number.isSafeInteger(distance))
156
+ return undefined;
157
+ return Object.freeze({ version, distance });
158
+ }
159
+ /** The repository's release-please configuration, if its root has one that is a JSON object. */
160
+ async function releasePleaseConfig(root) {
161
+ for (const name of RELEASE_PLEASE_CONFIGS) {
162
+ const config = await readJson(join(root, name));
163
+ if (config !== undefined)
164
+ return config;
165
+ }
166
+ return undefined;
167
+ }
168
+ /** The `name` of the `package.json` in a directory, if it has one. */
169
+ async function packageName(directory) {
170
+ return (await readJson(join(directory, 'package.json')))?.name;
171
+ }
172
+ /** A package name without its npm scope, as release-please makes a component of it. */
173
+ function unscoped(name) {
174
+ if (typeof name !== 'string' || name.length === 0)
175
+ return undefined;
176
+ return name.startsWith('@') ? name.slice(name.indexOf('/') + 1) || undefined : name;
177
+ }
178
+ /** The prefixes, unless one would be read as a glob by `git describe --match`. */
179
+ function literal(prefixes) {
180
+ return prefixes.every((prefix) => LITERAL_PREFIX.test(prefix)) ? prefixes : undefined;
181
+ }
182
+ async function readJson(path) {
183
+ let value;
184
+ try {
185
+ value = JSON.parse(await readFile(path, 'utf8'));
186
+ }
187
+ catch {
188
+ return undefined;
189
+ }
190
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
191
+ ? value
192
+ : undefined;
193
+ }
194
+ /**
195
+ * Run one read-only git command in a directory and return its trimmed stdout, or nothing when git
196
+ * is missing or the command fails. An interrupted deploy still stops here.
197
+ */
198
+ function git(cwd, signal, args) {
199
+ signal.throwIfAborted();
200
+ return new Promise((resolve, reject) => {
201
+ execFile('git', [...args], {
202
+ cwd,
203
+ // Never take the index lock: a deploy only reads the repository.
204
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: '0' },
205
+ signal,
206
+ timeout: GIT_TIMEOUT_MS,
207
+ windowsHide: true,
208
+ }, (error, stdout) => {
209
+ if (signal.aborted) {
210
+ reject(signal.reason);
211
+ return;
212
+ }
213
+ resolve(error === null ? stdout.trim() : undefined);
214
+ });
215
+ });
216
+ }
@@ -8,8 +8,12 @@ import type { LoadedEnvironment } from './project.js';
8
8
  * `deployRelease`, with a key generated for it, and zero Kernel calls.
9
9
  * - `legacy-direct`: today's stable target, replaced in place through `prepare` and `deploy`, which
10
10
  * serves a v3 Publication only. Its `reasons` name what selected it.
11
- * - `refused`: a shape whose only consumer pins an older SDK, refused before any effect. Each
12
- * refusal carries the configuration or command to write instead.
11
+ * - `refused`: a shape no deploy of this SDK serves, refused before any effect. Each refusal
12
+ * carries the configuration or command to write instead.
13
+ *
14
+ * A deploy never installs, so two shapes are refused before a mode is selected at all: an
15
+ * Environment that declares `installation` by `defineProject`, and `--deploy-only` by the
16
+ * command-line parser (AM-72).
13
17
  */
14
18
  export type DeployMode = {
15
19
  readonly kind: 'canonical';
@@ -31,28 +35,32 @@ export type LegacyDirectReason =
31
35
  | {
32
36
  readonly reason: 'no-namespace';
33
37
  }
34
- /** An adapter that declares no `deployRelease` (the contract before adapter interface v2). */
38
+ /**
39
+ * An adapter that declares no `deployRelease` (the contract before adapter interface v2),
40
+ * adapter-astrale's instance mode among them until adapter-astrale implements it.
41
+ */
35
42
  | {
36
43
  readonly reason: 'no-deploy-release';
37
44
  };
38
45
  export type CloudflareDirectParameter = 'route' | 'workerName' | 'identityIssuer' | 'addressing' | 'signingIdentity';
39
46
  export interface DeployRefusal {
40
- readonly reason: 'installation' | 'deploy-only' | 'astrale-instance' | 'astrale-signing-identity' | 'mixed-parameters' | 'scheduled-triggers' | 'development-canonical';
47
+ readonly reason: 'astrale-instance' | 'astrale-signing-identity' | 'mixed-parameters' | 'scheduled-triggers' | 'development-canonical';
41
48
  /** The configuration or command to write instead, for the author. */
42
49
  readonly guidance: string;
43
50
  }
44
51
  /** The commands that select a deploy mode. `publish` refuses every Environment that is not canonical. */
45
52
  export type DeployModeCommand = 'deploy' | 'dev';
46
53
  /**
47
- * Select the deploy mode of one Environment (CT33). It reads the configuration and the command line
54
+ * Select the deploy mode of one Environment (CT33). It reads the configuration and the command
48
55
  * only, and has no effect.
49
56
  *
50
- * A refusal wins over every other reason: an `installation` field, `--deploy-only`, and
51
- * adapter-astrale `instance` or `signingIdentity` reach only the SDK, whose one consumer of them
52
- * pins an older SDK, so they are refused before any effect (AM-72). Otherwise the Environment is on
53
- * the legacy direct-mode path when adapter-cloudflare names a direct-mode parameter or no
54
- * `namespace`, or when its adapter declares no `deployRelease`. Everything else is canonical, except
55
- * under `dev`, which serves the legacy direct-mode path only.
57
+ * A refusal wins over every other reason. Once adapter-astrale deploys immutable deployments
58
+ * (`deployRelease`), its `instance` and `signingIdentity` are refused before any effect: their one
59
+ * consumer pins an older SDK (AM-72). Until then they select its instance mode, on the legacy path
60
+ * with every adapter that declares no `deployRelease`. Otherwise the Environment is on the legacy
61
+ * direct-mode path when adapter-cloudflare names a direct-mode parameter or no `namespace`, or
62
+ * when its adapter declares no `deployRelease`. Everything else is canonical, except under `dev`,
63
+ * which serves the legacy direct-mode path only.
56
64
  *
57
65
  * Within canonical adapter-cloudflare (with `namespace`), a direct-mode parameter is refused
58
66
  * rather than silently selecting either mode, and so is `wrangler.triggers`: an immutable
@@ -63,5 +71,4 @@ export declare function selectDeployMode(input: {
63
71
  readonly command: DeployModeCommand;
64
72
  readonly environment: string;
65
73
  readonly definition: LoadedEnvironment;
66
- readonly deployOnly: boolean;
67
74
  }): DeployMode;
@@ -1,3 +1,4 @@
1
+ import { installCommand } from '../../project/install-command.js';
1
2
  /** The adapter names of the first-party adapters whose parameters CT33 classifies. */
2
3
  const CLOUDFLARE = 'cloudflare';
3
4
  const ASTRALE = 'astrale';
@@ -9,15 +10,16 @@ const CLOUDFLARE_DIRECT_PARAMETERS = [
9
10
  'signingIdentity',
10
11
  ];
11
12
  /**
12
- * Select the deploy mode of one Environment (CT33). It reads the configuration and the command line
13
+ * Select the deploy mode of one Environment (CT33). It reads the configuration and the command
13
14
  * only, and has no effect.
14
15
  *
15
- * A refusal wins over every other reason: an `installation` field, `--deploy-only`, and
16
- * adapter-astrale `instance` or `signingIdentity` reach only the SDK, whose one consumer of them
17
- * pins an older SDK, so they are refused before any effect (AM-72). Otherwise the Environment is on
18
- * the legacy direct-mode path when adapter-cloudflare names a direct-mode parameter or no
19
- * `namespace`, or when its adapter declares no `deployRelease`. Everything else is canonical, except
20
- * under `dev`, which serves the legacy direct-mode path only.
16
+ * A refusal wins over every other reason. Once adapter-astrale deploys immutable deployments
17
+ * (`deployRelease`), its `instance` and `signingIdentity` are refused before any effect: their one
18
+ * consumer pins an older SDK (AM-72). Until then they select its instance mode, on the legacy path
19
+ * with every adapter that declares no `deployRelease`. Otherwise the Environment is on the legacy
20
+ * direct-mode path when adapter-cloudflare names a direct-mode parameter or no `namespace`, or
21
+ * when its adapter declares no `deployRelease`. Everything else is canonical, except under `dev`,
22
+ * which serves the legacy direct-mode path only.
21
23
  *
22
24
  * Within canonical adapter-cloudflare (with `namespace`), a direct-mode parameter is refused
23
25
  * rather than silently selecting either mode, and so is `wrangler.triggers`: an immutable
@@ -30,34 +32,15 @@ export function selectDeployMode(input) {
30
32
  const parameters = definition.deployment.parameters;
31
33
  const refusals = [];
32
34
  const reasons = [];
33
- const installation = definition.installation;
34
- if (installation !== undefined) {
35
- refusals.push({
36
- reason: 'installation',
37
- guidance: `Environment ${environment} declares installation, and a deploy no longer installs. ` +
38
- 'Remove `installation` from the Environment, run ' +
39
- `\`astrale-domain deploy ${environment}\`, then install the URL it prints with ` +
40
- `\`astrale domain install <url> -i ${installation.instance}` +
41
- `${installation.identity === undefined ? '' : ` --as ${installation.identity}`}\`.`,
42
- });
43
- }
44
- if (input.deployOnly) {
45
- refusals.push({
46
- reason: 'deploy-only',
47
- guidance: '`--deploy-only` is no longer accepted: a deploy never installs. Run ' +
48
- `\`astrale-domain ${command} ${environment}\` without it, then ` +
49
- '`astrale domain install <url> -i <instance>`.',
50
- });
51
- }
52
- if (adapter.name === ASTRALE) {
35
+ const releases = 'deployRelease' in adapter && typeof adapter.deployRelease === 'function';
36
+ if (adapter.name === ASTRALE && releases) {
53
37
  if (declares(parameters, 'instance')) {
54
38
  refusals.push({
55
39
  reason: 'astrale-instance',
56
40
  guidance: `astrale({ instance: '${String(parameters.instance)}' }) deployed through that ` +
57
41
  "instance's Services, which no longer hosts deployments. Remove `instance`: " +
58
42
  `\`astrale-domain deploy ${environment}\` makes an immutable deployment on Admin's ` +
59
- 'Services, and `astrale domain install <url> -i ' +
60
- `${String(parameters.instance)}\` installs it.`,
43
+ `Services, and \`${installCommand(String(parameters.instance))}\` installs it.`,
61
44
  });
62
45
  }
63
46
  if (declares(parameters, 'signingIdentity')) {
@@ -93,9 +76,8 @@ export function selectDeployMode(input) {
93
76
  reasons.push(...direct.map((parameter) => ({ reason: 'direct-parameter', parameter })), { reason: 'no-namespace' });
94
77
  }
95
78
  }
96
- if (!('deployRelease' in adapter) || typeof adapter.deployRelease !== 'function') {
79
+ if (!releases)
97
80
  reasons.push({ reason: 'no-deploy-release' });
98
- }
99
81
  if (refusals.length > 0)
100
82
  return Object.freeze({ kind: 'refused', refusals: freeze(refusals) });
101
83
  if (reasons.length > 0)
@@ -108,7 +90,7 @@ export function selectDeployMode(input) {
108
90
  reason: 'development-canonical',
109
91
  guidance: `\`astrale-domain dev\` serves only legacy direct-mode Environments, and ${environment} ` +
110
92
  `deploys immutable deployments. Run \`astrale-domain deploy ${environment}\`, then ` +
111
- '`astrale domain install <url> -i <instance>`.',
93
+ `\`${installCommand()}\`.`,
112
94
  },
113
95
  ]),
114
96
  });
@@ -6,9 +6,7 @@ export type { DeploymentSecretsState } from '../../deployment/index.js';
6
6
  /**
7
7
  * `astrale-domain deploy <environment> --json` on stdout: the one deployment the deploy made or
8
8
  * reused, which CI installs by its URL. Progress and warnings go to stderr. It names no instance
9
- * and no version: a deploy numbers nothing, and the deploy of an immutable deployment installs
10
- * nothing. A legacy environment that declares `installation` still installs while it deploys and
11
- * reports that outcome on stderr only.
9
+ * and no version: a deploy numbers nothing and installs nothing.
12
10
  */
13
11
  export interface DeployResultV1 {
14
12
  readonly format: 'astrale.deploy-result';
@@ -62,14 +60,19 @@ export interface DeployResultV1 {
62
60
  readonly secrets?: {
63
61
  readonly state: DeploymentSecretsState;
64
62
  };
65
- /** The commit the deployment was built from, as its deployment record declares it. */
63
+ /**
64
+ * The commit the deployment was built from, as its deployment record declares it: the commit of
65
+ * the deploy that created it. A reused deployment keeps its record, so this can be an earlier
66
+ * commit than the one the deploy ran from; the label covers the release, never the commit.
67
+ */
66
68
  readonly commit: DeploymentCommitV1 | null;
67
69
  }
68
70
  /**
69
71
  * The deploy result of one immutable deployment, from the ready result of the adapter's
70
- * `deployRelease`. Its release, build, Schema revision and URL are those of the release the SDK
71
- * sealed before deploying; the deployment record the adapter returned must declare exactly that
72
- * release at that URL, so the result never names a release the deployment does not serve.
72
+ * `deployRelease`. Its release, build, Schema revision and URL are those the SDK sealed before
73
+ * deploying; the deployment record the adapter returned must declare exactly that release at that
74
+ * URL, so the result never names a release the deployment does not serve. Its commit is the
75
+ * record's, which a reused deployment kept from the deploy that created it.
73
76
  */
74
77
  export declare function deployResult(input: {
75
78
  readonly environment: string;
@@ -6,9 +6,10 @@ const SECRETS_BY_STATE = Object.freeze({
6
6
  });
7
7
  /**
8
8
  * The deploy result of one immutable deployment, from the ready result of the adapter's
9
- * `deployRelease`. Its release, build, Schema revision and URL are those of the release the SDK
10
- * sealed before deploying; the deployment record the adapter returned must declare exactly that
11
- * release at that URL, so the result never names a release the deployment does not serve.
9
+ * `deployRelease`. Its release, build, Schema revision and URL are those the SDK sealed before
10
+ * deploying; the deployment record the adapter returned must declare exactly that release at that
11
+ * URL, so the result never names a release the deployment does not serve. Its commit is the
12
+ * record's, which a reused deployment kept from the deploy that created it.
12
13
  */
13
14
  export function deployResult(input) {
14
15
  const { release, deployment } = input;
@@ -1,9 +1,8 @@
1
- import type { InstallationTarget, PreparedArtifact } from '../../../deployment/adapter/index.js';
1
+ import type { PreparedArtifact } from '../../../deployment/adapter/index.js';
2
2
  import type { StableTargetAdapter } from '../legacy/stable-target-adapter.js';
3
3
  export interface DevelopmentCandidate {
4
4
  readonly adapter: StableTargetAdapter<object>;
5
5
  readonly parameters: object;
6
- readonly installation?: InstallationTarget;
7
6
  readonly artifact: PreparedArtifact;
8
7
  readonly secrets: Readonly<Record<string, string>>;
9
8
  readonly sources: ReadonlySet<string>;
@@ -22,5 +21,5 @@ export declare class DevelopmentCandidateBuilder {
22
21
  }
23
22
  export declare function discoverRuntimePath(configPath: string): Promise<string>;
24
23
  export declare function sameEnvironment(current: DevelopmentCandidate, candidate: DevelopmentCandidate): boolean;
25
- /** Identify a rebuilt candidate that cannot change provider or installation state. */
24
+ /** Identify a rebuilt candidate that cannot change provider state. */
26
25
  export declare function sameDeploymentCandidate(current: DevelopmentCandidate, candidate: DevelopmentCandidate): boolean;
@@ -84,9 +84,6 @@ export class DevelopmentCandidateBuilder {
84
84
  const source = await adapter.prepare(parameters, {
85
85
  projectDir: this.#projectDir,
86
86
  environment: this.#environment,
87
- ...(environment.installation === undefined
88
- ? {}
89
- : { installation: environment.installation }),
90
87
  mode: 'release',
91
88
  recordSource: sources.record,
92
89
  signal,
@@ -102,9 +99,6 @@ export class DevelopmentCandidateBuilder {
102
99
  return Object.freeze({
103
100
  adapter,
104
101
  parameters,
105
- ...(environment.installation === undefined
106
- ? {}
107
- : { installation: environment.installation }),
108
102
  artifact,
109
103
  secrets: declared.values,
110
104
  sources: sources.snapshot(),
@@ -125,10 +119,9 @@ export function sameEnvironment(current, candidate) {
125
119
  candidate.adapter.version === current.adapter.version &&
126
120
  candidate.artifact.build.schema.compiled.root.origin ===
127
121
  current.artifact.build.schema.compiled.root.origin &&
128
- stable(candidate.parameters) === stable(current.parameters) &&
129
- stable(candidate.installation) === stable(current.installation));
122
+ stable(candidate.parameters) === stable(current.parameters));
130
123
  }
131
- /** Identify a rebuilt candidate that cannot change provider or installation state. */
124
+ /** Identify a rebuilt candidate that cannot change provider state. */
132
125
  export function sameDeploymentCandidate(current, candidate) {
133
126
  return (sameEnvironment(current, candidate) &&
134
127
  candidate.artifact.build.schema.compiled.root.revision ===
@@ -1,16 +1,10 @@
1
1
  import type { ReadyDeployResult } from '../../../deployment/adapter/index.js';
2
- import type { InstallationEvidence } from '../installation/index.js';
3
2
  import type { DevelopmentCandidate } from './candidate.js';
4
3
  export interface DevelopmentDeploymentInput {
5
4
  readonly projectDir: string;
6
5
  readonly environment: string;
7
- readonly deployOnly: boolean;
8
6
  readonly identity?: string;
9
7
  readonly credential?: string;
10
- readonly installationExecutable?: string;
11
8
  }
12
- /** Commit and verify one complete remote candidate, then reconcile its optional installation. */
13
- export declare function deployDevelopmentCandidate(candidate: DevelopmentCandidate, input: DevelopmentDeploymentInput, signal: AbortSignal): Promise<{
14
- readonly deployment: ReadyDeployResult;
15
- readonly installation?: InstallationEvidence;
16
- }>;
9
+ /** Commit and verify one complete remote candidate. A development deployment installs nothing. */
10
+ export declare function deployDevelopmentCandidate(candidate: DevelopmentCandidate, input: DevelopmentDeploymentInput, signal: AbortSignal): Promise<ReadyDeployResult>;
@@ -1,11 +1,9 @@
1
1
  import { verifyDeployment } from '../../../deployment/index.js';
2
- import { reconcileEnvironmentRelease } from '../environment-release.js';
3
- /** Commit and verify one complete remote candidate, then reconcile its optional installation. */
2
+ /** Commit and verify one complete remote candidate. A development deployment installs nothing. */
4
3
  export async function deployDevelopmentCandidate(candidate, input, signal) {
5
4
  const deployed = await candidate.adapter.deploy(candidate.parameters, {
6
5
  projectDir: input.projectDir,
7
6
  environment: input.environment,
8
- ...(candidate.installation === undefined ? {} : { installation: candidate.installation }),
9
7
  ...(input.identity === undefined ? {} : { identity: input.identity }),
10
8
  ...(input.credential === undefined ? {} : { credential: input.credential }),
11
9
  secrets: candidate.secrets,
@@ -18,22 +16,5 @@ export async function deployDevelopmentCandidate(candidate, input, signal) {
18
16
  cause: verified.error,
19
17
  });
20
18
  }
21
- const installation = await reconcileEnvironmentRelease({
22
- projectDir: input.projectDir,
23
- environment: input.environment,
24
- adapter: candidate.adapter,
25
- deployment: verified,
26
- ...(candidate.installation === undefined ? {} : { installation: candidate.installation }),
27
- deployOnly: input.deployOnly,
28
- ...(input.installationExecutable === undefined
29
- ? {}
30
- : { installationExecutable: input.installationExecutable }),
31
- ...(input.identity === undefined ? {} : { identity: input.identity }),
32
- ...(input.credential === undefined ? {} : { credential: input.credential }),
33
- signal,
34
- });
35
- return Object.freeze({
36
- deployment: verified,
37
- ...(installation === undefined ? {} : { installation }),
38
- });
19
+ return verified;
39
20
  }
@@ -1,11 +1,12 @@
1
- /** Watch one Project Environment and serialize complete remote deployment generations. */
1
+ /**
2
+ * Watch one Project Environment and serialize complete remote deployment generations. Each
3
+ * generation replaces the Environment's stable target in place; none installs anything.
4
+ */
2
5
  export declare function develop(input: {
3
6
  readonly configPath: string;
4
7
  readonly projectDir: string;
5
8
  readonly environment: string;
6
- readonly deployOnly: boolean;
7
9
  readonly identity?: string;
8
10
  readonly credentialFile?: string;
9
- readonly sessionFile?: string;
10
11
  readonly secretsFile?: string;
11
12
  }): Promise<number>;