@astrale-os/sdk 0.5.0-beta.98 → 0.6.0-beta.0
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 +165 -2
- package/dist/application/action/action.d.ts +7 -4
- package/dist/application/action/authoring.d.ts +2 -1
- package/dist/application/action/context.d.ts +18 -13
- package/dist/application/action/define.d.ts +5 -1
- package/dist/application/action/define.js +6 -1
- package/dist/application/action/index.d.ts +2 -1
- package/dist/application/dependency/index.d.ts +1 -0
- package/dist/application/dependency/invoker.d.ts +26 -0
- package/dist/application/error/error.d.ts +23 -0
- package/dist/application/error/error.js +87 -0
- package/dist/application/error/index.d.ts +2 -0
- package/dist/application/error/index.js +1 -0
- package/dist/application/index.d.ts +2 -2
- package/dist/application/integration/authentication.d.ts +10 -0
- package/dist/application/integration/authentication.typecheck.js +18 -0
- package/dist/application/integration/client.d.ts +2 -0
- package/dist/application/integration/client.js +19 -2
- package/dist/application/integration/index.d.ts +1 -0
- package/dist/application/integration/operation.d.ts +3 -0
- package/dist/application/mutation/authoring/builder.js +2 -2
- package/dist/application/mutation/authoring/endpoint.d.ts +3 -3
- package/dist/application/mutation/authoring/endpoint.js +9 -0
- package/dist/application/mutation/failure.js +13 -7
- package/dist/application/mutation/index.d.ts +1 -0
- package/dist/application/requirements/{profiles.d.ts → core.d.ts} +5 -3
- package/dist/application/requirements/{profiles.js → core.js} +6 -6
- package/dist/application/requirements/index.d.ts +4 -4
- package/dist/application/requirements/index.js +5 -5
- package/dist/application/requirements/requirement.d.ts +3 -2
- package/dist/application/runtime/authoring.d.ts +1 -0
- package/dist/application/runtime/callables.js +2 -2
- package/dist/application/runtime/declarations.d.ts +43 -0
- package/dist/application/runtime/define.d.ts +2 -1
- package/dist/application/view/index.d.ts +1 -1
- package/dist/application/view/route.d.ts +4 -0
- package/dist/application/view/route.js +5 -1
- package/dist/application/workflow/authoring.d.ts +1 -0
- package/dist/application/workflow/context.d.ts +2 -1
- package/dist/application/workflow/define.d.ts +5 -1
- package/dist/application/workflow/define.js +6 -1
- package/dist/application/workflow/index.d.ts +1 -0
- package/dist/application/workflow/runner/runner.d.ts +3 -2
- package/dist/application/workflow/workflow.d.ts +4 -3
- package/dist/deployment/adapter/adapter.d.ts +12 -7
- package/dist/deployment/adapter/define.d.ts +5 -2
- package/dist/deployment/adapter/define.js +15 -17
- package/dist/deployment/adapter/deploy/context.d.ts +6 -0
- package/dist/deployment/adapter/deploy/result.d.ts +2 -0
- package/dist/deployment/adapter/index.d.ts +2 -3
- package/dist/deployment/adapter/index.js +1 -1
- package/dist/deployment/adapter/prepare/context.d.ts +13 -5
- package/dist/deployment/authoring.d.ts +1 -1
- package/dist/deployment/build/compile.js +21 -19
- package/dist/deployment/build/frontend/compilation.d.ts +2 -1
- package/dist/deployment/build/frontend/compilation.js +1 -0
- package/dist/deployment/build/material.js +5 -5
- package/dist/deployment/deploy.d.ts +10 -4
- package/dist/deployment/deploy.js +5 -5
- package/dist/deployment/deployment.d.ts +8 -5
- package/dist/deployment/deployment.js +10 -1
- package/dist/deployment/index.d.ts +5 -4
- package/dist/deployment/index.js +3 -2
- package/dist/deployment/release/addressing.d.ts +6 -2
- package/dist/deployment/release/addressing.js +9 -4
- package/dist/deployment/release/publication/callables.js +1 -1
- package/dist/deployment/release/publication/index.d.ts +1 -1
- package/dist/deployment/release/publication/index.js +1 -1
- package/dist/deployment/release/publication/routes.js +1 -1
- package/dist/deployment/release/publication/seal.d.ts +0 -1
- package/dist/deployment/release/publication/seal.js +4 -5
- package/dist/deployment/release/publication/views.js +2 -1
- package/dist/deployment/verify/preflight/artifact.js +1 -1
- package/dist/deployment/verify/readiness/deployment.js +7 -3
- package/dist/deployment/verify/readiness/identity.js +1 -1
- package/dist/deployment/verify/readiness/publication.js +2 -2
- package/dist/execution/identity/authentication.js +0 -1
- package/dist/execution/identity/exchange.d.ts +1 -0
- package/dist/execution/identity/exchange.js +1 -1
- package/dist/execution/identity/jwks.js +1 -1
- package/dist/execution/invocation/admission/authentication.d.ts +6 -3
- package/dist/execution/invocation/admission/authentication.js +16 -6
- package/dist/execution/invocation/admission/authority.d.ts +2 -2
- package/dist/execution/invocation/dependency/bind.d.ts +5 -0
- package/dist/execution/invocation/dependency/bind.js +68 -0
- package/dist/execution/invocation/dependency/index.d.ts +1 -0
- package/dist/execution/invocation/dependency/index.js +1 -0
- package/dist/execution/invocation/dispatch/action.js +19 -2
- package/dist/execution/invocation/dispatch/context.d.ts +4 -4
- package/dist/execution/invocation/dispatch/context.js +29 -9
- package/dist/execution/invocation/dispatch/workflow.js +17 -4
- package/dist/execution/invocation/integration/authentication.d.ts +5 -0
- package/dist/execution/invocation/integration/authentication.js +53 -0
- package/dist/execution/invocation/integration/call.d.ts +7 -1
- package/dist/execution/invocation/integration/call.js +8 -3
- package/dist/execution/invocation/integration/clients.d.ts +2 -2
- package/dist/execution/invocation/integration/clients.js +2 -2
- package/dist/execution/invocation/lifecycle/active.js +30 -5
- package/dist/execution/invocation/result/errors.d.ts +1 -0
- package/dist/execution/invocation/result/errors.js +15 -10
- package/dist/execution/service/create.js +3 -1
- package/dist/execution/service/service.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/platform/auth/index.d.ts +1 -1
- package/dist/platform/auth/index.js +0 -1
- package/dist/platform/graph/alias.d.ts +6 -0
- package/dist/platform/graph/alias.js +6 -0
- package/dist/platform/graph/document/index.d.ts +2 -1
- package/dist/platform/graph/document/index.js +2 -1
- package/dist/platform/graph/index.d.ts +2 -1
- package/dist/platform/graph/index.js +2 -1
- package/dist/platform/schema/class-authoring.d.ts +37 -0
- package/dist/platform/schema/class-authoring.js +20 -0
- package/dist/platform/schema/class-icon.d.ts +15 -0
- package/dist/platform/schema/class-icon.js +46 -0
- package/dist/platform/schema/error.d.ts +11 -0
- package/dist/platform/schema/error.js +34 -0
- package/dist/platform/schema/errors/index.d.ts +3 -0
- package/dist/platform/schema/errors/index.js +2 -0
- package/dist/platform/schema/icons/index.d.ts +2 -0
- package/dist/platform/schema/icons/index.js +2 -0
- package/dist/platform/schema/index.d.ts +20 -13
- package/dist/platform/schema/index.js +9 -4
- package/dist/platform/schema/kernel.d.ts +22 -0
- package/dist/platform/schema/kernel.js +5 -0
- package/dist/platform/schema/node-id.d.ts +3 -0
- package/dist/platform/schema/node-id.js +6 -0
- package/dist/platform/schema/property.d.ts +12 -0
- package/dist/platform/schema/property.js +3 -0
- package/dist/platform/schema/schema-authoring.d.ts +48 -0
- package/dist/platform/schema/schema-authoring.js +3 -0
- package/dist/platform/schema/state-property.d.ts +11 -0
- package/dist/platform/schema/state-property.js +3 -0
- package/dist/project/admit.d.ts +4 -0
- package/dist/project/admit.js +22 -0
- package/dist/project/define.d.ts +12 -0
- package/dist/project/define.js +106 -0
- package/dist/project/index.d.ts +4 -0
- package/dist/project/index.js +2 -0
- package/dist/project/project.d.ts +24 -0
- package/dist/project/project.js +1 -0
- package/dist/testing/dataset/admission.d.ts +13 -0
- package/dist/testing/dataset/admission.js +107 -0
- package/dist/testing/dataset/builder.d.ts +42 -0
- package/dist/testing/dataset/builder.js +127 -0
- package/dist/testing/dataset/codec.d.ts +6 -0
- package/dist/testing/dataset/codec.js +113 -0
- package/dist/testing/dataset/dataset.d.ts +58 -0
- package/dist/testing/dataset/dataset.js +2 -0
- package/dist/testing/dataset/define.d.ts +33 -0
- package/dist/testing/dataset/define.js +105 -0
- package/dist/testing/dataset/index.d.ts +8 -0
- package/dist/testing/dataset/index.js +4 -0
- package/dist/testing/dataset/references.d.ts +14 -0
- package/dist/testing/dataset/references.js +56 -0
- package/dist/testing/index.d.ts +9 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/load.d.ts +14 -0
- package/dist/testing/load.js +44 -0
- package/dist/testing/reference.d.ts +11 -0
- package/dist/testing/reference.js +19 -0
- package/dist/testing/tests.d.ts +12 -0
- package/dist/testing/tests.js +31 -0
- package/dist/tooling/cli/arguments.d.ts +7 -4
- package/dist/tooling/cli/arguments.js +82 -107
- package/dist/tooling/cli/astrale/authority.d.ts +5 -0
- package/dist/tooling/cli/astrale/authority.js +11 -0
- package/dist/tooling/cli/astrale/executable.d.ts +2 -0
- package/dist/tooling/cli/astrale/executable.js +47 -0
- package/dist/tooling/cli/astrale/failure.d.ts +6 -0
- package/dist/tooling/cli/astrale/failure.js +48 -0
- package/dist/tooling/cli/astrale/instance.d.ts +11 -0
- package/dist/tooling/cli/astrale/instance.js +52 -0
- package/dist/tooling/cli/astrale/process.d.ts +13 -0
- package/dist/tooling/cli/astrale/process.js +94 -0
- package/dist/tooling/cli/astrale/version.d.ts +18 -0
- package/dist/tooling/cli/astrale/version.js +55 -0
- package/dist/tooling/cli/bun.js +1 -1
- package/dist/tooling/cli/credential.d.ts +2 -0
- package/dist/tooling/cli/credential.js +57 -0
- package/dist/tooling/cli/development/candidate.d.ts +25 -0
- package/dist/tooling/cli/development/candidate.js +176 -0
- package/dist/tooling/cli/development/deployment.d.ts +16 -0
- package/dist/tooling/cli/development/deployment.js +39 -0
- package/dist/tooling/cli/development/develop.d.ts +4 -3
- package/dist/tooling/cli/development/develop.js +125 -289
- package/dist/tooling/cli/development/lease.d.ts +18 -0
- package/dist/tooling/cli/development/lease.js +163 -0
- package/dist/tooling/cli/development/lifecycle.js +4 -0
- package/dist/tooling/cli/development/project-imports.d.ts +22 -0
- package/dist/tooling/cli/development/project-imports.js +39 -0
- package/dist/tooling/cli/development/project-lock.d.ts +3 -4
- package/dist/tooling/cli/development/project-lock.js +14 -166
- package/dist/tooling/cli/development/release-evidence.d.ts +4 -0
- package/dist/tooling/cli/development/release-evidence.js +10 -0
- package/dist/tooling/cli/development/report.d.ts +6 -0
- package/dist/tooling/cli/development/report.js +14 -0
- package/dist/tooling/cli/development/source-watch.js +53 -5
- package/dist/tooling/cli/development/sources.d.ts +7 -0
- package/dist/tooling/cli/development/sources.js +48 -0
- package/dist/tooling/cli/development/target-lock.d.ts +16 -0
- package/dist/tooling/cli/development/target-lock.js +68 -0
- package/dist/tooling/cli/development/view.d.ts +28 -0
- package/dist/tooling/cli/development/view.js +151 -0
- package/dist/tooling/cli/environment-release.d.ts +21 -0
- package/dist/tooling/cli/environment-release.js +46 -0
- package/dist/tooling/cli/evidence.d.ts +11 -0
- package/dist/tooling/cli/evidence.js +53 -0
- package/dist/tooling/cli/help.js +78 -34
- package/dist/tooling/cli/index.d.ts +1 -1
- package/dist/tooling/cli/index.js +1 -1
- package/dist/tooling/cli/installation/index.d.ts +2 -0
- package/dist/tooling/cli/installation/index.js +1 -0
- package/dist/tooling/cli/installation/observation.d.ts +10 -0
- package/dist/tooling/cli/installation/observation.js +44 -0
- package/dist/tooling/cli/installation/reconcile.d.ts +32 -0
- package/dist/tooling/cli/installation/reconcile.js +122 -0
- package/dist/tooling/cli/orchestrate.js +84 -44
- package/dist/tooling/cli/project.d.ts +17 -0
- package/dist/tooling/cli/project.js +18 -0
- package/dist/tooling/cli/test.d.ts +12 -0
- package/dist/tooling/cli/test.js +47 -0
- package/dist/tooling/cli/upgrade/index.d.ts +6 -0
- package/dist/tooling/cli/upgrade/index.js +115 -0
- package/dist/tooling/cli/upgrade/manifest.d.ts +18 -0
- package/dist/tooling/cli/upgrade/manifest.js +77 -0
- package/dist/tooling/cli/upgrade/process.d.ts +2 -0
- package/dist/tooling/cli/upgrade/process.js +57 -0
- package/dist/tooling/linter/adapters/typescript/imports.js +2 -0
- package/dist/tooling/linter/adapters/typescript/index.d.ts +1 -0
- package/dist/tooling/linter/adapters/typescript/index.js +1 -0
- package/dist/tooling/linter/adapters/typescript/query-collection-type.js +4 -51
- package/dist/tooling/linter/adapters/typescript/type-analysis.d.ts +9 -0
- package/dist/tooling/linter/adapters/typescript/type-analysis.js +55 -0
- package/dist/tooling/linter/implementations/source/callable-requirements.d.ts +28 -0
- package/dist/tooling/linter/implementations/source/callable-requirements.js +234 -0
- package/dist/tooling/linter/implementations/source/dependencies.d.ts +2 -0
- package/dist/tooling/linter/implementations/source/dependencies.js +219 -0
- package/dist/tooling/linter/implementations/source/global.js +61 -5
- package/dist/tooling/linter/implementations/source/mutations.js +520 -38
- package/dist/tooling/linter/implementations/source/providers.js +19 -164
- package/dist/tooling/linter/implementations/source/schema.js +454 -2
- package/dist/tooling/linter/implementations/source/verifiers.js +2 -0
- package/dist/tooling/linter/implementations/source/workflows.js +8 -35
- package/dist/tooling/linter/policy/generated.js +2 -2
- package/dist/tooling/linter/requirements/registry.js +9 -3
- package/dist/tooling/packaging/declarations/normalize.js +3 -38
- package/dist/tooling/packaging/declarations/paths.d.ts +5 -0
- package/dist/tooling/packaging/declarations/paths.js +38 -0
- package/dist/tooling/packaging/finalize.d.ts +13 -0
- package/dist/tooling/packaging/finalize.js +62 -0
- package/dist/tooling/packaging/index.d.ts +2 -0
- package/dist/tooling/packaging/index.js +1 -0
- package/dist/tooling/packaging/javascript.d.ts +3 -0
- package/dist/tooling/packaging/javascript.js +45 -2
- package/dist/tooling/packaging/packaging.d.ts +2 -5
- package/dist/tooling/packaging/packaging.js +6 -30
- package/package.json +42 -12
- package/CHANGELOG.md +0 -1499
- package/dist/deployment/adapter/development/context.d.ts +0 -18
- package/dist/deployment/adapter/development/index.d.ts +0 -4
- package/dist/deployment/adapter/development/input.d.ts +0 -6
- package/dist/deployment/adapter/development/placement.d.ts +0 -15
- package/dist/deployment/adapter/development/session.d.ts +0 -9
- /package/dist/{deployment/adapter/development → application/dependency}/index.js +0 -0
- /package/dist/{deployment/adapter/development/context.js → application/dependency/invoker.js} +0 -0
- /package/dist/{deployment/adapter/development/input.js → application/integration/authentication.js} +0 -0
- /package/dist/{deployment/adapter/development/placement.js → application/integration/authentication.typecheck.d.ts} +0 -0
- /package/dist/{deployment/adapter/development/session.js → application/runtime/declarations.js} +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { graph as schemaGraph } from '@astrale-os/kernel-core/schema';
|
|
2
|
+
import { isNodeId } from '../../platform/graph/node/index.js';
|
|
3
|
+
import { Path } from '../../platform/graph/path/index.js';
|
|
4
|
+
import { bundle, schema as schemaApi } from '../../platform/schema/index.js';
|
|
5
|
+
/** Symbolic projection supplies facts; only an explicit Dataset reference supplies identity. */
|
|
6
|
+
export function datasetReferenceNodes(domain) {
|
|
7
|
+
const result = new Map();
|
|
8
|
+
const direct = new Set([
|
|
9
|
+
domain.origin,
|
|
10
|
+
...Object.values(domain.dependencies).map(({ origin }) => origin),
|
|
11
|
+
]);
|
|
12
|
+
const sourceBundle = bundle.create(domain.source);
|
|
13
|
+
for (const source of [sourceBundle.root, ...sourceBundle.closure]) {
|
|
14
|
+
if (!direct.has(source.origin))
|
|
15
|
+
continue;
|
|
16
|
+
const owner = schemaApi.resolve(source);
|
|
17
|
+
const projection = schemaGraph.project(owner);
|
|
18
|
+
const allowed = new Set(owner
|
|
19
|
+
.definitions()
|
|
20
|
+
.filter(({ ref }) => ref.origin === owner.origin &&
|
|
21
|
+
['class', 'function', 'policy', 'view', 'core'].includes(ref.kind))
|
|
22
|
+
.map(({ ref }) => Path.project(ref).raw));
|
|
23
|
+
for (const part of [projection.managed, projection.core]) {
|
|
24
|
+
for (const node of part.graph.nodes) {
|
|
25
|
+
if (!('as' in node))
|
|
26
|
+
continue;
|
|
27
|
+
const path = part.paths.get(node.as);
|
|
28
|
+
if (path !== undefined && allowed.has(path))
|
|
29
|
+
result.set(path, node);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
/** Serialized reference associations lose their proof and must match the exact projection again. */
|
|
36
|
+
export function decodeDatasetReferences(input, domain, graph) {
|
|
37
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
38
|
+
throw new TypeError('Dataset references must be an object.');
|
|
39
|
+
}
|
|
40
|
+
const entries = Object.entries(input);
|
|
41
|
+
const projected = entries.length === 0 ? new Map() : datasetReferenceNodes(domain);
|
|
42
|
+
const identities = new Set();
|
|
43
|
+
for (const [path, id] of entries) {
|
|
44
|
+
const expected = projected.get(path);
|
|
45
|
+
const actual = isNodeId(id) ? graph.nodes.byId(id) : undefined;
|
|
46
|
+
if (expected === undefined ||
|
|
47
|
+
actual === undefined ||
|
|
48
|
+
identities.has(actual.id) ||
|
|
49
|
+
expected.class !== actual.class ||
|
|
50
|
+
JSON.stringify(expected.props) !== JSON.stringify(actual.props)) {
|
|
51
|
+
throw new TypeError(`Dataset reference ${path} does not identify its projected Node.`);
|
|
52
|
+
}
|
|
53
|
+
identities.add(actual.id);
|
|
54
|
+
}
|
|
55
|
+
return Object.freeze(Object.fromEntries(entries));
|
|
56
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Development and test resources: schema-admitted demo Datasets and their lazy project references. */
|
|
2
|
+
export { DATASET_FORMAT, DATASET_VERSION, DatasetAdmissionError, decodeDataset, defineDataset, encodeDataset, isDataset, } from './dataset/index.js';
|
|
3
|
+
export type { AuthoredDatasetVariables, Dataset, DatasetClasses, DatasetEdgeInput, DatasetEndpoint, DatasetGraph, DatasetInput, DatasetJson, DatasetNode, DatasetNodeInput, DatasetReferences, DatasetReferenceInput, DatasetVariable, DatasetVariableJson, DatasetVariables, DatasetVariablesOf, SchemaOfDataset, } from './dataset/index.js';
|
|
4
|
+
export { loadDataset, loadDatasets } from './load.js';
|
|
5
|
+
export type { DatasetModuleLoader, LoadedDataset } from './load.js';
|
|
6
|
+
export { dataset, isDatasetReference } from './reference.js';
|
|
7
|
+
export type { DatasetReference } from './reference.js';
|
|
8
|
+
export { isTests, tests } from './tests.js';
|
|
9
|
+
export type { Tests, TestsInput } from './tests.js';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Development and test resources: schema-admitted demo Datasets and their lazy project references. */
|
|
2
|
+
export { DATASET_FORMAT, DATASET_VERSION, DatasetAdmissionError, decodeDataset, defineDataset, encodeDataset, isDataset, } from './dataset/index.js';
|
|
3
|
+
export { loadDataset, loadDatasets } from './load.js';
|
|
4
|
+
export { dataset, isDatasetReference } from './reference.js';
|
|
5
|
+
export { isTests, tests } from './tests.js';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { schema } from '../platform/schema/index.js';
|
|
2
|
+
import type { Dataset } from './dataset/index.js';
|
|
3
|
+
import type { DatasetReference } from './reference.js';
|
|
4
|
+
import type { Tests } from './tests.js';
|
|
5
|
+
/** Import one referenced module; tooling owns path resolution against `astrale.config.ts`. */
|
|
6
|
+
export type DatasetModuleLoader = (path: string) => unknown | Promise<unknown>;
|
|
7
|
+
export interface LoadedDataset {
|
|
8
|
+
readonly reference: DatasetReference;
|
|
9
|
+
readonly dataset: Dataset;
|
|
10
|
+
}
|
|
11
|
+
/** Load the one default Dataset of a referenced module and prove it targets the exact Schema. */
|
|
12
|
+
export declare function loadDataset(reference: DatasetReference, load: DatasetModuleLoader, source: schema.DomainSchema): Promise<LoadedDataset>;
|
|
13
|
+
/** Load every referenced Dataset of one Tests value in declared order, rejecting duplicate ids. */
|
|
14
|
+
export declare function loadDatasets(value: Tests, load: DatasetModuleLoader, source: schema.DomainSchema): Promise<readonly LoadedDataset[]>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { isDataset } from './dataset/index.js';
|
|
2
|
+
import { isDatasetReference } from './reference.js';
|
|
3
|
+
import { isTests } from './tests.js';
|
|
4
|
+
/** Load the one default Dataset of a referenced module and prove it targets the exact Schema. */
|
|
5
|
+
export async function loadDataset(reference, load, source) {
|
|
6
|
+
if (!isDatasetReference(reference)) {
|
|
7
|
+
throw new TypeError('Dataset reference must be created by dataset(path).');
|
|
8
|
+
}
|
|
9
|
+
if (typeof load !== 'function')
|
|
10
|
+
throw new TypeError('Dataset module loader must be a function.');
|
|
11
|
+
const loaded = await load(reference.path);
|
|
12
|
+
if (loaded === null ||
|
|
13
|
+
typeof loaded !== 'object' ||
|
|
14
|
+
Array.isArray(loaded) ||
|
|
15
|
+
!Object.hasOwn(loaded, 'default') ||
|
|
16
|
+
Object.keys(loaded).some((key) => key !== 'default')) {
|
|
17
|
+
throw new TypeError(`Dataset module ${reference.path} must export exactly one default Dataset.`);
|
|
18
|
+
}
|
|
19
|
+
const dataset = Reflect.get(loaded, 'default');
|
|
20
|
+
if (!isDataset(dataset)) {
|
|
21
|
+
throw new TypeError(`Dataset module ${reference.path} default export is not an admitted Dataset.`);
|
|
22
|
+
}
|
|
23
|
+
if (dataset.schema !== source) {
|
|
24
|
+
throw new TypeError(`Dataset ${dataset.id} (${reference.path}) targets Schema ${dataset.schema.origin}, not the exact Application Schema.`);
|
|
25
|
+
}
|
|
26
|
+
return Object.freeze({ reference, dataset });
|
|
27
|
+
}
|
|
28
|
+
/** Load every referenced Dataset of one Tests value in declared order, rejecting duplicate ids. */
|
|
29
|
+
export async function loadDatasets(value, load, source) {
|
|
30
|
+
if (!isTests(value))
|
|
31
|
+
throw new TypeError('Tests value must be created by tests().');
|
|
32
|
+
const loaded = [];
|
|
33
|
+
const ids = new Map();
|
|
34
|
+
for (const reference of value.datasets) {
|
|
35
|
+
const entry = await loadDataset(reference, load, source);
|
|
36
|
+
const previous = ids.get(entry.dataset.id);
|
|
37
|
+
if (previous !== undefined) {
|
|
38
|
+
throw new TypeError(`Dataset id ${entry.dataset.id} is defined by both ${previous} and ${reference.path}.`);
|
|
39
|
+
}
|
|
40
|
+
ids.set(entry.dataset.id, reference.path);
|
|
41
|
+
loaded.push(entry);
|
|
42
|
+
}
|
|
43
|
+
return Object.freeze(loaded);
|
|
44
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** One stable Dataset module coordinate relative to the project configuration. */
|
|
2
|
+
export interface DatasetReference {
|
|
3
|
+
readonly kind: 'dataset-reference';
|
|
4
|
+
readonly path: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Reference one Dataset module lazily. Importing the project configuration never loads the
|
|
8
|
+
* referenced module; tooling resolves the path against `astrale.config.ts` on demand.
|
|
9
|
+
*/
|
|
10
|
+
export declare function dataset(path: string): DatasetReference;
|
|
11
|
+
export declare function isDatasetReference(input: unknown): input is DatasetReference;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const references = new WeakSet();
|
|
2
|
+
/**
|
|
3
|
+
* Reference one Dataset module lazily. Importing the project configuration never loads the
|
|
4
|
+
* referenced module; tooling resolves the path against `astrale.config.ts` on demand.
|
|
5
|
+
*/
|
|
6
|
+
export function dataset(path) {
|
|
7
|
+
if (typeof path !== 'string' ||
|
|
8
|
+
path.length === 0 ||
|
|
9
|
+
path.trim() !== path ||
|
|
10
|
+
path.includes('\0')) {
|
|
11
|
+
throw new TypeError('Dataset path must be a non-empty stable module coordinate.');
|
|
12
|
+
}
|
|
13
|
+
const reference = Object.freeze({ kind: 'dataset-reference', path });
|
|
14
|
+
references.add(reference);
|
|
15
|
+
return reference;
|
|
16
|
+
}
|
|
17
|
+
export function isDatasetReference(input) {
|
|
18
|
+
return input !== null && typeof input === 'object' && references.has(input);
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DatasetReference } from './reference.js';
|
|
2
|
+
/** Development and test resources of one project. Never part of a Build, Release, or install. */
|
|
3
|
+
export interface Tests {
|
|
4
|
+
readonly kind: 'tests';
|
|
5
|
+
readonly datasets: readonly DatasetReference[];
|
|
6
|
+
}
|
|
7
|
+
export interface TestsInput {
|
|
8
|
+
readonly datasets?: readonly DatasetReference[];
|
|
9
|
+
}
|
|
10
|
+
/** Capture the inert test resources of a project; Dataset modules stay unloaded. */
|
|
11
|
+
export declare function tests(input?: TestsInput): Tests;
|
|
12
|
+
export declare function isTests(input: unknown): input is Tests;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { isDatasetReference } from './reference.js';
|
|
2
|
+
const admittedTests = new WeakSet();
|
|
3
|
+
/** Capture the inert test resources of a project; Dataset modules stay unloaded. */
|
|
4
|
+
export function tests(input = {}) {
|
|
5
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input))
|
|
6
|
+
invalid();
|
|
7
|
+
if (Reflect.ownKeys(input).some((key) => key !== 'datasets'))
|
|
8
|
+
invalid();
|
|
9
|
+
const datasets = input.datasets ?? [];
|
|
10
|
+
if (!Array.isArray(datasets))
|
|
11
|
+
throw new TypeError('Tests datasets must be an array.');
|
|
12
|
+
const paths = new Set();
|
|
13
|
+
for (const reference of datasets) {
|
|
14
|
+
if (!isDatasetReference(reference)) {
|
|
15
|
+
throw new TypeError('Tests datasets must contain only references created by dataset(path).');
|
|
16
|
+
}
|
|
17
|
+
if (paths.has(reference.path)) {
|
|
18
|
+
throw new TypeError(`Dataset ${reference.path} is referenced more than once.`);
|
|
19
|
+
}
|
|
20
|
+
paths.add(reference.path);
|
|
21
|
+
}
|
|
22
|
+
const value = Object.freeze({ kind: 'tests', datasets: Object.freeze([...datasets]) });
|
|
23
|
+
admittedTests.add(value);
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
export function isTests(input) {
|
|
27
|
+
return input !== null && typeof input === 'object' && admittedTests.has(input);
|
|
28
|
+
}
|
|
29
|
+
function invalid() {
|
|
30
|
+
throw new TypeError('Tests definition may contain only datasets.');
|
|
31
|
+
}
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import type { LintReportFormat } from '../linter/index.js';
|
|
2
|
-
export declare const COMMANDS: readonly ['dev', 'build', 'deploy', 'lint', 'package'];
|
|
2
|
+
export declare const COMMANDS: readonly ['dev', 'build', 'deploy', 'test', 'lint', 'package', 'upgrade'];
|
|
3
3
|
export type Command = (typeof COMMANDS)[number];
|
|
4
4
|
export interface ParsedArgs {
|
|
5
5
|
readonly command: Command;
|
|
6
6
|
readonly env: string;
|
|
7
7
|
readonly fix?: boolean;
|
|
8
8
|
readonly format?: LintReportFormat;
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly sessionFile?: string;
|
|
9
|
+
readonly deployOnly?: boolean;
|
|
10
|
+
readonly identity?: string;
|
|
12
11
|
readonly credentialFile?: string;
|
|
12
|
+
readonly suite?: string;
|
|
13
|
+
readonly sessionFile?: string;
|
|
13
14
|
readonly secretsFile?: string;
|
|
15
|
+
readonly channel?: string;
|
|
16
|
+
readonly check?: boolean;
|
|
14
17
|
}
|
|
15
18
|
/** Parse the frozen `astrale-domain` command grammar without performing effects. */
|
|
16
19
|
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
@@ -1,44 +1,27 @@
|
|
|
1
|
-
import { isIP } from 'node:net';
|
|
2
1
|
import { isAbsolute, normalize } from 'node:path';
|
|
3
|
-
export const COMMANDS = ['dev', 'build', 'deploy', 'lint', 'package'];
|
|
2
|
+
export const COMMANDS = ['dev', 'build', 'deploy', 'test', 'lint', 'package', 'upgrade'];
|
|
4
3
|
/** Parse the frozen `astrale-domain` command grammar without performing effects. */
|
|
5
4
|
export function parseArgs(argv) {
|
|
6
5
|
const [command, ...rest] = argv;
|
|
6
|
+
if (command === 'upgrade')
|
|
7
|
+
return parseUpgrade(rest);
|
|
7
8
|
const fix = rest.includes('--fix');
|
|
8
|
-
|
|
9
|
-
let host;
|
|
9
|
+
const deployOnly = rest.includes('--deploy-only');
|
|
10
10
|
let sessionFile;
|
|
11
|
-
let credentialFile;
|
|
12
11
|
let secretsFile;
|
|
12
|
+
let credentialFile;
|
|
13
|
+
let identity;
|
|
14
|
+
let suite;
|
|
13
15
|
let format;
|
|
14
16
|
let environment;
|
|
15
17
|
const cleaned = [];
|
|
16
18
|
for (let index = 0; index < rest.length; index += 1) {
|
|
17
19
|
const argument = rest[index];
|
|
18
|
-
if (argument === '--
|
|
19
|
-
|
|
20
|
-
port = Number(value);
|
|
21
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65_535) {
|
|
22
|
-
throw new Error(`--port needs a port number, got "${value}"`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
else if (argument.startsWith('--port=')) {
|
|
26
|
-
port = Number(argument.slice('--port='.length));
|
|
27
|
-
if (!Number.isInteger(port) || port <= 0 || port > 65_535) {
|
|
28
|
-
throw new Error(`--port needs a port number, got "${argument}"`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
else if (argument === '--host') {
|
|
32
|
-
host = normalizeHost(rest[++index]);
|
|
33
|
-
}
|
|
34
|
-
else if (argument.startsWith('--host=')) {
|
|
35
|
-
host = normalizeHost(argument.slice('--host='.length));
|
|
36
|
-
}
|
|
37
|
-
else if (argument === '--session-file') {
|
|
38
|
-
sessionFile = sessionFilePath(rest[++index]);
|
|
20
|
+
if (argument === '--session-file') {
|
|
21
|
+
sessionFile = absoluteFilePath('--session-file', rest[++index]);
|
|
39
22
|
}
|
|
40
23
|
else if (argument.startsWith('--session-file=')) {
|
|
41
|
-
sessionFile =
|
|
24
|
+
sessionFile = absoluteFilePath('--session-file', argument.slice('--session-file='.length));
|
|
42
25
|
}
|
|
43
26
|
else if (argument === '--credential-file') {
|
|
44
27
|
credentialFile = absoluteFilePath('--credential-file', rest[++index]);
|
|
@@ -52,6 +35,12 @@ export function parseArgs(argv) {
|
|
|
52
35
|
else if (argument.startsWith('--secrets-file=')) {
|
|
53
36
|
secretsFile = absoluteFilePath('--secrets-file', argument.slice('--secrets-file='.length));
|
|
54
37
|
}
|
|
38
|
+
else if (argument === '--as') {
|
|
39
|
+
identity = requiredValue('--as', rest[++index]);
|
|
40
|
+
}
|
|
41
|
+
else if (argument.startsWith('--as=')) {
|
|
42
|
+
identity = requiredValue('--as', argument.slice('--as='.length));
|
|
43
|
+
}
|
|
55
44
|
else if (argument === '--format') {
|
|
56
45
|
format = lintFormat(rest[++index]);
|
|
57
46
|
}
|
|
@@ -64,7 +53,13 @@ export function parseArgs(argv) {
|
|
|
64
53
|
else if (argument.startsWith('--environment=')) {
|
|
65
54
|
environment = requiredValue('--environment', argument.slice('--environment='.length));
|
|
66
55
|
}
|
|
67
|
-
else if (argument === '--
|
|
56
|
+
else if (argument === '--suite') {
|
|
57
|
+
suite = requiredValue('--suite', rest[++index]);
|
|
58
|
+
}
|
|
59
|
+
else if (argument.startsWith('--suite=')) {
|
|
60
|
+
suite = requiredValue('--suite', argument.slice('--suite='.length));
|
|
61
|
+
}
|
|
62
|
+
else if (argument === '--fix' || argument === '--deploy-only') {
|
|
68
63
|
continue;
|
|
69
64
|
}
|
|
70
65
|
else if (argument.startsWith('-')) {
|
|
@@ -79,59 +74,69 @@ export function parseArgs(argv) {
|
|
|
79
74
|
if (format !== undefined && command !== 'lint') {
|
|
80
75
|
throw new Error('`--format` is only valid for `lint`.');
|
|
81
76
|
}
|
|
82
|
-
if ((
|
|
83
|
-
host !== undefined ||
|
|
84
|
-
sessionFile !== undefined ||
|
|
85
|
-
credentialFile !== undefined ||
|
|
86
|
-
secretsFile !== undefined) &&
|
|
77
|
+
if ((sessionFile !== undefined || credentialFile !== undefined || secretsFile !== undefined) &&
|
|
87
78
|
command !== 'dev') {
|
|
88
|
-
throw new Error('`--
|
|
79
|
+
throw new Error('`--session-file`, `--credential-file`, and `--secrets-file` are only valid for `dev`.');
|
|
80
|
+
}
|
|
81
|
+
if (identity !== undefined && credentialFile !== undefined) {
|
|
82
|
+
throw new Error('Choose either `--as` or `--credential-file`, not both.');
|
|
89
83
|
}
|
|
90
|
-
if (
|
|
91
|
-
throw new Error('`--
|
|
84
|
+
if ((deployOnly || identity !== undefined) && command !== 'dev' && command !== 'deploy') {
|
|
85
|
+
throw new Error('`--deploy-only` and `--as` are only valid for `dev` or `deploy`.');
|
|
86
|
+
}
|
|
87
|
+
if (environment !== undefined && command !== 'dev' && command !== 'deploy') {
|
|
88
|
+
if (command !== 'test') {
|
|
89
|
+
throw new Error('`--environment` is only valid for `dev`, `deploy`, or `test`.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (suite !== undefined && command !== 'test') {
|
|
93
|
+
throw new Error('`--suite` is only valid for `test`.');
|
|
92
94
|
}
|
|
93
95
|
const positionals = cleaned.filter((argument) => !argument.startsWith('-'));
|
|
94
96
|
if (environment !== undefined && positionals.length > 0) {
|
|
95
|
-
throw new Error('Choose either a positional
|
|
97
|
+
throw new Error('Choose either a positional Environment or `--environment`, not both.');
|
|
96
98
|
}
|
|
97
99
|
switch (command) {
|
|
98
100
|
case 'dev':
|
|
99
|
-
if (positionals.length > 1)
|
|
100
|
-
throw new Error('`dev` accepts at most one environment.');
|
|
101
|
-
return {
|
|
102
|
-
command: 'dev',
|
|
103
|
-
env: environment ?? positionals[0] ?? 'dev',
|
|
104
|
-
...(port !== undefined ? { port } : {}),
|
|
105
|
-
...(host !== undefined ? { host } : {}),
|
|
106
|
-
...(sessionFile !== undefined ? { sessionFile } : {}),
|
|
107
|
-
...(credentialFile !== undefined ? { credentialFile } : {}),
|
|
108
|
-
...(secretsFile !== undefined ? { secretsFile } : {}),
|
|
109
|
-
};
|
|
110
101
|
case 'deploy': {
|
|
111
|
-
if (positionals.length > 1)
|
|
112
|
-
throw new Error(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
102
|
+
if (positionals.length > 1) {
|
|
103
|
+
throw new Error(`\`${command}\` accepts exactly one Environment.`);
|
|
104
|
+
}
|
|
105
|
+
const env = environment ?? positionals[0] ?? (command === 'dev' ? 'development' : undefined);
|
|
106
|
+
if (!env) {
|
|
107
|
+
throw new Error(`\`${command}\` requires an Environment, e.g. \`${command} development\`.`);
|
|
108
|
+
}
|
|
116
109
|
return {
|
|
117
|
-
command
|
|
110
|
+
command,
|
|
118
111
|
env,
|
|
112
|
+
...(deployOnly ? { deployOnly: true } : {}),
|
|
113
|
+
...(identity === undefined ? {} : { identity }),
|
|
114
|
+
...(credentialFile === undefined ? {} : { credentialFile }),
|
|
115
|
+
...(sessionFile === undefined ? {} : { sessionFile }),
|
|
116
|
+
...(secretsFile === undefined ? {} : { secretsFile }),
|
|
119
117
|
};
|
|
120
118
|
}
|
|
121
119
|
case 'build':
|
|
122
|
-
if (positionals.length > 0)
|
|
123
|
-
throw new Error('`build` does not accept positional arguments.');
|
|
124
|
-
return { command: 'build', env: 'dev' };
|
|
125
120
|
case 'package':
|
|
126
|
-
if (positionals.length > 0)
|
|
127
|
-
throw new Error(
|
|
128
|
-
|
|
121
|
+
if (positionals.length > 0) {
|
|
122
|
+
throw new Error(`\`${command}\` does not accept positional arguments.`);
|
|
123
|
+
}
|
|
124
|
+
return { command, env: '' };
|
|
125
|
+
case 'test':
|
|
126
|
+
if (positionals.length > 0) {
|
|
127
|
+
throw new Error('`test` does not accept positional arguments; use `--environment`.');
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
command: 'test',
|
|
131
|
+
env: environment ?? '',
|
|
132
|
+
...(suite === undefined ? {} : { suite }),
|
|
133
|
+
};
|
|
129
134
|
case 'lint':
|
|
130
135
|
if (positionals.length > 0)
|
|
131
136
|
throw new Error('`lint` does not accept positional arguments.');
|
|
132
137
|
return {
|
|
133
138
|
command: 'lint',
|
|
134
|
-
env: '
|
|
139
|
+
env: '',
|
|
135
140
|
...(fix ? { fix: true } : {}),
|
|
136
141
|
...(format ? { format } : {}),
|
|
137
142
|
};
|
|
@@ -139,50 +144,23 @@ export function parseArgs(argv) {
|
|
|
139
144
|
throw new Error(command ? `Unknown command "${command}".` : 'Missing command.');
|
|
140
145
|
}
|
|
141
146
|
}
|
|
142
|
-
function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
throw new Error(`--host needs a valid URL, got "${input}"`);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
function privateHostname(input) {
|
|
163
|
-
const host = input
|
|
164
|
-
.toLowerCase()
|
|
165
|
-
.replace(/^\[|\]$/gu, '')
|
|
166
|
-
.replace(/\.$/u, '');
|
|
167
|
-
if (host === 'localhost' || host.endsWith('.localhost'))
|
|
168
|
-
return true;
|
|
169
|
-
if (isIP(host) === 4) {
|
|
170
|
-
const parts = host.split('.').map(Number);
|
|
171
|
-
return (parts[0] === 0 ||
|
|
172
|
-
parts[0] === 10 ||
|
|
173
|
-
parts[0] === 127 ||
|
|
174
|
-
(parts[0] === 169 && parts[1] === 254) ||
|
|
175
|
-
(parts[0] === 172 && (parts[1] ?? 0) >= 16 && (parts[1] ?? 0) <= 31) ||
|
|
176
|
-
(parts[0] === 192 && parts[1] === 168));
|
|
147
|
+
function parseUpgrade(args) {
|
|
148
|
+
let channel = 'beta';
|
|
149
|
+
let check = false;
|
|
150
|
+
for (let index = 0; index < args.length; index++) {
|
|
151
|
+
const argument = args[index];
|
|
152
|
+
if (argument === '--check')
|
|
153
|
+
check = true;
|
|
154
|
+
else if (argument === '--channel' || argument.startsWith('--channel=')) {
|
|
155
|
+
channel = requiredValue('--channel', argument === '--channel' ? args[++index] : argument.slice(10));
|
|
156
|
+
if (!/^[A-Za-z][A-Za-z0-9._-]*$/u.test(channel)) {
|
|
157
|
+
throw new Error('--channel must be a registry dist-tag, such as beta or latest.');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
throw new Error(`Unknown upgrade argument "${argument}".`);
|
|
177
162
|
}
|
|
178
|
-
|
|
179
|
-
return false;
|
|
180
|
-
return (host === '::' ||
|
|
181
|
-
host === '::1' ||
|
|
182
|
-
host.startsWith('fc') ||
|
|
183
|
-
host.startsWith('fd') ||
|
|
184
|
-
/^fe[89ab]/u.test(host) ||
|
|
185
|
-
/^::ffff:(?:0|10|127|169\.254|172\.(?:1[6-9]|2\d|3[01])|192\.168)\./u.test(host));
|
|
163
|
+
return { command: 'upgrade', env: '', channel, check };
|
|
186
164
|
}
|
|
187
165
|
function lintFormat(input) {
|
|
188
166
|
if (input !== 'stylish' && input !== 'json') {
|
|
@@ -195,9 +173,6 @@ function requiredValue(flag, input) {
|
|
|
195
173
|
throw new Error(`${flag} needs a value.`);
|
|
196
174
|
return input;
|
|
197
175
|
}
|
|
198
|
-
function sessionFilePath(input) {
|
|
199
|
-
return absoluteFilePath('--session-file', input);
|
|
200
|
-
}
|
|
201
176
|
function absoluteFilePath(flag, input) {
|
|
202
177
|
const value = requiredValue(flag, input);
|
|
203
178
|
if (!isAbsolute(value) || normalize(value) !== value) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Build one mutually exclusive authority argument set for an Astrale CLI operation. */
|
|
2
|
+
export function astraleAuthorityArguments(input) {
|
|
3
|
+
if (input.identity !== undefined && input.credential !== undefined) {
|
|
4
|
+
throw new TypeError('Astrale authority cannot combine an identity and a credential.');
|
|
5
|
+
}
|
|
6
|
+
return input.credential === undefined
|
|
7
|
+
? input.identity === undefined
|
|
8
|
+
? Object.freeze([])
|
|
9
|
+
: Object.freeze(['--as', input.identity])
|
|
10
|
+
: Object.freeze(['--creds', input.credential]);
|
|
11
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { constants } from 'node:fs';
|
|
2
|
+
import { access, readFile, realpath } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { delimiter, isAbsolute, join } from 'node:path';
|
|
5
|
+
/** Resolve one exact executable for a complete sequence of Kernel CLI operations. */
|
|
6
|
+
export async function resolveAstraleExecutable(input = 'astrale') {
|
|
7
|
+
const candidates = isAbsolute(input) ? [input] : await executableCandidates(input);
|
|
8
|
+
for (const candidate of candidates) {
|
|
9
|
+
try {
|
|
10
|
+
await access(candidate, constants.X_OK);
|
|
11
|
+
return await realpath(candidate);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Continue through the bounded recorded-install and PATH candidates.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw new TypeError('Astrale CLI is unavailable. Install it with the official Astrale installer.');
|
|
18
|
+
}
|
|
19
|
+
async function executableCandidates(input) {
|
|
20
|
+
const path = (process.env.PATH ?? '')
|
|
21
|
+
.split(delimiter)
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.map((directory) => join(directory, input));
|
|
24
|
+
if (input !== 'astrale')
|
|
25
|
+
return path;
|
|
26
|
+
const installed = await recordedInstallation();
|
|
27
|
+
return installed === undefined ? path : [installed, ...path];
|
|
28
|
+
}
|
|
29
|
+
async function recordedInstallation() {
|
|
30
|
+
const home = process.env.ASTRALE_HOME ?? join(homedir(), '.astrale');
|
|
31
|
+
try {
|
|
32
|
+
const decoded = JSON.parse(await readFile(join(home, 'install.json'), 'utf8'));
|
|
33
|
+
if (typeof decoded !== 'object' ||
|
|
34
|
+
decoded === null ||
|
|
35
|
+
!('method' in decoded) ||
|
|
36
|
+
decoded.method !== 'script' ||
|
|
37
|
+
!('bin' in decoded) ||
|
|
38
|
+
typeof decoded.bin !== 'string' ||
|
|
39
|
+
!isAbsolute(decoded.bin)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return decoded.bin;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AstraleProcessResult } from './process.js';
|
|
2
|
+
/** Only sanitized CLI diagnostics may be promoted through installation failures. */
|
|
3
|
+
export declare class AstraleCliError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export declare function astraleFailure(command: string, result: AstraleProcessResult): Error;
|
|
6
|
+
export declare function isDomainNotInstalledFailure(stderr: string): boolean;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** Only sanitized CLI diagnostics may be promoted through installation failures. */
|
|
2
|
+
export class AstraleCliError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export function astraleFailure(command, result) {
|
|
5
|
+
let diagnostic;
|
|
6
|
+
try {
|
|
7
|
+
const failure = record(JSON.parse(result.stderr));
|
|
8
|
+
const error = admittedCode(failure.error);
|
|
9
|
+
const reason = recordOrUndefined(failure.reason);
|
|
10
|
+
const reasonCode = admittedCode(reason?.code);
|
|
11
|
+
diagnostic =
|
|
12
|
+
error === undefined ? undefined : reasonCode === undefined ? error : `${error}/${reasonCode}`;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Never forward raw stderr because a credential-bearing command owns it.
|
|
16
|
+
}
|
|
17
|
+
return new AstraleCliError(`Astrale CLI ${command} failed${diagnostic === undefined ? '' : ` (${diagnostic})`}.`);
|
|
18
|
+
}
|
|
19
|
+
function admittedCode(input) {
|
|
20
|
+
return typeof input === 'string' && /^[A-Z][A-Z0-9_]{0,63}$/u.test(input) ? input : undefined;
|
|
21
|
+
}
|
|
22
|
+
function recordOrUndefined(input) {
|
|
23
|
+
return input !== null && typeof input === 'object' && !Array.isArray(input)
|
|
24
|
+
? input
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
export function isDomainNotInstalledFailure(stderr) {
|
|
28
|
+
try {
|
|
29
|
+
const failure = record(JSON.parse(stderr));
|
|
30
|
+
if (failure.error === 'DOMAIN_NOT_INSTALLED')
|
|
31
|
+
return true;
|
|
32
|
+
const reason = failure.reason;
|
|
33
|
+
return (failure.error === 'RESPONSE_ERROR' &&
|
|
34
|
+
reason !== null &&
|
|
35
|
+
typeof reason === 'object' &&
|
|
36
|
+
!Array.isArray(reason) &&
|
|
37
|
+
Reflect.get(reason, 'code') === 'SCHEMA_NOT_FOUND');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function record(input) {
|
|
44
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input)) {
|
|
45
|
+
throw new TypeError('Expected an object response.');
|
|
46
|
+
}
|
|
47
|
+
return input;
|
|
48
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { runAstrale } from './process.js';
|
|
2
|
+
/** Resolve and probe a bookmark through the pinned CLI, preserving its TLS trust. */
|
|
3
|
+
export declare function resolveAstraleInstanceIssuer(input: {
|
|
4
|
+
readonly executable: string;
|
|
5
|
+
readonly projectDir: string;
|
|
6
|
+
readonly instance: string;
|
|
7
|
+
readonly expectedIssuer?: string;
|
|
8
|
+
readonly signal: AbortSignal;
|
|
9
|
+
}, dependencies?: {
|
|
10
|
+
readonly run: typeof runAstrale;
|
|
11
|
+
}): Promise<string>;
|