@wordrhyme/auto-crud-server 1.2.0 → 1.3.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/README.md +34 -0
- package/dist/index.cjs +78 -11
- package/dist/index.d.cts +37 -9
- package/dist/index.d.ts +37 -9
- package/dist/index.js +69 -4
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -939,6 +939,40 @@ export const db = drizzle(client, { schema });
|
|
|
939
939
|
export const createContext = () => ({ db });
|
|
940
940
|
```
|
|
941
941
|
|
|
942
|
+
### 使用业务时区解析日期过滤
|
|
943
|
+
|
|
944
|
+
AutoCrud 默认保留历史行为:使用服务器本地时区解析日期过滤。如果宿主已经有用户或租户的业务时区,可在 tRPC context 中提供 `resolveDateRange`,将日历日期转换为 UTC 半开区间:
|
|
945
|
+
|
|
946
|
+
```typescript
|
|
947
|
+
import type { DateRangeResolver } from '@wordrhyme/auto-crud-server';
|
|
948
|
+
|
|
949
|
+
const resolveDateRange: DateRangeResolver = (filter) => {
|
|
950
|
+
// 由宿主根据 filter.value、filter.operator 和当前业务时区进行转换。
|
|
951
|
+
// resolveCalendarFilterToUtc 应返回 UTC Date 对象。
|
|
952
|
+
return resolveCalendarFilterToUtc(filter, getBusinessTimeZone());
|
|
953
|
+
};
|
|
954
|
+
|
|
955
|
+
export const createContext = () => ({
|
|
956
|
+
db,
|
|
957
|
+
resolveDateRange,
|
|
958
|
+
});
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
Resolver 返回值遵循以下契约:
|
|
962
|
+
|
|
963
|
+
```typescript
|
|
964
|
+
interface DateFilterRange {
|
|
965
|
+
start?: Date; // 日历范围的 UTC 起点,包含
|
|
966
|
+
endExclusive?: Date; // UTC 终点,不包含
|
|
967
|
+
}
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
- 返回 `undefined`:使用原有服务器本地时区逻辑。
|
|
971
|
+
- 返回非空范围:该结果是权威结果,不再回退到本地时区。
|
|
972
|
+
- `lt` / `gte` 需要 `start`;`lte` / `gt` 需要 `endExclusive`。
|
|
973
|
+
- `eq`、`ne`、`isBetween`、`isRelativeToToday` 可使用单边或双边范围。
|
|
974
|
+
- 无效日期、空范围、反向范围或缺少操作符所需边界会抛出 `RangeError`,避免过滤条件意外扩大。
|
|
975
|
+
|
|
942
976
|
### 与 Next.js 集成
|
|
943
977
|
|
|
944
978
|
```typescript
|
package/dist/index.cjs
CHANGED
|
@@ -31,8 +31,12 @@ let __trpc_server = require("@trpc/server");
|
|
|
31
31
|
__trpc_server = __toESM(__trpc_server);
|
|
32
32
|
let superjson = require("superjson");
|
|
33
33
|
superjson = __toESM(superjson);
|
|
34
|
-
let
|
|
35
|
-
|
|
34
|
+
let date_fns_addDays = require("date-fns/addDays");
|
|
35
|
+
date_fns_addDays = __toESM(date_fns_addDays);
|
|
36
|
+
let date_fns_endOfDay = require("date-fns/endOfDay");
|
|
37
|
+
date_fns_endOfDay = __toESM(date_fns_endOfDay);
|
|
38
|
+
let date_fns_startOfDay = require("date-fns/startOfDay");
|
|
39
|
+
date_fns_startOfDay = __toESM(date_fns_startOfDay);
|
|
36
40
|
|
|
37
41
|
//#region src/trpc.ts
|
|
38
42
|
const t = __trpc_server.initTRPC.context().create({ transformer: superjson.default });
|
|
@@ -75,12 +79,73 @@ function safeParseNumber(value) {
|
|
|
75
79
|
const num = Number(value);
|
|
76
80
|
return Number.isNaN(num) ? null : num;
|
|
77
81
|
}
|
|
78
|
-
function
|
|
82
|
+
function isValidDate(value) {
|
|
83
|
+
return value instanceof Date && !Number.isNaN(value.getTime());
|
|
84
|
+
}
|
|
85
|
+
function isDateRangeOperator(operator) {
|
|
86
|
+
switch (operator) {
|
|
87
|
+
case "eq":
|
|
88
|
+
case "ne":
|
|
89
|
+
case "lt":
|
|
90
|
+
case "lte":
|
|
91
|
+
case "gt":
|
|
92
|
+
case "gte":
|
|
93
|
+
case "isBetween":
|
|
94
|
+
case "isRelativeToToday": return true;
|
|
95
|
+
case "iLike":
|
|
96
|
+
case "notILike":
|
|
97
|
+
case "inArray":
|
|
98
|
+
case "notInArray":
|
|
99
|
+
case "isEmpty":
|
|
100
|
+
case "isNotEmpty": return false;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function normalizeDateRange(range) {
|
|
105
|
+
if (range.start !== void 0 && !isValidDate(range.start)) throw new RangeError("resolveDateRange returned an invalid start boundary");
|
|
106
|
+
if (range.endExclusive !== void 0 && !isValidDate(range.endExclusive)) throw new RangeError("resolveDateRange returned an invalid endExclusive boundary");
|
|
107
|
+
const { start, endExclusive } = range;
|
|
108
|
+
if (!start && !endExclusive) throw new RangeError("resolveDateRange must return at least one date boundary");
|
|
109
|
+
if (start && endExclusive && start >= endExclusive) throw new RangeError("resolveDateRange requires start to be before endExclusive");
|
|
110
|
+
return {
|
|
111
|
+
start,
|
|
112
|
+
endExclusive
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function requireDateBoundary(boundary, name, operator) {
|
|
116
|
+
if (!boundary) throw new RangeError(`resolveDateRange must return ${name} for operator ${operator}`);
|
|
117
|
+
return boundary;
|
|
118
|
+
}
|
|
119
|
+
function resolvedDateCondition(expr, operator, range) {
|
|
120
|
+
const { start, endExclusive } = range;
|
|
121
|
+
switch (operator) {
|
|
122
|
+
case "eq":
|
|
123
|
+
case "isBetween":
|
|
124
|
+
case "isRelativeToToday":
|
|
125
|
+
if (start && endExclusive) return (0, drizzle_orm.and)((0, drizzle_orm.gte)(expr, start), (0, drizzle_orm.lt)(expr, endExclusive));
|
|
126
|
+
if (start) return (0, drizzle_orm.gte)(expr, start);
|
|
127
|
+
return (0, drizzle_orm.lt)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
128
|
+
case "ne":
|
|
129
|
+
if (start && endExclusive) return (0, drizzle_orm.or)((0, drizzle_orm.lt)(expr, start), (0, drizzle_orm.gte)(expr, endExclusive));
|
|
130
|
+
if (start) return (0, drizzle_orm.lt)(expr, start);
|
|
131
|
+
return (0, drizzle_orm.gte)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
132
|
+
case "lt": return (0, drizzle_orm.lt)(expr, requireDateBoundary(start, "start", operator));
|
|
133
|
+
case "lte": return (0, drizzle_orm.lt)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
134
|
+
case "gt": return (0, drizzle_orm.gte)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
135
|
+
case "gte": return (0, drizzle_orm.gte)(expr, requireDateBoundary(start, "start", operator));
|
|
136
|
+
}
|
|
137
|
+
throw new Error("Unsupported resolved date operator");
|
|
138
|
+
}
|
|
139
|
+
function filterColumns({ table, filters, joinOperator, resolveColumn, resolveDateRange }) {
|
|
79
140
|
const joinFn = joinOperator === "and" ? drizzle_orm.and : drizzle_orm.or;
|
|
80
141
|
const validConditions = filters.map((filter) => {
|
|
81
142
|
const column = resolveColumn ? resolveColumn(filter.id) : getColumn(table, filter.id);
|
|
82
143
|
if (!column) return void 0;
|
|
83
144
|
const expr = toSqlTarget(column);
|
|
145
|
+
if ((filter.variant === "date" || filter.variant === "dateRange") && isDateRangeOperator(filter.operator)) {
|
|
146
|
+
const resolvedRange = resolveDateRange?.(filter);
|
|
147
|
+
if (resolvedRange !== void 0) return resolvedDateCondition(expr, filter.operator, normalizeDateRange(resolvedRange));
|
|
148
|
+
}
|
|
84
149
|
switch (filter.operator) {
|
|
85
150
|
case "iLike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.ilike)(expr, `%${filter.value}%`) : void 0;
|
|
86
151
|
case "notILike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.notIlike)(expr, `%${filter.value}%`) : void 0;
|
|
@@ -195,16 +260,16 @@ function filterColumns({ table, filters, joinOperator, resolveColumn }) {
|
|
|
195
260
|
if (!amount || !unit) return void 0;
|
|
196
261
|
switch (unit) {
|
|
197
262
|
case "days":
|
|
198
|
-
startDate = (0,
|
|
199
|
-
endDate = (0,
|
|
263
|
+
startDate = (0, date_fns_startOfDay.startOfDay)((0, date_fns_addDays.addDays)(today, Number.parseInt(amount, 10)));
|
|
264
|
+
endDate = (0, date_fns_endOfDay.endOfDay)(startDate);
|
|
200
265
|
break;
|
|
201
266
|
case "weeks":
|
|
202
|
-
startDate = (0,
|
|
203
|
-
endDate = (0,
|
|
267
|
+
startDate = (0, date_fns_startOfDay.startOfDay)((0, date_fns_addDays.addDays)(today, Number.parseInt(amount, 10) * 7));
|
|
268
|
+
endDate = (0, date_fns_endOfDay.endOfDay)((0, date_fns_addDays.addDays)(startDate, 6));
|
|
204
269
|
break;
|
|
205
270
|
case "months":
|
|
206
|
-
startDate = (0,
|
|
207
|
-
endDate = (0,
|
|
271
|
+
startDate = (0, date_fns_startOfDay.startOfDay)((0, date_fns_addDays.addDays)(today, Number.parseInt(amount, 10) * 30));
|
|
272
|
+
endDate = (0, date_fns_endOfDay.endOfDay)((0, date_fns_addDays.addDays)(startDate, 29));
|
|
208
273
|
break;
|
|
209
274
|
default: return;
|
|
210
275
|
}
|
|
@@ -1161,7 +1226,8 @@ function createCrudRouter(config) {
|
|
|
1161
1226
|
columnId: id,
|
|
1162
1227
|
allowedColumns: filterableColumns
|
|
1163
1228
|
});
|
|
1164
|
-
}
|
|
1229
|
+
},
|
|
1230
|
+
resolveDateRange: ctx.resolveDateRange
|
|
1165
1231
|
}) : void 0, buildCrudSearchCondition({
|
|
1166
1232
|
table,
|
|
1167
1233
|
search: effectiveInput.search,
|
|
@@ -1455,7 +1521,8 @@ function createCrudRouter(config) {
|
|
|
1455
1521
|
columnId: id,
|
|
1456
1522
|
allowedColumns: filterableColumns
|
|
1457
1523
|
});
|
|
1458
|
-
}
|
|
1524
|
+
},
|
|
1525
|
+
resolveDateRange: ctx.resolveDateRange
|
|
1459
1526
|
}) : void 0, buildCrudSearchCondition({
|
|
1460
1527
|
table,
|
|
1461
1528
|
search: effectiveInput.search,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,36 @@
|
|
|
1
|
+
import { AnyColumn, SQL, Table } from "drizzle-orm";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { PgTable } from "drizzle-orm/pg-core";
|
|
3
|
-
import * as
|
|
4
|
+
import * as _trpc_server2 from "@trpc/server";
|
|
4
5
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
5
|
-
import { AnyColumn, SQL } from "drizzle-orm";
|
|
6
6
|
|
|
7
|
+
//#region src/lib/filter-columns.d.ts
|
|
8
|
+
/** 过滤器变体类型 */
|
|
9
|
+
type FilterVariant = 'text' | 'number' | 'range' | 'date' | 'dateRange' | 'boolean' | 'select' | 'multiSelect';
|
|
10
|
+
/** 过滤器操作符 */
|
|
11
|
+
type FilterOperator = 'eq' | 'ne' | 'lt' | 'lte' | 'gt' | 'gte' | 'iLike' | 'notILike' | 'inArray' | 'notInArray' | 'isBetween' | 'isRelativeToToday' | 'isEmpty' | 'isNotEmpty';
|
|
12
|
+
/** 扩展的列过滤器 */
|
|
13
|
+
interface ExtendedColumnFilter<T extends Table = Table> {
|
|
14
|
+
id: keyof T | string;
|
|
15
|
+
value: unknown;
|
|
16
|
+
variant: FilterVariant;
|
|
17
|
+
operator: FilterOperator;
|
|
18
|
+
}
|
|
19
|
+
/** Host-resolved UTC boundaries for a calendar-date filter. */
|
|
20
|
+
interface DateFilterRange {
|
|
21
|
+
start?: Date;
|
|
22
|
+
endExclusive?: Date;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Optional host adapter for date filters.
|
|
26
|
+
*
|
|
27
|
+
* AutoCrud intentionally does not choose a time zone. A host that has business
|
|
28
|
+
* time-zone context can resolve the filter to UTC boundaries. Returning
|
|
29
|
+
* undefined preserves the legacy server-local parsing behavior. A non-empty
|
|
30
|
+
* result is authoritative and must contain valid boundaries for the operator.
|
|
31
|
+
*/
|
|
32
|
+
type DateRangeResolver<T extends Table = Table> = (filter: ExtendedColumnFilter<T>) => DateFilterRange | undefined;
|
|
33
|
+
//#endregion
|
|
7
34
|
//#region src/trpc.d.ts
|
|
8
35
|
/**
|
|
9
36
|
* tRPC Context 类型
|
|
@@ -11,14 +38,15 @@ import { AnyColumn, SQL } from "drizzle-orm";
|
|
|
11
38
|
*/
|
|
12
39
|
interface Context {
|
|
13
40
|
db: PostgresJsDatabase<Record<string, never>>;
|
|
41
|
+
resolveDateRange?: DateRangeResolver;
|
|
14
42
|
}
|
|
15
|
-
declare const router:
|
|
43
|
+
declare const router: _trpc_server2.TRPCRouterBuilder<{
|
|
16
44
|
ctx: Context;
|
|
17
45
|
meta: object;
|
|
18
|
-
errorShape:
|
|
46
|
+
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
19
47
|
transformer: true;
|
|
20
48
|
}>;
|
|
21
|
-
declare const publicProcedure:
|
|
49
|
+
declare const publicProcedure: _trpc_server2.TRPCProcedureBuilder<Context, object, object, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, false>;
|
|
22
50
|
//#endregion
|
|
23
51
|
//#region src/types/config.d.ts
|
|
24
52
|
type CrudOperation = 'list' | 'get' | 'create' | 'update' | 'delete' | 'deleteMany' | 'updateMany' | 'upsert' | 'export' | 'import' | 'createMany';
|
|
@@ -936,12 +964,12 @@ type CrudHookEventMap<PluginId extends string, ResourceName extends string, TCre
|
|
|
936
964
|
} } & { [K in `${PluginId}.${ResourceName}.createMany`]: TCreate[] };
|
|
937
965
|
//#endregion
|
|
938
966
|
//#region src/routers/index.d.ts
|
|
939
|
-
declare const appRouter:
|
|
967
|
+
declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
|
|
940
968
|
ctx: Context;
|
|
941
969
|
meta: object;
|
|
942
|
-
errorShape:
|
|
970
|
+
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
943
971
|
transformer: true;
|
|
944
|
-
},
|
|
972
|
+
}, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
|
|
945
973
|
type AppRouter = typeof appRouter;
|
|
946
974
|
//#endregion
|
|
947
|
-
export { type AnyProcedure, type AppRouter, type CreateManyMiddlewareParams, type CreateMiddlewareParams, type CrudCapabilityMetadata, type CrudColumnCapability, type CrudColumnConfig, type CrudColumnExpression, type CrudColumnRef, type CrudExtensionFilter, type CrudExtensionMetadata, type CrudExtensionsConfig, type CrudExtensionsProvider, type CrudHookEventMap, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type ExportInput, type ExportMiddlewareParams, type ExportResult, type GetInput, type GetMiddlewareParams, type ImportFailedRow, type ImportInput, type ImportMiddlewareParams, type ImportResult, 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, baseExportInputSchema, baseGetInputSchema, baseListInputSchema, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
|
|
975
|
+
export { type AnyProcedure, type AppRouter, type CreateManyMiddlewareParams, type CreateMiddlewareParams, type CrudCapabilityMetadata, type CrudColumnCapability, type CrudColumnConfig, type CrudColumnExpression, type CrudColumnRef, type CrudExtensionFilter, type CrudExtensionMetadata, type CrudExtensionsConfig, type CrudExtensionsProvider, type CrudHookEventMap, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DateFilterRange, type DateRangeResolver, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type ExportInput, type ExportMiddlewareParams, type ExportResult, type ExtendedColumnFilter, type FilterOperator, type FilterVariant, type GetInput, type GetMiddlewareParams, type ImportFailedRow, type ImportInput, type ImportMiddlewareParams, type ImportResult, 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, baseExportInputSchema, baseGetInputSchema, baseListInputSchema, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { AnyColumn, SQL } from "drizzle-orm";
|
|
3
|
-
import * as
|
|
2
|
+
import { AnyColumn, SQL, Table } from "drizzle-orm";
|
|
3
|
+
import * as _trpc_server2 from "@trpc/server";
|
|
4
4
|
import superjson from "superjson";
|
|
5
5
|
import { PgTable } from "drizzle-orm/pg-core";
|
|
6
6
|
import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
7
7
|
|
|
8
|
+
//#region src/lib/filter-columns.d.ts
|
|
9
|
+
/** 过滤器变体类型 */
|
|
10
|
+
type FilterVariant = 'text' | 'number' | 'range' | 'date' | 'dateRange' | 'boolean' | 'select' | 'multiSelect';
|
|
11
|
+
/** 过滤器操作符 */
|
|
12
|
+
type FilterOperator = 'eq' | 'ne' | 'lt' | 'lte' | 'gt' | 'gte' | 'iLike' | 'notILike' | 'inArray' | 'notInArray' | 'isBetween' | 'isRelativeToToday' | 'isEmpty' | 'isNotEmpty';
|
|
13
|
+
/** 扩展的列过滤器 */
|
|
14
|
+
interface ExtendedColumnFilter<T extends Table = Table> {
|
|
15
|
+
id: keyof T | string;
|
|
16
|
+
value: unknown;
|
|
17
|
+
variant: FilterVariant;
|
|
18
|
+
operator: FilterOperator;
|
|
19
|
+
}
|
|
20
|
+
/** Host-resolved UTC boundaries for a calendar-date filter. */
|
|
21
|
+
interface DateFilterRange {
|
|
22
|
+
start?: Date;
|
|
23
|
+
endExclusive?: Date;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Optional host adapter for date filters.
|
|
27
|
+
*
|
|
28
|
+
* AutoCrud intentionally does not choose a time zone. A host that has business
|
|
29
|
+
* time-zone context can resolve the filter to UTC boundaries. Returning
|
|
30
|
+
* undefined preserves the legacy server-local parsing behavior. A non-empty
|
|
31
|
+
* result is authoritative and must contain valid boundaries for the operator.
|
|
32
|
+
*/
|
|
33
|
+
type DateRangeResolver<T extends Table = Table> = (filter: ExtendedColumnFilter<T>) => DateFilterRange | undefined;
|
|
34
|
+
//#endregion
|
|
8
35
|
//#region src/trpc.d.ts
|
|
9
36
|
/**
|
|
10
37
|
* tRPC Context 类型
|
|
@@ -12,14 +39,15 @@ import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
|
|
|
12
39
|
*/
|
|
13
40
|
interface Context {
|
|
14
41
|
db: PostgresJsDatabase<Record<string, never>>;
|
|
42
|
+
resolveDateRange?: DateRangeResolver;
|
|
15
43
|
}
|
|
16
|
-
declare const router:
|
|
44
|
+
declare const router: _trpc_server2.TRPCRouterBuilder<{
|
|
17
45
|
ctx: Context;
|
|
18
46
|
meta: object;
|
|
19
|
-
errorShape:
|
|
47
|
+
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
20
48
|
transformer: true;
|
|
21
49
|
}>;
|
|
22
|
-
declare const publicProcedure:
|
|
50
|
+
declare const publicProcedure: _trpc_server2.TRPCProcedureBuilder<Context, object, object, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, _trpc_server2.TRPCUnsetMarker, false>;
|
|
23
51
|
//#endregion
|
|
24
52
|
//#region src/types/config.d.ts
|
|
25
53
|
type CrudOperation = 'list' | 'get' | 'create' | 'update' | 'delete' | 'deleteMany' | 'updateMany' | 'upsert' | 'export' | 'import' | 'createMany';
|
|
@@ -937,12 +965,12 @@ type CrudHookEventMap<PluginId extends string, ResourceName extends string, TCre
|
|
|
937
965
|
} } & { [K in `${PluginId}.${ResourceName}.createMany`]: TCreate[] };
|
|
938
966
|
//#endregion
|
|
939
967
|
//#region src/routers/index.d.ts
|
|
940
|
-
declare const appRouter:
|
|
968
|
+
declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
|
|
941
969
|
ctx: Context;
|
|
942
970
|
meta: object;
|
|
943
|
-
errorShape:
|
|
971
|
+
errorShape: _trpc_server2.TRPCDefaultErrorShape;
|
|
944
972
|
transformer: true;
|
|
945
|
-
},
|
|
973
|
+
}, _trpc_server2.TRPCDecorateCreateRouterOptions<{}>>;
|
|
946
974
|
type AppRouter = typeof appRouter;
|
|
947
975
|
//#endregion
|
|
948
|
-
export { type AnyProcedure, type AppRouter, type CreateManyMiddlewareParams, type CreateMiddlewareParams, type CrudCapabilityMetadata, type CrudColumnCapability, type CrudColumnConfig, type CrudColumnExpression, type CrudColumnRef, type CrudExtensionFilter, type CrudExtensionMetadata, type CrudExtensionsConfig, type CrudExtensionsProvider, type CrudHookEventMap, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type ExportInput, type ExportMiddlewareParams, type ExportResult, type GetInput, type GetMiddlewareParams, type ImportFailedRow, type ImportInput, type ImportMiddlewareParams, type ImportResult, 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, baseExportInputSchema, baseGetInputSchema, baseListInputSchema, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
|
|
976
|
+
export { type AnyProcedure, type AppRouter, type CreateManyMiddlewareParams, type CreateMiddlewareParams, type CrudCapabilityMetadata, type CrudColumnCapability, type CrudColumnConfig, type CrudColumnExpression, type CrudColumnRef, type CrudExtensionFilter, type CrudExtensionMetadata, type CrudExtensionsConfig, type CrudExtensionsProvider, type CrudHookEventMap, type CrudMiddleware, type CrudOperation, type CrudProcedures, type CrudRouterConfig, type DateFilterRange, type DateRangeResolver, type DeleteManyMiddlewareParams, type DeleteMiddlewareParams, type ExportInput, type ExportMiddlewareParams, type ExportResult, type ExtendedColumnFilter, type FilterOperator, type FilterVariant, type GetInput, type GetMiddlewareParams, type ImportFailedRow, type ImportInput, type ImportMiddlewareParams, type ImportResult, 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, baseExportInputSchema, baseGetInputSchema, baseListInputSchema, beforeMiddleware, composeMiddleware, createCrudRouter, publicProcedure, router };
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,9 @@ import { and, asc, desc, eq, getTableColumns, gt, gte, ilike, inArray, isNull, l
|
|
|
3
3
|
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
|
4
4
|
import { TRPCError, initTRPC } from "@trpc/server";
|
|
5
5
|
import superjson from "superjson";
|
|
6
|
-
import { addDays
|
|
6
|
+
import { addDays } from "date-fns/addDays";
|
|
7
|
+
import { endOfDay } from "date-fns/endOfDay";
|
|
8
|
+
import { startOfDay } from "date-fns/startOfDay";
|
|
7
9
|
|
|
8
10
|
//#region src/trpc.ts
|
|
9
11
|
const t = initTRPC.context().create({ transformer: superjson });
|
|
@@ -46,12 +48,73 @@ function safeParseNumber(value) {
|
|
|
46
48
|
const num = Number(value);
|
|
47
49
|
return Number.isNaN(num) ? null : num;
|
|
48
50
|
}
|
|
49
|
-
function
|
|
51
|
+
function isValidDate(value) {
|
|
52
|
+
return value instanceof Date && !Number.isNaN(value.getTime());
|
|
53
|
+
}
|
|
54
|
+
function isDateRangeOperator(operator) {
|
|
55
|
+
switch (operator) {
|
|
56
|
+
case "eq":
|
|
57
|
+
case "ne":
|
|
58
|
+
case "lt":
|
|
59
|
+
case "lte":
|
|
60
|
+
case "gt":
|
|
61
|
+
case "gte":
|
|
62
|
+
case "isBetween":
|
|
63
|
+
case "isRelativeToToday": return true;
|
|
64
|
+
case "iLike":
|
|
65
|
+
case "notILike":
|
|
66
|
+
case "inArray":
|
|
67
|
+
case "notInArray":
|
|
68
|
+
case "isEmpty":
|
|
69
|
+
case "isNotEmpty": return false;
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function normalizeDateRange(range) {
|
|
74
|
+
if (range.start !== void 0 && !isValidDate(range.start)) throw new RangeError("resolveDateRange returned an invalid start boundary");
|
|
75
|
+
if (range.endExclusive !== void 0 && !isValidDate(range.endExclusive)) throw new RangeError("resolveDateRange returned an invalid endExclusive boundary");
|
|
76
|
+
const { start, endExclusive } = range;
|
|
77
|
+
if (!start && !endExclusive) throw new RangeError("resolveDateRange must return at least one date boundary");
|
|
78
|
+
if (start && endExclusive && start >= endExclusive) throw new RangeError("resolveDateRange requires start to be before endExclusive");
|
|
79
|
+
return {
|
|
80
|
+
start,
|
|
81
|
+
endExclusive
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function requireDateBoundary(boundary, name, operator) {
|
|
85
|
+
if (!boundary) throw new RangeError(`resolveDateRange must return ${name} for operator ${operator}`);
|
|
86
|
+
return boundary;
|
|
87
|
+
}
|
|
88
|
+
function resolvedDateCondition(expr, operator, range) {
|
|
89
|
+
const { start, endExclusive } = range;
|
|
90
|
+
switch (operator) {
|
|
91
|
+
case "eq":
|
|
92
|
+
case "isBetween":
|
|
93
|
+
case "isRelativeToToday":
|
|
94
|
+
if (start && endExclusive) return and(gte(expr, start), lt(expr, endExclusive));
|
|
95
|
+
if (start) return gte(expr, start);
|
|
96
|
+
return lt(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
97
|
+
case "ne":
|
|
98
|
+
if (start && endExclusive) return or(lt(expr, start), gte(expr, endExclusive));
|
|
99
|
+
if (start) return lt(expr, start);
|
|
100
|
+
return gte(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
101
|
+
case "lt": return lt(expr, requireDateBoundary(start, "start", operator));
|
|
102
|
+
case "lte": return lt(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
103
|
+
case "gt": return gte(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
|
|
104
|
+
case "gte": return gte(expr, requireDateBoundary(start, "start", operator));
|
|
105
|
+
}
|
|
106
|
+
throw new Error("Unsupported resolved date operator");
|
|
107
|
+
}
|
|
108
|
+
function filterColumns({ table, filters, joinOperator, resolveColumn, resolveDateRange }) {
|
|
50
109
|
const joinFn = joinOperator === "and" ? and : or;
|
|
51
110
|
const validConditions = filters.map((filter) => {
|
|
52
111
|
const column = resolveColumn ? resolveColumn(filter.id) : getColumn(table, filter.id);
|
|
53
112
|
if (!column) return void 0;
|
|
54
113
|
const expr = toSqlTarget(column);
|
|
114
|
+
if ((filter.variant === "date" || filter.variant === "dateRange") && isDateRangeOperator(filter.operator)) {
|
|
115
|
+
const resolvedRange = resolveDateRange?.(filter);
|
|
116
|
+
if (resolvedRange !== void 0) return resolvedDateCondition(expr, filter.operator, normalizeDateRange(resolvedRange));
|
|
117
|
+
}
|
|
55
118
|
switch (filter.operator) {
|
|
56
119
|
case "iLike": return filter.variant === "text" && typeof filter.value === "string" ? ilike(expr, `%${filter.value}%`) : void 0;
|
|
57
120
|
case "notILike": return filter.variant === "text" && typeof filter.value === "string" ? notIlike(expr, `%${filter.value}%`) : void 0;
|
|
@@ -1132,7 +1195,8 @@ function createCrudRouter(config) {
|
|
|
1132
1195
|
columnId: id,
|
|
1133
1196
|
allowedColumns: filterableColumns
|
|
1134
1197
|
});
|
|
1135
|
-
}
|
|
1198
|
+
},
|
|
1199
|
+
resolveDateRange: ctx.resolveDateRange
|
|
1136
1200
|
}) : void 0, buildCrudSearchCondition({
|
|
1137
1201
|
table,
|
|
1138
1202
|
search: effectiveInput.search,
|
|
@@ -1426,7 +1490,8 @@ function createCrudRouter(config) {
|
|
|
1426
1490
|
columnId: id,
|
|
1427
1491
|
allowedColumns: filterableColumns
|
|
1428
1492
|
});
|
|
1429
|
-
}
|
|
1493
|
+
},
|
|
1494
|
+
resolveDateRange: ctx.resolveDateRange
|
|
1430
1495
|
}) : void 0, buildCrudSearchCondition({
|
|
1431
1496
|
table,
|
|
1432
1497
|
search: effectiveInput.search,
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordrhyme/auto-crud-server",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.3.1",
|
|
5
5
|
"description": "tRPC server utilities for auto-crud - automatic CRUD routers for Drizzle ORM",
|
|
6
6
|
"author": "wordrhyme",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
|
-
"url": "https://github.com/
|
|
10
|
+
"url": "https://github.com/denvey/wordrhyme-components",
|
|
11
11
|
"directory": "packages/auto-crud-server"
|
|
12
12
|
},
|
|
13
13
|
"main": "./dist/index.cjs",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"vitest": "^4.0.18",
|
|
50
50
|
"zod": "^4.3.6",
|
|
51
51
|
"@internal/eslint-config": "0.3.0",
|
|
52
|
-
"@internal/prettier-config": "0.0.1",
|
|
53
52
|
"@internal/tsconfig": "0.1.0",
|
|
54
53
|
"@internal/tsdown-config": "0.1.0",
|
|
55
|
-
"@internal/vitest-config": "0.1.0"
|
|
54
|
+
"@internal/vitest-config": "0.1.0",
|
|
55
|
+
"@internal/prettier-config": "0.0.1"
|
|
56
56
|
},
|
|
57
57
|
"prettier": "@internal/prettier-config",
|
|
58
58
|
"scripts": {
|