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.
- package/README.md +90 -5
- package/dist/compress.d.ts +6 -0
- package/dist/cookie.d.ts +12 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +1486 -0
- package/dist/middleware.d.ts +21 -0
- package/dist/rate-limit.d.ts +8 -0
- package/dist/router.d.ts +55 -0
- package/dist/serve.d.ts +19 -0
- package/dist/static.d.ts +7 -0
- package/dist/tsx.d.ts +17 -0
- package/dist/types.d.ts +9 -0
- package/dist/upload.d.ts +14 -0
- package/dist/validate.d.ts +9 -0
- package/package.json +14 -2
- package/AGENTS.md +0 -105
- package/compress.ts +0 -69
- package/cookie.ts +0 -58
- package/index.ts +0 -21
- package/middleware.ts +0 -178
- package/rate-limit.ts +0 -68
- package/router.ts +0 -701
- package/serve.ts +0 -126
- package/static.ts +0 -113
- package/test/compress.test.ts +0 -106
- package/test/cookie.test.ts +0 -79
- package/test/fixtures/pages/about/page.tsx +0 -3
- package/test/fixtures/pages/blog/[slug]/load.ts +0 -3
- package/test/fixtures/pages/blog/[slug]/page.tsx +0 -3
- package/test/fixtures/pages/blog/[slug]/route.ts +0 -7
- package/test/fixtures/pages/blog/layout.tsx +0 -3
- package/test/fixtures/pages/layout.tsx +0 -12
- package/test/fixtures/pages/page.tsx +0 -3
- package/test/middleware.test.ts +0 -407
- package/test/rate-limit.test.ts +0 -94
- package/test/static.test.ts +0 -93
- package/test/tsx.test.ts +0 -285
- package/test/unode.test.ts +0 -401
- package/test/upload.test.ts +0 -130
- package/test/validate.test.ts +0 -133
- package/tsconfig.json +0 -13
- package/tsx.ts +0 -374
- package/types.ts +0 -23
- package/upload.ts +0 -101
- 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 (
|
|
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 (
|
|
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
|
|
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
|
|
package/dist/cookie.d.ts
ADDED
|
@@ -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;
|
package/dist/index.d.ts
ADDED
|
@@ -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';
|