@mikro-orm/core 7.0.0-dev.12 → 7.0.0-dev.120
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 +85 -56
- package/EntityManager.js +332 -293
- package/MikroORM.d.ts +41 -32
- package/MikroORM.js +100 -140
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +8 -7
- 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 +16 -7
- package/connections/Connection.js +23 -14
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +35 -19
- package/drivers/IDatabaseDriver.d.ts +38 -17
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +95 -30
- package/entity/Collection.js +439 -99
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +72 -53
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +30 -15
- package/entity/EntityLoader.d.ts +7 -6
- package/entity/EntityLoader.js +84 -72
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityRepository.js +2 -2
- package/entity/Reference.d.ts +6 -5
- package/entity/Reference.js +34 -9
- package/entity/WrappedEntity.d.ts +2 -7
- package/entity/WrappedEntity.js +3 -8
- package/entity/defineEntity.d.ts +568 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +3 -2
- package/entity/index.js +3 -2
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +16 -4
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +21 -6
- package/enums.js +14 -1
- package/errors.d.ts +17 -9
- package/errors.js +41 -21
- package/events/EventManager.d.ts +2 -1
- package/events/EventManager.js +19 -11
- package/hydration/Hydrator.js +1 -2
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +50 -33
- package/index.d.ts +2 -2
- package/index.js +1 -2
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/DefaultLogger.js +1 -0
- package/logging/SimpleLogger.d.ts +1 -1
- package/logging/colors.d.ts +1 -1
- package/logging/colors.js +7 -6
- 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 +13 -17
- package/metadata/EntitySchema.js +67 -51
- package/metadata/MetadataDiscovery.d.ts +6 -10
- package/metadata/MetadataDiscovery.js +289 -298
- package/metadata/MetadataProvider.d.ts +11 -2
- package/metadata/MetadataProvider.js +46 -2
- package/metadata/MetadataStorage.d.ts +13 -11
- package/metadata/MetadataStorage.js +70 -37
- package/metadata/MetadataValidator.d.ts +2 -9
- package/metadata/MetadataValidator.js +22 -38
- 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 +480 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +8 -4
- package/naming-strategy/AbstractNamingStrategy.js +8 -2
- 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 +14 -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 +4 -0
- package/package.json +19 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +6 -13
- package/platforms/Platform.js +17 -43
- package/serialization/EntitySerializer.d.ts +5 -0
- package/serialization/EntitySerializer.js +47 -27
- package/serialization/EntityTransformer.js +28 -18
- package/serialization/SerializationContext.d.ts +6 -6
- package/serialization/SerializationContext.js +16 -13
- package/types/ArrayType.d.ts +1 -1
- package/types/ArrayType.js +2 -3
- package/types/BigIntType.d.ts +8 -6
- package/types/BigIntType.js +1 -1
- package/types/BlobType.d.ts +0 -1
- package/types/BlobType.js +0 -3
- package/types/BooleanType.d.ts +2 -1
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +3 -3
- package/types/DoubleType.js +2 -2
- 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 +124 -86
- package/typings.js +50 -42
- package/unit-of-work/ChangeSet.d.ts +2 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +14 -12
- package/unit-of-work/ChangeSetPersister.d.ts +5 -4
- package/unit-of-work/ChangeSetPersister.js +65 -33
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/UnitOfWork.d.ts +10 -3
- package/unit-of-work/UnitOfWork.js +139 -96
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +18 -16
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +753 -207
- package/utils/Configuration.js +145 -190
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +9 -6
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +65 -17
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +85 -43
- package/utils/QueryHelper.d.ts +14 -6
- package/utils/QueryHelper.js +87 -25
- package/utils/RawQueryFragment.d.ts +48 -25
- package/utils/RawQueryFragment.js +66 -70
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +12 -119
- package/utils/Utils.js +97 -373
- package/utils/clone.js +8 -23
- package/utils/env-vars.d.ts +7 -0
- package/utils/env-vars.js +97 -0
- package/utils/fs-utils.d.ts +32 -0
- package/utils/fs-utils.js +178 -0
- package/utils/index.d.ts +2 -1
- package/utils/index.js +2 -1
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +55 -4
- 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 -18
- package/decorators/Embedded.js +0 -18
- package/decorators/Entity.d.ts +0 -18
- 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 -40
- package/decorators/ManyToMany.js +0 -14
- package/decorators/ManyToOne.d.ts +0 -30
- 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 -24
- 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 -13
- 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 -116
- package/entity/ArrayCollection.js +0 -402
- 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.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
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
|
+
/** @internal */
|
|
7
8
|
export class QueryHelper {
|
|
8
9
|
static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
|
|
9
10
|
static processParams(params) {
|
|
@@ -28,16 +29,57 @@ export class QueryHelper {
|
|
|
28
29
|
return params;
|
|
29
30
|
}
|
|
30
31
|
static processObjectParams(params = {}) {
|
|
31
|
-
Utils.
|
|
32
|
+
Utils.getObjectQueryKeys(params).forEach(k => {
|
|
32
33
|
params[k] = QueryHelper.processParams(params[k]);
|
|
33
34
|
});
|
|
34
35
|
return params;
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* converts `{ account: { $or: [ [Object], [Object] ] } }`
|
|
39
|
+
* to `{ $or: [ { account: [Object] }, { account: [Object] } ] }`
|
|
40
|
+
*/
|
|
41
|
+
static liftGroupOperators(where, meta, metadata, key) {
|
|
42
|
+
if (!Utils.isPlainObject(where)) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
const keys = Object.keys(where);
|
|
46
|
+
const groupOperator = keys.find(k => {
|
|
47
|
+
return k in GroupOperator && Array.isArray(where[k]) && where[k].every(cond => {
|
|
48
|
+
return Utils.isPlainObject(cond) && Object.keys(cond).every(k2 => {
|
|
49
|
+
if (Utils.isOperator(k2, false)) {
|
|
50
|
+
if (k2 === '$not') {
|
|
51
|
+
return Object.keys(cond[k2]).every(k3 => meta.primaryKeys.includes(k3));
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return meta.primaryKeys.includes(k2);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
if (groupOperator) {
|
|
60
|
+
return groupOperator;
|
|
61
|
+
}
|
|
62
|
+
for (const k of keys) {
|
|
63
|
+
const value = where[k];
|
|
64
|
+
const prop = meta.properties[k];
|
|
65
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
|
69
|
+
if (op) {
|
|
70
|
+
delete where[k];
|
|
71
|
+
where[op] = value[op].map((v) => {
|
|
72
|
+
return { [k]: v };
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
36
78
|
static inlinePrimaryKeyObjects(where, meta, metadata, key) {
|
|
37
79
|
if (Array.isArray(where)) {
|
|
38
80
|
where.forEach((item, i) => {
|
|
39
81
|
if (this.inlinePrimaryKeyObjects(item, meta, metadata, key)) {
|
|
40
|
-
where[i] = Utils.getPrimaryKeyValues(item, meta
|
|
82
|
+
where[i] = Utils.getPrimaryKeyValues(item, meta, false);
|
|
41
83
|
}
|
|
42
84
|
});
|
|
43
85
|
}
|
|
@@ -47,7 +89,7 @@ export class QueryHelper {
|
|
|
47
89
|
if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
|
|
48
90
|
return !!key && !GroupOperator[key] && key !== '$not' && Object.keys(where).every(k => !Utils.isPlainObject(where[k]) || Object.keys(where[k]).every(v => {
|
|
49
91
|
if (Utils.isOperator(v, false)) {
|
|
50
|
-
return
|
|
92
|
+
return true;
|
|
51
93
|
}
|
|
52
94
|
if (meta.properties[k].primary && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
|
|
53
95
|
return this.inlinePrimaryKeyObjects(where[k], meta.properties[k].targetMeta, metadata, v);
|
|
@@ -56,9 +98,9 @@ export class QueryHelper {
|
|
|
56
98
|
}));
|
|
57
99
|
}
|
|
58
100
|
Object.keys(where).forEach(k => {
|
|
59
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
101
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
60
102
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
61
|
-
where[k] = Utils.getPrimaryKeyValues(where[k], meta2
|
|
103
|
+
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
62
104
|
}
|
|
63
105
|
});
|
|
64
106
|
return false;
|
|
@@ -69,13 +111,14 @@ export class QueryHelper {
|
|
|
69
111
|
const meta = metadata.find(entityName);
|
|
70
112
|
// inline PK-only objects in M:N queries, so we don't join the target entity when not needed
|
|
71
113
|
if (meta && root) {
|
|
114
|
+
QueryHelper.liftGroupOperators(where, meta, metadata);
|
|
72
115
|
QueryHelper.inlinePrimaryKeyObjects(where, meta, metadata);
|
|
73
116
|
}
|
|
74
117
|
if (platform.getConfig().get('ignoreUndefinedInQuery') && where && typeof where === 'object') {
|
|
75
118
|
Utils.dropUndefinedProperties(where);
|
|
76
119
|
}
|
|
77
120
|
where = QueryHelper.processParams(where) ?? {};
|
|
78
|
-
/* v8 ignore next
|
|
121
|
+
/* v8 ignore next */
|
|
79
122
|
if (!root && Utils.isPrimaryKey(where)) {
|
|
80
123
|
return where;
|
|
81
124
|
}
|
|
@@ -83,7 +126,7 @@ export class QueryHelper {
|
|
|
83
126
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
84
127
|
}
|
|
85
128
|
if (Array.isArray(where) && root) {
|
|
86
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
129
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
87
130
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
88
131
|
// @ts-ignore
|
|
89
132
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
@@ -95,12 +138,10 @@ export class QueryHelper {
|
|
|
95
138
|
if (!Utils.isPlainObject(where)) {
|
|
96
139
|
return where;
|
|
97
140
|
}
|
|
98
|
-
return
|
|
141
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
99
142
|
let value = where[key];
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const composite = keys > 1;
|
|
103
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
143
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
144
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
104
145
|
o[key] = value;
|
|
105
146
|
return o;
|
|
106
147
|
}
|
|
@@ -114,14 +155,17 @@ export class QueryHelper {
|
|
|
114
155
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
115
156
|
return o;
|
|
116
157
|
}
|
|
158
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
159
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
160
|
+
const composite = keys > 1;
|
|
117
161
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
118
162
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
119
163
|
}
|
|
120
164
|
const isJsonProperty = prop?.customType instanceof JsonType && Utils.isPlainObject(value) && !isRaw(value) && Object.keys(value)[0] !== '$eq';
|
|
121
|
-
if (isJsonProperty) {
|
|
165
|
+
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
122
166
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
123
167
|
}
|
|
124
|
-
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !
|
|
168
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
125
169
|
// comparing single composite key - use $eq instead of $in
|
|
126
170
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
127
171
|
o[key] = { [op]: value };
|
|
@@ -131,7 +175,7 @@ export class QueryHelper {
|
|
|
131
175
|
o[key] = QueryHelper.processWhere({
|
|
132
176
|
...options,
|
|
133
177
|
where: value,
|
|
134
|
-
entityName: prop?.
|
|
178
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
135
179
|
root: false,
|
|
136
180
|
});
|
|
137
181
|
}
|
|
@@ -141,7 +185,7 @@ export class QueryHelper {
|
|
|
141
185
|
return o;
|
|
142
186
|
}, {});
|
|
143
187
|
}
|
|
144
|
-
static getActiveFilters(
|
|
188
|
+
static getActiveFilters(meta, options, filters) {
|
|
145
189
|
if (options === false) {
|
|
146
190
|
return [];
|
|
147
191
|
}
|
|
@@ -153,14 +197,32 @@ export class QueryHelper {
|
|
|
153
197
|
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
154
198
|
}
|
|
155
199
|
return Object.keys(filters)
|
|
156
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
200
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
157
201
|
.map(f => {
|
|
158
202
|
filters[f].name = f;
|
|
159
203
|
return filters[f];
|
|
160
204
|
});
|
|
161
205
|
}
|
|
162
|
-
static
|
|
163
|
-
if (
|
|
206
|
+
static mergePropertyFilters(propFilters, options) {
|
|
207
|
+
if (!options || !propFilters || options === true || propFilters === true) {
|
|
208
|
+
return options ?? propFilters;
|
|
209
|
+
}
|
|
210
|
+
if (Array.isArray(propFilters)) {
|
|
211
|
+
propFilters = propFilters.reduce((o, item) => {
|
|
212
|
+
o[item] = true;
|
|
213
|
+
return o;
|
|
214
|
+
}, {});
|
|
215
|
+
}
|
|
216
|
+
if (Array.isArray(options)) {
|
|
217
|
+
options = options.reduce((o, item) => {
|
|
218
|
+
o[item] = true;
|
|
219
|
+
return o;
|
|
220
|
+
}, {});
|
|
221
|
+
}
|
|
222
|
+
return Utils.mergeConfig({}, propFilters, options);
|
|
223
|
+
}
|
|
224
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
225
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
164
226
|
return false;
|
|
165
227
|
}
|
|
166
228
|
if (options[filterName] === false) {
|
|
@@ -170,8 +232,8 @@ export class QueryHelper {
|
|
|
170
232
|
}
|
|
171
233
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
172
234
|
if (Utils.isPlainObject(cond)) {
|
|
173
|
-
return Utils.
|
|
174
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
235
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
236
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
175
237
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
176
238
|
}
|
|
177
239
|
else {
|
|
@@ -180,12 +242,12 @@ export class QueryHelper {
|
|
|
180
242
|
return o;
|
|
181
243
|
}, {});
|
|
182
244
|
}
|
|
183
|
-
if (key &&
|
|
245
|
+
if (key && JSON_KEY_OPERATORS.includes(key)) {
|
|
184
246
|
return Array.isArray(cond)
|
|
185
247
|
? platform.marshallArray(cond)
|
|
186
248
|
: cond;
|
|
187
249
|
}
|
|
188
|
-
if (Array.isArray(cond) && !(key &&
|
|
250
|
+
if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
|
|
189
251
|
return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
|
|
190
252
|
}
|
|
191
253
|
if (isRaw(cond)) {
|
|
@@ -1,34 +1,23 @@
|
|
|
1
|
-
import { inspect } from 'node:util';
|
|
2
1
|
import type { AnyString, Dictionary, EntityKey } from '../typings.js';
|
|
2
|
+
declare const rawFragmentSymbolBrand: unique symbol;
|
|
3
|
+
export type RawQueryFragmentSymbol = symbol & {
|
|
4
|
+
readonly [rawFragmentSymbolBrand]: true;
|
|
5
|
+
};
|
|
3
6
|
export declare class RawQueryFragment {
|
|
4
7
|
#private;
|
|
5
8
|
readonly sql: string;
|
|
6
9
|
readonly params: unknown[];
|
|
7
|
-
static cloneRegistry?: Set<string>;
|
|
8
10
|
constructor(sql: string, params?: unknown[]);
|
|
11
|
+
get key(): RawQueryFragmentSymbol;
|
|
9
12
|
as(alias: string): RawQueryFragment;
|
|
10
|
-
|
|
13
|
+
[Symbol.toPrimitive](hint: 'string'): RawQueryFragmentSymbol;
|
|
14
|
+
get [Symbol.toStringTag](): string;
|
|
11
15
|
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
|
-
};
|
|
16
|
+
clone(): this;
|
|
17
|
+
static isKnownFragmentSymbol(key: unknown): key is RawQueryFragmentSymbol;
|
|
18
|
+
static hasObjectFragments(object: unknown): boolean;
|
|
19
|
+
static isKnownFragment(key: unknown): key is RawQueryFragment | symbol;
|
|
20
|
+
static getKnownFragment(key: unknown): RawQueryFragment | undefined;
|
|
32
21
|
}
|
|
33
22
|
export { RawQueryFragment as Raw };
|
|
34
23
|
export declare function isRaw(value: unknown): value is RawQueryFragment;
|
|
@@ -72,8 +61,26 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
|
|
|
72
61
|
* ```ts
|
|
73
62
|
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
74
63
|
* ```
|
|
64
|
+
*
|
|
65
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
66
|
+
*
|
|
67
|
+
* ```ts
|
|
68
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
69
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
70
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
71
|
+
* @Entity({ schema: 'library' })
|
|
72
|
+
* export class Author { ... }
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
76
|
+
*
|
|
77
|
+
* ```ts
|
|
78
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
79
|
+
* @Entity({ schema: 'library' })
|
|
80
|
+
* export class Author { ... }
|
|
81
|
+
* ```
|
|
75
82
|
*/
|
|
76
|
-
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R
|
|
83
|
+
export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): NoInfer<R>;
|
|
77
84
|
/**
|
|
78
85
|
* Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
|
|
79
86
|
*
|
|
@@ -90,9 +97,25 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
|
|
|
90
97
|
*/
|
|
91
98
|
export declare function sql(sql: readonly string[], ...values: unknown[]): any;
|
|
92
99
|
export declare namespace sql {
|
|
93
|
-
var ref: <T extends object>(...keys: string[]) => RawQueryFragment
|
|
100
|
+
var ref: <T extends object>(...keys: string[]) => NoInfer<RawQueryFragment>;
|
|
94
101
|
var now: (length?: number) => string;
|
|
95
102
|
var lower: <T extends object>(key: string | ((alias: string) => string)) => string;
|
|
96
103
|
var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
|
|
97
104
|
}
|
|
98
105
|
export declare function createSqlFunction<T extends object, R = string>(func: string, key: string | ((alias: string) => string)): R;
|
|
106
|
+
/**
|
|
107
|
+
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
108
|
+
*
|
|
109
|
+
* Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
113
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
114
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
115
|
+
* @Entity({ schema: 'library' })
|
|
116
|
+
* export class Author { ... }
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function quote(expParts: readonly string[], ...values: (string | {
|
|
120
|
+
toString(): string;
|
|
121
|
+
})[]): any;
|
|
@@ -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
|
-
|
|
8
|
-
static #
|
|
9
|
-
static #index = 0n;
|
|
10
|
-
static cloneRegistry;
|
|
11
|
-
#assigned = false;
|
|
12
|
-
#used = 0;
|
|
5
|
+
// holds a weak reference to fragments used as object keys
|
|
6
|
+
static #rawQueryReferences = new WeakMap();
|
|
13
7
|
#key;
|
|
14
8
|
constructor(sql, params = []) {
|
|
15
9
|
this.sql = sql;
|
|
16
10
|
this.params = params;
|
|
17
|
-
this.#key = `[raw]: ${this.sql} (#${RawQueryFragment.#index++})`;
|
|
18
11
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return new RawQueryFragment(`${this.sql} as ${alias}`, this.params);
|
|
12
|
+
get key() {
|
|
13
|
+
if (!this.#key) {
|
|
14
|
+
this.#key = Symbol(this.toJSON());
|
|
15
|
+
RawQueryFragment.#rawQueryReferences.set(this.#key, this);
|
|
24
16
|
}
|
|
17
|
+
return this.#key;
|
|
18
|
+
}
|
|
19
|
+
as(alias) {
|
|
25
20
|
return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
|
|
26
21
|
}
|
|
27
|
-
|
|
22
|
+
[Symbol.toPrimitive](hint) {
|
|
23
|
+
// if a fragment is converted to string (used as an object key), return a unique symbol
|
|
24
|
+
// and save a weak reference to map so we can retrieve it when compiling the query
|
|
25
|
+
if (hint === 'string') {
|
|
26
|
+
return this.key;
|
|
27
|
+
}
|
|
28
28
|
throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
|
|
29
29
|
}
|
|
30
|
+
get [Symbol.toStringTag]() {
|
|
31
|
+
return this.toJSON();
|
|
32
|
+
}
|
|
30
33
|
toJSON() {
|
|
31
|
-
return this
|
|
34
|
+
return `raw('${this.sql}')`;
|
|
32
35
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
this.#used++;
|
|
36
|
-
return this.#key;
|
|
36
|
+
clone() {
|
|
37
|
+
return this;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (this.#assigned) {
|
|
41
|
-
throw new Error(`Cannot reassign already used RawQueryFragment: '${this.sql}'`);
|
|
42
|
-
}
|
|
43
|
-
this.#assigned = true;
|
|
39
|
+
static isKnownFragmentSymbol(key) {
|
|
40
|
+
return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
|
|
44
41
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return new RawQueryFragment(this.sql, this.params);
|
|
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;
|
|
42
|
+
static hasObjectFragments(object) {
|
|
43
|
+
return Utils.isPlainObject(object) && 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
|
}
|
|
@@ -147,6 +113,24 @@ export const ALIAS_REPLACEMENT_RE = '\\[::alias::\\]';
|
|
|
147
113
|
* ```ts
|
|
148
114
|
* @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
|
|
149
115
|
* ```
|
|
116
|
+
*
|
|
117
|
+
* The `raw` helper can be used within indexes and uniques to write database-agnostic SQL expressions. In that case, you can use `'??'` to tag your database identifiers (table name, column names, index name, ...) inside your expression, and pass those identifiers as a second parameter to the `raw` helper. Internally, those will automatically be quoted according to the database in use:
|
|
118
|
+
*
|
|
119
|
+
* ```ts
|
|
120
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("country")
|
|
121
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`country`)
|
|
122
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => raw(`create index ?? on ?? (??)`, ['custom_idx_on_name', table, columns.name]) })
|
|
123
|
+
* @Entity({ schema: 'library' })
|
|
124
|
+
* export class Author { ... }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* You can also use the `quote` tag function to write database-agnostic SQL expressions. The end-result is the same as using the `raw` function regarding database identifiers quoting, only to have a more elegant expression syntax:
|
|
128
|
+
*
|
|
129
|
+
* ```ts
|
|
130
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
131
|
+
* @Entity({ schema: 'library' })
|
|
132
|
+
* export class Author { ... }
|
|
133
|
+
* ```
|
|
150
134
|
*/
|
|
151
135
|
export function raw(sql, params) {
|
|
152
136
|
if (sql instanceof RawQueryFragment) {
|
|
@@ -189,11 +173,7 @@ export function raw(sql, params) {
|
|
|
189
173
|
* ```
|
|
190
174
|
*/
|
|
191
175
|
export function sql(sql, ...values) {
|
|
192
|
-
return raw(sql.
|
|
193
|
-
const valueExists = i < values.length;
|
|
194
|
-
const text = query + queryPart;
|
|
195
|
-
return valueExists ? text + '?' : text;
|
|
196
|
-
}, ''), values);
|
|
176
|
+
return raw(sql.join('?'), values);
|
|
197
177
|
}
|
|
198
178
|
export function createSqlFunction(func, key) {
|
|
199
179
|
if (typeof key === 'string') {
|
|
@@ -205,3 +185,19 @@ sql.ref = (...keys) => raw('??', [keys.join('.')]);
|
|
|
205
185
|
sql.now = (length) => raw('current_timestamp' + (length == null ? '' : `(${length})`));
|
|
206
186
|
sql.lower = (key) => createSqlFunction('lower', key);
|
|
207
187
|
sql.upper = (key) => createSqlFunction('upper', key);
|
|
188
|
+
/**
|
|
189
|
+
* Tag function providing quoting of db identifiers (table name, columns names, index names, ...).
|
|
190
|
+
*
|
|
191
|
+
* Within the template literal on which the tag function is applied, all placeholders are considered to be database identifiers, and will thus be quoted as so according to the database in use.
|
|
192
|
+
*
|
|
193
|
+
* ```ts
|
|
194
|
+
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
195
|
+
* // On mysql, will produce: create index `index custom_idx_on_name` on `library.author` (`name`)
|
|
196
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns) => quote`create index ${'custom_idx_on_name'} on ${table} (${columns.name})` })
|
|
197
|
+
* @Entity({ schema: 'library' })
|
|
198
|
+
* export class Author { ... }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export function quote(expParts, ...values) {
|
|
202
|
+
return raw(expParts.join('??'), values);
|
|
203
|
+
}
|
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;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { EntityManager } from '../EntityManager.js';
|
|
2
|
+
import { type TransactionOptions } from '../enums.js';
|
|
3
|
+
/**
|
|
4
|
+
* Manages transaction lifecycle and propagation for EntityManager.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TransactionManager {
|
|
7
|
+
private readonly em;
|
|
8
|
+
constructor(em: EntityManager);
|
|
9
|
+
/**
|
|
10
|
+
* Main entry point for handling transactional operations with propagation support.
|
|
11
|
+
*/
|
|
12
|
+
handle<T>(cb: (em: EntityManager) => T | Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
13
|
+
/**
|
|
14
|
+
* Executes the callback with the specified propagation type.
|
|
15
|
+
*/
|
|
16
|
+
private executeWithPropagation;
|
|
17
|
+
/**
|
|
18
|
+
* Suspends the current transaction and returns the suspended resources.
|
|
19
|
+
*/
|
|
20
|
+
private suspendTransaction;
|
|
21
|
+
/**
|
|
22
|
+
* Resumes a previously suspended transaction.
|
|
23
|
+
*/
|
|
24
|
+
private resumeTransaction;
|
|
25
|
+
/**
|
|
26
|
+
* Executes operation without transaction context.
|
|
27
|
+
*/
|
|
28
|
+
private executeWithoutTransaction;
|
|
29
|
+
/**
|
|
30
|
+
* Creates new independent transaction, suspending any existing one.
|
|
31
|
+
*/
|
|
32
|
+
private executeWithNewTransaction;
|
|
33
|
+
/**
|
|
34
|
+
* Creates new transaction context.
|
|
35
|
+
*/
|
|
36
|
+
private createNewTransaction;
|
|
37
|
+
/**
|
|
38
|
+
* Executes nested transaction with savepoint.
|
|
39
|
+
*/
|
|
40
|
+
private executeNestedTransaction;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a fork of the EntityManager with the given options.
|
|
43
|
+
*/
|
|
44
|
+
private createFork;
|
|
45
|
+
/**
|
|
46
|
+
* Determines if changes should be propagated to the upper context.
|
|
47
|
+
*/
|
|
48
|
+
private shouldPropagateToUpperContext;
|
|
49
|
+
/**
|
|
50
|
+
* Merges entities from fork to parent EntityManager.
|
|
51
|
+
*/
|
|
52
|
+
private mergeEntitiesToParent;
|
|
53
|
+
/**
|
|
54
|
+
* Registers a deletion handler to unset entity identities after flush.
|
|
55
|
+
*/
|
|
56
|
+
private registerDeletionHandler;
|
|
57
|
+
/**
|
|
58
|
+
* Processes transaction execution.
|
|
59
|
+
*/
|
|
60
|
+
private processTransaction;
|
|
61
|
+
/**
|
|
62
|
+
* Executes transaction workflow with entity synchronization.
|
|
63
|
+
*/
|
|
64
|
+
private executeTransactionFlow;
|
|
65
|
+
}
|