@wordrhyme/auto-crud-server 0.4.0 → 0.6.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/dist/index.d.cts CHANGED
@@ -72,89 +72,107 @@ interface ListResult<TSelect> {
72
72
  pageCount: number;
73
73
  }
74
74
  /**
75
- * CRUD 生命周期钩子
75
+ * 操作包装器 - 类似 tRPC middleware 的 next 模式
76
+ *
77
+ * 功能:
78
+ * - 修改输入数据(传参给 next)
79
+ * - 修改返回值(处理 next 的结果)
80
+ * - 执行副作用(日志、通知、审计)
81
+ * - 条件拦截(不调用 next 直接返回/抛错)
82
+ * - 包装事务(try/catch 包裹 next)
76
83
  *
77
84
  * @example
78
- * hooks: {
79
- * beforeCreate: async (ctx, data) => {
80
- * // 修改数据
81
- * return { ...data, slug: slugify(data.title) };
82
- * },
83
- * afterCreate: async (ctx, created) => {
84
- * // 发送通知
85
- * await sendNotification(created);
85
+ * // 修改输入 + 执行副作用
86
+ * middleware: {
87
+ * create: async ({ ctx, input, next }) => {
88
+ * const data = { ...input, slug: slugify(input.title) };
89
+ * const result = await next(data);
90
+ * await sendNotification(result);
91
+ * return result;
86
92
  * },
87
93
  * }
94
+ *
95
+ * @example
96
+ * // 简单副作用 - 使用工具函数
97
+ * import { afterMiddleware } from "@wordrhyme/auto-crud-server";
98
+ *
99
+ * middleware: {
100
+ * create: afterMiddleware(async (ctx, result) => {
101
+ * await sendEmail(result);
102
+ * }),
103
+ * }
88
104
  */
89
- interface CrudHooks<TContext = unknown> {
90
- /**
91
- * 列表查询前
92
- * 可修改查询参数
93
- */
94
- beforeList?: (ctx: TContext, input: ListInput) => ListInput | void | Promise<ListInput | void>;
95
- /**
96
- * 列表查询后
97
- * 可修改返回结果
98
- */
99
- afterList?: <TSelect>(ctx: TContext, result: ListResult<TSelect>) => ListResult<TSelect> | void | Promise<ListResult<TSelect> | void>;
100
- /**
101
- * 单条查询前
102
- */
103
- beforeGet?: (ctx: TContext, id: string) => void | Promise<void>;
104
- /**
105
- * 单条查询后
106
- * 可修改返回结果
107
- */
108
- afterGet?: <TSelect>(ctx: TContext, result: TSelect | null) => TSelect | null | void | Promise<TSelect | null | void>;
109
- /**
110
- * 创建前
111
- * 可修改输入数据,返回新数据或抛出错误阻止创建
112
- */
113
- beforeCreate?: <TInsert>(ctx: TContext, data: TInsert) => TInsert | void | Promise<TInsert | void>;
105
+ interface CrudMiddleware<TContext = unknown> {
114
106
  /**
115
- * 创建后
116
- * 用于副作用(日志、通知、同步等)
107
+ * 列表查询包装器
117
108
  */
118
- afterCreate?: <TSelect>(ctx: TContext, created: TSelect) => void | Promise<void>;
109
+ list?: (params: {
110
+ ctx: TContext;
111
+ input: ListInput;
112
+ next: (input?: ListInput) => Promise<ListResult<unknown>>;
113
+ }) => Promise<ListResult<unknown>>;
119
114
  /**
120
- * 更新前
121
- * 可修改输入数据,existing 为更新前的记录
115
+ * 单条查询包装器
122
116
  */
123
- beforeUpdate?: <TUpdate, TSelect>(ctx: TContext, id: string, data: TUpdate, existing: TSelect) => TUpdate | void | Promise<TUpdate | void>;
124
- /**
125
- * 更新后
126
- */
127
- afterUpdate?: <TSelect>(ctx: TContext, updated: TSelect) => void | Promise<void>;
117
+ get?: (params: {
118
+ ctx: TContext;
119
+ id: string;
120
+ next: () => Promise<unknown | null>;
121
+ }) => Promise<unknown | null>;
128
122
  /**
129
- * 删除前
130
- * existing 为删除前的记录,可抛出错误阻止删除
123
+ * 创建包装器
131
124
  */
132
- beforeDelete?: <TSelect>(ctx: TContext, id: string, existing: TSelect) => void | Promise<void>;
125
+ create?: (params: {
126
+ ctx: TContext;
127
+ input: unknown;
128
+ next: (input?: unknown) => Promise<unknown>;
129
+ }) => Promise<unknown>;
133
130
  /**
134
- * 删除后
131
+ * 更新包装器
132
+ * existing: 更新前的记录
135
133
  */
136
- afterDelete?: <TSelect>(ctx: TContext, deleted: TSelect) => void | Promise<void>;
134
+ update?: (params: {
135
+ ctx: TContext;
136
+ id: string;
137
+ data: unknown;
138
+ existing: unknown;
139
+ next: (data?: unknown) => Promise<unknown>;
140
+ }) => Promise<unknown>;
137
141
  /**
138
- * 批量删除前
142
+ * 删除包装器
143
+ * existing: 删除前的记录
139
144
  */
140
- beforeDeleteMany?: (ctx: TContext, ids: string[]) => void | Promise<void>;
145
+ delete?: (params: {
146
+ ctx: TContext;
147
+ id: string;
148
+ existing: unknown;
149
+ next: () => Promise<unknown>;
150
+ }) => Promise<unknown>;
141
151
  /**
142
- * 批量删除后
152
+ * 批量删除包装器
143
153
  */
144
- afterDeleteMany?: (ctx: TContext, result: {
154
+ deleteMany?: (params: {
155
+ ctx: TContext;
156
+ ids: string[];
157
+ next: () => Promise<{
158
+ deleted: number;
159
+ }>;
160
+ }) => Promise<{
145
161
  deleted: number;
146
- }) => void | Promise<void>;
147
- /**
148
- * 批量更新前
149
- * 可修改输入数据
150
- */
151
- beforeUpdateMany?: <TUpdate>(ctx: TContext, ids: string[], data: TUpdate) => TUpdate | void | Promise<TUpdate | void>;
162
+ }>;
152
163
  /**
153
- * 批量更新后
164
+ * 批量更新包装器
154
165
  */
155
- afterUpdateMany?: (ctx: TContext, result: {
166
+ updateMany?: (params: {
167
+ ctx: TContext;
168
+ ids: string[];
169
+ data: unknown;
170
+ next: (data?: unknown) => Promise<{
171
+ updated: number;
172
+ }>;
173
+ }) => Promise<{
156
174
  updated: number;
157
- }) => void | Promise<void>;
175
+ }>;
158
176
  }
159
177
  interface CrudRouterConfigBase<TContext = unknown> {
160
178
  /** Drizzle 表定义 */
@@ -192,20 +210,34 @@ interface CrudRouterConfigBase<TContext = unknown> {
192
210
  */
193
211
  softDelete?: SoftDeleteOption;
194
212
  /**
195
- * 生命周期钩子
196
- * CRUD 操作前后注入自定义逻辑
213
+ * 操作中间件
214
+ * 类似 tRPC middleware 的 next 模式,完全控制操作流程
197
215
  *
198
216
  * @example
199
- * hooks: {
200
- * beforeCreate: async (ctx, data) => {
201
- * return { ...data, slug: slugify(data.title) };
202
- * },
203
- * afterCreate: async (ctx, created) => {
204
- * await sendNotification(created);
217
+ * // 完整控制
218
+ * middleware: {
219
+ * create: async ({ ctx, input, next }) => {
220
+ * const data = { ...input, slug: slugify(input.title) };
221
+ * const result = await next(data);
222
+ * await sendNotification(result);
223
+ * return result;
205
224
  * },
206
225
  * }
226
+ *
227
+ * @example
228
+ * // 使用工具函数简化
229
+ * import { afterMiddleware, beforeMiddleware } from "@wordrhyme/auto-crud-server";
230
+ *
231
+ * middleware: {
232
+ * create: afterMiddleware(async (ctx, result) => {
233
+ * await sendEmail(result);
234
+ * }),
235
+ * update: beforeMiddleware(async (ctx, data) => {
236
+ * return { ...data, updatedAt: new Date() };
237
+ * }),
238
+ * }
207
239
  */
208
- hooks?: CrudHooks<TContext>;
240
+ middleware?: CrudMiddleware<TContext>;
209
241
  }
210
242
  interface DeclarativeConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
211
243
  mode?: "declarative";
@@ -326,6 +358,134 @@ declare function createCrudRouter<TContext = unknown>(config: CrudRouterConfig<T
326
358
  };
327
359
  };
328
360
  //#endregion
361
+ //#region src/lib/middleware-helpers.d.ts
362
+ type NextFn<TInput, TResult> = (input?: TInput) => Promise<TResult>;
363
+ interface MiddlewareParams<TContext, TInput, TResult> {
364
+ ctx: TContext;
365
+ input?: TInput;
366
+ next: NextFn<TInput, TResult>;
367
+ }
368
+ /**
369
+ * 创建 after-only 的 middleware(最常见场景)
370
+ * 在操作完成后执行副作用,不修改返回值
371
+ *
372
+ * @example
373
+ * middleware: {
374
+ * create: afterMiddleware(async (ctx, result) => {
375
+ * await sendEmail(result);
376
+ * await logAudit(ctx.user, 'create', result);
377
+ * }),
378
+ * }
379
+ */
380
+ declare function afterMiddleware<TContext, TResult>(fn: (ctx: TContext, result: TResult) => void | Promise<void>): ({
381
+ ctx,
382
+ next
383
+ }: {
384
+ ctx: TContext;
385
+ next: () => Promise<TResult>;
386
+ }) => Promise<TResult>;
387
+ /**
388
+ * 创建 after-only 的 middleware,可修改返回值
389
+ *
390
+ * @example
391
+ * middleware: {
392
+ * get: afterMiddlewareTransform(async (ctx, result) => {
393
+ * return { ...result, computed: calculateSomething(result) };
394
+ * }),
395
+ * }
396
+ */
397
+ declare function afterMiddlewareTransform<TContext, TResult>(fn: (ctx: TContext, result: TResult) => TResult | Promise<TResult>): ({
398
+ ctx,
399
+ next
400
+ }: {
401
+ ctx: TContext;
402
+ next: () => Promise<TResult>;
403
+ }) => Promise<TResult>;
404
+ /**
405
+ * 创建 before-only 的 middleware
406
+ * 在操作执行前修改输入数据
407
+ *
408
+ * @example
409
+ * middleware: {
410
+ * create: beforeMiddleware(async (ctx, input) => {
411
+ * return { ...input, slug: slugify(input.title), createdBy: ctx.user.id };
412
+ * }),
413
+ * }
414
+ */
415
+ declare function beforeMiddleware<TContext, TInput>(fn: (ctx: TContext, input: TInput) => TInput | Promise<TInput>): <TResult>({
416
+ ctx,
417
+ input,
418
+ next
419
+ }: MiddlewareParams<TContext, TInput, TResult>) => Promise<TResult>;
420
+ /**
421
+ * 组合多个 middleware 为一个
422
+ *
423
+ * @example
424
+ * middleware: {
425
+ * create: composeMiddleware(
426
+ * beforeMiddleware((ctx, input) => ({ ...input, slug: slugify(input.title) })),
427
+ * afterMiddleware((ctx, result) => sendEmail(result)),
428
+ * ),
429
+ * }
430
+ */
431
+ declare function composeMiddleware<TContext, TInput, TResult>(...middlewares: Array<(params: MiddlewareParams<TContext, TInput, TResult>) => Promise<TResult>>): (params: MiddlewareParams<TContext, TInput, TResult>) => Promise<TResult>;
432
+ /**
433
+ * 创建 list 操作的 after middleware
434
+ */
435
+ declare function afterList<TContext, TSelect>(fn: (ctx: TContext, result: ListResult<TSelect>) => void | Promise<void>): ({
436
+ ctx,
437
+ next
438
+ }: {
439
+ ctx: TContext;
440
+ next: () => Promise<ListResult<TSelect>>;
441
+ }) => Promise<ListResult<TSelect>>;
442
+ /**
443
+ * 创建 list 操作的 before middleware
444
+ */
445
+ declare function beforeList<TContext>(fn: (ctx: TContext, input: ListInput) => ListInput | Promise<ListInput>): <TResult>({
446
+ ctx,
447
+ input,
448
+ next
449
+ }: MiddlewareParams<TContext, ListInput, TResult>) => Promise<TResult>;
450
+ /**
451
+ * 创建 create 操作的 after middleware
452
+ */
453
+ declare function afterCreate<TContext, TSelect>(fn: (ctx: TContext, created: TSelect) => void | Promise<void>): ({
454
+ ctx,
455
+ next
456
+ }: {
457
+ ctx: TContext;
458
+ next: () => Promise<TSelect>;
459
+ }) => Promise<TSelect>;
460
+ /**
461
+ * 创建 create 操作的 before middleware
462
+ */
463
+ declare function beforeCreate<TContext, TInsert>(fn: (ctx: TContext, input: TInsert) => TInsert | Promise<TInsert>): <TResult>({
464
+ ctx,
465
+ input,
466
+ next
467
+ }: MiddlewareParams<TContext, TInsert, TResult>) => Promise<TResult>;
468
+ /**
469
+ * 创建 update 操作的 after middleware
470
+ */
471
+ declare function afterUpdate<TContext, TSelect>(fn: (ctx: TContext, updated: TSelect) => void | Promise<void>): ({
472
+ ctx,
473
+ next
474
+ }: {
475
+ ctx: TContext;
476
+ next: () => Promise<TSelect>;
477
+ }) => Promise<TSelect>;
478
+ /**
479
+ * 创建 delete 操作的 after middleware
480
+ */
481
+ declare function afterDelete<TContext, TSelect>(fn: (ctx: TContext, deleted: TSelect) => void | Promise<void>): ({
482
+ ctx,
483
+ next
484
+ }: {
485
+ ctx: TContext;
486
+ next: () => Promise<TSelect>;
487
+ }) => Promise<TSelect>;
488
+ //#endregion
329
489
  //#region src/routers/index.d.ts
330
490
  declare const appRouter: _trpc_server5.TRPCBuiltRouter<{
331
491
  ctx: Context;
@@ -335,4 +495,4 @@ declare const appRouter: _trpc_server5.TRPCBuiltRouter<{
335
495
  }, _trpc_server5.TRPCDecorateCreateRouterOptions<{}>>;
336
496
  type AppRouter = typeof appRouter;
337
497
  //#endregion
338
- export { type AnyProcedure, type AppRouter, type CrudHooks, type CrudOperation, type CrudRouterConfig, type DeclarativeConfig, type FactoryConfig, type ListInput, type ListResult, type ProceduresConfig, type SoftDeleteConfig, type SoftDeleteOption, type WriteOperation, appRouter, createCrudRouter, publicProcedure, router };
498
+ export { type AnyProcedure, type AppRouter, type CrudMiddleware, type CrudOperation, type CrudRouterConfig, type DeclarativeConfig, type FactoryConfig, type ListInput, type ListResult, type ProceduresConfig, type SoftDeleteConfig, type SoftDeleteOption, type WriteOperation, afterCreate, afterDelete, afterList, afterMiddleware, afterMiddlewareTransform, afterUpdate, appRouter, beforeCreate, beforeList, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };