{"version":3,"file":"artifact.d.ts","sourceRoot":"","sources":["../../src/components/artifact.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,KAAK,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAE/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,WAAW,0BAA0B;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,6BAA6B;IAC7C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,gCAAgC;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,kBAAkB,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAC;IAC/D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,UAAU,CAAC;CACvC;AAqBD,wBAAsB,2BAA2B,CAChD,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,gCAAgC,GACrC,OAAO,CAAC,0BAA0B,CAAC,CA8CrC;AAED,yEAAyE;AACzE,wBAAsB,2BAA2B,CAChD,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,cAAc,GACtB,OAAO,CAAC,6BAA6B,CAAC,CAoCxC;AAED,4EAA4E;AAC5E,wBAAsB,oCAAoC,CACzD,KAAK,EAAE,QAAQ,EACf,QAAQ,EAAE,6BAA6B,GACrC,OAAO,CAAC,0BAA0B,CAAC,CAerC;AAED;;;;;GAKG;AACH,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,QAAQ,EACf,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,cAAc,GACtB,OAAO,CAAC,0BAA0B,CAAC,CAIrC;AAED,wBAAsB,wBAAwB,CAC7C,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,0BAA0B,CAAC,CAoCrC;AAED,wBAAsB,6BAA6B,CAClD,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,qBAAqB,EAChC,QAAQ,EAAE,cAAc,GACtB,OAAO,CAAC,0BAA0B,CAAC,CAQrC","sourcesContent":["import { lstat, mkdir, open, readdir, readFile, rename, rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { isDigest } from \"../bundle/schema.ts\";\nimport type { EvoPaths } from \"../paths.ts\";\nimport { ensureEvoLayout } from \"../paths.ts\";\nimport { canonicalJson, sha256 } from \"../storage.ts\";\nimport type { EvoComponentManifest, EvoComponentSelection } from \"../types.ts\";\nimport { parseEvoComponentManifest } from \"./manifest.ts\";\nimport type { EvoAbiRegistry } from \"./registry.ts\";\n\nexport interface LoadedEvoComponentArtifact {\n\tdirectory: string;\n\tentrypoint: string;\n\tmanifest: EvoComponentManifest;\n}\n\nexport interface InspectedEvoComponentArtifact {\n\tmanifest: EvoComponentManifest;\n\tentrypointContent: Buffer;\n}\n\nexport interface PublishEvoComponentArtifactInput {\n\tid: string;\n\tversion: string;\n\tabi: string;\n\tactivationBoundary: EvoComponentManifest[\"activationBoundary\"];\n\tcapabilities: string[];\n\tentrypointName?: string;\n\tentrypointContent: string | Uint8Array;\n}\n\nfunction identityDigest(manifest: Omit<EvoComponentManifest, \"artifactDigest\">): string {\n\treturn sha256(canonicalJson({ componentArtifactSchemaVersion: 1, manifest }));\n}\n\nasync function writeExclusive(path: string, content: string | Uint8Array, mode: number): Promise<void> {\n\tconst handle = await open(path, \"wx\", mode);\n\ttry {\n\t\tawait handle.writeFile(content);\n\t\tawait handle.sync();\n\t} finally {\n\t\tawait handle.close();\n\t}\n}\n\nfunction componentArtifactDirectory(paths: EvoPaths, digest: string): string {\n\tif (!isDigest(digest)) throw new Error(`Invalid component artifact digest: ${digest}`);\n\treturn join(paths.components, digest);\n}\n\nexport async function publishEvoComponentArtifact(\n\tpaths: EvoPaths,\n\tinput: PublishEvoComponentArtifactInput,\n): Promise<LoadedEvoComponentArtifact> {\n\tawait ensureEvoLayout(paths);\n\tconst entrypoint = input.entrypointName ?? \"component.mjs\";\n\tconst entrypointBytes = Buffer.from(input.entrypointContent);\n\tconst unsigned = parseEvoComponentManifest({\n\t\tschemaVersion: 1,\n\t\tid: input.id,\n\t\tversion: input.version,\n\t\tartifactDigest: \"0\".repeat(64),\n\t\tentrypointSha256: sha256(entrypointBytes),\n\t\tabi: input.abi,\n\t\tactivationBoundary: input.activationBoundary,\n\t\tcapabilities: input.capabilities,\n\t\tentrypoint,\n\t});\n\tconst { artifactDigest: _, ...identity } = unsigned;\n\tconst digest = identityDigest(identity);\n\tconst manifest: EvoComponentManifest = { ...identity, artifactDigest: digest };\n\tconst destination = componentArtifactDirectory(paths, digest);\n\ttry {\n\t\treturn await loadEvoComponentArtifact(paths, digest);\n\t} catch (error) {\n\t\tif (typeof error !== \"object\" || error === null || !(\"code\" in error) || error.code !== \"ENOENT\") throw error;\n\t}\n\tconst temporary = join(paths.components, `.tmp-${process.pid}-${digest.slice(0, 12)}`);\n\tawait rm(temporary, { recursive: true, force: true });\n\tawait mkdir(temporary, { mode: 0o700 });\n\ttry {\n\t\tawait writeExclusive(join(temporary, entrypoint), entrypointBytes, 0o400);\n\t\tawait writeExclusive(join(temporary, \"manifest.json\"), `${JSON.stringify(manifest, undefined, \"\\t\")}\\n`, 0o400);\n\t\ttry {\n\t\t\tawait rename(temporary, destination);\n\t\t} catch (error) {\n\t\t\tif (\n\t\t\t\ttypeof error !== \"object\" ||\n\t\t\t\terror === null ||\n\t\t\t\t!(\"code\" in error) ||\n\t\t\t\t(error.code !== \"EEXIST\" && error.code !== \"ENOTEMPTY\")\n\t\t\t) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t\treturn await loadEvoComponentArtifact(paths, digest);\n\t} finally {\n\t\tawait rm(temporary, { recursive: true, force: true });\n\t}\n}\n\n/** Purely inspect an external artifact without publishing it locally. */\nexport async function inspectEvoComponentArtifact(\n\tsourceDirectory: string,\n\tregistry: EvoAbiRegistry,\n): Promise<InspectedEvoComponentArtifact> {\n\tconst directoryMetadata = await lstat(sourceDirectory);\n\tif (!directoryMetadata.isDirectory() || directoryMetadata.isSymbolicLink()) {\n\t\tthrow new Error(\"Imported component artifact is not a regular directory\");\n\t}\n\tconst entries = (await readdir(sourceDirectory, { withFileTypes: true })).sort((left, right) =>\n\t\tleft.name.localeCompare(right.name),\n\t);\n\tif (entries.some((entry) => !entry.isFile() || entry.isSymbolicLink())) {\n\t\tthrow new Error(\"Imported component artifact contains a non-regular file\");\n\t}\n\tconst manifest = parseEvoComponentManifest(\n\t\tJSON.parse(await readFile(join(sourceDirectory, \"manifest.json\"), \"utf8\")) as unknown,\n\t);\n\tconst expectedFiles = [manifest.entrypoint, \"manifest.json\"].sort();\n\tif (entries.map((entry) => entry.name).join(\"\\n\") !== expectedFiles.join(\"\\n\")) {\n\t\tthrow new Error(\"Imported component artifact file set is invalid\");\n\t}\n\tconst entrypointPath = join(sourceDirectory, manifest.entrypoint);\n\tconst entrypointMetadata = await lstat(entrypointPath);\n\tif (!entrypointMetadata.isFile() || entrypointMetadata.isSymbolicLink()) {\n\t\tthrow new Error(\"Imported component entrypoint is not a regular file\");\n\t}\n\tconst entrypointContent = await readFile(entrypointPath);\n\tif (sha256(entrypointContent) !== manifest.entrypointSha256) {\n\t\tthrow new Error(\"Imported component entrypoint digest mismatch\");\n\t}\n\tconst abi = registry.require(manifest.abi);\n\tif (abi.activationBoundary !== manifest.activationBoundary) {\n\t\tthrow new Error(`Imported component activation boundary does not match ABI ${manifest.abi}`);\n\t}\n\tconst ceiling = new Set(abi.capabilityCeiling);\n\tfor (const capability of manifest.capabilities) {\n\t\tif (!ceiling.has(capability)) throw new Error(`Imported component capability exceeds ABI ceiling: ${capability}`);\n\t}\n\treturn { manifest, entrypointContent };\n}\n\n/** Publish an already inspected artifact into the immutable local store. */\nexport async function publishInspectedEvoComponentArtifact(\n\tpaths: EvoPaths,\n\tartifact: InspectedEvoComponentArtifact,\n): Promise<LoadedEvoComponentArtifact> {\n\tconst manifest = artifact.manifest;\n\tconst published = await publishEvoComponentArtifact(paths, {\n\t\tid: manifest.id,\n\t\tversion: manifest.version,\n\t\tabi: manifest.abi,\n\t\tactivationBoundary: manifest.activationBoundary,\n\t\tcapabilities: manifest.capabilities,\n\t\tentrypointName: manifest.entrypoint,\n\t\tentrypointContent: artifact.entrypointContent,\n\t});\n\tif (canonicalJson(published.manifest) !== canonicalJson(manifest)) {\n\t\tthrow new Error(\"Imported component manifest identity mismatch\");\n\t}\n\treturn loadEvoComponentArtifact(paths, published.manifest.artifactDigest);\n}\n\n/**\n * Verify and publish an artifact received outside the local component store.\n * Only the manifest and its direct entrypoint are accepted; symlinks, support\n * files, digest drift, unknown ABIs, and capability-ceiling violations fail\n * before the immutable local copy is published.\n */\nexport async function importEvoComponentArtifact(\n\tpaths: EvoPaths,\n\tsourceDirectory: string,\n\tregistry: EvoAbiRegistry,\n): Promise<LoadedEvoComponentArtifact> {\n\tconst inspected = await inspectEvoComponentArtifact(sourceDirectory, registry);\n\tconst published = await publishInspectedEvoComponentArtifact(paths, inspected);\n\treturn loadEvoComponentArtifact(paths, published.manifest.artifactDigest, registry);\n}\n\nexport async function loadEvoComponentArtifact(\n\tpaths: EvoPaths,\n\tdigest: string,\n\tregistry?: EvoAbiRegistry,\n): Promise<LoadedEvoComponentArtifact> {\n\tconst directory = componentArtifactDirectory(paths, digest);\n\tconst metadata = await lstat(directory);\n\tif (!metadata.isDirectory() || metadata.isSymbolicLink()) throw new Error(\"Component artifact is not a directory\");\n\tconst entries = (await readdir(directory, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));\n\tif (entries.some((entry) => !entry.isFile() || entry.isSymbolicLink())) {\n\t\tthrow new Error(\"Component artifact contains a non-regular file\");\n\t}\n\tconst manifest = parseEvoComponentManifest(JSON.parse(await readFile(join(directory, \"manifest.json\"), \"utf8\")));\n\tif (manifest.artifactDigest !== digest)\n\t\tthrow new Error(\"Component manifest artifact digest does not match its path\");\n\tconst expectedFiles = [manifest.entrypoint, \"manifest.json\"].sort();\n\tif (entries.map((entry) => entry.name).join(\"\\n\") !== expectedFiles.join(\"\\n\")) {\n\t\tthrow new Error(\"Component artifact file set is invalid\");\n\t}\n\tconst entrypoint = join(directory, manifest.entrypoint);\n\tconst entrypointMetadata = await lstat(entrypoint);\n\tif (!entrypointMetadata.isFile() || entrypointMetadata.isSymbolicLink()) {\n\t\tthrow new Error(\"Component entrypoint is not a regular file\");\n\t}\n\tif (sha256(await readFile(entrypoint)) !== manifest.entrypointSha256) {\n\t\tthrow new Error(\"Component entrypoint digest mismatch\");\n\t}\n\tconst { artifactDigest: _, ...identity } = manifest;\n\tif (identityDigest(identity) !== digest) throw new Error(\"Component artifact identity digest mismatch\");\n\tif (registry) {\n\t\tconst abi = registry.require(manifest.abi);\n\t\tif (abi.activationBoundary !== manifest.activationBoundary) {\n\t\t\tthrow new Error(`Component activation boundary does not match ABI ${manifest.abi}`);\n\t\t}\n\t\tconst ceiling = new Set(abi.capabilityCeiling);\n\t\tfor (const capability of manifest.capabilities) {\n\t\t\tif (!ceiling.has(capability)) throw new Error(`Component capability exceeds ABI ceiling: ${capability}`);\n\t\t}\n\t}\n\treturn { directory, entrypoint, manifest };\n}\n\nexport async function validateEvoComponentSelection(\n\tpaths: EvoPaths,\n\tsurface: string,\n\tselection: EvoComponentSelection,\n\tregistry: EvoAbiRegistry,\n): Promise<LoadedEvoComponentArtifact> {\n\tregistry.validateSelection(surface, selection);\n\tconst artifact = await loadEvoComponentArtifact(paths, selection.artifactDigest, registry);\n\tif (artifact.manifest.id !== selection.id)\n\t\tthrow new Error(`Component selection id does not match artifact: ${surface}`);\n\tif (artifact.manifest.abi !== selection.abi)\n\t\tthrow new Error(`Component selection ABI does not match artifact: ${surface}`);\n\treturn artifact;\n}\n"]}