@astrale-os/adapter-astrale 0.5.0-beta.13 → 0.5.0-beta.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -1
- package/dist/adapter.d.ts +6 -14
- package/dist/adapter.js +38 -260
- package/dist/cli/commands/call.d.ts +9 -0
- package/dist/cli/commands/call.js +27 -0
- package/dist/cli/commands/install-domain.d.ts +10 -0
- package/dist/cli/commands/install-domain.js +29 -0
- package/dist/cli/commands/introspect.d.ts +8 -0
- package/dist/cli/commands/introspect.js +27 -0
- package/dist/cli/commands/version.d.ts +6 -0
- package/dist/cli/commands/version.js +50 -0
- package/dist/cli/diagnostic.d.ts +6 -0
- package/dist/cli/diagnostic.js +28 -0
- package/dist/cli/error.d.ts +7 -0
- package/dist/cli/error.js +29 -0
- package/dist/cli/executable.d.ts +2 -0
- package/dist/cli/executable.js +47 -0
- package/dist/cli/identity.d.ts +5 -0
- package/dist/cli/identity.js +11 -0
- package/dist/cli/json.d.ts +3 -0
- package/dist/cli/json.js +50 -0
- package/dist/cli/process.d.ts +18 -0
- package/dist/cli/process.js +108 -0
- package/dist/deploy/deploy.d.ts +22 -0
- package/dist/deploy/deploy.js +183 -0
- package/dist/{prerequisite.d.ts → deploy/ensure-services.d.ts} +6 -2
- package/dist/{prerequisite.js → deploy/ensure-services.js} +23 -28
- package/dist/deploy/progress.d.ts +7 -0
- package/dist/deploy/progress.js +18 -0
- package/dist/{service → deploy}/readiness.d.ts +1 -1
- package/dist/deploy/readiness.js +45 -0
- package/dist/deploy/service/base64.d.ts +8 -0
- package/dist/deploy/service/base64.js +13 -0
- package/dist/deploy/service/display-name.d.ts +1 -0
- package/dist/deploy/service/display-name.js +6 -0
- package/dist/deploy/service/method.d.ts +1 -0
- package/dist/deploy/service/method.js +3 -0
- package/dist/deploy/service/request.d.ts +47 -0
- package/dist/deploy/service/request.js +65 -0
- package/dist/deploy/service/response.d.ts +5 -0
- package/dist/deploy/service/response.js +54 -0
- package/dist/deploy/service/service-key.d.ts +1 -0
- package/dist/deploy/service/service-key.js +14 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.js +13 -5
- package/dist/params.d.ts +16 -31
- package/dist/prepare/parameters.d.ts +7 -0
- package/dist/prepare/parameters.js +12 -0
- package/dist/prepare/prepare.d.ts +3 -0
- package/dist/prepare/prepare.js +5 -0
- package/package.json +14 -6
- package/template/astrale.config.ts +23 -37
- package/dist/cli.d.ts +0 -15
- package/dist/cli.js +0 -48
- package/dist/service/addressing.d.ts +0 -3
- package/dist/service/addressing.js +0 -16
- package/dist/service/artifact.d.ts +0 -34
- package/dist/service/artifact.js +0 -122
- package/dist/service/readiness.js +0 -16
package/README.md
CHANGED
|
@@ -8,4 +8,59 @@ During the SDK beta program, install the beta package set explicitly:
|
|
|
8
8
|
npm install @astrale-os/sdk@beta @astrale-os/adapter-astrale@beta
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
One adapter value configures one Project Environment:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export default defineProject({
|
|
15
|
+
application,
|
|
16
|
+
environments: {
|
|
17
|
+
development: {
|
|
18
|
+
deployment: astrale({ secrets: '.env.dev' }),
|
|
19
|
+
installation: { instance: 'customer-development' },
|
|
20
|
+
},
|
|
21
|
+
preview: {
|
|
22
|
+
deployment: astrale({ instance: 'customer-development' }),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For an installed Environment, the adapter deploys through the Services Domain on
|
|
29
|
+
`installation.instance`; the generic Project orchestrator then reconciles the ready Release onto
|
|
30
|
+
that same Kernel. An Environment without installation must provide `astrale({ instance })` and
|
|
31
|
+
performs no Kernel installation calls. Declaring both coordinates is permitted only when they are
|
|
32
|
+
equal.
|
|
33
|
+
|
|
34
|
+
`astrale-domain dev <environment>` and `astrale-domain deploy <environment>` use the same remote
|
|
35
|
+
provider path. The adapter starts no local Worker, Vite server, or tunnel. Stopping a watcher retains
|
|
36
|
+
the remote Service, stable issuer, secrets, and optional installation. `--deploy-only` suppresses
|
|
37
|
+
only the generic installation stage for that invocation.
|
|
38
|
+
|
|
39
|
+
The adapter sends provider-neutral deployment material: Services receives no Schema revision,
|
|
40
|
+
Publication identity, issuer, or callable inventory. It admits exact Services evidence, assembles
|
|
41
|
+
the expected Release from the local Build, and verifies the live Publication before returning.
|
|
42
|
+
Public HTTPS readiness is cancellable and bounded to ten minutes, allowing a new managed
|
|
43
|
+
hostname's certificate to propagate. It returns immediately once verification succeeds;
|
|
44
|
+
certificate verification is never disabled. A timeout retains the provider deployment so
|
|
45
|
+
retrying the same Environment reuses its Service rather than allocating another hostname.
|
|
46
|
+
For a bundled frontend, readiness also verifies the exact browser-file generation on its View
|
|
47
|
+
routes. A frontend-only change does not need a Schema revision or Kernel reinstall. HTML responses
|
|
48
|
+
are not cached, so a refreshed View cannot reuse a browser-cached document naming retired assets.
|
|
49
|
+
Provider deletion and consumer installation remain independent lifecycle operations.
|
|
50
|
+
|
|
51
|
+
Services is also the cross-machine serialization boundary for one managed Service. Its graph
|
|
52
|
+
operation receipt admits one deployment at a time, and provider promotion is conditional on the
|
|
53
|
+
observed Cloudflare digest. The SDK separately locks the Project Environment inside one checkout
|
|
54
|
+
and the resolved `Kernel issuer + Domain origin` across local checkouts, including bookmark aliases;
|
|
55
|
+
neither local lease is
|
|
56
|
+
the distributed safety mechanism.
|
|
57
|
+
|
|
58
|
+
For an installed Environment with a frontend, development opens the default installed View and
|
|
59
|
+
reports its page URL. Each successful update refreshes that same local View session in place;
|
|
60
|
+
stopping closes the View session while retaining the Service and Domain installation. The existing
|
|
61
|
+
private `--credential-file` flag authenticates provider, installation, and View calls without
|
|
62
|
+
placing the credential in Project configuration or evidence.
|
|
63
|
+
|
|
64
|
+
Automation may add `--session-file /private/path/development-session.json`. The SDK exclusively
|
|
65
|
+
creates that non-existent path inside a caller-owned private directory and retains one atomic,
|
|
66
|
+
versioned `DevelopmentSessionFileV1` snapshot. It never contains secrets.
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { Deployment } from '@astrale-os/sdk/deployment';
|
|
2
|
+
import type { ArtifactSource, PrepareContext } from '@astrale-os/sdk/deployment/adapter';
|
|
3
|
+
import type { AstraleDeployDependencies } from './deploy/deploy.js';
|
|
3
4
|
import type { AstraleParams } from './params.js';
|
|
4
|
-
|
|
5
|
-
import { ensureServices } from './prerequisite.js';
|
|
6
|
-
import { waitForReadiness } from './service/readiness.js';
|
|
7
|
-
export interface AstraleDependencies {
|
|
5
|
+
export interface AstraleDependencies extends AstraleDeployDependencies {
|
|
8
6
|
prepare(parameters: AstraleParams, context: PrepareContext): Promise<ArtifactSource>;
|
|
9
|
-
watch(parameters: AstraleParams, context: WatchContext): Promise<WatchHandle>;
|
|
10
|
-
ensureServices: typeof ensureServices;
|
|
11
|
-
callAstrale: typeof callAstrale;
|
|
12
|
-
runAstrale: typeof runAstrale;
|
|
13
|
-
waitForReadiness: typeof waitForReadiness;
|
|
14
|
-
signingIdentity: typeof signingIdentity;
|
|
15
7
|
}
|
|
16
|
-
export declare function astrale(
|
|
17
|
-
export declare function astraleWithDependencies(
|
|
8
|
+
export declare function astrale(parameters?: AstraleParams): Deployment<AstraleParams>;
|
|
9
|
+
export declare function astraleWithDependencies(parameters: AstraleParams, dependencies: AstraleDependencies): Deployment<AstraleParams>;
|
package/dist/adapter.js
CHANGED
|
@@ -1,276 +1,54 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { signingIdentity
|
|
3
|
-
import { defineAdapter } from '@astrale-os/sdk/deployment/adapter';
|
|
4
|
-
import {
|
|
1
|
+
import { verifyWorkerFrontend } from '@astrale-os/adapter-cloudflare/build';
|
|
2
|
+
import { signingIdentity } from '@astrale-os/adapter-cloudflare/identity';
|
|
3
|
+
import { defineAdapter, defineDeployment } from '@astrale-os/sdk/deployment/adapter';
|
|
4
|
+
import { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
|
-
import { callAstrale
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
import { callAstrale } from './cli/commands/call.js';
|
|
7
|
+
import { installDomain } from './cli/commands/install-domain.js';
|
|
8
|
+
import { introspectDomain } from './cli/commands/introspect.js';
|
|
9
|
+
import { requireCliVersion } from './cli/commands/version.js';
|
|
10
|
+
import { resolveCliExecutable } from './cli/executable.js';
|
|
11
|
+
import { deployAstrale } from './deploy/deploy.js';
|
|
12
|
+
import { ensureServices } from './deploy/ensure-services.js';
|
|
13
|
+
import { waitForReadiness } from './deploy/readiness.js';
|
|
14
|
+
import { prepareAstrale } from './prepare/prepare.js';
|
|
13
15
|
const packageVersion = createRequire(import.meta.url)('../package.json')
|
|
14
16
|
.version;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
prepare: (parameters, context) => local.prepare(cloudflareParameters(parameters, false), context),
|
|
19
|
-
watch: (parameters, context) => local.watch(cloudflareParameters(parameters, true), context),
|
|
17
|
+
export function astrale(parameters = {}) {
|
|
18
|
+
return astraleWithDependencies(parameters, {
|
|
19
|
+
prepare: prepareAstrale,
|
|
20
20
|
ensureServices,
|
|
21
21
|
callAstrale,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
introspectDomain,
|
|
23
|
+
installDomain,
|
|
24
|
+
requireCliVersion,
|
|
25
|
+
resolveCliExecutable,
|
|
26
|
+
waitForReadiness: (release, signal, worker) => waitForReadiness(release, signal, async (release, options) => {
|
|
27
|
+
await verifyReadiness(release, options);
|
|
28
|
+
await verifyWorkerFrontend(release, worker, options);
|
|
29
|
+
}),
|
|
24
30
|
signingIdentity,
|
|
25
31
|
});
|
|
26
32
|
}
|
|
27
|
-
export function astraleWithDependencies(
|
|
28
|
-
|
|
33
|
+
export function astraleWithDependencies(parameters, dependencies) {
|
|
34
|
+
const adapter = defineAdapter({
|
|
29
35
|
name: 'astrale',
|
|
30
36
|
version: packageVersion,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return failed('prerequisite', progress(), new TypeError('Astrale deployment requires an instance.'));
|
|
37
|
-
}
|
|
38
|
-
let secrets;
|
|
39
|
-
let signing;
|
|
40
|
-
try {
|
|
41
|
-
signing = await dependencies.signingIdentity({
|
|
42
|
-
projectDir: context.projectDir,
|
|
43
|
-
source: parameters.signingIdentity,
|
|
44
|
-
origin: context.artifact.build.schema.compiled.root.origin,
|
|
45
|
-
mode: 'deploy',
|
|
46
|
-
});
|
|
47
|
-
secrets = signingIdentitySecrets(context.secrets, signing);
|
|
48
|
-
}
|
|
49
|
-
catch (cause) {
|
|
50
|
-
return failed('prerequisite', progress(), cause);
|
|
51
|
-
}
|
|
52
|
-
const instance = parameters.instance;
|
|
53
|
-
const servicesOrigin = parameters.servicesOrigin ?? SERVICES_ORIGIN;
|
|
54
|
-
const identity = identityArguments(parameters.identity);
|
|
55
|
-
let prerequisite;
|
|
56
|
-
try {
|
|
57
|
-
const evidence = await dependencies.ensureServices({
|
|
58
|
-
projectDir: context.projectDir,
|
|
59
|
-
instance,
|
|
60
|
-
servicesOrigin,
|
|
61
|
-
identity,
|
|
62
|
-
run: dependencies.runAstrale,
|
|
63
|
-
});
|
|
64
|
-
prerequisite =
|
|
65
|
-
evidence.state === 'installed'
|
|
66
|
-
? Object.freeze({ kind: 'applied', evidence })
|
|
67
|
-
: NONE;
|
|
68
|
-
}
|
|
69
|
-
catch (cause) {
|
|
70
|
-
const prerequisite = cause instanceof ServicesPrerequisiteError && cause.mutation === 'installed'
|
|
71
|
-
? Object.freeze({
|
|
72
|
-
kind: 'applied',
|
|
73
|
-
evidence: Object.freeze({ origin: servicesOrigin, state: 'installed' }),
|
|
74
|
-
})
|
|
75
|
-
: cause instanceof ServicesPrerequisiteError && cause.mutation === 'unknown'
|
|
76
|
-
? Object.freeze({ kind: 'unknown' })
|
|
77
|
-
: NONE;
|
|
78
|
-
return failed('prerequisite', progress({ prerequisite }), cause);
|
|
79
|
-
}
|
|
80
|
-
const origin = context.artifact.build.schema.compiled.root.origin;
|
|
81
|
-
const name = parameters.name ?? serviceName(origin);
|
|
82
|
-
let payload;
|
|
83
|
-
try {
|
|
84
|
-
payload = deploymentPayload(context.artifact, { origin, name, vars: parameters.vars });
|
|
85
|
-
}
|
|
86
|
-
catch (cause) {
|
|
87
|
-
return failed('provider', progress({ prerequisite }), cause);
|
|
88
|
-
}
|
|
89
|
-
let deployed;
|
|
90
|
-
try {
|
|
91
|
-
deployed = await dependencies.callAstrale({
|
|
92
|
-
projectDir: context.projectDir,
|
|
93
|
-
instance,
|
|
94
|
-
path: servicesDeployMethod(servicesOrigin),
|
|
95
|
-
identity,
|
|
96
|
-
data: payload,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
catch (cause) {
|
|
100
|
-
return failed('provider', progress({ prerequisite, provider: { kind: 'unknown' } }), cause);
|
|
101
|
-
}
|
|
102
|
-
const expectedPath = servicePath(origin);
|
|
103
|
-
let observation;
|
|
104
|
-
try {
|
|
105
|
-
observation = deployedService(deployed, expectedPath);
|
|
106
|
-
}
|
|
107
|
-
catch (cause) {
|
|
108
|
-
return failed('provider', progress({
|
|
109
|
-
prerequisite,
|
|
110
|
-
provider: { kind: 'partial' },
|
|
111
|
-
}), cause);
|
|
112
|
-
}
|
|
113
|
-
const url = observation.url;
|
|
114
|
-
const applied = Object.freeze({
|
|
115
|
-
kind: 'applied',
|
|
116
|
-
evidence: Object.freeze({
|
|
117
|
-
provider: 'astrale',
|
|
118
|
-
deployment: observation.digest,
|
|
119
|
-
artifact: context.artifact.digest,
|
|
120
|
-
}),
|
|
121
|
-
});
|
|
122
|
-
if (url === undefined) {
|
|
123
|
-
return failed('addressing', progress({ prerequisite, provider: applied }), new Error('Astrale Services deployment returned no URL.'));
|
|
124
|
-
}
|
|
125
|
-
let selected;
|
|
126
|
-
try {
|
|
127
|
-
selected = addressing(url);
|
|
128
|
-
}
|
|
129
|
-
catch (cause) {
|
|
130
|
-
return failed('addressing', progress({ prerequisite, provider: applied }), cause);
|
|
131
|
-
}
|
|
132
|
-
const provider = Object.freeze({
|
|
133
|
-
kind: 'applied',
|
|
134
|
-
evidence: Object.freeze({ ...applied.evidence, issuer: selected.issuer }),
|
|
135
|
-
});
|
|
136
|
-
let configuration = NONE;
|
|
137
|
-
const secretNames = [signing.binding, ...Object.keys(context.secrets).sort()];
|
|
138
|
-
const configured = [];
|
|
139
|
-
if (secretNames.length > 0) {
|
|
140
|
-
try {
|
|
141
|
-
for (const secret of secretNames) {
|
|
142
|
-
await dependencies.callAstrale({
|
|
143
|
-
projectDir: context.projectDir,
|
|
144
|
-
instance,
|
|
145
|
-
path: `${servicePath(origin)}::setSecret`,
|
|
146
|
-
identity,
|
|
147
|
-
data: { name: secret, value: secrets[secret] },
|
|
148
|
-
});
|
|
149
|
-
configured.push(secret);
|
|
150
|
-
}
|
|
151
|
-
configuration = Object.freeze({
|
|
152
|
-
kind: 'applied',
|
|
153
|
-
evidence: Object.freeze({ names: secretNames }),
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
catch (cause) {
|
|
157
|
-
const configuration = configured.length === 0
|
|
158
|
-
? { kind: 'unknown' }
|
|
159
|
-
: {
|
|
160
|
-
kind: 'partial',
|
|
161
|
-
evidence: Object.freeze({ names: Object.freeze([...configured]) }),
|
|
162
|
-
};
|
|
163
|
-
return failed('configuration', progress({ prerequisite, provider, configuration }), cause);
|
|
164
|
-
}
|
|
37
|
+
admit(parameters, context) {
|
|
38
|
+
const deploymentInstance = parameters.instance;
|
|
39
|
+
const installationInstance = context.installation?.instance;
|
|
40
|
+
if (deploymentInstance === undefined && installationInstance === undefined) {
|
|
41
|
+
throw new TypeError(`Astrale Environment ${context.environment} requires installation.instance or astrale({ instance }).`);
|
|
165
42
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
43
|
+
if (deploymentInstance !== undefined &&
|
|
44
|
+
installationInstance !== undefined &&
|
|
45
|
+
deploymentInstance !== installationInstance) {
|
|
46
|
+
throw new TypeError(`Astrale Environment ${context.environment} deployment and installation instances must match.`);
|
|
169
47
|
}
|
|
170
|
-
catch (cause) {
|
|
171
|
-
return failed('release', progress({ prerequisite, provider, configuration }), cause);
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
await dependencies.waitForReadiness(release);
|
|
175
|
-
}
|
|
176
|
-
catch (cause) {
|
|
177
|
-
return failed('readiness', progress({ prerequisite, provider, configuration }), cause, release);
|
|
178
|
-
}
|
|
179
|
-
let installation;
|
|
180
|
-
try {
|
|
181
|
-
await dependencies.runAstrale({
|
|
182
|
-
projectDir: context.projectDir,
|
|
183
|
-
input: [
|
|
184
|
-
'domain',
|
|
185
|
-
'install',
|
|
186
|
-
url,
|
|
187
|
-
'--direct',
|
|
188
|
-
'-i',
|
|
189
|
-
instance,
|
|
190
|
-
'--allow-identity-override',
|
|
191
|
-
'--json',
|
|
192
|
-
'--timeout',
|
|
193
|
-
'240000',
|
|
194
|
-
...identity,
|
|
195
|
-
],
|
|
196
|
-
});
|
|
197
|
-
installation = Object.freeze({
|
|
198
|
-
kind: 'applied',
|
|
199
|
-
evidence: Object.freeze({ instance, origin }),
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
catch (cause) {
|
|
203
|
-
return failed('installation', progress({ prerequisite, provider, configuration, installation: { kind: 'unknown' } }), cause, release);
|
|
204
|
-
}
|
|
205
|
-
return Object.freeze({
|
|
206
|
-
status: 'ready',
|
|
207
|
-
phase: 'complete',
|
|
208
|
-
progress: Object.freeze({
|
|
209
|
-
...progress({ prerequisite, configuration, installation }),
|
|
210
|
-
provider,
|
|
211
|
-
}),
|
|
212
|
-
release,
|
|
213
|
-
});
|
|
214
48
|
},
|
|
49
|
+
prepare: dependencies.prepare,
|
|
50
|
+
deploy: (parameters, context) => deployAstrale(parameters, context, dependencies),
|
|
215
51
|
secretsFile: (parameters) => parameters.secrets,
|
|
216
52
|
});
|
|
217
|
-
|
|
218
|
-
function cloudflareParameters(parameters, watch) {
|
|
219
|
-
return Object.freeze({
|
|
220
|
-
...(parameters.port === undefined ? {} : { port: parameters.port }),
|
|
221
|
-
...(watch && parameters.host !== undefined ? { host: parameters.host } : {}),
|
|
222
|
-
...(parameters.vars === undefined ? {} : { vars: parameters.vars }),
|
|
223
|
-
...(parameters.signingIdentity === undefined
|
|
224
|
-
? {}
|
|
225
|
-
: { signingIdentity: parameters.signingIdentity }),
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
function deployedService(input, expectedPath) {
|
|
229
|
-
if (input === null || typeof input !== 'object' || Array.isArray(input))
|
|
230
|
-
invalidDeployment();
|
|
231
|
-
const value = input;
|
|
232
|
-
if (value.error !== undefined && value.error !== null)
|
|
233
|
-
invalidDeployment();
|
|
234
|
-
if (value.state !== 'ready') {
|
|
235
|
-
throw new TypeError('Astrale Services deployment did not report ready provider state.');
|
|
236
|
-
}
|
|
237
|
-
if (value.path !== expectedPath) {
|
|
238
|
-
throw new TypeError('Astrale Services deployment returned a different Service path.');
|
|
239
|
-
}
|
|
240
|
-
if (typeof value.digest !== 'string' || value.digest.length === 0) {
|
|
241
|
-
throw new TypeError('Astrale Services deployment returned an invalid provider digest.');
|
|
242
|
-
}
|
|
243
|
-
let url;
|
|
244
|
-
if (typeof value.url === 'string') {
|
|
245
|
-
try {
|
|
246
|
-
url = new URL(value.url).href.replace(/\/$/u, '');
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
// Addressing admission below retains the successful provider effect.
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return Object.freeze({
|
|
253
|
-
...(url === undefined ? {} : { url }),
|
|
254
|
-
digest: value.digest,
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
function invalidDeployment() {
|
|
258
|
-
throw new TypeError('Astrale Services deployment returned invalid provider evidence.');
|
|
259
|
-
}
|
|
260
|
-
function progress(input = {}) {
|
|
261
|
-
return Object.freeze({
|
|
262
|
-
prerequisite: input.prerequisite ?? NONE,
|
|
263
|
-
provider: input.provider ?? NONE,
|
|
264
|
-
configuration: input.configuration ?? NONE,
|
|
265
|
-
installation: input.installation ?? NONE,
|
|
266
|
-
});
|
|
267
|
-
}
|
|
268
|
-
function failed(phase, deploymentProgress, cause, release) {
|
|
269
|
-
return Object.freeze({
|
|
270
|
-
status: 'failed',
|
|
271
|
-
phase,
|
|
272
|
-
progress: deploymentProgress,
|
|
273
|
-
error: cause instanceof Error ? cause : new Error(String(cause)),
|
|
274
|
-
...(release === undefined ? {} : { release }),
|
|
275
|
-
});
|
|
53
|
+
return defineDeployment(adapter, parameters);
|
|
276
54
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function callAstrale(input: {
|
|
2
|
+
readonly executable: string;
|
|
3
|
+
readonly projectDir: string;
|
|
4
|
+
readonly instance: string;
|
|
5
|
+
readonly path: string;
|
|
6
|
+
readonly identity: readonly string[];
|
|
7
|
+
readonly data: unknown;
|
|
8
|
+
readonly signal: AbortSignal;
|
|
9
|
+
}): Promise<unknown>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CliProcessError } from '../error.js';
|
|
2
|
+
import { decodeJson } from '../json.js';
|
|
3
|
+
import { runCliProcess } from '../process.js';
|
|
4
|
+
export async function callAstrale(input) {
|
|
5
|
+
const result = await runCliProcess({
|
|
6
|
+
executable: input.executable,
|
|
7
|
+
projectDir: input.projectDir,
|
|
8
|
+
args: [
|
|
9
|
+
'--ci',
|
|
10
|
+
'--no-prompt',
|
|
11
|
+
'call',
|
|
12
|
+
input.path,
|
|
13
|
+
'-i',
|
|
14
|
+
input.instance,
|
|
15
|
+
'--json',
|
|
16
|
+
'--timeout',
|
|
17
|
+
'240000',
|
|
18
|
+
...input.identity,
|
|
19
|
+
],
|
|
20
|
+
stdin: JSON.stringify(input.data),
|
|
21
|
+
signal: input.signal,
|
|
22
|
+
timeoutMs: 250_000,
|
|
23
|
+
});
|
|
24
|
+
if (!result.ok)
|
|
25
|
+
throw new CliProcessError('command', `call ${input.path}`, result.failure);
|
|
26
|
+
return decodeJson(result.stdout, `astrale call ${input.path} success`);
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function installDomain(input: {
|
|
2
|
+
readonly executable: string;
|
|
3
|
+
readonly projectDir: string;
|
|
4
|
+
readonly instance: string;
|
|
5
|
+
readonly target: string;
|
|
6
|
+
readonly direct: boolean;
|
|
7
|
+
readonly allowIdentityOverride?: boolean;
|
|
8
|
+
readonly identity: readonly string[];
|
|
9
|
+
readonly signal: AbortSignal;
|
|
10
|
+
}): Promise<unknown>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CliProcessError } from '../error.js';
|
|
2
|
+
import { decodeJson } from '../json.js';
|
|
3
|
+
import { runCliProcess } from '../process.js';
|
|
4
|
+
export async function installDomain(input) {
|
|
5
|
+
const result = await runCliProcess({
|
|
6
|
+
executable: input.executable,
|
|
7
|
+
projectDir: input.projectDir,
|
|
8
|
+
args: [
|
|
9
|
+
'--ci',
|
|
10
|
+
'--no-prompt',
|
|
11
|
+
'domain',
|
|
12
|
+
'install',
|
|
13
|
+
input.target,
|
|
14
|
+
...(input.direct ? ['--direct'] : []),
|
|
15
|
+
'-i',
|
|
16
|
+
input.instance,
|
|
17
|
+
...(input.allowIdentityOverride ? ['--allow-identity-override'] : []),
|
|
18
|
+
'--json',
|
|
19
|
+
'--timeout',
|
|
20
|
+
'240000',
|
|
21
|
+
...input.identity,
|
|
22
|
+
],
|
|
23
|
+
signal: input.signal,
|
|
24
|
+
timeoutMs: 250_000,
|
|
25
|
+
});
|
|
26
|
+
if (!result.ok)
|
|
27
|
+
throw new CliProcessError('command', 'domain install', result.failure);
|
|
28
|
+
return decodeJson(result.stdout, 'astrale domain install success');
|
|
29
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CliProcessError } from '../error.js';
|
|
2
|
+
import { decodeJson } from '../json.js';
|
|
3
|
+
import { runCliProcess } from '../process.js';
|
|
4
|
+
export async function introspectDomain(input) {
|
|
5
|
+
const result = await runCliProcess({
|
|
6
|
+
executable: input.executable,
|
|
7
|
+
projectDir: input.projectDir,
|
|
8
|
+
args: [
|
|
9
|
+
'--ci',
|
|
10
|
+
'--no-prompt',
|
|
11
|
+
'introspect',
|
|
12
|
+
input.origin,
|
|
13
|
+
'-i',
|
|
14
|
+
input.instance,
|
|
15
|
+
'--json',
|
|
16
|
+
'--timeout',
|
|
17
|
+
'60000',
|
|
18
|
+
...input.identity,
|
|
19
|
+
],
|
|
20
|
+
signal: input.signal,
|
|
21
|
+
timeoutMs: 70_000,
|
|
22
|
+
});
|
|
23
|
+
if (!result.ok) {
|
|
24
|
+
throw new CliProcessError('command', `introspect ${input.origin}`, result.failure);
|
|
25
|
+
}
|
|
26
|
+
return decodeJson(result.stdout, `astrale introspect ${input.origin} success`);
|
|
27
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { CliProcessError } from '../error.js';
|
|
2
|
+
import { runCliProcess } from '../process.js';
|
|
3
|
+
export const MINIMUM_ASTRALE_CLI_VERSION = '1.0.0-beta.48';
|
|
4
|
+
export async function requireCliVersion(input) {
|
|
5
|
+
const result = await runCliProcess({
|
|
6
|
+
executable: input.executable,
|
|
7
|
+
projectDir: input.projectDir,
|
|
8
|
+
args: ['--version'],
|
|
9
|
+
signal: input.signal,
|
|
10
|
+
timeoutMs: 10_000,
|
|
11
|
+
stdoutLimit: 256,
|
|
12
|
+
stderrLimit: 4_096,
|
|
13
|
+
});
|
|
14
|
+
if (!result.ok)
|
|
15
|
+
throw new CliProcessError('command', 'version', result.failure);
|
|
16
|
+
const version = result.stdout.trim().replace(/^v/u, '');
|
|
17
|
+
if (!isCompatible(version)) {
|
|
18
|
+
throw new TypeError(`Astrale CLI ${version || '<invalid>'} is incompatible; ${MINIMUM_ASTRALE_CLI_VERSION} or newer is required.`);
|
|
19
|
+
}
|
|
20
|
+
return version;
|
|
21
|
+
}
|
|
22
|
+
function isCompatible(input) {
|
|
23
|
+
const value = semver(input);
|
|
24
|
+
const minimum = semver(MINIMUM_ASTRALE_CLI_VERSION);
|
|
25
|
+
if (value === undefined)
|
|
26
|
+
return false;
|
|
27
|
+
for (let index = 0; index < 3; index += 1) {
|
|
28
|
+
if (value.core[index] > minimum.core[index])
|
|
29
|
+
return true;
|
|
30
|
+
if (value.core[index] < minimum.core[index])
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (value.prerelease === undefined)
|
|
34
|
+
return true;
|
|
35
|
+
if (minimum.prerelease === undefined)
|
|
36
|
+
return false;
|
|
37
|
+
if (value.prerelease.name !== minimum.prerelease.name) {
|
|
38
|
+
return value.prerelease.name.localeCompare(minimum.prerelease.name) > 0;
|
|
39
|
+
}
|
|
40
|
+
return value.prerelease.number >= minimum.prerelease.number;
|
|
41
|
+
}
|
|
42
|
+
function semver(input) {
|
|
43
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([a-z]+)\.(\d+))?$/u.exec(input);
|
|
44
|
+
if (match === null)
|
|
45
|
+
return undefined;
|
|
46
|
+
return {
|
|
47
|
+
core: [Number(match[1]), Number(match[2]), Number(match[3])],
|
|
48
|
+
...(match[4] === undefined ? {} : { prerelease: { name: match[4], number: Number(match[5]) } }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type PublicResponseDiagnostic = Readonly<{
|
|
2
|
+
code: number;
|
|
3
|
+
message?: string;
|
|
4
|
+
}>;
|
|
5
|
+
/** Admit only the CLI's bounded, caller-safe public response projection. */
|
|
6
|
+
export declare function publicResponseDiagnostic(input: Readonly<Record<string, unknown>> | undefined): PublicResponseDiagnostic | undefined;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const MAXIMUM_RESPONSE_MESSAGE_LENGTH = 1_024;
|
|
2
|
+
/** Admit only the CLI's bounded, caller-safe public response projection. */
|
|
3
|
+
export function publicResponseDiagnostic(input) {
|
|
4
|
+
if (input?.error !== 'RESPONSE_ERROR' || !publicResponseCode(input.code))
|
|
5
|
+
return undefined;
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
code: input.code,
|
|
8
|
+
...(publicResponseMessage(input.message) ? { message: input.message } : {}),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function publicResponseCode(input) {
|
|
12
|
+
return Number.isSafeInteger(input) && input >= 1_000 && input <= 9_999;
|
|
13
|
+
}
|
|
14
|
+
function publicResponseMessage(input) {
|
|
15
|
+
if (typeof input !== 'string' ||
|
|
16
|
+
input.length === 0 ||
|
|
17
|
+
input.length > MAXIMUM_RESPONSE_MESSAGE_LENGTH ||
|
|
18
|
+
input.trim() !== input ||
|
|
19
|
+
input.normalize('NFC') !== input) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
for (const character of input) {
|
|
23
|
+
const point = character.codePointAt(0);
|
|
24
|
+
if (point <= 0x1f || point === 0x7f)
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type CliProcessFailureKind = 'cancelled' | 'timeout' | 'output-limit' | 'spawn' | 'protocol' | 'command';
|
|
2
|
+
export declare class CliProcessError extends Error {
|
|
3
|
+
readonly kind: CliProcessFailureKind;
|
|
4
|
+
readonly command: string;
|
|
5
|
+
readonly diagnostic?: Readonly<Record<string, unknown>> | undefined;
|
|
6
|
+
constructor(kind: CliProcessFailureKind, command: string, diagnostic?: Readonly<Record<string, unknown>> | undefined, options?: ErrorOptions);
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { publicResponseDiagnostic } from './diagnostic.js';
|
|
2
|
+
export class CliProcessError extends Error {
|
|
3
|
+
kind;
|
|
4
|
+
command;
|
|
5
|
+
diagnostic;
|
|
6
|
+
constructor(kind, command, diagnostic, options) {
|
|
7
|
+
const code = diagnosticCode(diagnostic);
|
|
8
|
+
const message = publicResponseDiagnostic(diagnostic)?.message;
|
|
9
|
+
super(`Astrale CLI ${command} failed: ${kind}${code}.` +
|
|
10
|
+
(message === undefined ? '' : ` ${message}`), options);
|
|
11
|
+
this.kind = kind;
|
|
12
|
+
this.command = command;
|
|
13
|
+
this.diagnostic = diagnostic;
|
|
14
|
+
this.name = 'CliProcessError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function diagnosticCode(diagnostic) {
|
|
18
|
+
const error = safeCode(diagnostic?.error);
|
|
19
|
+
const responseCode = publicResponseDiagnostic(diagnostic)?.code;
|
|
20
|
+
const reason = diagnostic?.reason;
|
|
21
|
+
const reasonCode = reason !== null && typeof reason === 'object' && !Array.isArray(reason)
|
|
22
|
+
? safeCode(reason.code)
|
|
23
|
+
: undefined;
|
|
24
|
+
const value = [error, responseCode, reasonCode].filter((part) => part !== undefined).join('/');
|
|
25
|
+
return value.length === 0 ? '' : ` (${value})`;
|
|
26
|
+
}
|
|
27
|
+
function safeCode(value) {
|
|
28
|
+
return typeof value === 'string' && /^[A-Z][A-Z0-9_]{0,127}$/u.test(value) ? value : undefined;
|
|
29
|
+
}
|