feat: vibe-registry M1 — 万物皆注册框架骨架
- core: registerLayout/Widget/Action(泛型)/Schema + 校验闸门 - definePage: 页面只管业务,结构来自注册表 - presets: admin / app-shell 预设布局(vr add 物化) - cli: vr list/add/update 防覆盖 - examples/qa-orchestrator: 实战编排验证(workspace:* 依赖)
This commit is contained in:
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* definePage —— 页面声明层
|
||||
*
|
||||
* 理念(设计文档 §3.1):
|
||||
* - 页面只管业务:setup 只做数据/状态,结构全部来自注册表
|
||||
* - 布局/组件用 id 引用,不写任何布局 JSX
|
||||
* - 生成产物受控:引用的 id 必须是已注册项(validate 把关)
|
||||
*/
|
||||
import { type RegistryApi, registry } from './registry.ts'
|
||||
|
||||
/** 页面中一个块的声明:引用已注册 widget,并给 props/绑定 */
|
||||
export interface PageBlockDecl {
|
||||
/** widget 注册 id,如 'data-table'、'page-header' */
|
||||
readonly widget: string
|
||||
/** 静态 props(可选) */
|
||||
readonly props?: Record<string, unknown>
|
||||
/** 业务绑定:setup 返回的数据里取哪些字段喂给 widget(键:widget prop 名,值:setup 结果字段) */
|
||||
readonly bind?: Record<string, string>
|
||||
}
|
||||
|
||||
/** 页面声明:布局 id + 块列表 + 业务 */
|
||||
export interface PageDeclaration {
|
||||
/** 布局注册 id,如 'admin' —— 用户只需说"用 admin 布局" */
|
||||
readonly layout: string
|
||||
readonly blocks: readonly PageBlockDecl[]
|
||||
/** 业务层:返回的数据可供 blocks.bind 使用;只做业务,不碰 UI 结构 */
|
||||
readonly setup?: (ctx: PageContext) => Promise<Record<string, unknown>> | Record<string, unknown>
|
||||
/** 行为回调:业务动作(bivariant,允许实现方标注收窄的 payload 类型) */
|
||||
readonly actions?: Record<string, { bivarianceHack(payload: never, ctx: PageContext): unknown }['bivarianceHack']>
|
||||
/** 页标题等元信息(可选) */
|
||||
readonly title?: string
|
||||
}
|
||||
|
||||
export interface PageContext {
|
||||
readonly query: Record<string, string | undefined>
|
||||
readonly params: Record<string, string | undefined>
|
||||
}
|
||||
|
||||
/** 把声明物化为"已校验的页面对象";未知引用直接抛错(校验闸门) */
|
||||
export function definePage(decl: PageDeclaration, reg: RegistryApi = registry): PageDeclaration {
|
||||
const refs = [
|
||||
{ kind: 'layout' as const, id: decl.layout },
|
||||
...decl.blocks.map((b) => ({ kind: 'widget' as const, id: b.widget })),
|
||||
]
|
||||
const missing = reg.validateRefs(refs)
|
||||
if (missing.length > 0) {
|
||||
throw new Error(
|
||||
`[vibe-registry] definePage 引用了未注册项:${missing.join(', ')} ` +
|
||||
`(万物皆注册:请先注册,或用已有注册项)`,
|
||||
)
|
||||
}
|
||||
return decl
|
||||
}
|
||||
|
||||
/** 业务 setup 辅助类型:让页面文件只声明"取哪些数据" */
|
||||
export type SetupData<P extends PageDeclaration> = Awaited<ReturnType<NonNullable<P['setup']>>>
|
||||
Reference in New Issue
Block a user