refactor: make workspace descriptors schema v3 only

This commit is contained in:
2026-08-10 20:21:20 +02:00
parent 5d016ce635
commit 512b0261b1
12 changed files with 187 additions and 295 deletions
+15 -7
View File
@@ -15,11 +15,10 @@ import {
import { resolveRuntimeBindings } from "../workspaces/bindings.js";
import { buildInstallationContract, renderWorkspaceDocs } from "../workspaces/contracts.js";
import {
isCanonicalWorkspace,
parseWorkspaceYaml,
serializeWorkspaceYaml,
validateCanonicalWorkspace,
validateOperationalWorkspace,
validateWorkspaceDescriptor,
type CanonicalWorkspace,
type WorkspaceDescriptor,
} from "../workspaces/schema.js";
@@ -177,7 +176,7 @@ async function importDraft(source: Buffer, config: WorkspaceRegistryConfig): Pro
}
try {
const descriptor = parseWorkspaceYaml(utf8(files["workspace.yaml"]));
const workspace = validateCanonicalWorkspace(descriptor);
const workspace = validateWorkspaceDescriptor(descriptor);
const docs = renderWorkspaceDocs(workspace);
if (
workspace.workspace.id !== manifest.workspace_id
@@ -231,6 +230,14 @@ function workspaceErrorStatus(code: keyof typeof SAFE_MESSAGES): number {
return 400;
}
function validatedWorkspace(value: unknown): WorkspaceDescriptor | undefined {
try {
return validateWorkspaceDescriptor(value);
} catch {
return undefined;
}
}
function errorReply(reply: FastifyReply, error: unknown) {
const code = workspaceErrorCode(error);
const body: Record<string, unknown> = { code, message: SAFE_MESSAGES[code] };
@@ -253,7 +260,8 @@ function errorReply(reply: FastifyReply, error: unknown) {
) body[key] = revision;
}
for (const key of ["base", "local", "remote"] as const) {
if (conflict[key] && isCanonicalWorkspace(conflict[key] as WorkspaceDescriptor)) body[key] = conflict[key];
const workspace = validatedWorkspace(conflict[key]);
if (workspace) body[key] = workspace;
}
}
return reply.code(workspaceErrorStatus(code)).send(body);
@@ -262,7 +270,7 @@ function errorReply(reply: FastifyReply, error: unknown) {
function publishRequest(value: unknown): PublishWorkspaceRequest {
const parsed = publishPayload.parse(value);
if (parsed.action === "delete") return parsed;
return { ...parsed, workspace: validateCanonicalWorkspace(parsed.workspace) };
return { ...parsed, workspace: validateWorkspaceDescriptor(parsed.workspace) };
}
export function workspaceRoutes(app: FastifyInstance, deps: WorkspaceRoutesDeps): void {
@@ -321,7 +329,7 @@ export function workspaceRoutes(app: FastifyInstance, deps: WorkspaceRoutesDeps)
app.post("/workspaces/validate", async (request, reply) => {
try {
const { workspace } = workspacePayload.parse(request.body);
const canonical = validateCanonicalWorkspace(workspace);
const canonical = validateWorkspaceDescriptor(workspace);
return { workspace: canonical, contract: buildInstallationContract(canonical) };
} catch (error) {
return errorReply(reply, error);
@@ -365,7 +373,7 @@ export function workspaceRoutes(app: FastifyInstance, deps: WorkspaceRoutesDeps)
try {
const { id } = z.object({ id: workspaceId }).parse(request.params);
const { workspace } = await deps.registry.read(id);
const canonical = validateCanonicalWorkspace(workspace);
const canonical = validateWorkspaceDescriptor(workspace);
const bundle = await exportBundle(canonical);
return reply
.type("application/zip")