import { gt } from 'semver'; // Published identity is (Curio ID, version, target). Existing identities are immutable. export function appendRelease(catalog, manifest, artifact, releasedAt) { const next = structuredClone(catalog); let curio = next.curios.find(item => item.id === manifest.id); if (!curio) { curio = { id: manifest.id, name: manifest.name, description: manifest.description ?? '', ...(manifest.icon ? { icon: manifest.icon } : {}), versions: [] }; next.curios.push(curio); } let version = curio.versions.find(item => item.version === manifest.version); if (version && version.engine.trove !== manifest.engine.trove) { throw new Error(`${manifest.id}@${manifest.version}: published engine range cannot change; bump the plugin version`); } const existing = version?.artifacts.find(item => item.target === artifact.target); if (existing) { for (const key of ['sha256', 'size', 'url']) { if (existing[key] !== artifact[key]) throw new Error(`${manifest.id}@${manifest.version}/${artifact.target}: published ${key} differs; bump the plugin version`); } return next; } if (!version) { const newest = curio.versions.every(item => gt(manifest.version, item.version)); version = { version: manifest.version, engine: structuredClone(manifest.engine), releasedAt, artifacts: [] }; curio.versions.unshift(version); if (newest) { curio.name = manifest.name; curio.description = manifest.description ?? ''; if (manifest.icon) curio.icon = manifest.icon; } } version.artifacts.push(structuredClone(artifact)); next.generatedAt = next.generatedAt > releasedAt ? next.generatedAt : releasedAt; return next; }