Resolve an ASTRA project¶
resolveAnalysis() validates a complete project, selects one root universe,
and projects authored ASTRA into a recursive data structure for downstream
tools.
import {
AnalysisValidationError,
ProjectLoadError,
resolveAnalysis,
} from "@astra-spec/sdk";
import { createNodeProjectReader } from "@astra-spec/sdk/node";
const reader = createNodeProjectReader("/path/to/project");
try {
const bundle = await resolveAnalysis(reader, { universeId: "baseline" });
render(bundle.document, bundle.bindings);
} catch (error) {
if (error instanceof AnalysisValidationError) {
console.error(error.issues);
} else if (error instanceof ProjectLoadError) {
console.error(error.code, error.path, error.message);
}
}
An invalid authored project throws AnalysisValidationError; its issues have
the same contract returned by validateAnalysis(). Loading failures and an
explicitly requested universe that does not exist throw ProjectLoadError.
Universe selection¶
Pass universeId when a project has multiple root universes. Without it, the
first universe filename in lexical order is selected implicitly. When no
universe files exist, authored defaults are used and the resolved universe ID
is default.
The resolved document records the selection and all available root universe IDs:
bundle.document.universe;
// {
// universeId: "baseline",
// availableUniverseIds: ["baseline", "svm_focused"],
// source: "explicit"
// }
Bundle shape¶
interface ResolvedAnalysisBundle {
document: ResolvedAnalysisDocument;
bindings: ArtifactBinding[];
}
document is JSON-serializable and contains the universe metadata and one
recursive analysis tree. Collections remain ordered arrays. The resolver:
- assigns canonical paths to every analysis and record;
- resolves aliases while retaining the authored
fromvalue; - normalizes decision options and effective selections;
- evaluates conditions and exposes
activeon outputs and decisions; - resolves output provenance, supporting insights, and artifact evidence.
The root analysis path is $; child analyses use dotted IDs such as stage
and stage.leaf. Record paths include their collection, for example
outputs.figure and stage.outputs.plot.
Decode a transported bundle¶
A ResolvedAnalysisBundle produced in the same TypeScript process already has
the SDK type. Once a bundle crosses a JSON, structured-clone, storage, message,
or API boundary, treat it as unknown and restore that guarantee with the
runtime decoder:
import {
parseResolvedAnalysisBundle,
ResolvedAnalysisBundleValidationError,
} from "@astra-spec/sdk";
const candidate: unknown = JSON.parse(serializedBundle);
try {
const bundle = parseResolvedAnalysisBundle(candidate);
render(bundle.document, bundle.bindings);
} catch (error) {
if (error instanceof ResolvedAnalysisBundleValidationError) {
for (const issue of error.issues) {
console.error(`${issue.path}: ${issue.message}`);
}
}
}
For branch-based control flow, isResolvedAnalysisBundle(candidate) provides a
boolean TypeScript type guard instead.
parseResolvedAnalysisBundle() validates already-deserialized data; it is not
an alias for JSON.parse. It checks the complete known resolved shape and the
current RESOLVED_ANALYSIS_SCHEMA_VERSION, returns the original object without
cloning it, and performs no project I/O, reference checking, or semantic
resolution. It rejects non-finite numeric fields and negative artifact byte
sizes so accepted values remain safe to transport. Additional unknown fields
remain intact and do not invalidate the bundle.
Deterministic artifact paths¶
Artifact locations are derived directly; the resolver never scans results/.
| Output owner | Artifact path |
|---|---|
| Root analysis | results/<universe>/<output>.<format> |
| Inline descendant | results/<universe>/<analysis>.<output>.<format> |
| Deeper inline descendant | Additional analysis IDs joined with dots |
| Path-backed analysis | Its own results/ directory and a fresh dotted namespace |
A missing artifact is not a validation failure. Inactive outputs, outputs with no format, and outputs whose known file is absent simply have no artifact descriptor or binding.
Each materialized output has an ArtifactBinding:
cacheToken is an opaque equality token for cache-busting artifact reads. It
is SHA-256 over the project-relative path, integer modification time in
milliseconds, and byte size. It is deliberately not a content hash, revision
number, or value that consumers should parse or order.
Derived views¶
The resolved document avoids duplicated lookup structures. Derive them only when a consumer needs them:
import { collectCitedDois, indexAnalysis, walkAnalyses } from "@astra-spec/sdk";
const index = indexAnalysis(bundle.document);
const figure = index.recordByPath.get("stage.outputs.figure");
const owner = index.analysisByRecordPath.get("stage.outputs.figure"); // the "stage" node
for (const analysis of walkAnalyses(bundle.document)) {
console.log(analysis.canonicalPath);
}
const citedDois = collectCitedDois(bundle.document);