@wordrhyme/auto-crud-server 0.2.0 → 0.4.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 +94 -35
- package/dist/index.cjs +188 -62
- package/dist/index.d.cts +293 -127
- package/dist/index.d.ts +283 -116
- package/dist/index.js +188 -62
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { SQL } from "drizzle-orm";
|
|
2
3
|
import * as _trpc_server2 from "@trpc/server";
|
|
3
4
|
import superjson from "superjson";
|
|
4
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";
|
|
5
7
|
import { PgTable } from "drizzle-orm/pg-core";
|
|
6
8
|
|
|
7
9
|
//#region src/trpc.d.ts
|
|
@@ -12,14 +14,6 @@ import { PgTable } from "drizzle-orm/pg-core";
|
|
|
12
14
|
interface Context {
|
|
13
15
|
db: PostgresJsDatabase<Record<string, never>>;
|
|
14
16
|
}
|
|
15
|
-
declare const t: _trpc_server2.TRPCRootObject<Context, object, {
|
|
16
|
-
transformer: typeof superjson;
|
|
17
|
-
}, {
|
|
18
|
-
ctx: Context;
|
|
19
|
-
meta: object;
|
|
20
|
-
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
21
|
-
transformer: true;
|
|
22
|
-
}>;
|
|
23
17
|
declare const router: _trpc_server2.TRPCRouterBuilder<{
|
|
24
18
|
ctx: Context;
|
|
25
19
|
meta: object;
|
|
@@ -27,138 +21,311 @@ declare const router: _trpc_server2.TRPCRouterBuilder<{
|
|
|
27
21
|
transformer: true;
|
|
28
22
|
}>;
|
|
29
23
|
declare const publicProcedure: _trpc_server2.TRPCProcedureBuilder<Context, object, object, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, false>;
|
|
30
|
-
type AnyProcedure = typeof t.procedure;
|
|
31
24
|
//#endregion
|
|
32
|
-
//#region src/
|
|
25
|
+
//#region src/types/config.d.ts
|
|
26
|
+
type CrudOperation = "list" | "get" | "create" | "update" | "delete" | "deleteMany" | "updateMany";
|
|
27
|
+
type WriteOperation = "create" | "update";
|
|
28
|
+
type AnyProcedure = any;
|
|
29
|
+
interface SoftDeleteConfig {
|
|
30
|
+
/** 软删除标记列名 */
|
|
31
|
+
column: string;
|
|
32
|
+
/**
|
|
33
|
+
* 删除时设置的值
|
|
34
|
+
* - 默认: () => new Date()
|
|
35
|
+
* - 可自定义为其他值,如 true(用于 isDeleted 布尔字段)
|
|
36
|
+
*/
|
|
37
|
+
value?: () => unknown;
|
|
38
|
+
}
|
|
33
39
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
40
|
+
* 软删除配置类型
|
|
41
|
+
* - string: 列名(使用默认值 new Date())
|
|
42
|
+
* - boolean: true 表示使用默认列名 'deletedAt'
|
|
43
|
+
* - SoftDeleteConfig: 完整配置
|
|
36
44
|
*/
|
|
37
|
-
type
|
|
38
|
-
operation: "list" | "get" | "create" | "update" | "delete" | "deleteMany" | "updateMany";
|
|
39
|
-
input?: unknown;
|
|
40
|
-
}) => boolean | Promise<boolean>;
|
|
45
|
+
type SoftDeleteOption = string | boolean | SoftDeleteConfig;
|
|
41
46
|
/**
|
|
42
|
-
*
|
|
47
|
+
* 列表查询输入类型
|
|
43
48
|
*/
|
|
44
|
-
interface
|
|
49
|
+
interface ListInput {
|
|
50
|
+
page: number;
|
|
51
|
+
perPage: number;
|
|
52
|
+
sort?: Array<{
|
|
53
|
+
id: string;
|
|
54
|
+
desc: boolean;
|
|
55
|
+
}>;
|
|
56
|
+
filters?: Array<{
|
|
57
|
+
id: string;
|
|
58
|
+
value: string | string[];
|
|
59
|
+
variant: string;
|
|
60
|
+
operator: string;
|
|
61
|
+
filterId: string;
|
|
62
|
+
}>;
|
|
63
|
+
joinOperator: "and" | "or";
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 列表查询结果类型
|
|
67
|
+
*/
|
|
68
|
+
interface ListResult<TSelect> {
|
|
69
|
+
data: TSelect[];
|
|
70
|
+
total: number;
|
|
71
|
+
page: number;
|
|
72
|
+
perPage: number;
|
|
73
|
+
pageCount: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* CRUD 生命周期钩子
|
|
77
|
+
*
|
|
78
|
+
* @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);
|
|
87
|
+
* },
|
|
88
|
+
* }
|
|
89
|
+
*/
|
|
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>;
|
|
115
|
+
/**
|
|
116
|
+
* 创建后
|
|
117
|
+
* 用于副作用(日志、通知、同步等)
|
|
118
|
+
*/
|
|
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: {
|
|
146
|
+
deleted: number;
|
|
147
|
+
}) => void | Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* 批量更新前
|
|
150
|
+
* 可修改输入数据
|
|
151
|
+
*/
|
|
152
|
+
beforeUpdateMany?: <TUpdate>(ctx: TContext, ids: string[], data: TUpdate) => TUpdate | void | Promise<TUpdate | void>;
|
|
153
|
+
/**
|
|
154
|
+
* 批量更新后
|
|
155
|
+
*/
|
|
156
|
+
afterUpdateMany?: (ctx: TContext, result: {
|
|
157
|
+
updated: number;
|
|
158
|
+
}) => void | Promise<void>;
|
|
159
|
+
}
|
|
160
|
+
interface CrudRouterConfigBase<TContext = unknown> {
|
|
161
|
+
/** Drizzle 表定义 */
|
|
45
162
|
table: PgTable;
|
|
46
|
-
|
|
163
|
+
/** 查询返回 Schema */
|
|
164
|
+
selectSchema?: z.ZodType;
|
|
165
|
+
/** 创建输入 Schema */
|
|
47
166
|
insertSchema: z.ZodType;
|
|
167
|
+
/** 更新输入 Schema */
|
|
48
168
|
updateSchema: z.ZodType;
|
|
169
|
+
/** ID 字段名,默认 "id" */
|
|
49
170
|
idField?: string;
|
|
50
|
-
/**
|
|
51
|
-
* 可过滤的列白名单
|
|
52
|
-
* 未指定时默认允许所有列(不安全,仅用于开发)
|
|
53
|
-
*/
|
|
171
|
+
/** 可过滤的列白名单 */
|
|
54
172
|
filterableColumns?: string[];
|
|
173
|
+
/** 可排序的列白名单 */
|
|
174
|
+
sortableColumns?: string[];
|
|
175
|
+
/** 批量操作的最大数量限制,默认 100 */
|
|
176
|
+
maxBatchSize?: number;
|
|
55
177
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
178
|
+
* 软删除配置
|
|
179
|
+
* 开启后:
|
|
180
|
+
* - delete 操作 → UPDATE ... SET {column} = {value}
|
|
181
|
+
* - deleteMany 操作 → UPDATE ... SET {column} = {value} WHERE id IN (...)
|
|
182
|
+
* - list/get 操作 → 自动添加 WHERE {column} IS NULL
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* // 使用默认列名 'deletedAt'
|
|
186
|
+
* softDelete: true
|
|
187
|
+
*
|
|
188
|
+
* // 指定列名
|
|
189
|
+
* softDelete: 'deletedAt'
|
|
190
|
+
*
|
|
191
|
+
* // 完整配置(用于布尔字段)
|
|
192
|
+
* softDelete: { column: 'isDeleted', value: () => true }
|
|
58
193
|
*/
|
|
59
|
-
|
|
194
|
+
softDelete?: SoftDeleteOption;
|
|
60
195
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
196
|
+
* 生命周期钩子
|
|
197
|
+
* 在 CRUD 操作前后注入自定义逻辑
|
|
198
|
+
*
|
|
199
|
+
* @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);
|
|
206
|
+
* },
|
|
207
|
+
* }
|
|
63
208
|
*/
|
|
64
|
-
|
|
209
|
+
hooks?: CrudHooks<TContext>;
|
|
210
|
+
}
|
|
211
|
+
interface DeclarativeConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
|
|
212
|
+
mode?: "declarative";
|
|
65
213
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
214
|
+
* 基础 procedure(认证)
|
|
215
|
+
* 所有操作共用,除非被 guard 拦截
|
|
68
216
|
*/
|
|
69
217
|
procedure?: AnyProcedure;
|
|
70
218
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
219
|
+
* 操作级守卫(RBAC)
|
|
220
|
+
* 在 scope 之前执行,快速拒绝无权限请求
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* guard: (ctx, op) => op === 'delete' ? ctx.user.role === 'admin' : true
|
|
224
|
+
*/
|
|
225
|
+
guard?: (ctx: TContext, operation: CrudOperation) => boolean | Promise<boolean>;
|
|
226
|
+
/**
|
|
227
|
+
* 行级过滤(RLS)
|
|
228
|
+
* 返回 SQL 条件,自动注入到 WHERE 子句
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* scope: (ctx, table) => eq(table.tenantId, ctx.user.tenantId)
|
|
232
|
+
*/
|
|
233
|
+
scope?: (ctx: TContext, table: PgTable, operation: CrudOperation) => SQL | undefined;
|
|
234
|
+
/**
|
|
235
|
+
* 强制注入字段(写入时)
|
|
236
|
+
* 返回的字段会强制覆盖用户输入(防止伪造)
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* inject: (ctx) => ({ tenantId: ctx.user.tenantId, createdBy: ctx.user.id })
|
|
73
240
|
*/
|
|
74
|
-
|
|
241
|
+
inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
|
|
242
|
+
/**
|
|
243
|
+
* 资源级检查(ABAC)
|
|
244
|
+
* 在 get/update/delete 时,预取资源后调用
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* authorize: (ctx, resource, op) => resource.ownerId === ctx.user.id
|
|
248
|
+
*/
|
|
249
|
+
authorize?: (ctx: TContext, resource: Record<string, unknown>, operation: CrudOperation) => boolean | Promise<boolean>;
|
|
75
250
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
251
|
+
interface FactoryConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
|
|
252
|
+
mode: "factory";
|
|
253
|
+
/**
|
|
254
|
+
* Procedure 工厂函数
|
|
255
|
+
* 根据操作类型返回对应的 procedure
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* procedureFactory: (op) => {
|
|
259
|
+
* if (['list', 'get'].includes(op)) return publicProcedure;
|
|
260
|
+
* return protectedProcedure.meta({ permission: { action: op, subject: 'Task' } });
|
|
261
|
+
* }
|
|
262
|
+
*/
|
|
263
|
+
procedureFactory: (operation: CrudOperation) => AnyProcedure;
|
|
264
|
+
/**
|
|
265
|
+
* 强制注入字段(可选)
|
|
266
|
+
* 如果 DB 层已处理则不需要
|
|
267
|
+
*/
|
|
268
|
+
inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
|
|
269
|
+
}
|
|
270
|
+
interface ProceduresConfig<TContext = unknown> extends CrudRouterConfigBase<TContext> {
|
|
271
|
+
mode: "procedures";
|
|
272
|
+
/**
|
|
273
|
+
* 按操作指定不同的 procedure
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* procedures: {
|
|
277
|
+
* list: publicProcedure,
|
|
278
|
+
* get: publicProcedure,
|
|
279
|
+
* create: editorProcedure,
|
|
280
|
+
* update: editorProcedure,
|
|
281
|
+
* delete: adminProcedure,
|
|
282
|
+
* }
|
|
283
|
+
*/
|
|
284
|
+
procedures: Partial<Record<CrudOperation, AnyProcedure>>;
|
|
285
|
+
/**
|
|
286
|
+
* 默认 procedure(未指定操作时使用)
|
|
287
|
+
*/
|
|
288
|
+
defaultProcedure?: AnyProcedure;
|
|
289
|
+
/**
|
|
290
|
+
* 强制注入字段(可选)
|
|
291
|
+
*/
|
|
292
|
+
inject?: (ctx: TContext, operation: WriteOperation) => Record<string, unknown>;
|
|
293
|
+
}
|
|
294
|
+
type CrudRouterConfig<TContext = unknown> = DeclarativeConfig<TContext> | FactoryConfig<TContext> | ProceduresConfig<TContext>;
|
|
295
|
+
//#endregion
|
|
296
|
+
//#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<{
|
|
80
298
|
ctx: Context;
|
|
81
299
|
meta: object;
|
|
82
300
|
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
83
301
|
transformer: true;
|
|
84
302
|
}, _trpc_server2.TRPCDecorateCreateRouterOptions<{
|
|
85
|
-
list:
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
meta: object;
|
|
112
|
-
}>;
|
|
113
|
-
getById: _trpc_server2.TRPCQueryProcedure<{
|
|
114
|
-
input: string;
|
|
115
|
-
output: {
|
|
116
|
-
[x: string]: unknown;
|
|
117
|
-
} | null;
|
|
118
|
-
meta: object;
|
|
119
|
-
}>;
|
|
120
|
-
create: _trpc_server2.TRPCMutationProcedure<{
|
|
121
|
-
input: unknown;
|
|
122
|
-
output: {
|
|
123
|
-
[x: string]: unknown;
|
|
124
|
-
} | undefined;
|
|
125
|
-
meta: object;
|
|
126
|
-
}>;
|
|
127
|
-
update: _trpc_server2.TRPCMutationProcedure<{
|
|
128
|
-
input: {
|
|
129
|
-
id: string;
|
|
130
|
-
data: unknown;
|
|
131
|
-
};
|
|
132
|
-
output: {
|
|
133
|
-
[x: string]: unknown;
|
|
134
|
-
} | undefined;
|
|
135
|
-
meta: object;
|
|
136
|
-
}>;
|
|
137
|
-
delete: _trpc_server2.TRPCMutationProcedure<{
|
|
138
|
-
input: string;
|
|
139
|
-
output: {
|
|
140
|
-
[x: string]: unknown;
|
|
141
|
-
} | undefined;
|
|
142
|
-
meta: object;
|
|
143
|
-
}>;
|
|
144
|
-
deleteMany: _trpc_server2.TRPCMutationProcedure<{
|
|
145
|
-
input: string[];
|
|
146
|
-
output: {
|
|
147
|
-
deleted: number;
|
|
148
|
-
};
|
|
149
|
-
meta: object;
|
|
150
|
-
}>;
|
|
151
|
-
updateMany: _trpc_server2.TRPCMutationProcedure<{
|
|
152
|
-
input: {
|
|
153
|
-
ids: string[];
|
|
154
|
-
data: unknown;
|
|
155
|
-
};
|
|
156
|
-
output: {
|
|
157
|
-
updated: number;
|
|
158
|
-
};
|
|
159
|
-
meta: object;
|
|
160
|
-
}>;
|
|
161
|
-
}>>;
|
|
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
|
+
};
|
|
328
|
+
};
|
|
162
329
|
//#endregion
|
|
163
330
|
//#region src/routers/index.d.ts
|
|
164
331
|
declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
|
|
@@ -169,4 +336,4 @@ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
|
|
|
169
336
|
}, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
|
|
170
337
|
type AppRouter = typeof appRouter;
|
|
171
338
|
//#endregion
|
|
172
|
-
export { type AnyProcedure, type AppRouter, type
|
|
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 };
|