UNPKG

trellis

Version:

Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications

123 lines (120 loc) 4.35 kB
// src/registry/version-utils.ts function compareVersions(a, b) { const pa = a.split(".").map(Number); const pb = b.split(".").map(Number); for (let i = 0; i < Math.max(pa.length, pb.length); i++) { const na = pa[i] ?? 0; const nb = pb[i] ?? 0; if (na > nb) return 1; if (na < nb) return -1; } return 0; } function satisfies(version, range) { if (range === "*" || range === "latest") return true; if (range.startsWith(">=")) return compareVersions(version, range.slice(2)) >= 0; if (range.startsWith(">")) return compareVersions(version, range.slice(1)) > 0; if (range.startsWith("<=")) return compareVersions(version, range.slice(2)) <= 0; if (range.startsWith("<")) return compareVersions(version, range.slice(1)) < 0; if (range.startsWith("^")) { const min = range.slice(1); const parts = min.split("."); const major = parseInt(parts[0], 10); if (parts.length >= 2) { const nextMajor = `${major + 1}.0.0`; return compareVersions(version, min) >= 0 && compareVersions(version, nextMajor) < 0; } return version.startsWith(`${major}.`) || version === min; } if (range.startsWith("~")) { const min = range.slice(1); const parts = min.split("."); if (parts.length >= 2) { const nextMinor = `${parts[0]}.${parseInt(parts[1], 10) + 1}.0`; return compareVersions(version, min) >= 0 && compareVersions(version, nextMinor) < 0; } return version === min; } return version === range; } function latestSatisfying(versions, range) { const matching = versions.filter((v) => satisfies(v, range)); if (matching.length === 0) return null; matching.sort((a, b) => compareVersions(b, a)); return matching[0]; } // src/registry/lockfile.ts import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync } from "fs"; import { join, dirname } from "path"; import { createHash } from "crypto"; var CURRENT_VERSION = 1; var LOCKFILE_VERSION = "1.0.0"; var LOCKFILE_PATH = ".trellis/deps.json"; function computeContentHash(body) { const canonical = JSON.stringify(JSON.parse(body), Object.keys(JSON.parse(body)).sort()); return `sha256:${createHash("sha256").update(canonical).digest("hex")}`; } function readLockfile(rootPath) { const filePath = join(rootPath, LOCKFILE_PATH); if (!existsSync(filePath)) return null; const raw = readFileSync(filePath, "utf-8"); const data = JSON.parse(raw); validateLockfile(data); return data; } function writeLockfile(rootPath, data) { const filePath = join(rootPath, LOCKFILE_PATH); const dir = dirname(filePath); if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); const tmpPath = filePath + ".tmp"; writeFileSync(tmpPath, JSON.stringify(data, null, 2) + "\n"); renameSync(tmpPath, filePath); } function validateLockfile(data) { if (typeof data !== "object" || data === null) throw new Error("Invalid lockfile: not an object"); const d = data; if (d.version !== CURRENT_VERSION) throw new Error(`Invalid lockfile version: ${d.version}`); if (d.lockfileVersion !== LOCKFILE_VERSION) throw new Error(`Invalid lockfile version: ${d.lockfileVersion}`); if (typeof d.resolved !== "object" || d.resolved === null) throw new Error("Invalid lockfile: resolved is not an object"); if (typeof d.root !== "object" || d.root === null) throw new Error("Invalid lockfile: root is not an object"); } function createLockfile() { return { version: CURRENT_VERSION, lockfileVersion: LOCKFILE_VERSION, resolved: {}, root: { depends: {} } }; } function removeFromLockfile(lockfile, name) { if (!lockfile.resolved[name]) return false; delete lockfile.resolved[name]; return true; } function findDependents(lockfile, name) { const dependents = []; const pkg = lockfile.resolved[name]; if (!pkg) return dependents; const schemaIds = new Set(Object.keys(pkg.schemas)); for (const [pkgName, otherPkg] of Object.entries(lockfile.resolved)) { if (pkgName === name) continue; for (const schemaId of Object.keys(otherPkg.schemas)) { if (schemaIds.has(schemaId)) { dependents.push(pkgName); break; } } } return dependents; } export { compareVersions, satisfies, latestSatisfying, computeContentHash, readLockfile, writeLockfile, createLockfile, removeFromLockfile, findDependents };