@mikro-orm/core 7.0.0-dev.23 → 7.0.0-dev.230
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 +91 -59
- package/EntityManager.js +303 -251
- package/MikroORM.d.ts +44 -35
- package/MikroORM.js +109 -143
- package/README.md +2 -0
- package/cache/FileCacheAdapter.d.ts +1 -1
- package/cache/FileCacheAdapter.js +17 -8
- 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 +21 -12
- package/drivers/DatabaseDriver.d.ts +25 -16
- package/drivers/DatabaseDriver.js +118 -35
- package/drivers/IDatabaseDriver.d.ts +42 -19
- package/entity/BaseEntity.d.ts +61 -2
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +101 -29
- package/entity/Collection.js +436 -104
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +26 -18
- package/entity/EntityFactory.d.ts +7 -1
- package/entity/EntityFactory.js +83 -54
- package/entity/EntityHelper.d.ts +2 -2
- package/entity/EntityHelper.js +48 -15
- package/entity/EntityLoader.d.ts +7 -6
- package/entity/EntityLoader.js +215 -89
- package/entity/EntityRepository.d.ts +27 -7
- package/entity/EntityRepository.js +8 -2
- package/entity/PolymorphicRef.d.ts +12 -0
- package/entity/PolymorphicRef.js +18 -0
- package/entity/Reference.d.ts +1 -5
- package/entity/Reference.js +21 -12
- package/entity/WrappedEntity.d.ts +0 -5
- package/entity/WrappedEntity.js +2 -7
- package/entity/defineEntity.d.ts +380 -310
- package/entity/defineEntity.js +124 -273
- package/entity/index.d.ts +2 -2
- package/entity/index.js +2 -2
- package/entity/utils.js +1 -1
- package/entity/validators.d.ts +11 -0
- package/entity/validators.js +65 -0
- package/enums.d.ts +8 -6
- package/enums.js +2 -1
- package/errors.d.ts +20 -10
- package/errors.js +55 -23
- 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 +87 -35
- 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 +47 -23
- package/metadata/EntitySchema.js +92 -33
- package/metadata/MetadataDiscovery.d.ts +64 -9
- package/metadata/MetadataDiscovery.js +778 -325
- 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 +32 -9
- package/metadata/MetadataValidator.js +196 -41
- 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 +526 -0
- package/metadata/types.js +1 -0
- package/naming-strategy/AbstractNamingStrategy.d.ts +16 -4
- package/naming-strategy/AbstractNamingStrategy.js +20 -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 +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 +4 -0
- package/package.json +19 -11
- package/platforms/ExceptionConverter.js +1 -1
- package/platforms/Platform.d.ts +7 -14
- package/platforms/Platform.js +20 -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 +3 -3
- 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 +1 -0
- package/types/BooleanType.js +3 -0
- package/types/DecimalType.d.ts +6 -4
- 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 +381 -171
- package/typings.js +97 -44
- package/unit-of-work/ChangeSet.d.ts +4 -6
- package/unit-of-work/ChangeSet.js +4 -5
- package/unit-of-work/ChangeSetComputer.d.ts +1 -3
- package/unit-of-work/ChangeSetComputer.js +35 -14
- package/unit-of-work/ChangeSetPersister.d.ts +7 -3
- package/unit-of-work/ChangeSetPersister.js +83 -25
- package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
- package/unit-of-work/CommitOrderCalculator.js +13 -13
- package/unit-of-work/IdentityMap.d.ts +12 -0
- package/unit-of-work/IdentityMap.js +39 -1
- package/unit-of-work/UnitOfWork.d.ts +27 -3
- package/unit-of-work/UnitOfWork.js +258 -92
- package/utils/AbstractSchemaGenerator.d.ts +5 -5
- package/utils/AbstractSchemaGenerator.js +28 -17
- package/utils/AsyncContext.d.ts +6 -0
- package/utils/AsyncContext.js +42 -0
- package/utils/Configuration.d.ts +795 -209
- package/utils/Configuration.js +150 -192
- package/utils/ConfigurationLoader.d.ts +1 -54
- package/utils/ConfigurationLoader.js +1 -352
- package/utils/Cursor.d.ts +0 -3
- package/utils/Cursor.js +24 -11
- package/utils/DataloaderUtils.d.ts +10 -5
- package/utils/DataloaderUtils.js +29 -12
- package/utils/EntityComparator.d.ts +16 -9
- package/utils/EntityComparator.js +158 -58
- package/utils/QueryHelper.d.ts +18 -6
- package/utils/QueryHelper.js +76 -23
- package/utils/RawQueryFragment.d.ts +28 -34
- package/utils/RawQueryFragment.js +35 -71
- package/utils/RequestContext.js +2 -2
- package/utils/TransactionContext.js +2 -2
- package/utils/TransactionManager.js +28 -4
- package/utils/Utils.d.ts +14 -127
- package/utils/Utils.js +85 -397
- 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 +33 -0
- package/utils/fs-utils.js +192 -0
- package/utils/index.d.ts +1 -1
- package/utils/index.js +1 -1
- package/utils/upsert-utils.d.ts +9 -4
- package/utils/upsert-utils.js +46 -3
- 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.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,7 +50,7 @@ export class QueryHelper {
|
|
|
44
50
|
}
|
|
45
51
|
const keys = Object.keys(where);
|
|
46
52
|
const groupOperator = keys.find(k => {
|
|
47
|
-
return
|
|
53
|
+
return k in GroupOperator && Array.isArray(where[k]) && where[k].every(cond => {
|
|
48
54
|
return Utils.isPlainObject(cond) && Object.keys(cond).every(k2 => {
|
|
49
55
|
if (Utils.isOperator(k2, false)) {
|
|
50
56
|
if (k2 === '$not') {
|
|
@@ -62,7 +68,10 @@ export class QueryHelper {
|
|
|
62
68
|
for (const k of keys) {
|
|
63
69
|
const value = where[k];
|
|
64
70
|
const prop = meta.properties[k];
|
|
65
|
-
|
|
71
|
+
// Polymorphic relations use multiple columns (discriminator + FK), so they cannot
|
|
72
|
+
// participate in the standard single-column FK expansion. Query by discriminator
|
|
73
|
+
// column directly instead, e.g. { likeableType: 'post', likeableId: 1 }.
|
|
74
|
+
if (!prop || ![ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) || prop.polymorphic) {
|
|
66
75
|
continue;
|
|
67
76
|
}
|
|
68
77
|
const op = this.liftGroupOperators(value, prop.targetMeta, metadata, k);
|
|
@@ -98,7 +107,7 @@ export class QueryHelper {
|
|
|
98
107
|
}));
|
|
99
108
|
}
|
|
100
109
|
Object.keys(where).forEach(k => {
|
|
101
|
-
const meta2 = metadata.find(meta.properties[k]?.
|
|
110
|
+
const meta2 = metadata.find(meta.properties[k]?.targetMeta?.class) || meta;
|
|
102
111
|
if (this.inlinePrimaryKeyObjects(where[k], meta2, metadata, k)) {
|
|
103
112
|
where[k] = Utils.getPrimaryKeyValues(where[k], meta2, true);
|
|
104
113
|
}
|
|
@@ -118,7 +127,7 @@ export class QueryHelper {
|
|
|
118
127
|
Utils.dropUndefinedProperties(where);
|
|
119
128
|
}
|
|
120
129
|
where = QueryHelper.processParams(where) ?? {};
|
|
121
|
-
/* v8 ignore next
|
|
130
|
+
/* v8 ignore next */
|
|
122
131
|
if (!root && Utils.isPrimaryKey(where)) {
|
|
123
132
|
return where;
|
|
124
133
|
}
|
|
@@ -126,7 +135,7 @@ export class QueryHelper {
|
|
|
126
135
|
where = { [Utils.getPrimaryKeyHash(meta.primaryKeys)]: where };
|
|
127
136
|
}
|
|
128
137
|
if (Array.isArray(where) && root) {
|
|
129
|
-
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : entityName;
|
|
138
|
+
const rootPrimaryKey = meta ? Utils.getPrimaryKeyHash(meta.primaryKeys) : Utils.className(entityName);
|
|
130
139
|
let cond = { [rootPrimaryKey]: { $in: where } };
|
|
131
140
|
// @ts-ignore
|
|
132
141
|
// detect tuple comparison, use `$or` in case the number of constituents don't match
|
|
@@ -138,12 +147,10 @@ export class QueryHelper {
|
|
|
138
147
|
if (!Utils.isPlainObject(where)) {
|
|
139
148
|
return where;
|
|
140
149
|
}
|
|
141
|
-
return
|
|
150
|
+
return Utils.getObjectQueryKeys(where).reduce((o, key) => {
|
|
142
151
|
let value = where[key];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
const composite = keys > 1;
|
|
146
|
-
if (Array.isArray(value) && value.length === 0 && RawQueryFragment.isKnownFragment(key)) {
|
|
152
|
+
const customExpression = Raw.isKnownFragmentSymbol(key);
|
|
153
|
+
if (Array.isArray(value) && value.length === 0 && customExpression) {
|
|
147
154
|
o[key] = value;
|
|
148
155
|
return o;
|
|
149
156
|
}
|
|
@@ -157,6 +164,9 @@ export class QueryHelper {
|
|
|
157
164
|
o[rootPrimaryKey] = { [key]: QueryHelper.processWhere({ ...options, where: value, root: false }) };
|
|
158
165
|
return o;
|
|
159
166
|
}
|
|
167
|
+
const prop = customExpression ? null : this.findProperty(key, options);
|
|
168
|
+
const keys = prop?.joinColumns?.length ?? 0;
|
|
169
|
+
const composite = keys > 1;
|
|
160
170
|
if (prop?.customType && convertCustomTypes && !isRaw(value)) {
|
|
161
171
|
value = QueryHelper.processCustomType(prop, value, platform, undefined, true);
|
|
162
172
|
}
|
|
@@ -164,7 +174,7 @@ export class QueryHelper {
|
|
|
164
174
|
if (isJsonProperty && prop?.kind !== ReferenceKind.EMBEDDED) {
|
|
165
175
|
return this.processJsonCondition(o, value, [prop.fieldNames[0]], platform, aliased);
|
|
166
176
|
}
|
|
167
|
-
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !
|
|
177
|
+
if (Array.isArray(value) && !Utils.isOperator(key) && !QueryHelper.isSupportedOperator(key) && !(customExpression && Raw.getKnownFragment(key).params.length > 0) && options.type !== 'orderBy') {
|
|
168
178
|
// comparing single composite key - use $eq instead of $in
|
|
169
179
|
const op = composite && !value.every(v => Array.isArray(v)) ? '$eq' : '$in';
|
|
170
180
|
o[key] = { [op]: value };
|
|
@@ -174,7 +184,7 @@ export class QueryHelper {
|
|
|
174
184
|
o[key] = QueryHelper.processWhere({
|
|
175
185
|
...options,
|
|
176
186
|
where: value,
|
|
177
|
-
entityName: prop?.
|
|
187
|
+
entityName: prop?.targetMeta?.class ?? entityName,
|
|
178
188
|
root: false,
|
|
179
189
|
});
|
|
180
190
|
}
|
|
@@ -184,7 +194,7 @@ export class QueryHelper {
|
|
|
184
194
|
return o;
|
|
185
195
|
}, {});
|
|
186
196
|
}
|
|
187
|
-
static getActiveFilters(
|
|
197
|
+
static getActiveFilters(meta, options, filters) {
|
|
188
198
|
if (options === false) {
|
|
189
199
|
return [];
|
|
190
200
|
}
|
|
@@ -196,14 +206,32 @@ export class QueryHelper {
|
|
|
196
206
|
Object.keys(options).forEach(filter => opts[filter] = options[filter]);
|
|
197
207
|
}
|
|
198
208
|
return Object.keys(filters)
|
|
199
|
-
.filter(f => QueryHelper.isFilterActive(
|
|
209
|
+
.filter(f => QueryHelper.isFilterActive(meta, f, filters[f], opts))
|
|
200
210
|
.map(f => {
|
|
201
211
|
filters[f].name = f;
|
|
202
212
|
return filters[f];
|
|
203
213
|
});
|
|
204
214
|
}
|
|
205
|
-
static
|
|
206
|
-
if (
|
|
215
|
+
static mergePropertyFilters(propFilters, options) {
|
|
216
|
+
if (!options || !propFilters || options === true || propFilters === true) {
|
|
217
|
+
return options ?? propFilters;
|
|
218
|
+
}
|
|
219
|
+
if (Array.isArray(propFilters)) {
|
|
220
|
+
propFilters = propFilters.reduce((o, item) => {
|
|
221
|
+
o[item] = true;
|
|
222
|
+
return o;
|
|
223
|
+
}, {});
|
|
224
|
+
}
|
|
225
|
+
if (Array.isArray(options)) {
|
|
226
|
+
options = options.reduce((o, item) => {
|
|
227
|
+
o[item] = true;
|
|
228
|
+
return o;
|
|
229
|
+
}, {});
|
|
230
|
+
}
|
|
231
|
+
return Utils.mergeConfig({}, propFilters, options);
|
|
232
|
+
}
|
|
233
|
+
static isFilterActive(meta, filterName, filter, options) {
|
|
234
|
+
if (filter.entity && !filter.entity.includes(meta.className)) {
|
|
207
235
|
return false;
|
|
208
236
|
}
|
|
209
237
|
if (options[filterName] === false) {
|
|
@@ -213,8 +241,8 @@ export class QueryHelper {
|
|
|
213
241
|
}
|
|
214
242
|
static processCustomType(prop, cond, platform, key, fromQuery) {
|
|
215
243
|
if (Utils.isPlainObject(cond)) {
|
|
216
|
-
return Utils.
|
|
217
|
-
if (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k)) {
|
|
244
|
+
return Utils.getObjectQueryKeys(cond).reduce((o, k) => {
|
|
245
|
+
if (!Raw.isKnownFragmentSymbol(k) && (Utils.isOperator(k, true) || prop.referencedPKs?.includes(k))) {
|
|
218
246
|
o[k] = QueryHelper.processCustomType(prop, cond[k], platform, k, fromQuery);
|
|
219
247
|
}
|
|
220
248
|
else {
|
|
@@ -223,12 +251,12 @@ export class QueryHelper {
|
|
|
223
251
|
return o;
|
|
224
252
|
}, {});
|
|
225
253
|
}
|
|
226
|
-
if (key &&
|
|
254
|
+
if (key && JSON_KEY_OPERATORS.includes(key)) {
|
|
227
255
|
return Array.isArray(cond)
|
|
228
256
|
? platform.marshallArray(cond)
|
|
229
257
|
: cond;
|
|
230
258
|
}
|
|
231
|
-
if (Array.isArray(cond) && !(key &&
|
|
259
|
+
if (Array.isArray(cond) && !(key && ARRAY_OPERATORS.includes(key))) {
|
|
232
260
|
return cond.map(v => QueryHelper.processCustomType(prop, v, platform, key, fromQuery));
|
|
233
261
|
}
|
|
234
262
|
if (isRaw(cond)) {
|
|
@@ -272,4 +300,29 @@ export class QueryHelper {
|
|
|
272
300
|
const meta = entityName ? options.metadata.find(entityName) : undefined;
|
|
273
301
|
return meta?.properties[propName];
|
|
274
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Merges multiple orderBy sources with key-level deduplication (first-seen key wins).
|
|
305
|
+
* RawQueryFragment symbol keys are never deduped (each is unique).
|
|
306
|
+
*/
|
|
307
|
+
static mergeOrderBy(...sources) {
|
|
308
|
+
const result = [];
|
|
309
|
+
const seenKeys = new Set();
|
|
310
|
+
for (const source of sources) {
|
|
311
|
+
if (source == null) {
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
for (const item of Utils.asArray(source)) {
|
|
315
|
+
for (const key of Utils.getObjectQueryKeys(item)) {
|
|
316
|
+
if (typeof key === 'symbol') {
|
|
317
|
+
result.push({ [key]: item[key] });
|
|
318
|
+
}
|
|
319
|
+
else if (!seenKeys.has(key)) {
|
|
320
|
+
seenKeys.add(key);
|
|
321
|
+
result.push({ [key]: item[key] });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return result;
|
|
327
|
+
}
|
|
275
328
|
}
|
|
@@ -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,64 @@
|
|
|
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
|
-
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;
|
|
41
|
+
static hasObjectFragments(object) {
|
|
42
|
+
return Utils.isPlainObject(object) && Object.getOwnPropertySymbols(object).some(symbol => this.isKnownFragmentSymbol(symbol));
|
|
61
43
|
}
|
|
62
44
|
static isKnownFragment(key) {
|
|
63
45
|
if (key instanceof RawQueryFragment) {
|
|
64
46
|
return true;
|
|
65
47
|
}
|
|
66
|
-
return this
|
|
48
|
+
return this.isKnownFragmentSymbol(key);
|
|
67
49
|
}
|
|
68
|
-
static getKnownFragment(key
|
|
50
|
+
static getKnownFragment(key) {
|
|
69
51
|
if (key instanceof RawQueryFragment) {
|
|
70
52
|
return key;
|
|
71
53
|
}
|
|
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) {
|
|
54
|
+
if (typeof key !== 'symbol') {
|
|
81
55
|
return;
|
|
82
56
|
}
|
|
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
|
-
}
|
|
57
|
+
return this.#rawQueryReferences.get(key);
|
|
93
58
|
}
|
|
94
|
-
/* v8 ignore next 8 */
|
|
95
59
|
/** @ignore */
|
|
96
|
-
|
|
60
|
+
/* v8 ignore next */
|
|
61
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
97
62
|
if (this.params) {
|
|
98
63
|
return { sql: this.sql, params: this.params };
|
|
99
64
|
}
|
|
@@ -204,14 +169,13 @@ export function raw(sql, params) {
|
|
|
204
169
|
*
|
|
205
170
|
* // value can be empty array
|
|
206
171
|
* await em.find(User, { [sql`(select 1 = 1)`]: [] });
|
|
172
|
+
*
|
|
173
|
+
* // with type parameter for assignment without casting
|
|
174
|
+
* entity.date = sql<Date>`now()`;
|
|
207
175
|
* ```
|
|
208
176
|
*/
|
|
209
177
|
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);
|
|
178
|
+
return raw(sql.join('?'), values);
|
|
215
179
|
}
|
|
216
180
|
export function createSqlFunction(func, key) {
|
|
217
181
|
if (typeof key === 'string') {
|
|
@@ -231,7 +195,7 @@ sql.upper = (key) => createSqlFunction('upper', key);
|
|
|
231
195
|
* ```ts
|
|
232
196
|
* // On postgres, will produce: create index "index custom_idx_on_name" on "library.author" ("name")
|
|
233
197
|
* // 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 ${
|
|
198
|
+
* @Index({ name: 'custom_idx_on_name', expression: (table, columns, indexName) => quote`create index ${indexName} on ${table} (${columns.name})` })
|
|
235
199
|
* @Entity({ schema: 'library' })
|
|
236
200
|
* export class Author { ... }
|
|
237
201
|
* ```
|
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;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { TransactionPropagation } from '../enums.js';
|
|
1
|
+
import { ReferenceKind, TransactionPropagation } from '../enums.js';
|
|
2
2
|
import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
|
|
3
3
|
import { TransactionContext } from '../utils/TransactionContext.js';
|
|
4
4
|
import { ChangeSetType } from '../unit-of-work/ChangeSet.js';
|
|
5
5
|
import { TransactionStateError } from '../errors.js';
|
|
6
|
+
import { helper } from '../entity/wrap.js';
|
|
6
7
|
/**
|
|
7
8
|
* Manages transaction lifecycle and propagation for EntityManager.
|
|
8
9
|
*/
|
|
@@ -16,9 +17,7 @@ export class TransactionManager {
|
|
|
16
17
|
*/
|
|
17
18
|
async handle(cb, options = {}) {
|
|
18
19
|
const em = this.em.getContext(false);
|
|
19
|
-
// Set NESTED as the default propagation type
|
|
20
20
|
options.propagation ??= TransactionPropagation.NESTED;
|
|
21
|
-
// Set the context to the current transaction context if not already set
|
|
22
21
|
options.ctx ??= em.getTransactionContext();
|
|
23
22
|
const hasExistingTransaction = !!em.getTransactionContext();
|
|
24
23
|
return this.executeWithPropagation(options.propagation, em, cb, options, hasExistingTransaction);
|
|
@@ -149,8 +148,33 @@ export class TransactionManager {
|
|
|
149
148
|
* Merges entities from fork to parent EntityManager.
|
|
150
149
|
*/
|
|
151
150
|
mergeEntitiesToParent(fork, parent) {
|
|
151
|
+
const parentUoW = parent.getUnitOfWork(false);
|
|
152
|
+
// perf: if parent is empty, we can just move all entities from the fork to skip the `em.merge` overhead
|
|
153
|
+
if (parentUoW.getIdentityMap().keys().length === 0) {
|
|
154
|
+
for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
|
|
155
|
+
parentUoW.getIdentityMap().store(entity);
|
|
156
|
+
helper(entity).__em = parent;
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
152
160
|
for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
|
|
153
|
-
|
|
161
|
+
const wrapped = helper(entity);
|
|
162
|
+
const meta = wrapped.__meta;
|
|
163
|
+
// eslint-disable-next-line dot-notation
|
|
164
|
+
const parentEntity = parentUoW.getById(meta.class, wrapped.getPrimaryKey(), parent['_schema'], true);
|
|
165
|
+
if (parentEntity && parentEntity !== entity) {
|
|
166
|
+
const parentWrapped = helper(parentEntity);
|
|
167
|
+
parentWrapped.__data = wrapped.__data;
|
|
168
|
+
parentWrapped.__originalEntityData = wrapped.__originalEntityData;
|
|
169
|
+
for (const prop of meta.hydrateProps) {
|
|
170
|
+
if (prop.kind === ReferenceKind.SCALAR) {
|
|
171
|
+
parentEntity[prop.name] = entity[prop.name];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
parentUoW.merge(entity, new Set([entity]));
|
|
177
|
+
}
|
|
154
178
|
}
|
|
155
179
|
}
|
|
156
180
|
/**
|