weifuwu 0.33.11 → 0.35.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,34 @@ esbuild.build({
317
315
  })
318
316
  ```
319
317
 
320
- ### 信号系统
318
+ ### 状态管理 — 深度 Proxy
319
+
320
+ `ctx.ui.$` 是**深度 Proxy**:任何属性/数组/对象层面的写入自动触发渲染,无需手动调用。
321
321
 
322
322
  ```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) // 类型判断
323
+ const $ = ctx.ui.$
324
+
325
+ // 顶层属性赋值
326
+ $.count = 0 // 自动渲染
327
+ $.user = { name: 'alice' } // → 新值自动深度包装
328
+
329
+ // 数组突变
330
+ $.items.push(newItem) // → 自动渲染
331
+ $.items.pop() // → 自动渲染
332
+ $.items.splice(i, 1) // → 自动渲染
333
+
334
+ // 对象属性突变(数组内部也支持)
335
+ $.items[0].done = true // → 自动渲染
336
+ $.msgs[idx].content += event.text // → 自动渲染
337
+
338
+ // 不可变更新(同样支持)
339
+ $.items = [...$.items, newItem]
329
340
  ```
330
341
 
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 |
342
+ | API | 说明 |
343
+ |------|------|
344
+ | `ctx.ui.$` | 深度 Proxy 对象,所有写入自动触发渲染 |
345
+ | `ctx.ui.dirty()` | 极少需要 仅当绕过 Proxy 直接操作底层对象时 |
339
346
 
340
347
  ### JSX 运行时
341
348
 
@@ -352,37 +359,46 @@ isSignal(value) // 类型判断
352
359
 
353
360
  **Signal 属性自动绑定:** `<input value={signalVal} />` — 信号变化时只更新对应 DOM 属性。
354
361
 
355
- ### 控制流
362
+ ### 条件与列表
363
+
364
+ 使用原生 JS 表达式,不需要框架 API:
356
365
 
357
366
  ```tsx
358
- <Show when={isLoggedIn} fallback={<Login />}>
359
- <Dashboard />
360
- </Show>
367
+ // 条件
368
+ {$.loading && <Loading />}
369
+ {$.error ? <Error msg={$.error} /> : <Content />}
361
370
 
362
- <For each={items} keyBy="id">
363
- {(item) => <div>{item.name}</div>}
364
- </For>
371
+ // 列表
372
+ {$.items.map(item => (
373
+ <div key={item.id}>{item.name}</div>
374
+ ))}
365
375
  ```
366
376
 
367
- | 组件 | 属性 | 说明 |
368
- |------|------|------|
369
- | `Show` | `when: boolean \| Signal<boolean>`, `fallback?`, `children?` | 条件渲染,Signal 响应式切换 |
370
- | `For` | `each: T[] \| Signal<T[]>`, `children: (item, index) => Node`, `keyBy?` | 列表渲染,支持 keyed 复用 |
377
+ | 模式 | 说明 |
378
+ |------|------|
379
+ | `{cond && <A/>}` | 条件渲染(cond true 时渲染 A) |
380
+ | `{cond ? <A/> : <B/>}` | 二选一 |
381
+ | `{arr.map(x => <div key={x.id}/>)}` | 列表渲染,必须加 `key` |
371
382
 
372
383
  ### 生命周期
373
384
 
385
+ 使用 `ref` 回调替代生命周期钩子:
386
+
374
387
  ```tsx
375
- onMount(() => {
376
- init()
377
- return () => cleanup() // 自动清理
378
- })
379
- onCleanup(() => clearInterval(id))
388
+ <div ref={el => {
389
+ if (el) {
390
+ // 挂载:el DOM 元素
391
+ fetchData()
392
+ el.addEventListener('scroll', handler)
393
+ }
394
+ if (!el) {
395
+ // 卸载:el 为 null
396
+ cleanup()
397
+ }
398
+ }} />
380
399
  ```
381
400
 
382
- | 函数 | 说明 |
383
- |------|------|
384
- | `onMount(fn)` | 组件挂载后执行,返回函数在卸载时自动清理 |
385
- | `onCleanup(fn)` | 组件卸载时执行 |
401
+ `ref` 在挂载时传入 DOM 元素,卸载时传入 `null`。一个回调覆盖 mount/unmount 两个场景。
386
402
 
387
403
  ### 应用
388
404
 
@@ -491,12 +507,10 @@ const routes = [
491
507
  app.use(ws({ url: '/ws' }))
492
508
 
493
509
  // 组件中:
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>
510
+ const unsub = ctx.ws?.onMessage((data) => { ... })
511
+ // 清理在 ref 中处理
512
+ ctx.ws?.send({ type: 'chat', body: 'hello' })
513
+ {ctx.ws?.isConnected && <span>🟢 已连接</span>}
500
514
  ```
501
515
 
502
516
  | `ctx.ws` | 类型 | 说明 |
@@ -547,10 +561,14 @@ await ctx.api.delete('/users/1')
547
561
  app.use(auth())
548
562
 
549
563
  // 组件中:
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>
564
+ {ctx.auth?.isLoggedIn ? (
565
+ <div>
566
+ <span>{ctx.auth?.user?.name}</span>
567
+ <button onClick={() => ctx.auth?.logout()}>退出</button>
568
+ </div>
569
+ ) : (
570
+ <Login />
571
+ )}
554
572
 
555
573
  // 登录
556
574
  ctx.auth.login('jwt-token', { id: 1, name: 'Alice' })
@@ -687,26 +705,19 @@ function myMiddleware(): AppMiddleware {
687
705
 
688
706
  | React | weifuwu/client |
689
707
  |-------|----------------|
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()` |
708
+ | `useState(0)` | `$.count = 0` |
709
+ | `useMemo(() => a*2, [a])` | `const doubled = a * 2`(render 时计算) |
710
+ | `useEffect(() => f, [])` | `if (!ctx.ui.ready) { f() }` |
711
+ | `{cond && <X/>}` | 相同 |
712
+ | `{arr.map(i => <X/>)}` | 相同,加 `key` |
713
+ | `Suspense` | `{$.loading && <Loading/>}` |
714
+ | `useNavigate()` | `ctx.app?.navigate()` |
715
+ | `useParams()` | `ctx.route?.params` |
716
+ | `axios.get()` | `ctx.api?.get()` |
706
717
 
707
718
  ### 前端类型
708
719
 
709
- `Signal`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `ApiOptions`, `ApiRequestOptions`, `AuthClient`, `AuthUser`, `AuthOptions`, `ResourceOptions`, `ResourceState`, `FormOptions`, `FormReturn`, `FormFieldBindings`, `FormValidators`, `LazyComponentOptions`, `LazyStatus`
720
+ `VNode`, `Component`, `WfuiContext`, `AppMiddleware`, `RouteDef`, `ApiClient`, `AuthClient`
710
721
 
711
722
  ---
712
723
 
@@ -781,7 +792,7 @@ node server.ts
781
792
  # http://localhost:3000
782
793
  ```
783
794
 
784
- Demo 包含:嵌套布局、signal 待办列表、useForm 表单、createResource 数据请求、api + auth 认证、WebSocket 实时通信。
795
+ Demo 包含:嵌套布局、ctx.ui.$ 待办列表、手动表单、async fetch 数据请求、api + auth 认证、WebSocket 实时通信。
785
796
 
786
797
  ---
787
798
 
@@ -799,13 +810,10 @@ src/
799
810
  ├── graphql.ts GraphQL
800
811
  ├── client/
801
812
  │ ├── index.ts 前端导出入口
802
- │ ├── signal.ts 响应式系统
803
- │ ├── jsx-runtime.ts JSX DOM, Show/For/ErrorBoundary/Portal
813
+ │ ├── vnode.ts VNode 类型 + JSX 工厂
814
+ │ ├── render.ts VDOM 渲染器(render + patchValue)
804
815
  │ ├── router.ts 路由中间件 + RouteView
805
816
  │ ├── app.ts createApp 应用实例
806
- │ ├── resource.ts createResource 异步数据
807
- │ ├── form.ts useForm 表单
808
- │ ├── lazy.ts 组件懒加载
809
817
  │ ├── types.ts 前端类型
810
818
  │ └── middleware/
811
819
  │ ├── ws.ts WebSocket 客户端
@@ -1,34 +1,25 @@
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 首次执行标记(由渲染器管理)
10
+ *
11
+ * ctx.ui.$ 是深度 Proxy:
12
+ * $.x = val → 自动渲染(顶层属性赋值)
13
+ * $.items.push(val) → 自动渲染(数组突变方法)
14
+ * $.items[0].x = val → 自动渲染(对象属性突变)
15
+ * $.items.push({x: 1}) → 新对象自动深度包装
16
+ * ctx.ui.dirty() → 极少需要(仅当绕过 Proxy 直接操作底层对象)
12
17
  */
13
18
  import type { WfuiContext, AppMiddleware } from './types.ts';
14
- import type { Component } from './jsx-runtime.ts';
15
- /**
16
- * 创建 weifuwu/client 应用
17
- */
19
+ import type { Component } from './vnode.ts';
18
20
  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;
21
+ readonly ctx: WfuiContext;
22
+ use(mw: AppMiddleware): /*elided*/ any;
23
+ mount(rootSelector: string, RootComponent: Component): Promise<void>;
24
+ destroy(): void;
34
25
  };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * ErrorBoundary — 错误边界组件
3
+ *
4
+ * 捕获子组件渲染时的错误,展示 fallback 替代崩溃白屏。
5
+ *
6
+ * ```tsx
7
+ * <ErrorBoundary fallback={<p>出错了</p>}>
8
+ * <UserProfile />
9
+ * </ErrorBoundary>
10
+ * ```
11
+ *
12
+ * 基于 ctx.ui._errorHandler 机制:在渲染子组件前注入错误处理器,
13
+ * 子组件 render 抛错时设置 $.error → 重新渲染 fallback。
14
+ */
15
+ import type { WfuiContext } from './types.ts';
16
+ import type { VNode } from './vnode.ts';
17
+ export interface ErrorBoundaryProps {
18
+ fallback?: VNode | ((props: {
19
+ error: unknown;
20
+ }) => VNode) | null;
21
+ children?: any;
22
+ }
23
+ export declare function ErrorBoundary(props: ErrorBoundaryProps, ctx: WfuiContext): VNode | null;
@@ -1,239 +1,31 @@
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';
30
+ export { ErrorBoundary } from './error-boundary.ts';
31
+ export type { ErrorBoundaryProps } from './error-boundary.ts';