Go→TS 通知契约
INV-WS-12:wescode 的 Go 后端到 TypeScript 前端通知方法的闭合契约。
问题背景
两个功能曾"悄悄什么都不做"地上线:
channelEvent(扫码登录 QR 到不了设置页)codeintel/index-complete(调用图缓存永不全局失效)
原因:_dispatchNotification 使用 if-else 链且没有 default,新增 Method 忘了接线时运行时静默丢弃。
三层保证
1. Go 侧:封闭集合
internal/notify/method.go 定义 notify.Method(带未导出字段的 struct):
// 字面量构造不出来——方法名只能来自这个文件
var ChatStream = Method{name: "chat/stream"}
var CodeintelIndexComplete = Method{name: "codeintel/index-complete"}
// ...
2. 生成 TS 类型
notify/tsgen.go 从 Go AST 生成 wescodeNotifications.ts:
- 禁止手改
go test ./internal/notify断言生成物与method.go逐字相等
3. TS 侧:assertNever
_dispatchNotification 使用 switch + default: assertNever(method):
- 新增 Method 忘了接线 →
tsc报错 - 不存在"其余不管"这个选项
三项测试
| 测试 | 断言 |
|---|---|
TestGeneratedTypeScriptIsCurrent | 生成物与 method.go 逐字相等 |
TestMethodsWalkIsNotVacuous | AST walk 非空(避免恒真) |
TestEveryMethodHasASender | 每个 Method 在 backend/ 真有发送方 |
事件载荷
fireToWindow / fireToBackend 的事件名与 payload 由类型系统约束:
WescodeEventName从IWescodeBackendService派生WescodeEventPayload<K>类型安全- 拼错事件名 → 编译错(此前是
event: string, payload: any)
添加新通知
- 在
internal/notify/method.go添加var XxxYyy = Method{name: "xxx/yyy"} - 在 Go 代码中使用
Notify(notify.XxxYyy, payload) - 运行
make notify-contract(排在vet之前) - 在
_dispatchNotification的 switch 添加 case
忽略通知
如果通知冗余(如 engine/health 由独立 watchdog 轮询),仍需显式写 case 并注明原因。
相关文档
- 多窗口架构 →
multi-window.md - 日志与可观测 →
logging.md