← Files RivetARCHIVED FILE
node_modules/rivet-design/dist/services/ProjectLibrary.js
11.7 KB · Nov 5, 2026 · 12:09 UTC
"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.mapSHA-256: b03c93a8d23b1da6f9feff7bf8ba1fde114eab5ef4cbbb50662574702d4f452e