weifuwu 0.56.0 → 0.57.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/README.md CHANGED
@@ -230,6 +230,10 @@ createApp().use(router({ routes })).mount('#root', RouteView, { hydrate: true })
230
230
  | `weifuwu` | **redis** | Redis 客户端(自研 RESP2 协议)→ `ctx.redis` | Router, REDIS_URL |
231
231
  | `weifuwu` | **ui** | SSR 渲染 + esbuild JS/CSS 动态编译 → `ctx.ui` | Router |
232
232
  | `weifuwu` | **uiSsr** | 路由级 SSR:匹配 routes → 自动完整 HTML + `__DATA__` + bundle | Router, ui |
233
+ | `weifuwu` | **rateLimit** | 限流中间件(fixed/sliding,redis 多实例原子)→ `ctx.limit` | Router, redis |
234
+ | `weifuwu` | **email** | 邮件发送(Resend/SMTP 自研/自定义适配器)→ `ctx.email` | Router |
235
+ | `weifuwu` | **userSystem** | 用户系统(scrypt 密码哈希 + 混合会话)→ `ctx.user` / `ctx.auth` + `/api/auth/*` | Router, postgres |
236
+ | `weifuwu` | **queue** | 可靠任务队列(Redis Streams,at-least-once + DLQ)→ `ctx.queue` | Router, redis |
233
237
  | `weifuwu/dev` | **dev loader** | Node loader:服务端直接跑 `.ts/.tsx`(`--import weifuwu/dev`) | esbuild |
234
238
  | `weifuwu` | **graphql** | GraphQL 端点(支持 GraphiQL) | Router |
235
239
  | `weifuwu` | **createMiddleware** | 类型安全中间件工厂 | — |
@@ -679,7 +683,7 @@ app.use(db)
679
683
 
680
684
  ## redis — Redis 客户端(自研)
681
685
 
682
- > **自研 RESP2 协议**(零第三方依赖)——连接/重连(断线 pending 拒绝、指数退避)/离线队列/管道/Pub-Sub(订阅断线自动重放)+ 消除 ioredis 高频痛点(TTL 参数顺序、JSON 手动序列化、缓存样板)。
686
+ > **自研 RESP2 协议**(零第三方依赖)——连接/重连(断线 pending 拒绝、指数退避)/离线队列/管道/Pub-Sub(订阅断线自动重放)+ 消除 ioredis 高频痛点(TTL 参数顺序、JSON 手动序列化、缓存样板)。**二进制安全**:`getBuffer(key)` 原样返回字节(缓存序列化 payload 不损坏)。
683
687
 
684
688
  ```ts
685
689
  import { redis } from 'weifuwu'
@@ -1019,6 +1023,8 @@ app.get('/secure', () => {
1019
1023
 
1020
1024
  ## 响应辅助函数
1021
1025
 
1026
+ > 以下为完整 API 参考,按需查阅。四个 SaaS 地基模块(rateLimit / email / userSystem / queue)见文末「SaaS 地基模块」章节。
1027
+
1022
1028
  消除 `Response.json(...)` 重复模式:
1023
1029
 
1024
1030
  ```ts
@@ -2722,3 +2728,124 @@ node scripts/release.mjs <version> # 发布
2722
2728
  # 测试前启动依赖服务
2723
2729
  docker compose up -d
2724
2730
  ```
2731
+
2732
+ ---
2733
+
2734
+ # SaaS 地基模块(rateLimit / email / userSystem / queue)
2735
+
2736
+ 四个内建模块组成一个"基本 SaaS 底座":认证、异步任务、限流、邮件——零新增依赖
2737
+ (只依赖已自研的 redis / postgres 客户端与 node 标准库)。
2738
+
2739
+ ## rateLimit — 限流
2740
+
2741
+ ```ts
2742
+ import { rateLimit } from 'weifuwu'
2743
+
2744
+ app.use(redis()) // 依赖 ctx.redis
2745
+ app.use(rateLimit({ windowMs: 60_000, max: 100 })) // 全局限流(默认固定窗口)
2746
+
2747
+ app.get('/api/search', async (req, ctx) => {
2748
+ await ctx.limit('search', { max: 30, windowMs: 60_000 }) // 手动限流,超限抛 429
2749
+ })
2750
+
2751
+ // 登录防爆破(配合 userSystem):组合键 ip:email
2752
+ app.use(rateLimit({ key: (req) => `login:${req.ip}:${req.email}`, max: 5, windowMs: 15 * 60_000 }))
2753
+ ```
2754
+
2755
+ | 选项 | 默认 | 说明 |
2756
+ |------|------|------|
2757
+ | `windowMs` | `60000` | 时间窗口 |
2758
+ | `max` | `100` | 窗口内最大请求 |
2759
+ | `key` | X-Forwarded-For | 限流键(生产环境配置反向代理注入) |
2760
+ | `algorithm` | `fixed` | `fixed`(INCR+EXPIRE,原子)\| `sliding`(ZSET,仅 redis) |
2761
+ | `store` | `redis` | `redis`(多实例一致)\| `memory`(仅单实例/开发) |
2762
+
2763
+ - 响应自动带 `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` / `Retry-After`
2764
+ - 多实例共享计数:计数在 redis,水平扩展天然一致
2765
+
2766
+ ## email — 邮件发送
2767
+
2768
+ ```ts
2769
+ import { email } from 'weifuwu'
2770
+
2771
+ app.use(email({ from: 'no-reply@your.app', adapter: 'resend', resend: { apiKey: process.env.RESEND_API_KEY } }))
2772
+ // 或 adapter: 'smtp' + smtp: { host, port, user, pass }(自研 SMTP 客户端,零依赖)
2773
+
2774
+ app.post('/api/notify', async (req, ctx) => {
2775
+ await ctx.email.send({ to: 'user@x.com', subject: '通知', html: '<h1>hi</h1>' })
2776
+ })
2777
+ ```
2778
+
2779
+ - 适配器:`resend`(默认,一个 POST)/ `smtp`(自研 node:net + node:tls:EHLO/STARTTLS/AUTH PLAIN/DATA/dot-stuffing,非 ASCII subject 自动 RFC2047 编码)/ 自定义函数
2780
+ - 裁剪:附件、退信/送达率(服务商职责)、批量营销不支持
2781
+
2782
+ ## userSystem — 用户系统
2783
+
2784
+ ```ts
2785
+ import { userSystem } from 'weifuwu'
2786
+
2787
+ const db = postgres()
2788
+ await db.migrate()
2789
+ const users = userSystem({ sql: db.sql, secret: process.env.AUTH_SECRET })
2790
+ await users.migrate() // 幂等建表(users + sessions)
2791
+ app.use(db)
2792
+ app.use(users) // 注入 ctx.user / ctx.auth
2793
+ users.routes(app) // POST /api/auth/register|login|logout|refresh + GET /api/auth/me
2794
+
2795
+ app.get('/me', (req, ctx) => ok(ctx.user)) // 已注入
2796
+ app.post('/secure', (req, ctx) => { ctx.auth.requireAuth(); ... })
2797
+ ```
2798
+
2799
+ - **安全基线**:scrypt 密码哈希(per-user salt + timing-safe,异步不阻塞);access token = HMAC-SHA256 JWT(与 `weifuwu/client` 的 `auth()` 天然配对);refresh token = 不透明随机串,DB 只存哈希,logout/轮换即撤销
2800
+ - **防枚举**:登录失败统一 401(不泄露邮箱是否存在)
2801
+ - **`ctx.auth` 方法面**:`register` / `login` / `logout` / `requireAuth` / `setPassword(userId, newPwd)` / `createToken(type, payload, { ttlSeconds })`(邮箱验证/密码重置自接)
2802
+ - **裁剪**:OAuth、邮箱验证邮件(给底层 API 自接)、多因素、RBAC 权限引擎(只留 `role` 字段)、多租户语义(tenant-ready:`tenant` 字段 + token claim 已预留)
2803
+
2804
+ ## queue — 可靠任务队列
2805
+
2806
+ ```ts
2807
+ import { queue } from 'weifuwu'
2808
+
2809
+ const q = queue() // 默认 REDIS_URL
2810
+ app.use(q) // 注入 ctx.queue
2811
+
2812
+ app.post('/api/generate', async (req, ctx) => {
2813
+ await ctx.queue.add('llm.batch', { prompt: '...' }, { attempts: 3 })
2814
+ return new Response(null, { status: 202 }) // 立即 202,任务后台执行
2815
+ })
2816
+
2817
+ // 消费者(独立进程或同进程均可,多开安全)
2818
+ const worker = q.worker('llm.batch', async (job) => {
2819
+ await runLLM(job.data) // 失败自动重试 → 用尽进 DLQ(q:llm.batch:dead)
2820
+ }, { concurrency: 5, visibilityTimeout: 30_000 })
2821
+ await worker.start()
2822
+ await worker.stop() // 优雅停止
2823
+ ```
2824
+
2825
+ - **语义**:at-least-once(handler 可能重复执行——幂等由业务保证);Redis Streams 消费组,多 worker 实例不重复消费
2826
+ - **可靠性**:失败 → 延迟重试(间隔 = `visibilityTimeout`,ZSET 延迟队列)→ attempts 用尽 → DLQ;worker 崩溃 → pending 由其他实例 `XAUTOCLAIM` 接管
2827
+ - 裁剪:延迟调度(除重试外)、cron、优先级、指数退避、速率限制不支持
2828
+
2829
+ ## 组合示例:注册 → 验证邮件 → 欢迎任务 → 登录防爆破
2830
+
2831
+ ```ts
2832
+ app.use(redis())
2833
+ app.use(rateLimit({ key: (req) => `login:${req.ip}`, max: 5, windowMs: 60_000 })) // 防爆破
2834
+ app.use(email({ from: 'no-reply@x.com', adapter: 'resend', resend: { apiKey } }))
2835
+ app.use(db)
2836
+ app.use(users)
2837
+ users.routes(app)
2838
+
2839
+ // 注册:限流守卫 → 用户系统 → 验证邮件 → 欢迎任务入队
2840
+ app.post('/api/auth/register', async (req, ctx) => {
2841
+ await ctx.limit(`register:${req.ip}`, { max: 10, windowMs: 60_000 })
2842
+ const result = await ctx.auth.register(await req.json())
2843
+ const token = ctx.auth.createToken('verify', { sub: result.user.id }, { ttlSeconds: 86400 })
2844
+ await ctx.email.send({ to: result.user.email, subject: '验证邮箱', html: `...?token=${token}` })
2845
+ await ctx.queue.add('welcome.flow', { userId: result.user.id })
2846
+ return created(result)
2847
+ })
2848
+
2849
+ const worker = q.worker('welcome.flow', async (job) => { /* 欢迎流程 */ })
2850
+ await worker.start()
2851
+ ```
@@ -1,4 +1,4 @@
1
- import type { Context, Handler, Middleware, ErrorHandler, Closeable } from '../types.ts';
1
+ import { type Context, type Handler, type Middleware, type ErrorHandler, type Closeable } from '../types.ts';
2
2
  import { type WebSocketHandler, type WsUpgradeHandler } from './ws.ts';
3
3
  import type { GraphQLHandler } from '../graphql.ts';
4
4
  /**
@@ -39,9 +39,9 @@ export declare class PgConnection {
39
39
  private onReady;
40
40
  private onAuthFail;
41
41
  private expectingAuth;
42
- private authStage;
43
42
  private authCtx;
44
43
  private prepared;
44
+ private static readonly PREPARED_MAX;
45
45
  private stmtSeq;
46
46
  private currentQuery;
47
47
  private onData;
@@ -49,6 +49,10 @@ export declare class PgConnection {
49
49
  /** SCRAM client-final 消息 */
50
50
  private scramFinal;
51
51
  private scramServerSignature;
52
+ /** LRU 读取:命中移到尾部(最近使用),超限删最旧 */
53
+ private getPrepared;
54
+ /** LRU 写入:刷新位置;超上限淘汰最旧(长运行服务防无限累积) */
55
+ private setPrepared;
52
56
  /** 事务:BEGIN → fn(tx) → COMMIT;fn 抛错 → ROLLBACK(回滚失败吞掉,保留原始错误) */
53
57
  transaction<T>(fn: (tx: {
54
58
  query: (sql: string, params?: (string | number | boolean | object | null)[]) => Promise<Row[]>;
@@ -21,7 +21,10 @@ export declare function startupMessage(params: Record<string, string>): Uint8Arr
21
21
  export declare function queryMessage(sql: string): Uint8Array;
22
22
  /** Parse: P + statementName\0 + query\0 + paramTypeCount(2) + OIDs */
23
23
  export declare function parseMessage(name: string, sql: string, paramTypes?: number[]): Uint8Array;
24
- /** Bind: B + portal\0 + statement\0 + fmtCount + formats + paramCount + params + resultFmtCount */
24
+ /**
25
+ * Bind: B + portal\0 + statement\0 + fmtCount + formats + paramCount + params + resultFmtCount
26
+ * 两遍法:先算 payload 总长 → 预分配一次写入(buffer + offset 指针,避免 number[] 累积 O(n²))。
27
+ */
25
28
  export declare function bindMessage(statement: string, params: (string | Uint8Array | null)[], paramFormats?: number[]): Uint8Array;
26
29
  /** Execute: E + portal\0 + maxRows(4) */
27
30
  export declare function executeMessage(portal?: string, maxRows?: number): Uint8Array;
@@ -15,9 +15,14 @@ export declare class RedisClient {
15
15
  private constructor();
16
16
  /** 建立连接并返回就绪的客户端 */
17
17
  static connect(options?: RedisClientOptions): Promise<RedisClient>;
18
- /** 底层命令透传(RESP 值) */
19
- command(name: string, ...args: (string | number)[]): Promise<RespValue>;
18
+ /** 底层命令透传(RESP 值);Buffer 参数字节原样发送 */
19
+ command(name: string, ...args: (string | number | Buffer)[]): Promise<RespValue>;
20
20
  get(key: string): Promise<string | null>;
21
+ /**
22
+ * 二进制安全读取:返回原始字节(Uint8Array),不经过字符串解码。
23
+ * 用于缓存二进制 payload(序列化字节、图片等)。key 不存在返回 null。
24
+ */
25
+ getBuffer(key: string): Promise<Uint8Array | null>;
21
26
  /**
22
27
  * SET。ttl 秒可省略;传入即安全生效(内部转 SET key val EX ttl)。
23
28
  */
@@ -18,6 +18,8 @@ export interface RedisConnectionOptions {
18
18
  maxRetries?: number;
19
19
  /** 未连接时命令是否入队等待(ioredis enableOfflineQueue 语义)。默认 true。 */
20
20
  enableOfflineQueue?: boolean;
21
+ /** 离线队列上限。默认 5000。超限命令立即 reject(防断线期间无限累积)。 */
22
+ maxOfflineQueue?: number;
21
23
  }
22
24
  export declare class RedisConnection {
23
25
  readonly ready = false;
@@ -25,6 +27,8 @@ export declare class RedisConnection {
25
27
  private socket;
26
28
  private parser;
27
29
  private pending;
30
+ /** pending 头指针(避免 shift() O(n)——消费后定期 compact) */
31
+ private pendingHead;
28
32
  private offlineQueue;
29
33
  private subs;
30
34
  private psubs;
@@ -42,8 +46,13 @@ export declare class RedisConnection {
42
46
  private openSocket;
43
47
  private handleDisconnect;
44
48
  private onData;
45
- /** 发送命令并等待响应(单连接严格有序)。未 ready 时入离线队列(enableOfflineQueue)或拒绝。 */
46
- command(name: string, ...args: (string | number)[]): Promise<RespValue>;
49
+ /**
50
+ * 发送命令并等待响应(单连接严格有序)。未 ready 时入离线队列(enableOfflineQueue)或拒绝。
51
+ * opts.asBuffer=true:响应保留原始字节(Uint8Array)——getBuffer 等二进制场景。
52
+ */
53
+ command(name: string, ...args: (string | number | Buffer | {
54
+ asBuffer?: boolean;
55
+ })[]): Promise<RespValue>;
47
56
  private sendNow;
48
57
  private flushOffline;
49
58
  /** 批量执行:一次 write 发送所有命令字节,响应按序路由(管道) */
@@ -34,6 +34,8 @@ export declare class RedisPool {
34
34
  private next;
35
35
  command(name: string, ...args: (string | number)[]): Promise<RespValue>;
36
36
  get(key: string): Promise<string | null>;
37
+ /** 二进制安全读取(原始字节,不解码) */
38
+ getBuffer(key: string): Promise<Uint8Array | null>;
37
39
  set(key: string, value: string | number, ttl?: number): Promise<'OK'>;
38
40
  del(...keys: string[]): Promise<number>;
39
41
  incr(key: string): Promise<number>;
@@ -7,7 +7,7 @@
7
7
  * 解码为增量式:连接层可喂入任意分片,累积到完整消息后取回。
8
8
  */
9
9
  import { DbError } from '../errors.ts';
10
- export type RespValue = string | number | null | RespError | RespValue[];
10
+ export type RespValue = string | number | null | RespError | RespValue[] | Uint8Array;
11
11
  /** 服务器错误响应(-ERR ...) */
12
12
  export declare class RespError extends DbError {
13
13
  constructor(message: string);
@@ -16,9 +16,11 @@ export declare class RespError extends DbError {
16
16
  export declare class IncompleteError extends Error {
17
17
  constructor();
18
18
  }
19
- /** 编码命令为 RESP 数组字节 */
19
+ /** 递归解码字节值(bulk string)。decodeBytes=false 时字节原样保留(二进制安全)。 */
20
+ export declare function decodeValue(v: RespValue, decodeBytes: boolean): RespValue;
21
+ /** 编码命令为 RESP 数组字节。Buffer 参数字节原样写入(二进制安全,零损坏)。 */
20
22
  export declare function encodeCommand(args: (string | number | Buffer)[]): Uint8Array;
21
- /** 从完整 buffer 解析单个 RESP 值(非增量——单消息场景) */
23
+ /** 从完整 buffer 解析单个 RESP 值(非增量——单消息场景,解码为 string 语义) */
22
24
  export declare function parseReply(data: Uint8Array): RespValue;
23
25
  export declare class RespParser {
24
26
  private buf;
@@ -35,8 +37,13 @@ export declare class RespParser {
35
37
  /** 全部消费后压缩(释放底层) */
36
38
  private compact;
37
39
  private parseValue;
40
+ /**
41
+ * 读整数(: 或 $ 长度):扫描数字字符边算值,直到 \r\n(手动解析,免 parseInt + 字符串)。
42
+ * 支持负号(-1)。未读到终止符抛 IncompleteError(push 回滚)。
43
+ */
44
+ private readInt;
38
45
  /** 读一行(到 \r\n),返回行内容(不含 type 字节与 \r\n),并推进 off */
39
46
  private readLine;
40
- /** 读 len 字节的 bulk 内容 + \r\n */
41
- private readBulk;
47
+ /** 读 len 字节的 bulk 内容 + \r\n(字节中立——不 decode,由调用方决定 string/Buffer) */
48
+ private readBulkBytes;
42
49
  }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * weifuwu/email — 邮件发送中间件
3
+ *
4
+ * 注入 ctx.email.send —— 统一发送接口,适配器抽象底层服务商。
5
+ *
6
+ * 适配器:
7
+ * - 'resend'(默认推荐):一个 POST,独立开发者首选
8
+ * - 'smtp':自研 SMTP 客户端(node:net + node:tls,零依赖),任意服务商/自建 Postfix
9
+ * - 自定义函数:`adapter: async (msg) => ...` 一行接入任意服务商
10
+ *
11
+ * 裁剪声明:
12
+ * ✅ 统一 send 接口 / text + html / 多收件人 / 自定义适配器
13
+ * ❌ 附件(SMTP MIME multipart 未实现)、退信/送达率(服务商职责)、
14
+ * 批量营销、隐式入队(文档给"发邮件 = ctx.queue.add"示例)
15
+ *
16
+ * ```ts
17
+ * import { email } from 'weifuwu'
18
+ *
19
+ * app.use(email({
20
+ * from: 'no-reply@your.app',
21
+ * adapter: 'resend', // 或 'smtp'
22
+ * resend: { apiKey: process.env.RESEND_API_KEY },
23
+ * }))
24
+ *
25
+ * app.post('/api/notify', async (req, ctx) => {
26
+ * await ctx.email.send({ to: 'user@x.com', subject: '通知', html: '<h1>hi</h1>' })
27
+ * return ok()
28
+ * })
29
+ * ```
30
+ */
31
+ import type { Context, Middleware } from '../types.ts';
32
+ import { type SmtpConfig } from './smtp.ts';
33
+ export interface EmailMessage {
34
+ /** 收件人(可多个) */
35
+ to: string | string[];
36
+ subject: string;
37
+ text?: string;
38
+ html?: string;
39
+ /** 覆盖全局 from(可选) */
40
+ from?: string;
41
+ }
42
+ export interface EmailResult {
43
+ /** 服务商返回的邮件 ID(有则给) */
44
+ id?: string;
45
+ accepted: boolean;
46
+ }
47
+ /** 适配器:输入已标准化的邮件消息,输出结果。 */
48
+ export type EmailAdapter = (msg: EmailMessage) => Promise<EmailResult>;
49
+ export interface EmailOptions {
50
+ /** 默认发件人 */
51
+ from: string;
52
+ adapter?: 'resend' | 'smtp' | EmailAdapter;
53
+ resend?: {
54
+ apiKey?: string;
55
+ /** 测试可注入 mock 地址 */
56
+ baseUrl?: string;
57
+ };
58
+ smtp?: SmtpConfig;
59
+ }
60
+ export interface EmailInjected {
61
+ email: {
62
+ send: (msg: EmailMessage) => Promise<EmailResult>;
63
+ };
64
+ }
65
+ declare module '../types.ts' {
66
+ interface Context {
67
+ email?: EmailInjected['email'];
68
+ }
69
+ }
70
+ export interface EmailClient extends Middleware<Context, Context & EmailInjected> {
71
+ }
72
+ export declare function email(options: EmailOptions): EmailClient;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 自研 SMTP 客户端(零依赖,node:net + node:tls)
3
+ *
4
+ * 覆盖流程:连接 → 220 → EHLO(特性协商)→ STARTTLS(可选)→ AUTH PLAIN
5
+ * → MAIL FROM → RCPT TO → DATA(dot-stuffing)→ QUIT。
6
+ *
7
+ * 诚实裁剪:
8
+ * - 无连接池/批处理(每次发送新建连接)
9
+ * - 无附件(MIME multipart 未实现,v1 只 text/html)
10
+ * - Subject 非 ASCII 自动 RFC2047 encoded-word(UTF-8)
11
+ * - AUTH 仅 PLAIN(LOGIN/CRAM-MD5 不支持——明确报错而非静默)
12
+ */
13
+ export interface SmtpConfig {
14
+ host: string;
15
+ /** 默认 465(secure=true)/ 587(secure=false) */
16
+ port?: number;
17
+ user?: string;
18
+ pass?: string;
19
+ /** true = 直连 TLS(465);false = 明文 + STARTTLS(587)。默认 false。 */
20
+ secure?: boolean;
21
+ /** secure=false 时 STARTTLS 失败是否中止。默认 false(继续明文,测试/内网场景)。 */
22
+ requireTls?: boolean;
23
+ timeoutMs?: number;
24
+ /** 自签证书/测试环境放行。默认 false。 */
25
+ rejectUnauthorized?: boolean;
26
+ }
27
+ export interface SmtpMessage {
28
+ from: string;
29
+ to: string[];
30
+ subject: string;
31
+ text?: string;
32
+ html?: string;
33
+ }
34
+ export declare function sendSmtp(cfg: SmtpConfig, msg: SmtpMessage): Promise<void>;
package/dist/index.d.ts CHANGED
@@ -11,6 +11,16 @@ export { cors } from './middleware/cors.ts';
11
11
  export type { CORSOptions } from './middleware/cors.ts';
12
12
  export { serveStatic } from './middleware/static.ts';
13
13
  export type { ServeStaticOptions } from './middleware/static.ts';
14
+ export { rateLimit } from './middleware/rate-limit.ts';
15
+ export type { RateLimitOptions, RateLimitInjected, RateLimitAlgorithm, RateLimitStore } from './middleware/rate-limit.ts';
16
+ export { email } from './email/index.ts';
17
+ export type { EmailOptions, EmailMessage, EmailResult, EmailAdapter, EmailInjected } from './email/index.ts';
18
+ export { sendSmtp } from './email/smtp.ts';
19
+ export type { SmtpConfig } from './email/smtp.ts';
20
+ export { userSystem } from './user/index.ts';
21
+ export type { UserSystemOptions, UserInjected, AuthApi, RegisterInput } from './user/index.ts';
22
+ export { queue } from './queue/index.ts';
23
+ export type { QueueOptions, QueueClient, QueueInjected, QueueWorker, WorkerOptions, AddOptions, Job } from './queue/index.ts';
14
24
  export { ui } from './ui/index.ts';
15
25
  export { uiSsr } from './ui/ssr-page.ts';
16
26
  export type { UiSsrOptions } from './ui/ssr-page.ts';