import type { PlayArtifactKind } from '../play-runtime/backend'; import type { PlayStaticPipeline } from './static-pipeline'; import type { AdmittedPlayAuthoringContract } from './authoring-contract'; export const PLAY_COMPILER_MANIFEST_VERSION = 1; export type PlayCompilerDependencyManifest = { playName: string; /** Original source path for direct imported definePlay dependencies. */ filePath?: string; sourceHash: string; graphHash: string; artifactHash: string; staticPipeline: PlayStaticPipeline; /** Compact transitive dependency closure from the child manifest. */ importedPlayDependencies?: PlayCompilerDependencyManifest[]; }; /** * A unique named ctx.runPlay target resolved while compiling the root Play. * Keeping this separate from source imports lets artifact registration reuse a * freshly resolved child shape without embedding it at every call site. */ export type PlayCompilerNamedPlayDependency = { playName: string; staticPipeline: PlayStaticPipeline; }; export type PlayCompilerManifest = { compilerVersion: number; playName: string; sourceHash: string; graphHash: string; artifactHash: string; artifactKind?: PlayArtifactKind; staticPipeline: PlayStaticPipeline; importedPlayDependencies: PlayCompilerDependencyManifest[]; namedPlayDependencies?: PlayCompilerNamedPlayDependency[]; authoringContract?: AdmittedPlayAuthoringContract; }; export type PlayRuntimeManifest = { playName?: string; graphHash: string; artifactHash: string; artifactStorageKey: string; staticPipelineHash: string; staticPipeline: PlayStaticPipeline; compiledAt: number; compilerVersion: string; sourceCode?: string | null; bundledCode?: string | null; maxCreditsPerRun?: number | null; }; export type PlayRuntimeManifestMap = Record; /** * Indexes the complete compact dependency closure carried by compiler * manifests. A direct child can itself reference unpublished local plays, so * callers must not limit resolution to the root's direct imports. */ export function collectPlayCompilerDependencies( dependencies: readonly PlayCompilerDependencyManifest[], ): Map { const byName = new Map(); const visit = (dependency: PlayCompilerDependencyManifest) => { const existing = byName.get(dependency.playName); if (existing) { if ( existing.graphHash !== dependency.graphHash || existing.artifactHash !== dependency.artifactHash ) { throw new Error( `Conflicting compiler manifests for imported Play "${dependency.playName}".`, ); } return; } byName.set(dependency.playName, dependency); for (const child of dependency.importedPlayDependencies ?? []) { visit(child); } }; for (const dependency of dependencies) visit(dependency); return byName; } export function collectNamedPlayCompilerDependencies( dependencies: readonly PlayCompilerNamedPlayDependency[] | undefined, ): Map { const byName = new Map(); for (const dependency of dependencies ?? []) { const existing = byName.get(dependency.playName); if ( existing && JSON.stringify(existing.staticPipeline) !== JSON.stringify(dependency.staticPipeline) ) { throw new Error( `Conflicting compiler manifests for named Play "${dependency.playName}".`, ); } byName.set(dependency.playName, dependency); } return byName; }