@wordrhyme/auto-crud-server 1.2.0 → 1.3.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 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
@@ -75,12 +75,73 @@ function safeParseNumber(value) {
75
75
  const num = Number(value);
76
76
  return Number.isNaN(num) ? null : num;
77
77
  }
78
- function filterColumns({ table, filters, joinOperator, resolveColumn }) {
78
+ function isValidDate(value) {
79
+ return value instanceof Date && !Number.isNaN(value.getTime());
80
+ }
81
+ function isDateRangeOperator(operator) {
82
+ switch (operator) {
83
+ case "eq":
84
+ case "ne":
85
+ case "lt":
86
+ case "lte":
87
+ case "gt":
88
+ case "gte":
89
+ case "isBetween":
90
+ case "isRelativeToToday": return true;
91
+ case "iLike":
92
+ case "notILike":
93
+ case "inArray":
94
+ case "notInArray":
95
+ case "isEmpty":
96
+ case "isNotEmpty": return false;
97
+ }
98
+ return false;
99
+ }
100
+ function normalizeDateRange(range) {
101
+ if (range.start !== void 0 && !isValidDate(range.start)) throw new RangeError("resolveDateRange returned an invalid start boundary");
102
+ if (range.endExclusive !== void 0 && !isValidDate(range.endExclusive)) throw new RangeError("resolveDateRange returned an invalid endExclusive boundary");
103
+ const { start, endExclusive } = range;
104
+ if (!start && !endExclusive) throw new RangeError("resolveDateRange must return at least one date boundary");
105
+ if (start && endExclusive && start >= endExclusive) throw new RangeError("resolveDateRange requires start to be before endExclusive");
106
+ return {
107
+ start,
108
+ endExclusive
109
+ };
110
+ }
111
+ function requireDateBoundary(boundary, name, operator) {
112
+ if (!boundary) throw new RangeError(`resolveDateRange must return ${name} for operator ${operator}`);
113
+ return boundary;
114
+ }
115
+ function resolvedDateCondition(expr, operator, range) {
116
+ const { start, endExclusive } = range;
117
+ switch (operator) {
118
+ case "eq":
119
+ case "isBetween":
120
+ case "isRelativeToToday":
121
+ if (start && endExclusive) return (0, drizzle_orm.and)((0, drizzle_orm.gte)(expr, start), (0, drizzle_orm.lt)(expr, endExclusive));
122
+ if (start) return (0, drizzle_orm.gte)(expr, start);
123
+ return (0, drizzle_orm.lt)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
124
+ case "ne":
125
+ if (start && endExclusive) return (0, drizzle_orm.or)((0, drizzle_orm.lt)(expr, start), (0, drizzle_orm.gte)(expr, endExclusive));
126
+ if (start) return (0, drizzle_orm.lt)(expr, start);
127
+ return (0, drizzle_orm.gte)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
128
+ case "lt": return (0, drizzle_orm.lt)(expr, requireDateBoundary(start, "start", operator));
129
+ case "lte": return (0, drizzle_orm.lt)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
130
+ case "gt": return (0, drizzle_orm.gte)(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
131
+ case "gte": return (0, drizzle_orm.gte)(expr, requireDateBoundary(start, "start", operator));
132
+ }
133
+ throw new Error("Unsupported resolved date operator");
134
+ }
135
+ function filterColumns({ table, filters, joinOperator, resolveColumn, resolveDateRange }) {
79
136
  const joinFn = joinOperator === "and" ? drizzle_orm.and : drizzle_orm.or;
80
137
  const validConditions = filters.map((filter) => {
81
138
  const column = resolveColumn ? resolveColumn(filter.id) : getColumn(table, filter.id);
82
139
  if (!column) return void 0;
83
140
  const expr = toSqlTarget(column);
141
+ if ((filter.variant === "date" || filter.variant === "dateRange") && isDateRangeOperator(filter.operator)) {
142
+ const resolvedRange = resolveDateRange?.(filter);
143
+ if (resolvedRange !== void 0) return resolvedDateCondition(expr, filter.operator, normalizeDateRange(resolvedRange));
144
+ }
84
145
  switch (filter.operator) {
85
146
  case "iLike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.ilike)(expr, `%${filter.value}%`) : void 0;
86
147
  case "notILike": return filter.variant === "text" && typeof filter.value === "string" ? (0, drizzle_orm.notIlike)(expr, `%${filter.value}%`) : void 0;
@@ -1161,7 +1222,8 @@ function createCrudRouter(config) {
1161
1222
  columnId: id,
1162
1223
  allowedColumns: filterableColumns
1163
1224
  });
1164
- }
1225
+ },
1226
+ resolveDateRange: ctx.resolveDateRange
1165
1227
  }) : void 0, buildCrudSearchCondition({
1166
1228
  table,
1167
1229
  search: effectiveInput.search,
@@ -1455,7 +1517,8 @@ function createCrudRouter(config) {
1455
1517
  columnId: id,
1456
1518
  allowedColumns: filterableColumns
1457
1519
  });
1458
- }
1520
+ },
1521
+ resolveDateRange: ctx.resolveDateRange
1459
1522
  }) : void 0, buildCrudSearchCondition({
1460
1523
  table,
1461
1524
  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 _trpc_server0 from "@trpc/server";
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: _trpc_server0.TRPCRouterBuilder<{
43
+ declare const router: _trpc_server2.TRPCRouterBuilder<{
16
44
  ctx: Context;
17
45
  meta: object;
18
- errorShape: _trpc_server0.TRPCDefaultErrorShape;
46
+ errorShape: _trpc_server2.TRPCDefaultErrorShape;
19
47
  transformer: true;
20
48
  }>;
21
- declare const publicProcedure: _trpc_server0.TRPCProcedureBuilder<Context, object, object, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, false>;
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: _trpc_server0.TRPCBuiltRouter<{
967
+ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
940
968
  ctx: Context;
941
969
  meta: object;
942
- errorShape: _trpc_server0.TRPCDefaultErrorShape;
970
+ errorShape: _trpc_server2.TRPCDefaultErrorShape;
943
971
  transformer: true;
944
- }, _trpc_server0.TRPCDecorateCreateRouterOptions<{}>>;
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 _trpc_server0 from "@trpc/server";
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: _trpc_server0.TRPCRouterBuilder<{
44
+ declare const router: _trpc_server2.TRPCRouterBuilder<{
17
45
  ctx: Context;
18
46
  meta: object;
19
- errorShape: _trpc_server0.TRPCDefaultErrorShape;
47
+ errorShape: _trpc_server2.TRPCDefaultErrorShape;
20
48
  transformer: true;
21
49
  }>;
22
- declare const publicProcedure: _trpc_server0.TRPCProcedureBuilder<Context, object, object, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, _trpc_server0.TRPCUnsetMarker, false>;
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: _trpc_server0.TRPCBuiltRouter<{
968
+ declare const appRouter: _trpc_server2.TRPCBuiltRouter<{
941
969
  ctx: Context;
942
970
  meta: object;
943
- errorShape: _trpc_server0.TRPCDefaultErrorShape;
971
+ errorShape: _trpc_server2.TRPCDefaultErrorShape;
944
972
  transformer: true;
945
- }, _trpc_server0.TRPCDecorateCreateRouterOptions<{}>>;
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
@@ -46,12 +46,73 @@ function safeParseNumber(value) {
46
46
  const num = Number(value);
47
47
  return Number.isNaN(num) ? null : num;
48
48
  }
49
- function filterColumns({ table, filters, joinOperator, resolveColumn }) {
49
+ function isValidDate(value) {
50
+ return value instanceof Date && !Number.isNaN(value.getTime());
51
+ }
52
+ function isDateRangeOperator(operator) {
53
+ switch (operator) {
54
+ case "eq":
55
+ case "ne":
56
+ case "lt":
57
+ case "lte":
58
+ case "gt":
59
+ case "gte":
60
+ case "isBetween":
61
+ case "isRelativeToToday": return true;
62
+ case "iLike":
63
+ case "notILike":
64
+ case "inArray":
65
+ case "notInArray":
66
+ case "isEmpty":
67
+ case "isNotEmpty": return false;
68
+ }
69
+ return false;
70
+ }
71
+ function normalizeDateRange(range) {
72
+ if (range.start !== void 0 && !isValidDate(range.start)) throw new RangeError("resolveDateRange returned an invalid start boundary");
73
+ if (range.endExclusive !== void 0 && !isValidDate(range.endExclusive)) throw new RangeError("resolveDateRange returned an invalid endExclusive boundary");
74
+ const { start, endExclusive } = range;
75
+ if (!start && !endExclusive) throw new RangeError("resolveDateRange must return at least one date boundary");
76
+ if (start && endExclusive && start >= endExclusive) throw new RangeError("resolveDateRange requires start to be before endExclusive");
77
+ return {
78
+ start,
79
+ endExclusive
80
+ };
81
+ }
82
+ function requireDateBoundary(boundary, name, operator) {
83
+ if (!boundary) throw new RangeError(`resolveDateRange must return ${name} for operator ${operator}`);
84
+ return boundary;
85
+ }
86
+ function resolvedDateCondition(expr, operator, range) {
87
+ const { start, endExclusive } = range;
88
+ switch (operator) {
89
+ case "eq":
90
+ case "isBetween":
91
+ case "isRelativeToToday":
92
+ if (start && endExclusive) return and(gte(expr, start), lt(expr, endExclusive));
93
+ if (start) return gte(expr, start);
94
+ return lt(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
95
+ case "ne":
96
+ if (start && endExclusive) return or(lt(expr, start), gte(expr, endExclusive));
97
+ if (start) return lt(expr, start);
98
+ return gte(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
99
+ case "lt": return lt(expr, requireDateBoundary(start, "start", operator));
100
+ case "lte": return lt(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
101
+ case "gt": return gte(expr, requireDateBoundary(endExclusive, "endExclusive", operator));
102
+ case "gte": return gte(expr, requireDateBoundary(start, "start", operator));
103
+ }
104
+ throw new Error("Unsupported resolved date operator");
105
+ }
106
+ function filterColumns({ table, filters, joinOperator, resolveColumn, resolveDateRange }) {
50
107
  const joinFn = joinOperator === "and" ? and : or;
51
108
  const validConditions = filters.map((filter) => {
52
109
  const column = resolveColumn ? resolveColumn(filter.id) : getColumn(table, filter.id);
53
110
  if (!column) return void 0;
54
111
  const expr = toSqlTarget(column);
112
+ if ((filter.variant === "date" || filter.variant === "dateRange") && isDateRangeOperator(filter.operator)) {
113
+ const resolvedRange = resolveDateRange?.(filter);
114
+ if (resolvedRange !== void 0) return resolvedDateCondition(expr, filter.operator, normalizeDateRange(resolvedRange));
115
+ }
55
116
  switch (filter.operator) {
56
117
  case "iLike": return filter.variant === "text" && typeof filter.value === "string" ? ilike(expr, `%${filter.value}%`) : void 0;
57
118
  case "notILike": return filter.variant === "text" && typeof filter.value === "string" ? notIlike(expr, `%${filter.value}%`) : void 0;
@@ -1132,7 +1193,8 @@ function createCrudRouter(config) {
1132
1193
  columnId: id,
1133
1194
  allowedColumns: filterableColumns
1134
1195
  });
1135
- }
1196
+ },
1197
+ resolveDateRange: ctx.resolveDateRange
1136
1198
  }) : void 0, buildCrudSearchCondition({
1137
1199
  table,
1138
1200
  search: effectiveInput.search,
@@ -1426,7 +1488,8 @@ function createCrudRouter(config) {
1426
1488
  columnId: id,
1427
1489
  allowedColumns: filterableColumns
1428
1490
  });
1429
- }
1491
+ },
1492
+ resolveDateRange: ctx.resolveDateRange
1430
1493
  }) : void 0, buildCrudSearchCondition({
1431
1494
  table,
1432
1495
  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.2.0",
4
+ "version": "1.3.0",
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/pixpilot/shadcn-components.git",
10
+ "url": "https://github.com/denvey/wordrhyme-components",
11
11
  "directory": "packages/auto-crud-server"
12
12
  },
13
13
  "main": "./dist/index.cjs",
@@ -49,9 +49,9 @@
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
- "@internal/tsconfig": "0.1.0",
54
52
  "@internal/tsdown-config": "0.1.0",
53
+ "@internal/tsconfig": "0.1.0",
54
+ "@internal/prettier-config": "0.0.1",
55
55
  "@internal/vitest-config": "0.1.0"
56
56
  },
57
57
  "prettier": "@internal/prettier-config",