@wordrhyme/auto-crud-server 0.6.2 → 0.8.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.cjs CHANGED
@@ -417,38 +417,53 @@ function resolveSoftDelete(option) {
417
417
  getValue: config.value ?? (() => /* @__PURE__ */ new Date())
418
418
  };
419
419
  }
420
- function resolveConfig(config) {
421
- const mode = config.mode || "declarative";
422
- if (mode === "factory") {
423
- const factoryConfig = config;
424
- return {
425
- procedureFactory: factoryConfig.procedureFactory,
426
- guard: void 0,
427
- getScope: () => void 0,
428
- getInject: (ctx, op) => factoryConfig.inject?.(ctx, op) ?? {},
429
- checkAuthorize: async () => true
430
- };
420
+ /**
421
+ * 判断 procedure 配置的类型
422
+ */
423
+ function isProcedureMap(config) {
424
+ if (!config) return false;
425
+ if (typeof config === "function") return false;
426
+ if (typeof config === "object" && config !== null) {
427
+ const keys = Object.keys(config);
428
+ const validKeys = [
429
+ "list",
430
+ "get",
431
+ "create",
432
+ "update",
433
+ "delete",
434
+ "deleteMany",
435
+ "updateMany",
436
+ "upsert",
437
+ "default"
438
+ ];
439
+ return keys.some((k) => validKeys.includes(k));
431
440
  }
432
- if (mode === "procedures") {
433
- const proceduresConfig = config;
434
- const defaultProc = proceduresConfig.defaultProcedure ?? publicProcedure;
435
- return {
436
- procedureFactory: (op) => proceduresConfig.procedures[op] ?? defaultProc,
437
- guard: void 0,
438
- getScope: () => void 0,
439
- getInject: (ctx, op) => proceduresConfig.inject?.(ctx, op) ?? {},
440
- checkAuthorize: async () => true
441
- };
441
+ return false;
442
+ }
443
+ function isProcedureFactory(config) {
444
+ return typeof config === "function";
445
+ }
446
+ /**
447
+ * 解析 procedure 配置为工厂函数
448
+ */
449
+ function resolveProcedureFactory(config) {
450
+ if (!config) return () => publicProcedure;
451
+ if (isProcedureFactory(config)) return config;
452
+ if (isProcedureMap(config)) {
453
+ const map = config;
454
+ const defaultProc = map.default ?? publicProcedure;
455
+ return (op) => map[op] ?? defaultProc;
442
456
  }
443
- const declarativeConfig = config;
444
- const baseProcedure = declarativeConfig.procedure ?? publicProcedure;
457
+ return () => config;
458
+ }
459
+ function resolveConfig(config) {
445
460
  return {
446
- procedureFactory: () => baseProcedure,
447
- guard: declarativeConfig.guard,
448
- getScope: (ctx, tbl, op) => declarativeConfig.scope?.(ctx, tbl, op),
449
- getInject: (ctx, op) => declarativeConfig.inject?.(ctx, op) ?? {},
461
+ procedureFactory: resolveProcedureFactory(config.procedure),
462
+ guard: config.guard,
463
+ getScope: (ctx, tbl, op) => config.scope?.(ctx, tbl, op),
464
+ getInject: (ctx, op) => config.inject?.(ctx, op) ?? {},
450
465
  checkAuthorize: async (ctx, resource, op) => {
451
- if (declarativeConfig.authorize) return declarativeConfig.authorize(ctx, resource, op);
466
+ if (config.authorize) return config.authorize(ctx, resource, op);
452
467
  return true;
453
468
  }
454
469
  };
package/dist/index.d.cts CHANGED
@@ -191,15 +191,137 @@ interface CrudMiddleware<TContext = unknown, TSelect = unknown, TInsert = unknow
191
191
  isNew: boolean;
192
192
  }>;
193
193
  }
194
- interface CrudRouterConfigBase<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
194
+ /**
195
+ * Procedure 映射对象
196
+ * 按操作指定不同的 procedure,支持 default 回退
197
+ */
198
+ interface ProcedureMap {
199
+ list?: AnyProcedure;
200
+ get?: AnyProcedure;
201
+ create?: AnyProcedure;
202
+ update?: AnyProcedure;
203
+ delete?: AnyProcedure;
204
+ deleteMany?: AnyProcedure;
205
+ updateMany?: AnyProcedure;
206
+ upsert?: AnyProcedure;
207
+ /** 未指定操作时的默认 procedure */
208
+ default?: AnyProcedure;
209
+ }
210
+ /**
211
+ * Procedure 工厂函数
212
+ * 根据操作类型动态返回 procedure
213
+ */
214
+ type ProcedureFactory = (operation: CrudOperation) => AnyProcedure;
215
+ /**
216
+ * 统一的 Procedure 配置
217
+ * 支持三种形式:
218
+ * 1. 单一 procedure - 所有操作共用
219
+ * 2. 对象映射 - 按操作指定
220
+ * 3. 工厂函数 - 动态生成
221
+ *
222
+ * @example
223
+ * // 形式 1:单一 procedure
224
+ * procedure: protectedProcedure
225
+ *
226
+ * // 形式 2:对象映射(推荐常用场景)
227
+ * procedure: {
228
+ * list: publicProcedure,
229
+ * get: publicProcedure,
230
+ * create: editorProcedure,
231
+ * delete: adminProcedure,
232
+ * default: protectedProcedure,
233
+ * }
234
+ *
235
+ * // 形式 3:工厂函数(适合复杂逻辑)
236
+ * procedure: (op) => {
237
+ * if (['list', 'get'].includes(op)) return publicProcedure;
238
+ * return protectedProcedure.meta({ permission: { action: op, subject: 'Task' } });
239
+ * }
240
+ */
241
+ type ProcedureConfig = AnyProcedure | ProcedureMap | ProcedureFactory;
242
+ /**
243
+ * CRUD Router 配置
244
+ *
245
+ * v2.0 统一 API 设计:
246
+ * - 所有配置项可自由组合,无互斥模式
247
+ * - procedure 支持多种形式(单一/映射/工厂)
248
+ * - guard/scope/authorize 可与任意 procedure 形式组合
249
+ *
250
+ * @example
251
+ * // 简单场景:公开读,保护写
252
+ * createCrudRouter({
253
+ * table: posts,
254
+ * insertSchema,
255
+ * updateSchema,
256
+ * procedure: {
257
+ * list: publicProcedure,
258
+ * get: publicProcedure,
259
+ * default: protectedProcedure,
260
+ * },
261
+ * })
262
+ *
263
+ * // 复杂场景:自定义 procedure + 行级安全
264
+ * createCrudRouter({
265
+ * table: posts,
266
+ * insertSchema,
267
+ * updateSchema,
268
+ * procedure: adminProcedure,
269
+ * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId),
270
+ * authorize: (ctx, resource) => resource.ownerId === ctx.user.id,
271
+ * })
272
+ */
273
+ interface CrudRouterConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
195
274
  /** Drizzle 表定义 */
196
275
  table: PgTable;
197
- /** 查询返回 Schema */
198
- selectSchema?: z.ZodType<TSelect>;
199
276
  /** 创建输入 Schema */
200
277
  insertSchema: z.ZodType<TInsert>;
201
278
  /** 更新输入 Schema */
202
279
  updateSchema: z.ZodType<TUpdate>;
280
+ /**
281
+ * Procedure 配置(统一字段)
282
+ *
283
+ * 支持三种形式:
284
+ * 1. 单一 procedure - 所有操作共用
285
+ * 2. 对象映射 - 按操作指定(推荐)
286
+ * 3. 工厂函数 - 动态生成
287
+ *
288
+ * 默认: publicProcedure
289
+ */
290
+ procedure?: ProcedureConfig;
291
+ /**
292
+ * 操作级守卫(RBAC)
293
+ * 在 scope 之前执行,快速拒绝无权限请求
294
+ *
295
+ * @example
296
+ * guard: (ctx, op) => op === 'delete' ? ctx.user.role === 'admin' : true
297
+ */
298
+ guard?: (ctx: TContext, operation: CrudOperation) => boolean | Promise<boolean>;
299
+ /**
300
+ * 行级过滤(RLS)
301
+ * 返回 SQL 条件,自动注入到 WHERE 子句
302
+ *
303
+ * @example
304
+ * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId)
305
+ */
306
+ scope?: (ctx: TContext, table: PgTable, operation: CrudOperation) => SQL | undefined;
307
+ /**
308
+ * 强制注入字段(写入时)
309
+ * 返回的字段会强制覆盖用户输入(防止伪造)
310
+ *
311
+ * @example
312
+ * inject: (ctx) => ({ tenantId: ctx.user.tenantId, createdBy: ctx.user.id })
313
+ */
314
+ inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
315
+ /**
316
+ * 资源级检查(ABAC)
317
+ * 在 get/update/delete 时,预取资源后调用
318
+ *
319
+ * @example
320
+ * authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
321
+ */
322
+ authorize?: (ctx: TContext, resource: TSelect, operation: CrudOperation) => boolean | Promise<boolean>;
323
+ /** 查询返回 Schema */
324
+ selectSchema?: z.ZodType<TSelect>;
203
325
  /** ID 字段名,默认 "id" */
204
326
  idField?: string;
205
327
  /** 可过滤的列白名单 */
@@ -256,90 +378,6 @@ interface CrudRouterConfigBase<TContext = unknown, TSelect = unknown, TInsert =
256
378
  */
257
379
  middleware?: CrudMiddleware<TContext, TSelect, TInsert, TUpdate>;
258
380
  }
259
- interface DeclarativeConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
260
- mode?: "declarative";
261
- /**
262
- * 基础 procedure(认证)
263
- * 所有操作共用,除非被 guard 拦截
264
- */
265
- procedure?: AnyProcedure;
266
- /**
267
- * 操作级守卫(RBAC)
268
- * 在 scope 之前执行,快速拒绝无权限请求
269
- *
270
- * @example
271
- * guard: (ctx, op) => op === 'delete' ? ctx.user.role === 'admin' : true
272
- */
273
- guard?: (ctx: TContext, operation: CrudOperation) => boolean | Promise<boolean>;
274
- /**
275
- * 行级过滤(RLS)
276
- * 返回 SQL 条件,自动注入到 WHERE 子句
277
- *
278
- * @example
279
- * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId)
280
- */
281
- scope?: (ctx: TContext, table: PgTable, operation: CrudOperation) => SQL | undefined;
282
- /**
283
- * 强制注入字段(写入时)
284
- * 返回的字段会强制覆盖用户输入(防止伪造)
285
- *
286
- * @example
287
- * inject: (ctx) => ({ tenantId: ctx.user.tenantId, createdBy: ctx.user.id })
288
- */
289
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
290
- /**
291
- * 资源级检查(ABAC)
292
- * 在 get/update/delete 时,预取资源后调用
293
- *
294
- * @example
295
- * authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
296
- */
297
- authorize?: (ctx: TContext, resource: TSelect, operation: CrudOperation) => boolean | Promise<boolean>;
298
- }
299
- interface FactoryConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
300
- mode: "factory";
301
- /**
302
- * Procedure 工厂函数
303
- * 根据操作类型返回对应的 procedure
304
- *
305
- * @example
306
- * procedureFactory: (op) => {
307
- * if (['list', 'get'].includes(op)) return publicProcedure;
308
- * return protectedProcedure.meta({ permission: { action: op, subject: 'Task' } });
309
- * }
310
- */
311
- procedureFactory: (operation: CrudOperation) => AnyProcedure;
312
- /**
313
- * 强制注入字段(可选)
314
- * 如果 DB 层已处理则不需要
315
- */
316
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
317
- }
318
- interface ProceduresConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
319
- mode: "procedures";
320
- /**
321
- * 按操作指定不同的 procedure
322
- *
323
- * @example
324
- * procedures: {
325
- * list: publicProcedure,
326
- * get: publicProcedure,
327
- * create: editorProcedure,
328
- * update: editorProcedure,
329
- * delete: adminProcedure,
330
- * }
331
- */
332
- procedures: Partial<Record<CrudOperation, AnyProcedure>>;
333
- /**
334
- * 默认 procedure(未指定操作时使用)
335
- */
336
- defaultProcedure?: AnyProcedure;
337
- /**
338
- * 强制注入字段(可选)
339
- */
340
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
341
- }
342
- 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>;
343
381
  interface CrudProcedures<TSelect, TInsert, TUpdate> {
344
382
  list: AnyProcedure;
345
383
  get: AnyProcedure;
@@ -430,4 +468,4 @@ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
430
468
  }, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
431
469
  type AppRouter = typeof appRouter;
432
470
  //#endregion
433
- 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 };
471
+ export { type AnyProcedure, type AppRouter, type CreateMiddlewareParams, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type GetMiddlewareParams, type ListInput, type ListMiddlewareParams, type ListResult, type ProcedureConfig, type ProcedureFactory, type ProcedureMap, type SoftDeleteConfig, type SoftDeleteOption, type UpdateManyMiddlewareParams, type UpdateMiddlewareParams, type UpsertMiddlewareParams, type WriteOperation, afterMiddleware, appRouter, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
package/dist/index.d.ts CHANGED
@@ -192,15 +192,137 @@ interface CrudMiddleware<TContext = unknown, TSelect = unknown, TInsert = unknow
192
192
  isNew: boolean;
193
193
  }>;
194
194
  }
195
- interface CrudRouterConfigBase<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
195
+ /**
196
+ * Procedure 映射对象
197
+ * 按操作指定不同的 procedure,支持 default 回退
198
+ */
199
+ interface ProcedureMap {
200
+ list?: AnyProcedure;
201
+ get?: AnyProcedure;
202
+ create?: AnyProcedure;
203
+ update?: AnyProcedure;
204
+ delete?: AnyProcedure;
205
+ deleteMany?: AnyProcedure;
206
+ updateMany?: AnyProcedure;
207
+ upsert?: AnyProcedure;
208
+ /** 未指定操作时的默认 procedure */
209
+ default?: AnyProcedure;
210
+ }
211
+ /**
212
+ * Procedure 工厂函数
213
+ * 根据操作类型动态返回 procedure
214
+ */
215
+ type ProcedureFactory = (operation: CrudOperation) => AnyProcedure;
216
+ /**
217
+ * 统一的 Procedure 配置
218
+ * 支持三种形式:
219
+ * 1. 单一 procedure - 所有操作共用
220
+ * 2. 对象映射 - 按操作指定
221
+ * 3. 工厂函数 - 动态生成
222
+ *
223
+ * @example
224
+ * // 形式 1:单一 procedure
225
+ * procedure: protectedProcedure
226
+ *
227
+ * // 形式 2:对象映射(推荐常用场景)
228
+ * procedure: {
229
+ * list: publicProcedure,
230
+ * get: publicProcedure,
231
+ * create: editorProcedure,
232
+ * delete: adminProcedure,
233
+ * default: protectedProcedure,
234
+ * }
235
+ *
236
+ * // 形式 3:工厂函数(适合复杂逻辑)
237
+ * procedure: (op) => {
238
+ * if (['list', 'get'].includes(op)) return publicProcedure;
239
+ * return protectedProcedure.meta({ permission: { action: op, subject: 'Task' } });
240
+ * }
241
+ */
242
+ type ProcedureConfig = AnyProcedure | ProcedureMap | ProcedureFactory;
243
+ /**
244
+ * CRUD Router 配置
245
+ *
246
+ * v2.0 统一 API 设计:
247
+ * - 所有配置项可自由组合,无互斥模式
248
+ * - procedure 支持多种形式(单一/映射/工厂)
249
+ * - guard/scope/authorize 可与任意 procedure 形式组合
250
+ *
251
+ * @example
252
+ * // 简单场景:公开读,保护写
253
+ * createCrudRouter({
254
+ * table: posts,
255
+ * insertSchema,
256
+ * updateSchema,
257
+ * procedure: {
258
+ * list: publicProcedure,
259
+ * get: publicProcedure,
260
+ * default: protectedProcedure,
261
+ * },
262
+ * })
263
+ *
264
+ * // 复杂场景:自定义 procedure + 行级安全
265
+ * createCrudRouter({
266
+ * table: posts,
267
+ * insertSchema,
268
+ * updateSchema,
269
+ * procedure: adminProcedure,
270
+ * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId),
271
+ * authorize: (ctx, resource) => resource.ownerId === ctx.user.id,
272
+ * })
273
+ */
274
+ interface CrudRouterConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> {
196
275
  /** Drizzle 表定义 */
197
276
  table: PgTable;
198
- /** 查询返回 Schema */
199
- selectSchema?: z.ZodType<TSelect>;
200
277
  /** 创建输入 Schema */
201
278
  insertSchema: z.ZodType<TInsert>;
202
279
  /** 更新输入 Schema */
203
280
  updateSchema: z.ZodType<TUpdate>;
281
+ /**
282
+ * Procedure 配置(统一字段)
283
+ *
284
+ * 支持三种形式:
285
+ * 1. 单一 procedure - 所有操作共用
286
+ * 2. 对象映射 - 按操作指定(推荐)
287
+ * 3. 工厂函数 - 动态生成
288
+ *
289
+ * 默认: publicProcedure
290
+ */
291
+ procedure?: ProcedureConfig;
292
+ /**
293
+ * 操作级守卫(RBAC)
294
+ * 在 scope 之前执行,快速拒绝无权限请求
295
+ *
296
+ * @example
297
+ * guard: (ctx, op) => op === 'delete' ? ctx.user.role === 'admin' : true
298
+ */
299
+ guard?: (ctx: TContext, operation: CrudOperation) => boolean | Promise<boolean>;
300
+ /**
301
+ * 行级过滤(RLS)
302
+ * 返回 SQL 条件,自动注入到 WHERE 子句
303
+ *
304
+ * @example
305
+ * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId)
306
+ */
307
+ scope?: (ctx: TContext, table: PgTable, operation: CrudOperation) => SQL | undefined;
308
+ /**
309
+ * 强制注入字段(写入时)
310
+ * 返回的字段会强制覆盖用户输入(防止伪造)
311
+ *
312
+ * @example
313
+ * inject: (ctx) => ({ tenantId: ctx.user.tenantId, createdBy: ctx.user.id })
314
+ */
315
+ inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
316
+ /**
317
+ * 资源级检查(ABAC)
318
+ * 在 get/update/delete 时,预取资源后调用
319
+ *
320
+ * @example
321
+ * authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
322
+ */
323
+ authorize?: (ctx: TContext, resource: TSelect, operation: CrudOperation) => boolean | Promise<boolean>;
324
+ /** 查询返回 Schema */
325
+ selectSchema?: z.ZodType<TSelect>;
204
326
  /** ID 字段名,默认 "id" */
205
327
  idField?: string;
206
328
  /** 可过滤的列白名单 */
@@ -257,90 +379,6 @@ interface CrudRouterConfigBase<TContext = unknown, TSelect = unknown, TInsert =
257
379
  */
258
380
  middleware?: CrudMiddleware<TContext, TSelect, TInsert, TUpdate>;
259
381
  }
260
- interface DeclarativeConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
261
- mode?: "declarative";
262
- /**
263
- * 基础 procedure(认证)
264
- * 所有操作共用,除非被 guard 拦截
265
- */
266
- procedure?: AnyProcedure;
267
- /**
268
- * 操作级守卫(RBAC)
269
- * 在 scope 之前执行,快速拒绝无权限请求
270
- *
271
- * @example
272
- * guard: (ctx, op) => op === 'delete' ? ctx.user.role === 'admin' : true
273
- */
274
- guard?: (ctx: TContext, operation: CrudOperation) => boolean | Promise<boolean>;
275
- /**
276
- * 行级过滤(RLS)
277
- * 返回 SQL 条件,自动注入到 WHERE 子句
278
- *
279
- * @example
280
- * scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId)
281
- */
282
- scope?: (ctx: TContext, table: PgTable, operation: CrudOperation) => SQL | undefined;
283
- /**
284
- * 强制注入字段(写入时)
285
- * 返回的字段会强制覆盖用户输入(防止伪造)
286
- *
287
- * @example
288
- * inject: (ctx) => ({ tenantId: ctx.user.tenantId, createdBy: ctx.user.id })
289
- */
290
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
291
- /**
292
- * 资源级检查(ABAC)
293
- * 在 get/update/delete 时,预取资源后调用
294
- *
295
- * @example
296
- * authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
297
- */
298
- authorize?: (ctx: TContext, resource: TSelect, operation: CrudOperation) => boolean | Promise<boolean>;
299
- }
300
- interface FactoryConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
301
- mode: "factory";
302
- /**
303
- * Procedure 工厂函数
304
- * 根据操作类型返回对应的 procedure
305
- *
306
- * @example
307
- * procedureFactory: (op) => {
308
- * if (['list', 'get'].includes(op)) return publicProcedure;
309
- * return protectedProcedure.meta({ permission: { action: op, subject: 'Task' } });
310
- * }
311
- */
312
- procedureFactory: (operation: CrudOperation) => AnyProcedure;
313
- /**
314
- * 强制注入字段(可选)
315
- * 如果 DB 层已处理则不需要
316
- */
317
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
318
- }
319
- interface ProceduresConfig<TContext = unknown, TSelect = unknown, TInsert = unknown, TUpdate = unknown> extends CrudRouterConfigBase<TContext, TSelect, TInsert, TUpdate> {
320
- mode: "procedures";
321
- /**
322
- * 按操作指定不同的 procedure
323
- *
324
- * @example
325
- * procedures: {
326
- * list: publicProcedure,
327
- * get: publicProcedure,
328
- * create: editorProcedure,
329
- * update: editorProcedure,
330
- * delete: adminProcedure,
331
- * }
332
- */
333
- procedures: Partial<Record<CrudOperation, AnyProcedure>>;
334
- /**
335
- * 默认 procedure(未指定操作时使用)
336
- */
337
- defaultProcedure?: AnyProcedure;
338
- /**
339
- * 强制注入字段(可选)
340
- */
341
- inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
342
- }
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
382
  interface CrudProcedures<TSelect, TInsert, TUpdate> {
345
383
  list: AnyProcedure;
346
384
  get: AnyProcedure;
@@ -431,4 +469,4 @@ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
431
469
  }, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
432
470
  type AppRouter = typeof appRouter;
433
471
  //#endregion
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 };
472
+ export { type AnyProcedure, type AppRouter, type CreateMiddlewareParams, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type GetMiddlewareParams, type ListInput, type ListMiddlewareParams, type ListResult, type ProcedureConfig, type ProcedureFactory, type ProcedureMap, type SoftDeleteConfig, type SoftDeleteOption, type UpdateManyMiddlewareParams, type UpdateMiddlewareParams, type UpsertMiddlewareParams, type WriteOperation, afterMiddleware, appRouter, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
package/dist/index.js CHANGED
@@ -389,38 +389,53 @@ function resolveSoftDelete(option) {
389
389
  getValue: config.value ?? (() => /* @__PURE__ */ new Date())
390
390
  };
391
391
  }
392
- function resolveConfig(config) {
393
- const mode = config.mode || "declarative";
394
- if (mode === "factory") {
395
- const factoryConfig = config;
396
- return {
397
- procedureFactory: factoryConfig.procedureFactory,
398
- guard: void 0,
399
- getScope: () => void 0,
400
- getInject: (ctx, op) => factoryConfig.inject?.(ctx, op) ?? {},
401
- checkAuthorize: async () => true
402
- };
392
+ /**
393
+ * 判断 procedure 配置的类型
394
+ */
395
+ function isProcedureMap(config) {
396
+ if (!config) return false;
397
+ if (typeof config === "function") return false;
398
+ if (typeof config === "object" && config !== null) {
399
+ const keys = Object.keys(config);
400
+ const validKeys = [
401
+ "list",
402
+ "get",
403
+ "create",
404
+ "update",
405
+ "delete",
406
+ "deleteMany",
407
+ "updateMany",
408
+ "upsert",
409
+ "default"
410
+ ];
411
+ return keys.some((k) => validKeys.includes(k));
403
412
  }
404
- if (mode === "procedures") {
405
- const proceduresConfig = config;
406
- const defaultProc = proceduresConfig.defaultProcedure ?? publicProcedure;
407
- return {
408
- procedureFactory: (op) => proceduresConfig.procedures[op] ?? defaultProc,
409
- guard: void 0,
410
- getScope: () => void 0,
411
- getInject: (ctx, op) => proceduresConfig.inject?.(ctx, op) ?? {},
412
- checkAuthorize: async () => true
413
- };
413
+ return false;
414
+ }
415
+ function isProcedureFactory(config) {
416
+ return typeof config === "function";
417
+ }
418
+ /**
419
+ * 解析 procedure 配置为工厂函数
420
+ */
421
+ function resolveProcedureFactory(config) {
422
+ if (!config) return () => publicProcedure;
423
+ if (isProcedureFactory(config)) return config;
424
+ if (isProcedureMap(config)) {
425
+ const map = config;
426
+ const defaultProc = map.default ?? publicProcedure;
427
+ return (op) => map[op] ?? defaultProc;
414
428
  }
415
- const declarativeConfig = config;
416
- const baseProcedure = declarativeConfig.procedure ?? publicProcedure;
429
+ return () => config;
430
+ }
431
+ function resolveConfig(config) {
417
432
  return {
418
- procedureFactory: () => baseProcedure,
419
- guard: declarativeConfig.guard,
420
- getScope: (ctx, tbl, op) => declarativeConfig.scope?.(ctx, tbl, op),
421
- getInject: (ctx, op) => declarativeConfig.inject?.(ctx, op) ?? {},
433
+ procedureFactory: resolveProcedureFactory(config.procedure),
434
+ guard: config.guard,
435
+ getScope: (ctx, tbl, op) => config.scope?.(ctx, tbl, op),
436
+ getInject: (ctx, op) => config.inject?.(ctx, op) ?? {},
422
437
  checkAuthorize: async (ctx, resource, op) => {
423
- if (declarativeConfig.authorize) return declarativeConfig.authorize(ctx, resource, op);
438
+ if (config.authorize) return config.authorize(ctx, resource, op);
424
439
  return true;
425
440
  }
426
441
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wordrhyme/auto-crud-server",
3
3
  "type": "module",
4
- "version": "0.6.2",
4
+ "version": "0.8.0",
5
5
  "description": "tRPC server utilities for auto-crud - automatic CRUD routers for Drizzle ORM",
6
6
  "author": "wordrhyme",
7
7
  "license": "MIT",
@@ -47,11 +47,11 @@
47
47
  "typescript": "^5.9.3",
48
48
  "vitest": "^4.0.18",
49
49
  "zod": "^4.1.13",
50
+ "@internal/eslint-config": "0.3.0",
50
51
  "@internal/prettier-config": "0.0.1",
51
52
  "@internal/tsconfig": "0.1.0",
52
- "@internal/tsdown-config": "0.1.0",
53
53
  "@internal/vitest-config": "0.1.0",
54
- "@internal/eslint-config": "0.3.0"
54
+ "@internal/tsdown-config": "0.1.0"
55
55
  },
56
56
  "prettier": "@internal/prettier-config",
57
57
  "scripts": {