weifuwu 0.23.2 → 0.23.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.
package/README.md CHANGED
@@ -475,6 +475,26 @@ app.use(cors({ credentials: true, maxAge: 3600 }))
475
475
  | `credentials` | `boolean` | `false` | Allow cookies/credentials |
476
476
  | `maxAge` | `number` | — | Preflight cache duration (seconds) |
477
477
 
478
+ ### flash [α]
479
+
480
+ Cookie-based flash message. Read from request, write via redirect.
481
+
482
+ ```ts
483
+ app.use(flash())
484
+
485
+ app.get('/', (req, ctx) => {
486
+ const msg = ctx.flash.value // { type: 'success', text: 'Saved!' } or undefined
487
+ })
488
+
489
+ app.post('/save', (req, ctx) => {
490
+ return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
491
+ })
492
+ ```
493
+
494
+ | Option | Type | Default | Description |
495
+ |--------|------|---------|-------------|
496
+ | `name` | `string` | `'flash'` | Cookie name |
497
+
478
498
  ### cache [α]
479
499
 
480
500
  Response caching middleware with memory and Redis stores. Caches GET/HEAD responses, with tag-based invalidation.
@@ -1092,6 +1112,24 @@ class MyModule extends PgModule {
1092
1112
 
1093
1113
  Where helpers + `and`/`or`/`not` can be imported from `'weifuwu'` alongside `postgres`. Full column builders and table helpers are in the same barrel.
1094
1114
 
1115
+ ### cron-utils
1116
+
1117
+ Shared cron expression parsing utilities. All functions operate in **local timezone**.
1118
+
1119
+ ```ts
1120
+ import { cronNext } from 'weifuwu'
1121
+
1122
+ // Next weekday at 09:00
1123
+ const next = cronNext('0 9 * * 1-5')
1124
+ console.log(new Date(next))
1125
+ ```
1126
+
1127
+ | Function | Description |
1128
+ |----------|-------------|
1129
+ | `parsePattern(pattern)` | Parse 5-field cron pattern into `Set<number>[]` |
1130
+ | `matches(fields, date)` | Check if a date matches a parsed pattern |
1131
+ | `cronNext(expr, from?)` | Calculate next matching timestamp (`from` defaults to now) |
1132
+
1095
1133
  ### fts — Full-Text Search (PostgreSQL)
1096
1134
 
1097
1135
  Utilities for PostgreSQL full-text search: create GIN indexes, search with ranking, and generate highlighted snippets.
@@ -1315,7 +1353,24 @@ app.use(requestId({ header: 'X-Request-Id', generator: () => crypto.randomUUID()
1315
1353
  | `header` | `string` | `'X-Request-ID'` | Header name to read/write |
1316
1354
  | `generator` | `() => string` | `crypto.randomUUID()` | ID generator |
1317
1355
 
1356
+ ### trace
1318
1357
 
1358
+ Request-scoped tracing via `AsyncLocalStorage`. Used internally by `serve()`.
1359
+
1360
+ ```ts
1361
+ import { currentTraceId, runWithTrace, traceElapsed } from 'weifuwu'
1362
+
1363
+ // Inside a middleware or handler
1364
+ const traceId = currentTraceId() // UUID or incoming X-Trace-Id
1365
+ const elapsed = traceElapsed() // ms since request started
1366
+ ```
1367
+
1368
+ | Function | Description |
1369
+ |----------|-------------|
1370
+ | `currentTraceId()` | Current request trace ID, or `undefined` outside a request |
1371
+ | `currentTrace()` | Full `{ traceId, startTime }` context |
1372
+ | `traceElapsed()` | Milliseconds elapsed since the trace started |
1373
+ | `runWithTrace(traceId, fn)` | Execute `fn` inside a trace scope |
1319
1374
 
1320
1375
  ### s3 [α] — S3-compatible object storage
1321
1376
 
@@ -1,8 +1,8 @@
1
- // ui/app/page.tsx
1
+ // cli/template/ui/app/page.tsx
2
2
  import { useState } from "react";
3
3
  import { useWebsocket, useLoaderData, useLocale, useTheme } from "weifuwu/react";
4
4
 
5
- // ui/components/Greeting.tsx
5
+ // cli/template/ui/components/Greeting.tsx
6
6
  import { jsxs } from "react/jsx-runtime";
7
7
  function Greeting({ name }) {
8
8
  return /* @__PURE__ */ jsxs("span", { className: "text-red-500 font-bold", children: [
@@ -11,7 +11,7 @@ function Greeting({ name }) {
11
11
  ] });
12
12
  }
13
13
 
14
- // ui/app/page.tsx
14
+ // cli/template/ui/app/page.tsx
15
15
  import { jsx, jsxs as jsxs2 } from "react/jsx-runtime";
16
16
  function Home() {
17
17
  const [input, setInput] = useState("");
@@ -1,15 +1,35 @@
1
1
  import type { Middleware } from './types.ts';
2
2
  import { Router } from './router.ts';
3
+ /** Options for {@link analytics}. */
3
4
  export interface AnalyticsOptions {
5
+ /** Path prefixes to exclude from analytics (default: `['/__analytics', '/__wfw', '/static']`). */
4
6
  excluded?: string[];
7
+ /** PostgreSQL client for persistent storage. Required for production use. */
5
8
  pg?: {
6
9
  sql: (strings: TemplateStringsArray, ...values: any[]) => Promise<any[]>;
7
10
  table: (name: string, cols: any) => any;
8
11
  };
9
12
  }
13
+ /** Analytics module returned by {@link analytics}. */
10
14
  export interface AnalyticsModule extends Router {
15
+ /** Middleware that records page views. */
11
16
  middleware: () => Middleware;
17
+ /** Create/update the analytics database tables. */
12
18
  migrate: () => Promise<void>;
19
+ /** Close the in-memory store and any timers. */
13
20
  close: () => Promise<void>;
14
21
  }
22
+ /**
23
+ * Page view analytics module.
24
+ *
25
+ * Records page views with referrer and device type. Supports in-memory and PostgreSQL storage.
26
+ * Provides a dashboard at `GET /__analytics`.
27
+ *
28
+ * ```ts
29
+ * import { analytics, postgres } from 'weifuwu'
30
+ *
31
+ * const pg = postgres({ connection: DATABASE_URL })
32
+ * app.use(analytics({ pg }).middleware())
33
+ * ```
34
+ */
15
35
  export declare function analytics(options?: AnalyticsOptions): AnalyticsModule;
@@ -1,3 +1,23 @@
1
+ /**
2
+ * React hook to read and change the locale on the client side.
3
+ *
4
+ * Changes are made via SPA navigation to `/__lang/{locale}`, which is
5
+ * intercepted by the client router and applied without a full page reload.
6
+ *
7
+ * ```tsx
8
+ * import { useLocale } from 'weifuwu/react'
9
+ *
10
+ * function LangSwitcher() {
11
+ * const { locale, setLocale, t } = useLocale()
12
+ * return (
13
+ * <>
14
+ * <p>{t('greeting')}</p>
15
+ * <button onClick={() => setLocale('zh')}>中文</button>
16
+ * </>
17
+ * )
18
+ * }
19
+ * ```
20
+ */
1
21
  export declare function useLocale(): {
2
22
  locale: string | undefined;
3
23
  setLocale: (locale: string) => Promise<void>;
@@ -1,4 +1,33 @@
1
+ /**
2
+ * Apply a theme value to the DOM by setting `document.documentElement.dataset.theme`.
3
+ * Resolves `'system'` to the user's `prefers-color-scheme` preference.
4
+ * Call this directly from non-React code (e.g. a `<script>` tag).
5
+ *
6
+ * ```ts
7
+ * import { applyTheme } from 'weifuwu/react'
8
+ * applyTheme('dark')
9
+ * ```
10
+ */
1
11
  declare function applyTheme(theme: string): void;
12
+ /**
13
+ * React hook to read and change the theme on the client side.
14
+ *
15
+ * Applies the resolved theme to `document.documentElement.dataset.theme`.
16
+ * Tracks system preference changes when theme is `'system'`.
17
+ *
18
+ * ```tsx
19
+ * import { useTheme } from 'weifuwu/react'
20
+ *
21
+ * function ThemeToggle() {
22
+ * const { theme, resolvedTheme, setTheme } = useTheme()
23
+ * return (
24
+ * <button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
25
+ * Current: {resolvedTheme}
26
+ * </button>
27
+ * )
28
+ * }
29
+ * ```
30
+ */
2
31
  export declare function useTheme(): {
3
32
  theme: string;
4
33
  resolvedTheme: string;
@@ -1,6 +1,20 @@
1
1
  import type { Middleware } from './types.ts';
2
+ /** Options for {@link compress}. */
2
3
  export interface CompressOptions {
4
+ /** Compression level (1-9, default: 6). */
3
5
  level?: number;
6
+ /** Minimum response body size in bytes to compress (default: 1024). */
4
7
  threshold?: number;
5
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
+ */
6
20
  export declare function compress(options?: CompressOptions): Middleware;
package/dist/cors.d.ts CHANGED
@@ -1,10 +1,25 @@
1
1
  import type { Middleware } from './types.ts';
2
+ /** Options for {@link cors}. */
2
3
  export interface CORSOptions {
4
+ /** Allowed origin(s). Default `'*'`. If `credentials: true`, reflects the request origin. */
3
5
  origin?: string | string[] | ((origin: string) => string | boolean | undefined);
6
+ /** Allowed HTTP methods. Default: `GET, HEAD, PUT, PATCH, POST, DELETE`. */
4
7
  methods?: string[];
8
+ /** Allowed request headers. Default: `Content-Type, Authorization`. */
5
9
  allowedHeaders?: string[];
10
+ /** Exposed response headers. */
6
11
  exposedHeaders?: string[];
12
+ /** Whether to expose `Access-Control-Allow-Credentials`. */
7
13
  credentials?: boolean;
14
+ /** `Access-Control-Max-Age` in seconds. */
8
15
  maxAge?: number;
9
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
+ */
10
25
  export declare function cors(options?: CORSOptions): Middleware;
@@ -1,8 +1,73 @@
1
1
  /**
2
2
  * Shared cron expression parsing utilities.
3
3
  * Used by both queue (Redis-backed) and in-memory scheduler.
4
+ *
5
+ * All functions operate in **local timezone**.
6
+ *
7
+ * ```ts
8
+ * import { cronNext } from 'weifuwu'
9
+ *
10
+ * // Get next weekday at 09:00
11
+ * const next = cronNext('0 9 * * 1-5')
12
+ * console.log(new Date(next))
13
+ * ```
14
+ */
15
+ /**
16
+ * Parse a single cron field (e.g. `"* /5"`, `"1-10"`, `"1,3,5"`) into a set
17
+ * of matching integer values within `[min, max]`.
18
+ *
19
+ * @param field - The cron field expression.
20
+ * @param min - Minimum valid value (inclusive).
21
+ * @param max - Maximum valid value (inclusive).
22
+ * @returns Set of matching integer values.
23
+ * @throws If the field contains an invalid expression (step of 0, NaN, etc.).
24
+ *
25
+ * ```ts
26
+ * parseField('1-5', 1, 31) // Set { 1, 2, 3, 4, 5 }
27
+ * ```
4
28
  */
5
29
  export declare function parseField(field: string, min: number, max: number): Set<number>;
30
+ /**
31
+ * Parse a full 5-field cron expression into an array of 5 Sets.
32
+ *
33
+ * @param pattern - Standard cron expression: `minute hour day month weekday`
34
+ * @returns Array of 5 Sets (minute, hour, day, month, weekday).
35
+ * @throws If the pattern does not contain exactly 5 fields.
36
+ *
37
+ * ```ts
38
+ * const fields = parsePattern('0 9 * * 1-5')
39
+ * fields[1] // hour: { 9 }
40
+ * fields[4] // weekday: { 1, 2, 3, 4, 5 }
41
+ * ```
42
+ */
6
43
  export declare function parsePattern(pattern: string): Set<number>[];
44
+ /**
45
+ * Check whether a given date matches a parsed cron pattern.
46
+ * Uses local timezone methods (getMinutes, getHours, etc.).
47
+ *
48
+ * @param fields - Parsed pattern from {@link parsePattern}.
49
+ * @param date - Date to check.
50
+ * @returns `true` if the date matches the pattern.
51
+ *
52
+ * ```ts
53
+ * const fields = parsePattern('0 9 * * 1-5') // weekdays at 09:00
54
+ * matches(fields, new Date('2026-06-15T09:00:00')) // true (Monday)
55
+ * ```
56
+ */
7
57
  export declare function matches(fields: Set<number>[], date: Date): boolean;
58
+ /**
59
+ * Calculate the next future timestamp (ms since epoch) matching a cron expression.
60
+ * Scans forward minute by minute, up to 1 year ahead.
61
+ * Uses local timezone.
62
+ *
63
+ * @param expr - Standard 5-field cron expression.
64
+ * @param from - Starting point (default: now). The result is always > `from`.
65
+ * @returns Unix timestamp (ms) of the next matching time.
66
+ * @throws If no matching time is found within 1 year.
67
+ *
68
+ * ```ts
69
+ * const next = cronNext('30 14 * * *') // next 14:30
70
+ * console.log(new Date(next).toISOString())
71
+ * ```
72
+ */
8
73
  export declare function cronNext(expr: string, from?: Date): number;
package/dist/csrf.d.ts CHANGED
@@ -1,10 +1,39 @@
1
1
  import type { Context, Middleware } from './types.ts';
2
+ /** Options for {@link csrf}. */
2
3
  export interface CsrfOptions {
4
+ /** Cookie name for CSRF token (default: `'_csrf'`). */
3
5
  cookie?: string;
6
+ /** Request header name for CSRF token (default: `'x-csrf-token'`). */
4
7
  header?: string;
8
+ /** Form body key for CSRF token (default: `'_csrf'`). */
5
9
  key?: string;
10
+ /** HTTP methods to exclude from CSRF protection (default: `['GET', 'HEAD', 'OPTIONS']`). */
6
11
  excludeMethods?: string[];
7
12
  }
13
+ /**
14
+ * CSRF protection middleware.
15
+ *
16
+ * On excluded methods (GET, HEAD, OPTIONS), generates a token and stores it
17
+ * in a cookie. On other methods, validates the token from header or body
18
+ * against the cookie.
19
+ *
20
+ * Injects `ctx.csrfToken` for use in forms.
21
+ *
22
+ * ```ts
23
+ * import { csrf } from 'weifuwu'
24
+ * app.use(csrf())
25
+ *
26
+ * // In a form:
27
+ * app.get('/form', (req, ctx) => {
28
+ * return new Response(`
29
+ * <form method="POST">
30
+ * <input type="hidden" name="_csrf" value="${ctx.csrfToken}" />
31
+ * <input type="submit" />
32
+ * </form>
33
+ * `, { headers: { 'content-type': 'text/html' } })
34
+ * })
35
+ * ```
36
+ */
8
37
  export declare function csrf(options?: CsrfOptions): Middleware<Context, Context & {
9
38
  csrfToken: string;
10
39
  }>;
package/dist/env.d.ts CHANGED
@@ -1,8 +1,28 @@
1
- /** Whether NODE_ENV is explicitly set to 'development'.
2
- * Used for dev-only features: HMR, livereload, no minification, createRoot (not hydrate).
3
- * NOT the opposite of isProd() — when NODE_ENV is unset, both return false. */
1
+ /**
2
+ * Whether `NODE_ENV` is explicitly set to `'development'`.
3
+ *
4
+ * Used for dev-only features: HMR, livereload, React `createRoot` (not hydrate).
5
+ * **Not** the opposite of {@link isProd} — when `NODE_ENV` is unset, both return `false`.
6
+ */
4
7
  export declare function isDev(): boolean;
5
- /** Whether NODE_ENV is explicitly set to 'production'.
6
- * Used for production-only behavior: plain-text 404, suppressed warnings, minification. */
8
+ /**
9
+ * Whether `NODE_ENV` is explicitly set to `'production'`.
10
+ *
11
+ * Used for production-only behavior: plain-text 404, suppressed warnings, minified output.
12
+ */
7
13
  export declare function isProd(): boolean;
14
+ /**
15
+ * Load environment variables from a `.env` file into `process.env`.
16
+ *
17
+ * Does **not** override existing `process.env` values.
18
+ * Supports quoted values and inline comments.
19
+ *
20
+ * @param path - Path to `.env` file (default: `'.env'` relative to cwd).
21
+ *
22
+ * ```ts
23
+ * import { loadEnv } from 'weifuwu'
24
+ * loadEnv()
25
+ * console.log(process.env.PORT)
26
+ * ```
27
+ */
8
28
  export declare function loadEnv(path?: string): void;
package/dist/flash.d.ts CHANGED
@@ -1,17 +1,19 @@
1
- import type { Middleware } from './types.ts';
2
- export interface FlashOptions {
3
- /** Cookie name (default: 'flash'). */
4
- name?: string;
5
- }
6
1
  /**
7
2
  * Flash message middleware.
8
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
9
  * ```ts
10
+ * import { flash } from 'weifuwu'
11
+ *
10
12
  * app.use(flash())
11
13
  *
12
14
  * // Read flash
13
15
  * app.get('/', (req, ctx) => {
14
- * const msg = ctx.flash.value // { type: 'success', text: 'Saved!' }
16
+ * const msg = ctx.flash.value // e.g. { type: 'success', text: 'Saved!' }
15
17
  * })
16
18
  *
17
19
  * // Set flash + redirect
@@ -21,4 +23,61 @@ export interface FlashOptions {
21
23
  * })
22
24
  * ```
23
25
  */
24
- export declare function flash(options?: FlashOptions): Middleware;
26
+ import type { Context, Middleware } from './types.ts';
27
+ /** Options for {@link flash}. */
28
+ export interface FlashOptions {
29
+ /**
30
+ * Cookie name to store the flash message.
31
+ * @default 'flash'
32
+ */
33
+ name?: string;
34
+ }
35
+ /**
36
+ * Flash message object injected into `ctx.flash`.
37
+ *
38
+ * Access the current flash with `.value`, or set a new flash with `.set()`.
39
+ * The `.value` is automatically cleared after being read.
40
+ */
41
+ export interface FlashInjected {
42
+ /**
43
+ * The flash value read from the incoming cookie.
44
+ * `undefined` if no flash cookie is present.
45
+ * Automatically cleared after the response is sent.
46
+ */
47
+ value: unknown;
48
+ /**
49
+ * Set a flash message and return a 302 redirect response.
50
+ *
51
+ * @param data - Any JSON-serializable value to store as the flash message.
52
+ * @param location - Redirect location (defaults to the `Referer` header).
53
+ * @returns A 302 Response with a `Set-Cookie` header.
54
+ *
55
+ * ```ts
56
+ * return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
57
+ * ```
58
+ */
59
+ set: (data: unknown, location?: string) => Response;
60
+ }
61
+ /**
62
+ * Flash message middleware — injects `ctx.flash`.
63
+ *
64
+ * @param options - Cookie name configuration.
65
+ * @returns Middleware that injects `ctx.flash` (`FlashInjected`).
66
+ *
67
+ * ```ts
68
+ * app.use(flash())
69
+ *
70
+ * // Read
71
+ * app.get('/', (req, ctx) => {
72
+ * const msg = ctx.flash.value
73
+ * })
74
+ *
75
+ * // Write + redirect
76
+ * app.post('/save', async (req, ctx) => {
77
+ * return ctx.flash.set({ type: 'success', text: 'Saved!' }, '/articles')
78
+ * })
79
+ * ```
80
+ */
81
+ export declare function flash(options?: FlashOptions): Middleware<Context, Context & {
82
+ flash: FlashInjected;
83
+ }>;
package/dist/health.d.ts CHANGED
@@ -1,6 +1,24 @@
1
1
  import { Router } from './router.ts';
2
+ /** Options for {@link health}. */
2
3
  export interface HealthOptions {
4
+ /** Health check endpoint path (default: `'/__health'`). */
3
5
  path?: string;
6
+ /** Async function that throws if the service is unhealthy. Called on each request. */
4
7
  check?: () => Promise<void>;
5
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
+ */
6
24
  export declare function health(options?: HealthOptions): Router;
package/dist/helmet.d.ts CHANGED
@@ -1,18 +1,33 @@
1
1
  import type { Middleware } from './types.ts';
2
+ /** Options for {@link helmet}. Set any header to `false` to omit it. */
2
3
  export interface HelmetOptions {
4
+ /** `Content-Security-Policy` header value. */
3
5
  contentSecurityPolicy?: string | false;
6
+ /** `Cross-Origin-Embedder-Policy` header value. */
4
7
  crossOriginEmbedderPolicy?: string | false;
8
+ /** `Cross-Origin-Opener-Policy` header value. */
5
9
  crossOriginOpenerPolicy?: string | false;
10
+ /** `Cross-Origin-Resource-Policy` header value. */
6
11
  crossOriginResourcePolicy?: string | false;
12
+ /** `Origin-Agent-Cluster` header value. */
7
13
  originAgentCluster?: string | false;
14
+ /** `Referrer-Policy` header value. */
8
15
  referrerPolicy?: string | false;
16
+ /** `Strict-Transport-Security` header value. */
9
17
  strictTransportSecurity?: string | false;
18
+ /** `X-Content-Type-Options` header value. */
10
19
  xContentTypeOptions?: string | false;
20
+ /** `X-DNS-Prefetch-Control` header value. */
11
21
  xDnsPrefetchControl?: string | false;
22
+ /** `X-Download-Options` header value. */
12
23
  xDownloadOptions?: string | false;
24
+ /** `X-Frame-Options` header value. */
13
25
  xFrameOptions?: string | false;
26
+ /** `X-Permitted-Cross-Domain-Policies` header value. */
14
27
  xPermittedCrossDomainPolicies?: string | false;
28
+ /** `X-XSS-Protection` header value. */
15
29
  xXssProtection?: string | false;
30
+ /** `Permissions-Policy` header value. */
16
31
  permissionsPolicy?: string | false;
17
32
  }
18
33
  export declare function helmet(options?: HelmetOptions): Middleware;
package/dist/hub.d.ts CHANGED
@@ -1,12 +1,36 @@
1
- import type { Redis } from './vendor.ts';
1
+ import type { Redis, WebSocket } from './vendor.ts';
2
+ /** Options for {@link createHub}. */
2
3
  export interface HubOptions {
4
+ /** Optional Redis client for cross-process pub/sub broadcast. */
3
5
  redis?: Redis;
6
+ /** Key prefix for Redis channels (default: `'hub:'`). */
4
7
  prefix?: string;
5
8
  }
9
+ /**
10
+ * In-memory (and optionally Redis-backed) pub/sub hub for WebSocket rooms.
11
+ *
12
+ * Used internally by the WebSocket handler to implement `ctx.ws.join()` / `ctx.ws.sendRoom()`. */
6
13
  export interface Hub {
14
+ /** Subscribe a WebSocket to a room/group. */
7
15
  join(key: string, ws: WebSocket): void;
16
+ /** Unsubscribe a WebSocket from all rooms. */
8
17
  leave(ws: WebSocket): void;
18
+ /** Send a JSON message to all members of a room. */
9
19
  broadcast(key: string, data: unknown): void;
20
+ /** Close the hub, disconnect Redis subscribers, clear all rooms. */
10
21
  close(): Promise<void>;
11
22
  }
23
+ /**
24
+ * Create a pub/sub hub for WebSocket room management.
25
+ *
26
+ * In-memory by default. Pass `redis` to enable cross-process broadcasting.
27
+ *
28
+ * ```ts
29
+ * import { createHub } from 'weifuwu'
30
+ *
31
+ * const hub = createHub()
32
+ * hub.join('room:general', ws)
33
+ * hub.broadcast('room:general', { type: 'chat', text: 'Hello' })
34
+ * ```
35
+ */
12
36
  export declare function createHub(opts?: HubOptions): Hub;