结论摘要
定位
VBI Provider 是一个库型 SDK,不是独立 HTTP 服务。它把平台资源抽象成三类一等 Provider:
ChartProvider、InsightProvider、ReportProvider。
核心关系
固定调用关系是 client -> provider -> builder。列表和元信息走 REST,编辑能力通过 REST 取
session 后进入 Hocuspocus/Yjs 协作文档。
Review 重点
最大风险不在接口数量,而在 REST 直接变更与已打开协作文档之间的状态一致性,以及 WebSocket 鉴权/headers 没有贯通。
模块地图
| 模块 | 职责 | 关键文件 |
|---|---|---|
client |
创建三类 provider,并提供资源列表入口。 | apps/vbi_provider/src/client.ts |
types |
定义 SDK public surface:client options、resource、chart、insight、report、remote session。 | apps/vbi_provider/src/types/*.ts |
remote |
统一 REST 请求、资源 CRUD、builder 生命周期、Hocuspocus/Yjs 协作连接。 |
src/remote/http.ts、resource-api.ts、builder-provider.ts、collaboration.ts
|
chart / insight / report |
把通用资源 API 映射成领域 provider,并处理 DSL 与后端 response 之间的转换。 | src/chart/*、src/insight/*、src/report/* |
agent |
面向 Agent 的 workspace slot、默认资源 id、connector registry。 | src/agent/kit.ts、workspace.ts、connector-registry.ts |
demo connector |
内置 demo connector,用 supermarket CSV + VQuery 支持示例查询。 |
src/demo-connector*.ts、src/dataset/supermarket.csv |
Public SDK 接口
入口导出
| 导出 | 用途 |
|---|---|
createVBIProviderClient(config) |
主入口,返回 VBIProviderClient。 |
createVBIProviderAgentKit(options) |
创建 { client, workspace },给 agent/CLI 直接打开 chart/report 使用。 |
createVBIProviderWorkspace({ client, chartId, reportId }) |
把 provider 包成可缓存的 workspace slot。 |
demoConnector / registerDemoConnector() |
注册内置 demo 数据源 connector。 |
Client Options
| 字段 | 类型 | 作用 |
|---|---|---|
baseUrl |
string |
REST 根路径,例如 http://localhost:3030/api/v1。 |
fetch |
RemoteFetch |
可选 fetch 实现。缺省使用 globalThis.fetch,Node 旧环境需要注入。 |
headers |
Record<string,string> | () => Record |
REST 请求 headers,支持异步工厂。 |
syncTimeoutMs |
number |
等待 Hocuspocus 初始同步的超时时间,默认 10000ms。 |
webSocketPolyfill |
unknown |
Node 场景注入 WebSocket polyfill,例如 ws 的 WebSocket。 |
VBIProviderClient
| 方法 | 返回 | 说明 |
|---|---|---|
chart(id?) |
ChartProvider |
可绑定已有 chart id,也可无 id 用于 create-first。 |
insight(id?) |
InsightProvider |
同上,面向 insight。 |
report(id?) |
ReportProvider |
同上,面向 report。 |
listCharts() / listInsights() / listReports() |
Promise<Summary[]> |
分别请求 GET /charts、GET /insights、GET /reports。 |
Provider 接口矩阵
| 能力 | ChartProvider | InsightProvider | ReportProvider |
|---|---|---|---|
| 资源 id | getResourceId() |
getResourceId() |
getResourceId() |
| CRUD | create、remove、rename |
create、remove、rename、update |
create、remove、rename |
| 打开协作 builder | open() / getBuilder() |
open() / getBuilder() |
open() / getBuilder() |
| 详情和快照 | getDetail()、snapshot() |
getDetail()、snapshot() |
getDetail()、snapshot()、exportSnapshot() |
| 引用关系 | getReferences() 返回 report/page 引用 |
getReferences() 返回 report/page 引用 |
无反向引用接口 |
| Report pages | 无 | 无 |
createPage、updatePage、removePage、reorderPages
|
REST 端点契约
Provider SDK 只读取响应里的 data 字段。后端通过全局 interceptor 包装成
{ code, message, data },这与 SDK 的 requestRemote 假设一致。
| SDK 方法 | HTTP | 后端来源 | 返回语义 |
|---|---|---|---|
listCharts / chart.getSummary / chart.getDetail |
GET /charts、GET /charts/:id |
ChartController + ChartService |
summary 或带 dsl 的 chart detail。 |
chart.create / rename / remove |
POST /charts、PATCH /charts/:id、DELETE /charts/:id |
ChartService.create/update/remove |
创建后 SDK 会写入 state.resourceId 并再取一次详情来归一化 summary。 |
chart.getReferences |
GET /charts/:id/references |
findReportUsages |
返回 { reportId, pageId } 数组。被引用的 chart 删除会 409。 |
insight.getDetail / insight.update |
GET /insights/:id、PATCH /insights/:id |
InsightService |
后端返回 content,SDK 映射为 VBIInsightDSL。 |
report.getDetail / report.exportSnapshot |
GET /reports/:id、GET /reports/:id/snapshot |
ReportService.findOne/snapshot |
详情返回 pages,SDK 映射成 report DSL;exportSnapshot 会嵌入引用 chart/insight DSL。 |
report.createPage / updatePage / removePage /
reorderPages
|
POST /reports/:id/pages、PATCH /reports/:id/pages/:pageId、DELETE /reports/:id/pages/:pageId、PATCH /reports/:id/pages/reorder
|
ReportService |
直接改持久化 report doc;createPage 会同时创建新的 chart 和 insight。 |
provider.open() |
GET /{resource}/:id/collaboration + WebSocket |
getCollaborationSession + HocuspocusServer |
先拿 roomName/websocketUrl,再进入 Yjs 文档同步。 |
数据流逻辑
1. 列表/详情/普通 CRUD
createVBIProviderClient 生成 provider
baseUrl + path,注入 headers,解析 data
/api/v1 controller + service
2. 打开 Builder 协作编辑
roomName 和 websocketUrl
chart:ID 等 room 追加 provider path
synced=true
Y.Doc 创建 VBI builder
3. 后端协作持久化
-
HocuspocusServer.onLoadDocument根据 room 前缀chart:、insight:、report:找到资源,加载主 snapshot,再按 id 顺序 apply collaboration updates。 onChange把每次 Yjs update 追加到对应的 collaboration update 表。onStoreDocument把完整 Yjs state 编码后写回主资源表。- REST 侧部分变更会直接重建 Yjs doc 并清空 update 表,例如 insight content update、report page 变更。
4. Report snapshot
report.exportSnapshot() 调用 GET /reports/:id/snapshot。后端先从 report pages 读取
chartId 和 insightId,逐个加载引用资源的 Yjs snapshot,转换成 chart/insight
DSL,再组装成 { report, charts, insights }。
Agent Workspace
| 对象 | 方法 | 行为 |
|---|---|---|
workspace.chart |
open(id?)、snapshot(id?)、describe(id?)、close(id?)
|
按资源 id 缓存 provider。打开 chart 后会读取 DSL 里的 connectorId,并尝试确保 connector
已注册。
|
workspace.report |
同 chart slot | 只处理 report builder 生命周期,不处理 connector。 |
workspace.connectors |
register、registerChart、getChartConnectorId、ensureKnownConnector
|
目前内置识别 demo connector,其它 connector 需要调用方注册。 |
Review 发现
InsightProvider.update() 通过 REST 改后端 doc;如果 builder 已经 open,随后
getDetail() 会走本地 builder,而不是远端新数据。Report 的
createPage/updatePage/removePage/reorderPages 也直接改持久化 doc,但已打开的 report builder
不会被显式刷新。结果是“接口返回的新 detail”和“provider 后续 snapshot/getDetail 的本地 builder
状态”可能分叉。
相关文件:src/insight/remote-provider.ts、src/report/remote-provider.ts、apps/vbi_be/src/insight/insight.service.ts、apps/vbi_be/src/report/report.service.ts
Client options 支持 headers,但只用于 REST。协作 WebSocket 创建时只传 url 和可选
polyfill;后端 onAuthenticate 当前固定返回 anonymous。未来只要 REST
加鉴权,协作通道会成为独立的鉴权缺口或不可用点。
相关文件:src/remote/http.ts、src/remote/collaboration.ts、apps/vbi_be/src/app/hocuspocus-server.ts
InsightProvider.snapshot() 冷启动语义和 Chart/Report
不一致
Chart/Report 在未打开 builder 时可通过 REST detail 构造 snapshot;Insight 的 snapshot 直接使用
core.getLocalSnapshot,会强制打开协作连接。如果只是想读 insight 当前 DSL,WebSocket
不可用会导致读操作失败。
相关文件:src/insight/remote-provider.ts
开发规则里 Builder owns DSL mutation。Provider 现在提供 REST page 变更方法,同时也提供 report builder。如果两条路径都作为一等写路径,需要定义同步策略;否则建议把 page 写入收敛到 builder 协作流,REST 只做资源生命周期和 export。
requestRemote 成功时直接 response.json() 并取 payload.data,不校验
code/message,也没有处理 204/空响应。当前后端一致返回 envelope,所以能工作;作为 SDK public
contract,最好把 envelope 类型和异常格式写入测试。
package.json 里 repository directory 是 apps/packages/vbi-provider,实际路径是
apps/vbi_provider。这不会影响运行,但会影响包元数据和跳转。
测试覆盖现状
已有覆盖
- Client 创建 provider、列表接口、agent kit 默认 id。
- Chart CRUD 路由、references、Report snapshot export、page reorder。
- 协作 open 流程:attach、等待 sync、in-flight open 复用、socket url path。
- Demo connector 注册和 supermarket CSV 查询。
覆盖数字
当前 coverage-summary.json 显示 total:lines 57.89%,statements 57.51%,functions
39.28%,branches 47.45%。
低覆盖集中在 Insight provider/API、Report provider/API、HTTP error 分支和 provider 本地/远程分支切换。
建议补测
-
打开 builder 后调用
insight.update,断言后续getDetail/snapshot是否符合预期。 - 打开 report builder 后调用 page REST mutation,断言本地 builder 与 REST 返回是否一致。
InsightProvider.snapshot()未 open 且 WebSocket 不可用时的行为。headers工厂、错误 envelope、空 response、fetch 缺失。- WebSocket 鉴权参数、query token 或子协议透传策略。
推荐 Review 顺序
- 先定 provider 写入模型:REST 写 DSL 还是 builder/Yjs 写 DSL,避免双写路径长期并存。
- 再定协作通道鉴权:REST headers、cookie、query token、Hocuspocus authenticate 需要一条完整链路。
-
统一三类 provider 的冷读语义:未 open 时
getDetail/snapshot都应可走 REST,除非明确要求协作连接。 - 把 endpoint contract 写进 provider tests:尤其是 envelope、error、page mutation、references、snapshot。
- 清理小型元数据问题:repository directory、README 中 REST envelope/WS 约束说明。
源码索引
apps/vbi_provider/README.md
apps/vbi_provider/package.json
apps/vbi_provider/src/index.ts
apps/vbi_provider/src/client.ts
apps/vbi_provider/src/types/*.ts
apps/vbi_provider/src/remote/http.ts
apps/vbi_provider/src/remote/resource-api.ts
apps/vbi_provider/src/remote/builder-provider.ts
apps/vbi_provider/src/remote/collaboration.ts
apps/vbi_provider/src/remote/socket-url.ts
apps/vbi_provider/src/chart/*
apps/vbi_provider/src/insight/*
apps/vbi_provider/src/report/*
apps/vbi_provider/src/agent/*
apps/vbi_provider/tests/*.test.ts
apps/vbi_be/src/chart/*
apps/vbi_be/src/insight/*
apps/vbi_be/src/report/*
apps/vbi_be/src/app/hocuspocus-server.ts
apps/vbi_be/src/common/vbi-doc.ts