weifuwu 0.2.1 → 0.2.3

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 (45) hide show
  1. package/README.md +168 -0
  2. package/dist/compress.d.ts +6 -0
  3. package/dist/cookie.d.ts +12 -0
  4. package/dist/index.d.ts +21 -0
  5. package/dist/index.js +1420 -0
  6. package/dist/middleware.d.ts +21 -0
  7. package/dist/rate-limit.d.ts +8 -0
  8. package/dist/router.d.ts +55 -0
  9. package/dist/serve.d.ts +19 -0
  10. package/dist/static.d.ts +7 -0
  11. package/dist/tsx.d.ts +17 -0
  12. package/dist/types.d.ts +9 -0
  13. package/dist/upload.d.ts +14 -0
  14. package/dist/validate.d.ts +9 -0
  15. package/package.json +14 -2
  16. package/AGENTS.md +0 -105
  17. package/compress.ts +0 -69
  18. package/cookie.ts +0 -58
  19. package/index.ts +0 -21
  20. package/middleware.ts +0 -178
  21. package/rate-limit.ts +0 -68
  22. package/router.ts +0 -702
  23. package/serve.ts +0 -126
  24. package/static.ts +0 -113
  25. package/test/compress.test.ts +0 -106
  26. package/test/cookie.test.ts +0 -79
  27. package/test/fixtures/pages/about/page.tsx +0 -3
  28. package/test/fixtures/pages/blog/[slug]/load.ts +0 -3
  29. package/test/fixtures/pages/blog/[slug]/page.tsx +0 -3
  30. package/test/fixtures/pages/blog/[slug]/route.ts +0 -7
  31. package/test/fixtures/pages/blog/layout.tsx +0 -3
  32. package/test/fixtures/pages/layout.tsx +0 -12
  33. package/test/fixtures/pages/page.tsx +0 -3
  34. package/test/middleware.test.ts +0 -407
  35. package/test/rate-limit.test.ts +0 -94
  36. package/test/static.test.ts +0 -93
  37. package/test/tsx.test.ts +0 -285
  38. package/test/unode.test.ts +0 -401
  39. package/test/upload.test.ts +0 -130
  40. package/test/validate.test.ts +0 -133
  41. package/tsconfig.json +0 -13
  42. package/tsx.ts +0 -354
  43. package/types.ts +0 -23
  44. package/upload.ts +0 -101
  45. package/validate.ts +0 -88
package/README.md CHANGED
@@ -8,6 +8,7 @@
8
8
  - **Trie router** — static > param > wildcard, sub-router mounting, path params
9
9
  - **Middleware** — global, path-scoped, route-level — onion model, short-circuit
10
10
  - **Built-in middleware** — `auth()`, `cors()`, `logger()`, `rateLimit()`, `compress()`
11
+ - **React SSR + Hydration** — `tsx({ dir })` — page.tsx / load.ts / layout.tsx / route.ts
11
12
  - **WebSocket** — `router.ws()` with upgrade middleware (auth before connect)
12
13
  - **GraphQL** — `router.graphql()` with GraphiQL IDE
13
14
  - **AI streaming** — `router.ai()` via Vercel AI SDK
@@ -27,6 +28,158 @@ import { serve } from 'weifuwu'
27
28
  serve((req, ctx) => new Response('Hello, World!'), { port: 3000 })
28
29
  ```
29
30
 
31
+ ## React pages with tsx()
32
+
33
+ ```ts
34
+ import { serve, Router } from 'weifuwu'
35
+ import { tsx } from 'weifuwu/tsx'
36
+
37
+ const app = new Router()
38
+ app.use('/', await tsx({ dir: './pages/' }))
39
+
40
+ serve(app.handler(), { port: 3000 })
41
+ ```
42
+
43
+ ### File conventions
44
+
45
+ ```
46
+ pages/
47
+ page.tsx → GET / (React component, default export)
48
+ layout.tsx → root layout (HTML shell, receives req/ctx, NOT hydrated)
49
+ about/page.tsx → GET /about
50
+ blog/[slug]/
51
+ page.tsx → GET /blog/:slug
52
+ load.ts → data fetching (server-only, default export)
53
+ route.ts → POST /blog/:slug (API, named exports GET/POST/...)
54
+ blog/layout.tsx → /blog/* layout (UI structure, receives children, hydrated)
55
+ ```
56
+
57
+ ### page.tsx — page component
58
+
59
+ ```tsx
60
+ export default function Page({ params, query }: {
61
+ params: { slug: string }
62
+ query: Record<string, string>
63
+ }) {
64
+ return <article><h1>{params.slug}</h1></article>
65
+ }
66
+ ```
67
+
68
+ ### load.ts — data fetching (server-only)
69
+
70
+ ```ts
71
+ import { db } from './db.ts'
72
+
73
+ export default async function load({ params, query }: {
74
+ params: Record<string, string>
75
+ query: Record<string, string>
76
+ }) {
77
+ const data = await db.query(params.slug)
78
+ return { data } // merged into props passed to page.tsx
79
+ }
80
+ ```
81
+
82
+ `load()` runs only on the server. Its return value is merged with `{ params, query }` and passed to the page component. The merged props are serialized as `window.__WEIFUWU_PROPS` for client hydration.
83
+
84
+ ### layout.tsx — root layout vs nested layouts
85
+
86
+ Two types of layouts, distinguished by their position in the directory tree:
87
+
88
+ **Root layout** (`pages/layout.tsx`) — receives `{ children, req, ctx }`:
89
+
90
+ ```tsx
91
+ export default function RootLayout({ children, req, ctx }: {
92
+ children: React.ReactNode
93
+ req: Request
94
+ ctx: Context
95
+ }) {
96
+ const theme = req.headers.get('Cookie')?.includes('theme=dark') ? 'dark' : 'light'
97
+ return (
98
+ <html class={theme}>
99
+ <head><title>App</title></head>
100
+ <body>
101
+ <div id="__weifuwu_root">{children}</div>
102
+ </body>
103
+ </html>
104
+ )
105
+ }
106
+ ```
107
+
108
+ - Controls the full HTML shell (`<html>`, `<head>`, `<body>`)
109
+ - Has access to `req`/`ctx` for cookie/header-based customization
110
+ - **Not hydrated** — safe to use `req`/`ctx` (never serialized to client)
111
+
112
+ **Nested layouts** (`pages/blog/layout.tsx`) — receives only `{ children }`:
113
+
114
+ ```tsx
115
+ export default function BlogLayout({ children }: { children: React.ReactNode }) {
116
+ return <div className="sidebar-layout">{children}</div>
117
+ }
118
+ ```
119
+
120
+ - Provide UI structure (sidebar, nav, search box)
121
+ - **Hydrated** on the client — can use `useState`, event handlers
122
+ - No access to `req`/`ctx` (not serializable)
123
+
124
+ Layouts auto-nest by directory depth — `pages/blog/layout.tsx` wraps `pages/blog/*` pages inside `pages/layout.tsx`.
125
+
126
+ ### `TsxContext` and `useTsx()`
127
+
128
+ Any component in the tree can access routing context without prop drilling:
129
+
130
+ ```tsx
131
+ import { useTsx } from 'weifuwu'
132
+
133
+ function Sidebar() {
134
+ const { params, query, user, parsed } = useTsx()
135
+ // params.slug, query.page, user.name — from any depth
136
+ return <aside>User: {user?.name}</aside>
137
+ }
138
+ ```
139
+
140
+ Available fields:
141
+
142
+ | Field | Source | Description |
143
+ |-------|--------|-------------|
144
+ | `params` | URL path | Route parameters (`:slug`, `:id`) |
145
+ | `query` | URL search | Query string (`?page=1`) |
146
+ | `user` | `auth()` middleware | Set by `verify` callback |
147
+ | `parsed` | `validate()` / `upload()` | Validated body / uploaded files |
148
+
149
+ ### route.ts — API (co-located with page)
150
+
151
+ ```ts
152
+ export const POST: Handler = async (req, ctx) => {
153
+ const body = await req.json()
154
+ return Response.json({ ...body, slug: ctx.params.slug })
155
+ }
156
+ ```
157
+
158
+ Route.ts exports `POST`/`PUT`/`DELETE`/`PATCH` (GET is handled by page.tsx). The same `route.ts` file coexists with `page.tsx` in the same directory for handling form submissions or AJAX requests.
159
+
160
+ ### Usage within a full app
161
+
162
+ ```ts
163
+ import { serve, Router } from 'weifuwu'
164
+ import { tsx } from 'weifuwu/tsx'
165
+
166
+ const r = new Router()
167
+ r.use('/', await tsx({ dir: './pages/' }))
168
+
169
+ // Other features coexist in the same process
170
+ r.ws('/chat', { message(ws, _, data) { ws.send(data) } })
171
+ r.graphql('/graphql', { schema: `...`, resolvers: { ... } })
172
+
173
+ serve(r.handler())
174
+ ```
175
+
176
+ ```bash
177
+ node --watch app.ts # development
178
+ node app.ts # production
179
+ ```
180
+
181
+ No build step, no configuration file — just Node.js and React.
182
+
30
183
  ## Router
31
184
 
32
185
  ```ts
@@ -277,6 +430,18 @@ const app = new Router()
277
430
 
278
431
  Returns `{ stop, port, hostname, ready }`.
279
432
 
433
+ ### `tsx(options)`
434
+
435
+ ```ts
436
+ import { tsx } from 'weifuwu/tsx'
437
+ ```
438
+
439
+ | Option | Default | Description |
440
+ |--------|---------|-------------|
441
+ | `dir` | — | Pages directory path |
442
+
443
+ Returns `Promise<Router>`.
444
+
280
445
  ### `Router`
281
446
 
282
447
  | Method | Description |
@@ -310,6 +475,9 @@ Returns `{ stop, port, hostname, ready }`.
310
475
  | `getCookies(req)` | Parse Cookie header → object |
311
476
  | `setCookie(res, name, value, options?)` | Set cookie (returns new Response) |
312
477
  | `deleteCookie(res, name)` | Delete cookie (returns new Response) |
478
+ | `useTsx()` | Hook returning `{ params, query, user, parsed }` from `TsxContext` |
479
+
480
+ Import `useTsx` and `TsxContext` from `'weifuwu'`.
313
481
 
314
482
  ## License
315
483
 
@@ -0,0 +1,6 @@
1
+ import type { Middleware } from './types.ts';
2
+ export interface CompressOptions {
3
+ level?: number;
4
+ threshold?: number;
5
+ }
6
+ export declare function compress(options?: CompressOptions): Middleware;
@@ -0,0 +1,12 @@
1
+ export interface CookieOptions {
2
+ domain?: string;
3
+ path?: string;
4
+ maxAge?: number;
5
+ expires?: Date;
6
+ httpOnly?: boolean;
7
+ secure?: boolean;
8
+ sameSite?: 'strict' | 'lax' | 'none';
9
+ }
10
+ export declare function getCookies(req: Request): Record<string, string>;
11
+ export declare function setCookie(res: Response, name: string, value: string, options?: CookieOptions): Response;
12
+ export declare function deleteCookie(res: Response, name: string, options?: Omit<CookieOptions, 'maxAge'>): Response;
@@ -0,0 +1,21 @@
1
+ export type { Context, Handler, Middleware, ErrorHandler } from './types.ts';
2
+ export { serve } from './serve.ts';
3
+ export type { ServeOptions, Server } from './serve.ts';
4
+ export { Router } from './router.ts';
5
+ export type { WebSocketHandler, GraphQLOptions, AIHandler } from './router.ts';
6
+ export { tsx, TsxContext, useTsx } from './tsx.ts';
7
+ export type { TsxOptions } from './tsx.ts';
8
+ export { auth, cors, logger } from './middleware.ts';
9
+ export type { AuthOptions, CORSOptions, LoggerOptions } from './middleware.ts';
10
+ export { serveStatic } from './static.ts';
11
+ export type { ServeStaticOptions } from './static.ts';
12
+ export { validate } from './validate.ts';
13
+ export type { ValidationSchemas } from './validate.ts';
14
+ export { getCookies, setCookie, deleteCookie } from './cookie.ts';
15
+ export type { CookieOptions } from './cookie.ts';
16
+ export { upload } from './upload.ts';
17
+ export type { UploadOptions, UploadedFile } from './upload.ts';
18
+ export { rateLimit } from './rate-limit.ts';
19
+ export type { RateLimitOptions } from './rate-limit.ts';
20
+ export { compress } from './compress.ts';
21
+ export type { CompressOptions } from './compress.ts';