weifuwu 0.60.1 → 0.61.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +78 -17
  2. package/dist/ai/agent.d.ts +29 -1
  3. package/dist/ai/client.d.ts +15 -0
  4. package/dist/ai/index.d.ts +2 -1
  5. package/dist/client/diff.d.ts +15 -0
  6. package/dist/client/hydration.d.ts +13 -0
  7. package/dist/client/index.d.ts +4 -0
  8. package/dist/client/index.js +3 -2226
  9. package/dist/client/middleware/api.d.ts +2 -0
  10. package/dist/client/motion.d.ts +8 -0
  11. package/dist/client/registry.d.ts +34 -0
  12. package/dist/client/render.d.ts +13 -14
  13. package/dist/client/types.d.ts +28 -0
  14. package/dist/client/ui.d.ts +56 -0
  15. package/dist/components/Badge/Badge.d.ts +1 -1
  16. package/dist/components/Button/Button.d.ts +3 -0
  17. package/dist/components/Card/Card.d.ts +2 -0
  18. package/dist/components/Confirm/Confirm.d.ts +8 -1
  19. package/dist/components/Dropdown/Dropdown.d.ts +2 -0
  20. package/dist/components/Icon/Icon.d.ts +19 -0
  21. package/dist/components/Input/Input.d.ts +5 -0
  22. package/dist/components/Modal/Modal.d.ts +2 -0
  23. package/dist/components/PageHeader/PageHeader.d.ts +2 -0
  24. package/dist/components/StatCard/StatCard.d.ts +2 -0
  25. package/dist/components/Table/Table.d.ts +4 -0
  26. package/dist/components/Toast/Toast.d.ts +13 -1
  27. package/dist/components/index.d.ts +2 -0
  28. package/dist/components/index.js +1 -3387
  29. package/dist/components/style.css +282 -134
  30. package/dist/core/router.d.ts +10 -10
  31. package/dist/core/serve.d.ts +1 -1
  32. package/dist/index.d.ts +3 -1
  33. package/dist/index.js +523 -11
  34. package/dist/layout/weifuwu-layout.css +106 -17
  35. package/dist/messager/index.d.ts +107 -0
  36. package/dist/types.d.ts +2 -2
  37. package/dist/user/index.d.ts +3 -2
  38. package/package.json +1 -1
package/README.md CHANGED
@@ -19,7 +19,7 @@ npm install weifuwu
19
19
 
20
20
  ### 一句话
21
21
 
22
- **weifuwu = 一个包的全栈框架:全自研、零配置、消灭样板。** 下面三条核心哲学与十条技术原则都是这句话的展开——我们不做缝合框架,每一层都自研且可预测。
22
+ **weifuwu = 一个包的全栈框架:全自研、零配置、消灭样板、SaaS 地基随包内置。** 下面四条核心哲学与十一条技术原则都是这句话的展开——我们不做缝合框架,每一层都自研且可预测。
23
23
 
24
24
  ### 核心哲学
25
25
 
@@ -36,6 +36,8 @@ npm install weifuwu
36
36
  | 数据样板 | `ctx.data.get` 一个 API 覆盖 SSR 预取 / hydration 命中 / SPA fetch,写数据像写同步代码 |
37
37
  | 协议样板 | 自研 PG/Redis 客户端消灭双重编码、parseRow 样板、`'EX'` 参数顺序陷阱 |
38
38
 
39
+ **④ SaaS 地基,应用必须的一等能力。** 不只是库——rateLimit / email / userSystem / messager / queue 五个中间件随包内置,且互相咬合:**身份是消息的路由,消息是身份的交互**(`sendTo(ctx.user.id)` 按身份路由、`createConversation(ctx.user.id)` 创建者即身份、成员校验自动对齐),AI 对话走同一协议。开发者从「自建基础设施」变「声明业务」——`app.use(...)` 一行接入,一个多租户 AI 平台(agent-platform)已完整消费这层地基(auth / AI / 消息 / UI / 数据管道全部框架能力)。
40
+
39
41
  ### 技术原则(哲学的展开)
40
42
 
41
43
  **零运行时依赖** — 前端无 npm 运行时依赖(自研 VDOM,不引入 Virtual DOM 库、rxjs、immer 等)。后端仅依赖 `esbuild`(TSX→JS 编译)+ `graphql` + `ws`(语言/协议本身)——**数据库客户端(PostgreSQL/Redis 协议)、GraphQL schema 工具全部自研**。esbuild 作为运行时依赖随 `npm install weifuwu` 自动安装,`ctx.ui.js()` 开箱即用。
@@ -44,20 +46,24 @@ npm install weifuwu
44
46
 
45
47
  **Proxy 驱动渲染** — `ctx.ui.$()` 返回深度 Proxy,`$.x = val` 自动触发当前组件的 VDOM patch;也支持手动 `ctx.ui.render()` 精确控制渲染时机。**组件库手动优先、业务层自动优先**——同一框架内按角色选模式(详见[组件库](#组件库-weifuwucomponents))。
46
48
 
47
- **中间件注入一切** — 后端和前端共用同一理念:中间件向 `ctx` 注入能力(`ctx.sql` / `ctx.redis` / `ctx.api` / `ctx.auth` / `ctx.i18n` / `ctx.limit` / `ctx.email` / `ctx.queue` / `ctx.ai` 等),Handler/组件从 `ctx` 读取。
49
+ **中间件注入一切** — 后端和前端共用同一理念:中间件向 `ctx` 注入能力(`ctx.sql` / `ctx.redis` / `ctx.api` / `ctx.auth` / `ctx.i18n` / `ctx.limit` / `ctx.email` / `ctx.queue` / `ctx.ai` / `ctx.msg` 等),Handler/组件从 `ctx` 读取。
48
50
 
49
51
  **async 工厂组件** — `async (ctx) => (initProps, ctx) => (props) => VNode`:工厂层声明数据(`await ctx.data.get`)、mount 初始化状态(`$`)、render 输出视图。异步只在工厂边界,mount/render 保持同步;数据经闭包注入,写数据像写同步代码。三条纪律见[核心概念 · async 组件](#核心概念)。
50
52
 
51
53
  **SPA/SSR/Hydration 统一透明** — 同一份路由定义(`routes`)一个组件三场景自动适配:后端 `uiSsr({ routes })` 匹配即自动 SSR(完整 HTML + `__DATA__`),客户端 `router({ routes })` + `RouteView` + `mount(..., { hydrate: true })` 按 URL 同源匹配并收养服务端 HTML(不重建、无闪跳)。`ctx.data.get` 一个 API:SSR 预取 / hydration 命中(不重复请求)/ SPA 触发 fetch。服务端直接用 `.tsx`(`weifuwu/dev` Node loader),前后端同一 JSX 运行时。
52
54
 
53
- **AI 是一等公民** — 自研 OpenAI 兼容协议(`docs/ai-contract.md`)+ 零依赖流式客户端 + agent 工具循环 + HITL 人工审批。后端 `ctx.ai` 一个入口:`chat()` / `stream()` / `agent()` / `approve()`;前端 `ctx.ui.useChat()`(会话语义)+ `AiChat` 组件(标准对话界面)——流式 token / 工具调用卡 / 审批卡开箱即用,协议对页面完全透明,不用 ai-sdk。
55
+ **AI 是一等公民** — 自研 OpenAI 兼容协议(`docs/ai-contract.md`)+ 零依赖流式客户端 + agent 工具循环 + HITL 人工审批 + embedding 向量化。后端 `ctx.ai` 一个入口:`chat()` / `stream()` / `agent()`(`stream(messages, { emit })` emitter 抽象——事件可接任意通道,`runToResult()` 结构化结果)/ `approve()` / `embed()` / `embedMany()`;前端 `ctx.ui.useChat()`(会话语义)+ `AiChat` 组件(标准对话界面)——流式 token / 工具调用卡 / 审批卡开箱即用,协议对页面完全透明,不用 ai-sdk。
56
+
57
+ **SaaS 地基随包内置** — rateLimit(限流)/ email(邮件)/ userSystem(用户认证)/ messager(消息系统)/ queue(可靠队列)以中间件形态随包提供,`app.use(...)` 一行接入(详见文末[SaaS 地基模块](#saas-地基模块ratelimit--email--usersystem--messager--queue))。互相咬合成协作基础:身份(userSystem)+ 消息(messager)的组合让「谁能跟谁说话、消息如何送达」天然对齐,不再需要第三套权限系统。
54
58
 
55
- **SaaS 地基随包内置** rateLimit(限流)/ email(邮件)/ userSystem(用户认证)/ queue(可靠队列)以中间件形态随包提供,`app.use(...)` 一行接入(详见文末[SaaS 地基模块](#saas-地基模块ratelimit--email--usersystem--queue))。
59
+ **机制与策略分离**框架管**机制**(token 怎么签、消息怎么送达、agent 循环怎么跑),开发者管**策略**(谁能建群、租户隔离 SQL、技能注册表)。这是「诚实裁剪」的积极面:**框架不越界,应用层不被绑架**——agent-platform 迁移验证了边界:多租户隔离(`WHERE tenant_id`)、技能编排、聊天产品模型留在应用层,框架守住通用能力(auth / ai / messager / UI / 数据管道)。
56
60
 
57
61
  **零自定义 CSS 设计系统** — 一个 CSS 文件 = 双层 Token + 布局原语 + 工具类 + 组件样式。业务页面不写 style.css:组件 + `wf-*` 原语写业务,品牌/组件定制改变量(`--wf-brand-500` / `--wf-btn-radius`),暗色自动(详见[布局系统](#布局系统-weifuwulayout))。
58
62
 
59
63
  **自研数据层** — `ctx.sql`(PG v3 协议)与 `ctx.redis`(RESP2 协议)为**自研客户端**:确定性输出、行为可预测、统一错误模型。jsonb 自动解码、TTL 安全 API、schema 写前校验——高频痛点(双重编码/parseRow 样板/`'EX'` 参数顺序)从根上消除。
60
64
 
65
+ > **实践验证**:多租户 AI 平台(`apps/agent-platform`——14 页 + 部门聊天 + 知识库 + HITL 审批)已完全运行在框架上:auth(userSystem)/ AI 引擎(ai)/ 实时消息(messager)/ UI(48 组件)/ 数据管道(ctx.api)零自研替代。框架哲学(中间件注入、诚实裁剪、机制与策略分离)经受住了真实复杂应用的检验——这也是我们确定「哪些进框架、哪些留应用层」的依据。
66
+
61
67
  ---
62
68
 
63
69
  ## 快速开始
@@ -236,8 +242,8 @@ createApp().use(router({ routes })).mount('#root', RouteView, { hydrate: true })
236
242
  | 资源 | CDN 地址 | 说明 |
237
243
  |------|---------|------|
238
244
  | `weifuwu/client` | `https://unpkg.com/weifuwu@latest/dist/client/index.js` | 客户端核心(createApp, h, 路由, 状态管理等) |
239
- | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/index.js` | 46 个 UI 组件(Button, Card, Table, Modal 等) |
240
- | 组件样式 | `https://unpkg.com/weifuwu@latest/dist/components/style.css` | 组件 CSS + 115 个主题 Token + 67 个布局原语 |
245
+ | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/index.js` | 48 个 UI 组件(Button, Card, Table, Modal, Icon 等) |
246
+ | `weifuwu/components` | `https://unpkg.com/weifuwu@latest/dist/components/style.css` | 组件 CSS + 141 个主题 Token + 67 个布局原语 |
241
247
  | 独立布局系统 | `https://unpkg.com/weifuwu@latest/dist/layout/weifuwu-layout.css` | 仅 CSS 布局,不依赖 JS |
242
248
 
243
249
 
@@ -257,9 +263,10 @@ createApp().use(router({ routes })).mount('#root', RouteView, { hydrate: true })
257
263
  | `weifuwu` | **uiSsr** | 路由级 SSR:匹配 routes → 自动完整 HTML + `__DATA__` + bundle | Router, ui |
258
264
  | `weifuwu` | **rateLimit** | 限流中间件(fixed/sliding,redis 多实例原子)→ `ctx.limit` | Router, redis |
259
265
  | `weifuwu` | **email** | 邮件发送(Resend/SMTP 自研/自定义适配器)→ `ctx.email` | Router |
260
- | `weifuwu` | **userSystem** | 用户系统(scrypt 密码哈希 + 混合会话)→ `ctx.user` / `ctx.auth` + `/api/auth/*` | Router, postgres |
266
+ | `weifuwu` | **userSystem** | 用户系统(scrypt 密码哈希 + 混合会话 + 多租户感知)→ `ctx.user` / `ctx.auth` / `ctx.tenantId` + `/api/auth/*` | Router, postgres |
267
+ | `weifuwu` | **messager** | 消息系统(会话/消息持久化 + WS 实时投递 + Redis 跨进程广播)→ `ctx.msg` + `/api/messages/*` | Router, postgres, (redis) |
261
268
  | `weifuwu` | **queue** | 可靠任务队列(Redis Streams,at-least-once + DLQ)→ `ctx.queue` | Router, redis |
262
- | `weifuwu` | **ai** | LLM 对话(自研 OpenAI 兼容协议 + 自研 SSE 解码,默认 DeepSeek)→ `ctx.ai` + `ctx.ui.useChat` + `AiChat` | Router |
269
+ | `weifuwu` | **ai** | LLM 对话(自研 OpenAI 兼容协议 + 自研 SSE 解码,默认 DeepSeek)→ `ctx.ai` + embedding + `ctx.ui.useChat` + `AiChat` | Router |
263
270
  | `weifuwu/dev` | **dev loader** | Node loader:服务端直接跑 `.ts/.tsx`(`--import weifuwu/dev`) | esbuild |
264
271
  | `weifuwu` | **graphql** | GraphQL 端点(支持 GraphiQL) | Router |
265
272
  | `weifuwu` | **createMiddleware** | 类型安全中间件工厂 | — |
@@ -275,8 +282,8 @@ createApp().use(router({ routes })).mount('#root', RouteView, { hydrate: true })
275
282
  | `weifuwu/client` | **ErrorBoundary** | 错误边界组件 | createApp |
276
283
  | `weifuwu/client` | **lockScroll/trapFocus** | 滚动锁定 / 焦点陷阱工具 | — |
277
284
  | `weifuwu/client` | **popup** | 弹层 fixed 定位工具(`computeFixedPos` / `computeFixedPosRect`) | — |
278
- | `weifuwu/components` | **47 个组件** | Button/Table/Modal/Confirm/Toast/... + `confirm()` / `toast()` 命令式中间件 | weifuwu/client |
279
- | `weifuwu/layout` | **CSS 布局** | 67 个布局原语 + 115 个主题 Token(也支持 `weifuwu/layout/style.css`) | — |
285
+ | `weifuwu/components` | **48 个组件** | Button/Table/Modal/Confirm/Toast/... + `confirm()` / `toast()` 命令式中间件 | weifuwu/client |
286
+ | `weifuwu/layout` | **CSS 布局** | 67 个布局原语 + 141 个主题 Token(也支持 `weifuwu/layout/style.css`) | — |
280
287
 
281
288
  ---
282
289
 
@@ -1070,6 +1077,8 @@ app.wsHub(redisHub)
1070
1077
 
1071
1078
  WebSocket 原生 `ws.send()` 发送,`ws.on('message', cb)` WebSocket 接收。
1072
1079
 
1080
+ > **实时应用推荐用 `messager()`**(SaaS 地基模块):协议内置(`connected/subscribe/ping`)+ 持久化 + 跨进程广播 + 点对点,不必自写 Hub/协议——见[消息系统章节](#messager--消息系统)。
1081
+
1073
1082
  ---
1074
1083
 
1075
1084
  ## HttpError — HTTP 错误
@@ -1092,7 +1101,7 @@ app.get('/secure', () => {
1092
1101
 
1093
1102
  ## 响应辅助函数
1094
1103
 
1095
- > 以下为完整 API 参考,按需查阅。四个 SaaS 地基模块(rateLimit / email / userSystem / queue)见文末「SaaS 地基模块」章节。
1104
+ > 以下为完整 API 参考,按需查阅。五个 SaaS 地基模块(rateLimit / email / userSystem / messager / queue)见文末「SaaS 地基模块」章节。
1096
1105
 
1097
1106
  消除 `Response.json(...)` 重复模式:
1098
1107
 
@@ -1161,6 +1170,7 @@ import type { CORSOptions } from 'weifuwu'
1161
1170
  import type { ServeStaticOptions } from 'weifuwu'
1162
1171
  import type { PostgresOptions, PostgresClient, PostgresInjected } from 'weifuwu'
1163
1172
  import type { RedisOptions, RedisClient, RedisInjected } from 'weifuwu'
1173
+ import type { MessagerOptions, MessagerClient, MessagerInjected } from 'weifuwu'
1164
1174
  import type { GraphQLOptions, GraphQLHandler } from 'weifuwu'
1165
1175
  ```
1166
1176
 
@@ -2376,6 +2386,7 @@ props 变化 ──────────────────────
2376
2386
  | Field | `Field` | `label`, `error`, `required`, `help` | 字段包装 |
2377
2387
  | FileUpload | `FileUpload` | `accept`, `multiple`, `maxSize`, `onFiles` | 文件上传 |
2378
2388
  | SearchInput | `SearchInput` | `value`, `placeholder`, `onSearch`, `loading` | 搜索框 |
2389
+ | SegmentedControl | `SegmentedControl` | `options: SegmentedOption[]`, `value`, `onChange`, `size` | 分段选择器 |
2379
2390
  | ProgressBar | `ProgressBar` | `value`, `max`, `variant`, `size`, `label` | 进度条 |
2380
2391
 
2381
2392
  ### 数据展示
@@ -2387,6 +2398,7 @@ props 变化 ──────────────────────
2387
2398
  | Badge | `Badge` | `variant: BadgeVariant`, `count`, `dot`, `max` | 徽标 |
2388
2399
  | Tag | `Tag` | `variant`, `closable`, `onClose` | 标签 |
2389
2400
  | Avatar | `Avatar` | `src`, `name`, `size`, `shape` | 头像 |
2401
+ | Icon | `Icon` | `name: IconName`, `size`, `color`, `strokeWidth` | 图标(内置 100+ 图标,stroke 风格) |
2390
2402
  | StatCard | `StatCard` | `title`, `value`, `trend`, `icon`, `variant` | 统计卡片 |
2391
2403
  | PageHeader | `PageHeader` | `title`, `subtitle`, `actions`, `onBack`, `breadcrumb` | 页面标题 |
2392
2404
  | Img | `Img` | `src`, `alt`, `fallback`, `lazy`, `fit` | 图片(含 fallback) |
@@ -2450,7 +2462,7 @@ props 变化 ──────────────────────
2450
2462
 
2451
2463
  # 布局系统 (`weifuwu/layout`)
2452
2464
 
2453
- 纯 CSS 布局原语 + 工具类 + 115 个主题 Token。不绑定任何 JS 框架。
2465
+ 纯 CSS 布局原语 + 工具类 + 141 个主题 Token。不绑定任何 JS 框架。
2454
2466
 
2455
2467
  > **学习路径与命名规范**:见 [`docs/style-guide.md`](./docs/style-guide.md)——统一语法 `wf-<域>-<名>`、三档学习(组件 → 10 核心原语 → 完整速查)、场景速查、变量定制。
2456
2468
 
@@ -2547,7 +2559,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
2547
2559
  | `wf-truncate` | 单行省略(ellipsis) |
2548
2560
  | `wf-line-clamp-2/3` | 多行截断 |
2549
2561
 
2550
- ## 115 个主题 Token
2562
+ ## 141 个主题 Token
2551
2563
 
2552
2564
  **双层结构**:原始层(Primitive,色值只定义一次,品牌/暗色调校改这里)+ 语义层(Semantic,组件消费)。
2553
2565
 
@@ -2567,6 +2579,11 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
2567
2579
  --wf-color-error / --wf-color-error-bg
2568
2580
  --wf-color-info / --wf-color-info-bg
2569
2581
 
2582
+ /* 语义文字色(P2):浅底可读 700 级,文字用 -text、填充用 500 级 */
2583
+ --wf-color-primary-text / --wf-color-success-text / --wf-color-warning-text / --wf-color-error-text / --wf-color-info-text
2584
+ --wf-color-on-brand /* 实心品牌/语义底上的文字与图标 */
2585
+ --wf-overlay /* 浮层遮罩(Modal/Drawer),暗色自动加深 */
2586
+
2570
2587
  /* 文字色 */
2571
2588
  --wf-color-text / --wf-color-text-secondary / --wf-color-text-tertiary / --wf-color-text-disabled
2572
2589
 
@@ -2579,7 +2596,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
2579
2596
  /* 字体 */
2580
2597
  --wf-font-sans / --wf-font-mono
2581
2598
 
2582
- /* 字号: xs sm base lg xl 2xl 3xl 4xl 5xl */
2599
+ /* 字号: xs sm base lg xl 2xl 3xl 4xl 5xl display */
2583
2600
  --wf-font-size-*
2584
2601
 
2585
2602
  /* 字重: normal medium semibold bold */
@@ -2603,6 +2620,17 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
2603
2620
  /* 阴影: sm md lg */
2604
2621
  --wf-shadow-*
2605
2622
 
2623
+ /* 动效(P0):时长阶梯/缓动曲线/位移量,全站动效统一引用 */
2624
+ --wf-dur-fast / --wf-dur-base / --wf-dur-slow
2625
+ --wf-ease-out / --wf-ease-in / --wf-ease-snap
2626
+ --wf-motion-sm / --wf-motion-md / --wf-motion-lg
2627
+
2628
+ /* 表头/分组标题(P5):CJK 感知,默认 none/0,英文可覆盖 */
2629
+ --wf-heading-case / --wf-heading-tracking
2630
+
2631
+ /* 数字(P5):tabular-nums 防宽度抖动(wf-nums 工具类) */
2632
+ --wf-nums
2633
+
2606
2634
  /* 其他 */
2607
2635
  --wf-border-width / --wf-focus-ring
2608
2636
  --wf-transition-duration / --wf-transition-timing
@@ -2909,7 +2937,9 @@ docker compose up -d
2909
2937
 
2910
2938
  ---
2911
2939
 
2912
- # SaaS 地基模块(rateLimit / email / userSystem / queue)
2940
+ # SaaS 地基模块(rateLimit / email / userSystem / messager / queue)
2941
+
2942
+ 五个模块(限流 / 邮件 / 用户系统 / 消息系统 / 队列)以中间件形态随包提供,`app.use(...)` 一行接入。
2913
2943
 
2914
2944
  四个内建模块组成一个"基本 SaaS 底座":认证、异步任务、限流、邮件——零新增依赖
2915
2945
  (只依赖已自研的 redis / postgres 客户端与 node 标准库)。
@@ -2977,7 +3007,36 @@ app.post('/secure', (req, ctx) => { ctx.auth.requireAuth(); ... })
2977
3007
  - **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/client` 的 `auth()` 天然配对);refresh token = 不透明随机串,DB 只存哈希,logout/轮换即撤销
2978
3008
  - **防枚举**:登录失败统一 401(不泄露邮箱是否存在)
2979
3009
  - **`ctx.auth` 方法面**:`register` / `login` / `logout` / `requireAuth` / `setPassword(userId, newPwd)` / `createToken(type, payload, { ttlSeconds })`(邮箱验证/密码重置自接)
2980
- - **裁剪**:OAuth、邮箱验证邮件(给底层 API 自接)、多因素、RBAC 权限引擎(只留 `role` 字段)、多租户语义(tenant-ready:`tenant` 字段 + token claim 已预留)
3010
+ - **多租户感知**:`issueSession` token payload 携带 `tenantId`(来自 `user.tenant`)——中间件自动注入 `ctx.tenantId`,并将会话字段(userId/tenantId/email/name/role)合并到 `ctx.auth`,多租户应用免写 token 解码/租户中间件(数据隔离 SQL 是应用职责)
3011
+ - **`routes` 支持 `exclude`**:`users.routes(app, { exclude: ['register'] })`——应用自定义注册流程(如注册时建租户)时跳过框架路由
3012
+ - **裁剪**:OAuth、邮箱验证邮件(给底层 API 自接)、多因素、RBAC 权限引擎(只留 `role` 字段)、租户隔离 SQL(框架只做感知,`WHERE tenant_id` 属应用层)
3013
+
3014
+ ## messager — 消息系统
3015
+
3016
+ ```ts
3017
+ import { messager } from 'weifuwu'
3018
+
3019
+ const db = postgres()
3020
+ await db.migrate()
3021
+ const msg = messager({ sql: db.sql, redis: rds }) // redis 可选:跨进程广播
3022
+ await msg.migrate() // 幂等建表(conversations + members + messages)
3023
+ app.use(db)
3024
+ app.use(msg) // 注入 ctx.msg
3025
+ msg.routes(app) // /api/messages/*(会话/历史/发消息/已读)
3026
+ app.ws('/ws', ctx.msg.handler()) // 标准 WS 协议内置
3027
+
3028
+ // 业务代码:持久化 + 鉴权 + 广播 + 未读 + 历史,一次调用
3029
+ const conv = await ctx.msg.createConversation(ctx.user.id, { type: 'group', memberIds: ['u2'] })
3030
+ await ctx.msg.sendMessage(conv.id, { senderType: 'user', senderId: ctx.user.id, content: '你好' })
3031
+ ctx.msg.broadcast(`conv:${conv.id}`, { type: 'order_chat', orderId: 'o1' }) // 任意实时事件
3032
+ ctx.msg.sendTo('u2', { type: 'mention' }) // 用户维度点对点
3033
+ ```
3034
+
3035
+ - **数据模型**:`_weifuwu_conversations` / `_weifuwu_conversation_members` / `_weifuwu_messages`(`sender_type + sender_id` 不 FK users——user/agent/system 消息天然可存);direct 会话同对用户唯一、历史游标分页、未读数(`last_read_at`)、编辑/删除软删
3036
+ - **实时协议内置**:`handler()` 提供 `connected / subscribe→subscribed / unsubscribe / ping→pong`——前端 `ctx.ws.send({ type: 'subscribe', room })` 直接可用,两端协议由框架定义
3037
+ - **跨进程**:`redis` 选项 → Redis pub/sub 广播(psubscribe 模式),多实例部署天然一致;无 redis 优雅降级单进程
3038
+ - **与 userSystem 咬合**:`sendTo(ctx.user.id)` 按身份路由、`createConversation(ctx.user.id)` 创建者即身份、成员校验自动对齐——身份是消息的路由,消息是身份的交互
3039
+ - **裁剪**:已读回执状态机(只做未读数)、附件存储、全文搜索、消息确认/重试(可靠投递用 queue)、移动端推送
2981
3040
 
2982
3041
  ## queue — 可靠任务队列
2983
3042
 
@@ -3084,9 +3143,11 @@ return () => <AiChat chat={$} />
3084
3143
 
3085
3144
  - **协议**:`wf:` 命名空间(message_start/token/tool_call/tool_progress/usage/done/error + agent 扩展 step/approval_request),SSE 下行 + POST 上行,错误即值、未知事件透传、`x:*` 自定义事件(详见 [docs/ai-contract.md](./docs/ai-contract.md))
3086
3145
  - **agent 引擎**:`a.agent({ systemPrompt, tools, humanInTheLoop })` 工具循环(LLM → tool_call → 执行 → 回喂 → 重复);工具可 `emit` 进度/自定义事件、接收 `signal` 取消;HITL 审批(`ctx.ai.approve` 响应,拒绝≠终止、modified 改参、超时兜底)
3146
+ - **emitter 抽象**:`agent.stream(messages, { emit })`——`wf:*` 事件(step/token/tool_result/usage/done)可接任意通道(SSE/WS/回调),协议不焊死在传输层;`agent.runToResult(messages)` 返回结构化结果 `{ content, steps, usage }`(非流式/worker 场景)
3147
+ - **embedding**:`ctx.ai.embed(text)` / `embedMany(texts)` 向量化(默认 `DASHSCOPE_API_KEY` + `text-embedding-v4`,compatible-mode 端点);未配置抛 `AiError('unsupported')`(惰性检查,不静默降级)——知识库/语义检索开箱即用
3087
3148
  - **零依赖**:自研 OpenAI 兼容客户端(fetch + SSE 解析),默认 DeepSeek,`baseUrl` 可换任意 OpenAI 兼容端点(Ollama/vLLM/Moonshot…)
3088
3149
  - **追踪**:前端自动生成 `X-Trace-Id` → 后端以之作为 `message_start.id` → 工具内请求继承同一 traceId,整个 agent run 一次搜完
3089
- - **裁剪**:embeddings、Anthropic 原生协议、审批持久化(连接断=会话亡)暂不支持;多 agent 编排不承诺(子 agent = 工具已支持)
3150
+ - **裁剪**:Anthropic 原生协议、审批持久化(连接断=会话亡)暂不支持;多 agent 编排不承诺(子 agent = 工具已支持);embedding 仅文本(图片/多模态不做)
3090
3151
 
3091
3152
  ## 组合示例:注册 → 验证邮件 → 欢迎任务 → 登录防爆破
3092
3153
 
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { type WfEmitter } from './sse.ts';
16
16
  import type { AiClient } from './client.ts';
17
- import type { ChatMessage } from './types.ts';
17
+ import type { ChatMessage, WfUsage } from './types.ts';
18
18
  export interface ToolContext {
19
19
  /** 工具执行声道:emit('wf:tool_progress', ...) 或 emit('x:*', ...) */
20
20
  emit: WfEmitter;
@@ -43,8 +43,36 @@ export interface AgentRunOptions {
43
43
  signal?: AbortSignal;
44
44
  traceId?: string;
45
45
  }
46
+ /** agent 执行步骤(结构化结果用,协议 wf:step/wf:tool_result 的汇总) */
47
+ export interface AgentStep {
48
+ type: 'llm' | 'tool_call' | 'tool_result';
49
+ content?: string;
50
+ toolCall?: {
51
+ id: string;
52
+ name: string;
53
+ arguments: string;
54
+ };
55
+ toolResult?: string;
56
+ }
57
+ /** 结构化运行结果(非流式服务编排用;流式场景走 stream/SSE 的 wf:* 事件) */
58
+ export interface AgentRunResult {
59
+ content: string;
60
+ steps: AgentStep[];
61
+ usage?: WfUsage;
62
+ }
46
63
  export interface AgentRunner {
47
64
  /** 运行 agent → SSE Response(wf: 协议事件流),路由直接 return */
48
65
  run: (messages: ChatMessage[], options?: AgentRunOptions) => Response;
66
+ /**
67
+ * 事件流模式:wf:* 事件打到自定义 emitter(SSE 只是默认实现)。
68
+ * 应用层可把事件接到 WS/回调/自有协议(协议适配器)——不绑死传输通道。
69
+ */
70
+ stream: (messages: ChatMessage[], options?: {
71
+ emit: WfEmitter;
72
+ signal?: AbortSignal;
73
+ traceId?: string;
74
+ }) => Promise<void>;
75
+ /** 结构化结果模式:收集 wf:* 事件 → AgentRunResult(非流式服务编排用) */
76
+ runToResult: (messages: ChatMessage[], options?: AgentRunOptions) => Promise<AgentRunResult>;
49
77
  }
50
78
  export declare function createAgent(client: AiClient, config: AgentConfig): AgentRunner;
@@ -57,6 +57,17 @@ export interface AiClientOptions {
57
57
  baseUrl: string;
58
58
  apiKey: string;
59
59
  defaultModel: string;
60
+ /** embedding provider 配置(可选;未配时 embed/embedMany 抛 AiError('unsupported')) */
61
+ embedding?: AiEmbeddingOptions;
62
+ }
63
+ /** embedding provider 配置——默认参数与 DashScope compatible-mode 对齐(DeepSeek 无 embedding API) */
64
+ export interface AiEmbeddingOptions {
65
+ /** 默认读 DASHSCOPE_API_KEY */
66
+ apiKey?: string;
67
+ /** 默认 'https://dashscope.aliyuncs.com/compatible-mode/v1' */
68
+ baseUrl?: string;
69
+ /** 默认读 DASHSCOPE_EMBEDDING_MODEL,回退 'text-embedding-v4' */
70
+ defaultModel?: string;
60
71
  }
61
72
  /** 单轮 LLM 流式调用的聚合结果(agent 循环用) */
62
73
  export interface StreamFinishResult {
@@ -96,6 +107,10 @@ export interface AiClient {
96
107
  name: string;
97
108
  args: Record<string, unknown>;
98
109
  }, emit: WfEmitter, timeoutMs?: number): Promise<WfApprovalResponse>;
110
+ /** 单文本嵌入(知识库/语义检索;需 ai({ embedding }) 配置,未配抛 AiError) */
111
+ embed(text: string): Promise<number[]>;
112
+ /** 批量文本嵌入(按输入顺序返回) */
113
+ embedMany(texts: string[]): Promise<number[][]>;
99
114
  }
100
115
  export declare function createAiClient(opts: AiClientOptions): AiClient;
101
116
  /** 审批默认超时:5 分钟 */
@@ -29,6 +29,8 @@
29
29
  import type { Context, Middleware } from '../types.ts';
30
30
  import { type AiClient, type AiClientOptions } from './client.ts';
31
31
  import { type AgentConfig, type AgentRunner } from './agent.ts';
32
+ export type { AiEmbeddingOptions } from './client.ts';
33
+ export type { AgentRunResult, AgentStep, AgentTool, AgentConfig, AgentRunner, ToolContext } from './agent.ts';
32
34
  export interface AiOptions extends Partial<AiClientOptions> {
33
35
  }
34
36
  export interface AiInjected {
@@ -51,4 +53,3 @@ export type { WfStreamEvent, WfMessageStart, WfToken, WfUsage, WfDone, WfError,
51
53
  export type { WfEmitter } from './sse.ts';
52
54
  export { AiError } from './client.ts';
53
55
  export type { AiClient, ChatResponse } from './client.ts';
54
- export type { AgentConfig, AgentTool, AgentRunner, ToolContext } from './agent.ts';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * weifuwu/client 增量 diff — patchValue 及全部 diff 辅助
3
+ *
4
+ * 从 render.ts 拆出(P2 结构拆分)。依赖方向:
5
+ * diff.ts → render.ts(renderValue/mountComponent/patchPortal/renderPortal)
6
+ * diff.ts → registry.ts(callRefCleanup)
7
+ * render.ts 不依赖 diff.ts(单向,无环)
8
+ */
9
+ import type { WfuiContext } from './types.ts';
10
+ export declare function patchValue(parent: Node, oldNode: Node | null, oldInput: any, newInput: any, ctx: WfuiContext): Node | null;
11
+ export declare function patchProps(el: Element, oldProps: any, newProps: any): void;
12
+ /** 为无 key 的子节点自动分配位置 key,确保 keyed diff 正确性 */
13
+ export declare function ensureKeys(oldChildren: any[], newChildren: any[]): void;
14
+ export declare function normalize(children: any): any[];
15
+ export declare function patchKeyedChildren(parent: Node, oldChildren: any[], newChildren: any[], ctx: WfuiContext): void;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * weifuwu/client Hydration(游标收养)— 收养服务端 HTML,不重建 DOM
3
+ *
4
+ * 从 render.ts 拆出(P2 结构拆分)。依赖:render.ts(flattenChildren/SVG 常量)、
5
+ * diff.ts(patchProps)、registry.ts(idRegistry/nextComponentId/resolveAsyncFactory)。
6
+ */
7
+ import type { VNode } from './vnode.ts';
8
+ import type { WfuiContext } from './types.ts';
9
+ /**
10
+ * Hydration 挂载入口:收养 container 内现有服务端 HTML。
11
+ * 渲染完收尾:删除服务端有、客户端没有的残留 DOM。
12
+ */
13
+ export declare function hydrateVNode(container: Element, vnode: VNode, ctx: WfuiContext): Promise<void>;
@@ -42,6 +42,10 @@ export { i18n } from './i18n.ts';
42
42
  export type { I18nOptions, I18nState, I18nInjected } from './i18n.ts';
43
43
  export { lockScroll, unlockScroll } from './scroll-lock.ts';
44
44
  export { trapFocus } from './focus-trap.ts';
45
+ export { mountVNode, callRefCleanup, clearAsyncComponentCache } from './render.ts';
46
+ export { hydrateVNode } from './hydration.ts';
47
+ export { patchValue } from './diff.ts';
48
+ export { animateOut } from './motion.ts';
45
49
  export { computeFixedPos, computeFixedPosRect } from './popup.ts';
46
50
  export type { FixedPos, Placement } from './popup.ts';
47
51
  export { zhCN } from './locale/zh_CN.ts';