weifuwu 0.33.11 → 0.34.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 CHANGED
@@ -6,7 +6,7 @@
6
6
  npm install weifuwu
7
7
  ```
8
8
 
9
- 一个包,无上游依赖。后端提供 HTTP 路由、数据库、中间件;前端提供信号驱动的无 VDOM 响应式框架。
9
+ 一个包,无上游依赖。后端提供 HTTP 路由、数据库、中间件;前端提供 VDOM + Proxy 驱动的前端框架。
10
10
 
11
11
  ---
12
12
 
@@ -22,21 +22,19 @@ npm install weifuwu
22
22
  | **redis** | `redis` | Redis 客户端 → `ctx.redis` | `Router` |
23
23
  | **ui** | `ui` | SSR 渲染 + 动态 JS 编译 → `ctx.ui.html/css/js` | `Router` |
24
24
  | **graphql** | `router.graphql()` | GraphQL 端点 | `Router` |
25
- | **client** | 28 个导出 | 前端响应式框架(见下方表格) | — |
25
+ | **client** | | 前端 VDOM + Proxy 框架 | — |
26
26
 
27
27
  **前端 `weifuwu/client` 模块总览:**
28
28
 
29
29
  | 类别 | 导出 | 用途 |
30
30
  |------|------|------|
31
- | **信号系统** | `signal`, `computed`, `effect`, `batch`, `untrack`, `isSignal` | 响应式状态 |
32
- | **JSX 运行时** | `jsx`/`jsxs`/`jsxDEV`, `Fragment` | TSX 编译目标 |
33
- | **控制流** | `Show`, `For` | 条件/列表渲染 |
34
- | **生命周期** | `onMount`, `onCleanup` | 组件挂载/卸载 |
35
- | **应用** | `createApp` | 中间件链 + 挂载 |
36
- | **路由** | `router`, `RouteView`, `lazy` | 嵌套布局 + 代码分割 + 滚动恢复 |
37
- | **中间件** | `ws`, `api`, `auth` | WebSocket / HTTP 客户端 / 认证状态 |
38
- | **工具** | `createResource`, `useForm`, `ErrorBoundary`, `createPortal`, `wrap`, `createContext`, `extendCtx`, `domMount` | 异步数据 / 表单 / 错误边界 / Portal |
39
- | **类型 (19)** | `Signal`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `AuthClient`, `ResourceState`, `FormReturn`, 等 | — |
31
+ | **渲染引擎** | `VNode`, `Component` | 虚拟 DOM 节点与组件类型 | |
32
+ | **JSX 运行时** | `jsx`/`jsxs`/`jsxDEV`, `Fragment` | TSX 编译目标 | — |
33
+ | **应用** | `createApp` | 中间件链 + 挂载 | — |
34
+ | **路由** | `router`, `RouteView` | 嵌套布局路由 | — |
35
+ | **中间件** | `ws`, `api`, `auth` | WebSocket / HTTP 客户端 / 认证状态 | — |
36
+ | **工具** | `extendCtx` | ctx 扩展 | |
37
+ | **类型** | `WfuiContext`, `AppMiddleware`, `RouteDef`, `VNodeType`, `Component`, `ApiClient`, `AuthClient` | | |
40
38
 
41
39
  ---
42
40
 
@@ -94,13 +92,16 @@ serve(app, { port: 3000 })
94
92
 
95
93
  ```tsx
96
94
  // src/main.tsx — 前端
97
- import {
98
- signal, computed, Show, For, ErrorBoundary, createPortal, wrap,
99
- createApp, router, RouteView, lazy,
100
- ws, api, auth, useForm, createResource,
101
- } from 'weifuwu/client'
95
+ import { createApp, router, RouteView, ws, api, auth } from 'weifuwu/client'
102
96
  import type { WfuiContext, RouteDef } from 'weifuwu/client'
103
97
 
98
+ // 组件 = (props, ctx) => VNode
99
+ function Home(_props: {}, ctx: WfuiContext) {
100
+ const $ = ctx.ui.$
101
+ if (!ctx.ui.ready) { $.items = [{ id: 1, text: 'hello' }] }
102
+ return <div>{$.items.map((i: any) => <div key={i.id}>{i.text}</div>)}</div>
103
+ }
104
+
104
105
  const app = createApp()
105
106
  app.use(api({ baseURL: '' }))
106
107
  app.use(auth())
@@ -111,9 +112,6 @@ app.use(router({
111
112
  {
112
113
  path: '/dashboard',
113
114
  layout: DashboardLayout,
114
- children: [
115
- { path: '/overview', component: lazy(() => import('./Overview')) },
116
- { path: '/settings', component: lazy(() => import('./Settings')) },
117
115
  ],
118
116
  },
119
117
  ],
@@ -317,25 +315,28 @@ esbuild.build({
317
315
  })
318
316
  ```
319
317
 
320
- ### 信号系统
318
+ ### 状态管理
321
319
 
322
320
  ```tsx
323
- const count = signal(0) // 创建
324
- const doubled = computed(() => count.value * 2) // 衍生
325
- effect(() => console.log('count:', count.value)) // 副作用
326
- batch(() => { a.value = 1; b.value = 2 }) // 批量更新
327
- untrack(() => theme.value) // 不追踪依赖
328
- isSignal(value) // 类型判断
321
+ const $ = ctx.ui.$ // 获取状态对象
322
+
323
+ $.count = 0 // 赋值 → 自动渲染
324
+ $.items = [...$.items, newItem] // 不可变更新
325
+ $.items.push(newItem); ctx.ui.dirty() // 可变更新需显式 dirty
326
+
327
+ // 派生值 — 每次 render 重新计算
328
+ const doubled = $.count * 2
329
+ const completed = $.todos.filter(t => t.done)
329
330
  ```
330
331
 
331
- | 函数 | 签名 | 说明 |
332
- |------|------|------|
333
- | `signal` | `<T>(initial: T) => Signal<T>` | 响应式数据容器 |
334
- | `computed` | `<T>(fn: () => T) => Signal<T>` | 衍生值,自动缓存 |
335
- | `effect` | `(fn: () => void) => dispose()` | 自动追踪依赖,变化时重跑 |
336
- | `batch` | `(fn: () => void) => void` | 合并多次写入为一次通知 |
337
- | `untrack` | `<T>(fn: () => T) => T` | 读取信号但不追踪依赖 |
338
- | `isSignal` | `(value: unknown) => value is Signal` | 判断是否为 Signal |
332
+ | API | 说明 |
333
+ |------|------|
334
+ | `ctx.ui.$` | 状态代理对象,赋值自动触发渲染 |
335
+ | `ctx.ui.dirty()` | 显式标记脏状态(嵌套突变后用) |
336
+ | `ctx.ui.render()` | 同步立即渲染 |
337
+
338
+ **原理**:`ctx.ui.$` Proxy 对象,任何属性赋值(`$.x = val`)自动调用 `queueMicrotask(render)`。\
339
+ 多次赋值在同一个微任务内合并为一次渲染。
339
340
 
340
341
  ### JSX 运行时
341
342
 
@@ -352,37 +353,46 @@ isSignal(value) // 类型判断
352
353
 
353
354
  **Signal 属性自动绑定:** `<input value={signalVal} />` — 信号变化时只更新对应 DOM 属性。
354
355
 
355
- ### 控制流
356
+ ### 条件与列表
357
+
358
+ 使用原生 JS 表达式,不需要框架 API:
356
359
 
357
360
  ```tsx
358
- <Show when={isLoggedIn} fallback={<Login />}>
359
- <Dashboard />
360
- </Show>
361
+ // 条件
362
+ {$.loading && <Loading />}
363
+ {$.error ? <Error msg={$.error} /> : <Content />}
361
364
 
362
- <For each={items} keyBy="id">
363
- {(item) => <div>{item.name}</div>}
364
- </For>
365
+ // 列表
366
+ {$.items.map(item => (
367
+ <div key={item.id}>{item.name}</div>
368
+ ))}
365
369
  ```
366
370
 
367
- | 组件 | 属性 | 说明 |
368
- |------|------|------|
369
- | `Show` | `when: boolean \| Signal<boolean>`, `fallback?`, `children?` | 条件渲染,Signal 响应式切换 |
370
- | `For` | `each: T[] \| Signal<T[]>`, `children: (item, index) => Node`, `keyBy?` | 列表渲染,支持 keyed 复用 |
371
+ | 模式 | 说明 |
372
+ |------|------|
373
+ | `{cond && <A/>}` | 条件渲染(cond true 时渲染 A) |
374
+ | `{cond ? <A/> : <B/>}` | 二选一 |
375
+ | `{arr.map(x => <div key={x.id}/>)}` | 列表渲染,必须加 `key` |
371
376
 
372
377
  ### 生命周期
373
378
 
379
+ 使用 `ref` 回调替代生命周期钩子:
380
+
374
381
  ```tsx
375
- onMount(() => {
376
- init()
377
- return () => cleanup() // 自动清理
378
- })
379
- onCleanup(() => clearInterval(id))
382
+ <div ref={el => {
383
+ if (el) {
384
+ // 挂载:el DOM 元素
385
+ fetchData()
386
+ el.addEventListener('scroll', handler)
387
+ }
388
+ if (!el) {
389
+ // 卸载:el 为 null
390
+ cleanup()
391
+ }
392
+ }} />
380
393
  ```
381
394
 
382
- | 函数 | 说明 |
383
- |------|------|
384
- | `onMount(fn)` | 组件挂载后执行,返回函数在卸载时自动清理 |
385
- | `onCleanup(fn)` | 组件卸载时执行 |
395
+ `ref` 在挂载时传入 DOM 元素,卸载时传入 `null`。一个回调覆盖 mount/unmount 两个场景。
386
396
 
387
397
  ### 应用
388
398
 
@@ -491,12 +501,10 @@ const routes = [
491
501
  app.use(ws({ url: '/ws' }))
492
502
 
493
503
  // 组件中:
494
- onMount(() => {
495
- const unsub = ctx.ws.onMessage((data) => { ... })
496
- onCleanup(() => unsub())
497
- })
498
- ctx.ws.send({ type: 'chat', body: 'hello' })
499
- <Show when={ctx.ws.isConnected}>🟢 已连接</Show>
504
+ const unsub = ctx.ws?.onMessage((data) => { ... })
505
+ // 清理在 ref 中处理
506
+ ctx.ws?.send({ type: 'chat', body: 'hello' })
507
+ {ctx.ws?.isConnected && <span>🟢 已连接</span>}
500
508
  ```
501
509
 
502
510
  | `ctx.ws` | 类型 | 说明 |
@@ -547,10 +555,14 @@ await ctx.api.delete('/users/1')
547
555
  app.use(auth())
548
556
 
549
557
  // 组件中:
550
- <Show when={ctx.auth.isLoggedIn} fallback={<Login />}>
551
- <span>{ctx.auth.user.value?.name}</span>
552
- <button onClick={() => ctx.auth.logout()}>退出</button>
553
- </Show>
558
+ {ctx.auth?.isLoggedIn ? (
559
+ <div>
560
+ <span>{ctx.auth?.user?.name}</span>
561
+ <button onClick={() => ctx.auth?.logout()}>退出</button>
562
+ </div>
563
+ ) : (
564
+ <Login />
565
+ )}
554
566
 
555
567
  // 登录
556
568
  ctx.auth.login('jwt-token', { id: 1, name: 'Alice' })
@@ -687,26 +699,19 @@ function myMiddleware(): AppMiddleware {
687
699
 
688
700
  | React | weifuwu/client |
689
701
  |-------|----------------|
690
- | `useState(0)` | `signal(0)` |
691
- | `useMemo(() => a*2, [a])` | `computed(() => a.value * 2)` |
692
- | `useEffect(() => f, [])` | `onMount(f)` |
693
- | `useEffect(() => f, [dep])` | `effect(f)` |
694
- | `{cond && <X/>}` | `<Show when={cond}><X/></Show>` |
695
- | `{arr.map(i => <X/>)}` | `<For each={arr}>{(i) => <X/>}</For>` |
696
- | `Suspense` | `<Show when={!loading}>` |
697
- | `ErrorBoundary` | `<ErrorBoundary>` |
698
- | `createPortal` | `createPortal` |
699
- | `useNavigate()` | `ctx.app.navigate()` |
700
- | `useParams()` | `ctx.route.params` |
701
- | `useFormik()` | `useForm()` |
702
- | `axios.get()` | `ctx.api.get()` |
703
- | `useSWR()` | `createResource()` |
704
- | `React.lazy()` | `lazy()` |
705
- | `useContext()` | `createContext()` |
702
+ | `useState(0)` | `$.count = 0` |
703
+ | `useMemo(() => a*2, [a])` | `const doubled = a * 2`(render 时计算) |
704
+ | `useEffect(() => f, [])` | `if (!ctx.ui.ready) { f() }` |
705
+ | `{cond && <X/>}` | 相同 |
706
+ | `{arr.map(i => <X/>)}` | 相同,加 `key` |
707
+ | `Suspense` | `{$.loading && <Loading/>}` |
708
+ | `useNavigate()` | `ctx.app?.navigate()` |
709
+ | `useParams()` | `ctx.route?.params` |
710
+ | `axios.get()` | `ctx.api?.get()` |
706
711
 
707
712
  ### 前端类型
708
713
 
709
- `Signal`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `ApiOptions`, `ApiRequestOptions`, `AuthClient`, `AuthUser`, `AuthOptions`, `ResourceOptions`, `ResourceState`, `FormOptions`, `FormReturn`, `FormFieldBindings`, `FormValidators`, `LazyComponentOptions`, `LazyStatus`
714
+ `VNode`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `AuthClient`
710
715
 
711
716
  ---
712
717
 
@@ -781,7 +786,7 @@ node server.ts
781
786
  # http://localhost:3000
782
787
  ```
783
788
 
784
- Demo 包含:嵌套布局、signal 待办列表、useForm 表单、createResource 数据请求、api + auth 认证、WebSocket 实时通信。
789
+ Demo 包含:嵌套布局、ctx.ui.$ 待办列表、手动表单、async fetch 数据请求、api + auth 认证、WebSocket 实时通信。
785
790
 
786
791
  ---
787
792
 
@@ -799,13 +804,10 @@ src/
799
804
  ├── graphql.ts GraphQL
800
805
  ├── client/
801
806
  │ ├── index.ts 前端导出入口
802
- │ ├── signal.ts 响应式系统
803
- │ ├── jsx-runtime.ts JSX DOM, Show/For/ErrorBoundary/Portal
807
+ │ ├── vnode.ts VNode 类型 + JSX 工厂
808
+ │ ├── render.ts VDOM 渲染器(render + patchValue)
804
809
  │ ├── router.ts 路由中间件 + RouteView
805
810
  │ ├── app.ts createApp 应用实例
806
- │ ├── resource.ts createResource 异步数据
807
- │ ├── form.ts useForm 表单
808
- │ ├── lazy.ts 组件懒加载
809
811
  │ ├── types.ts 前端类型
810
812
  │ └── middleware/
811
813
  │ ├── ws.ts WebSocket 客户端
@@ -1,34 +1,18 @@
1
1
  /**
2
- * weifuwu/client 应用 — 创建 ctx + 中间件链 + 挂载组件
2
+ * weifuwu/client 应用 — createApp + ctx.ui.render
3
3
  *
4
- * ```tsx
5
- * import { createApp, ws, router } from 'weifuwu/client'
4
+ * createApp() → app.use(mw) → app.mount('#root', RootComponent)
6
5
  *
7
- * const app = createApp()
8
- * app.use(ws())
9
- * app.use(router({ routes }))
10
- * app.mount('#root', AppShell)
11
- * ```
6
+ * ctx.ui mount 时注入:
7
+ * ctx.ui.render() 触发组件重渲染
8
+ * ctx.ui.$ 持久化组件状态(由渲染器管理)
9
+ * ctx.ui.ready 首次执行标记(由渲染器管理)
12
10
  */
13
11
  import type { WfuiContext, AppMiddleware } from './types.ts';
14
- import type { Component } from './jsx-runtime.ts';
15
- /**
16
- * 创建 weifuwu/client 应用
17
- */
12
+ import type { Component } from './vnode.ts';
18
13
  export declare function createApp(): {
19
- ctx: WfuiContext;
20
- use: (mw: AppMiddleware) => any;
21
- mount: (rootSelector: string, RootComponent: Component) => Promise<void>;
22
- /**
23
- * Hydrate 一个已由 SSR 渲染的区域。
24
- * 不清除目标容器内的内容,只附加组件输出。
25
- *
26
- * ```ts
27
- * const app = createApp()
28
- * app.use(api())
29
- * app.use(auth())
30
- * app.hydrate('#comments', CommentSection, { postId: '123' })
31
- * ```
32
- */
33
- hydrate: (selector: string, Component: Component, props?: Record<string, unknown>) => void;
14
+ readonly ctx: WfuiContext;
15
+ use(mw: AppMiddleware): /*elided*/ any;
16
+ mount(rootSelector: string, RootComponent: Component): Promise<void>;
17
+ destroy(): void;
34
18
  };
@@ -1,239 +1,29 @@
1
1
  /**
2
- * weifuwu/client — 响应式前端框架,TSX + Signal
3
- *
4
- * 零虚拟 DOM,零外部依赖。组件模型:(props, ctx) => Node。
5
- *
6
- * ```tsx
7
- * import { signal, createApp, api, auth, ws, router, RouteView, Show, For } from 'weifuwu/client'
8
- * import type { WfuiContext } from 'weifuwu/client'
9
- *
10
- * const app = createApp()
11
- * app.use(api())
12
- * app.use(auth())
13
- * app.use(ws())
14
- * app.use(router({ routes }))
15
- * app.mount('#root', AppShell)
16
- * ```
17
- *
18
- * ## 核心概念
19
- *
20
- * | 导出 | 类型 | 用途 |
21
- * |------|------|------|
22
- * | `signal()` | function | 响应式数据容器 |
23
- * | `computed()` | function | 衍生信号 |
24
- * | `effect()` | function | 自动追踪依赖的副作用 |
25
- * | `batch()` | function | 批量更新(合并多次写入为一次通知)|
26
- * | `untrack()` | function | 不追踪依赖地读取信号 |
27
- * | `isSignal()` | function | 判断值是否为 Signal 实例 |
28
- * | `onMount()` | function | 组件挂载回调 |
29
- * | `onCleanup()` | function | 组件卸载回调 |
30
- * | `createApp()` | function | 应用实例(中间件链 + 挂载) |
31
- * | `createResource()` | function | 异步数据资源(loading/error/data)|
32
- * | `useForm()` | function | 表单状态管理(字段绑定/验证/提交)|
33
- * | `ErrorBoundary()` | component | 错误捕获边界 |
34
- * | `createPortal()` | function | 渲染到指定 DOM 位置 |
35
- * | `wrap()` | function | 封装三方库为组件 |
36
- * | `domMount()` | function | 低层 DOM 挂载 |
37
- * | `createContext()` | function | 类型安全 provide/inject |
38
- * | `extendCtx()` | function | 扩展上下文 |
39
- * | `api()` | middleware | HTTP 客户端中间件 |
40
- * | `auth()` | middleware | 认证状态管理中间件 |
41
- */
42
- /** 创建响应式数据容器。变化时自动通知依赖方。 */
43
- export { signal } from './signal.ts';
44
- /** 基于其他 signal 的衍生值,自动缓存。 */
45
- export { computed } from './signal.ts';
46
- /** 自动追踪 signal 依赖,变化时重跑回调。返回 dispose 函数。 */
47
- export { effect } from './signal.ts';
48
- /**
49
- * 批量更新 — 将多个信号写入合并为一次通知。
50
- *
51
- * ```ts
52
- * batch(() => {
53
- * a.value = 1
54
- * b.value = 2
55
- * })
56
- * // 只触发一次 effect,而非两次
57
- * ```
58
- */
59
- export { batch } from './signal.ts';
60
- /** Signal 类型。 */
61
- export type { Signal } from './signal.ts';
62
- /** 不追踪依赖地读取信号值。 */
63
- export { untrack } from './signal.ts';
64
- /** 判断值是否为 Signal 实例。 */
65
- export { isSignal } from './signal.ts';
66
- /** JSX 工厂函数 — 由 esbuild / tsc 自动调用。 */
67
- export { jsx, jsxs, jsxDEV } from './jsx-runtime.ts';
68
- /** 不产生包装元素的片段组件。 */
69
- export { Fragment } from './jsx-runtime.ts';
70
- /**
71
- * 条件渲染 — when 为 Signal 时响应式切换。
72
- *
73
- * ```tsx
74
- * <Show when={isLoggedIn} fallback={<LoginPage />}>
75
- * <Dashboard />
76
- * </Show>
77
- * ```
78
- */
79
- export { Show } from './jsx-runtime.ts';
80
- /**
81
- * 列表渲染 — each 为 Signal 时响应式更新。支持 keyBy 属性复用 DOM 节点。
82
- *
83
- * ```tsx
84
- * <For each={items} keyBy="id">
85
- * {(item) => <div>{item.name}</div>}
86
- * </For>
87
- * ```
88
- */
89
- export { For } from './jsx-runtime.ts';
90
- /**
91
- * onMount — 组件根元素挂载到 DOM 后执行的回调。
92
- * 返回函数在组件卸载时自动清理。
93
- *
94
- * ```tsx
95
- * function Chart() {
96
- * onMount(() => {
97
- * const chart = echarts.init(el)
98
- * return () => chart.dispose()
99
- * })
100
- * return <div ref={el => chartEl = el} />
101
- * }
102
- * ```
103
- */
104
- export { onMount } from './jsx-runtime.ts';
105
- /**
106
- * onCleanup — 组件卸载时执行的回调(清理定时器、订阅等)。
107
- *
108
- * ```tsx
109
- * function Timer() {
110
- * const id = setInterval(tick, 1000)
111
- * onCleanup(() => clearInterval(id))
112
- * return <div>...</div>
113
- * }
114
- * ```
115
- */
116
- export { onCleanup } from './jsx-runtime.ts';
117
- /** 组件类型签名:(props, ctx) => Node */
118
- export type { Component } from './jsx-runtime.ts';
119
- /**
120
- * 捕获子组件渲染时的异常并显示 fallback。
121
- *
122
- * ```tsx
123
- * <ErrorBoundary fallback={(e) => <p>出错了: {e.message}</p>}>
124
- * {() => <Dashboard />}
125
- * </ErrorBoundary>
126
- * ```
127
- */
128
- export { ErrorBoundary } from './jsx-runtime.ts';
129
- /**
130
- * Portal — 将组件渲染到父容器之外的 DOM 位置。
131
- *
132
- * ```tsx
133
- * <Show when={show}>
134
- * {createPortal(<div class="modal">...</div>, document.body)}
135
- * </Show>
136
- * ```
137
- */
138
- export { createPortal } from './jsx-runtime.ts';
139
- /**
140
- * wrap — 封装第三方库为组件,自动管理 mount/unmount 生命周期。
141
- */
142
- export { wrap } from './jsx-runtime.ts';
143
- /** 低层挂载函数,一般用 createApp().mount() */
144
- export { domMount } from './jsx-runtime.ts';
145
- /** 应用上下文 — 组件通过第二个参数 ctx 访问。 */
146
- export type { WfuiContext } from './types.ts';
147
- /** 中间件签名:(ctx) => ctx */
148
- export type { AppMiddleware } from './types.ts';
149
- /** 路由定义:path / component / auth / title / loader */
150
- export type { RouteDef } from './types.ts';
151
- /**
152
- * 类型安全的上下文工厂 — 创建 provide/inject 对。
153
- *
154
- * ```ts
155
- * const ThemeCtx = createContext<string>('theme')
156
- * ThemeCtx.provide(ctx, 'dark')
157
- * const theme = ThemeCtx.inject(ctx)
158
- * ```
159
- */
160
- export { createContext, extendCtx } from './types.ts';
161
- /**
162
- * 创建 weifuwu/client 应用实例。
163
- *
164
- * ```tsx
165
- * const app = createApp()
166
- * app.use(api())
167
- * app.use(auth())
168
- * app.use(router({ routes }))
169
- * await app.mount('#root', AppShell)
170
- * ```
171
- */
2
+ * weifuwu/client — VDOM 前端框架
3
+ *
4
+ * 概念:
5
+ * 组件 = (props, ctx) => VNode
6
+ * 状态 = JS 变量
7
+ * 更新 = ctx.ui.render()
8
+ * 挂载/卸载 = ref 回调
9
+ *
10
+ * 导出:
11
+ * h / jsx / jsxs / jsxDEV / Fragment → JSX 工厂
12
+ * createApp → 应用引导
13
+ * router / RouteView → 路由
14
+ * api / auth / ws → 中间件
15
+ * extendCtx → ctx 扩展
16
+ * WfuiContext / AppMiddleware / RouteDef → 类型
17
+ */
18
+ export { h, jsx, jsxs, jsxDEV, Fragment } from './vnode.ts';
19
+ export type { VNode, VNodeType, Component } from './vnode.ts';
172
20
  export { createApp } from './app.ts';
173
- /**
174
- * 路由中间件 — 注入 ctx.route / ctx.app.navigate。
175
- * 支持 hash / history 模式,loader 数据预取,auth 守卫,嵌套布局。
176
- */
177
- export { router } from './router.ts';
178
- /**
179
- * RouteView 组件 — 渲染当前路由匹配的组件。
180
- * 用 `<RouteView />` 放在布局中作为路由出口。
181
- */
182
- export { RouteView } from './router.ts';
183
- /**
184
- * lazy — 组件懒加载(代码分割)。
185
- * 配合 esbuild code splitting 使用。
186
- *
187
- * ```tsx
188
- * const AdminPage = lazy(() => import('./pages/AdminPage'))
189
- * const routes = [{ path: '/admin', component: AdminPage }]
190
- * ```
191
- */
192
- export { lazy } from './lazy.ts';
193
- export type { LazyComponentOptions, LazyStatus } from './lazy.ts';
194
- /**
195
- * WebSocket 中间件 — 注入 ctx.ws(send / onMessage / join / leave)。
196
- * 自动重连,支持房间。
197
- */
21
+ export { router, RouteView } from './router.ts';
198
22
  export { ws } from './middleware/ws.ts';
199
- /**
200
- * API 客户端中间件 — 注入 ctx.api(get/post/put/patch/delete)。
201
- * 支持 baseURL、请求/响应拦截器。
202
- *
203
- * ```ts
204
- * app.use(api({ baseURL: '/api' }))
205
- * // 组件中:ctx.api.get<User[]>('/users')
206
- * ```
207
- */
208
23
  export { api } from './middleware/api.ts';
24
+ export { auth } from './middleware/auth.ts';
209
25
  export type { ApiClient, ApiOptions, ApiRequestOptions } from './middleware/api.ts';
210
26
  export { ApiError } from './middleware/api.ts';
211
- /**
212
- * 认证状态管理中间件 注入 ctx.auth(token/user/login/logout)。
213
- *
214
- * ```ts
215
- * app.use(auth())
216
- * // 组件中:ctx.auth.token, ctx.auth.isLoggedIn, ctx.auth.login()
217
- * ```
218
- */
219
- export { auth } from './middleware/auth.ts';
220
- export type { AuthClient, AuthUser, AuthOptions } from './middleware/auth.ts';
221
- /**
222
- * 异步数据资源 — 自动管理 loading/error/data 信号。
223
- *
224
- * ```ts
225
- * const [data, { loading, error, refetch }] = createResource(() => fetch('/api/users'))
226
- * ```
227
- */
228
- export { createResource } from './resource.ts';
229
- export type { ResourceOptions, ResourceState } from './resource.ts';
230
- /**
231
- * 表单状态管理 — 字段绑定、验证、提交、重置。
232
- *
233
- * ```ts
234
- * const form = useForm({ initial: { name: '' }, validate: {...}, onSubmit: async (v) => {...} })
235
- * // <input {...form.field('name')} />
236
- * ```
237
- */
238
- export { useForm } from './form.ts';
239
- export type { FormOptions, FormReturn, FormFieldBindings, FormValidators } from './form.ts';
27
+ export type { AuthClient, AuthOptions } from './middleware/auth.ts';
28
+ export { extendCtx } from './types.ts';
29
+ export type { WfuiContext, AppMiddleware, RouteDef } from './types.ts';