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