weifuwu 0.27.2 → 0.27.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 +0 -19
- package/dist/ai/provider.d.ts +45 -0
- package/dist/ai/stream.d.ts +13 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +131 -0
- package/dist/core/cookie.d.ts +36 -0
- package/dist/core/env.d.ts +69 -0
- package/dist/core/logger.d.ts +16 -0
- package/dist/core/router.d.ts +72 -0
- package/dist/core/serve.d.ts +38 -0
- package/dist/core/sse.d.ts +47 -0
- package/dist/core/trace.d.ts +95 -0
- package/dist/graphql.d.ts +16 -0
- package/dist/hub.d.ts +36 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +3937 -0
- package/dist/mailer.d.ts +51 -0
- package/dist/middleware/compress.d.ts +20 -0
- package/dist/middleware/cors.d.ts +25 -0
- package/dist/middleware/csrf.d.ts +47 -0
- package/dist/middleware/flash.d.ts +90 -0
- package/dist/middleware/health.d.ts +24 -0
- package/dist/middleware/helmet.d.ts +33 -0
- package/dist/middleware/i18n.d.ts +39 -0
- package/dist/middleware/rate-limit.d.ts +44 -0
- package/dist/middleware/request-id.d.ts +40 -0
- package/dist/middleware/static.d.ts +23 -0
- package/dist/middleware/theme.d.ts +31 -0
- package/dist/middleware/upload.d.ts +55 -0
- package/dist/middleware/validate.d.ts +32 -0
- package/dist/postgres/client.d.ts +4 -0
- package/dist/postgres/index.d.ts +4 -0
- package/dist/postgres/module.d.ts +16 -0
- package/dist/postgres/schema/columns.d.ts +99 -0
- package/dist/postgres/schema/index.d.ts +6 -0
- package/dist/postgres/schema/sql.d.ts +22 -0
- package/dist/postgres/schema/table.d.ts +141 -0
- package/dist/postgres/schema/where.d.ts +29 -0
- package/dist/postgres/types.d.ts +49 -0
- package/dist/queue/cron.d.ts +9 -0
- package/dist/queue/index.d.ts +2 -0
- package/dist/queue/types.d.ts +61 -0
- package/dist/redis/client.d.ts +2 -0
- package/{redis/index.ts → dist/redis/index.d.ts} +2 -2
- package/dist/redis/types.d.ts +17 -0
- package/dist/test/test-utils.d.ts +193 -0
- package/dist/types.d.ts +50 -0
- package/package.json +10 -12
- package/ai/provider.ts +0 -129
- package/ai/stream.ts +0 -63
- package/cli.ts +0 -147
- package/core/cookie.ts +0 -114
- package/core/env.ts +0 -142
- package/core/logger.ts +0 -72
- package/core/router.ts +0 -795
- package/core/serve.ts +0 -294
- package/core/sse.ts +0 -85
- package/core/trace.ts +0 -146
- package/graphql.ts +0 -267
- package/hub.ts +0 -133
- package/index.ts +0 -71
- package/mailer.ts +0 -81
- package/middleware/compress.ts +0 -103
- package/middleware/cors.ts +0 -81
- package/middleware/csrf.ts +0 -112
- package/middleware/flash.ts +0 -144
- package/middleware/health.ts +0 -44
- package/middleware/helmet.ts +0 -98
- package/middleware/i18n.ts +0 -175
- package/middleware/rate-limit.ts +0 -167
- package/middleware/request-id.ts +0 -60
- package/middleware/static.ts +0 -149
- package/middleware/theme.ts +0 -84
- package/middleware/upload.ts +0 -168
- package/middleware/validate.ts +0 -186
- package/postgres/client.ts +0 -132
- package/postgres/index.ts +0 -4
- package/postgres/module.ts +0 -37
- package/postgres/schema/columns.ts +0 -186
- package/postgres/schema/index.ts +0 -36
- package/postgres/schema/sql.ts +0 -39
- package/postgres/schema/table.ts +0 -548
- package/postgres/schema/where.ts +0 -99
- package/postgres/types.ts +0 -48
- package/queue/cron.ts +0 -90
- package/queue/index.ts +0 -654
- package/queue/types.ts +0 -60
- package/redis/client.ts +0 -24
- package/redis/types.ts +0 -28
- package/types.ts +0 -78
package/dist/mailer.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { type Transporter } from 'nodemailer';
|
|
2
|
+
import type { Closeable } from './types.ts';
|
|
3
|
+
/** Options for sending an email. */
|
|
4
|
+
export interface MailOptions {
|
|
5
|
+
/** Recipient address(es). */
|
|
6
|
+
to: string | string[];
|
|
7
|
+
/** Email subject. */
|
|
8
|
+
subject: string;
|
|
9
|
+
/** Plain text body. */
|
|
10
|
+
text?: string;
|
|
11
|
+
/** HTML body. */
|
|
12
|
+
html?: string;
|
|
13
|
+
/** Sender address (overrides `MailerOptions.from`). */
|
|
14
|
+
from?: string;
|
|
15
|
+
/** CC recipient(s). */
|
|
16
|
+
cc?: string | string[];
|
|
17
|
+
/** BCC recipient(s). */
|
|
18
|
+
bcc?: string | string[];
|
|
19
|
+
}
|
|
20
|
+
/** Options for {@link mailer}. */
|
|
21
|
+
export interface MailerOptions {
|
|
22
|
+
/** Nodemailer transport string or pre-built transporter object. */
|
|
23
|
+
transport?: string | Transporter;
|
|
24
|
+
/** Default sender address. */
|
|
25
|
+
from?: string;
|
|
26
|
+
/** Custom send function (bypasses nodemailer). */
|
|
27
|
+
send?: (opts: MailOptions) => Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
/** Mailer instance returned by {@link mailer}. */
|
|
30
|
+
export interface Mailer extends Closeable {
|
|
31
|
+
/** Send an email. */
|
|
32
|
+
send: (opts: MailOptions) => Promise<void>;
|
|
33
|
+
/** Close the nodemailer transport. */
|
|
34
|
+
close: () => Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Create a mailer instance.
|
|
38
|
+
*
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { mailer } from 'weifuwu'
|
|
41
|
+
*
|
|
42
|
+
* const email = mailer({ transport: 'smtp://user:pass@smtp.example.com' })
|
|
43
|
+
* await email.send({
|
|
44
|
+
* to: 'user@example.com',
|
|
45
|
+
* subject: 'Hello',
|
|
46
|
+
* text: 'Hello from weifuwu!',
|
|
47
|
+
* })
|
|
48
|
+
* await email.close()
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function mailer(options: MailerOptions): Mailer;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Middleware } from '../types.ts';
|
|
2
|
+
/** Options for {@link compress}. */
|
|
3
|
+
export interface CompressOptions {
|
|
4
|
+
/** Compression level (1-9, default: 6). */
|
|
5
|
+
level?: number;
|
|
6
|
+
/** Minimum response body size in bytes to compress (default: 1024). */
|
|
7
|
+
threshold?: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Response compression middleware (brotli, gzip, deflate).
|
|
11
|
+
*
|
|
12
|
+
* Automatically selects the best encoding based on `Accept-Encoding` header.
|
|
13
|
+
* Skips compression for small responses, images, audio, video, and already-encoded responses.
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { compress } from 'weifuwu'
|
|
17
|
+
* app.use(compress())
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function compress(options?: CompressOptions): Middleware;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Middleware, Context } from '../types.ts';
|
|
2
|
+
/** Options for {@link cors}. */
|
|
3
|
+
export interface CORSOptions {
|
|
4
|
+
/** Allowed origin(s). Default `'*'`. If `credentials: true`, reflects the request origin. */
|
|
5
|
+
origin?: string | string[] | ((origin: string) => string | boolean | undefined);
|
|
6
|
+
/** Allowed HTTP methods. Default: `GET, HEAD, PUT, PATCH, POST, DELETE`. */
|
|
7
|
+
methods?: string[];
|
|
8
|
+
/** Allowed request headers. Default: `Content-Type, Authorization`. */
|
|
9
|
+
allowedHeaders?: string[];
|
|
10
|
+
/** Exposed response headers. */
|
|
11
|
+
exposedHeaders?: string[];
|
|
12
|
+
/** Whether to expose `Access-Control-Allow-Credentials`. */
|
|
13
|
+
credentials?: boolean;
|
|
14
|
+
/** `Access-Control-Max-Age` in seconds. */
|
|
15
|
+
maxAge?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* CORS middleware.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { cors } from 'weifuwu'
|
|
22
|
+
* app.use(cors({ origin: 'https://myapp.com', credentials: true }))
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function cors(options?: CORSOptions): Middleware<Context, Context>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Context, Middleware } from '../types.ts';
|
|
2
|
+
declare module '../types.ts' {
|
|
3
|
+
interface Context {
|
|
4
|
+
csrf: CsrfInjected;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export interface CsrfInjected {
|
|
8
|
+
token: string;
|
|
9
|
+
}
|
|
10
|
+
/** Options for {@link csrf}. */
|
|
11
|
+
/** CSRF protection module — a {@link Middleware} that injects `ctx.csrf`. */
|
|
12
|
+
export type CsrfModule = Middleware<Context, Context & CsrfInjected>;
|
|
13
|
+
export interface CsrfOptions {
|
|
14
|
+
/** Cookie name for CSRF token (default: `'_csrf'`). */
|
|
15
|
+
cookie?: string;
|
|
16
|
+
/** Request header name for CSRF token (default: `'x-csrf-token'`). */
|
|
17
|
+
header?: string;
|
|
18
|
+
/** Form body key for CSRF token (default: `'_csrf'`). */
|
|
19
|
+
key?: string;
|
|
20
|
+
/** HTTP methods to exclude from CSRF protection (default: `['GET', 'HEAD', 'OPTIONS']`). */
|
|
21
|
+
excludeMethods?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* CSRF protection middleware.
|
|
25
|
+
*
|
|
26
|
+
* On excluded methods (GET, HEAD, OPTIONS), generates a token and stores it
|
|
27
|
+
* in a cookie. On other methods, validates the token from header or body
|
|
28
|
+
* against the cookie.
|
|
29
|
+
*
|
|
30
|
+
* Injects `ctx.csrf.token` for use in forms.
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { csrf } from 'weifuwu'
|
|
34
|
+
* app.use(csrf())
|
|
35
|
+
*
|
|
36
|
+
* // In a form:
|
|
37
|
+
* app.get('/form', (req, ctx) => {
|
|
38
|
+
* return new Response(`
|
|
39
|
+
* <form method="POST">
|
|
40
|
+
* <input type="hidden" name="_csrf" value="${ctx.csrf.token}" />
|
|
41
|
+
* <input type="submit" />
|
|
42
|
+
* </form>
|
|
43
|
+
* `, { headers: { 'content-type': 'text/html' } })
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function csrf(options?: CsrfOptions): Middleware<Context, Context & CsrfInjected>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flash message middleware.
|
|
3
|
+
*
|
|
4
|
+
* Provides a cookie-based flash message system:
|
|
5
|
+
* - Read: `ctx.flash.value` parses the incoming flash cookie
|
|
6
|
+
* - Write: `ctx.flash.set(data, location)` sets a new flash and redirects
|
|
7
|
+
* - Auto-clear: after reading, the flash cookie is cleared from the response
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { flash } from 'weifuwu'
|
|
11
|
+
*
|
|
12
|
+
* app.use(flash())
|
|
13
|
+
*
|
|
14
|
+
* // Read flash
|
|
15
|
+
* app.get('/', (req, ctx) => {
|
|
16
|
+
* const msg = ctx.flash.value // e.g. { type: 'success', text: 'Saved!' }
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // Set flash + redirect
|
|
20
|
+
* app.post('/save', async (req, ctx) => {
|
|
21
|
+
* await save()
|
|
22
|
+
* return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import type { Context, Middleware } from '../types.ts';
|
|
27
|
+
declare module '../types.ts' {
|
|
28
|
+
interface Context {
|
|
29
|
+
flash: FlashInjected;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Flash message module — a {@link Middleware} that injects `ctx.flash`. */
|
|
33
|
+
export type FlashModule = Middleware<Context, Context & FlashInjected>;
|
|
34
|
+
/** Options for {@link flash}. */
|
|
35
|
+
export interface FlashOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Cookie name to store the flash message.
|
|
38
|
+
* @default 'flash'
|
|
39
|
+
*/
|
|
40
|
+
name?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Flash message object injected into `ctx.flash`.
|
|
44
|
+
*
|
|
45
|
+
* Access the current flash with `.value`, or set a new flash with `.set()`.
|
|
46
|
+
* The `.value` is automatically cleared after being read.
|
|
47
|
+
*/
|
|
48
|
+
export interface FlashInjected {
|
|
49
|
+
/**
|
|
50
|
+
* The flash value read from the incoming cookie.
|
|
51
|
+
* `undefined` if no flash cookie is present.
|
|
52
|
+
* Automatically cleared after the response is sent.
|
|
53
|
+
*/
|
|
54
|
+
value: unknown;
|
|
55
|
+
/**
|
|
56
|
+
* Set a flash message and return a 302 redirect response.
|
|
57
|
+
*
|
|
58
|
+
* @param data - Any JSON-serializable value to store as the flash message.
|
|
59
|
+
* @param location - Redirect location (defaults to the `Referer` header).
|
|
60
|
+
* @returns A 302 Response with a `Set-Cookie` header.
|
|
61
|
+
*
|
|
62
|
+
* ```ts
|
|
63
|
+
* return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
set: (data: unknown, location?: string) => Response;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Flash message middleware — injects `ctx.flash`.
|
|
70
|
+
*
|
|
71
|
+
* @param options - Cookie name configuration.
|
|
72
|
+
* @returns Middleware that injects `ctx.flash` (`FlashInjected`).
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* app.use(flash())
|
|
76
|
+
*
|
|
77
|
+
* // Read
|
|
78
|
+
* app.get('/', (req, ctx) => {
|
|
79
|
+
* const msg = ctx.flash.value
|
|
80
|
+
* })
|
|
81
|
+
*
|
|
82
|
+
* // Write + redirect
|
|
83
|
+
* app.post('/save', async (req, ctx) => {
|
|
84
|
+
* return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
|
|
85
|
+
* })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function flash(options?: FlashOptions): Middleware<Context, Context & {
|
|
89
|
+
flash: FlashInjected;
|
|
90
|
+
}>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Router } from '../core/router.ts';
|
|
2
|
+
/** Options for {@link health}. */
|
|
3
|
+
export interface HealthOptions {
|
|
4
|
+
/** Health check endpoint path (default: `'/__health'`). */
|
|
5
|
+
path?: string;
|
|
6
|
+
/** Async function that throws if the service is unhealthy. Called on each request. */
|
|
7
|
+
check?: () => Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Health check endpoint.
|
|
11
|
+
*
|
|
12
|
+
* Returns 200 with `'OK'` if the check passes, 503 if it fails.
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { health } from 'weifuwu'
|
|
16
|
+
*
|
|
17
|
+
* app.use(health({
|
|
18
|
+
* check: async () => {
|
|
19
|
+
* await db.query('SELECT 1')
|
|
20
|
+
* },
|
|
21
|
+
* }))
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export declare function health(options?: HealthOptions): Router;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Middleware, Context } from '../types.ts';
|
|
2
|
+
/** Options for {@link helmet}. Set any header to `false` to omit it. */
|
|
3
|
+
export interface HelmetOptions {
|
|
4
|
+
/** `Content-Security-Policy` header value. */
|
|
5
|
+
contentSecurityPolicy?: string | false;
|
|
6
|
+
/** `Cross-Origin-Embedder-Policy` header value. */
|
|
7
|
+
crossOriginEmbedderPolicy?: string | false;
|
|
8
|
+
/** `Cross-Origin-Opener-Policy` header value. */
|
|
9
|
+
crossOriginOpenerPolicy?: string | false;
|
|
10
|
+
/** `Cross-Origin-Resource-Policy` header value. */
|
|
11
|
+
crossOriginResourcePolicy?: string | false;
|
|
12
|
+
/** `Origin-Agent-Cluster` header value. */
|
|
13
|
+
originAgentCluster?: string | false;
|
|
14
|
+
/** `Referrer-Policy` header value. */
|
|
15
|
+
referrerPolicy?: string | false;
|
|
16
|
+
/** `Strict-Transport-Security` header value. */
|
|
17
|
+
strictTransportSecurity?: string | false;
|
|
18
|
+
/** `X-Content-Type-Options` header value. */
|
|
19
|
+
xContentTypeOptions?: string | false;
|
|
20
|
+
/** `X-DNS-Prefetch-Control` header value. */
|
|
21
|
+
xDnsPrefetchControl?: string | false;
|
|
22
|
+
/** `X-Download-Options` header value. */
|
|
23
|
+
xDownloadOptions?: string | false;
|
|
24
|
+
/** `X-Frame-Options` header value. */
|
|
25
|
+
xFrameOptions?: string | false;
|
|
26
|
+
/** `X-Permitted-Cross-Domain-Policies` header value. */
|
|
27
|
+
xPermittedCrossDomainPolicies?: string | false;
|
|
28
|
+
/** `X-XSS-Protection` header value. */
|
|
29
|
+
xXssProtection?: string | false;
|
|
30
|
+
/** `Permissions-Policy` header value. */
|
|
31
|
+
permissionsPolicy?: string | false;
|
|
32
|
+
}
|
|
33
|
+
export declare function helmet(options?: HelmetOptions): Middleware<Context, Context>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Context, Middleware } from '../types.ts';
|
|
2
|
+
import { Router } from '../core/router.ts';
|
|
3
|
+
declare module '../types.ts' {
|
|
4
|
+
interface Context {
|
|
5
|
+
i18n: I18nInjected;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export interface I18nInjected {
|
|
9
|
+
locale: string;
|
|
10
|
+
messages?: Record<string, unknown>;
|
|
11
|
+
t: (key: string, params?: Record<string, string>, fallback?: string) => string;
|
|
12
|
+
set?: (value: string, loc?: string) => Response;
|
|
13
|
+
}
|
|
14
|
+
export interface I18nOptions {
|
|
15
|
+
/** Default locale (default: 'en'). */
|
|
16
|
+
default?: string;
|
|
17
|
+
/** Directory containing `{locale}.json` translation files. */
|
|
18
|
+
dir?: string;
|
|
19
|
+
/** Inline translation messages keyed by locale. */
|
|
20
|
+
messages?: Record<string, Record<string, unknown>>;
|
|
21
|
+
/** Cookie name for locale (default: 'locale'). Set empty to disable. */
|
|
22
|
+
cookie?: string;
|
|
23
|
+
/** Whether to detect locale from Accept-Language header (default: true). */
|
|
24
|
+
fromAcceptLanguage?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* i18n module. Returns a Router with an attached `.middleware()` method.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* const l = i18n({ dir: './locales' })
|
|
31
|
+
* app.use(l.middleware()) // → ctx.i18n = { locale, t, set }
|
|
32
|
+
* app.use('/', l) // → GET /__lang/:locale (switch route)
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export interface I18nModule extends Router {
|
|
36
|
+
/** Middleware that injects `ctx.i18n = { locale, t, set }`. */
|
|
37
|
+
middleware: () => Middleware<Context, Context & I18nInjected>;
|
|
38
|
+
}
|
|
39
|
+
export declare function i18n(options?: I18nOptions): I18nModule;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Redis, Context, Middleware, Closeable } from '../types.ts';
|
|
2
|
+
/** Options for {@link rateLimit}. */
|
|
3
|
+
export interface RateLimitOptions {
|
|
4
|
+
/** Maximum requests within the window (default: 100). */
|
|
5
|
+
max?: number;
|
|
6
|
+
/** Window duration in ms (default: 60000 = 1 minute). */
|
|
7
|
+
window?: number;
|
|
8
|
+
/** Custom key function. Default: IP from `x-forwarded-for` or `x-real-ip` or `cf-connecting-ip`. */
|
|
9
|
+
key?: (req: Request, ctx: Context) => string;
|
|
10
|
+
/** Custom 429 response body." */
|
|
11
|
+
message?: string;
|
|
12
|
+
/** Store backend. `'memory'` (default) or `'redis'`. */
|
|
13
|
+
store?: 'memory' | 'redis';
|
|
14
|
+
/** Redis client (required when `store: 'redis'`). */
|
|
15
|
+
redis?: Redis;
|
|
16
|
+
/** Redis key prefix (default: `'ratelimit:'`). */
|
|
17
|
+
prefix?: string;
|
|
18
|
+
}
|
|
19
|
+
/** Rate limit module — middleware + stats. */
|
|
20
|
+
export interface RateLimitModule extends Middleware<Context, Context>, Closeable {
|
|
21
|
+
stats(): {
|
|
22
|
+
store: string;
|
|
23
|
+
entries?: number;
|
|
24
|
+
maxEntries: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Rate limiting middleware (in-memory or Redis-backed).
|
|
29
|
+
*
|
|
30
|
+
* Limits requests per key (default: client IP) within a rolling window.
|
|
31
|
+
* Returns 429 when the limit is exceeded, with `Retry-After` header.
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* import { rateLimit } from 'weifuwu'
|
|
35
|
+
*
|
|
36
|
+
* // In-memory (single process)
|
|
37
|
+
* app.use(rateLimit({ max: 60, window: 60_000 }))
|
|
38
|
+
*
|
|
39
|
+
* // Redis-backed (multi-process)
|
|
40
|
+
* import { Redis } from 'ioredis'
|
|
41
|
+
* app.use(rateLimit({ store: 'redis', redis: new Redis(), max: 100 }))
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function rateLimit(options?: RateLimitOptions): RateLimitModule;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Context, Middleware } from '../types.ts';
|
|
2
|
+
declare module '../types.ts' {
|
|
3
|
+
interface Context {
|
|
4
|
+
requestId: string;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Options for {@link requestId}. */
|
|
8
|
+
/** Request ID module — a {@link Middleware} that injects `ctx.requestId`. */
|
|
9
|
+
export type RequestIdModule = Middleware<Context, Context & {
|
|
10
|
+
requestId: string;
|
|
11
|
+
}>;
|
|
12
|
+
export interface RequestIdOptions {
|
|
13
|
+
/** Header name for request ID (default: `'X-Request-ID'`). */
|
|
14
|
+
header?: string;
|
|
15
|
+
/** Custom ID generator (default: `crypto.randomUUID`). */
|
|
16
|
+
generator?: () => string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Request ID middleware.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Use `trace()` from 'weifuwu' instead — it injects `ctx.trace.requestId`
|
|
22
|
+
* along with `traceId` and `elapsed()` in a single middleware.
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* // Old:
|
|
26
|
+
* app.use(requestId())
|
|
27
|
+
* ctx.requestId
|
|
28
|
+
*
|
|
29
|
+
* // New:
|
|
30
|
+
* app.use(trace())
|
|
31
|
+
* ctx.trace.requestId
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Reads an incoming `X-Request-ID` header (or custom header name) from the
|
|
35
|
+
* request. If absent, generates a new UUID. Sets the response header and
|
|
36
|
+
* injects `ctx.requestId`.
|
|
37
|
+
*/
|
|
38
|
+
export declare function requestId(options?: RequestIdOptions): Middleware<Context, Context & {
|
|
39
|
+
requestId: string;
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Handler } from '../types.ts';
|
|
2
|
+
/** Options for {@link serveStatic}. */
|
|
3
|
+
export interface ServeStaticOptions {
|
|
4
|
+
/** Directory index filename (default: `'index.html'`). */
|
|
5
|
+
index?: string;
|
|
6
|
+
/** `Cache-Control max-age` in seconds. */
|
|
7
|
+
maxAge?: number;
|
|
8
|
+
/** Add `immutable` to `Cache-Control` (requires `maxAge`). */
|
|
9
|
+
immutable?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Static file serving handler.
|
|
13
|
+
*
|
|
14
|
+
* Serves files from a root directory. Supports ETag/304, directory index,
|
|
15
|
+
* Content-Type detection by extension, and directory traversal protection.
|
|
16
|
+
*
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { serveStatic, Router } from 'weifuwu'
|
|
19
|
+
* const app = new Router()
|
|
20
|
+
* app.get('/static/*', serveStatic('./public'))
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare function serveStatic(root: string, options?: ServeStaticOptions): Handler;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Context, Middleware } from '../types.ts';
|
|
2
|
+
import { Router } from '../core/router.ts';
|
|
3
|
+
declare module '../types.ts' {
|
|
4
|
+
interface Context {
|
|
5
|
+
theme: ThemeInjected;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export interface ThemeInjected {
|
|
9
|
+
value: string;
|
|
10
|
+
set: (value: string, loc?: string) => Response;
|
|
11
|
+
}
|
|
12
|
+
export interface ThemeOptions {
|
|
13
|
+
/** Default theme value (default: 'system'). */
|
|
14
|
+
default?: string;
|
|
15
|
+
/** Cookie name (default: 'theme'). Set to empty string to disable cookie. */
|
|
16
|
+
cookie?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Theme module. Returns a Router with an attached `.middleware()` method.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* const t = theme()
|
|
23
|
+
* app.use(t.middleware()) // → ctx.theme = { value, set }
|
|
24
|
+
* app.use('/', t) // → GET /__theme/dark (switch route)
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export interface ThemeModule extends Router {
|
|
28
|
+
/** Middleware that injects `ctx.theme = { value, set }`. */
|
|
29
|
+
middleware: () => Middleware<Context, Context & ThemeInjected>;
|
|
30
|
+
}
|
|
31
|
+
export declare function theme(options?: ThemeOptions): ThemeModule;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Context, Middleware } from '../types.ts';
|
|
2
|
+
declare module '../types.ts' {
|
|
3
|
+
interface Context {
|
|
4
|
+
parsed: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
/** Upload middleware — a {@link Middleware} that injects `ctx.parsed` with file fields. */
|
|
8
|
+
export type UploadModule = Middleware<Context, Context & {
|
|
9
|
+
parsed: Record<string, unknown>;
|
|
10
|
+
}>;
|
|
11
|
+
/** A parsed file from a multipart upload. */
|
|
12
|
+
export interface UploadedFile {
|
|
13
|
+
/** Original filename from the client. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** MIME type from the `Content-Type` part header. */
|
|
16
|
+
type: string;
|
|
17
|
+
/** File size in bytes. */
|
|
18
|
+
size: number;
|
|
19
|
+
/** Path where the file was saved (when `dir` option is set). */
|
|
20
|
+
path?: string;
|
|
21
|
+
/** File content as Buffer (when `dir` option is not set). */
|
|
22
|
+
buffer?: Buffer;
|
|
23
|
+
}
|
|
24
|
+
/** Options for {@link upload}. */
|
|
25
|
+
export interface UploadOptions {
|
|
26
|
+
/** Directory to save uploaded files. If not set, files stay in memory via `.buffer`. */
|
|
27
|
+
dir?: string;
|
|
28
|
+
/** Maximum file size in bytes. Default: 10 MB. Set `0` to allow unlimited. */
|
|
29
|
+
maxFileSize?: number;
|
|
30
|
+
/** Allowed MIME types (e.g. `['image/jpeg', 'image/png']`). Empty array allows all. */
|
|
31
|
+
allowedTypes?: string[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Multipart file upload middleware.
|
|
35
|
+
*
|
|
36
|
+
* Parses `multipart/form-data` requests, extracting files and fields.
|
|
37
|
+
* Files can be saved to disk (`dir` option) or kept in memory as Buffers.
|
|
38
|
+
* Parsed fields are available in `ctx.parsed`.
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { upload } from 'weifuwu'
|
|
42
|
+
*
|
|
43
|
+
* // Save to disk
|
|
44
|
+
* app.use(upload({ dir: './uploads', maxFileSize: 5_000_000 }))
|
|
45
|
+
*
|
|
46
|
+
* // In-memory
|
|
47
|
+
* app.post('/upload', async (req, ctx) => {
|
|
48
|
+
* const file = ctx.parsed?.file as UploadedFile
|
|
49
|
+
* console.log(file.name, file.type, file.buffer!.length)
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function upload(options?: UploadOptions): Middleware<Context, Context & {
|
|
54
|
+
parsed: Record<string, unknown>;
|
|
55
|
+
}>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ZodSchema } from 'zod';
|
|
2
|
+
import type { Middleware } from '../types.ts';
|
|
3
|
+
declare module '../types.ts' {
|
|
4
|
+
interface Context {
|
|
5
|
+
parsed: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/** Validation middleware — a {@link Middleware} that injects `ctx.parsed` with validated data. */
|
|
9
|
+
export type ValidateModule = Middleware;
|
|
10
|
+
export interface ValidationSchemas {
|
|
11
|
+
body?: ZodSchema;
|
|
12
|
+
query?: ZodSchema;
|
|
13
|
+
params?: ZodSchema;
|
|
14
|
+
headers?: ZodSchema;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Request validation middleware using Zod schemas.
|
|
18
|
+
*
|
|
19
|
+
* Validates `params`, `query`, `body`, and/or `headers` against schemas.
|
|
20
|
+
* Returns 422 with error details on mismatch.
|
|
21
|
+
* Injects `ctx.parsed` with validated-and-transformed values.
|
|
22
|
+
*
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { z } from 'zod'
|
|
25
|
+
*
|
|
26
|
+
* app.get('/users/:id', validate({
|
|
27
|
+
* params: z.object({ id: z.string() }),
|
|
28
|
+
* query: z.object({ include: z.string().optional() }),
|
|
29
|
+
* }), handler)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function validate(schemas?: ValidationSchemas): Middleware;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PostgresOptions, PostgresClient } from './types.ts';
|
|
2
|
+
/** Migration tracking table name. Created automatically on first migrate(). */
|
|
3
|
+
export declare const MIGRATIONS_TABLE = "_weifuwu_migrations";
|
|
4
|
+
export declare function postgres(opts?: string | PostgresOptions): PostgresClient;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PostgresClient } from './types.ts';
|
|
2
|
+
import type { SqlClient, Closeable } from '../types.ts';
|
|
3
|
+
import type { ColumnBuilder, BoundTable, Table } from './schema/index.ts';
|
|
4
|
+
export declare class PgModule implements Closeable {
|
|
5
|
+
protected sql: SqlClient;
|
|
6
|
+
protected pg: PostgresClient;
|
|
7
|
+
constructor(pg: PostgresClient);
|
|
8
|
+
table<R extends Record<string, unknown>>(tableOrSchema: string | Table<R>, builders?: {
|
|
9
|
+
[K in keyof R]: ColumnBuilder<R[K]>;
|
|
10
|
+
}): BoundTable<R>;
|
|
11
|
+
transaction<T>(fn: (sql: SqlClient) => Promise<T>, retryOpts?: {
|
|
12
|
+
maxRetries?: number;
|
|
13
|
+
}): Promise<T>;
|
|
14
|
+
migrate(): Promise<void>;
|
|
15
|
+
close(): Promise<void>;
|
|
16
|
+
}
|