@mikro-orm/core 7.0.0-dev.30 → 7.0.0-dev.301
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/EntityManager.d.ts +69 -61
- package/EntityManager.js +365 -283
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -142
- package/README.md +2 -0
- package/cache/FileCacheAdapter.d.ts +1 -2
- package/cache/FileCacheAdapter.js +19 -14
- package/cache/GeneratedCacheAdapter.d.ts +0 -1
- package/cache/GeneratedCacheAdapter.js +0 -2
- package/cache/index.d.ts +0 -1
- package/cache/index.js +0 -1
- package/connections/Connection.d.ts +12 -5
- package/connections/Connection.js +37 -15
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +144 -43
- package/drivers/IDatabaseDriver.d.ts +118 -23
- package/entity/BaseEntity.d.ts +63 -4
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +473 -115
- package/entity/EntityAssigner.js +37 -25
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +116 -64
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +69 -27
- package/entity/EntityLoader.d.ts +11 -10
- package/entity/EntityLoader.js +262 -98
- package/entity/EntityRepository.d.ts +28 -8
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +2 -6
- package/entity/Reference.js +52 -19
- package/entity/WrappedEntity.d.ts +3 -8
- package/entity/WrappedEntity.js +6 -7
- package/entity/defineEntity.d.ts +525 -311
- package/entity/defineEntity.js +134 -290
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.d.ts +6 -1
- package/entity/utils.js +46 -11
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +66 -0
- package/enums.d.ts +8 -6
- package/enums.js +13 -17
- package/errors.d.ts +20 -10
- package/errors.js +73 -31
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +20 -12
- package/exceptions.js +7 -2
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +102 -43
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +3 -4
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +5 -7
- package/logging/index.d.ts +1 -0
- package/logging/index.js +1 -0
- package/logging/inspect.d.ts +2 -0
- package/logging/inspect.js +11 -0
- package/metadata/EntitySchema.d.ts +47 -23
- package/metadata/EntitySchema.js +103 -34
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +864 -352
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +71 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +72 -41
- package/metadata/MetadataValidator.d.ts +32 -9
- package/metadata/MetadataValidator.js +214 -44
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +40 -0
- package/metadata/index.d.ts +1 -1
- package/metadata/index.js +1 -1
- package/metadata/types.d.ts +577 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +25 -4
- package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
- package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
- package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
- package/naming-strategy/MongoNamingStrategy.js +6 -6
- package/naming-strategy/NamingStrategy.d.ts +28 -4
- package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
- package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
- package/not-supported.d.ts +2 -0
- package/not-supported.js +8 -0
- package/package.json +47 -36
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +11 -15
- package/platforms/Platform.js +70 -67
- package/serialization/EntitySerializer.d.ts +6 -3
- package/serialization/EntitySerializer.js +53 -29
- package/serialization/EntityTransformer.js +33 -21
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +4 -4
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.js +2 -2
- package/types/DoubleType.js +1 -1
- package/types/EnumArrayType.js +1 -2
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/TinyIntType.js +1 -1
- package/types/Type.d.ts +2 -4
- package/types/Type.js +3 -3
- package/types/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +1 -4
- package/types/index.d.ts +1 -1
- package/typings.d.ts +427 -170
- package/typings.js +100 -45
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +8 -9
- package/unit-of-work/ChangeSetComputer.d.ts +3 -8
- package/unit-of-work/ChangeSetComputer.js +49 -26
- package/unit-of-work/ChangeSetPersister.d.ts +13 -12
- package/unit-of-work/ChangeSetPersister.js +106 -43
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +17 -15
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +34 -4
- package/unit-of-work/UnitOfWork.js +293 -106
- package/utils/AbstractMigrator.d.ts +101 -0
- package/utils/AbstractMigrator.js +303 -0
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +30 -18
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +795 -211
- package/utils/Configuration.js +160 -197
- package/utils/ConfigurationLoader.d.ts +1 -52
- package/utils/ConfigurationLoader.js +1 -330
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +28 -13
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +42 -22
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +197 -91
- package/utils/QueryHelper.d.ts +18 -6
- package/utils/QueryHelper.js +113 -48
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +37 -72
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +11 -7
- package/utils/Utils.d.ts +16 -127
- package/utils/Utils.js +104 -400
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +98 -0
- package/utils/fs-utils.d.ts +34 -0
- package/utils/fs-utils.js +193 -0
- package/utils/index.d.ts +1 -3
- package/utils/index.js +1 -3
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +51 -5
- package/decorators/Check.d.ts +0 -3
- package/decorators/Check.js +0 -13
- package/decorators/CreateRequestContext.d.ts +0 -3
- package/decorators/CreateRequestContext.js +0 -32
- package/decorators/Embeddable.d.ts +0 -8
- package/decorators/Embeddable.js +0 -11
- package/decorators/Embedded.d.ts +0 -12
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -33
- package/decorators/Entity.js +0 -12
- package/decorators/Enum.d.ts +0 -9
- package/decorators/Enum.js +0 -16
- package/decorators/Filter.d.ts +0 -2
- package/decorators/Filter.js +0 -8
- package/decorators/Formula.d.ts +0 -4
- package/decorators/Formula.js +0 -15
- package/decorators/Indexed.d.ts +0 -19
- package/decorators/Indexed.js +0 -20
- package/decorators/ManyToMany.d.ts +0 -42
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -34
- package/decorators/ManyToOne.js +0 -14
- package/decorators/OneToMany.d.ts +0 -28
- package/decorators/OneToMany.js +0 -17
- package/decorators/OneToOne.d.ts +0 -28
- package/decorators/OneToOne.js +0 -7
- package/decorators/PrimaryKey.d.ts +0 -8
- package/decorators/PrimaryKey.js +0 -20
- package/decorators/Property.d.ts +0 -250
- package/decorators/Property.js +0 -32
- package/decorators/Transactional.d.ts +0 -14
- package/decorators/Transactional.js +0 -28
- package/decorators/hooks.d.ts +0 -16
- package/decorators/hooks.js +0 -47
- package/decorators/index.d.ts +0 -17
- package/decorators/index.js +0 -17
- package/entity/ArrayCollection.d.ts +0 -118
- package/entity/ArrayCollection.js +0 -407
- package/entity/EntityValidator.d.ts +0 -19
- package/entity/EntityValidator.js +0 -150
- package/metadata/ReflectMetadataProvider.d.ts +0 -8
- package/metadata/ReflectMetadataProvider.js +0 -44
- package/utils/resolveContextProvider.d.ts +0 -10
- package/utils/resolveContextProvider.js +0 -28
package/utils/QueryHelper.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import type { Dictionary, EntityMetadata, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
1
|
+
import type { Dictionary, EntityMetadata, EntityName, EntityProperty, FilterDef, FilterQuery } from '../typings.js';
|
|
2
|
+
import { type QueryOrderMap } from '../enums.js';
|
|
2
3
|
import type { Platform } from '../platforms/Platform.js';
|
|
3
4
|
import type { MetadataStorage } from '../metadata/MetadataStorage.js';
|
|
5
|
+
import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
6
|
/** @internal */
|
|
5
7
|
export declare class QueryHelper {
|
|
6
8
|
static readonly SUPPORTED_OPERATORS: string[];
|
|
9
|
+
/**
|
|
10
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
11
|
+
*/
|
|
12
|
+
static findDiscriminatorValue<T>(discriminatorMap: Dictionary<T>, targetClass: T): string | undefined;
|
|
7
13
|
static processParams(params: unknown): any;
|
|
8
|
-
static processObjectParams<T extends
|
|
14
|
+
static processObjectParams<T extends Dictionary>(params?: T): T;
|
|
9
15
|
/**
|
|
10
16
|
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
11
17
|
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
@@ -13,21 +19,27 @@ export declare class QueryHelper {
|
|
|
13
19
|
static liftGroupOperators<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): string | undefined;
|
|
14
20
|
static inlinePrimaryKeyObjects<T extends object>(where: Dictionary, meta: EntityMetadata<T>, metadata: MetadataStorage, key?: string): boolean;
|
|
15
21
|
static processWhere<T extends object>(options: ProcessWhereOptions<T>): FilterQuery<T>;
|
|
16
|
-
static getActiveFilters(
|
|
17
|
-
static
|
|
22
|
+
static getActiveFilters<T>(meta: EntityMetadata<T>, options: FilterOptions | undefined, filters: Dictionary<FilterDef>): FilterDef[];
|
|
23
|
+
static mergePropertyFilters(propFilters: FilterOptions | undefined, options: FilterOptions | undefined): FilterOptions | undefined;
|
|
24
|
+
static isFilterActive<T>(meta: EntityMetadata<T>, filterName: string, filter: FilterDef, options: Dictionary<boolean | Dictionary>): boolean;
|
|
18
25
|
static processCustomType<T extends object>(prop: EntityProperty<T>, cond: FilterQuery<T>, platform: Platform, key?: string, fromQuery?: boolean): FilterQuery<T>;
|
|
19
26
|
private static isSupportedOperator;
|
|
20
27
|
private static processJsonCondition;
|
|
21
28
|
private static getValueType;
|
|
22
29
|
static findProperty<T>(fieldName: string, options: ProcessWhereOptions<T>): EntityProperty<T> | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
32
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
33
|
+
*/
|
|
34
|
+
static mergeOrderBy<T>(...sources: (QueryOrderMap<T> | QueryOrderMap<T>[] | undefined)[]): QueryOrderMap<T>[];
|
|
23
35
|
}
|
|
24
36
|
interface ProcessWhereOptions<T> {
|
|
25
37
|
where: FilterQuery<T>;
|
|
26
|
-
entityName:
|
|
38
|
+
entityName: EntityName<T>;
|
|
27
39
|
metadata: MetadataStorage;
|
|
28
40
|
platform: Platform;
|
|
29
41
|
aliased?: boolean;
|
|
30
|
-
aliasMap?: Dictionary<
|
|
42
|
+
aliasMap?: Dictionary<EntityName>;
|
|
31
43
|
convertCustomTypes?: boolean;
|
|
32
44
|
root?: boolean;
|
|
33
45
|
type?: 'where' | 'orderBy';
|
package/utils/QueryHelper.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { Reference } from '../entity/Reference.js';
|
|
2
2
|
import { Utils } from './Utils.js';
|
|
3
|
-
import { GroupOperator, ReferenceKind } from '../enums.js';
|
|
3
|
+
import { ARRAY_OPERATORS, GroupOperator, JSON_KEY_OPERATORS, ReferenceKind } from '../enums.js';
|
|
4
4
|
import { JsonType } from '../types/JsonType.js';
|
|
5
5
|
import { helper } from '../entity/wrap.js';
|
|
6
|
-
import {
|
|
6
|
+
import { isRaw, Raw } from './RawQueryFragment.js';
|
|
7
7
|
/** @internal */
|
|
8
8
|
export class QueryHelper {
|
|
9
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
10
|
+
/**
|
|
11
|
+
* Finds the discriminator value (key) for a given entity class in a discriminator map.
|
|
12
|
+
*/
|
|
13
|
+
static findDiscriminatorValue(discriminatorMap, targetClass) {
|
|
14
|
+
return Object.entries(discriminatorMap).find(([, cls]) => cls === targetClass)?.[0];
|
|
15
|
+
}
|
|
10
16
|
static processParams(params) {
|
|
11
17
|
if (Reference.isReference(params)) {
|
|
12
18
|
params = params.unwrap();
|
|
@@ -29,7 +35,7 @@ export class QueryHelper {
|
|
|
29
35
|
return params;
|
|
30
36
|
}
|
|
31
37
|
static processObjectParams(params = {}) {
|
|
32
|
-
Utils.
|
|
38
|
+
Utils.getObjectQueryKeys(params).forEach(k => {
|
|
33
39
|
params[k] = QueryHelper.processParams(params[k]);
|
|
34
40
|
});
|
|
35
41
|
return params;
|
|
@@ -44,17 +50,21 @@ export class QueryHelper {
|
|
|
44
50
|
}
|
|
45
51
|
const keys = Object.keys(where);
|
|
46
52
|
const groupOperator = keys.find(k => {
|
|
47
|
-
return
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
return (k in GroupOperator &&
|
|
54
|
+
Array.isArray(where[k]) &&
|
|
55
|
+
where[k].every(cond => {
|
|
56
|
+
return (Utils.isPlainObject(cond) &&
|
|
57
|
+
Object.keys(cond).every(k2 => {
|
|
58
|
+
if (Utils.isOperator(k2, false)) {
|
|
59
|
+
if (k2 === '$not') {
|
|
60
|
+
return Object.keys(cond[k2]).every(k3 => meta.primaryKeys.includes(k3));
|
|
61
|
+
}
|
|
62
|
+
/* v8 ignore next */
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return meta.primaryKeys.includes(k2);
|
|
66
|
+
}));
|
|
67
|
+
}));
|
|
58
68
|
});
|
|
59
69
|
if (groupOperator) {
|
|
60
70
|
return groupOperator;
|
|
@@ -62,7 +72,10 @@ export class QueryHelper {
|
|
|
62
72
|
for (const k of keys) {
|
|
63
73
|
const value = where[k];
|
|
64
74
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
75
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
76
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
77
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
78
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
79
|
continue;
|
|
67
80
|
}
|
|
68
81
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
|
@@ -87,18 +100,24 @@ export class QueryHelper {
|
|
|
87
100
|
return false;
|
|
88
101
|
}
|
|
89
102
|
if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
|
|
90
|
-
return !!key &&
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
return (!!key &&
|
|
104
|
+
!GroupOperator[key] &&
|
|
105
|
+
key !== '$not' &&
|
|
106
|
+
Object.keys(where).every(k => !Utils.isPlainObject(where[k]) ||
|
|
107
|
+
Object.keys(where[k]).every(v => {
|
|
108
|
+
if (Utils.isOperator(v, false)) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (meta.properties[k].primary &&
|
|
112
|
+
[ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
|
|
113
|
+
return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
|
|
114
|
+
}
|
|
115
|
+
/* v8 ignore next */
|
|
116
|
+
return true;
|
|
117
|
+
})));
|
|
99
118
|
}
|
|
100
119
|
Object.keys(where).forEach(k => {
|
|
101
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
120
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
102
121
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
103
122
|
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
104
123
|
}
|
|
@@ -118,7 +137,7 @@ export class QueryHelper {
|
|
|
118
137
|
Utils.dropUndefinedProperties(where);
|
|
119
138
|
}
|
|
120
139
|
where = QueryHelper.processParams(where) ?? {};
|
|
121
|
-
/* v8 ignore next
|
|
140
|
+
/* v8 ignore next */
|
|
122
141
|
if (!root && Utils.isPrimaryKey(where)) {
|
|
123
142
|
return where;
|
|
124
143
|
}
|
|
@@ -126,11 +145,13 @@ export class QueryHelper {
|
|
|
126
145
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
127
146
|
}
|
|
128
147
|
if (Array.isArray(where) && root) {
|
|
129
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
148
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
130
149
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
131
150
|
// @ts-ignore
|
|
132
151
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
133
|
-
if (meta &&
|
|
152
|
+
if (meta &&
|
|
153
|
+
!where.every(c => Utils.isPrimaryKey(c) ||
|
|
154
|
+
(Array.isArray(c) && c.length === meta.primaryKeys.length && c.every(i => Utils.isPrimaryKey(i))))) {
|
|
134
155
|
cond = { $or: where };
|
|
135
156
|
}
|
|
136
157
|
return QueryHelper.processWhere({ ...options, where: cond, root: false });
|
|
@@ -138,12 +159,10 @@ export class QueryHelper {
|
|
|
138
159
|
if (!Utils.isPlainObject(where)) {
|
|
139
160
|
return where;
|
|
140
161
|
}
|
|
141
|
-
return
|
|
162
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
163
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
164
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
165
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
166
|
o[key] = value;
|
|
148
167
|
return o;
|
|
149
168
|
}
|
|
@@ -157,14 +176,19 @@ export class QueryHelper {
|
|
|
157
176
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
177
|
return o;
|
|
159
178
|
}
|
|
179
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
180
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
181
|
+
const composite = keys > 1;
|
|
160
182
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
183
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
184
|
}
|
|
185
|
+
// oxfmt-ignore
|
|
163
186
|
const isJsonProperty = prop?.customType instanceof JsonType && Utils.isPlainObject(value) && !isRaw(value) && Object.keys(value)[0] !== '$eq';
|
|
164
187
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
188
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
189
|
}
|
|
167
|
-
|
|
190
|
+
// oxfmt-ignore
|
|
191
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
192
|
// comparing single composite key - use $eq instead of $in
|
|
169
193
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
194
|
o[key] = { [op]: value };
|
|
@@ -174,7 +198,7 @@ export class QueryHelper {
|
|
|
174
198
|
o[key] = QueryHelper.processWhere({
|
|
175
199
|
...options,
|
|
176
200
|
where: value,
|
|
177
|
-
entityName: prop?.
|
|
201
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
178
202
|
root: false,
|
|
179
203
|
});
|
|
180
204
|
}
|
|
@@ -184,26 +208,44 @@ export class QueryHelper {
|
|
|
184
208
|
return o;
|
|
185
209
|
}, {});
|
|
186
210
|
}
|
|
187
|
-
static getActiveFilters(
|
|
211
|
+
static getActiveFilters(meta, options, filters) {
|
|
188
212
|
if (options === false) {
|
|
189
213
|
return [];
|
|
190
214
|
}
|
|
191
215
|
const opts = {};
|
|
192
216
|
if (Array.isArray(options)) {
|
|
193
|
-
options.forEach(filter => opts[filter] = true);
|
|
217
|
+
options.forEach(filter => (opts[filter] = true));
|
|
194
218
|
}
|
|
195
219
|
else if (Utils.isPlainObject(options)) {
|
|
196
|
-
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
220
|
+
Object.keys(options).forEach(filter => (opts[filter] = options[filter]));
|
|
197
221
|
}
|
|
198
222
|
return Object.keys(filters)
|
|
199
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
223
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
200
224
|
.map(f => {
|
|
201
225
|
filters[f].name = f;
|
|
202
226
|
return filters[f];
|
|
203
227
|
});
|
|
204
228
|
}
|
|
205
|
-
static
|
|
206
|
-
if (
|
|
229
|
+
static mergePropertyFilters(propFilters, options) {
|
|
230
|
+
if (!options || !propFilters || options === true || propFilters === true) {
|
|
231
|
+
return options ?? propFilters;
|
|
232
|
+
}
|
|
233
|
+
if (Array.isArray(propFilters)) {
|
|
234
|
+
propFilters = propFilters.reduce((o, item) => {
|
|
235
|
+
o[item] = true;
|
|
236
|
+
return o;
|
|
237
|
+
}, {});
|
|
238
|
+
}
|
|
239
|
+
if (Array.isArray(options)) {
|
|
240
|
+
options = options.reduce((o, item) => {
|
|
241
|
+
o[item] = true;
|
|
242
|
+
return o;
|
|
243
|
+
}, {});
|
|
244
|
+
}
|
|
245
|
+
return Utils.mergeConfig({}, propFilters, options);
|
|
246
|
+
}
|
|
247
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
248
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
207
249
|
return false;
|
|
208
250
|
}
|
|
209
251
|
if (options[filterName] === false) {
|
|
@@ -213,8 +255,8 @@ export class QueryHelper {
|
|
|
213
255
|
}
|
|
214
256
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
215
257
|
if (Utils.isPlainObject(cond)) {
|
|
216
|
-
return Utils.
|
|
217
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
258
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
259
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
218
260
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
219
261
|
}
|
|
220
262
|
else {
|
|
@@ -223,12 +265,10 @@ export class QueryHelper {
|
|
|
223
265
|
return o;
|
|
224
266
|
}, {});
|
|
225
267
|
}
|
|
226
|
-
if (key &&
|
|
227
|
-
return Array.isArray(cond)
|
|
228
|
-
? platform.marshallArray(cond)
|
|
229
|
-
: cond;
|
|
268
|
+
if (key && JSON_KEY_OPERATORS.includes(key)) {
|
|
269
|
+
return Array.isArray(cond) ? platform.marshallArray(cond) : cond;
|
|
230
270
|
}
|
|
231
|
-
if (Array.isArray(cond) && !(key &&
|
|
271
|
+
if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
|
|
232
272
|
return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
|
|
233
273
|
}
|
|
234
274
|
if (isRaw(cond)) {
|
|
@@ -272,4 +312,29 @@ export class QueryHelper {
|
|
|
272
312
|
const meta = entityName ? options.metadata.find(entityName) : undefined;
|
|
273
313
|
return meta?.properties[propName];
|
|
274
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
317
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
318
|
+
*/
|
|
319
|
+
static mergeOrderBy(...sources) {
|
|
320
|
+
const result = [];
|
|
321
|
+
const seenKeys = new Set();
|
|
322
|
+
for (const source of sources) {
|
|
323
|
+
if (source == null) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
for (const item of Utils.asArray(source)) {
|
|
327
|
+
for (const key of Utils.getObjectQueryKeys(item)) {
|
|
328
|
+
if (typeof key === 'symbol') {
|
|
329
|
+
result.push({ [key]: item[key] });
|
|
330
|
+
}
|
|
331
|
+
else if (!seenKeys.has(key)) {
|
|
332
|
+
seenKeys.add(key);
|
|
333
|
+
result.push({ [key]: item[key] });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
275
340
|
}
|
|
@@ -1,34 +1,25 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
1
|
import type { AnyString, Dictionary, EntityKey } from '../typings.js';
|
|
3
|
-
|
|
2
|
+
declare const rawFragmentSymbolBrand: unique symbol;
|
|
3
|
+
export type RawQueryFragmentSymbol = symbol & {
|
|
4
|
+
readonly [rawFragmentSymbolBrand]: true;
|
|
5
|
+
};
|
|
6
|
+
export declare class RawQueryFragment<Alias extends string = string> {
|
|
4
7
|
#private;
|
|
5
8
|
readonly sql: string;
|
|
6
9
|
readonly params: unknown[];
|
|
7
|
-
|
|
10
|
+
/** @internal Type-level only - used to track the alias for type inference */
|
|
11
|
+
private readonly __alias?;
|
|
8
12
|
constructor(sql: string, params?: unknown[]);
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
get key(): RawQueryFragmentSymbol;
|
|
14
|
+
as<A extends string>(alias: A): RawQueryFragment<A>;
|
|
15
|
+
[Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
|
|
16
|
+
get [Symbol.toStringTag](): string;
|
|
11
17
|
toJSON(): string;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
static
|
|
17
|
-
/**
|
|
18
|
-
* @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
|
|
19
|
-
*/
|
|
20
|
-
static checkCacheSize(): number;
|
|
21
|
-
static isKnownFragment(key: string | RawQueryFragment): boolean;
|
|
22
|
-
static getKnownFragment(key: string | RawQueryFragment, cleanup?: boolean): RawQueryFragment | undefined;
|
|
23
|
-
static remove(key: string): void;
|
|
24
|
-
/** @ignore */
|
|
25
|
-
[inspect.custom](): {
|
|
26
|
-
sql: string;
|
|
27
|
-
params: unknown[];
|
|
28
|
-
} | {
|
|
29
|
-
sql: string;
|
|
30
|
-
params?: undefined;
|
|
31
|
-
};
|
|
18
|
+
clone(): this;
|
|
19
|
+
static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
|
|
20
|
+
static hasObjectFragments(object: unknown): boolean;
|
|
21
|
+
static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
|
|
22
|
+
static getKnownFragment(key: unknown): RawQueryFragment<any> | undefined;
|
|
32
23
|
}
|
|
33
24
|
export { RawQueryFragment as Raw };
|
|
34
25
|
export declare function isRaw(value: unknown): value is RawQueryFragment;
|
|
@@ -91,7 +82,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
91
82
|
* export class Author { ... }
|
|
92
83
|
* ```
|
|
93
84
|
*/
|
|
94
|
-
export declare function raw<
|
|
85
|
+
export declare function raw<R = RawQueryFragment & symbol, T extends object = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
|
|
95
86
|
/**
|
|
96
87
|
* Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
|
|
97
88
|
*
|
|
@@ -104,16 +95,19 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
|
|
|
104
95
|
*
|
|
105
96
|
* // value can be empty array
|
|
106
97
|
* await em.find(User, { [sql`(select 1 = 1)`]: [] });
|
|
98
|
+
*
|
|
99
|
+
* // with type parameter for assignment without casting
|
|
100
|
+
* entity.date = sql<Date>`now()`;
|
|
107
101
|
* ```
|
|
108
102
|
*/
|
|
109
|
-
export declare function sql(sql: readonly string[], ...values: unknown[]):
|
|
103
|
+
export declare function sql<R = RawQueryFragment & symbol>(sql: readonly string[], ...values: unknown[]): R;
|
|
110
104
|
export declare namespace sql {
|
|
111
|
-
var ref: <T extends object>(...keys: string[]) => RawQueryFragment;
|
|
112
|
-
var now: (length?: number) => string;
|
|
113
|
-
var lower: <T extends object>(key: string | ((alias: string) => string)) =>
|
|
114
|
-
var upper: <T extends object>(key: string | ((alias: string) => string)) =>
|
|
105
|
+
var ref: <T extends object = any>(...keys: string[]) => RawQueryFragment<string> & symbol;
|
|
106
|
+
var now: (length?: number) => RawQueryFragment<string> & symbol;
|
|
107
|
+
var lower: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
|
|
108
|
+
var upper: <R = RawQueryFragment<string> & symbol, T extends object = any>(key: string | ((alias: string) => string)) => R;
|
|
115
109
|
}
|
|
116
|
-
export declare function createSqlFunction<T extends object
|
|
110
|
+
export declare function createSqlFunction<R = RawQueryFragment & symbol, T extends object = any>(func: string, key: string | ((alias: string) => string)): R;
|
|
117
111
|
/**
|
|
118
112
|
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
119
113
|
*
|
|
@@ -122,11 +116,11 @@ export declare function createSqlFunction<T extends object, R = string>(func: st
|
|
|
122
116
|
* ```ts
|
|
123
117
|
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
124
118
|
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
125
|
-
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${
|
|
119
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
|
|
126
120
|
* @Entity({ schema: 'library' })
|
|
127
121
|
* export class Author { ... }
|
|
128
122
|
* ```
|
|
129
123
|
*/
|
|
130
124
|
export declare function quote(expParts: readonly string[], ...values: (string | {
|
|
131
125
|
toString(): string;
|
|
132
|
-
})[]):
|
|
126
|
+
})[]): RawQueryFragment<string> & symbol;
|
|
@@ -1,99 +1,65 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
-
import { inspect } from 'node:util';
|
|
3
1
|
import { Utils } from './Utils.js';
|
|
4
2
|
export class RawQueryFragment {
|
|
5
3
|
sql;
|
|
6
4
|
params;
|
|
7
|
-
static #
|
|
8
|
-
static #storage = new AsyncLocalStorage();
|
|
9
|
-
static #index = 0n;
|
|
10
|
-
static cloneRegistry;
|
|
11
|
-
#assigned = false;
|
|
12
|
-
#used = 0;
|
|
5
|
+
static #rawQueryReferences = new WeakMap();
|
|
13
6
|
#key;
|
|
14
7
|
constructor(sql, params = []) {
|
|
15
8
|
this.sql = sql;
|
|
16
9
|
this.params = params;
|
|
17
|
-
this.#key = `[raw]: ${this.sql} (#${RawQueryFragment.#index++})`;
|
|
18
10
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return new RawQueryFragment(`${this.sql} as ${alias}`, this.params);
|
|
11
|
+
get key() {
|
|
12
|
+
if (!this.#key) {
|
|
13
|
+
this.#key = Symbol(this.toJSON());
|
|
14
|
+
RawQueryFragment.#rawQueryReferences.set(this.#key, this);
|
|
24
15
|
}
|
|
16
|
+
return this.#key;
|
|
17
|
+
}
|
|
18
|
+
as(alias) {
|
|
25
19
|
return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
|
|
26
20
|
}
|
|
27
|
-
|
|
21
|
+
[Symbol.toPrimitive](hint) {
|
|
22
|
+
// if a fragment is converted to string (used as an object key), return a unique symbol
|
|
23
|
+
// and save a weak reference to map so we can retrieve it when compiling the query
|
|
24
|
+
if (hint === 'string') {
|
|
25
|
+
return this.key;
|
|
26
|
+
}
|
|
28
27
|
throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
|
|
29
28
|
}
|
|
29
|
+
get [Symbol.toStringTag]() {
|
|
30
|
+
return this.toJSON();
|
|
31
|
+
}
|
|
30
32
|
toJSON() {
|
|
31
|
-
return this
|
|
33
|
+
return `raw('${this.sql}')`;
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.#used++;
|
|
36
|
-
return this.#key;
|
|
35
|
+
clone() {
|
|
36
|
+
return this;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (this.#assigned) {
|
|
41
|
-
throw new Error(`Cannot reassign already used RawQueryFragment: '${this.sql}'`);
|
|
42
|
-
}
|
|
43
|
-
this.#assigned = true;
|
|
38
|
+
static isKnownFragmentSymbol(key) {
|
|
39
|
+
return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
|
|
44
40
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
static async run(cb) {
|
|
50
|
-
const removeStack = new Set();
|
|
51
|
-
const res = await this.#storage.run(removeStack, cb);
|
|
52
|
-
removeStack.forEach(key => RawQueryFragment.remove(key));
|
|
53
|
-
removeStack.clear();
|
|
54
|
-
return res;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* @internal allows testing we don't leak memory, as the raw fragments cache needs to be cleared automatically
|
|
58
|
-
*/
|
|
59
|
-
static checkCacheSize() {
|
|
60
|
-
return this.#rawQueryCache.size;
|
|
41
|
+
static hasObjectFragments(object) {
|
|
42
|
+
return (Utils.isPlainObject(object) &&
|
|
43
|
+
Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol)));
|
|
61
44
|
}
|
|
62
45
|
static isKnownFragment(key) {
|
|
63
46
|
if (key instanceof RawQueryFragment) {
|
|
64
47
|
return true;
|
|
65
48
|
}
|
|
66
|
-
return this
|
|
49
|
+
return this.isKnownFragmentSymbol(key);
|
|
67
50
|
}
|
|
68
|
-
static getKnownFragment(key
|
|
51
|
+
static getKnownFragment(key) {
|
|
69
52
|
if (key instanceof RawQueryFragment) {
|
|
70
53
|
return key;
|
|
71
54
|
}
|
|
72
|
-
|
|
73
|
-
if (raw && cleanup) {
|
|
74
|
-
this.remove(key);
|
|
75
|
-
}
|
|
76
|
-
return raw;
|
|
77
|
-
}
|
|
78
|
-
static remove(key) {
|
|
79
|
-
const raw = this.#rawQueryCache.get(key);
|
|
80
|
-
if (!raw) {
|
|
55
|
+
if (typeof key !== 'symbol') {
|
|
81
56
|
return;
|
|
82
57
|
}
|
|
83
|
-
|
|
84
|
-
if (raw.#used <= 0) {
|
|
85
|
-
const removeStack = this.#storage.getStore();
|
|
86
|
-
if (removeStack) {
|
|
87
|
-
removeStack.add(key);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
this.#rawQueryCache.delete(key);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
58
|
+
return this.#rawQueryReferences.get(key);
|
|
93
59
|
}
|
|
94
|
-
/* v8 ignore next 8 */
|
|
95
60
|
/** @ignore */
|
|
96
|
-
|
|
61
|
+
/* v8 ignore next */
|
|
62
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
97
63
|
if (this.params) {
|
|
98
64
|
return { sql: this.sql, params: this.params };
|
|
99
65
|
}
|
|
@@ -204,20 +170,19 @@ export function raw(sql, params) {
|
|
|
204
170
|
*
|
|
205
171
|
* // value can be empty array
|
|
206
172
|
* await em.find(User, { [sql`(select 1 = 1)`]: [] });
|
|
173
|
+
*
|
|
174
|
+
* // with type parameter for assignment without casting
|
|
175
|
+
* entity.date = sql<Date>`now()`;
|
|
207
176
|
* ```
|
|
208
177
|
*/
|
|
209
178
|
export function sql(sql, ...values) {
|
|
210
|
-
return raw(sql.
|
|
211
|
-
const valueExists = i < values.length;
|
|
212
|
-
const text = query + queryPart;
|
|
213
|
-
return valueExists ? text + '?' : text;
|
|
214
|
-
}, ''), values);
|
|
179
|
+
return raw(sql.join('?'), values);
|
|
215
180
|
}
|
|
216
181
|
export function createSqlFunction(func, key) {
|
|
217
182
|
if (typeof key === 'string') {
|
|
218
183
|
return raw(`${func}(${key})`);
|
|
219
184
|
}
|
|
220
|
-
return raw(a => `${func}(${
|
|
185
|
+
return raw(a => `${func}(${key(a)})`);
|
|
221
186
|
}
|
|
222
187
|
sql.ref = (...keys) => raw('??', [keys.join('.')]);
|
|
223
188
|
sql.now = (length) => raw('current_timestamp' + (length == null ? '' : `(${length})`));
|
|
@@ -231,7 +196,7 @@ sql.upper = (key) => createSqlFunction('upper', key);
|
|
|
231
196
|
* ```ts
|
|
232
197
|
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
233
198
|
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
234
|
-
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${
|
|
199
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
|
|
235
200
|
* @Entity({ schema: 'library' })
|
|
236
201
|
* export class Author { ... }
|
|
237
202
|
* ```
|
package/utils/RequestContext.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAsyncContext } from './AsyncContext.js';
|
|
2
2
|
/**
|
|
3
3
|
* Uses `AsyncLocalStorage` to create async context that holds the current EM fork.
|
|
4
4
|
*/
|
|
5
5
|
export class RequestContext {
|
|
6
6
|
map;
|
|
7
|
-
static storage =
|
|
7
|
+
static storage = createAsyncContext();
|
|
8
8
|
static counter = 1;
|
|
9
9
|
id = RequestContext.counter++;
|
|
10
10
|
constructor(map) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createAsyncContext } from './AsyncContext.js';
|
|
2
2
|
export class TransactionContext {
|
|
3
3
|
em;
|
|
4
|
-
static storage =
|
|
4
|
+
static storage = createAsyncContext();
|
|
5
5
|
id;
|
|
6
6
|
constructor(em) {
|
|
7
7
|
this.em = em;
|