Build Camino packages from explicit contract inputs
Project source-free capability contracts and keep exact source provenance in package assembly. Reuse compiled groups across unrelated graph and dependency implementation edits. Keep React public inputs separate from workspace query lowering and retain explicit type-companion dependencies. Allow independent execution-package checks, scope resource compilation to declared dependencies, and memoize source closure traversal. Document generic-specialization limits and measured Reader rebuild behavior. Verified protocol, artifact, package, React, and managed activation checks; retained-Git independent/workspace artifact equality and incompatible type dependency rejection; nix fmt -- --check and lint. One-off Reader measurements recompiled 1 of 13 groups for a child edit and 0 for a workspace QX default edit.
This commit is contained in:
@@ -20,15 +20,19 @@ export interface ArtifactImplementation {
|
||||
files: ArtifactFile[];
|
||||
contentDigest: string;
|
||||
}
|
||||
export interface PackageArtifacts {
|
||||
export interface ArtifactGroup {
|
||||
schemaVersion: 1;
|
||||
packageRevisionId: string;
|
||||
bindingDigest: string;
|
||||
sourceDigest: string;
|
||||
versions: ExecutionPlan["definition"]["versions"];
|
||||
implementations: ArtifactImplementation[];
|
||||
components?: ComponentArtifact[];
|
||||
}
|
||||
/** Installation evidence binds a reusable group to the selected exact source.
|
||||
* Compilation receipts never claim that a cache hit was built at a new pin. */
|
||||
export interface PackageArtifacts extends ArtifactGroup {
|
||||
sourceDigest: string;
|
||||
}
|
||||
export const componentSharedModules = [
|
||||
"react",
|
||||
"react-dom",
|
||||
@@ -199,11 +203,11 @@ function parseComponent(value: unknown, index: number): ComponentArtifact {
|
||||
if (componentArtifactDigest(body) !== contentDigest) fail(path, "Component metadata digest mismatch");
|
||||
return component;
|
||||
}
|
||||
export function parsePackageArtifacts(value: unknown): PackageArtifacts {
|
||||
export function parseArtifactGroup(value: unknown): ArtifactGroup {
|
||||
const record = object(value, "manifest");
|
||||
keys(
|
||||
record,
|
||||
["schemaVersion", "packageRevisionId", "bindingDigest", "sourceDigest", "versions", "implementations"],
|
||||
["schemaVersion", "packageRevisionId", "bindingDigest", "versions", "implementations"],
|
||||
["components"],
|
||||
"manifest",
|
||||
);
|
||||
@@ -266,13 +270,19 @@ export function parsePackageArtifacts(value: unknown): PackageArtifacts {
|
||||
schemaVersion: 1,
|
||||
packageRevisionId: text(record.packageRevisionId, "packageRevisionId"),
|
||||
bindingDigest: digest(record.bindingDigest, "bindingDigest"),
|
||||
sourceDigest: digest(record.sourceDigest, "sourceDigest"),
|
||||
versions: versions as unknown as PackageArtifacts["versions"],
|
||||
implementations,
|
||||
...(components ? { components } : {}),
|
||||
};
|
||||
}
|
||||
function checkCoverage(expected: PlanPackage, manifest: PackageArtifacts): void {
|
||||
export function parsePackageArtifacts(value: unknown): PackageArtifacts {
|
||||
const { sourceDigest, ...group } = object(value, "manifest");
|
||||
return { ...parseArtifactGroup(group), sourceDigest: digest(sourceDigest, "sourceDigest") };
|
||||
}
|
||||
export function checkPackageCoverage(
|
||||
expected: Pick<PlanPackage, "revision" | "modules" | "components" | "exports" | "bindingDigest" | "sourceDigest">,
|
||||
manifest: PackageArtifacts,
|
||||
): void {
|
||||
for (const module of expected.modules ?? [])
|
||||
if (!expected.components?.some((component) => component.id === module.id))
|
||||
fail(module.id, `No artifact consumer for module contract ${module.contract} and its subject`);
|
||||
@@ -315,6 +325,21 @@ function checkCoverage(expected: PlanPackage, manifest: PackageArtifacts): void
|
||||
}
|
||||
}
|
||||
export type ArtifactReader = (packageRevisionId: string, path: string, maxBytes: number) => Promise<Uint8Array>;
|
||||
/** Shared by standalone package checks and complete world installation. */
|
||||
export async function checkArtifactFiles(manifest: ArtifactGroup, read: ArtifactReader): Promise<void> {
|
||||
const files = new Map<string, ArtifactFile>();
|
||||
for (const artifact of [...manifest.implementations, ...(manifest.components ?? [])])
|
||||
for (const file of artifact.files) {
|
||||
const existing = files.get(file.path);
|
||||
if (existing && canonicalJson(existing) !== canonicalJson(file)) fail(file.path, "Conflicting file descriptors");
|
||||
files.set(file.path, file);
|
||||
}
|
||||
for (const file of files.values()) {
|
||||
const bytes = await read(manifest.packageRevisionId, file.path, file.bytes);
|
||||
if (bytes.byteLength !== file.bytes || createHash("sha256").update(bytes).digest("hex") !== file.digest)
|
||||
fail(file.path, "Artifact bytes do not match the checked content digest");
|
||||
}
|
||||
}
|
||||
/** Caller supplies a reader confined to the resolved immutable package output, including symlink checks. */
|
||||
export async function checkExecutionWorld(
|
||||
plan: ExecutionPlan,
|
||||
@@ -335,17 +360,11 @@ export async function checkExecutionWorld(
|
||||
const manifest =
|
||||
manifests.find((m) => m.packageRevisionId === expected.revision) ??
|
||||
fail(expected.revision, "Missing package artifact");
|
||||
checkCoverage(expected, manifest);
|
||||
checkPackageCoverage(expected, manifest);
|
||||
if (canonicalJson(manifest.versions) !== canonicalJson(plan.definition.versions))
|
||||
fail(expected.revision, "Incompatible runtime codecs");
|
||||
const files = new Map<string, ArtifactFile>();
|
||||
await checkArtifactFiles(manifest, read);
|
||||
for (const implementation of manifest.implementations) {
|
||||
for (const file of implementation.files) {
|
||||
const existing = files.get(file.path);
|
||||
if (existing && canonicalJson(existing) !== canonicalJson(file))
|
||||
fail(file.path, "Conflicting file descriptors");
|
||||
files.set(file.path, file);
|
||||
}
|
||||
artifacts.push({
|
||||
binding: JSON.stringify([expected.revision, implementation.id]),
|
||||
target: implementation.target,
|
||||
@@ -353,23 +372,12 @@ export async function checkExecutionWorld(
|
||||
});
|
||||
}
|
||||
for (const component of manifest.components ?? []) {
|
||||
for (const file of component.files) {
|
||||
const existing = files.get(file.path);
|
||||
if (existing && canonicalJson(existing) !== canonicalJson(file))
|
||||
fail(file.path, "Conflicting file descriptors");
|
||||
files.set(file.path, file);
|
||||
}
|
||||
artifacts.push({
|
||||
binding: JSON.stringify([expected.revision, "component", component.id]),
|
||||
target: "browser-renderer",
|
||||
digest: component.contentDigest,
|
||||
});
|
||||
}
|
||||
for (const file of files.values()) {
|
||||
const bytes = await read(expected.revision, file.path, file.bytes);
|
||||
if (bytes.byteLength !== file.bytes || createHash("sha256").update(bytes).digest("hex") !== file.digest)
|
||||
fail(file.path, "Artifact bytes do not match the checked content digest");
|
||||
}
|
||||
}
|
||||
return {
|
||||
codec: "camino-world-v1",
|
||||
|
||||
Reference in New Issue
Block a user