Files
vibe-registry/src/page.ts
T
hellow-da-sb 2277d55ac9 feat(render): M2 — React 渲染内核 + 布局画廊(playground)
- core 新增 render 子路径(vibe-registry/render):PageView + WidgetView + LayoutGallery
- layout 注册项支持 render 实现;缺省兜底 DefaultLayout
- widget 注册项 component 挂真实 React 组件
- PageBlockDecl 增加 slot 字段(块落在布局哪个槽位)
- playground:admin 布局真实骨架 + 组件 + 订单页,dev 预览 + 生产构建通过
- 全量回归:core/qa-orchestrator/playground typecheck 全绿
2026-09-03 00:59:31 +08:00

59 lines
2.5 KiB
TypeScript

/**
* 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
/** 落在布局的哪个槽位(缺省 'content';渲染内核按布局 slots 装配) */
readonly slot?: 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']>>>