/*
RFC-0480: Declarative Layer C external-surface contracts — URL schema,
JSON-LD types, and sitemap shape. Consumed by `surface.contract.validate`
in @warpgogol/site-kernel-handoff and by contract tests in @warpgogol/werkstatt-shared.
- Do not generate C-surfaces here — this module only declares the contract.
- Do not import from @warpgogol/site-kernel-handoff or any app package.
- RFC-0480: initial external-surfaces contracts (url-schema, jsonld-types, sitemap-shape).
- RFC-0498: add surfacePolicy to jsonldTypesContract — per-depth required/prohibited JSON-LD types for surface pages.
- RFC-0499: add mediaLeakagePolicy to jsonldTypesContract — prohibited strings, matching strategies, required labels for surface pages.
- RFC-1149: migrate ambient filesystem and process IO to the WorkspaceIO port across engine and shared
*/
import path from "node:path";
import { createDefaultIO } from "../../kernel/workspace-io.ts";
import { fileURLToPath } from "node:url";
import { z } from "zod";
import { parse as parseYaml } from "yaml";
// ---------------------------------------------------------------------------
// Schema definitions
// ---------------------------------------------------------------------------
const routeParamSchema = z.object({
optional: z.boolean().optional(),
enum: z.array(z.string()).optional(),
type: z.string().optional(),
from: z.string().optional(),
});
const routePatternSchema = z.object({
pattern: z.string(),
params: z.record(z.string(), routeParamSchema).optional(),
generated: z.boolean(),
});
const localePrefixSchema = z.object({
strategy: z.string(),
default: z.string(),
});
const urlSchemaContract = z.object({
routePatterns: z.array(routePatternSchema),
localePrefix: localePrefixSchema,
});
const jsonldTypeSchema = z.object({
"@type": z.string(),
required: z.array(z.string()),
optional: z.array(z.string()),
});
const surfacePolicyEntrySchema = z.object({
surface: z.string(),
depth: z.number(),
requiredTypes: z.array(z.string()),
prohibitedTypes: z.array(z.string()),
});
const mediaLeakageProhibitedStringSchema = z.object({
pattern: z.string(),
matchingStrategy: z.enum(["exact", "whole-word", "context-aware"]),
contextSelector: z.string().optional(),
reason: z.string(),
});
const mediaLeakageRequiredLabelSchema = z.object({
label: z.string(),
lang: z.string(),
});
const mediaLeakagePolicySchema = z.object({
prohibitedStrings: z.array(mediaLeakageProhibitedStringSchema),
requiredLabels: z.array(mediaLeakageRequiredLabelSchema),
requiredLinkPattern: z.string(),
aiImageAttribute: z.string(),
});
const jsonldTypesContract = z.object({
types: z.array(jsonldTypeSchema),
surfacePolicy: z.array(surfacePolicyEntrySchema).optional(),
mediaLeakagePolicy: mediaLeakagePolicySchema.optional(),
});
const sitemapAlternatesSchema = z.object({
required: z.array(z.string()),
optional: z.array(z.string()),
});
const sitemapShapeContract = z.object({
urlEntry: z.object({
required: z.array(z.string()),
optional: z.array(z.string()),
}),
alternates: sitemapAlternatesSchema,
});
// ---------------------------------------------------------------------------
// Contract loading
// ---------------------------------------------------------------------------
const __dirname = path.dirname(fileURLToPath(import.meta.url));
async function loadYaml(filename: string): Promise {
const filePath = path.join(__dirname, filename);
const raw = await createDefaultIO().io.readFile(filePath);
return parseYaml(raw) as T;
}
export type UrlSchemaContract = z.infer;
export type JsonldTypesContract = z.infer;
export type SitemapShapeContract = z.infer;
export type JsonldSurfacePolicyEntry = z.infer;
export type MediaLeakageProhibitedString = z.infer;
export type MediaLeakageRequiredLabel = z.infer;
export type MediaLeakagePolicy = z.infer;
/** Parsed and validated URL schema contract. */
export const urlSchema: UrlSchemaContract = urlSchemaContract.parse(
await loadYaml("url-schema.yaml"),
);
/** Parsed and validated JSON-LD types contract. */
export const jsonldTypes: JsonldTypesContract = jsonldTypesContract.parse(
await loadYaml("jsonld-types.yaml"),
);
/** Parsed and validated sitemap shape contract. */
export const sitemapShape: SitemapShapeContract = sitemapShapeContract.parse(
await loadYaml("sitemap-shape.yaml"),
);
// ---------------------------------------------------------------------------
// Re-exports for consumers
// ---------------------------------------------------------------------------
export {
urlSchemaContract,
jsonldTypesContract,
sitemapShapeContract,
routePatternSchema,
jsonldTypeSchema,
surfacePolicyEntrySchema,
mediaLeakageProhibitedStringSchema,
mediaLeakageRequiredLabelSchema,
mediaLeakagePolicySchema,
};
/** All declarative C-contract files in this directory. */
export const EXTERNAL_SURFACE_CONTRACT_FILES = [
"url-schema.yaml",
"jsonld-types.yaml",
"sitemap-shape.yaml",
] as const;