← Files RivetARCHIVED FILE

node_modules/rivet-design/dist/services/ProjectLibrary.js

11.7 KB · Nov 7, 2026 · 13:16 UTC

↓ Download file

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectLibrary = exports.currentLibraryRoot = void 0;
const libraryErrors_1 = require("./libraryErrors");
const fs_1 = __importDefault(require("fs"));
const installationChannel_1 = require("../install/installationChannel");
const path_1 = __importDefault(require("path"));
const ProjectRegistry_1 = require("./ProjectRegistry");
const ProjectImport_1 = require("./ProjectImport");
const BundleMetadataStore_1 = require("../storage/library/BundleMetadataStore");
const ProjectStorage_1 = require("./ProjectStorage");
const TreeStore_1 = require("./TreeStore");
const VariantHistoryService_1 = require("./VariantHistoryService");
const historyRoots_1 = require("./historyRoots");
const errorMessage_1 = require("../utils/errorMessage");
const currentLibraryRoot = (env = process.env) => {
    const channel = (0, installationChannel_1.installationChannel)(env);
    return env.RIVET_LIBRARY_ROOT
        ? path_1.default.resolve(env.RIVET_LIBRARY_ROOT)
        : (0, ProjectRegistry_1.libraryRoot)({ channel });
};
exports.currentLibraryRoot = currentLibraryRoot;
class ProjectLibrary {
    registry;
    isReadOnly;
    owners = new Map();
    issues = new Map();
    historyIssues = new Map();
    isClosed = false;
    constructor(registry, isReadOnly = false) {
        this.registry = registry;
        this.isReadOnly = isReadOnly;
    }
    static async open(root = (0, exports.currentLibraryRoot)()) {
        return new ProjectLibrary(await ProjectRegistry_1.ProjectRegistry.open(root));
    }
    static openReadOnly(root = (0, exports.currentLibraryRoot)()) {
        const registry = ProjectRegistry_1.ProjectRegistry.openReadOnly(root);
        return registry ? new ProjectLibrary(registry, true) : null;
    }
    async prepare(projectPath) {
        const project = this.registry.register({
            projectPath,
            isExplicitOpen: true,
        });
        for (const nested of (0, historyRoots_1.nestedFreshSessionWorkdirs)(project.projectPath))
            this.registry.attachHistoryRoot({
                projectId: project.id,
                projectPath: nested,
            });
        let migration;
        try {
            migration = await (0, ProjectImport_1.importProject)({
                registry: this.registry,
                projectId: project.id,
            });
        }
        catch (error) {
            migration = {
                status: 'deferred',
                reason: (0, errorMessage_1.errorMessage)(error),
            };
        }
        if (project.storageKind !== this.registry.get(project.id).storageKind)
            await this.invalidate(project.id);
        if (migration.status === 'diverged' || migration.status === 'deferred')
            this.issues.set(project.id, migration.reason ??
                'Preserved source history differs from the imported bundle');
        return { project: this.registry.get(project.id), migration };
    }
    async relink(args) {
        const project = this.registry.relink(args);
        await this.invalidate(project.id);
        this.issues.delete(project.id);
        this.historyIssues.delete(project.id);
        return project;
    }
    async invalidate(projectId) {
        const pending = this.owners.get(projectId);
        this.owners.delete(projectId);
        if (!pending)
            return;
        const owner = await pending;
        owner.history.metadata.closeAll();
        owner.bundle?.close();
    }
    listProjects() {
        return this.registry.list().map((project) => ({
            ...project,
            issue: this.historyIssues.get(project.id) ?? this.issues.get(project.id),
            historyIssue: this.historyIssues.get(project.id),
        }));
    }
    async snapshot() {
        const variants = await this.listVariants();
        const projects = this.listProjects();
        const visibleIds = new Set(projects.map(({ id }) => id));
        return {
            projects,
            variants: variants
                .filter(({ projectId }) => visibleIds.has(projectId))
                .map(({ projectId, manifest, hasStaticPreview }) => ({
                projectId,
                sessionId: manifest.sessionId,
                variantId: manifest.variantId,
                label: manifest.label,
                brief: manifest.brief,
                createdAt: manifest.createdAt,
                hasStaticPreview,
                hasInteractivePreview: manifest.serve?.kind === 'dev-server',
            })),
        };
    }
    async updateVariant(args) {
        const found = await this.variant(args);
        if (!found)
            throw new libraryErrors_1.LibraryError('variant_not_found', 'Unknown saved direction');
        const target = { ...args, projectPath: found.projectPath };
        if (args.label !== undefined)
            await found.history.renameVariant({ ...target, label: args.label });
        if (args.status)
            await found.history.markStatus({ ...target, status: args.status });
    }
    async assertNavigationTarget(target) {
        if (target.projectId)
            this.registry.get(target.projectId);
        if (target.projectId &&
            target.sessionId &&
            target.variantId &&
            !(await this.variant({
                projectId: target.projectId,
                sessionId: target.sessionId,
                variantId: target.variantId,
            })))
            throw new libraryErrors_1.LibraryError('variant_not_found', 'Unknown saved direction');
    }
    async history(projectId) {
        return (await this.owner(projectId)).history;
    }
    async attachGeneratedRoot({ projectId, projectPath, }) {
        fs_1.default.mkdirSync(projectPath, { recursive: true });
        this.registry.attachHistoryRoot({ projectId, projectPath });
        const owner = await this.owner(projectId);
        if (!owner.bundle)
            return;
        const dataDirectory = this.registry.bundleDirectory(projectId);
        for (const root of new Set([
            path_1.default.resolve(projectPath),
            fs_1.default.realpathSync(projectPath),
        ])) {
            owner.storage.attach({
                projectPath: root,
                dataDirectory,
                cacheDirectory: path_1.default.join(this.registry.root, 'cache', projectId),
            });
        }
    }
    async listProject(projectId) {
        const project = this.registry.get(projectId);
        if (!project.isAvailable && project.storageKind === 'project')
            throw new Error('Working folder and project-local history are unavailable');
        const history = await this.history(projectId);
        const roots = project.storageKind === 'bundle'
            ? [project.projectPath]
            : this.registry.historyRoots(projectId);
        const entries = (await Promise.all(roots.map(async (projectPath) => (await (this.isReadOnly
            ? history.listVariants({ projectPath })
            : history.listVariantsFromIndex({ projectPath }))).map((entry) => ({ ...entry, projectId, projectPath }))))).flat();
        const unique = new Map();
        for (const entry of entries) {
            if (entry.manifest.status === 'removed' ||
                entry.manifest.status === 'cancelled')
                continue;
            const key = `${entry.manifest.sessionId}:${entry.manifest.variantId}`;
            const existing = unique.get(key);
            if (existing &&
                JSON.stringify(existing.manifest) !== JSON.stringify(entry.manifest))
                throw new Error('Conflicting history identities across attached roots');
            unique.set(key, entry);
        }
        return [...unique.values()];
    }
    async listVariants(projectId) {
        if (projectId)
            return this.listProject(projectId);
        const projects = this.registry.list();
        const results = await Promise.allSettled(projects.map((project) => this.listProject(project.id)));
        return results
            .flatMap((result, index) => {
            if (result.status === 'fulfilled') {
                this.historyIssues.delete(projects[index].id);
                return result.value;
            }
            this.historyIssues.set(projects[index].id, result.reason instanceof Error
                ? result.reason.message
                : String(result.reason));
            return [];
        })
            .sort((a, b) => b.manifest.createdAt.localeCompare(a.manifest.createdAt));
    }
    async findVariant(reference, read) {
        const project = this.registry.get(reference.projectId);
        const history = await this.history(project.id);
        const roots = project.storageKind === 'bundle'
            ? [project.projectPath]
            : this.registry.historyRoots(project.id);
        for (const projectPath of roots) {
            const value = await read(history, projectPath);
            if (value)
                return { value, projectPath, history };
        }
        return null;
    }
    async variantSummary(reference) {
        const found = await this.findVariant(reference, (history, projectPath) => history.getVariantSummary({ ...reference, projectPath }));
        return found
            ? { summary: found.value, projectPath: found.projectPath }
            : null;
    }
    async variant(reference) {
        const found = await this.findVariant(reference, (history, projectPath) => history.getVariant({ ...reference, projectPath }));
        return found
            ? {
                detail: found.value,
                projectPath: found.projectPath,
                history: found.history,
            }
            : null;
    }
    close() {
        if (this.isClosed)
            return;
        this.isClosed = true;
        for (const owner of this.owners.values())
            void owner.then((value) => {
                value.history.metadata.closeAll();
                value.bundle?.close();
            }, () => undefined);
        this.registry.close();
    }
    owner(projectId) {
        const existing = this.owners.get(projectId);
        if (existing)
            return existing;
        const pending = this.createOwner(projectId);
        this.owners.set(projectId, pending);
        void pending.catch(() => this.owners.delete(projectId));
        return pending;
    }
    async createOwner(projectId) {
        const project = this.registry.get(projectId);
        const storage = new ProjectStorage_1.ProjectStorage();
        let bundle;
        if (project.storageKind === 'bundle') {
            const dataDirectory = this.registry.bundleDirectory(projectId);
            if (!fs_1.default.existsSync(path_1.default.join(dataDirectory, 'metadata.sqlite')))
                throw new Error('The saved project database is missing');
            const roots = this.registry.historyRoots(projectId);
            bundle = this.isReadOnly
                ? BundleMetadataStore_1.BundleMetadataStore.openReadOnly({
                    directory: dataDirectory,
                    project,
                })
                : await BundleMetadataStore_1.BundleMetadataStore.open({ directory: dataDirectory, project });
            for (const projectPath of roots) {
                storage.attach({
                    projectPath,
                    dataDirectory,
                    cacheDirectory: path_1.default.join(this.registry.root, 'cache', projectId),
                });
            }
        }
        const history = new VariantHistoryService_1.VariantHistoryService(new TreeStore_1.TreeStore(storage), undefined, { bundle });
        return { history, storage, bundle };
    }
}
exports.ProjectLibrary = ProjectLibrary;
//# sourceMappingURL=ProjectLibrary.js.map

SHA-256: b03c93a8d23b1da6f9feff7bf8ba1fde114eab5ef4cbbb50662574702d4f452e