/*
Maintains packages/share/src/schemas/navigation.ts as an authored share authored module so agents can evolve it without rediscovering local boundaries.
- Do not handle runtime resolution or feature graph integration.
- Do not contain business logic.
- Do not handle backward compatibility (RFC-0046: no backward compatibility needed).
- RFC-1097: step 6 — compass.migrate codemod run
Mechanical v1 to v2 header migration across the workspace: 942 files rewritten — CHANGE_SUMMARY windows collapsed into history, forbidden v1 blocks stripped, KEY_DECISIONS seeded from @ai-invariant comments (5 files) or TODO placeholders (103 files), blocks reordered to canonical order.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
- RFC-1257: structured CtaTarget + scenario-plumbed contact links
*/
import { z } from "zod";
import { TARGET_QUERY_KEY_PATTERN, TARGET_QUERY_VALUE_PATTERN } from "../referral/patterns.ts";
/**
* Creates a Zod enum for navigation groups from an array of allowed group names.
* Apps call this in their content.config.ts or navigation.ts with their specific groups.
*
* @param groups - Array of allowed navigation group names for the app
* @returns Zod enum schema for navigation groups
*/
export function createNavigationGroupEnum(groups: string[]) {
return z.enum(groups as [string, ...string[]]);
}
// Default groups for backward compatibility with existing apps
export const defaultNavigationGroups = ["navigation", "legal", "contact"] as const;
/**
* Creates a navigation target schema with configurable group validation.
*
* @param groupEnum - Zod enum schema for allowed navigation groups
* @returns Zod schema for a single navigation target
*/
export const navigationTargetSchema = (groupEnum: z.ZodEnum>) =>
z.object({
id: z.string(),
label: z.string(),
semanticTarget: z.union([
z.object({
kind: z.literal("internal"),
pageId: z.string(),
// RFC-0048: anchor may be a plain string (language-neutral) or a
// language-keyed record (e.g. { de: "unser-ansatz", en: "our-approach" })
anchor: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
// RFC-1257: bounded query map — emitted as path?k=v before #anchor.
query: z
.record(
z.string().regex(TARGET_QUERY_KEY_PATTERN),
z.string().regex(TARGET_QUERY_VALUE_PATTERN),
)
.optional(),
}),
z.object({
kind: z.literal("external"),
href: z.url(),
}),
]),
routeSlug: z.string().optional(),
group: groupEnum.optional(),
});
/**
* Creates a navigation schema with configurable group validation.
*
* @param groupEnum - Zod enum schema for allowed navigation groups
* @returns Zod schema for the full navigation structure
*/
export const navigationSchema = (groupEnum: z.ZodEnum>) =>
z.object({
targets: z.array(navigationTargetSchema(groupEnum)),
});
/**
* Type helper for navigation target.
* The group field is loosely typed (string) here; per-app strict validation comes
* from the runtime Zod schema returned by navigationTargetSchema(groupEnum).
*/
export type NavigationTarget = z.infer>;