weifuwu 0.2.2 → 0.2.4

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 +90 -5
  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 +1486 -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 -701
  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 -374
  43. package/types.ts +0 -23
  44. package/upload.ts +0 -101
  45. package/validate.ts +0 -88
package/README.md CHANGED
@@ -45,13 +45,13 @@ serve(app.handler(), { port: 3000 })
45
45
  ```
46
46
  pages/
47
47
  page.tsx → GET / (React component, default export)
48
- layout.tsx → root layout (wraps all pages)
48
+ layout.tsx → root layout (HTML shell, receives req/ctx, NOT hydrated)
49
49
  about/page.tsx → GET /about
50
50
  blog/[slug]/
51
51
  page.tsx → GET /blog/:slug
52
52
  load.ts → data fetching (server-only, default export)
53
53
  route.ts → POST /blog/:slug (API, named exports GET/POST/...)
54
- blog/layout.tsx → /blog/* layout (auto-wraps blog pages)
54
+ blog/layout.tsx → /blog/* layout (UI structure, receives children, hydrated)
55
55
  ```
56
56
 
57
57
  ### page.tsx — page component
@@ -81,12 +81,21 @@ export default async function load({ params, query }: {
81
81
 
82
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
83
 
84
- ### layout.tsx — nested layouts
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 }`:
85
89
 
86
90
  ```tsx
87
- export default function RootLayout({ children }: { children: React.ReactNode }) {
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'
88
97
  return (
89
- <html>
98
+ <html class={theme}>
90
99
  <head><title>App</title></head>
91
100
  <body>
92
101
  <div id="__weifuwu_root">{children}</div>
@@ -96,8 +105,47 @@ export default function RootLayout({ children }: { children: React.ReactNode })
96
105
  }
97
106
  ```
98
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
+
99
124
  Layouts auto-nest by directory depth — `pages/blog/layout.tsx` wraps `pages/blog/*` pages inside `pages/layout.tsx`.
100
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
+
101
149
  ### route.ts — API (co-located with page)
102
150
 
103
151
  ```ts
@@ -359,6 +407,40 @@ const app = new Router()
359
407
  serve(app.handler(), { port: 3000 })
360
408
  ```
361
409
 
410
+ ## Graceful shutdown
411
+
412
+ ```ts
413
+ import { serve } from 'weifuwu'
414
+ import type { Server } from 'weifuwu'
415
+
416
+ const ac = new AbortController()
417
+ let server: Server
418
+
419
+ process.on('SIGTERM', () => {
420
+ console.log('shutting down…')
421
+ ac.abort()
422
+ server.stop()
423
+ })
424
+
425
+ server = serve((req, ctx) => new Response('Hello'), {
426
+ port: 3000,
427
+ signal: ac.signal,
428
+ })
429
+ await server.ready
430
+ console.log(`listening on :${server.port}`)
431
+ ```
432
+
433
+ ### Using with WebSocket
434
+
435
+ ```ts
436
+ const app = new Router().ws('/chat', { … })
437
+ const server = serve(app.handler(), {
438
+ port: 3000,
439
+ signal: ac.signal,
440
+ websocket: app.websocketHandler(),
441
+ })
442
+ ```
443
+
362
444
  ## Error handling
363
445
 
364
446
  ```ts
@@ -427,6 +509,9 @@ Returns `Promise<Router>`.
427
509
  | `getCookies(req)` | Parse Cookie header → object |
428
510
  | `setCookie(res, name, value, options?)` | Set cookie (returns new Response) |
429
511
  | `deleteCookie(res, name)` | Delete cookie (returns new Response) |
512
+ | `useTsx()` | Hook returning `{ params, query, user, parsed }` from `TsxContext` |
513
+
514
+ Import `useTsx` and `TsxContext` from `'weifuwu'`.
430
515
 
431
516
  ## License
432
517
 
@@ -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';