@zudojs/database 0.1.0 → 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.
- package/LICENSE +21 -0
- package/README.md +223 -17
- package/dist/cache/cache.memory.d.ts +167 -0
- package/dist/cache/cache.memory.js +186 -40
- package/dist/cache/index.d.ts +2 -2
- package/dist/cache/index.js +2 -2
- package/dist/database/database.core.d.ts +91 -0
- package/dist/database/database.core.js +21 -4
- package/dist/databaseClient/databaseClient.core.d.ts +165 -14
- package/dist/databaseClient/databaseClient.core.js +304 -70
- package/dist/databaseClient/databaseClient.errors.d.ts +112 -0
- package/dist/databaseClient/databaseClient.errors.js +425 -0
- package/dist/databaseClient/index.d.ts +2 -1
- package/dist/databaseClient/index.js +2 -1
- package/dist/databaseConnection/databaseConnection.manager.d.ts +163 -0
- package/dist/databaseConnection/databaseConnection.manager.js +138 -76
- package/dist/databaseConnection/index.d.ts +1 -1
- package/dist/databaseType/databaseType.type.d.ts +224 -0
- package/dist/databaseType/databaseType.type.js +0 -35
- package/dist/health/health.check.d.ts +74 -0
- package/dist/health/health.check.js +78 -47
- package/dist/health/index.d.ts +1 -1
- package/dist/health/index.js +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +11 -11
- package/dist/locks/index.d.ts +2 -2
- package/dist/locks/index.js +2 -2
- package/dist/locks/locks.core.d.ts +128 -0
- package/dist/locks/locks.core.js +144 -85
- package/dist/migration/index.d.ts +3 -1
- package/dist/migration/index.js +2 -0
- package/dist/migration/migration.dialect.d.ts +65 -0
- package/dist/migration/migration.dialect.js +63 -0
- package/dist/migration/migration.helpers.d.ts +65 -0
- package/dist/migration/migration.helpers.js +60 -18
- package/dist/migration/migration.runner.d.ts +75 -0
- package/dist/migration/migration.runner.js +195 -107
- package/dist/migration/migration.types.d.ts +85 -0
- package/dist/pagination/index.d.ts +2 -1
- package/dist/pagination/index.js +2 -1
- package/dist/pagination/pagination.core.d.ts +163 -0
- package/dist/pagination/pagination.core.js +78 -9
- package/dist/pagination/pagination.keyset.d.ts +55 -0
- package/dist/pagination/pagination.keyset.js +108 -0
- package/dist/queryBuilder/index.d.ts +4 -3
- package/dist/queryBuilder/index.js +3 -2
- package/dist/queryBuilder/queryBuilder.core.d.ts +178 -0
- package/dist/queryBuilder/queryBuilder.core.js +92 -14
- package/dist/queryBuilder/queryBuilder.factory.d.ts +6 -0
- package/dist/queryBuilder/queryBuilder.factory.js +0 -25
- package/dist/queryBuilder/queryBuilder.filter.d.ts +170 -0
- package/dist/queryBuilder/queryBuilder.filter.js +191 -30
- package/dist/queryBuilder/queryBuilder.prisma.d.ts +60 -0
- package/dist/queryBuilder/queryBuilder.prisma.js +233 -0
- package/dist/queryBuilder/queryBuilder.type.d.ts +48 -0
- package/dist/relations/index.d.ts +1 -1
- package/dist/relations/index.js +1 -1
- package/dist/relations/relations.definition.d.ts +184 -0
- package/dist/relations/relations.definition.js +186 -19
- package/dist/repository/index.d.ts +2 -1
- package/dist/repository/index.js +1 -0
- package/dist/repository/repository.base.d.ts +270 -0
- package/dist/repository/repository.base.js +350 -84
- package/dist/repository/repository.errors.d.ts +60 -0
- package/dist/repository/repository.errors.js +235 -0
- package/dist/seed/index.d.ts +2 -2
- package/dist/seed/index.js +1 -1
- package/dist/seed/seed.runner.d.ts +160 -0
- package/dist/seed/seed.runner.js +194 -225
- package/dist/transaction/index.d.ts +1 -1
- package/dist/transaction/index.js +1 -1
- package/dist/transaction/transaction.core.d.ts +117 -0
- package/dist/transaction/transaction.core.js +83 -56
- package/dist/unitOfWork/unitOfWork.core.d.ts +44 -0
- package/dist/unitOfWork/unitOfWork.core.js +9 -20
- package/package.json +40 -24
- package/dist/cache/cache.memory.d.ts.map +0 -1
- package/dist/cache/cache.memory.js.map +0 -1
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/index.js.map +0 -1
- package/dist/database/database.core.d.ts.map +0 -1
- package/dist/database/database.core.js.map +0 -1
- package/dist/database/index.d.ts.map +0 -1
- package/dist/database/index.js.map +0 -1
- package/dist/databaseClient/databaseClient.core.d.ts.map +0 -1
- package/dist/databaseClient/databaseClient.core.js.map +0 -1
- package/dist/databaseClient/databaseClient.logger.d.ts.map +0 -1
- package/dist/databaseClient/databaseClient.logger.js.map +0 -1
- package/dist/databaseClient/index.d.ts.map +0 -1
- package/dist/databaseClient/index.js.map +0 -1
- package/dist/databaseConnection/databaseConnection.manager.d.ts.map +0 -1
- package/dist/databaseConnection/databaseConnection.manager.js.map +0 -1
- package/dist/databaseConnection/index.d.ts.map +0 -1
- package/dist/databaseConnection/index.js.map +0 -1
- package/dist/databaseType/databaseType.type.d.ts.map +0 -1
- package/dist/databaseType/databaseType.type.js.map +0 -1
- package/dist/databaseType/index.d.ts.map +0 -1
- package/dist/databaseType/index.js.map +0 -1
- package/dist/health/health.check.d.ts.map +0 -1
- package/dist/health/health.check.js.map +0 -1
- package/dist/health/index.d.ts.map +0 -1
- package/dist/health/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/locks/index.d.ts.map +0 -1
- package/dist/locks/index.js.map +0 -1
- package/dist/locks/locks.core.d.ts.map +0 -1
- package/dist/locks/locks.core.js.map +0 -1
- package/dist/migration/index.d.ts.map +0 -1
- package/dist/migration/index.js.map +0 -1
- package/dist/migration/migration.helpers.d.ts.map +0 -1
- package/dist/migration/migration.helpers.js.map +0 -1
- package/dist/migration/migration.runner.d.ts.map +0 -1
- package/dist/migration/migration.runner.js.map +0 -1
- package/dist/migration/migration.types.d.ts.map +0 -1
- package/dist/migration/migration.types.js.map +0 -1
- package/dist/pagination/index.d.ts.map +0 -1
- package/dist/pagination/index.js.map +0 -1
- package/dist/pagination/pagination.core.d.ts.map +0 -1
- package/dist/pagination/pagination.core.js.map +0 -1
- package/dist/queryBuilder/index.d.ts.map +0 -1
- package/dist/queryBuilder/index.js.map +0 -1
- package/dist/queryBuilder/queryBuilder.core.d.ts.map +0 -1
- package/dist/queryBuilder/queryBuilder.core.js.map +0 -1
- package/dist/queryBuilder/queryBuilder.factory.d.ts.map +0 -1
- package/dist/queryBuilder/queryBuilder.factory.js.map +0 -1
- package/dist/queryBuilder/queryBuilder.filter.d.ts.map +0 -1
- package/dist/queryBuilder/queryBuilder.filter.js.map +0 -1
- package/dist/queryBuilder/queryBuilder.type.d.ts.map +0 -1
- package/dist/queryBuilder/queryBuilder.type.js.map +0 -1
- package/dist/relations/index.d.ts.map +0 -1
- package/dist/relations/index.js.map +0 -1
- package/dist/relations/relations.definition.d.ts.map +0 -1
- package/dist/relations/relations.definition.js.map +0 -1
- package/dist/repository/index.d.ts.map +0 -1
- package/dist/repository/index.js.map +0 -1
- package/dist/repository/repository.base.d.ts.map +0 -1
- package/dist/repository/repository.base.js.map +0 -1
- package/dist/seed/index.d.ts.map +0 -1
- package/dist/seed/index.js.map +0 -1
- package/dist/seed/seed.runner.d.ts.map +0 -1
- package/dist/seed/seed.runner.js.map +0 -1
- package/dist/transaction/index.d.ts.map +0 -1
- package/dist/transaction/index.js.map +0 -1
- package/dist/transaction/transaction.core.d.ts.map +0 -1
- package/dist/transaction/transaction.core.js.map +0 -1
- package/dist/unitOfWork/index.d.ts.map +0 -1
- package/dist/unitOfWork/index.js.map +0 -1
- package/dist/unitOfWork/unitOfWork.core.d.ts.map +0 -1
- package/dist/unitOfWork/unitOfWork.core.js.map +0 -1
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { cloneFilter } from "./queryBuilder.
|
|
1
|
+
import { cloneFilter } from "./queryBuilder.filter.js";
|
|
2
|
+
import { toPrismaArgs, } from "./queryBuilder.prisma.js";
|
|
3
|
+
import { includeRelation, } from "../relations/relations.definition.js";
|
|
2
4
|
/**
|
|
3
5
|
* Query builder used to construct database-neutral query definitions.
|
|
4
6
|
*
|
|
@@ -11,11 +13,11 @@ export class QueryBuilder {
|
|
|
11
13
|
paginationState;
|
|
12
14
|
sortState = [];
|
|
13
15
|
selectState = [];
|
|
14
|
-
|
|
16
|
+
offsetState;
|
|
17
|
+
includeState = [];
|
|
15
18
|
_cachedBuild;
|
|
16
19
|
invalidateCache() {
|
|
17
20
|
this._cachedBuild = undefined;
|
|
18
|
-
this._revision++;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Adds an equality condition.
|
|
@@ -189,6 +191,34 @@ export class QueryBuilder {
|
|
|
189
191
|
this.invalidateCache();
|
|
190
192
|
return this;
|
|
191
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Sets an explicit row offset. Takes precedence over `page()`.
|
|
196
|
+
*/
|
|
197
|
+
offset(offset) {
|
|
198
|
+
if (!Number.isFinite(offset) || offset < 0) {
|
|
199
|
+
throw new TypeError("Query offset must be a non-negative number.");
|
|
200
|
+
}
|
|
201
|
+
this.offsetState = Math.floor(offset);
|
|
202
|
+
this.invalidateCache();
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Adds relations to include. Accepts relation names or include
|
|
207
|
+
* definitions created with `includeRelation`.
|
|
208
|
+
*/
|
|
209
|
+
include(...relations) {
|
|
210
|
+
for (const relation of relations) {
|
|
211
|
+
const include = typeof relation === "string" ? includeRelation(relation) : relation;
|
|
212
|
+
if (!include || typeof include.relation !== "string") {
|
|
213
|
+
throw new TypeError("A relation include is required.");
|
|
214
|
+
}
|
|
215
|
+
if (!this.includeState.some((e) => e.relation === include.relation)) {
|
|
216
|
+
this.includeState.push(include);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
this.invalidateCache();
|
|
220
|
+
return this;
|
|
221
|
+
}
|
|
192
222
|
/**
|
|
193
223
|
* Sorts ascending by a field.
|
|
194
224
|
*/
|
|
@@ -260,6 +290,27 @@ export class QueryBuilder {
|
|
|
260
290
|
this.invalidateCache();
|
|
261
291
|
return this;
|
|
262
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Clears included relations.
|
|
295
|
+
*/
|
|
296
|
+
clearInclude() {
|
|
297
|
+
this.includeState = [];
|
|
298
|
+
this.invalidateCache();
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Resets the builder to its initial empty state.
|
|
303
|
+
*/
|
|
304
|
+
reset() {
|
|
305
|
+
this.filterState = undefined;
|
|
306
|
+
this.paginationState = undefined;
|
|
307
|
+
this.offsetState = undefined;
|
|
308
|
+
this.sortState = [];
|
|
309
|
+
this.selectState = [];
|
|
310
|
+
this.includeState = [];
|
|
311
|
+
this.invalidateCache();
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
263
314
|
/**
|
|
264
315
|
* Returns the immutable query definition.
|
|
265
316
|
*/
|
|
@@ -267,32 +318,41 @@ export class QueryBuilder {
|
|
|
267
318
|
if (this._cachedBuild) {
|
|
268
319
|
return this._cachedBuild;
|
|
269
320
|
}
|
|
270
|
-
const result =
|
|
321
|
+
const result = deepFreeze({
|
|
271
322
|
filter: cloneFilter(this.filterState),
|
|
272
323
|
pagination: this.paginationState
|
|
273
|
-
?
|
|
274
|
-
...this.paginationState,
|
|
275
|
-
})
|
|
324
|
+
? { ...this.paginationState }
|
|
276
325
|
: undefined,
|
|
326
|
+
offset: this.offsetState,
|
|
277
327
|
sort: this.sortState.length > 0
|
|
278
|
-
?
|
|
279
|
-
: undefined,
|
|
280
|
-
select: this.selectState.length > 0
|
|
281
|
-
? Object.freeze([...this.selectState])
|
|
328
|
+
? this.sortState.map((entry) => ({ ...entry }))
|
|
282
329
|
: undefined,
|
|
330
|
+
select: this.selectState.length > 0 ? [...this.selectState] : undefined,
|
|
331
|
+
include: this.includeState.length > 0 ? [...this.includeState] : undefined,
|
|
283
332
|
});
|
|
284
333
|
this._cachedBuild = result;
|
|
285
334
|
return result;
|
|
286
335
|
}
|
|
287
336
|
/**
|
|
288
|
-
* Converts the builder into generic query options
|
|
337
|
+
* Converts the builder into generic query options (pagination, sort and
|
|
338
|
+
* the built filter).
|
|
289
339
|
*/
|
|
290
340
|
toQueryOptions() {
|
|
291
341
|
return {
|
|
292
|
-
|
|
342
|
+
filter: cloneFilter(this.filterState),
|
|
343
|
+
pagination: this.paginationState
|
|
344
|
+
? { ...this.paginationState }
|
|
345
|
+
: undefined,
|
|
346
|
+
offset: this.offsetState,
|
|
293
347
|
sort: this.sortState.length > 0 ? [...this.sortState] : undefined,
|
|
294
348
|
};
|
|
295
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Translates the builder into Prisma `findMany`-style arguments.
|
|
352
|
+
*/
|
|
353
|
+
toPrismaArgs(options) {
|
|
354
|
+
return toPrismaArgs(this.build(), options);
|
|
355
|
+
}
|
|
296
356
|
/**
|
|
297
357
|
* Creates an independent copy of the builder.
|
|
298
358
|
*/
|
|
@@ -304,8 +364,10 @@ export class QueryBuilder {
|
|
|
304
364
|
...this.paginationState,
|
|
305
365
|
}
|
|
306
366
|
: undefined;
|
|
307
|
-
builder.
|
|
367
|
+
builder.offsetState = this.offsetState;
|
|
368
|
+
builder.sortState = this.sortState.map((entry) => ({ ...entry }));
|
|
308
369
|
builder.selectState = [...this.selectState];
|
|
370
|
+
builder.includeState = [...this.includeState];
|
|
309
371
|
return builder;
|
|
310
372
|
}
|
|
311
373
|
/**
|
|
@@ -339,4 +401,20 @@ export class QueryBuilder {
|
|
|
339
401
|
return this;
|
|
340
402
|
}
|
|
341
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Recursively freezes plain objects and arrays.
|
|
406
|
+
*/
|
|
407
|
+
function deepFreeze(value) {
|
|
408
|
+
if (value === null || typeof value !== "object" || Object.isFrozen(value)) {
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
if (value instanceof Date) {
|
|
412
|
+
return value;
|
|
413
|
+
}
|
|
414
|
+
Object.freeze(value);
|
|
415
|
+
for (const entry of Object.values(value)) {
|
|
416
|
+
deepFreeze(entry);
|
|
417
|
+
}
|
|
418
|
+
return value;
|
|
419
|
+
}
|
|
342
420
|
//# sourceMappingURL=queryBuilder.core.js.map
|
|
@@ -5,29 +5,4 @@ import { QueryBuilder } from "./queryBuilder.core.js";
|
|
|
5
5
|
export function createQueryBuilder() {
|
|
6
6
|
return new QueryBuilder();
|
|
7
7
|
}
|
|
8
|
-
/**
|
|
9
|
-
* Clones a query filter without sharing mutable arrays.
|
|
10
|
-
*/
|
|
11
|
-
export function cloneFilter(filter) {
|
|
12
|
-
if (!filter) {
|
|
13
|
-
return undefined;
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
conditions: filter.conditions
|
|
17
|
-
? filter.conditions.map((condition) => ({
|
|
18
|
-
...condition,
|
|
19
|
-
...(Array.isArray(condition.value)
|
|
20
|
-
? {
|
|
21
|
-
value: [...condition.value],
|
|
22
|
-
}
|
|
23
|
-
: {}),
|
|
24
|
-
}))
|
|
25
|
-
: undefined,
|
|
26
|
-
and: filter.and
|
|
27
|
-
? filter.and.map((child) => cloneFilter(child))
|
|
28
|
-
: undefined,
|
|
29
|
-
or: filter.or ? filter.or.map((child) => cloneFilter(child)) : undefined,
|
|
30
|
-
not: filter.not ? cloneFilter(filter.not) : undefined,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
8
|
//# sourceMappingURL=queryBuilder.factory.js.map
|
|
@@ -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
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
const EMPTY_FILTER = Object.freeze({
|
|
2
|
+
conditions: Object.freeze([]),
|
|
3
|
+
});
|
|
4
|
+
const FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
1
5
|
/**
|
|
2
6
|
* Creates an equality filter.
|
|
3
7
|
*/
|
|
@@ -81,7 +85,7 @@ export function isNotNull(field) {
|
|
|
81
85
|
*/
|
|
82
86
|
export function and(...filters) {
|
|
83
87
|
return {
|
|
84
|
-
and: filters.map(cloneFilter),
|
|
88
|
+
and: filters.map((filter) => cloneFilter(filter)),
|
|
85
89
|
};
|
|
86
90
|
}
|
|
87
91
|
/**
|
|
@@ -89,7 +93,7 @@ export function and(...filters) {
|
|
|
89
93
|
*/
|
|
90
94
|
export function or(...filters) {
|
|
91
95
|
return {
|
|
92
|
-
or: filters.map(cloneFilter),
|
|
96
|
+
or: filters.map((filter) => cloneFilter(filter)),
|
|
93
97
|
};
|
|
94
98
|
}
|
|
95
99
|
/**
|
|
@@ -120,11 +124,9 @@ export function condition(field, operator, value) {
|
|
|
120
124
|
* Combines multiple filters with AND only when necessary.
|
|
121
125
|
*/
|
|
122
126
|
export function allOf(filters) {
|
|
123
|
-
const normalized = filters.filter(
|
|
127
|
+
const normalized = filters.filter(hasConditions).map((filter) => cloneFilter(filter));
|
|
124
128
|
if (normalized.length === 0) {
|
|
125
|
-
return
|
|
126
|
-
conditions: [],
|
|
127
|
-
};
|
|
129
|
+
return EMPTY_FILTER;
|
|
128
130
|
}
|
|
129
131
|
if (normalized.length === 1) {
|
|
130
132
|
return normalized[0];
|
|
@@ -137,11 +139,9 @@ export function allOf(filters) {
|
|
|
137
139
|
* Combines multiple filters with OR only when necessary.
|
|
138
140
|
*/
|
|
139
141
|
export function anyOf(filters) {
|
|
140
|
-
const normalized = filters.filter(
|
|
142
|
+
const normalized = filters.filter(hasConditions).map((filter) => cloneFilter(filter));
|
|
141
143
|
if (normalized.length === 0) {
|
|
142
|
-
return
|
|
143
|
-
conditions: [],
|
|
144
|
-
};
|
|
144
|
+
return EMPTY_FILTER;
|
|
145
145
|
}
|
|
146
146
|
if (normalized.length === 1) {
|
|
147
147
|
return normalized[0];
|
|
@@ -156,11 +156,16 @@ export function anyOf(filters) {
|
|
|
156
156
|
* Every property becomes an equality condition.
|
|
157
157
|
*/
|
|
158
158
|
export function fromObject(values) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
+
}
|
|
164
169
|
return {
|
|
165
170
|
conditions,
|
|
166
171
|
};
|
|
@@ -187,20 +192,144 @@ export function dateRange(field, options) {
|
|
|
187
192
|
value: new Date(options.to.getTime()),
|
|
188
193
|
});
|
|
189
194
|
}
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
conditions: [],
|
|
193
|
-
};
|
|
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`.");
|
|
194
197
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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`.");
|
|
199
211
|
}
|
|
200
212
|
return {
|
|
201
|
-
|
|
213
|
+
conditions: [
|
|
214
|
+
{
|
|
215
|
+
field,
|
|
216
|
+
operator: "gte",
|
|
217
|
+
value: cloneValue(from),
|
|
218
|
+
},
|
|
202
219
|
{
|
|
203
|
-
|
|
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),
|
|
204
333
|
},
|
|
205
334
|
],
|
|
206
335
|
};
|
|
@@ -263,6 +392,9 @@ export function hasConditions(filter) {
|
|
|
263
392
|
* Flattens an AND-only filter into individual conditions.
|
|
264
393
|
*/
|
|
265
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
|
+
}
|
|
266
398
|
const result = [];
|
|
267
399
|
if (filter.conditions) {
|
|
268
400
|
result.push(...filter.conditions.map(cloneCondition));
|
|
@@ -272,16 +404,16 @@ export function flattenAnd(filter) {
|
|
|
272
404
|
}
|
|
273
405
|
return result;
|
|
274
406
|
}
|
|
275
|
-
/**
|
|
276
|
-
* Creates a new filter without mutating the source.
|
|
277
|
-
*/
|
|
278
407
|
export function cloneFilter(filter) {
|
|
408
|
+
if (!filter) {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
279
411
|
return {
|
|
280
412
|
conditions: filter.conditions
|
|
281
413
|
? filter.conditions.map(cloneCondition)
|
|
282
414
|
: undefined,
|
|
283
|
-
and: filter.and ? filter.and.map(cloneFilter) : undefined,
|
|
284
|
-
or: filter.or ? filter.or.map(cloneFilter) : undefined,
|
|
415
|
+
and: filter.and ? filter.and.map((child) => cloneFilter(child)) : undefined,
|
|
416
|
+
or: filter.or ? filter.or.map((child) => cloneFilter(child)) : undefined,
|
|
285
417
|
not: filter.not ? cloneFilter(filter.not) : undefined,
|
|
286
418
|
};
|
|
287
419
|
}
|
|
@@ -311,12 +443,38 @@ function cloneValue(value) {
|
|
|
311
443
|
if (value && typeof value === "object") {
|
|
312
444
|
const result = {};
|
|
313
445
|
for (const [key, entry] of Object.entries(value)) {
|
|
446
|
+
if (FORBIDDEN_KEYS.has(key)) {
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
314
449
|
result[key] = cloneValue(entry);
|
|
315
450
|
}
|
|
316
451
|
return result;
|
|
317
452
|
}
|
|
318
453
|
return value;
|
|
319
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
|
+
}
|
|
320
478
|
/**
|
|
321
479
|
* Validates a filter field.
|
|
322
480
|
*/
|
|
@@ -324,6 +482,9 @@ function validateField(field) {
|
|
|
324
482
|
if (typeof field !== "string" || field.trim().length === 0) {
|
|
325
483
|
throw new TypeError("A filter field is required.");
|
|
326
484
|
}
|
|
485
|
+
if (FORBIDDEN_KEYS.has(field)) {
|
|
486
|
+
throw new TypeError(`Invalid filter field "${field}".`);
|
|
487
|
+
}
|
|
327
488
|
}
|
|
328
489
|
/**
|
|
329
490
|
* Validates a date value.
|