export const THEME_MANIFEST_VERSION = 1 export interface ThemeManifest { // 契约刻意放宽:spec §9.1 的 'pilot-minimal' 字面量属于 pilot 主题自身的 manifest 文件; // 共享注册契约面向多主题(第二个主题不得要求改本契约),故 key 为 string——勿"纠正"回字面量。 key: string version: string renderContractVersion: string supportedContentTypes: string[] requiredSiteConfig: string[] templates: Array<{ key: string; appliesTo: string }> } const SEMVER = /^\d+\.\d+\.\d+$/ // 守卫查值类型(不只查键存在):semver 格式、字符串字段、数组元素类型全部校验 export function isThemeManifest(v: unknown): v is ThemeManifest { if (typeof v !== 'object' || v === null) return false const m = v as ThemeManifest return typeof m.key === 'string' && typeof m.version === 'string' && SEMVER.test(m.version) && typeof m.renderContractVersion === 'string' && Array.isArray(m.supportedContentTypes) && m.supportedContentTypes.every((t) => typeof t === 'string') && Array.isArray(m.requiredSiteConfig) && m.requiredSiteConfig.every((t) => typeof t === 'string') && Array.isArray(m.templates) && m.templates.every((t) => typeof t === 'object' && t !== null && typeof (t as { key: unknown }).key === 'string' && typeof (t as { appliesTo: unknown }).appliesTo === 'string') }