import { z } from 'zod'; import type { PortfolioContent, PublicAssetManifest, ProductionInputs, SiteConfig } from './types.ts'; const canonicalNavigationIds = [ 'start', 'profile', 'experience', 'infrastructure', 'selected-builds', 'additional-work', 'toolkit', 'contact', ] as const; const socialHosts = { GitHub: 'github.com', GitLab: 'gitlab.com', LinkedIn: 'linkedin.com', } as const; const approvedPrivateProjectTitles = [ 'Private self-hosted medication tracker', 'Private healthcare platform', 'Private game prototype', ] as const; const hasUnpairedSurrogate = (value: string): boolean => { for (let index = 0; index < value.length; index += 1) { const codeUnit = value.charCodeAt(index); if (codeUnit >= 0xd800 && codeUnit <= 0xdbff) { const next = value.charCodeAt(index + 1); if (next < 0xdc00 || next > 0xdfff) return true; index += 1; } else if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff) { return true; } } return false; }; const hasVisibleCopy = (value: string): boolean => value.trim().length > 0 && value === value.normalize('NFC') && !hasUnpairedSurrogate(value) && !/[\u0000-\u001f\u007f]/u.test(value); const visibleString = z.string().refine(hasVisibleCopy, 'Visible copy must be non-empty, well-formed Unicode, NFC-normalized, and control-character free.'); const opaqueId = z.string().regex(/^[a-z][a-z0-9-]{0,63}$/, 'IDs must be generated safe identifiers.'); const httpUrl = z.string().refine((value) => { if (value !== value.normalize('NFC') || hasUnpairedSurrogate(value) || /[\u0000-\u001f\u007f]/u.test(value)) { return false; } try { const url = new URL(value); const authority = /^https:\/\/([^/?#]+)/u.exec(value)?.[1]; return url.protocol === 'https:' && !url.username && !url.password && !url.search && !url.hash && authority !== undefined && !authority.includes(':'); } catch { return false; } }, 'URL must be an absolute HTTPS URL without credentials, query, or fragment.'); const navigationItemSchema = z.object({ id: z.enum(canonicalNavigationIds), label: visibleString, }).strict(); const projectSchema = z.object({ id: opaqueId, title: visibleString, visibility: z.enum(['public', 'private']), maturity: z.enum(['learning', 'experimental', 'prototype', 'work-in-progress', 'active']), summary: visibleString, problem: visibleString, contribution: visibleString, technicalDecisions: z.array(visibleString).min(1), outcome: visibleString, url: httpUrl.optional(), assetIds: z.array(opaqueId), }).strict().superRefine((project, context) => { if (project.visibility === 'private' && project.url !== undefined) { context.addIssue({ code: 'custom', message: 'A private project may not include a url.', path: ['url'] }); } if (project.visibility === 'private' && !approvedPrivateProjectTitles.includes(project.title as (typeof approvedPrivateProjectTitles)[number])) { context.addIssue({ code: 'custom', message: 'Private project title must use an approved generic title.', path: ['title'] }); } }); const portfolioContentSchema = z.object({ identity: z.object({ name: z.literal('Luca Pîslaru'), role: z.literal('System Administrator & Full-Stack Developer'), valueProposition: visibleString, }).strict(), navigation: z.array(navigationItemSchema).length(8), profile: z.object({ introduction: visibleString, screeningFacts: z.array(z.object({ label: visibleString, value: visibleString }).strict()), }).strict(), experience: z.array(z.object({ id: opaqueId, role: visibleString, organization: visibleString, dates: visibleString, location: visibleString.optional(), highlights: z.array(visibleString).min(2).max(4), }).strict()).min(1), systemsOperations: z.object({ id: opaqueId, title: z.literal('Professional systems operations'), summary: visibleString, evidence: z.array(visibleString).min(1), }).strict(), infrastructure: z.object({ id: opaqueId, title: z.literal('Secure self-hosted platform and lifecycle'), summary: visibleString, bands: z.array(z.object({ id: opaqueId, title: visibleString, summary: visibleString }).strict()).length(4), }).strict(), projects: z.object({ selected: z.array(projectSchema).length(3), additional: z.array(projectSchema).min(1), }).strict(), toolkit: z.array(z.object({ title: visibleString, technologies: z.array(z.object({ name: visibleString, assetId: opaqueId.optional() }).strict()).min(1), }).strict()).min(1), contact: z.object({ email: z.email(), social: z.array(z.object({ service: z.enum(['GitHub', 'GitLab', 'LinkedIn']), url: httpUrl, }).strict()).superRefine((links, context) => { for (const [index, link] of links.entries()) { const host = new URL(link.url).hostname; if (host !== socialHosts[link.service]) { context.addIssue({ code: 'custom', message: `Social URL must use the approved ${link.service} host.`, path: [index, 'url'] }); } } }), cvMessage: visibleString, }).strict(), metadata: z.object({ title: visibleString, description: visibleString, socialPreviewAssetId: opaqueId.optional(), }).strict(), sceneDescription: visibleString, }).strict().superRefine((content, context) => { const navigationIds = content.navigation.map((item) => item.id); if (new Set(navigationIds).size !== canonicalNavigationIds.length || canonicalNavigationIds.some((id) => !navigationIds.includes(id))) { context.addIssue({ code: 'custom', message: 'Navigation must contain each canonical section ID exactly once.', path: ['navigation'] }); } const projects = [...content.projects.selected, ...content.projects.additional]; const projectIds = projects.map((project) => project.id); if (new Set(projectIds).size !== projectIds.length) { context.addIssue({ code: 'custom', message: 'Project IDs must be unique.', path: ['projects'] }); } if (content.projects.selected.filter((project) => project.maturity === 'experimental').length !== 1) { context.addIssue({ code: 'custom', message: 'Selected builds must include exactly one supporting experimental research panel.', path: ['projects', 'selected'] }); } const experienceIds = content.experience.map((record) => record.id); if (new Set(experienceIds).size !== experienceIds.length) { context.addIssue({ code: 'custom', message: 'Experience IDs must be unique.', path: ['experience'] }); } const bandIds = content.infrastructure.bands.map((band) => band.id); if (new Set(bandIds).size !== bandIds.length) { context.addIssue({ code: 'custom', message: 'Infrastructure band IDs must be unique.', path: ['infrastructure', 'bands'] }); } }); const mediaTypes = ['image/avif', 'image/jpeg', 'image/png', 'image/svg+xml', 'image/webp', 'video/mp4', 'video/webm'] as const; const fileExtensionByMediaType: Record<(typeof mediaTypes)[number], string> = { 'image/avif': 'avif', 'image/jpeg': 'jpg', 'image/png': 'png', 'image/svg+xml': 'svg', 'image/webp': 'webp', 'video/mp4': 'mp4', 'video/webm': 'webm', }; const publicAssetSchema = z.object({ id: opaqueId, fileName: z.string().regex(/^[a-z0-9][a-z0-9-]*\.(avif|jpg|png|svg|webp|mp4|webm)$/, 'Asset filenames must be generated safe filenames.'), sha256: z.string().regex(/^[a-f0-9]{64}$/, 'Asset sha256 must be lowercase hexadecimal.'), mediaType: z.enum(mediaTypes), width: z.number().int().positive().nullable(), height: z.number().int().positive().nullable(), durationMs: z.number().int().positive().nullable(), rightsHolder: visibleString, license: visibleString, attribution: visibleString, }).strict().superRefine((asset, context) => { if (!asset.fileName.endsWith(`.${fileExtensionByMediaType[asset.mediaType]}`)) { context.addIssue({ code: 'custom', message: 'Asset filename extension must match media type.', path: ['fileName'] }); } const image = asset.mediaType.startsWith('image/'); if (image && (asset.width === null || asset.height === null || asset.durationMs !== null)) { context.addIssue({ code: 'custom', message: 'Image assets require dimensions and no duration.', path: ['width'] }); } if (!image && (asset.width === null || asset.height === null || asset.durationMs === null)) { context.addIssue({ code: 'custom', message: 'Video assets require dimensions and duration.', path: ['durationMs'] }); } }); const publicAssetManifestSchema = z.object({ assets: z.array(publicAssetSchema) }).strict().superRefine((manifest, context) => { const ids = manifest.assets.map((asset) => asset.id); if (new Set(ids).size !== ids.length) { context.addIssue({ code: 'custom', message: 'Duplicate asset ID in manifest.', path: ['assets'] }); } const filenames = manifest.assets.map((asset) => asset.fileName); if (new Set(filenames).size !== filenames.length || new Set(filenames.map((name) => name.toLocaleLowerCase('en-US'))).size !== filenames.length) { context.addIssue({ code: 'custom', message: 'Asset filenames must be unique without case-fold collisions.', path: ['assets'] }); } }); const siteConfigSchema = z.object({ environment: z.enum(['preview', 'production']), canonicalUrl: httpUrl, buildId: z.string().regex(/^[a-z0-9][a-z0-9._-]{0,127}$/, 'Build ID must be a safe immutable identifier.'), }).strict().superRefine((site, context) => { if (site.environment === 'preview' && site.canonicalUrl !== 'https://portfolio.localhost/') { context.addIssue({ code: 'custom', message: 'Preview canonical URL must be exactly https://portfolio.localhost/.', path: ['canonicalUrl'] }); } if (site.environment === 'production') { context.addIssue({ code: 'custom', message: 'Production hostname is not approved.', path: ['canonicalUrl'] }); } }); export const parsePortfolioContent = (input: unknown): PortfolioContent => portfolioContentSchema.parse(input) as PortfolioContent; export const parsePublicAssetManifest = (input: unknown): PublicAssetManifest => publicAssetManifestSchema.parse(input) as PublicAssetManifest; export const parseSiteConfig = (input: unknown): SiteConfig => siteConfigSchema.parse(input) as SiteConfig; export const validateProductionInputs = ( contentInput: unknown, manifestInput: unknown, siteInput: unknown, _preparedAssetRoot: URL, ): ProductionInputs => { const content = parsePortfolioContent(contentInput); const manifest = parsePublicAssetManifest(manifestInput); const site = parseSiteConfig(siteInput); const manifestIds = new Set(manifest.assets.map((asset) => asset.id)); const referencedAssetIds = [ ...content.projects.selected.flatMap((project) => project.assetIds), ...content.projects.additional.flatMap((project) => project.assetIds), ...content.toolkit.flatMap((group) => group.technologies.flatMap((technology) => technology.assetId === undefined ? [] : [technology.assetId])), ...(content.metadata.socialPreviewAssetId === undefined ? [] : [content.metadata.socialPreviewAssetId]), ]; for (const assetId of referencedAssetIds) { if (!manifestIds.has(assetId)) { throw new Error(`Unmanifested asset reference: ${assetId}`); } } return { content, manifest, site }; };