/* RFC-0481: content migrator — creates the PBP business singleton (business-profile/{lang}/business.md) from legacy business/{lang}/company.md frontmatter. This completes the RFC-0471 content migration that was left incomplete. Does not migrate content references ({business.*.*}) — that is a separate future RFC. Does not delete the legacy business/ directory — that is a separate future RFC. Does not create de/ locale PBP entities beyond business.md — operator-authored content. RFC-0481: initial content migrator — create PBP business singleton from legacy company.md. */ import path from "node:path"; import type { Migrator, SternsystemData, MigrationContext } from "./types.ts"; import { MigrationError } from "./types.ts"; import { parseFrontmatter, serializeFrontmatter } from "./yaml-utils.ts"; import { createDefaultIO, type WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; export const RFC_0481_MIGRATOR_ID = "rfc-0481"; interface LegacyCompanyFrontmatter { brand?: { name?: string; author?: string }; name?: string; description?: string; mission?: string; foundingYear?: string; } function mapCompanyToBusiness( frontmatter: Record, _lang: string, ): Record { const company = frontmatter as LegacyCompanyFrontmatter; const name = company.brand?.name ?? company.name ?? "Warpgogol"; const description = company.description ?? ""; const mission = company.mission ?? ""; const yearEstablished = company.foundingYear ? parseInt(company.foundingYear, 10) : undefined; const business: Record = { schema: "pbp/business@1", id: "https://warpgogol.com/id/business", type: "business", status: "published", name, description, mission, brandRefs: { default: { ref: "https://warpgogol.com/id/brand", expectedType: "brand", }, }, legalIdentityRef: { ref: "https://warpgogol.com/id/legal-identity", expectedType: "legal-identity", }, placeRefs: { office: { ref: "https://warpgogol.com/id/places/backnang", expectedType: "place", }, }, contactPointRefs: { default: { ref: "https://warpgogol.com/id/contact-points/general-email", expectedType: "contact-point", }, }, webPresenceRefs: { default: { ref: "https://warpgogol.com/id/web-presences/primary", expectedType: "web-presence", }, }, governance: { authorityRef: "https://warpgogol.com/id/business", effectiveFrom: "2026-01-01", reviewEvery: "P1Y", }, }; if (yearEstablished !== undefined && !Number.isNaN(yearEstablished)) { business.yearEstablished = yearEstablished; } return business; } async function isPbpBusinessFile( filePath: string, io: WorkspaceIO = createDefaultIO().io, ): Promise { try { const raw = await io.readFile(filePath); return raw.includes("schema: pbp/business@1") || raw.includes('schema: "pbp/business@1"'); } catch { return false; } } async function transformBusinessSingleton( data: SternsystemData, ctx: MigrationContext, io: WorkspaceIO = createDefaultIO().io, ): Promise { const locales = await readLocalesSafe(data.rootPath, ctx); for (const lang of locales) { const targetPath = path.join( data.rootPath, "src", "content", "business-profile", lang, "business.md", ); if (await isPbpBusinessFile(targetPath)) { ctx.logger.info( `[migrator rfc-0481] business-profile/${lang}/business.md already PBP — skip`, ); continue; } const sourcePath = path.join(data.rootPath, "src", "content", "business", lang, "company.md"); let sourceRaw: string; try { sourceRaw = await io.readFile(sourcePath); } catch { throw new MigrationError( RFC_0481_MIGRATOR_ID, `src/content/business/${lang}/company.md`, "", "source file not found — cannot create PBP business singleton without legacy company.md", ); } const { frontmatter } = parseFrontmatter(sourceRaw); const business = mapCompanyToBusiness(frontmatter, lang); const output = serializeFrontmatter(business); await io.mkdir(path.dirname(targetPath)); await io.writeFile(targetPath, output); ctx.logger.info(`[migrator rfc-0481] created business-profile/${lang}/business.md`); } return data; } export async function readLocalesSafe( rootPath: string, ctx: MigrationContext, io: WorkspaceIO = createDefaultIO().io, ): Promise { const systemMdPath = path.join(rootPath, "src", "content", "system.md"); try { const raw = await io.readFile(systemMdPath); const { frontmatter } = parseFrontmatter(raw); const i18n = frontmatter.i18n as { supported?: Record; default?: string } | undefined; if (i18n?.supported && typeof i18n.supported === "object") { return Object.keys(i18n.supported); } if (i18n?.default) { return [i18n.default]; } } catch { ctx.logger.info(`[migrator rfc-0481] no system.md found — defaulting to ["de"]`); } return ["de"]; } export const rfc0481Migrator: Migrator = { id: RFC_0481_MIGRATOR_ID, fromVersion: "4.4.0", toVersion: "4.5.0", description: "Create PBP business singleton from legacy business/company.md", transform: async (data, ctx) => { return transformBusinessSingleton(data, ctx); }, };