@wordrhyme/auto-crud-server 0.4.0 → 0.6.1

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
@@ -3,7 +3,6 @@ import { SQL } from "drizzle-orm";
3
3
  import * as _trpc_server2 from "@trpc/server";
4
4
  import superjson from "superjson";
5
5
  import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
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";
7
6
  import { PgTable } from "drizzle-orm/pg-core";
8
7
 
9
8
  //#region src/trpc.d.ts
@@ -23,7 +22,7 @@ declare const router: _trpc_server2.TRPCRouterBuilder<{
23
22
  declare const publicProcedure: _trpc_server2.TRPCProcedureBuilder<Context, object, object, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, false>;
24
23
  //#endregion
25
24
  //#region src/types/config.d.ts
26
- type CrudOperation = "list" | "get" | "create" | "update" | "delete" | "deleteMany" | "updateMany";
25
+ type CrudOperation = "list" | "get" | "create" | "update" | "delete" | "deleteMany" | "updateMany" | "upsert";
27
26
  type WriteOperation = "create" | "update";
28
27
  type AnyProcedure = any;
29
28
  interface SoftDeleteConfig {
@@ -72,100 +71,136 @@ interface ListResult<TSelect> {
72
71
  perPage: number;
73
72
  pageCount: number;
74
73
  }
74
+ interface ListMiddlewareParams<TContext, TSelect> {
75
+ ctx: TContext;
76
+ input: ListInput;
77
+ next: (input?: ListInput) => Promise<ListResult<TSelect>>;
78
+ }
79
+ interface GetMiddlewareParams<TContext, TSelect> {
80
+ ctx: TContext;
81
+ id: string;
82
+ next: () => Promise<TSelect | null>;
83
+ }
84
+ interface CreateMiddlewareParams<TContext, TSelect, TInsert> {
85
+ ctx: TContext;
86
+ input: TInsert;
87
+ next: (input?: TInsert) => Promise<TSelect>;
88
+ }
89
+ interface UpdateMiddlewareParams<TContext, TSelect, TUpdate> {
90
+ ctx: TContext;
91
+ id: string;
92
+ data: TUpdate;
93
+ existing: TSelect;
94
+ next: (data?: TUpdate) => Promise<TSelect>;
95
+ }
96
+ interface DeleteMiddlewareParams<TContext, TSelect> {
97
+ ctx: TContext;
98
+ id: string;
99
+ existing: TSelect;
100
+ next: () => Promise<TSelect>;
101
+ }
102
+ interface DeleteManyMiddlewareParams<TContext> {
103
+ ctx: TContext;
104
+ ids: string[];
105
+ next: () => Promise<{
106
+ deleted: number;
107
+ }>;
108
+ }
109
+ interface UpdateManyMiddlewareParams<TContext, TUpdate> {
110
+ ctx: TContext;
111
+ ids: string[];
112
+ data: TUpdate;
113
+ next: (data?: TUpdate) => Promise<{
114
+ updated: number;
115
+ }>;
116
+ }
117
+ interface UpsertMiddlewareParams<TContext, TSelect, TInsert> {
118
+ ctx: TContext;
119
+ input: TInsert;
120
+ next: (input?: TInsert) => Promise<{
121
+ data: TSelect;
122
+ isNew: boolean;
123
+ }>;
124
+ }
75
125
  /**
76
- * CRUD 生命周期钩子
126
+ * 操作包装器 - 类似 tRPC middleware 的 next 模式
127
+ *
128
+ * 功能:
129
+ * - 修改输入数据(传参给 next)
130
+ * - 修改返回值(处理 next 的结果)
131
+ * - 执行副作用(日志、通知、审计)
132
+ * - 条件拦截(不调用 next 直接返回/抛错)
133
+ * - 包装事务(try/catch 包裹 next)
134
+ *
135
+ * @typeParam TContext - 上下文类型
136
+ * @typeParam TSelect - 查询返回类型(从 selectSchema 推断)
137
+ * @typeParam TInsert - 创建输入类型(从 insertSchema 推断)
138
+ * @typeParam TUpdate - 更新输入类型(从 updateSchema 推断)
77
139
  *
78
140
  * @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);
141
+ * // 修改输入 + 执行副作用
142
+ * middleware: {
143
+ * create: async ({ ctx, input, next }) => {
144
+ * const data = { ...input, slug: slugify(input.title) };
145
+ * const result = await next(data);
146
+ * await sendNotification(result);
147
+ * return result;
87
148
  * },
88
149
  * }
89
150
  */
90
- interface CrudHooks<TContext = unknown> {
151
+ interface CrudMiddleware<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
91
152
  /**
92
- * 列表查询前
93
- * 可修改查询参数
153
+ * 列表查询包装器
94
154
  */
95
- beforeList?: (ctx: TContext, input: ListInput) => ListInput | void | Promise<ListInput | void>;
155
+ list?: (params: ListMiddlewareParams<TContext, TSelect>) => Promise<ListResult<TSelect>>;
96
156
  /**
97
- * 列表查询后
98
- * 可修改返回结果
157
+ * 单条查询包装器
99
158
  */
100
- afterList?: <TSelect>(ctx: TContext, result: ListResult<TSelect>) => ListResult<TSelect> | void | Promise<ListResult<TSelect> | void>;
159
+ get?: (params: GetMiddlewareParams<TContext, TSelect>) => Promise<TSelect | null>;
101
160
  /**
102
- * 单条查询前
161
+ * 创建包装器
103
162
  */
104
- beforeGet?: (ctx: TContext, id: string) => void | Promise<void>;
163
+ create?: (params: CreateMiddlewareParams<TContext, TSelect, TInsert>) => Promise<TSelect>;
105
164
  /**
106
- * 单条查询后
107
- * 可修改返回结果
165
+ * 更新包装器
166
+ * existing: 更新前的记录
108
167
  */
109
- afterGet?: <TSelect>(ctx: TContext, result: TSelect | null) => TSelect | null | void | Promise<TSelect | null | void>;
168
+ update?: (params: UpdateMiddlewareParams<TContext, TSelect, TUpdate>) => Promise<TSelect>;
110
169
  /**
111
- * 创建前
112
- * 可修改输入数据,返回新数据或抛出错误阻止创建
170
+ * 删除包装器
171
+ * existing: 删除前的记录
113
172
  */
114
- beforeCreate?: <TInsert>(ctx: TContext, data: TInsert) => TInsert | void | Promise<TInsert | void>;
173
+ delete?: (params: DeleteMiddlewareParams<TContext, TSelect>) => Promise<TSelect>;
115
174
  /**
116
- * 创建后
117
- * 用于副作用(日志、通知、同步等)
175
+ * 批量删除包装器
118
176
  */
119
- afterCreate?: <TSelect>(ctx: TContext, created: TSelect) => void | Promise<void>;
120
- /**
121
- * 更新前
122
- * 可修改输入数据,existing 为更新前的记录
123
- */
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>;
129
- /**
130
- * 删除前
131
- * existing 为删除前的记录,可抛出错误阻止删除
132
- */
133
- beforeDelete?: <TSelect>(ctx: TContext, id: string, existing: TSelect) => void | Promise<void>;
134
- /**
135
- * 删除后
136
- */
137
- afterDelete?: <TSelect>(ctx: TContext, deleted: TSelect) => void | Promise<void>;
138
- /**
139
- * 批量删除前
140
- */
141
- beforeDeleteMany?: (ctx: TContext, ids: string[]) => void | Promise<void>;
142
- /**
143
- * 批量删除后
144
- */
145
- afterDeleteMany?: (ctx: TContext, result: {
177
+ deleteMany?: (params: DeleteManyMiddlewareParams<TContext>) => Promise<{
146
178
  deleted: number;
147
- }) => void | Promise<void>;
179
+ }>;
148
180
  /**
149
- * 批量更新前
150
- * 可修改输入数据
181
+ * 批量更新包装器
151
182
  */
152
- beforeUpdateMany?: <TUpdate>(ctx: TContext, ids: string[], data: TUpdate) => TUpdate | void | Promise<TUpdate | void>;
183
+ updateMany?: (params: UpdateManyMiddlewareParams<TContext, TUpdate>) => Promise<{
184
+ updated: number;
185
+ }>;
153
186
  /**
154
- * 批量更新后
187
+ * Upsert 包装器
188
+ * isNew: 是否为新建(冲突检测后)
155
189
  */
156
- afterUpdateMany?: (ctx: TContext, result: {
157
- updated: number;
158
- }) => void | Promise<void>;
190
+ upsert?: (params: UpsertMiddlewareParams<TContext, TSelect, TInsert>) => Promise<{
191
+ data: TSelect;
192
+ isNew: boolean;
193
+ }>;
159
194
  }
160
- interface CrudRouterConfigBase<TContext = unknown> {
195
+ interface CrudRouterConfigBase<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
161
196
  /** Drizzle 表定义 */
162
197
  table: PgTable;
163
198
  /** 查询返回 Schema */
164
- selectSchema?: z.ZodType;
199
+ selectSchema?: z.ZodType<TSelect>;
165
200
  /** 创建输入 Schema */
166
- insertSchema: z.ZodType;
201
+ insertSchema: z.ZodType<TInsert>;
167
202
  /** 更新输入 Schema */
168
- updateSchema: z.ZodType;
203
+ updateSchema: z.ZodType<TUpdate>;
169
204
  /** ID 字段名,默认 "id" */
170
205
  idField?: string;
171
206
  /** 可过滤的列白名单 */
@@ -193,22 +228,36 @@ interface CrudRouterConfigBase<TContext = unknown> {
193
228
  */
194
229
  softDelete?: SoftDeleteOption;
195
230
  /**
196
- * 生命周期钩子
197
- * CRUD 操作前后注入自定义逻辑
231
+ * 操作中间件
232
+ * 类似 tRPC middleware 的 next 模式,完全控制操作流程
198
233
  *
199
234
  * @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);
235
+ * // 完整控制
236
+ * middleware: {
237
+ * create: async ({ ctx, input, next }) => {
238
+ * const data = { ...input, slug: slugify(input.title) };
239
+ * const result = await next(data);
240
+ * await sendNotification(result);
241
+ * return result;
206
242
  * },
207
243
  * }
244
+ *
245
+ * @example
246
+ * // 使用工具函数简化
247
+ * import { afterMiddleware, beforeMiddleware } from "@wordrhyme/auto-crud-server";
248
+ *
249
+ * middleware: {
250
+ * create: afterMiddleware(async (ctx, result) => {
251
+ * await sendEmail(result);
252
+ * }),
253
+ * update: beforeMiddleware(async (ctx, data) => {
254
+ * return { ...data, updatedAt: new Date() };
255
+ * }),
256
+ * }
208
257
  */
209
- hooks?: CrudHooks<TContext>;
258
+ middleware?: CrudMiddleware<TContext, TSelect, TInsert, TUpdate>;
210
259
  }
211
- interface DeclarativeConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
260
+ interface DeclarativeConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
212
261
  mode?: "declarative";
213
262
  /**
214
263
  * 基础 procedure(认证)
@@ -246,9 +295,9 @@ interface DeclarativeConfig<TContext = unknown> extends CrudRouterConfigBase<TCo
246
295
  * @example
247
296
  * authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
248
297
  */
249
- authorize?: (ctx: TContext, resource: Record<string, unknown>, operation: CrudOperation) => boolean | Promise<boolean>;
298
+ authorize?: (ctx: TContext, resource: TSelect, operation: CrudOperation) => boolean | Promise<boolean>;
250
299
  }
251
- interface FactoryConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
300
+ interface FactoryConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
252
301
  mode: "factory";
253
302
  /**
254
303
  * Procedure 工厂函数
@@ -267,7 +316,7 @@ interface FactoryConfig<TContext = unknown> extends CrudRouterConfigBase<TContex
267
316
  */
268
317
  inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
269
318
  }
270
- interface ProceduresConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
319
+ interface ProceduresConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
271
320
  mode: "procedures";
272
321
  /**
273
322
  * 按操作指定不同的 procedure
@@ -291,41 +340,87 @@ interface ProceduresConfig<TContext = unknown> extends CrudRouterConfigBase<TCon
291
340
  */
292
341
  inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
293
342
  }
294
- type CrudRouterConfig<TContext = unknown> = DeclarativeConfig<TContext> | FactoryConfig<TContext> | ProceduresConfig<TContext>;
343
+ type CrudRouterConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> = DeclarativeConfig<TContext, TSelect, TInsert, TUpdate> | FactoryConfig<TContext, TSelect, TInsert, TUpdate> | ProceduresConfig<TContext, TSelect, TInsert, TUpdate>;
344
+ interface CrudProcedures<TSelect, TInsert, TUpdate> {
345
+ list: AnyProcedure;
346
+ get: AnyProcedure;
347
+ create: AnyProcedure;
348
+ update: AnyProcedure;
349
+ delete: AnyProcedure;
350
+ deleteMany: AnyProcedure;
351
+ updateMany: AnyProcedure;
352
+ upsert: AnyProcedure;
353
+ }
295
354
  //#endregion
296
355
  //#region src/routers/_factory.d.ts
297
- declare function createCrudRouter<TContext = unknown>(config: CrudRouterConfig<TContext>): node_modules__trpc_server_dist_unstable_core_do_not_import_d_CjQPvBRI_mjs0.Router<{
298
- ctx: Context;
299
- meta: object;
300
- errorShape: _trpc_server2.TRPCDefaultErrorShape;
301
- transformer: true;
302
- }, _trpc_server2.TRPCDecorateCreateRouterOptions<{
303
- list: any;
304
- getById: any;
305
- create: any;
306
- update: any;
307
- delete: any;
308
- deleteMany: any;
309
- updateMany: any;
310
- }>> & _trpc_server2.TRPCDecorateCreateRouterOptions<{
311
- list: any;
312
- getById: any;
313
- create: any;
314
- update: any;
315
- delete: any;
316
- deleteMany: any;
317
- updateMany: any;
318
- }> & {
319
- procedures: {
320
- list: any;
321
- getById: any;
322
- create: any;
323
- update: any;
324
- delete: any;
325
- deleteMany: any;
326
- updateMany: any;
327
- };
356
+ /**
357
+ * 创建 CRUD Router
358
+ *
359
+ * @typeParam TContext - 上下文类型(包含 db)
360
+ * @typeParam TSelect - 查询返回类型(从 selectSchema 或 insertSchema 推断)
361
+ * @typeParam TInsert - 创建输入类型(从 insertSchema 推断)
362
+ * @typeParam TUpdate - 更新输入类型(从 updateSchema 推断)
363
+ */
364
+ declare function createCrudRouter<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown>(config: CrudRouterConfig<TContext, TSelect, TInsert, TUpdate>): ReturnType<typeof router> & {
365
+ procedures: CrudProcedures<TSelect, TInsert, TUpdate>;
366
+ };
367
+ //#endregion
368
+ //#region src/lib/middleware-helpers.d.ts
369
+ /**
370
+ * Middleware 便捷工具函数
371
+ * 简化常见场景的 middleware 创建
372
+ */
373
+ /**
374
+ * 创建 after-only 的 middleware(最常见场景)
375
+ * 在操作完成后执行副作用,不修改返回值
376
+ *
377
+ * @example
378
+ * middleware: {
379
+ * create: afterMiddleware(async (ctx, result) => {
380
+ * await sendEmail(result);
381
+ * await logAudit(ctx.user, 'create', result);
382
+ * }),
383
+ * }
384
+ */
385
+ declare function afterMiddleware<TContext, TResult>(fn: (ctx: TContext, result: TResult) => void | Promise<void>): (params: {
386
+ ctx: TContext;
387
+ next: () => Promise<TResult>;
388
+ [key: string]: any;
389
+ }) => Promise<TResult>;
390
+ /**
391
+ * 创建 before-only 的 middleware
392
+ * 在操作执行前修改输入数据
393
+ *
394
+ * @example
395
+ * middleware: {
396
+ * create: beforeMiddleware(async (ctx, input) => {
397
+ * return { ...input, slug: slugify(input.title), createdBy: ctx.user.id };
398
+ * }),
399
+ * }
400
+ */
401
+ declare function beforeMiddleware<TContext, TInput>(fn: (ctx: TContext, input: TInput) => TInput | Promise<TInput>): <TResult>(params: {
402
+ ctx: TContext;
403
+ input: TInput;
404
+ next: (input?: TInput) => Promise<TResult>;
405
+ [key: string]: any;
406
+ }) => Promise<TResult>;
407
+ type AnyMiddlewareParams<TContext> = {
408
+ ctx: TContext;
409
+ next: (...args: any[]) => Promise<any>;
410
+ [key: string]: any;
328
411
  };
412
+ /**
413
+ * 组合多个 middleware 为一个
414
+ *
415
+ * @example
416
+ * middleware: {
417
+ * create: composeMiddleware(
418
+ * beforeMiddleware((ctx, input) => ({ ...input, slug: slugify(input.title) })),
419
+ * afterMiddleware((ctx, result) => sendEmail(result)),
420
+ * ),
421
+ * }
422
+ */
423
+ declare function composeMiddleware<TContext, TResult>(...middlewares: Array<(params: AnyMiddlewareParams<TContext>) => Promise<any>>): (params: AnyMiddlewareParams<TContext>) => Promise<TResult>;
329
424
  //#endregion
330
425
  //#region src/routers/index.d.ts
331
426
  declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
@@ -336,4 +431,4 @@ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
336
431
  }, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
337
432
  type AppRouter = typeof appRouter;
338
433
  //#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 };
434
+ export { type AnyProcedure, type AppRouter, type CreateMiddlewareParams, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type CrudRouterConfigBase, type DeclarativeConfig, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type FactoryConfig, type GetMiddlewareParams, type ListInput, type ListMiddlewareParams, type ListResult, type ProceduresConfig, type SoftDeleteConfig, type SoftDeleteOption, type UpdateManyMiddlewareParams, type UpdateMiddlewareParams, type UpsertMiddlewareParams, type WriteOperation, afterMiddleware, appRouter, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };