76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
import { parseAllDocuments } from "yaml";
|
|
import { z } from "zod";
|
|
import type { WorkspaceDescriptor } from "./schema.js";
|
|
|
|
export const CATALOG_PATH = "thoth-workspaces.yaml";
|
|
|
|
export interface WorkspaceCatalogEntry {
|
|
id: string;
|
|
name: string;
|
|
description?: string;
|
|
}
|
|
|
|
export interface WorkspaceCatalog {
|
|
schema_version: 1;
|
|
workspaces: WorkspaceCatalogEntry[];
|
|
}
|
|
|
|
const workspaceId = z.string().trim().regex(/^[a-z][a-z0-9-]{2,62}$/, {
|
|
message: "workspace id must match ^[a-z][a-z0-9-]{2,62}$",
|
|
}).refine((value) => value !== "workspace-docs", {
|
|
message: "workspace id is reserved",
|
|
});
|
|
|
|
const catalogEntry = z.object({
|
|
id: workspaceId,
|
|
name: z.string().trim().min(1),
|
|
description: z.string().trim().min(1).optional(),
|
|
}).strict();
|
|
|
|
const catalogSchema = z.object({
|
|
schema_version: z.literal(1),
|
|
workspaces: z.array(catalogEntry),
|
|
}).strict().superRefine((catalog, context) => {
|
|
const seen = new Set<string>();
|
|
catalog.workspaces.forEach((entry, index) => {
|
|
if (seen.has(entry.id)) {
|
|
context.addIssue({
|
|
code: "custom",
|
|
path: ["workspaces", index, "id"],
|
|
message: "workspace id is duplicated in the catalog",
|
|
});
|
|
}
|
|
seen.add(entry.id);
|
|
});
|
|
});
|
|
|
|
function safeCatalogError(): Error {
|
|
return new Error("Workspace catalog is invalid");
|
|
}
|
|
|
|
export function parseWorkspaceCatalogYaml(source: string): WorkspaceCatalog {
|
|
try {
|
|
const documents = parseAllDocuments(source, { uniqueKeys: true });
|
|
if (documents.length !== 1) throw safeCatalogError();
|
|
const document = documents[0];
|
|
if (document.errors.length > 0 || document.warnings.length > 0) throw safeCatalogError();
|
|
return catalogSchema.parse(document.toJSON()) as WorkspaceCatalog;
|
|
} catch (error) {
|
|
if (error instanceof Error && error.message === "Workspace catalog is invalid") throw error;
|
|
throw safeCatalogError();
|
|
}
|
|
}
|
|
|
|
export function assertCatalogMatchesDescriptor(
|
|
entry: WorkspaceCatalogEntry,
|
|
workspace: WorkspaceDescriptor,
|
|
): void {
|
|
if (
|
|
entry.id !== workspace.workspace.id
|
|
|| entry.name !== workspace.workspace.name
|
|
|| entry.description !== workspace.workspace.description
|
|
) {
|
|
throw new Error("Workspace catalog metadata does not match descriptor");
|
|
}
|
|
}
|