weifuwu 0.74.0 → 0.76.0
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.
- package/README.md +20 -20
- package/dist/ai/types.d.ts +3 -1
- package/dist/components/AiChat/AiChat.d.ts +5 -1
- package/dist/components/ApprovalCard/ApprovalCard.d.ts +5 -2
- package/dist/components/Button/Button.d.ts +2 -0
- package/dist/components/CitationCard/CitationCard.d.ts +38 -0
- package/dist/components/Command/Command.d.ts +1 -1
- package/dist/components/JsonSchemaForm/JsonSchemaForm.d.ts +48 -0
- package/dist/components/ReasoningBlock/ReasoningBlock.d.ts +25 -0
- package/dist/components/Select/Select.d.ts +11 -1
- package/dist/components/SessionList/SessionList.d.ts +42 -0
- package/dist/components/TreeSelect/TreeSelect.d.ts +3 -2
- package/dist/components/index.d.ts +10 -2
- package/dist/components/index.js +14 -14
- package/dist/components/style.css +608 -20
- package/dist/index.d.ts +0 -2
- package/dist/index.js +141 -433
- package/dist/layout/weifuwu-layout.css +9 -0
- package/dist/test/client/setup.d.ts +12 -0
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui-dom/Confirm.d.ts +5 -41
- package/dist/ui-dom/Notification.d.ts +8 -70
- package/dist/ui-dom/Toast.d.ts +8 -43
- package/dist/ui-dom/ai.d.ts +1 -1
- package/dist/ui-dom/browser.d.ts +1 -1
- package/dist/ui-dom/focus-trap.d.ts +1 -1
- package/dist/ui-dom/hooks/chat.d.ts +11 -0
- package/dist/ui-dom/hooks/events.d.ts +30 -0
- package/dist/ui-dom/hooks/external.d.ts +20 -0
- package/dist/ui-dom/hooks/index.d.ts +31 -0
- package/dist/ui-dom/hooks/input.d.ts +33 -0
- package/dist/ui-dom/hooks/media.d.ts +17 -0
- package/dist/ui-dom/hooks/popup.d.ts +37 -0
- package/dist/ui-dom/hooks/stable.d.ts +39 -0
- package/dist/ui-dom/hooks/test/hooks.test.d.ts +1 -0
- package/dist/ui-dom/hooks/types.d.ts +78 -0
- package/dist/ui-dom/index.d.ts +19 -17
- package/dist/ui-dom/index.js +12 -10
- package/dist/ui-dom/jsx-runtime.js +1 -1
- package/dist/ui-dom/middleware/api.d.ts +3 -3
- package/dist/ui-dom/middleware/auth.d.ts +1 -1
- package/dist/ui-dom/motion.d.ts +1 -1
- package/dist/ui-dom/popup.d.ts +1 -1
- package/dist/ui-dom/scroll-lock.d.ts +1 -1
- package/dist/ui-dom/store.d.ts +24 -0
- package/dist/ui-dom/testing.d.ts +74 -0
- package/dist/ui-dom/testing.js +1 -0
- package/dist/ui-dom/testing.test.d.ts +1 -0
- package/dist/ui-dom/types.d.ts +34 -17
- package/dist/ui-dom/use-chat.d.ts +19 -14
- package/dist/ui-dom/vdom/build.d.ts +34 -0
- package/dist/ui-dom/vdom/diff.d.ts +34 -0
- package/dist/ui-dom/vdom/hydration.d.ts +17 -0
- package/dist/ui-dom/vdom/index.d.ts +23 -0
- package/dist/ui-dom/vdom/mount.d.ts +49 -0
- package/dist/ui-dom/vdom/registry.d.ts +27 -0
- package/dist/ui-dom/vdom/render.d.ts +13 -0
- package/dist/ui-dom/vdom/scheduler.d.ts +13 -0
- package/dist/ui-dom/vdom/serve.d.ts +30 -0
- package/dist/ui-dom/vdom/ssr.d.ts +44 -0
- package/dist/ui-dom/vnode.d.ts +11 -44
- package/dist/user/token.d.ts +1 -1
- package/docs/compat-three-library.md +1 -1
- package/docs/components-map.md +19 -2
- package/docs/components.md +9 -4
- package/docs/custom-components.md +19 -14
- package/docs/data.md +16 -33
- package/docs/environment.md +1 -1
- package/docs/frontend-middleware.md +65 -79
- package/docs/frontend-ui-dom.md +4 -2
- package/docs/frontend.md +35 -38
- package/docs/layout.md +2 -2
- package/docs/realtime.md +23 -28
- package/docs/saas.md +8 -6
- package/package.json +5 -1
- package/dist/ui/ssr-page.d.ts +0 -41
- package/dist/ui/ssr.d.ts +0 -35
- package/dist/ui-dom/diff.d.ts +0 -28
- package/dist/ui-dom/hydration.d.ts +0 -13
- package/dist/ui-dom/reactive.d.ts +0 -6
- package/dist/ui-dom/registry.d.ts +0 -39
- package/dist/ui-dom/render.d.ts +0 -46
- package/dist/ui-dom/route-match.d.ts +0 -22
- package/dist/ui-dom/serve.d.ts +0 -28
- package/dist/ui-dom/ssr.d.ts +0 -56
- package/dist/ui-dom/ui.d.ts +0 -78
package/docs/frontend.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 前端 API 核心(weifuwu/ui-dom)
|
|
2
2
|
|
|
3
|
-
> ⚠️ **`weifuwu/client`
|
|
3
|
+
> ⚠️ **`weifuwu/client` 已并入 `weifuwu/ui-dom`**(`src/client/` 已删除)——本页 import 均用 `weifuwu/ui-dom`。前端运行时唯一入口是 **`weifuwu/ui-dom`**(UIRouter 纯路由 + uiServe 渲染运行时 + SSR/hydration),权威参考见 **[docs/frontend-ui-dom.md](frontend-ui-dom.md)**。
|
|
4
4
|
|
|
5
5
|
> 以下为完整 API 参考,按需查阅。新手建议先阅读 README 的「核心概念」和「快速开始」。
|
|
6
6
|
|
|
@@ -13,40 +13,37 @@
|
|
|
13
13
|
```js
|
|
14
14
|
esbuild.build({
|
|
15
15
|
jsx: 'automatic',
|
|
16
|
-
jsxImportSource: 'weifuwu/
|
|
16
|
+
jsxImportSource: 'weifuwu/ui-dom',
|
|
17
17
|
bundle: true,
|
|
18
18
|
})
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## 应用引导(UIRouter + uiServe)
|
|
24
24
|
|
|
25
25
|
```tsx
|
|
26
|
-
import {
|
|
26
|
+
import { UIRouter, uiServe } from 'weifuwu/ui-dom'
|
|
27
27
|
|
|
28
|
-
const app =
|
|
28
|
+
const app = new UIRouter()
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// 注册中间件(ctx 注入)
|
|
31
31
|
app.use(middleware1)
|
|
32
32
|
app.use(middleware2)
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
app.
|
|
34
|
+
// 页面路由(handler = 异步组件)
|
|
35
|
+
app.get('/', () => h(Home, {}))
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// 销毁
|
|
41
|
-
app.destroy()
|
|
37
|
+
// 落地:客户端渲染(hydrate: true 收养 SSR HTML)
|
|
38
|
+
const handle = uiServe(app, { root: '#root' })
|
|
42
39
|
```
|
|
43
40
|
|
|
44
|
-
|
|
|
41
|
+
| API | 说明 |
|
|
45
42
|
|------|------|
|
|
46
|
-
| `
|
|
47
|
-
| `app.use(mw)` |
|
|
48
|
-
| `app.
|
|
49
|
-
| `app.
|
|
43
|
+
| `new UIRouter()` | 创建路由实例 |
|
|
44
|
+
| `app.use(mw)` | 注册中间件(AppMiddleware / UIMiddleware) |
|
|
45
|
+
| `app.get(path, handler)` / `use(prefix, sub)` / `notFound(handler)` | 页面路由 / 子路由树 / 404 |
|
|
46
|
+
| `uiServe(app, { root, hydrate })` | 客户端落地(返回 handle,`handle.close()` 卸载) |
|
|
50
47
|
| `app.ctx` | 当前 WfuiContext |
|
|
51
48
|
|
|
52
49
|
---
|
|
@@ -54,7 +51,7 @@ app.destroy()
|
|
|
54
51
|
## 组件模型
|
|
55
52
|
|
|
56
53
|
```tsx
|
|
57
|
-
import type { Component, WfuiContext } from 'weifuwu/
|
|
54
|
+
import type { Component, WfuiContext } from 'weifuwu/ui-dom'
|
|
58
55
|
|
|
59
56
|
// 两阶段组件:mount(只一次)→ render(每次 dirty/props 变化)
|
|
60
57
|
const Counter: Component = (_init, ctx) => {
|
|
@@ -74,8 +71,8 @@ const Badge: Component = () =>
|
|
|
74
71
|
### 类型流(props 泛型 + ctx 注入)
|
|
75
72
|
|
|
76
73
|
```tsx
|
|
77
|
-
import type { Component } from 'weifuwu/
|
|
78
|
-
import type { ApiInjected, RouteInjected } from 'weifuwu/
|
|
74
|
+
import type { Component } from 'weifuwu/ui-dom'
|
|
75
|
+
import type { ApiInjected, RouteInjected } from 'weifuwu/ui-dom'
|
|
79
76
|
|
|
80
77
|
// ① props 泛型:JSX 使用时自动类型检查(传错类型编译期报错)
|
|
81
78
|
interface DeckCardProps { title: string; pages: number }
|
|
@@ -92,13 +89,13 @@ const Home: Component<{}, ApiInjected & RouteInjected> = (_init, ctx) => {
|
|
|
92
89
|
}
|
|
93
90
|
// 未声明的注入字段编译期报错——注入从"文档约定"变成"类型保证"
|
|
94
91
|
|
|
95
|
-
|
|
92
|
+
const app = new UIRouter<{}>()
|
|
96
93
|
.use(api()) // 注入 ctx.api
|
|
97
|
-
.use(
|
|
98
|
-
|
|
94
|
+
.use(toast()) // 注入 ctx.toast
|
|
95
|
+
uiServe(app, { root: '#root' }) // 类型累积完整(UIRouter<C & O>)
|
|
99
96
|
```
|
|
100
97
|
|
|
101
|
-
> 各中间件的注入接口:`api()` → `ApiInjected`、`auth()` → `AuthInjected`、`ws()` → `WsInjected`、`i18n()` → `I18nInjected`、`router()` → `RouteInjected`(均可从 `weifuwu/
|
|
98
|
+
> 各中间件的注入接口:`api()` → `ApiInjected`、`auth()` → `AuthInjected`、`ws()` → `WsInjected`、`i18n()` → `I18nInjected`、`router()` → `RouteInjected`(均可从 `weifuwu/ui-dom` 导入)。
|
|
102
99
|
|
|
103
100
|
| 规则 | 说明 |
|
|
104
101
|
|------|------|
|
|
@@ -113,8 +110,8 @@ createApp()
|
|
|
113
110
|
### JSX 工厂
|
|
114
111
|
|
|
115
112
|
```tsx
|
|
116
|
-
// 由 esbuild 自动调用(jsxImportSource: 'weifuwu/
|
|
117
|
-
import { h, jsx, jsxs, jsxDEV, Fragment } from 'weifuwu/
|
|
113
|
+
// 由 esbuild 自动调用(jsxImportSource: 'weifuwu/ui-dom')
|
|
114
|
+
import { h, jsx, jsxs, jsxDEV, Fragment } from 'weifuwu/ui-dom'
|
|
118
115
|
|
|
119
116
|
// h 支持 variadic children
|
|
120
117
|
h('div', { class: 'x' }, child1, child2)
|
|
@@ -131,7 +128,7 @@ h('div', { class: 'x' }, child1, child2)
|
|
|
131
128
|
| `Portal` / `createPortal(children, portalKey?)` | 渲染到 `document.body#__wf_portal` 独立容器(弹层/对话框,脱离父级 overflow 裁剪) |
|
|
132
129
|
|
|
133
130
|
```tsx
|
|
134
|
-
import { createPortal } from 'weifuwu/
|
|
131
|
+
import { createPortal } from 'weifuwu/ui-dom'
|
|
135
132
|
|
|
136
133
|
// 内容渲染到 body 下的独立容器(不在父组件的 DOM 树内)
|
|
137
134
|
const Tooltip = (_init, ctx) =>
|
|
@@ -754,7 +751,7 @@ const UserProfile: Component = (initProps, ctx) => {
|
|
|
754
751
|
}
|
|
755
752
|
```
|
|
756
753
|
|
|
757
|
-
### async
|
|
754
|
+
### async 组件(原生)
|
|
758
755
|
|
|
759
756
|
组件 = 函数,async 组件 = async 函数:签名与同步组件一致 `(initProps, ctx) => renderFn`,渲染器按「返回值是 Promise」原生判别。数据经闭包注入,渲染无 loading 分支:
|
|
760
757
|
|
|
@@ -771,9 +768,9 @@ const UserProfile = async (initProps, ctx) => {
|
|
|
771
768
|
}
|
|
772
769
|
```
|
|
773
770
|
|
|
774
|
-
-
|
|
771
|
+
- **客户端**:主路径 `buildVNode` async 预构建(await 全部工厂;兄弟并行)→ 落地零占位;动态挂载兑底占位 → 局部补全——N 处实例 = N 次工厂调用,数据走 `ctx.data` 则零成本(缓存 + 并发合并)
|
|
775
772
|
- **服务端**:`ctx.ui.ssr()` 直接 await 工厂 → 数据进 HTML(无占位)
|
|
776
|
-
-
|
|
773
|
+
- **占位显示**:动态挂载的 async 组件占位 = 注释节点,resolve 后局部补全(Suspense 边界已裁剪)
|
|
777
774
|
- 会变的数据:初始值 seed 自服务端数据(`$.count = data.count`),交互改 `$`;初始状态必须确定性(禁止 `window.innerWidth` 直接初始化 → SSR/hydration mismatch)
|
|
778
775
|
|
|
779
776
|
---
|
|
@@ -781,15 +778,15 @@ const UserProfile = async (initProps, ctx) => {
|
|
|
781
778
|
## 前端类型
|
|
782
779
|
|
|
783
780
|
```tsx
|
|
784
|
-
import type { VNode, VNodeType, Component, WfuiContext, AppMiddleware, RouteDef } from 'weifuwu/
|
|
785
|
-
import type { ApiClient, ApiOptions, ApiRequestOptions, ApiError } from 'weifuwu/
|
|
786
|
-
import type { AuthClient, AuthOptions } from 'weifuwu/
|
|
787
|
-
import type { ErrorBoundaryProps } from 'weifuwu/
|
|
788
|
-
import type { I18nOptions, I18nState, LocalePackage } from 'weifuwu/
|
|
789
|
-
import type { PopupPositionOptions, PopupPosition } from 'weifuwu/
|
|
781
|
+
import type { VNode, VNodeType, Component, WfuiContext, AppMiddleware, RouteDef } from 'weifuwu/ui-dom'
|
|
782
|
+
import type { ApiClient, ApiOptions, ApiRequestOptions, ApiError } from 'weifuwu/ui-dom'
|
|
783
|
+
import type { AuthClient, AuthOptions } from 'weifuwu/ui-dom'
|
|
784
|
+
import type { ErrorBoundaryProps } from 'weifuwu/ui-dom'
|
|
785
|
+
import type { I18nOptions, I18nState, LocalePackage } from 'weifuwu/ui-dom'
|
|
786
|
+
import type { PopupPositionOptions, PopupPosition } from 'weifuwu/ui-dom'
|
|
790
787
|
import type { ConfirmProps, ConfirmOptions } from 'weifuwu/components'
|
|
791
788
|
import type { ToastOptions, ToastPosition } from 'weifuwu/components'
|
|
792
|
-
import type { RouterOptions } from 'weifuwu/
|
|
789
|
+
import type { RouterOptions } from 'weifuwu/ui-dom'
|
|
793
790
|
```
|
|
794
791
|
|
|
795
792
|
| 类型 | 说明 |
|
package/docs/layout.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> 本页为 weifuwu 官方文档拆分页 · [返回 README](../README.md)
|
|
4
4
|
|
|
5
|
-
纯 CSS
|
|
5
|
+
纯 CSS 58 个布局原语 + 136 个工具类 + 167 个主题 Token。不绑定任何 JS 框架。
|
|
6
6
|
|
|
7
7
|
> **学习路径与命名规范**:见 [`design/style-guide.md`](../design/style-guide.md)——统一语法 `wf-<域>-<名>`、三档学习(组件 → 10 核心原语 → 完整速查)、场景速查、变量定制。
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ app.get('/layout.css', (req, ctx) => ctx.ui.css('weifuwu/layout'))
|
|
|
25
25
|
|
|
26
26
|
也支持相对路径:`ctx.ui.css('./src/style.css')`。
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## 58 个布局原语
|
|
29
29
|
|
|
30
30
|
| 类别 | 原语 | 效果 |
|
|
31
31
|
|------|------|------|
|
package/docs/realtime.md
CHANGED
|
@@ -70,13 +70,12 @@ app.get('/page', (req, ctx) => ctx.ui.html`
|
|
|
70
70
|
```ts
|
|
71
71
|
app.get('/app.js', (req, ctx) => ctx.ui.js('./src/main.tsx')) // 相对路径
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
app.get('/app.js', (req, ctx) => ctx.ui.js('weifuwu/client')) // 或包名
|
|
73
|
+
app.get('/app.js', (req, ctx) => ctx.ui.js('weifuwu/ui-dom')) // 或包名(exports map 解析)
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
使用 esbuild 编译:
|
|
78
77
|
- `bundle: true`, `format: 'esm'`, `platform: 'browser'`
|
|
79
|
-
- `jsx: 'automatic'`, `jsxImportSource: 'weifuwu/
|
|
78
|
+
- `jsx: 'automatic'`, `jsxImportSource: 'weifuwu/ui-dom'`
|
|
80
79
|
- 带 mtime 缓存验证(开发时编辑文件后自动失效)
|
|
81
80
|
|
|
82
81
|
### ctx.ui.css — CSS 编译
|
|
@@ -129,10 +128,11 @@ app.get('/blog/:slug', async (req, ctx) => {
|
|
|
129
128
|
服务端 HTML + `window.__DATA__`(ctx.data 种子)到达客户端后,`mount(..., { hydrate: true })` **收养现有 DOM**(不重建、不闪跳),只接线事件/ref/$:
|
|
130
129
|
|
|
131
130
|
```ts
|
|
132
|
-
import {
|
|
131
|
+
import { UIRouter, uiServe } from 'weifuwu/ui-dom'
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
const app = new UIRouter()
|
|
134
|
+
app.get('/blog/:slug', (loc, ctx) => h(BlogPage, { slug: ctx.params.slug }))
|
|
135
|
+
uiServe(app, { root: '#root', hydrate: true }) // 容器已有服务端 HTML → 收养
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
- **游标收养**:元素/文本按位置匹配现有 DOM;tag 不匹配 → 局部替换;文本不一致 → 就地修正;服务端多余节点 → 收尾清理
|
|
@@ -140,32 +140,27 @@ createApp()
|
|
|
140
140
|
- hydration 后 `$`/dirty/事件全量可用(与纯 SPA 无差别)
|
|
141
141
|
- 诚实裁剪:Portal 内容就地收养(不移动到 `#__wf_portal`);渲染期非确定性(Date/random)会导致 mismatch(dev 警告)
|
|
142
142
|
|
|
143
|
-
###
|
|
143
|
+
### 路由级 SSR(UIRouter 两端共享)
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
同一份 UIRouter 路由定义,后端匹配即自动 SSR,无需手写 handler/模板/序列化:
|
|
146
146
|
|
|
147
147
|
```tsx
|
|
148
|
-
//
|
|
149
|
-
import
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
// blog-hydrate.ts —— 客户端:同一份 routes,router() 注入 ctx.route.params(两端同源)
|
|
161
|
-
createApp()
|
|
162
|
-
.use(router({ routes }))
|
|
163
|
-
.mount('#root', routes[0].component, { hydrate: true })
|
|
148
|
+
// router.ts —— 前后端共用
|
|
149
|
+
import { UIRouter, h } from 'weifuwu/ui-dom'
|
|
150
|
+
|
|
151
|
+
export const app = new UIRouter()
|
|
152
|
+
app.get('/blog/:slug', (loc, ctx) => h(BlogPage, { slug: ctx.params.slug }))
|
|
153
|
+
|
|
154
|
+
// server.ts —— ssrPage 服务端落地:完整 HTML + __DATA__ + styles
|
|
155
|
+
import { ssrPage } from 'weifuwu/ui-dom'
|
|
156
|
+
const { page } = await ssrPage(app, { url: '/blog/hello', styles: ['/components.css'] })
|
|
157
|
+
|
|
158
|
+
// client.ts —— uiServe 客户端收养(hydrate: true)
|
|
159
|
+
uiServe(app, { root: '#root', hydrate: true })
|
|
164
160
|
```
|
|
165
161
|
|
|
166
|
-
- 组件工厂读 `ctx.
|
|
167
|
-
-
|
|
168
|
-
- 可自定义 `title` / `template`
|
|
162
|
+
- 组件工厂读 `ctx.params`(`/blog/:slug` → `ctx.params.slug`)——两端同源注入
|
|
163
|
+
- `ssrPage` 选项:`styles`(stylesheet link)/ `title` / `lang` / `rootId`
|
|
169
164
|
|
|
170
165
|
### weifuwu/dev — 服务端直接跑 .tsx
|
|
171
166
|
|
|
@@ -180,7 +175,7 @@ Node 原生 TS 只剥离类型(不支持 JSX)。`weifuwu/dev` 注册 esbuild
|
|
|
180
175
|
}
|
|
181
176
|
```
|
|
182
177
|
|
|
183
|
-
- 前后端同一 JSX 运行时(`jsxImportSource: weifuwu/
|
|
178
|
+
- 前后端同一 JSX 运行时(`jsxImportSource: weifuwu/ui-dom`)→ 两端 VNode 一致 → hydration 可靠
|
|
184
179
|
- 与 `ctx.ui.js` 前端动态编译同一理念:无构建、无产物、改代码即生效
|
|
185
180
|
|
|
186
181
|
---
|
package/docs/saas.md
CHANGED
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
```ts
|
|
13
13
|
import { rateLimit } from 'weifuwu'
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
app.use(
|
|
15
|
+
const r = redis() // 注意:REDIS_URL 缺 env 构造抛错
|
|
16
|
+
app.use(r) // 依赖 ctx.redis
|
|
17
|
+
app.use(rateLimit({ redis: r.redis, windowMs: 60_000, max: 100 })) // redis 必传(模式 A 显式注入)
|
|
17
18
|
|
|
18
19
|
app.get('/api/search', async (req, ctx) => {
|
|
19
20
|
await ctx.limit('search', { max: 30, windowMs: 60_000 }) // 手动限流,超限抛 429
|
|
@@ -77,7 +78,7 @@ app.post('/secure', (req, ctx) => { ctx.auth.requireAuth(); ... })
|
|
|
77
78
|
|
|
78
79
|
|
|
79
80
|
> ⚠️ **weifuwu/client 已删除**——前端运行时唯一入口为 `weifuwu/ui-dom`,见 [frontend-ui-dom.md](frontend-ui-dom.md)。
|
|
80
|
-
- **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/
|
|
81
|
+
- **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/ui-dom` 的 `auth()` 天然配对);refresh token = 不透明随机串,DB 只存哈希,logout/轮换即撤销
|
|
81
82
|
- **防枚举**:登录失败统一 401(不泄露邮箱是否存在)
|
|
82
83
|
- **`ctx.auth` 方法面**:`register` / `login` / `logout` / `requireAuth` / `setPassword(userId, newPwd)` / `createToken(type, payload, { ttlSeconds })`(邮箱验证/密码重置自接)
|
|
83
84
|
- **多租户感知**:`issueSession` 的 token payload 携带 `tenantId`(来自 `user.tenant`)——中间件自动注入 `ctx.tenantId`,并将会话字段(userId/tenantId/email/name/role)合并到 `ctx.auth`,多租户应用免写 token 解码/租户中间件(数据隔离 SQL 是应用职责)
|
|
@@ -117,7 +118,8 @@ ctx.msg.sendTo('u2', { type: 'mention' }) // 用户维度点对点
|
|
|
117
118
|
```ts
|
|
118
119
|
import { queue } from 'weifuwu'
|
|
119
120
|
|
|
120
|
-
const
|
|
121
|
+
const r = redis(); app.use(r) // Redis 中间件(注入 ctx.redis)
|
|
122
|
+
const q = queue({ redis: r.redis }) // queue 必传 Redis(池命令走轮询连接)
|
|
121
123
|
app.use(q) // 注入 ctx.queue
|
|
122
124
|
|
|
123
125
|
app.post('/api/generate', async (req, ctx) => {
|
|
@@ -184,10 +186,10 @@ app.post('/api/approve', async (req, ctx) => {
|
|
|
184
186
|
})
|
|
185
187
|
```
|
|
186
188
|
|
|
187
|
-
前端解码(`weifuwu/
|
|
189
|
+
前端解码(`weifuwu/ui-dom`):
|
|
188
190
|
|
|
189
191
|
```ts
|
|
190
|
-
import { aiStream } from 'weifuwu/
|
|
192
|
+
import { aiStream } from 'weifuwu/ui-dom'
|
|
191
193
|
|
|
192
194
|
const handle = aiStream('/api/chat', { messages }, {
|
|
193
195
|
onToken: (text) => { /* 增量 append 到消息 */ },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "weifuwu",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.76.0",
|
|
5
5
|
"description": "AI SaaS framework — (req, ctx) => Response",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"types": "./dist/ui-dom/index.d.ts",
|
|
17
17
|
"default": "./dist/ui-dom/jsx-runtime.js"
|
|
18
18
|
},
|
|
19
|
+
"./ui-dom/testing": {
|
|
20
|
+
"types": "./dist/ui-dom/testing.d.ts",
|
|
21
|
+
"default": "./dist/ui-dom/testing.js"
|
|
22
|
+
},
|
|
19
23
|
"./layout": "./dist/layout/weifuwu-layout.css",
|
|
20
24
|
"./layout/style.css": "./dist/layout/weifuwu-layout.css",
|
|
21
25
|
"./components": {
|
package/dist/ui/ssr-page.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* uiSsr — 路由级 SSR 中间件:前端路由表驱动的自动服务端渲染
|
|
3
|
-
*
|
|
4
|
-
* 开发者只声明路由(path + component),GET 匹配即自动 SSR:
|
|
5
|
-
* 匹配 → 注入 params → ctx.ui.ssr 渲染组件(async 工厂 await)→ 页面模板 + __DATA__
|
|
6
|
-
* 未匹配 → next()(交给后续 API/静态/404)
|
|
7
|
-
*
|
|
8
|
-
* 与客户端 router() 共用同一份路由定义(route-match.ts),SPA/SSR 一个声明。
|
|
9
|
-
*/
|
|
10
|
-
import type { Middleware } from '../types.ts';
|
|
11
|
-
import type { RouteDef } from '../ui-dom/types.ts';
|
|
12
|
-
export interface UiSsrOptions {
|
|
13
|
-
/** 前端路由定义(与 router() 共享同一份) */
|
|
14
|
-
routes: RouteDef[];
|
|
15
|
-
/** 客户端 bundle 路径(自动注入 <script src>,如 '/static/app.js') */
|
|
16
|
-
bundle?: string;
|
|
17
|
-
/** CSS 路径数组(自动注入 <link rel="stylesheet">,如 ['/static/style.css']) */
|
|
18
|
-
styles?: string[];
|
|
19
|
-
/** 自定义 title(默认取路由定义 title 或 'weifuwu') */
|
|
20
|
-
title?: (ctx: {
|
|
21
|
-
params: Record<string, string>;
|
|
22
|
-
def: RouteDef;
|
|
23
|
-
}) => string;
|
|
24
|
-
/** 自定义页面模板:返回完整 HTML(默认内置,含 head/root/__DATA__/styles/bundle) */
|
|
25
|
-
template?: (parts: {
|
|
26
|
-
html: string;
|
|
27
|
-
dataScript: string;
|
|
28
|
-
title: string;
|
|
29
|
-
bundle?: string;
|
|
30
|
-
styles?: string[];
|
|
31
|
-
}) => string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* 路由级 SSR 中间件:
|
|
35
|
-
*
|
|
36
|
-
* ```ts
|
|
37
|
-
* const app = new Router()
|
|
38
|
-
* app.use(uiSsr({ routes, bundle: '/static/app.js' }))
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export declare function uiSsr(opts: UiSsrOptions): Middleware;
|
package/dist/ui/ssr.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 服务端 SSR 字符串遍历器 — async 工厂组件(形态 C)的 HTML 序列化
|
|
3
|
-
*
|
|
4
|
-
* 支持:
|
|
5
|
-
* - 同步组件(mount → render → VNode → HTML)
|
|
6
|
-
* - async 工厂组件(await 工厂 → 数据进 HTML;服务端不缓存定义,数据 per-request)
|
|
7
|
-
* - Fragment / Portal(内联子节点)
|
|
8
|
-
* - innerHTML prop(原样输出)、class/style 对象序列化
|
|
9
|
-
* - 事件处理器 / ref 剥离、文本自动转义(XSS)
|
|
10
|
-
* - 服务端 ctx shim($ no-op dirty、ctx.data 预取、selfId 隔离)
|
|
11
|
-
*
|
|
12
|
-
* 产物为 HtmlSafe 标记的安全 HTML 片段,可直接内联进 ctx.ui.html 模板。
|
|
13
|
-
*/
|
|
14
|
-
import type { Component, AsyncComponent } from '../ui-dom/vnode.ts';
|
|
15
|
-
import { HtmlSafe } from './html-safe.ts';
|
|
16
|
-
export { HtmlSafe } from './html-safe.ts';
|
|
17
|
-
export interface SsrOptions {
|
|
18
|
-
/** 服务端数据存储:ctx.data 预取结果写入此处,调用方序列化进 __DATA__ */
|
|
19
|
-
data?: Map<string, unknown>;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* SSR 渲染组件 → HTML 片段(HtmlSafe 标记,可直接内联进 ctx.ui.html 模板,不二次转义)。
|
|
23
|
-
*
|
|
24
|
-
* ```ts
|
|
25
|
-
* const data = new Map()
|
|
26
|
-
* const html = await ctx.ui.ssr(BlogPage, { slug }, { data })
|
|
27
|
-
* return ctx.ui.html`
|
|
28
|
-
* <div id="root">${html}</div>
|
|
29
|
-
* ${ctx.ui.ssrData(data)}
|
|
30
|
-
* `
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export declare function ssrToString(Comp: Component | AsyncComponent, props: Record<string, any>, serverCtx: any, opts?: SsrOptions): Promise<HtmlSafe>;
|
|
34
|
-
/** 序列化数据存储 → window.__DATA__ 脚本(防 XSS:转义 <) */
|
|
35
|
-
export declare function serializeData(data: Map<string, unknown>): string;
|
package/dist/ui-dom/diff.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
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 { VNodeChild } from './vnode.ts';
|
|
10
|
-
import type { WfuiContext } from './types.ts';
|
|
11
|
-
export declare function patchValue(parent: Node, oldNode: Node | null, oldInput: VNodeChild, newInput: VNodeChild, ctx: WfuiContext): Node | null;
|
|
12
|
-
export declare function patchProps(el: Element, oldProps: Record<string, unknown> | null, newProps: Record<string, unknown>): void;
|
|
13
|
-
/** 为无 key 的子节点自动分配位置 key,确保 keyed diff 正确性 */
|
|
14
|
-
export declare function ensureKeys(oldChildren: VNodeChild[], newChildren: VNodeChild[]): void;
|
|
15
|
-
/**
|
|
16
|
-
* 将 children 映射到它们实际产生的 DOM 节点(每个 child 一个 Node[])。
|
|
17
|
-
*
|
|
18
|
-
* 1 VNode ≠ 1 DOM 节点:Fragment 展开成多个直属节点(_childNodes),
|
|
19
|
-
* null/boolean/Portal 产生 0 个。explicitNodes 提供限定范围(Fragment 子项
|
|
20
|
-
* 用其自身 _childNodes),否则从 source(parent.childNodes 或给定快照)按计数游标分配。
|
|
21
|
-
*/
|
|
22
|
-
export declare function mapChildDomNodes(source: Node[], children: VNodeChild[]): (Node[] | null)[];
|
|
23
|
-
export declare function normalize(children: VNodeChild): VNodeChild[];
|
|
24
|
-
/**
|
|
25
|
-
* keyed 子节点 diff。返回每个 new child 的 DOM 节点范围(newNodes,可为空)。
|
|
26
|
-
* oldNodes:old children → 实际 DOM 节点(mapChildDomNodes 结果)。
|
|
27
|
-
*/
|
|
28
|
-
export declare function patchKeyedChildren(parent: Node, oldChildren: VNodeChild[], newChildren: VNodeChild[], ctx: WfuiContext, oldNodes?: (Node[] | null)[], rangeStart?: Node | null): (Node[] | null)[];
|
|
@@ -1,13 +0,0 @@
|
|
|
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>;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* weifuwu/ui-dom 注册表 — 工厂版(serve 创建局部实例,隔离于 createApp 的模块级状态)
|
|
3
|
-
*
|
|
4
|
-
* 复制自 client/registry.ts(算法相同),但**无模块级单例**——uiServe 创建实例
|
|
5
|
-
* 注入 ctx.__registry,render/diff/ui 经 getRegistry(ctx) 读取,与 createApp 零交叉。
|
|
6
|
-
*/
|
|
7
|
-
import type { VNode, Component, AsyncComponent } from './vnode.ts';
|
|
8
|
-
import type { WfuiContext } from './types.ts';
|
|
9
|
-
type UnmountHook = (id: string) => void;
|
|
10
|
-
interface FactoryEntry {
|
|
11
|
-
promise: Promise<Component<any, any>>;
|
|
12
|
-
resolved?: Component<any, any>;
|
|
13
|
-
}
|
|
14
|
-
/** 注册表实例状态 */
|
|
15
|
-
export interface Registry {
|
|
16
|
-
idCounter: number;
|
|
17
|
-
idRegistry: Map<string, VNode>;
|
|
18
|
-
unmountHooks: UnmountHook[];
|
|
19
|
-
asyncFactoryCache: WeakMap<AsyncComponent<any, any>, FactoryEntry>;
|
|
20
|
-
}
|
|
21
|
-
/** 创建局部注册表(uiServe 每实例一个——组件 id/dirty/卸载钩子与 createApp 隔离) */
|
|
22
|
-
export declare function createRegistry(): Registry;
|
|
23
|
-
/** 从 ctx 取注入的注册表(render/diff/ui 统一入口;serve 注入 __registry) */
|
|
24
|
-
export declare function getRegistry(ctx: any): Registry;
|
|
25
|
-
/** 指定实例生成组件 ID */
|
|
26
|
-
export declare function nextComponentIdFor(reg: Registry): string;
|
|
27
|
-
/** 指定实例注册卸载钩子(组件从 idRegistry 注销时触发) */
|
|
28
|
-
export declare function onComponentUnmountFor(reg: Registry, hook: UnmountHook): () => void;
|
|
29
|
-
/** 启动 async 工厂(幂等,缓存) */
|
|
30
|
-
export declare function startAsyncFactory(reg: Registry, Comp: AsyncComponent, ctx: WfuiContext): FactoryEntry;
|
|
31
|
-
/** async 模式:await 工厂定义 */
|
|
32
|
-
export declare function resolveAsyncFactory(reg: Registry, Comp: AsyncComponent, ctx: WfuiContext): Promise<Component>;
|
|
33
|
-
/** sync 模式:工厂已解析 → 定义;未解析 → undefined */
|
|
34
|
-
export declare function resolveAsyncFactorySync(reg: Registry, Comp: AsyncComponent): Component | undefined;
|
|
35
|
-
/** 包裹 ref 回调调用——用户 ref 逻辑抛错时不中断渲染/卸载管线 */
|
|
36
|
-
export declare function safeCallRef(ref: Function, arg: any, phase: 'mount' | 'cleanup', name?: string): void;
|
|
37
|
-
/** 通知 ref 清理 + Portal 子容器清理(指定实例) */
|
|
38
|
-
export declare function callRefCleanupFor(input: any, reg: Registry): void;
|
|
39
|
-
export {};
|
package/dist/ui-dom/render.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* weifuwu/client 渲染器 — VNode → DOM + patchValue diff
|
|
3
|
-
*
|
|
4
|
-
* render(vnode, ctx) → 首次渲染,返回 DOM
|
|
5
|
-
* patchValue(el, old, new, ctx) → 增量更新
|
|
6
|
-
*
|
|
7
|
-
* 支持:
|
|
8
|
-
* - key 属性(keyed diff)
|
|
9
|
-
* - ref / keyed diff
|
|
10
|
-
*
|
|
11
|
-
* 状态管理:组件使用闭包变量 + ctx.ui.render() 手动触发重渲染。
|
|
12
|
-
*/
|
|
13
|
-
import type { VNode, VNodeChild, Component, AsyncComponent } from './vnode.ts';
|
|
14
|
-
import type { WfuiContext } from './types.ts';
|
|
15
|
-
export declare const SVG_NS = "http://www.w3.org/2000/svg";
|
|
16
|
-
export declare const SVG_TAGS: Set<string>;
|
|
17
|
-
export declare function render(input: VNodeChild, ctx: WfuiContext): Node | null;
|
|
18
|
-
export declare function renderValue(v: VNodeChild, ctx: WfuiContext): Node | null;
|
|
19
|
-
/**
|
|
20
|
-
* 异步组件工厂缓存:同一工厂只执行一次,多实例/多渲染共享。
|
|
21
|
-
* resolved 记录已解析的定义(同步快速路径用)。
|
|
22
|
-
* (缓存本体在 registry.ts,本文件仅保留依赖 render 的调度函数)
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* 占位完成后整树重渲染(async 工厂已解析,diff 收敛到目标位置)。
|
|
26
|
-
* 优先走 serve 的调度(重新执行 handler → 新树 → diff 传递 _asyncDef);
|
|
27
|
-
* 无 serve 注入(SSR/独立渲染)时回退 ui.render(['_wf_root'])。
|
|
28
|
-
*/
|
|
29
|
-
export declare function scheduleFullReRender(ctx: WfuiContext): void;
|
|
30
|
-
/**
|
|
31
|
-
* 同步 mount 组件(async 工厂占位策略):
|
|
32
|
-
* - 同步组件 → def(props, ctx) → render fn → 输出 VNode
|
|
33
|
-
* - async 工厂已解析 → 同同步组件
|
|
34
|
-
* - async 工厂未解析 → 占位(返回 null)+ 启动工厂 + 完成后整树重渲染
|
|
35
|
-
*/
|
|
36
|
-
export declare function mountComponent(Comp: Component | AsyncComponent, props: VNode['props'], vnode: VNode, ctx: WfuiContext): VNode | null;
|
|
37
|
-
/** 首次渲染 Portal:创建远程容器、渲染子节点(不返回占位节点) */
|
|
38
|
-
export declare function renderPortal(vnode: VNode, ctx: WfuiContext): void;
|
|
39
|
-
/** 更新 Portal:复用远程容器,patch 子节点(不操作父 DOM) */
|
|
40
|
-
export declare function patchPortal(oldV: VNode | null, newV: VNode, ctx: WfuiContext): void;
|
|
41
|
-
/** 展平嵌套数组 */
|
|
42
|
-
export declare function flattenChildren(children: VNodeChild): VNodeChild[];
|
|
43
|
-
export declare function mountVNode(container: Element, vnode: VNode, ctx: WfuiContext): void;
|
|
44
|
-
export { patchValue } from './diff.ts';
|
|
45
|
-
/** 模块级兼容(无 ctx 场景——独立清理,不影响活跃 registry) */
|
|
46
|
-
export declare function callRefCleanup(input: any): void;
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 路由匹配纯函数 — 客户端 router 与服务端 uiSsr 共用(无 DOM 依赖)
|
|
3
|
-
*
|
|
4
|
-
* 支持路径参数 :id、通配符 *、嵌套路由(children → chain)。
|
|
5
|
-
* 与后端 Router(Trie)独立——本模块服务于"前端路由表驱动的页面"。
|
|
6
|
-
*/
|
|
7
|
-
import type { RouteDef } from './types.ts';
|
|
8
|
-
export interface FlattenedRoute {
|
|
9
|
-
re: RegExp;
|
|
10
|
-
keys: string[];
|
|
11
|
-
def: RouteDef;
|
|
12
|
-
chain: RouteDef[];
|
|
13
|
-
}
|
|
14
|
-
export declare function joinPaths(a: string, b: string): string;
|
|
15
|
-
export declare function compilePath(path: string): {
|
|
16
|
-
re: RegExp;
|
|
17
|
-
keys: string[];
|
|
18
|
-
};
|
|
19
|
-
export declare function flattenRoutes(routes: RouteDef[], basePath?: string, chain?: RouteDef[]): FlattenedRoute[];
|
|
20
|
-
export declare function matchRoute(path: string, routes: FlattenedRoute[]): FlattenedRoute | null;
|
|
21
|
-
/** 从匹配结果提取路径参数 */
|
|
22
|
-
export declare function extractParams(path: string, match: FlattenedRoute): Record<string, string>;
|
package/dist/ui-dom/serve.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* weifuwu/ui-dom serve — 渲染运行时(VDOM 落地机制)
|
|
3
|
-
*
|
|
4
|
-
* 定稿架构:serveUI = VDOM(落地,对齐后端 serve(router) = HTTP 传输)。
|
|
5
|
-
*
|
|
6
|
-
* uiServe(router, { root }) 调用时路由已全部注册——serve 负责:
|
|
7
|
-
* 1. 创建渲染运行时:局部 registry + createUi(19 原语)+ popup-tracker + ctx.data/browser
|
|
8
|
-
* 2. 监听 popstate/hashchange → router.match(params 注入)→ 中间件链 → handler → vnode
|
|
9
|
-
* 3. renderValue / patchValue diff/patch DOM;组件 $ dirty → renderByIds(局部 registry)
|
|
10
|
-
*
|
|
11
|
-
* 与 createApp 隔离:registry/popup-tracker/dirty 集合全部局部实例(client 零改动)。
|
|
12
|
-
*/
|
|
13
|
-
import type { UIRouter } from './router.ts';
|
|
14
|
-
import type { WfuiContext } from './types.ts';
|
|
15
|
-
/** uiServe 选项 */
|
|
16
|
-
export interface UIServeOptions {
|
|
17
|
-
root: string | Element;
|
|
18
|
-
hydrate?: boolean;
|
|
19
|
-
}
|
|
20
|
-
/** serve 句柄 */
|
|
21
|
-
export interface UIServeHandle<C extends object = {}> {
|
|
22
|
-
/** 释放全部资源(监听/渲染状态/注册表) */
|
|
23
|
-
close(): void;
|
|
24
|
-
/** 当前 ctx(调试/测试用)——含 UIRouter ctx 注入的类型扩展 */
|
|
25
|
-
ctx: WfuiContext & C;
|
|
26
|
-
}
|
|
27
|
-
/** uiServe — 绑定唯一根节点 + URL 驱动渲染(= VDOM 落地,对齐 serve(router)) */
|
|
28
|
-
export declare function uiServe<RC extends object = {}>(router: UIRouter<RC>, options: UIServeOptions): UIServeHandle<RC>;
|