weifuwu 0.32.0 → 0.33.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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * KB — RAG knowledge base module.
3
+ *
4
+ * Splits documents into chunks, embeds via DashScope text-embedding-v4,
5
+ * stores vectors in PostgreSQL with pgvector, and provides similarity search.
6
+ *
7
+ * Requires `postgres()` middleware registered first.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * import { serve, Router, postgres, kb } from 'weifuwu'
12
+ *
13
+ * const app = new Router()
14
+ * app.use(postgres())
15
+ * app.use(kb())
16
+ *
17
+ * // Import
18
+ * app.post('/api/kb/import', async (req, ctx) => {
19
+ * const { title, content } = await req.json()
20
+ * const result = await ctx.kb.importText(title, content)
21
+ * return Response.json(result, { status: 201 })
22
+ * })
23
+ *
24
+ * // Search (RAG)
25
+ * app.post('/api/kb/search', async (req, ctx) => {
26
+ * const { query } = await req.json()
27
+ * const results = await ctx.kb.search(query, { limit: 5 })
28
+ * return Response.json(results)
29
+ * })
30
+ *
31
+ * // Use with agent
32
+ * app.use(agent({
33
+ * model: openai('deepseek-v4-flash', { baseURL: 'https://api.deepseek.com/v1' }),
34
+ * knowledge: {
35
+ * search: async (query, ctx) => ctx.kb.search(query),
36
+ * },
37
+ * }))
38
+ * ```
39
+ */
40
+ export { KB } from './module.ts';
41
+ export type { KBAPI, KBOptions, Document, Chunk, SearchResult, ImportOptions, SearchOptions, } from './types.ts';
42
+ import type { Context, Middleware } from '../types.ts';
43
+ import type { KBAPI, KBOptions } from './types.ts';
44
+ /**
45
+ * KB factory — creates the KB instance and returns a middleware
46
+ * that injects `ctx.kb`.
47
+ *
48
+ * Must be used **after** `postgres()`.
49
+ *
50
+ * ```ts
51
+ * app.use(postgres())
52
+ * app.use(kb())
53
+ * ```
54
+ */
55
+ export declare function kb(opts?: KBOptions): Middleware<Context, Context & {
56
+ kb: KBAPI;
57
+ }>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * KB — RAG knowledge base module.
3
+ *
4
+ * Splits documents into chunks, embeds them via DashScope text-embedding-v4,
5
+ * stores vectors in PostgreSQL with pgvector, and provides similarity search.
6
+ *
7
+ * Depends on `postgres()` middleware registered first.
8
+ *
9
+ * ```ts
10
+ * import { serve, Router, postgres, kb } from 'weifuwu'
11
+ *
12
+ * const app = new Router()
13
+ * app.use(postgres())
14
+ * app.use(kb())
15
+ *
16
+ * // Import
17
+ * await ctx.kb.importText('Getting Started', 'First, install the package...')
18
+ *
19
+ * // Search
20
+ * const results = await ctx.kb.search('how to install?', { limit: 5 })
21
+ * ```
22
+ */
23
+ import type { Context, Handler, SqlClient } from '../types.ts';
24
+ import type { KBAPI, KBOptions } from './types.ts';
25
+ export declare class KB {
26
+ private migrated;
27
+ private embedFn;
28
+ private dimensions;
29
+ private chunkSize;
30
+ private chunkOverlap;
31
+ constructor(opts?: KBOptions);
32
+ private ms;
33
+ migrate(sql: SqlClient): Promise<void>;
34
+ private ensureMigrated;
35
+ bind(ctx: Context): KBAPI;
36
+ middleware(req: Request, ctx: Context, next: Handler): Promise<Response>;
37
+ }
@@ -0,0 +1,83 @@
1
+ declare module '../types.ts' {
2
+ interface Context {
3
+ /** KB — RAG knowledge base module. */
4
+ kb: import('./types.ts').KBAPI;
5
+ }
6
+ }
7
+ export interface Document {
8
+ id: string;
9
+ title: string;
10
+ source: string | null;
11
+ metadata: Record<string, unknown>;
12
+ chunk_count: number;
13
+ created_at: Date;
14
+ updated_at: Date;
15
+ }
16
+ export interface Chunk {
17
+ id: string;
18
+ document_id: string | null;
19
+ content: string;
20
+ chunk_index: number;
21
+ tokens: number;
22
+ created_at: Date;
23
+ }
24
+ export interface SearchResult {
25
+ chunk_id: string;
26
+ document_id: string | null;
27
+ content: string;
28
+ score: number;
29
+ source?: string;
30
+ title?: string;
31
+ }
32
+ export interface ImportOptions {
33
+ /** Source identifier (e.g. URL, filename). */
34
+ source?: string;
35
+ /** Arbitrary metadata stored in the document. */
36
+ metadata?: Record<string, unknown>;
37
+ /** Chunk size in tokens (default: 512). */
38
+ chunkSize?: number;
39
+ /** Chunk overlap in tokens (default: 64). */
40
+ chunkOverlap?: number;
41
+ }
42
+ export interface SearchOptions {
43
+ /** Number of results (default: 5, max: 50). */
44
+ limit?: number;
45
+ /** Minimum similarity score filter (0-1). */
46
+ minScore?: number;
47
+ /** Optional metadata filter. */
48
+ filter?: Record<string, unknown>;
49
+ }
50
+ export interface KBOptions {
51
+ /** Embedding function. Defaults to DashScope text-embedding-v4. */
52
+ embed?: (text: string) => Promise<number[]>;
53
+ /** Embedding dimensions (default: 1536 for text-embedding-v4). */
54
+ dimensions?: number;
55
+ /** Default chunk size in tokens (default: 512). */
56
+ chunkSize?: number;
57
+ /** Chunk overlap in tokens (default: 64). */
58
+ chunkOverlap?: number;
59
+ /** User table name for reference (default: 'users'). */
60
+ usersTable?: string;
61
+ }
62
+ export interface KBAPI {
63
+ /** Import text: split into chunks, embed, and store. Returns document + chunks. */
64
+ importText(title: string, text: string, opts?: ImportOptions): Promise<{
65
+ document: Document;
66
+ chunks: Chunk[];
67
+ }>;
68
+ /** Import multiple documents at once. */
69
+ importDocuments(docs: Array<{
70
+ title: string;
71
+ content: string;
72
+ } & ImportOptions>): Promise<Document[]>;
73
+ /** RAG search: embed query → vector similarity search → return chunks. */
74
+ search(query: string, opts?: SearchOptions): Promise<SearchResult[]>;
75
+ /** List all documents. */
76
+ list(): Promise<Document[]>;
77
+ /** Get a document by id. */
78
+ get(id: string): Promise<Document | null>;
79
+ /** Get chunks for a document. */
80
+ getChunks(documentId: string): Promise<Chunk[]>;
81
+ /** Delete a document and its chunks. */
82
+ delete(id: string): Promise<boolean>;
83
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Messager — instant messaging module for weifuwu.
3
+ *
4
+ * Requires `postgres()` and `user()` middleware registered first.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { serve, Router, postgres, user, messager } from 'weifuwu'
9
+ *
10
+ * const app = new Router()
11
+ * app.use(postgres())
12
+ * app.use(user())
13
+ * app.use(messager())
14
+ *
15
+ * // WebSocket — auto-join all conversations on connect
16
+ * app.ws('/ws', {
17
+ * async open(ws, ctx) {
18
+ * for (const c of await ctx.messager.getConversations()) {
19
+ * ctx.ws.join(`conversation:${c.id}`)
20
+ * }
21
+ * },
22
+ * })
23
+ *
24
+ * // REST API
25
+ * app.post('/api/conversations', async (req, ctx) => {
26
+ * const { type, title, userIds } = await req.json()
27
+ * return type === 'direct'
28
+ * ? Response.json(await ctx.messager.createDirectConversation(userIds[0]))
29
+ * : Response.json(await ctx.messager.createGroupConversation(title, userIds))
30
+ * })
31
+ *
32
+ * app.get('/api/conversations', async (req, ctx) => {
33
+ * return Response.json(await ctx.messager.getConversations())
34
+ * })
35
+ *
36
+ * app.post('/api/conversations/:id/messages', async (req, ctx) => {
37
+ * const { body } = await req.json()
38
+ * const msg = await ctx.messager.sendMessage(ctx.params.id, body)
39
+ * return Response.json(msg, { status: 201 })
40
+ * })
41
+ *
42
+ * app.get('/api/conversations/:id/messages', async (req, ctx) => {
43
+ * const url = new URL(req.url)
44
+ * const opts = {
45
+ * before: url.searchParams.get('before') || undefined,
46
+ * limit: parseInt(url.searchParams.get('limit') || '50'),
47
+ * }
48
+ * return Response.json(await ctx.messager.getMessages(ctx.params.id, opts))
49
+ * })
50
+ *
51
+ * serve(app)
52
+ * ```
53
+ */
54
+ export { Messager } from './module.ts';
55
+ export type { MessagerAPI, MessagerOptions, Conversation, ConversationType, Participant, ParticipantUser, Message, MessagePreview, CreateGroupInput, GetMessagesOptions, } from './types.ts';
56
+ import type { Context, Middleware } from '../types.ts';
57
+ import type { MessagerAPI, MessagerOptions } from './types.ts';
58
+ /**
59
+ * Messager factory — creates the Messager instance and returns
60
+ * a middleware that injects `ctx.messager`.
61
+ *
62
+ * Must be used **after** `postgres()` and `user()`.
63
+ *
64
+ * ```ts
65
+ * app.use(postgres())
66
+ * app.use(user())
67
+ * app.use(messager())
68
+ * ```
69
+ */
70
+ export declare function messager(opts?: MessagerOptions): Middleware<Context, Context & {
71
+ messager: MessagerAPI;
72
+ }>;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Messager — instant messaging module for weifuwu.
3
+ *
4
+ * Depends on `postgres()` and `user()` middleware registered first.
5
+ *
6
+ * ```ts
7
+ * import { serve, Router, postgres, user, messager } from 'weifuwu'
8
+ *
9
+ * const app = new Router()
10
+ * app.use(postgres())
11
+ * app.use(user())
12
+ * app.use(messager())
13
+ *
14
+ * app.ws('/ws', {
15
+ * async open(ws, ctx) {
16
+ * for (const c of await ctx.messager.getConversations()) {
17
+ * ctx.ws.join(`conversation:${c.id}`)
18
+ * }
19
+ * },
20
+ * })
21
+ *
22
+ * app.post('/api/messages', async (req, ctx) => {
23
+ * const { conversationId, body } = await req.json()
24
+ * const msg = await ctx.messager.sendMessage(conversationId, body)
25
+ * return Response.json(msg, { status: 201 })
26
+ * })
27
+ * ```
28
+ */
29
+ import type { Context, Handler, SqlClient } from '../types.ts';
30
+ import type { MessagerAPI, MessagerOptions } from './types.ts';
31
+ export declare class Messager {
32
+ private migrated;
33
+ private prefix;
34
+ private usersTable;
35
+ constructor(opts?: MessagerOptions);
36
+ private get tConversations();
37
+ private get tParticipants();
38
+ private get tMessages();
39
+ private get tUsers();
40
+ private q;
41
+ migrate(sql: SqlClient): Promise<void>;
42
+ private ensureMigrated;
43
+ bind(ctx: Context): MessagerAPI;
44
+ private _getConversationById;
45
+ middleware(req: Request, ctx: Context, next: Handler): Promise<Response>;
46
+ }
@@ -0,0 +1,99 @@
1
+ declare module '../types.ts' {
2
+ interface Context {
3
+ /** Messager instance for conversations and messages. */
4
+ messager: import('./types.ts').MessagerAPI;
5
+ }
6
+ }
7
+ export type ConversationType = 'direct' | 'group';
8
+ export interface Conversation {
9
+ id: string;
10
+ type: ConversationType;
11
+ title: string | null;
12
+ created_by: string;
13
+ created_at: Date;
14
+ updated_at: Date;
15
+ /** Number of participants (convenience field) */
16
+ participant_count?: number;
17
+ /** Last message preview (for list display) */
18
+ last_message?: MessagePreview | null;
19
+ /** Current user's unread count in this conversation */
20
+ unread_count?: number;
21
+ }
22
+ export interface Participant {
23
+ conversation_id: string;
24
+ user_id: string;
25
+ role: 'member' | 'admin';
26
+ last_read_at: Date | null;
27
+ joined_at: Date;
28
+ is_active: boolean;
29
+ }
30
+ /** A user in a conversation (includes user_name for display). */
31
+ export interface ParticipantUser extends Participant {
32
+ user_name: string;
33
+ user_email: string;
34
+ user_avatar?: string;
35
+ }
36
+ export interface Message {
37
+ id: string;
38
+ conversation_id: string;
39
+ sender_id: string;
40
+ sender_name?: string;
41
+ body: string;
42
+ created_at: Date;
43
+ updated_at: Date;
44
+ edited: boolean;
45
+ deleted_at: Date | null;
46
+ }
47
+ /** Lightweight message used for conversation last_message preview. */
48
+ export interface MessagePreview {
49
+ id: string;
50
+ body: string;
51
+ sender_id: string;
52
+ sender_name: string;
53
+ created_at: Date;
54
+ }
55
+ export interface CreateGroupInput {
56
+ title: string;
57
+ userIds: string[];
58
+ }
59
+ export interface GetMessagesOptions {
60
+ /** Return messages created before this ID (cursor pagination). */
61
+ before?: string;
62
+ /** Number of messages (default: 50, max: 200). */
63
+ limit?: number;
64
+ }
65
+ export interface MessagerOptions {
66
+ /** PostgreSQL table prefix for messager tables (default: '' — 'conversations', 'participants', 'messages'). */
67
+ tablePrefix?: string;
68
+ /** User table name for sender name lookups (default: 'users'). */
69
+ usersTable?: string;
70
+ }
71
+ export interface MessagerAPI {
72
+ /** Find or create a direct conversation with another user. */
73
+ createDirectConversation(otherUserId: string): Promise<Conversation>;
74
+ /** Create a group conversation. */
75
+ createGroupConversation(title: string, userIds: string[]): Promise<Conversation>;
76
+ /** Get conversation by ID. Returns null if not found or caller is not a participant. */
77
+ getConversation(conversationId: string): Promise<Conversation | null>;
78
+ /** List current user's active conversations, most recent first. */
79
+ getConversations(): Promise<Conversation[]>;
80
+ /** Add participants to a group conversation. Only group admin can do this. */
81
+ addParticipants(conversationId: string, userIds: string[]): Promise<void>;
82
+ /** Remove yourself or (as admin) another participant from a group conversation. */
83
+ removeParticipant(conversationId: string, userId?: string): Promise<boolean>;
84
+ /** Send a message to a conversation. Returns the created message. */
85
+ sendMessage(conversationId: string, body: string): Promise<Message>;
86
+ /** Get messages in a conversation, newest first (cursor pagination). */
87
+ getMessages(conversationId: string, opts?: GetMessagesOptions): Promise<Message[]>;
88
+ /** Edit a message (only by the original sender, within 24h). */
89
+ editMessage(messageId: string, body: string): Promise<Message | null>;
90
+ /** Soft-delete a message (only by the original sender). */
91
+ deleteMessage(messageId: string): Promise<boolean>;
92
+ /** Mark all messages in a conversation as read. */
93
+ markRead(conversationId: string): Promise<void>;
94
+ /** Get unread message count across all conversations. */
95
+ getUnreadCount(): Promise<{
96
+ total: number;
97
+ byConversation: Record<string, number>;
98
+ }>;
99
+ }
package/dist/types.d.ts CHANGED
@@ -19,7 +19,7 @@ export interface WebSocket {
19
19
  removeEventListener(event: string, handler: (...args: unknown[]) => void): void;
20
20
  }
21
21
  export type { Redis, RedisOptions } from 'ioredis';
22
- /** User injected by auth() middleware. */
22
+ /** User injected by user() or custom auth middleware. */
23
23
  export interface User {
24
24
  id: string;
25
25
  role?: string;
@@ -42,7 +42,7 @@ export interface Context {
42
42
  params: Record<string, string>;
43
43
  query: Record<string, string>;
44
44
  mountPath?: string;
45
- /** Currently authenticated user (set by user/auth middleware). */
45
+ /** Currently authenticated user (set by user() or custom auth middleware). */
46
46
  user?: unknown;
47
47
  /** Server-side data loaded for the current page (React SSR). */
48
48
  loaderData?: Record<string, unknown>;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * User module — complete authentication & user management system.
3
+ *
4
+ * Requires `postgres()` middleware registered first (provides `ctx.sql`).
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { serve, Router, postgres, user, requireRole } from 'weifuwu'
9
+ *
10
+ * const app = new Router()
11
+ * app.use(postgres())
12
+ * app.use(user({ secret: process.env.JWT_SECRET }))
13
+ *
14
+ * // Public routes
15
+ * app.post('/api/register', async (req, ctx) => {
16
+ * const result = await ctx.userModule.register(await req.json())
17
+ * return Response.json(result)
18
+ * })
19
+ *
20
+ * app.post('/api/login', async (req, ctx) => {
21
+ * const { email, password } = await req.json()
22
+ * const result = await ctx.userModule.login(email, password)
23
+ * if (!result) return new Response('Unauthorized', { status: 401 })
24
+ * return Response.json(result)
25
+ * })
26
+ *
27
+ * // Protected — any authenticated user
28
+ * app.get('/api/me', async (req, ctx) => {
29
+ * if (!ctx.user) return new Response('Unauthorized', { status: 401 })
30
+ * return Response.json(ctx.user)
31
+ * })
32
+ *
33
+ * // Protected — admin only
34
+ * app.get('/api/admin/users', requireRole('admin'), async (req, ctx) => {
35
+ * const users = await ctx.userModule.listUsers()
36
+ * return Response.json(users)
37
+ * })
38
+ *
39
+ * serve(app)
40
+ * ```
41
+ */
42
+ export { UserModule } from './module.ts';
43
+ export { requireRole } from './types.ts';
44
+ export type { UserModuleAPI, UserModuleOptions, UserRecord, CreateUserInput, UpdateUserInput, TokenPayload, } from './types.ts';
45
+ import type { Context, Middleware } from '../types.ts';
46
+ import type { UserModuleAPI, UserModuleOptions } from './types.ts';
47
+ /**
48
+ * User system factory — creates the UserModule instance and returns
49
+ * a middleware that injects `ctx.userModule` (per-request bound API)
50
+ * and resolves `ctx.user` from JWT tokens.
51
+ *
52
+ * Must be used **after** `postgres()` so that `ctx.sql` is available.
53
+ *
54
+ * ```ts
55
+ * app.use(postgres())
56
+ * app.use(user({ secret: process.env.JWT_SECRET }))
57
+ * ```
58
+ */
59
+ export declare function user(opts?: UserModuleOptions): Middleware<Context, Context & {
60
+ userModule: UserModuleAPI;
61
+ }>;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * User module — authentication, registration, and user CRUD.
3
+ *
4
+ * Depends on `postgres()` middleware registered first (provides `ctx.sql`).
5
+ *
6
+ * ```ts
7
+ * import { serve, Router, postgres, user } from 'weifuwu'
8
+ *
9
+ * const app = new Router()
10
+ * app.use(postgres())
11
+ * app.use(user({ secret: process.env.JWT_SECRET }))
12
+ *
13
+ * app.post('/api/register', async (req, ctx) => {
14
+ * const result = await ctx.userModule.register(await req.json())
15
+ * return Response.json(result)
16
+ * })
17
+ *
18
+ * app.post('/api/login', async (req, ctx) => {
19
+ * const { email, password } = await req.json()
20
+ * const result = await ctx.userModule.login(email, password)
21
+ * if (!result) return new Response('Unauthorized', { status: 401 })
22
+ * return Response.json(result)
23
+ * })
24
+ *
25
+ * app.get('/api/me', async (req, ctx) => {
26
+ * if (!ctx.user) return new Response('Unauthorized', { status: 401 })
27
+ * return Response.json(ctx.user)
28
+ * })
29
+ * ```
30
+ */
31
+ import type { Context, Handler, SqlClient } from '../types.ts';
32
+ import type { UserModuleAPI, UserModuleOptions } from './types.ts';
33
+ export declare class UserModule {
34
+ readonly table: string;
35
+ readonly secret: string;
36
+ readonly tokenExpiry: number;
37
+ private migrated;
38
+ constructor(opts?: UserModuleOptions);
39
+ migrate(sql: SqlClient): Promise<void>;
40
+ /**
41
+ * Create a per-request `UserModuleAPI` bound to the request's SQL context.
42
+ * Called internally by the middleware. Thread-safe because each request
43
+ * gets its own closure with a captured `ctx`.
44
+ */
45
+ bind(ctx: Context): UserModuleAPI;
46
+ private ensureMigrated;
47
+ private _register;
48
+ private _createUser;
49
+ private _getUserById;
50
+ private _getUserByEmail;
51
+ private _updateUser;
52
+ private _deleteUser;
53
+ private _listUsers;
54
+ private _login;
55
+ private _changePassword;
56
+ private _generateToken;
57
+ private _verifyToken;
58
+ private _refreshToken;
59
+ /**
60
+ * Middleware entry point. Injects `ctx.userModule` (per-request bound API)
61
+ * and resolves `ctx.user` from the Authorization header or `token` cookie.
62
+ */
63
+ middleware(req: Request, ctx: Context, next: Handler): Promise<Response>;
64
+ }
@@ -0,0 +1,98 @@
1
+ import type { Middleware } from '../types.ts';
2
+ declare module '../types.ts' {
3
+ interface Context {
4
+ /** User module instance for downstream CRUD and auth operations. */
5
+ userModule: import('./types.ts').UserModuleAPI;
6
+ }
7
+ }
8
+ /** User record stored in the database. Password field is never exposed. */
9
+ export interface UserRecord {
10
+ id: string;
11
+ email: string;
12
+ name: string;
13
+ role: string;
14
+ avatar?: string;
15
+ is_active: boolean;
16
+ created_at: Date;
17
+ updated_at: Date;
18
+ last_login_at?: Date;
19
+ }
20
+ export interface CreateUserInput {
21
+ email: string;
22
+ name: string;
23
+ password: string;
24
+ role?: string;
25
+ avatar?: string;
26
+ is_active?: boolean;
27
+ }
28
+ export interface UpdateUserInput {
29
+ name?: string;
30
+ email?: string;
31
+ password?: string;
32
+ role?: string;
33
+ avatar?: string;
34
+ is_active?: boolean;
35
+ }
36
+ /** JWT-like HMAC token payload. */
37
+ export interface TokenPayload {
38
+ sub: string;
39
+ email: string;
40
+ role: string;
41
+ iat: number;
42
+ exp: number;
43
+ }
44
+ export interface UserModuleOptions {
45
+ /** HMAC secret for signing tokens (default: process.env.JWT_SECRET). */
46
+ secret?: string;
47
+ /** Token expiry in ms (default: 7 days). */
48
+ tokenExpiry?: number;
49
+ /** PostgreSQL table name (default: 'users'). */
50
+ table?: string;
51
+ }
52
+ /**
53
+ * Per-request user module API injected via `ctx.userModule`.
54
+ * Each method is already bound to the request's SQL context,
55
+ * so callers never need to pass `ctx`.
56
+ */
57
+ export interface UserModuleAPI {
58
+ /** Register a new user. Returns user + signed token. */
59
+ register(input: CreateUserInput): Promise<{
60
+ user: UserRecord;
61
+ token: string;
62
+ }>;
63
+ /** Create a user directly (no token). Throws if email exists. */
64
+ createUser(input: CreateUserInput): Promise<UserRecord>;
65
+ /** Authenticate with email + password. Returns user + token or null. */
66
+ login(email: string, password: string): Promise<{
67
+ user: UserRecord;
68
+ token: string;
69
+ } | null>;
70
+ /** Lookup user by primary key. */
71
+ getUserById(id: string): Promise<UserRecord | null>;
72
+ /** Lookup user by email. */
73
+ getUserByEmail(email: string): Promise<UserRecord | null>;
74
+ /** Update user fields. Returns updated record or null if not found. */
75
+ updateUser(id: string, input: Partial<UpdateUserInput>): Promise<UserRecord | null>;
76
+ /** Soft-delete (deactivate) a user. Returns false if not found. */
77
+ deleteUser(id: string): Promise<boolean>;
78
+ /** List users. Defaults to active only; pass true to include deactivated. */
79
+ listUsers(includeInactive?: boolean): Promise<UserRecord[]>;
80
+ /** Change password — verifies currentPassword before updating. */
81
+ changePassword(id: string, currentPassword: string, newPassword: string): Promise<boolean>;
82
+ /** Verify a plaintext password against a stored scrypt hash. */
83
+ verifyPassword(password: string, hash: string): Promise<boolean>;
84
+ /** Generate a signed HMAC token for a user. */
85
+ generateToken(user: UserRecord): Promise<string>;
86
+ /** Verify and decode a token. Returns payload or null if expired/invalid. */
87
+ verifyToken(token: string): Promise<TokenPayload | null>;
88
+ /** Re-issue a token with a fresh expiry. Returns null if token is invalid. */
89
+ refreshToken(token: string): Promise<string | null>;
90
+ }
91
+ /**
92
+ * Middleware factory: returns a middleware that checks `ctx.user.role`.
93
+ *
94
+ * ```ts
95
+ * app.get('/admin', requireRole('admin'), handler)
96
+ * ```
97
+ */
98
+ export declare function requireRole(...roles: string[]): Middleware;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "weifuwu",
3
3
  "type": "module",
4
- "version": "0.32.0",
4
+ "version": "0.33.0",
5
5
  "description": "Web-standard HTTP microframework for Node.js — (req, ctx) => Response",
6
6
  "exports": {
7
7
  ".": {