@wordrhyme/auto-crud-server 0.2.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 ADDED
@@ -0,0 +1,529 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc$1) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc$1 = __getOwnPropDesc(from, key)) || desc$1.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ let zod = require("zod");
25
+ zod = __toESM(zod);
26
+ let drizzle_orm = require("drizzle-orm");
27
+ drizzle_orm = __toESM(drizzle_orm);
28
+ let __trpc_server = require("@trpc/server");
29
+ __trpc_server = __toESM(__trpc_server);
30
+ let superjson = require("superjson");
31
+ superjson = __toESM(superjson);
32
+ let date_fns = require("date-fns");
33
+ date_fns = __toESM(date_fns);
34
+
35
+ //#region src/trpc.ts
36
+ const t = __trpc_server.initTRPC.context().create({ transformer: superjson.default });
37
+ const router = t.router;
38
+ const publicProcedure = t.procedure;
39
+
40
+ //#endregion
41
+ //#region src/lib/filter-columns.ts
42
+ /**
43
+ * 检查列是否为空
44
+ */
45
+ function isEmpty(column) {
46
+ return (0, drizzle_orm.or)((0, drizzle_orm.isNull)(column), (0, drizzle_orm.eq)(column, ""));
47
+ }
48
+ /**
49
+ * 安全解析日期时间戳
50
+ * @returns Date 对象,如果解析失败返回 null
51
+ */
52
+ function safeParseDate(value) {
53
+ if (value === void 0 || value === null || value === "") return null;
54
+ const timestamp = typeof value === "string" ? Number(value) : value;
55
+ if (Number.isNaN(timestamp)) return null;
56
+ const date = new Date(timestamp);
57
+ if (Number.isNaN(date.getTime())) return null;
58
+ return date;
59
+ }
60
+ /**
61
+ * 安全解析数字
62
+ * @returns number,如果解析失败返回 null
63
+ */
64
+ function safeParseNumber(value) {
65
+ if (value === void 0 || value === null || value.trim() === "") return null;
66
+ const num = Number(value);
67
+ return Number.isNaN(num) ? null : num;
68
+ }
69
+ function filterColumns({ table, filters, joinOperator }) {
70
+ const joinFn = joinOperator === "and" ? drizzle_orm.and : drizzle_orm.or;
71
+ const validConditions = filters.map((filter) => {
72
+ const column = getColumn(table, filter.id);
73
+ switch (filter.operator) {
74
+ case "iLike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.ilike)(column, `%${filter.value}%`) : void 0;
75
+ case "notILike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.notIlike)(column, `%${filter.value}%`) : void 0;
76
+ case "eq":
77
+ if (column.dataType === "boolean" && typeof filter.value === "string") return (0, drizzle_orm.eq)(column, filter.value === "true");
78
+ if (filter.variant === "dateRange" && Array.isArray(filter.value) && filter.value.length === 2) {
79
+ const startDate = safeParseDate(filter.value[0]);
80
+ const endDate = safeParseDate(filter.value[1]);
81
+ if (!startDate && !endDate) return void 0;
82
+ if (startDate) startDate.setHours(0, 0, 0, 0);
83
+ if (endDate) endDate.setHours(23, 59, 59, 999);
84
+ return (0, drizzle_orm.and)(startDate ? (0, drizzle_orm.gte)(column, startDate) : void 0, endDate ? (0, drizzle_orm.lte)(column, endDate) : void 0);
85
+ }
86
+ if (filter.variant === "date" && typeof filter.value === "string") {
87
+ const date = safeParseDate(filter.value);
88
+ if (!date) return void 0;
89
+ date.setHours(0, 0, 0, 0);
90
+ const end = new Date(date);
91
+ end.setHours(23, 59, 59, 999);
92
+ return (0, drizzle_orm.and)((0, drizzle_orm.gte)(column, date), (0, drizzle_orm.lte)(column, end));
93
+ }
94
+ return (0, drizzle_orm.eq)(column, filter.value);
95
+ case "ne":
96
+ if (column.dataType === "boolean" && typeof filter.value === "string") return (0, drizzle_orm.ne)(column, filter.value === "true");
97
+ if (filter.variant === "date" || filter.variant === "dateRange") {
98
+ const date = safeParseDate(filter.value);
99
+ if (!date) return void 0;
100
+ date.setHours(0, 0, 0, 0);
101
+ const end = new Date(date);
102
+ end.setHours(23, 59, 59, 999);
103
+ return (0, drizzle_orm.or)((0, drizzle_orm.lt)(column, date), (0, drizzle_orm.gt)(column, end));
104
+ }
105
+ return (0, drizzle_orm.ne)(column, filter.value);
106
+ case "inArray":
107
+ if (Array.isArray(filter.value)) return (0, drizzle_orm.inArray)(column, filter.value);
108
+ return;
109
+ case "notInArray":
110
+ if (Array.isArray(filter.value)) return (0, drizzle_orm.notInArray)(column, filter.value);
111
+ return;
112
+ case "lt":
113
+ if (filter.variant === "number" || filter.variant === "range") {
114
+ const num = safeParseNumber(filter.value);
115
+ return num !== null ? (0, drizzle_orm.lt)(column, num) : void 0;
116
+ }
117
+ if (filter.variant === "date" && typeof filter.value === "string") {
118
+ const date = safeParseDate(filter.value);
119
+ if (!date) return void 0;
120
+ date.setHours(23, 59, 59, 999);
121
+ return (0, drizzle_orm.lt)(column, date);
122
+ }
123
+ return;
124
+ case "lte":
125
+ if (filter.variant === "number" || filter.variant === "range") {
126
+ const num = safeParseNumber(filter.value);
127
+ return num !== null ? (0, drizzle_orm.lte)(column, num) : void 0;
128
+ }
129
+ if (filter.variant === "date" && typeof filter.value === "string") {
130
+ const date = safeParseDate(filter.value);
131
+ if (!date) return void 0;
132
+ date.setHours(23, 59, 59, 999);
133
+ return (0, drizzle_orm.lte)(column, date);
134
+ }
135
+ return;
136
+ case "gt":
137
+ if (filter.variant === "number" || filter.variant === "range") {
138
+ const num = safeParseNumber(filter.value);
139
+ return num !== null ? (0, drizzle_orm.gt)(column, num) : void 0;
140
+ }
141
+ if (filter.variant === "date" && typeof filter.value === "string") {
142
+ const date = safeParseDate(filter.value);
143
+ if (!date) return void 0;
144
+ date.setHours(0, 0, 0, 0);
145
+ return (0, drizzle_orm.gt)(column, date);
146
+ }
147
+ return;
148
+ case "gte":
149
+ if (filter.variant === "number" || filter.variant === "range") {
150
+ const num = safeParseNumber(filter.value);
151
+ return num !== null ? (0, drizzle_orm.gte)(column, num) : void 0;
152
+ }
153
+ if (filter.variant === "date" && typeof filter.value === "string") {
154
+ const date = safeParseDate(filter.value);
155
+ if (!date) return void 0;
156
+ date.setHours(0, 0, 0, 0);
157
+ return (0, drizzle_orm.gte)(column, date);
158
+ }
159
+ return;
160
+ case "isBetween":
161
+ if ((filter.variant === "date" || filter.variant === "dateRange") && Array.isArray(filter.value) && filter.value.length === 2) {
162
+ const startDate = safeParseDate(filter.value[0]);
163
+ const endDate = safeParseDate(filter.value[1]);
164
+ if (!startDate && !endDate) return void 0;
165
+ if (startDate) startDate.setHours(0, 0, 0, 0);
166
+ if (endDate) endDate.setHours(23, 59, 59, 999);
167
+ return (0, drizzle_orm.and)(startDate ? (0, drizzle_orm.gte)(column, startDate) : void 0, endDate ? (0, drizzle_orm.lte)(column, endDate) : void 0);
168
+ }
169
+ if ((filter.variant === "number" || filter.variant === "range") && Array.isArray(filter.value) && filter.value.length === 2) {
170
+ const firstValue = safeParseNumber(filter.value[0]);
171
+ const secondValue = safeParseNumber(filter.value[1]);
172
+ if (firstValue === null && secondValue === null) return;
173
+ if (firstValue !== null && secondValue === null) return (0, drizzle_orm.eq)(column, firstValue);
174
+ if (firstValue === null && secondValue !== null) return (0, drizzle_orm.eq)(column, secondValue);
175
+ return (0, drizzle_orm.and)(firstValue !== null ? (0, drizzle_orm.gte)(column, firstValue) : void 0, secondValue !== null ? (0, drizzle_orm.lte)(column, secondValue) : void 0);
176
+ }
177
+ return;
178
+ case "isRelativeToToday":
179
+ if ((filter.variant === "date" || filter.variant === "dateRange") && typeof filter.value === "string") {
180
+ const today = /* @__PURE__ */ new Date();
181
+ const [amount, unit] = filter.value.split(" ") ?? [];
182
+ let startDate;
183
+ let endDate;
184
+ if (!amount || !unit) return void 0;
185
+ switch (unit) {
186
+ case "days":
187
+ startDate = (0, date_fns.startOfDay)((0, date_fns.addDays)(today, Number.parseInt(amount, 10)));
188
+ endDate = (0, date_fns.endOfDay)(startDate);
189
+ break;
190
+ case "weeks":
191
+ startDate = (0, date_fns.startOfDay)((0, date_fns.addDays)(today, Number.parseInt(amount, 10) * 7));
192
+ endDate = (0, date_fns.endOfDay)((0, date_fns.addDays)(startDate, 6));
193
+ break;
194
+ case "months":
195
+ startDate = (0, date_fns.startOfDay)((0, date_fns.addDays)(today, Number.parseInt(amount, 10) * 30));
196
+ endDate = (0, date_fns.endOfDay)((0, date_fns.addDays)(startDate, 29));
197
+ break;
198
+ default: return;
199
+ }
200
+ return (0, drizzle_orm.and)((0, drizzle_orm.gte)(column, startDate), (0, drizzle_orm.lte)(column, endDate));
201
+ }
202
+ return;
203
+ case "isEmpty": return isEmpty(column);
204
+ case "isNotEmpty": return (0, drizzle_orm.not)(isEmpty(column));
205
+ default: throw new Error(`Unsupported operator: ${filter.operator}`);
206
+ }
207
+ }).filter((condition) => condition !== void 0);
208
+ return validConditions.length > 0 ? joinFn(...validConditions) : void 0;
209
+ }
210
+ function getColumn(table, columnKey) {
211
+ return table[columnKey];
212
+ }
213
+
214
+ //#endregion
215
+ //#region src/config/data-table.ts
216
+ const dataTableConfig = {
217
+ textOperators: [
218
+ {
219
+ label: "Contains",
220
+ value: "iLike"
221
+ },
222
+ {
223
+ label: "Does not contain",
224
+ value: "notILike"
225
+ },
226
+ {
227
+ label: "Is",
228
+ value: "eq"
229
+ },
230
+ {
231
+ label: "Is not",
232
+ value: "ne"
233
+ },
234
+ {
235
+ label: "Is empty",
236
+ value: "isEmpty"
237
+ },
238
+ {
239
+ label: "Is not empty",
240
+ value: "isNotEmpty"
241
+ }
242
+ ],
243
+ numericOperators: [
244
+ {
245
+ label: "Is",
246
+ value: "eq"
247
+ },
248
+ {
249
+ label: "Is not",
250
+ value: "ne"
251
+ },
252
+ {
253
+ label: "Is less than",
254
+ value: "lt"
255
+ },
256
+ {
257
+ label: "Is less than or equal to",
258
+ value: "lte"
259
+ },
260
+ {
261
+ label: "Is greater than",
262
+ value: "gt"
263
+ },
264
+ {
265
+ label: "Is greater than or equal to",
266
+ value: "gte"
267
+ },
268
+ {
269
+ label: "Is between",
270
+ value: "isBetween"
271
+ },
272
+ {
273
+ label: "Is empty",
274
+ value: "isEmpty"
275
+ },
276
+ {
277
+ label: "Is not empty",
278
+ value: "isNotEmpty"
279
+ }
280
+ ],
281
+ dateOperators: [
282
+ {
283
+ label: "Is",
284
+ value: "eq"
285
+ },
286
+ {
287
+ label: "Is not",
288
+ value: "ne"
289
+ },
290
+ {
291
+ label: "Is before",
292
+ value: "lt"
293
+ },
294
+ {
295
+ label: "Is after",
296
+ value: "gt"
297
+ },
298
+ {
299
+ label: "Is on or before",
300
+ value: "lte"
301
+ },
302
+ {
303
+ label: "Is on or after",
304
+ value: "gte"
305
+ },
306
+ {
307
+ label: "Is between",
308
+ value: "isBetween"
309
+ },
310
+ {
311
+ label: "Is relative to today",
312
+ value: "isRelativeToToday"
313
+ },
314
+ {
315
+ label: "Is empty",
316
+ value: "isEmpty"
317
+ },
318
+ {
319
+ label: "Is not empty",
320
+ value: "isNotEmpty"
321
+ }
322
+ ],
323
+ selectOperators: [
324
+ {
325
+ label: "Is",
326
+ value: "eq"
327
+ },
328
+ {
329
+ label: "Is not",
330
+ value: "ne"
331
+ },
332
+ {
333
+ label: "Is empty",
334
+ value: "isEmpty"
335
+ },
336
+ {
337
+ label: "Is not empty",
338
+ value: "isNotEmpty"
339
+ }
340
+ ],
341
+ multiSelectOperators: [
342
+ {
343
+ label: "Has any of",
344
+ value: "inArray"
345
+ },
346
+ {
347
+ label: "Has none of",
348
+ value: "notInArray"
349
+ },
350
+ {
351
+ label: "Is empty",
352
+ value: "isEmpty"
353
+ },
354
+ {
355
+ label: "Is not empty",
356
+ value: "isNotEmpty"
357
+ }
358
+ ],
359
+ booleanOperators: [{
360
+ label: "Is",
361
+ value: "eq"
362
+ }, {
363
+ label: "Is not",
364
+ value: "ne"
365
+ }],
366
+ sortOrders: [{
367
+ label: "Asc",
368
+ value: "asc"
369
+ }, {
370
+ label: "Desc",
371
+ value: "desc"
372
+ }],
373
+ filterVariants: [
374
+ "text",
375
+ "number",
376
+ "range",
377
+ "date",
378
+ "dateRange",
379
+ "boolean",
380
+ "select",
381
+ "multiSelect"
382
+ ],
383
+ operators: [
384
+ "iLike",
385
+ "notILike",
386
+ "eq",
387
+ "ne",
388
+ "inArray",
389
+ "notInArray",
390
+ "isEmpty",
391
+ "isNotEmpty",
392
+ "lt",
393
+ "lte",
394
+ "gt",
395
+ "gte",
396
+ "isBetween",
397
+ "isRelativeToToday"
398
+ ],
399
+ joinOperators: ["and", "or"]
400
+ };
401
+
402
+ //#endregion
403
+ //#region src/routers/_factory.ts
404
+ /**
405
+ * 过滤器项 Schema
406
+ */
407
+ const filterItemSchema = zod.z.object({
408
+ id: zod.z.string(),
409
+ value: zod.z.union([zod.z.string(), zod.z.array(zod.z.string())]),
410
+ variant: zod.z.enum(dataTableConfig.filterVariants),
411
+ operator: zod.z.enum(dataTableConfig.operators),
412
+ filterId: zod.z.string()
413
+ });
414
+ /**
415
+ * 列表查询输入 Schema
416
+ */
417
+ const listInputSchema = zod.z.object({
418
+ page: zod.z.number().min(1).default(1),
419
+ perPage: zod.z.number().min(1).max(100).default(10),
420
+ sort: zod.z.array(zod.z.object({
421
+ id: zod.z.string(),
422
+ desc: zod.z.boolean()
423
+ })).optional(),
424
+ filters: zod.z.array(filterItemSchema).optional(),
425
+ joinOperator: zod.z.enum(["and", "or"]).default("and")
426
+ });
427
+ /**
428
+ * 验证列是否在白名单中
429
+ */
430
+ function validateColumn(columnId, allowedColumns, columnType) {
431
+ if (!allowedColumns || allowedColumns.length === 0) return true;
432
+ return allowedColumns.includes(columnId);
433
+ }
434
+ /**
435
+ * 创建通用 CRUD Router
436
+ */
437
+ function createCrudRouter(config) {
438
+ const { table, insertSchema, updateSchema, idField = "id", filterableColumns, sortableColumns, maxBatchSize = 100, procedure: baseProcedure = publicProcedure, authorize } = config;
439
+ const getIdColumn = () => table[idField];
440
+ const withAuthorize = async (operation, input) => {
441
+ if (authorize) {
442
+ if (!await authorize({
443
+ operation,
444
+ input
445
+ })) throw new Error(`Unauthorized: ${operation} operation not allowed`);
446
+ }
447
+ };
448
+ return router({
449
+ list: baseProcedure.input(listInputSchema).query(async ({ ctx, input }) => {
450
+ await withAuthorize("list", input);
451
+ const offset = (input.page - 1) * input.perPage;
452
+ const validatedFilters = input.filters?.filter((filter) => validateColumn(filter.id, filterableColumns, "filter"));
453
+ const where = validatedFilters?.length ? filterColumns({
454
+ table,
455
+ filters: validatedFilters,
456
+ joinOperator: input.joinOperator
457
+ }) : void 0;
458
+ let query = ctx.db.select().from(table).$dynamic();
459
+ if (where) query = query.where(where);
460
+ if (input.sort?.length) {
461
+ const sortField = input.sort[0];
462
+ if (sortField && validateColumn(sortField.id, sortableColumns, "sort")) {
463
+ const column = table[sortField.id];
464
+ if (column) query = query.orderBy(sortField.desc ? (0, drizzle_orm.desc)(column) : (0, drizzle_orm.asc)(column));
465
+ }
466
+ }
467
+ const data = await query.limit(input.perPage).offset(offset);
468
+ let countQuery = ctx.db.select({ count: drizzle_orm.sql`count(*)::int` }).from(table).$dynamic();
469
+ if (where) countQuery = countQuery.where(where);
470
+ const count = (await countQuery)[0]?.count ?? 0;
471
+ return {
472
+ data,
473
+ total: count,
474
+ page: input.page,
475
+ perPage: input.perPage,
476
+ pageCount: Math.ceil(count / input.perPage)
477
+ };
478
+ }),
479
+ getById: baseProcedure.input(zod.z.string()).query(async ({ ctx, input }) => {
480
+ await withAuthorize("get", input);
481
+ const column = getIdColumn();
482
+ const [item] = await ctx.db.select().from(table).where((0, drizzle_orm.eq)(column, input));
483
+ return item ?? null;
484
+ }),
485
+ create: baseProcedure.input(insertSchema).mutation(async ({ ctx, input }) => {
486
+ await withAuthorize("create", input);
487
+ const [created] = await ctx.db.insert(table).values(input).returning();
488
+ return created;
489
+ }),
490
+ update: baseProcedure.input(zod.z.object({
491
+ id: zod.z.string(),
492
+ data: updateSchema
493
+ })).mutation(async ({ ctx, input }) => {
494
+ await withAuthorize("update", input);
495
+ const column = getIdColumn();
496
+ const [updated] = await ctx.db.update(table).set(input.data).where((0, drizzle_orm.eq)(column, input.id)).returning();
497
+ return updated;
498
+ }),
499
+ delete: baseProcedure.input(zod.z.string()).mutation(async ({ ctx, input }) => {
500
+ await withAuthorize("delete", input);
501
+ const column = getIdColumn();
502
+ const [deleted] = await ctx.db.delete(table).where((0, drizzle_orm.eq)(column, input)).returning();
503
+ return deleted;
504
+ }),
505
+ deleteMany: baseProcedure.input(zod.z.array(zod.z.string()).max(maxBatchSize, `Maximum ${maxBatchSize} items allowed`)).mutation(async ({ ctx, input }) => {
506
+ await withAuthorize("deleteMany", input);
507
+ const column = getIdColumn();
508
+ return { deleted: (await ctx.db.delete(table).where((0, drizzle_orm.inArray)(column, input)).returning()).length };
509
+ }),
510
+ updateMany: baseProcedure.input(zod.z.object({
511
+ ids: zod.z.array(zod.z.string()).max(maxBatchSize, `Maximum ${maxBatchSize} items allowed`),
512
+ data: updateSchema
513
+ })).mutation(async ({ ctx, input }) => {
514
+ await withAuthorize("updateMany", input);
515
+ const column = getIdColumn();
516
+ return { updated: (await ctx.db.update(table).set(input.data).where((0, drizzle_orm.inArray)(column, input.ids)).returning()).length };
517
+ })
518
+ });
519
+ }
520
+
521
+ //#endregion
522
+ //#region src/routers/index.ts
523
+ const appRouter = router({});
524
+
525
+ //#endregion
526
+ exports.appRouter = appRouter;
527
+ exports.createCrudRouter = createCrudRouter;
528
+ exports.publicProcedure = publicProcedure;
529
+ exports.router = router;
@@ -0,0 +1,172 @@
1
+ import * as _trpc_server12 from "@trpc/server";
2
+ import superjson from "superjson";
3
+ import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
4
+ import { z } from "zod";
5
+ import { PgTable } from "drizzle-orm/pg-core";
6
+
7
+ //#region src/trpc.d.ts
8
+ /**
9
+ * tRPC Context 类型
10
+ * 消费者需要提供包含 db 的 context
11
+ */
12
+ interface Context {
13
+ db: PostgresJsDatabase<Record<string, never>>;
14
+ }
15
+ declare const t: _trpc_server12.TRPCRootObject<Context, object, {
16
+ transformer: typeof superjson;
17
+ }, {
18
+ ctx: Context;
19
+ meta: object;
20
+ errorShape: _trpc_server12.TRPCDefaultErrorShape;
21
+ transformer: true;
22
+ }>;
23
+ declare const router: _trpc_server12.TRPCRouterBuilder<{
24
+ ctx: Context;
25
+ meta: object;
26
+ errorShape: _trpc_server12.TRPCDefaultErrorShape;
27
+ transformer: true;
28
+ }>;
29
+ declare const publicProcedure: _trpc_server12.TRPCProcedureBuilder<Context, object, object, _trpc_server12.TRPCUnsetMarker, _trpc_server12.TRPCUnsetMarker, _trpc_server12.TRPCUnsetMarker, _trpc_server12.TRPCUnsetMarker, false>;
30
+ type AnyProcedure = typeof t.procedure;
31
+ //#endregion
32
+ //#region src/routers/_factory.d.ts
33
+ /**
34
+ * 授权回调类型
35
+ * 返回 true 允许操作,返回 false 或抛出错误拒绝操作
36
+ */
37
+ type AuthorizeCallback = (ctx: {
38
+ operation: "list" | "get" | "create" | "update" | "delete" | "deleteMany" | "updateMany";
39
+ input?: unknown;
40
+ }) => boolean | Promise<boolean>;
41
+ /**
42
+ * CRUD Router 配置
43
+ */
44
+ interface CrudRouterConfig {
45
+ table: PgTable;
46
+ selectSchema: z.ZodType;
47
+ insertSchema: z.ZodType;
48
+ updateSchema: z.ZodType;
49
+ idField?: string;
50
+ /**
51
+ * 可过滤的列白名单
52
+ * 未指定时默认允许所有列(不安全,仅用于开发)
53
+ */
54
+ filterableColumns?: string[];
55
+ /**
56
+ * 可排序的列白名单
57
+ * 未指定时默认允许所有列(不安全,仅用于开发)
58
+ */
59
+ sortableColumns?: string[];
60
+ /**
61
+ * 批量操作的最大数量限制
62
+ * 默认: 100
63
+ */
64
+ maxBatchSize?: number;
65
+ /**
66
+ * 自定义 procedure(用于注入授权中间件)
67
+ * 默认使用 publicProcedure
68
+ */
69
+ procedure?: AnyProcedure;
70
+ /**
71
+ * 授权回调(简单场景下的快捷授权方式)
72
+ * 如果需要更复杂的授权逻辑,建议使用 procedure 注入中间件
73
+ */
74
+ authorize?: AuthorizeCallback;
75
+ }
76
+ /**
77
+ * 创建通用 CRUD Router
78
+ */
79
+ declare function createCrudRouter(config: CrudRouterConfig): _trpc_server12.TRPCBuiltRouter<{
80
+ ctx: Context;
81
+ meta: object;
82
+ errorShape: _trpc_server12.TRPCDefaultErrorShape;
83
+ transformer: true;
84
+ }, _trpc_server12.TRPCDecorateCreateRouterOptions<{
85
+ list: _trpc_server12.TRPCQueryProcedure<{
86
+ input: {
87
+ page?: number | undefined;
88
+ perPage?: number | undefined;
89
+ sort?: {
90
+ id: string;
91
+ desc: boolean;
92
+ }[] | undefined;
93
+ filters?: {
94
+ id: string;
95
+ value: string | string[];
96
+ variant: "number" | "boolean" | "date" | "text" | "range" | "dateRange" | "select" | "multiSelect";
97
+ operator: "iLike" | "notILike" | "eq" | "ne" | "isEmpty" | "isNotEmpty" | "lt" | "lte" | "gt" | "gte" | "isBetween" | "isRelativeToToday" | "inArray" | "notInArray";
98
+ filterId: string;
99
+ }[] | undefined;
100
+ joinOperator?: "and" | "or" | undefined;
101
+ };
102
+ output: {
103
+ data: {
104
+ [x: string]: unknown;
105
+ }[];
106
+ total: number;
107
+ page: number;
108
+ perPage: number;
109
+ pageCount: number;
110
+ };
111
+ meta: object;
112
+ }>;
113
+ getById: _trpc_server12.TRPCQueryProcedure<{
114
+ input: string;
115
+ output: {
116
+ [x: string]: unknown;
117
+ } | null;
118
+ meta: object;
119
+ }>;
120
+ create: _trpc_server12.TRPCMutationProcedure<{
121
+ input: unknown;
122
+ output: {
123
+ [x: string]: unknown;
124
+ } | undefined;
125
+ meta: object;
126
+ }>;
127
+ update: _trpc_server12.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_server12.TRPCMutationProcedure<{
138
+ input: string;
139
+ output: {
140
+ [x: string]: unknown;
141
+ } | undefined;
142
+ meta: object;
143
+ }>;
144
+ deleteMany: _trpc_server12.TRPCMutationProcedure<{
145
+ input: string[];
146
+ output: {
147
+ deleted: number;
148
+ };
149
+ meta: object;
150
+ }>;
151
+ updateMany: _trpc_server12.TRPCMutationProcedure<{
152
+ input: {
153
+ ids: string[];
154
+ data: unknown;
155
+ };
156
+ output: {
157
+ updated: number;
158
+ };
159
+ meta: object;
160
+ }>;
161
+ }>>;
162
+ //#endregion
163
+ //#region src/routers/index.d.ts
164
+ declare const appRouter: _trpc_server12.TRPCBuiltRouter<{
165
+ ctx: Context;
166
+ meta: object;
167
+ errorShape: _trpc_server12.TRPCDefaultErrorShape;
168
+ transformer: true;
169
+ }, _trpc_server12.TRPCDecorateCreateRouterOptions<{}>>;
170
+ type AppRouter = typeof appRouter;
171
+ //#endregion
172
+ export { type AnyProcedure, type AppRouter, type AuthorizeCallback, type CrudRouterConfig, appRouter, createCrudRouter, publicProcedure, router };