weifuwu 0.30.1 → 0.31.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/package.json CHANGED
@@ -1,12 +1,24 @@
1
1
  {
2
2
  "name": "weifuwu",
3
3
  "type": "module",
4
- "version": "0.30.1",
4
+ "version": "0.31.0",
5
5
  "description": "Web-standard HTTP microframework for Node.js — (req, ctx) => Response",
6
6
  "exports": {
7
7
  ".": {
8
8
  "types": "./dist/index.d.ts",
9
9
  "default": "./dist/index.js"
10
+ },
11
+ "./react": {
12
+ "types": "./dist/react/index.d.ts",
13
+ "default": "./dist/react/index.js"
14
+ },
15
+ "./react/client": {
16
+ "types": "./dist/react/client.d.ts",
17
+ "default": "./dist/react/client.js"
18
+ },
19
+ "./react/navigation": {
20
+ "types": "./dist/react/navigation.d.ts",
21
+ "default": "./dist/react/navigation.js"
10
22
  }
11
23
  },
12
24
  "files": [
@@ -17,8 +29,8 @@
17
29
  "build": "node scripts/build.mjs",
18
30
  "prepublishOnly": "npm run build && tsc --emitDeclarationOnly --outdir dist",
19
31
  "typecheck": "tsc --noEmit",
20
- "test": "node --test 'src/test/**/*.test.ts'",
21
- "test:coverage": "node --experimental-test-coverage --test 'src/test/**/*.test.ts'",
32
+ "pretest": "docker compose up -d --wait 2>/dev/null; sleep 1",
33
+ "test": "node --env-file=.env --test 'src/test/**/*.test.ts'",
22
34
  "release": "node scripts/release.mjs",
23
35
  "release:dry": "node scripts/release.mjs --dry-run"
24
36
  },
@@ -27,14 +39,30 @@
27
39
  "graphql": "^16",
28
40
  "ioredis": "^5.11.0",
29
41
  "postgres": "^3.4.9",
30
- "ws": "^8",
31
- "zod": "^4.4.3"
42
+ "ws": "^8"
43
+ },
44
+ "peerDependencies": {
45
+ "react": "19",
46
+ "react-dom": "19"
47
+ },
48
+ "peerDependenciesMeta": {
49
+ "react": {
50
+ "optional": true
51
+ },
52
+ "react-dom": {
53
+ "optional": true
54
+ }
32
55
  },
33
56
  "devDependencies": {
57
+ "@eslint/js": "^10.0.1",
34
58
  "@types/node": "^25",
59
+ "@types/react": "^19.2.17",
60
+ "@types/react-dom": "^19.2.3",
35
61
  "@types/ws": "^8",
36
- "@eslint/js": "^10.0.1",
62
+ "esbuild": "^0.28.1",
37
63
  "globals": "^17.7.0",
64
+ "react": "^19.2.7",
65
+ "react-dom": "^19.2.7",
38
66
  "typescript-eslint": "^8.62.1"
39
67
  },
40
68
  "sideEffects": false
@@ -1,36 +0,0 @@
1
- /** Options for setting a cookie. All fields map to standard Set-Cookie attributes. */
2
- export interface CookieOptions {
3
- domain?: string;
4
- path?: string;
5
- maxAge?: number;
6
- expires?: Date;
7
- httpOnly?: boolean;
8
- secure?: boolean;
9
- sameSite?: 'strict' | 'lax' | 'none';
10
- }
11
- /** Parse cookies from a Request's `Cookie` header.
12
- *
13
- * @example
14
- * ```ts
15
- * const cookies = getCookies(req)
16
- * console.log(cookies.session_id) // value or undefined
17
- * ``` */
18
- export declare function getCookies(req: Request): Record<string, string>;
19
- /** Set a cookie on a Response.
20
- *
21
- * Appends a `Set-Cookie` header to the existing response headers.
22
- * Returns a new Response with the added header.
23
- *
24
- * @example
25
- * ```ts
26
- * const res = new Response('ok')
27
- * return setCookie(res, 'session', token, { httpOnly: true, path: '/' })
28
- * ``` */
29
- export declare function setCookie(res: Response, name: string, value: string, options?: CookieOptions): Response;
30
- /** Delete a cookie by setting `Max-Age=0`.
31
- *
32
- * @example
33
- * ```ts
34
- * return deleteCookie(res, 'session')
35
- * ``` */
36
- export declare function deleteCookie(res: Response, name: string, options?: Omit<CookieOptions, 'maxAge'>): Response;
@@ -1,69 +0,0 @@
1
- import type { Context, Middleware } from '../types.ts';
2
- /**
3
- * Get all public environment variables (those prefixed with `WEIFUWU_PUBLIC_`),
4
- * with the prefix stripped.
5
- *
6
- * ```ts
7
- * const pub = getPublicEnv()
8
- * // WEIFUWU_PUBLIC_API_URL=http://api.example.com → { API_URL: 'http://api.example.com' }
9
- * ```
10
- */
11
- export declare function getPublicEnv(): Record<string, string>;
12
- declare module '../types.ts' {
13
- interface Context {
14
- env?: Record<string, string>;
15
- }
16
- }
17
- /**
18
- * Whether this code is running from the compiled `dist/index.js` bundle.
19
- * `false` when running TypeScript source directly (dev workflow in weifuwu repo).
20
- *
21
- * Used by modules that need to resolve package-internal files differently
22
- * depending on whether they are compiled (published npm package) or raw TS.
23
- */
24
- export declare function isBundled(): boolean;
25
- /**
26
- * Whether `NODE_ENV` is explicitly set to `'development'`.
27
- *
28
- * Used for dev-only features: HMR, livereload, React `createRoot` (not hydrate).
29
- * **Not** the opposite of {@link isProd} — when `NODE_ENV` is unset, both return `false`.
30
- */
31
- export declare function isDev(): boolean;
32
- /**
33
- * Whether `NODE_ENV` is explicitly set to `'production'`.
34
- *
35
- * Used for production-only behavior: plain-text 404, suppressed warnings, minified output.
36
- */
37
- export declare function isProd(): boolean;
38
- /**
39
- * Load environment variables from a `.env` file into `process.env`.
40
- *
41
- * Does **not** override existing `process.env` values.
42
- * Supports quoted values and inline comments.
43
- *
44
- * @param path - Path to `.env` file (default: `'.env'` relative to cwd).
45
- *
46
- * ```ts
47
- * import { loadEnv } from 'weifuwu'
48
- * loadEnv()
49
- * console.log(process.env.PORT)
50
- * ```
51
- */
52
- export declare function loadEnv(path?: string): void;
53
- /**
54
- * Public env middleware.
55
- *
56
- * Injects `ctx.env` with all environment variables prefixed with `WEIFUWU_PUBLIC_`,
57
- * with the prefix stripped. Safe to expose to the client.
58
- *
59
- * ```ts
60
- * import { env } from 'weifuwu'
61
- * app.use(env())
62
- *
63
- * // .env: WEIFUWU_PUBLIC_API_URL=https://api.example.com
64
- * // ctx: ctx.env.API_URL === 'https://api.example.com'
65
- * ```
66
- */
67
- export declare function env(): Middleware<Context, Context & {
68
- env: Record<string, string>;
69
- }>;
@@ -1,34 +0,0 @@
1
- /**
2
- * Safe HTML rendering via tagged template literals.
3
- *
4
- * Auto-escapes interpolated values to prevent XSS.
5
- * Use `raw()` for trusted HTML that should not be escaped.
6
- *
7
- * ```ts
8
- * import { html, raw } from '@weifuwujs/core'
9
- *
10
- * html`<h1>${title}</h1>` // auto-escaped
11
- * html`<div>${raw(trustedHtml)}</div>` // unescaped
12
- * html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>` // arrays
13
- * html`${isAdmin && html`<button>Admin</button>`}` // conditionals
14
- * html`<div>${html`<span>nested</span>`}</div>` // nested (safe)
15
- * ```
16
- */
17
- interface RawHtml {
18
- _raw: string;
19
- }
20
- export type HtmlValue = string | number | boolean | null | undefined | HtmlValue[] | RawHtml;
21
- /**
22
- * Tagged template literal for safe HTML.
23
- *
24
- * Interpolated values are auto-escaped. Use {@link raw} to bypass escaping
25
- * for trusted HTML. Nested `html` calls are safe (not double-escaped).
26
- */
27
- export declare function html(strings: TemplateStringsArray, ...values: HtmlValue[]): string;
28
- /**
29
- * Bypass HTML escaping for trusted content.
30
- *
31
- * Can be used standalone or inside {@link html} tagged templates.
32
- */
33
- export declare function raw(content: string): HtmlValue;
34
- export {};
@@ -1,47 +0,0 @@
1
- /**
2
- * Format an SSE message string with a named event type.
3
- *
4
- * ```ts
5
- * formatSSE('ping', { ts: Date.now() })
6
- * // "event: ping\ndata: {"ts":...}\n\n"
7
- * ```
8
- */
9
- export declare function formatSSE(event: string, data: unknown): string;
10
- /**
11
- * Format an SSE message string with only a data line (no event type).
12
- *
13
- * ```ts
14
- * formatSSEData({ message: 'hello' })
15
- * // "data: {"message":"hello"}\n\n"
16
- * ```
17
- */
18
- export declare function formatSSEData(data: unknown): string;
19
- /** An SSE event to be sent via {@link createSSEStream}. */
20
- export interface SSEEvent {
21
- /** Event type (maps to `event:` field). */
22
- event: string;
23
- /** Event payload (serialized as JSON in `data:` field). */
24
- data: unknown;
25
- }
26
- /**
27
- * Create a Server-Sent Events (SSE) `Response` from an async iterable.
28
- *
29
- * Each item in the iterable is serialized as an SSE message:
30
- * - If the item has a `.type` property → `event: {type}` + `data: {item}`
31
- * - Otherwise → `data: {item}`
32
- *
33
- * Errors are sent as `event: error` messages. `AbortError` is silently ignored.
34
- *
35
- * ```ts
36
- * app.get('/events', () => {
37
- * async function* generate() {
38
- * yield { type: 'ping', data: { ts: Date.now() } }
39
- * }
40
- * return createSSEStream(generate())
41
- * })
42
- * ```
43
- */
44
- export declare function createSSEStream(iterable: AsyncIterable<any>, opts?: {
45
- headers?: Record<string, string>;
46
- status?: number;
47
- }): Response;
@@ -1,31 +0,0 @@
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
- /** CSRF protection module — a {@link Middleware} that injects `ctx.csrf`. */
11
- export type CsrfModule = Middleware<Context, Context & CsrfInjected>;
12
- export interface CsrfOptions {
13
- /** Cookie name for CSRF token (default: `'_csrf'`). */
14
- cookie?: string;
15
- /** Request header name for CSRF token (default: `'x-csrf-token'`). */
16
- header?: string;
17
- /** Form body key for CSRF token (default: `'_csrf'`). */
18
- key?: string;
19
- /** HTTP methods to exclude from CSRF protection (default: `['GET', 'HEAD', 'OPTIONS']`). */
20
- excludeMethods?: string[];
21
- }
22
- /**
23
- * CSRF protection middleware.
24
- *
25
- * On excluded methods (GET, HEAD, OPTIONS), generates a token and stores it
26
- * in a cookie. On other methods, validates the token from header or body
27
- * against the cookie.
28
- *
29
- * Injects `ctx.csrf.token` for use in forms.
30
- */
31
- export declare function csrf(options?: CsrfOptions): Middleware<Context, Context & CsrfInjected>;
@@ -1,44 +0,0 @@
1
- import type { Context, Middleware } from '../types.ts';
2
- declare module '../types.ts' {
3
- interface Context {
4
- flash: FlashInjected;
5
- }
6
- }
7
- /** Flash message module — a {@link Middleware} that injects `ctx.flash`. */
8
- export type FlashModule = Middleware<Context, Context & FlashInjected>;
9
- /** Options for {@link flash}. */
10
- export interface FlashOptions {
11
- /**
12
- * Cookie name to store the flash message.
13
- * @default 'flash'
14
- */
15
- name?: string;
16
- }
17
- /**
18
- * Flash message object injected into `ctx.flash`.
19
- */
20
- export interface FlashInjected {
21
- /**
22
- * The flash value read from the incoming cookie.
23
- * `undefined` if no flash cookie is present.
24
- * Automatically cleared after the response is sent.
25
- */
26
- value: unknown;
27
- /**
28
- * Set a flash message and return a 302 redirect response.
29
- *
30
- * @param data - Any JSON-serializable value to store as the flash message.
31
- * @param location - Redirect location (defaults to the `Referer` header).
32
- * @returns A 302 Response with a `Set-Cookie` header.
33
- */
34
- set: (data: unknown, location?: string) => Response;
35
- }
36
- /**
37
- * Flash message middleware — injects `ctx.flash`.
38
- *
39
- * @param options - Cookie name configuration.
40
- * @returns Middleware that injects `ctx.flash` (`FlashInjected`).
41
- */
42
- export declare function flash(options?: FlashOptions): Middleware<Context, Context & {
43
- flash: FlashInjected;
44
- }>;
@@ -1,24 +0,0 @@
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;
@@ -1,39 +0,0 @@
1
- import { Router } from '../core/router.ts';
2
- import type { Context, Middleware } from '../types.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.mount('/', 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;
@@ -1,40 +0,0 @@
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
- }>;
@@ -1,31 +0,0 @@
1
- import { Router } from '../core/router.ts';
2
- import type { Context, Middleware } from '../types.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.mount('/', 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;
@@ -1,32 +0,0 @@
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;