/** * vibe-registry core —— 万物皆注册的类型基石 * * 设计要点(v0.5): * - 注册项类型封闭 + id 即引用键 * - 所有注册项可带 demo 预览数据(预览即真实) * - 纯 TS、零依赖、不接 UI(渲染内核后续接) */ export type RegistryKind = 'layout' | 'widget' | 'action' | 'schema' | 'theme' /** 布局渲染函数签名(React 无关,render 层传入类型;避免 core 依赖 React) */ export interface LayoutRenderProps { readonly slots: readonly S[] /** slot → 已填充的 ReactNode 数组 */ readonly blocks: Record /** 页面声明本身(含 title 等元信息) */ readonly page: { readonly layout: string; readonly title?: string } } /** 布局骨架:页面级结构。渲染内核据 slots 填充 blocks。 */ export interface LayoutRegistryEntry { readonly kind: 'layout' /** 简易 id,如 'admin' —— 用户/agent 只需说这个 id */ readonly id: string /** 布局所含槽位顺序,渲染时 blocks 依 slot 填入 */ readonly slots: readonly string[] /** 描述,供画廊/agent 展示 */ readonly description: string /** 布局渲染实现(slots → 页面骨架)。可选:缺省用画廊的通用骨架 */ readonly render?: (props: LayoutRenderProps) => unknown /** demo 数据(预览即真实,与实装共用渲染内核) */ readonly preview?: LayoutPreview } export interface LayoutPreview { /** 标题文本,画廊首页展示 */ readonly title: string /** 缩略示意:每个 slot 放什么 demo block(id 或描述) */ readonly demoBlocks?: ReadonlyArray<{ readonly slot: string readonly widget: string readonly props?: Record }> } /** 业务组件注册项(映射到已有 UI 库,不重造原子组件) */ export interface WidgetRegistryEntry { readonly kind: 'widget' readonly id: string /** 渲染组件(React 组件;render 层消费,core 不依赖 React 类型) */ readonly component?: unknown /** 关联字段/列 schema id(可选) */ readonly schemaId?: string readonly description?: string } /** 行为注册项:可复用的事件语义(泛型:payload/结果类型随注册保留,不丢业务语义) */ export interface ActionRegistryEntry

{ readonly kind: 'action' readonly id: string readonly handler: (payload: P, ctx: unknown) => R | Promise readonly description?: string } /** 数据/字段注册项:表单字段、表格列 */ export interface SchemaRegistryEntry { readonly kind: 'schema' readonly id: string readonly fields?: readonly unknown[] readonly description?: string } export type RegistryEntry = | LayoutRegistryEntry | WidgetRegistryEntry | ActionRegistryEntry | SchemaRegistryEntry /** 注册表集合:kind → id → entry(单一事实来源) */ export type RegistryStore = { readonly layouts: ReadonlyMap readonly widgets: ReadonlyMap readonly actions: ReadonlyMap readonly schemas: ReadonlyMap } /* ── 注册与查询 ─────────────────────────────────────────────── */ export type RegisterFn = (entry: E) => E export interface RegistryApi { readonly store: RegistryStore registerLayout: RegisterFn<'layout', LayoutRegistryEntry> registerWidget: RegisterFn<'widget', WidgetRegistryEntry> registerAction: (entry: ActionRegistryEntry) => ActionRegistryEntry registerSchema: RegisterFn<'schema', SchemaRegistryEntry> /** 校验:给定引用集合,返回未注册的 id 清单(空 = 通过) */ validateRefs: (refs: ReadonlyArray<{ readonly kind: RegistryKind; readonly id: string }>) => string[] /** 画廊用的清单:全部已注册项(dev 用) */ listAll: () => RegistryEntry[] } /** 创建注册表(单例由 createRegistry 返回) */ export function createRegistry(): RegistryApi { const layouts = new Map() const widgets = new Map() const actions = new Map() const schemas = new Map() function register( map: Map, entry: E, ): E { const existed = map.get(entry.id) if (existed) { throw new Error(`[vibe-registry] 注册冲突:${entry.kind} '${entry.id}' 已存在(万物皆注册,id 唯一)`) } map.set(entry.id, entry as RegistryEntry) return entry } return { store: { layouts, widgets, actions, schemas }, registerLayout: (e) => register(layouts, e), registerWidget: (e) => register(widgets, e), registerAction: (entry: ActionRegistryEntry) => { if (actions.has(entry.id)) { throw new Error(`[vibe-registry] 注册冲突:action '${entry.id}' 已存在(万物皆注册,id 唯一)`) } actions.set(entry.id, entry as ActionRegistryEntry) return entry }, registerSchema: (e) => register(schemas, e), validateRefs(refs) { const missing: string[] = [] for (const r of refs) { const map = r.kind === 'layout' ? layouts : r.kind === 'widget' ? widgets : r.kind === 'action' ? actions : schemas if (!map.has(r.id)) missing.push(`${r.kind}:${r.id}`) } return missing }, listAll() { return [...layouts.values(), ...widgets.values(), ...actions.values(), ...schemas.values()] }, } } /* ── 全局注册表(单例,项目内 import 即用) ────────────────────── */ const globalRegistry = createRegistry() export const registerLayout: RegistryApi['registerLayout'] = (e) => globalRegistry.registerLayout(e) export const registerWidget: RegistryApi['registerWidget'] = (e) => globalRegistry.registerWidget(e) export const registerAction: RegistryApi['registerAction'] = (e) => globalRegistry.registerAction(e) export const registerSchema: RegistryApi['registerSchema'] = (e) => globalRegistry.registerSchema(e) export const registry: RegistryApi = globalRegistry