@zudojs/database 0.0.1 → 1.0.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.
Files changed (134) hide show
  1. package/README.md +223 -17
  2. package/dist/cache/cache.memory.d.ts +167 -0
  3. package/dist/cache/cache.memory.js +186 -40
  4. package/dist/cache/index.d.ts +2 -2
  5. package/dist/cache/index.js +2 -2
  6. package/dist/database/database.core.d.ts +91 -0
  7. package/dist/database/database.core.js +150 -0
  8. package/dist/databaseClient/databaseClient.core.d.ts +165 -14
  9. package/dist/databaseClient/databaseClient.core.js +304 -70
  10. package/dist/databaseClient/databaseClient.errors.d.ts +112 -0
  11. package/dist/databaseClient/databaseClient.errors.js +425 -0
  12. package/dist/databaseClient/index.d.ts +2 -1
  13. package/dist/databaseClient/index.js +2 -1
  14. package/dist/databaseConnection/databaseConnection.manager.d.ts +163 -0
  15. package/dist/databaseConnection/databaseConnection.manager.js +138 -76
  16. package/dist/databaseConnection/index.d.ts +1 -1
  17. package/dist/databaseType/databaseType.type.d.ts +224 -0
  18. package/dist/databaseType/databaseType.type.js +10 -0
  19. package/dist/health/health.check.d.ts +74 -0
  20. package/dist/health/health.check.js +78 -47
  21. package/dist/health/index.d.ts +1 -1
  22. package/dist/health/index.js +1 -1
  23. package/dist/index.d.ts +14 -14
  24. package/dist/index.js +11 -11
  25. package/dist/locks/index.d.ts +2 -2
  26. package/dist/locks/index.js +2 -2
  27. package/dist/locks/locks.core.d.ts +128 -0
  28. package/dist/locks/locks.core.js +144 -85
  29. package/dist/migration/index.d.ts +3 -1
  30. package/dist/migration/index.js +2 -0
  31. package/dist/migration/migration.dialect.d.ts +65 -0
  32. package/dist/migration/migration.dialect.js +63 -0
  33. package/dist/migration/migration.helpers.d.ts +65 -0
  34. package/dist/migration/migration.helpers.js +141 -0
  35. package/dist/migration/migration.runner.d.ts +75 -0
  36. package/dist/migration/migration.runner.js +195 -107
  37. package/dist/migration/migration.types.d.ts +85 -0
  38. package/dist/migration/migration.types.js +2 -0
  39. package/dist/pagination/index.d.ts +2 -1
  40. package/dist/pagination/index.js +2 -1
  41. package/dist/pagination/pagination.core.d.ts +163 -0
  42. package/dist/pagination/pagination.core.js +279 -0
  43. package/dist/pagination/pagination.keyset.d.ts +55 -0
  44. package/dist/pagination/pagination.keyset.js +108 -0
  45. package/dist/queryBuilder/index.d.ts +4 -3
  46. package/dist/queryBuilder/index.js +3 -2
  47. package/dist/queryBuilder/queryBuilder.core.d.ts +178 -0
  48. package/dist/queryBuilder/queryBuilder.core.js +420 -0
  49. package/dist/queryBuilder/queryBuilder.factory.d.ts +6 -0
  50. package/dist/queryBuilder/queryBuilder.factory.js +8 -0
  51. package/dist/queryBuilder/queryBuilder.filter.d.ts +170 -0
  52. package/dist/queryBuilder/queryBuilder.filter.js +497 -0
  53. package/dist/queryBuilder/queryBuilder.prisma.d.ts +60 -0
  54. package/dist/queryBuilder/queryBuilder.prisma.js +233 -0
  55. package/dist/queryBuilder/queryBuilder.type.d.ts +48 -0
  56. package/dist/queryBuilder/queryBuilder.type.js +2 -0
  57. package/dist/relations/index.d.ts +1 -1
  58. package/dist/relations/index.js +1 -1
  59. package/dist/relations/relations.definition.d.ts +184 -0
  60. package/dist/relations/relations.definition.js +362 -0
  61. package/dist/repository/index.d.ts +2 -1
  62. package/dist/repository/index.js +1 -0
  63. package/dist/repository/repository.base.d.ts +270 -0
  64. package/dist/repository/repository.base.js +350 -84
  65. package/dist/repository/repository.errors.d.ts +60 -0
  66. package/dist/repository/repository.errors.js +235 -0
  67. package/dist/seed/index.d.ts +2 -2
  68. package/dist/seed/index.js +1 -1
  69. package/dist/seed/seed.runner.d.ts +160 -0
  70. package/dist/seed/seed.runner.js +194 -225
  71. package/dist/transaction/index.d.ts +1 -1
  72. package/dist/transaction/index.js +1 -1
  73. package/dist/transaction/transaction.core.d.ts +117 -0
  74. package/dist/transaction/transaction.core.js +83 -56
  75. package/dist/unitOfWork/unitOfWork.core.d.ts +44 -0
  76. package/dist/unitOfWork/unitOfWork.core.js +9 -20
  77. package/package.json +35 -19
  78. package/dist/cache/cache.memory.d.ts.map +0 -1
  79. package/dist/cache/cache.memory.js.map +0 -1
  80. package/dist/cache/index.d.ts.map +0 -1
  81. package/dist/cache/index.js.map +0 -1
  82. package/dist/database/database.core.d.ts.map +0 -1
  83. package/dist/database/index.d.ts.map +0 -1
  84. package/dist/database/index.js.map +0 -1
  85. package/dist/databaseClient/databaseClient.core.d.ts.map +0 -1
  86. package/dist/databaseClient/databaseClient.core.js.map +0 -1
  87. package/dist/databaseClient/databaseClient.logger.d.ts.map +0 -1
  88. package/dist/databaseClient/databaseClient.logger.js.map +0 -1
  89. package/dist/databaseClient/index.d.ts.map +0 -1
  90. package/dist/databaseClient/index.js.map +0 -1
  91. package/dist/databaseConnection/databaseConnection.manager.d.ts.map +0 -1
  92. package/dist/databaseConnection/databaseConnection.manager.js.map +0 -1
  93. package/dist/databaseConnection/index.d.ts.map +0 -1
  94. package/dist/databaseConnection/index.js.map +0 -1
  95. package/dist/databaseType/index.d.ts.map +0 -1
  96. package/dist/databaseType/index.js.map +0 -1
  97. package/dist/health/health.check.d.ts.map +0 -1
  98. package/dist/health/health.check.js.map +0 -1
  99. package/dist/health/index.d.ts.map +0 -1
  100. package/dist/health/index.js.map +0 -1
  101. package/dist/index.d.ts.map +0 -1
  102. package/dist/index.js.map +0 -1
  103. package/dist/locks/index.d.ts.map +0 -1
  104. package/dist/locks/index.js.map +0 -1
  105. package/dist/locks/locks.core.d.ts.map +0 -1
  106. package/dist/locks/locks.core.js.map +0 -1
  107. package/dist/migration/index.d.ts.map +0 -1
  108. package/dist/migration/index.js.map +0 -1
  109. package/dist/migration/migration.helpers.d.ts.map +0 -1
  110. package/dist/migration/migration.runner.d.ts.map +0 -1
  111. package/dist/migration/migration.runner.js.map +0 -1
  112. package/dist/migration/migration.types.d.ts.map +0 -1
  113. package/dist/pagination/index.d.ts.map +0 -1
  114. package/dist/pagination/index.js.map +0 -1
  115. package/dist/queryBuilder/index.d.ts.map +0 -1
  116. package/dist/queryBuilder/index.js.map +0 -1
  117. package/dist/relations/index.d.ts.map +0 -1
  118. package/dist/relations/index.js.map +0 -1
  119. package/dist/repository/index.d.ts.map +0 -1
  120. package/dist/repository/index.js.map +0 -1
  121. package/dist/repository/repository.base.d.ts.map +0 -1
  122. package/dist/repository/repository.base.js.map +0 -1
  123. package/dist/seed/index.d.ts.map +0 -1
  124. package/dist/seed/index.js.map +0 -1
  125. package/dist/seed/seed.runner.d.ts.map +0 -1
  126. package/dist/seed/seed.runner.js.map +0 -1
  127. package/dist/transaction/index.d.ts.map +0 -1
  128. package/dist/transaction/index.js.map +0 -1
  129. package/dist/transaction/transaction.core.d.ts.map +0 -1
  130. package/dist/transaction/transaction.core.js.map +0 -1
  131. package/dist/unitOfWork/index.d.ts.map +0 -1
  132. package/dist/unitOfWork/index.js.map +0 -1
  133. package/dist/unitOfWork/unitOfWork.core.d.ts.map +0 -1
  134. package/dist/unitOfWork/unitOfWork.core.js.map +0 -1
@@ -0,0 +1,170 @@
1
+ import type { QueryCondition, QueryFilter, QueryOperator, RelationOperator } from "./queryBuilder.type.js";
2
+ /**
3
+ * Creates an equality filter.
4
+ */
5
+ export declare function equals(field: string, value: unknown): QueryFilter;
6
+ /**
7
+ * Creates a not-equal filter.
8
+ */
9
+ export declare function notEquals(field: string, value: unknown): QueryFilter;
10
+ /**
11
+ * Creates an IN filter.
12
+ */
13
+ export declare function inList(field: string, values: readonly unknown[]): QueryFilter;
14
+ /**
15
+ * Creates a NOT IN filter.
16
+ */
17
+ export declare function notInList(field: string, values: readonly unknown[]): QueryFilter;
18
+ /**
19
+ * Creates a less-than filter.
20
+ */
21
+ export declare function lessThan(field: string, value: unknown): QueryFilter;
22
+ /**
23
+ * Creates a less-than-or-equal filter.
24
+ */
25
+ export declare function lessThanOrEqual(field: string, value: unknown): QueryFilter;
26
+ /**
27
+ * Creates a greater-than filter.
28
+ */
29
+ export declare function greaterThan(field: string, value: unknown): QueryFilter;
30
+ /**
31
+ * Creates a greater-than-or-equal filter.
32
+ */
33
+ export declare function greaterThanOrEqual(field: string, value: unknown): QueryFilter;
34
+ /**
35
+ * Creates a contains filter.
36
+ */
37
+ export declare function contains(field: string, value: string): QueryFilter;
38
+ /**
39
+ * Creates a starts-with filter.
40
+ */
41
+ export declare function startsWith(field: string, value: string): QueryFilter;
42
+ /**
43
+ * Creates an ends-with filter.
44
+ */
45
+ export declare function endsWith(field: string, value: string): QueryFilter;
46
+ /**
47
+ * Creates an IS NULL filter.
48
+ */
49
+ export declare function isNull(field: string): QueryFilter;
50
+ /**
51
+ * Creates an IS NOT NULL filter.
52
+ */
53
+ export declare function isNotNull(field: string): QueryFilter;
54
+ /**
55
+ * Combines filters using AND.
56
+ */
57
+ export declare function and(...filters: readonly QueryFilter[]): QueryFilter;
58
+ /**
59
+ * Combines filters using OR.
60
+ */
61
+ export declare function or(...filters: readonly QueryFilter[]): QueryFilter;
62
+ /**
63
+ * Negates a filter.
64
+ */
65
+ export declare function not(filter: QueryFilter): QueryFilter;
66
+ /**
67
+ * Creates a generic filter condition.
68
+ */
69
+ export declare function condition(field: string, operator: QueryOperator, value?: unknown): QueryFilter;
70
+ /**
71
+ * Combines multiple filters with AND only when necessary.
72
+ */
73
+ export declare function allOf(filters: readonly QueryFilter[]): QueryFilter;
74
+ /**
75
+ * Combines multiple filters with OR only when necessary.
76
+ */
77
+ export declare function anyOf(filters: readonly QueryFilter[]): QueryFilter;
78
+ /**
79
+ * Creates a filter from a plain object.
80
+ *
81
+ * Every property becomes an equality condition.
82
+ */
83
+ export declare function fromObject<T extends Record<string, unknown>>(values: T): QueryFilter;
84
+ /**
85
+ * Creates filters for a date range.
86
+ */
87
+ export declare function dateRange(field: string, options: {
88
+ readonly from?: Date;
89
+ readonly to?: Date;
90
+ }): QueryFilter;
91
+ /**
92
+ * Creates an inclusive range filter (`field >= from AND field <= to`).
93
+ */
94
+ export declare function between(field: string, from: number | string | Date | bigint, to: number | string | Date | bigint): QueryFilter;
95
+ /**
96
+ * Creates a SQL-style pattern filter using `%` wildcards.
97
+ *
98
+ * Only leading and/or trailing wildcards are supported (`%abc%`, `abc%`,
99
+ * `%abc`); the pattern is translated to `contains`, `startsWith`,
100
+ * `endsWith` or `equals` when converted for an ORM.
101
+ */
102
+ export declare function matchesPattern(field: string, pattern: string): QueryFilter;
103
+ /**
104
+ * Creates a filter matching null or empty-string values.
105
+ */
106
+ export declare function isEmpty(field: string): QueryFilter;
107
+ /**
108
+ * Creates a filter matching values that are neither null nor empty.
109
+ */
110
+ export declare function isNotEmpty(field: string): QueryFilter;
111
+ /**
112
+ * Creates a filter matching any timestamp on the given UTC calendar day.
113
+ */
114
+ export declare function dateOnly(field: string, date: Date): QueryFilter;
115
+ /**
116
+ * Creates a filter for timestamps strictly before a date.
117
+ */
118
+ export declare function isBefore(field: string, date: Date): QueryFilter;
119
+ /**
120
+ * Creates a filter for timestamps strictly after a date.
121
+ */
122
+ export declare function isAfter(field: string, date: Date): QueryFilter;
123
+ /**
124
+ * Creates an inclusive date range filter.
125
+ */
126
+ export declare function isBetween(field: string, from: Date, to: Date): QueryFilter;
127
+ /**
128
+ * Creates a negated single condition.
129
+ */
130
+ export declare function notCondition(field: string, operator: QueryOperator, value?: unknown): QueryFilter;
131
+ /**
132
+ * Creates a filter on a related entity.
133
+ *
134
+ * `some` / `every` / `none` target collection relations; `is` / `isNot`
135
+ * target single relations.
136
+ */
137
+ export declare function relational(relation: string, filter: QueryFilter, operator?: RelationOperator): QueryFilter;
138
+ /**
139
+ * Creates a filter for a value being one of several options.
140
+ */
141
+ export declare function oneOf<T>(field: string, values: readonly T[]): QueryFilter;
142
+ /**
143
+ * Creates a filter for a value not being one of several options.
144
+ */
145
+ export declare function noneOf<T>(field: string, values: readonly T[]): QueryFilter;
146
+ /**
147
+ * Creates an optional equality filter.
148
+ *
149
+ * Returns an empty filter when the value is undefined.
150
+ */
151
+ export declare function optionalEquals(field: string, value: unknown): QueryFilter;
152
+ /**
153
+ * Creates an optional text search filter.
154
+ */
155
+ export declare function optionalContains(field: string, value?: string | null): QueryFilter;
156
+ /**
157
+ * Checks whether a filter contains any actual constraints.
158
+ */
159
+ export declare function hasConditions(filter?: QueryFilter): boolean;
160
+ /**
161
+ * Flattens an AND-only filter into individual conditions.
162
+ */
163
+ export declare function flattenAnd(filter: QueryFilter): QueryCondition[];
164
+ /**
165
+ * Deeply clones a filter (conditions, nested groups, Date/array/object
166
+ * values) so the copy shares no mutable state with the source.
167
+ */
168
+ export declare function cloneFilter(filter: QueryFilter): QueryFilter;
169
+ export declare function cloneFilter(filter?: QueryFilter): QueryFilter | undefined;
170
+ //# sourceMappingURL=queryBuilder.filter.d.ts.map
@@ -0,0 +1,497 @@
1
+ const EMPTY_FILTER = Object.freeze({
2
+ conditions: Object.freeze([]),
3
+ });
4
+ const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
5
+ /**
6
+ * Creates an equality filter.
7
+ */
8
+ export function equals(field, value) {
9
+ return condition(field, "equals", value);
10
+ }
11
+ /**
12
+ * Creates a not-equal filter.
13
+ */
14
+ export function notEquals(field, value) {
15
+ return condition(field, "not", value);
16
+ }
17
+ /**
18
+ * Creates an IN filter.
19
+ */
20
+ export function inList(field, values) {
21
+ return condition(field, "in", [...values]);
22
+ }
23
+ /**
24
+ * Creates a NOT IN filter.
25
+ */
26
+ export function notInList(field, values) {
27
+ return condition(field, "notIn", [...values]);
28
+ }
29
+ /**
30
+ * Creates a less-than filter.
31
+ */
32
+ export function lessThan(field, value) {
33
+ return condition(field, "lt", value);
34
+ }
35
+ /**
36
+ * Creates a less-than-or-equal filter.
37
+ */
38
+ export function lessThanOrEqual(field, value) {
39
+ return condition(field, "lte", value);
40
+ }
41
+ /**
42
+ * Creates a greater-than filter.
43
+ */
44
+ export function greaterThan(field, value) {
45
+ return condition(field, "gt", value);
46
+ }
47
+ /**
48
+ * Creates a greater-than-or-equal filter.
49
+ */
50
+ export function greaterThanOrEqual(field, value) {
51
+ return condition(field, "gte", value);
52
+ }
53
+ /**
54
+ * Creates a contains filter.
55
+ */
56
+ export function contains(field, value) {
57
+ return condition(field, "contains", value);
58
+ }
59
+ /**
60
+ * Creates a starts-with filter.
61
+ */
62
+ export function startsWith(field, value) {
63
+ return condition(field, "startsWith", value);
64
+ }
65
+ /**
66
+ * Creates an ends-with filter.
67
+ */
68
+ export function endsWith(field, value) {
69
+ return condition(field, "endsWith", value);
70
+ }
71
+ /**
72
+ * Creates an IS NULL filter.
73
+ */
74
+ export function isNull(field) {
75
+ return condition(field, "isNull");
76
+ }
77
+ /**
78
+ * Creates an IS NOT NULL filter.
79
+ */
80
+ export function isNotNull(field) {
81
+ return condition(field, "isNotNull");
82
+ }
83
+ /**
84
+ * Combines filters using AND.
85
+ */
86
+ export function and(...filters) {
87
+ return {
88
+ and: filters.map((filter) => cloneFilter(filter)),
89
+ };
90
+ }
91
+ /**
92
+ * Combines filters using OR.
93
+ */
94
+ export function or(...filters) {
95
+ return {
96
+ or: filters.map((filter) => cloneFilter(filter)),
97
+ };
98
+ }
99
+ /**
100
+ * Negates a filter.
101
+ */
102
+ export function not(filter) {
103
+ return {
104
+ not: cloneFilter(filter),
105
+ };
106
+ }
107
+ /**
108
+ * Creates a generic filter condition.
109
+ */
110
+ export function condition(field, operator, value) {
111
+ validateField(field);
112
+ const result = {
113
+ field,
114
+ operator,
115
+ };
116
+ if (value !== undefined) {
117
+ result.value = cloneValue(value);
118
+ }
119
+ return {
120
+ conditions: [result],
121
+ };
122
+ }
123
+ /**
124
+ * Combines multiple filters with AND only when necessary.
125
+ */
126
+ export function allOf(filters) {
127
+ const normalized = filters.filter(hasConditions).map((filter) => cloneFilter(filter));
128
+ if (normalized.length === 0) {
129
+ return EMPTY_FILTER;
130
+ }
131
+ if (normalized.length === 1) {
132
+ return normalized[0];
133
+ }
134
+ return {
135
+ and: normalized,
136
+ };
137
+ }
138
+ /**
139
+ * Combines multiple filters with OR only when necessary.
140
+ */
141
+ export function anyOf(filters) {
142
+ const normalized = filters.filter(hasConditions).map((filter) => cloneFilter(filter));
143
+ if (normalized.length === 0) {
144
+ return EMPTY_FILTER;
145
+ }
146
+ if (normalized.length === 1) {
147
+ return normalized[0];
148
+ }
149
+ return {
150
+ or: normalized,
151
+ };
152
+ }
153
+ /**
154
+ * Creates a filter from a plain object.
155
+ *
156
+ * Every property becomes an equality condition.
157
+ */
158
+ export function fromObject(values) {
159
+ if (values === null || typeof values !== "object" || Array.isArray(values)) {
160
+ throw new TypeError("fromObject requires a plain object.");
161
+ }
162
+ const conditions = [];
163
+ for (const [field, value] of Object.entries(values)) {
164
+ if (FORBIDDEN_KEYS.has(field)) {
165
+ throw new TypeError(`Invalid filter field "${field}".`);
166
+ }
167
+ conditions.push(...condition(field, "equals", value).conditions);
168
+ }
169
+ return {
170
+ conditions,
171
+ };
172
+ }
173
+ /**
174
+ * Creates filters for a date range.
175
+ */
176
+ export function dateRange(field, options) {
177
+ validateField(field);
178
+ const conditions = [];
179
+ if (options.from) {
180
+ validateDate(options.from, "from");
181
+ conditions.push({
182
+ field,
183
+ operator: "gte",
184
+ value: new Date(options.from.getTime()),
185
+ });
186
+ }
187
+ if (options.to) {
188
+ validateDate(options.to, "to");
189
+ conditions.push({
190
+ field,
191
+ operator: "lte",
192
+ value: new Date(options.to.getTime()),
193
+ });
194
+ }
195
+ if (options.from && options.to && options.from.getTime() > options.to.getTime()) {
196
+ throw new RangeError("dateRange requires `from` to be on or before `to`.");
197
+ }
198
+ return {
199
+ conditions,
200
+ };
201
+ }
202
+ /**
203
+ * Creates an inclusive range filter (`field >= from AND field <= to`).
204
+ */
205
+ export function between(field, from, to) {
206
+ validateField(field);
207
+ validateRangeBound(from, "from");
208
+ validateRangeBound(to, "to");
209
+ if (compareBounds(from, to) > 0) {
210
+ throw new RangeError("between requires `from` to be on or before `to`.");
211
+ }
212
+ return {
213
+ conditions: [
214
+ {
215
+ field,
216
+ operator: "gte",
217
+ value: cloneValue(from),
218
+ },
219
+ {
220
+ field,
221
+ operator: "lte",
222
+ value: cloneValue(to),
223
+ },
224
+ ],
225
+ };
226
+ }
227
+ /**
228
+ * Creates a SQL-style pattern filter using `%` wildcards.
229
+ *
230
+ * Only leading and/or trailing wildcards are supported (`%abc%`, `abc%`,
231
+ * `%abc`); the pattern is translated to `contains`, `startsWith`,
232
+ * `endsWith` or `equals` when converted for an ORM.
233
+ */
234
+ export function matchesPattern(field, pattern) {
235
+ if (typeof pattern !== "string" || pattern.length === 0) {
236
+ throw new TypeError("A pattern is required.");
237
+ }
238
+ // The lookbehind pins the trailing match to the start of a `%` run; plain
239
+ // `%+$` retries from every `%` in an interior run, which is quadratic.
240
+ const inner = pattern.replace(/^%+/, "").replace(/(?<!%)%+$/, "");
241
+ if (inner.length === 0 || inner.includes("%") || inner.includes("_")) {
242
+ throw new TypeError("matchesPattern only supports leading and/or trailing % wildcards.");
243
+ }
244
+ return condition(field, "like", pattern);
245
+ }
246
+ /**
247
+ * Creates a filter matching null or empty-string values.
248
+ */
249
+ export function isEmpty(field) {
250
+ return or(isNull(field), equals(field, ""));
251
+ }
252
+ /**
253
+ * Creates a filter matching values that are neither null nor empty.
254
+ */
255
+ export function isNotEmpty(field) {
256
+ return and(isNotNull(field), notEquals(field, ""));
257
+ }
258
+ /**
259
+ * Creates a filter matching any timestamp on the given UTC calendar day.
260
+ */
261
+ export function dateOnly(field, date) {
262
+ validateField(field);
263
+ validateDate(date, "date");
264
+ const start = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
265
+ const end = new Date(start.getTime() + 24 * 60 * 60 * 1000);
266
+ return {
267
+ conditions: [
268
+ {
269
+ field,
270
+ operator: "gte",
271
+ value: start,
272
+ },
273
+ {
274
+ field,
275
+ operator: "lt",
276
+ value: end,
277
+ },
278
+ ],
279
+ };
280
+ }
281
+ /**
282
+ * Creates a filter for timestamps strictly before a date.
283
+ */
284
+ export function isBefore(field, date) {
285
+ validateDate(date, "date");
286
+ return condition(field, "lt", date);
287
+ }
288
+ /**
289
+ * Creates a filter for timestamps strictly after a date.
290
+ */
291
+ export function isAfter(field, date) {
292
+ validateDate(date, "date");
293
+ return condition(field, "gt", date);
294
+ }
295
+ /**
296
+ * Creates an inclusive date range filter.
297
+ */
298
+ export function isBetween(field, from, to) {
299
+ validateDate(from, "from");
300
+ validateDate(to, "to");
301
+ return between(field, from, to);
302
+ }
303
+ /**
304
+ * Creates a negated single condition.
305
+ */
306
+ export function notCondition(field, operator, value) {
307
+ return not(condition(field, operator, value));
308
+ }
309
+ /**
310
+ * Creates a filter on a related entity.
311
+ *
312
+ * `some` / `every` / `none` target collection relations; `is` / `isNot`
313
+ * target single relations.
314
+ */
315
+ export function relational(relation, filter, operator = "some") {
316
+ if (operator !== "some" &&
317
+ operator !== "every" &&
318
+ operator !== "none" &&
319
+ operator !== "is" &&
320
+ operator !== "isNot") {
321
+ throw new TypeError(`Invalid relation operator "${String(operator)}".`);
322
+ }
323
+ if (!filter || typeof filter !== "object") {
324
+ throw new TypeError("A relation filter is required.");
325
+ }
326
+ validateField(relation);
327
+ return {
328
+ conditions: [
329
+ {
330
+ field: relation,
331
+ operator,
332
+ value: cloneFilter(filter),
333
+ },
334
+ ],
335
+ };
336
+ }
337
+ /**
338
+ * Creates a filter for a value being one of several options.
339
+ */
340
+ export function oneOf(field, values) {
341
+ return inList(field, values);
342
+ }
343
+ /**
344
+ * Creates a filter for a value not being one of several options.
345
+ */
346
+ export function noneOf(field, values) {
347
+ return notInList(field, values);
348
+ }
349
+ /**
350
+ * Creates an optional equality filter.
351
+ *
352
+ * Returns an empty filter when the value is undefined.
353
+ */
354
+ export function optionalEquals(field, value) {
355
+ if (value === undefined) {
356
+ return {
357
+ conditions: [],
358
+ };
359
+ }
360
+ return equals(field, value);
361
+ }
362
+ /**
363
+ * Creates an optional text search filter.
364
+ */
365
+ export function optionalContains(field, value) {
366
+ if (value === undefined || value === null || value.trim().length === 0) {
367
+ return {
368
+ conditions: [],
369
+ };
370
+ }
371
+ return contains(field, value.trim());
372
+ }
373
+ /**
374
+ * Checks whether a filter contains any actual constraints.
375
+ */
376
+ export function hasConditions(filter) {
377
+ if (!filter) {
378
+ return false;
379
+ }
380
+ if (filter.conditions && filter.conditions.length > 0) {
381
+ return true;
382
+ }
383
+ if (filter.and?.some(hasConditions)) {
384
+ return true;
385
+ }
386
+ if (filter.or?.some(hasConditions)) {
387
+ return true;
388
+ }
389
+ return filter.not ? hasConditions(filter.not) : false;
390
+ }
391
+ /**
392
+ * Flattens an AND-only filter into individual conditions.
393
+ */
394
+ export function flattenAnd(filter) {
395
+ if ((filter.or && filter.or.length > 0) || filter.not) {
396
+ throw new TypeError("flattenAnd only accepts AND-only filters; OR/NOT branches cannot be flattened.");
397
+ }
398
+ const result = [];
399
+ if (filter.conditions) {
400
+ result.push(...filter.conditions.map(cloneCondition));
401
+ }
402
+ for (const child of filter.and ?? []) {
403
+ result.push(...flattenAnd(child));
404
+ }
405
+ return result;
406
+ }
407
+ export function cloneFilter(filter) {
408
+ if (!filter) {
409
+ return undefined;
410
+ }
411
+ return {
412
+ conditions: filter.conditions
413
+ ? filter.conditions.map(cloneCondition)
414
+ : undefined,
415
+ and: filter.and ? filter.and.map((child) => cloneFilter(child)) : undefined,
416
+ or: filter.or ? filter.or.map((child) => cloneFilter(child)) : undefined,
417
+ not: filter.not ? cloneFilter(filter.not) : undefined,
418
+ };
419
+ }
420
+ /**
421
+ * Clones a single condition.
422
+ */
423
+ function cloneCondition(condition) {
424
+ return {
425
+ ...condition,
426
+ ...(condition.value !== undefined
427
+ ? {
428
+ value: cloneValue(condition.value),
429
+ }
430
+ : {}),
431
+ };
432
+ }
433
+ /**
434
+ * Clones supported filter values.
435
+ */
436
+ function cloneValue(value) {
437
+ if (value instanceof Date) {
438
+ return new Date(value.getTime());
439
+ }
440
+ if (Array.isArray(value)) {
441
+ return value.map(cloneValue);
442
+ }
443
+ if (value && typeof value === "object") {
444
+ const result = {};
445
+ for (const [key, entry] of Object.entries(value)) {
446
+ if (FORBIDDEN_KEYS.has(key)) {
447
+ continue;
448
+ }
449
+ result[key] = cloneValue(entry);
450
+ }
451
+ return result;
452
+ }
453
+ return value;
454
+ }
455
+ function validateRangeBound(value, name) {
456
+ if (value instanceof Date) {
457
+ validateDate(value, name);
458
+ return;
459
+ }
460
+ if (typeof value === "number" && Number.isNaN(value)) {
461
+ throw new TypeError(`Invalid ${name} bound.`);
462
+ }
463
+ if (typeof value !== "number" &&
464
+ typeof value !== "string" &&
465
+ typeof value !== "bigint") {
466
+ throw new TypeError(`Invalid ${name} bound.`);
467
+ }
468
+ }
469
+ function compareBounds(from, to) {
470
+ if (from instanceof Date && to instanceof Date) {
471
+ return from.getTime() - to.getTime();
472
+ }
473
+ if (typeof from === typeof to && from > to) {
474
+ return 1;
475
+ }
476
+ return 0;
477
+ }
478
+ /**
479
+ * Validates a filter field.
480
+ */
481
+ function validateField(field) {
482
+ if (typeof field !== "string" || field.trim().length === 0) {
483
+ throw new TypeError("A filter field is required.");
484
+ }
485
+ if (FORBIDDEN_KEYS.has(field)) {
486
+ throw new TypeError(`Invalid filter field "${field}".`);
487
+ }
488
+ }
489
+ /**
490
+ * Validates a date value.
491
+ */
492
+ function validateDate(value, name) {
493
+ if (!(value instanceof Date) || Number.isNaN(value.getTime())) {
494
+ throw new TypeError(`Invalid ${name} date.`);
495
+ }
496
+ }
497
+ //# sourceMappingURL=queryBuilder.filter.js.map