@mikro-orm/core 7.2.6-dev.31 → 7.2.6-dev.33
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/metadata/MetadataDiscovery.d.ts +5 -0
- package/metadata/MetadataDiscovery.js +44 -13
- package/metadata/MetadataStorage.d.ts +1 -0
- package/metadata/MetadataStorage.js +20 -3
- package/metadata/MetadataValidator.js +2 -1
- package/package.json +1 -1
- package/utils/Utils.js +11 -6
- package/utils/clone.js +5 -7
|
@@ -34,6 +34,11 @@ export declare class MetadataDiscovery {
|
|
|
34
34
|
private initManyToOneFields;
|
|
35
35
|
private initOneToManyFields;
|
|
36
36
|
private processEntity;
|
|
37
|
+
/**
|
|
38
|
+
* Groups the relations of all entities by target class name in one pass, so `findReferencingProperties()`
|
|
39
|
+
* does not go over the relations of every entity for each entity. Lists keep the order of `metadata` and `relations`.
|
|
40
|
+
*/
|
|
41
|
+
private indexReferencingProperties;
|
|
37
42
|
private findReferencingProperties;
|
|
38
43
|
private initFactoryField;
|
|
39
44
|
private ensureCorrectFKOrderInPivotEntity;
|
|
@@ -183,10 +183,11 @@ export class MetadataDiscovery {
|
|
|
183
183
|
}
|
|
184
184
|
discovered.forEach(meta => meta.sync(true));
|
|
185
185
|
this.#metadataProvider.combineCache();
|
|
186
|
+
const referencingProperties = this.indexReferencingProperties(filtered);
|
|
186
187
|
return discovered.map(meta => {
|
|
187
188
|
meta = this.#metadata.get(meta.class);
|
|
188
189
|
meta.sync(true);
|
|
189
|
-
this.findReferencingProperties(meta,
|
|
190
|
+
this.findReferencingProperties(meta, referencingProperties);
|
|
190
191
|
if (meta.inheritanceType === 'tpt') {
|
|
191
192
|
this.computeTPTOwnProps(meta);
|
|
192
193
|
}
|
|
@@ -217,18 +218,20 @@ export class MetadataDiscovery {
|
|
|
217
218
|
.replace(/Array<(.*)>/, '$1') // unwrap array
|
|
218
219
|
.replace(/\[]$/, '') // remove array suffix
|
|
219
220
|
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
221
|
+
const discoveredNames = new Set(this.#discovered.map(m => m.className));
|
|
220
222
|
// Names can be ambiguous when a minifier mangles two classes to the same name,
|
|
221
223
|
// so class references also need to be checked by identity.
|
|
224
|
+
const discoveredClasses = new Set(this.#discovered.map(m => m.class));
|
|
222
225
|
const discoveredByIdentity = (target) => {
|
|
223
226
|
const cls = EntitySchema.is(target) ? target.meta.class : target;
|
|
224
|
-
return typeof cls !== 'function' ||
|
|
227
|
+
return typeof cls !== 'function' || discoveredClasses.has(cls);
|
|
225
228
|
};
|
|
226
229
|
const missing = [];
|
|
227
230
|
this.#discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
228
231
|
const indirect = (prop.kind === ReferenceKind.MANY_TO_MANY ? prop.pivotEntity : prop.through);
|
|
229
232
|
if (indirect) {
|
|
230
233
|
const target = typeof indirect === 'function' && !indirect.prototype ? indirect() : indirect;
|
|
231
|
-
if (!
|
|
234
|
+
if (!discoveredNames.has(Utils.className(target)) || !discoveredByIdentity(target)) {
|
|
232
235
|
missing.push(target);
|
|
233
236
|
}
|
|
234
237
|
}
|
|
@@ -236,7 +239,7 @@ export class MetadataDiscovery {
|
|
|
236
239
|
const target = typeof prop.entity === 'function' && !prop.entity.prototype ? prop.entity() : prop.type;
|
|
237
240
|
if (!unwrap(prop.type)
|
|
238
241
|
.split(/ ?\| ?/)
|
|
239
|
-
.every(type =>
|
|
242
|
+
.every(type => discoveredNames.has(type)) ||
|
|
240
243
|
!Utils.asArray(target).every(discoveredByIdentity)) {
|
|
241
244
|
missing.push(...Utils.asArray(target));
|
|
242
245
|
}
|
|
@@ -302,7 +305,8 @@ export class MetadataDiscovery {
|
|
|
302
305
|
if (validate) {
|
|
303
306
|
this.#validator.validateDiscovered(this.#discovered, this.#config.get('discovery'));
|
|
304
307
|
}
|
|
305
|
-
|
|
308
|
+
const foundNames = new Set(found.map(m => m.name));
|
|
309
|
+
return this.#discovered.filter(meta => foundNames.has(meta.className));
|
|
306
310
|
}
|
|
307
311
|
reset(entityName) {
|
|
308
312
|
const exists = this.#discovered.findIndex(m => m.class === entityName || m.className === Utils.className(entityName));
|
|
@@ -668,20 +672,47 @@ export class MetadataDiscovery {
|
|
|
668
672
|
}
|
|
669
673
|
return [];
|
|
670
674
|
}
|
|
671
|
-
|
|
675
|
+
/**
|
|
676
|
+
* Groups the relations of all entities by target class name in one pass, so `findReferencingProperties()`
|
|
677
|
+
* does not go over the relations of every entity for each entity. Lists keep the order of `metadata` and `relations`.
|
|
678
|
+
*/
|
|
679
|
+
indexReferencingProperties(metadata) {
|
|
680
|
+
const index = { byType: new Map(), withTargetKey: new Map() };
|
|
681
|
+
const add = (map, key, value) => {
|
|
682
|
+
const list = map.get(key);
|
|
683
|
+
if (list) {
|
|
684
|
+
list.push(value);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
map.set(key, [value]);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
672
690
|
for (const meta2 of metadata) {
|
|
673
691
|
for (const prop2 of meta2.relations) {
|
|
674
|
-
if (prop2.kind !== ReferenceKind.SCALAR
|
|
675
|
-
|
|
692
|
+
if (prop2.kind !== ReferenceKind.SCALAR) {
|
|
693
|
+
add(index.byType, prop2.type, [meta2, prop2]);
|
|
676
694
|
}
|
|
677
|
-
if (prop2.targetKey
|
|
678
|
-
(prop2.type
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
695
|
+
if (prop2.targetKey) {
|
|
696
|
+
const targets = new Set([prop2.type, ...(prop2.polymorphTargets?.map(t => t.className) ?? [])]);
|
|
697
|
+
for (const target of targets) {
|
|
698
|
+
add(index.withTargetKey, target, prop2);
|
|
699
|
+
}
|
|
682
700
|
}
|
|
683
701
|
}
|
|
684
702
|
}
|
|
703
|
+
return index;
|
|
704
|
+
}
|
|
705
|
+
findReferencingProperties(meta, index) {
|
|
706
|
+
for (const [meta2, prop2] of index.byType.get(meta.className) ?? []) {
|
|
707
|
+
meta.referencingProperties.push({ meta: meta2, prop: prop2 });
|
|
708
|
+
}
|
|
709
|
+
for (const prop2 of index.withTargetKey.get(meta.className) ?? []) {
|
|
710
|
+
const targetKey = prop2.targetKey;
|
|
711
|
+
if (!(meta.primaryKeys.length === 1 && meta.primaryKeys[0] === targetKey) &&
|
|
712
|
+
!meta.root.targetKeys?.includes(targetKey)) {
|
|
713
|
+
(meta.root.targetKeys ??= []).push(targetKey);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
685
716
|
}
|
|
686
717
|
initFactoryField(meta, prop) {
|
|
687
718
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
@@ -9,6 +9,7 @@ export declare class MetadataStorage {
|
|
|
9
9
|
/** Returns the global metadata dictionary, or a specific entry by entity name and path (keyed by the class reference when `target` is provided). */
|
|
10
10
|
static getMetadata(): Dictionary<EntityMetadata>;
|
|
11
11
|
static getMetadata<T = any>(entity: string, path: string, target?: EntityCtor): EntityMetadata<T>;
|
|
12
|
+
private static hashPath;
|
|
12
13
|
/** Checks whether an entity with the given class name exists in the global metadata. */
|
|
13
14
|
static isKnownEntity(name: string): boolean;
|
|
14
15
|
/** Clears all entries from the global metadata registry. */
|
|
@@ -13,6 +13,10 @@ export class MetadataStorage {
|
|
|
13
13
|
static PATH_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.PATH_SYMBOL');
|
|
14
14
|
static META_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.META_SYMBOL');
|
|
15
15
|
static #metadata = getGlobalStorage('metadata');
|
|
16
|
+
/** Lives in the global storage next to the registry, so it stays in sync when several copies of this module share it (e.g. after `vi.resetModules()`). */
|
|
17
|
+
static #classNames = getGlobalStorage('metadata-class-names');
|
|
18
|
+
/** Hashes of entity file paths, decorators call `getMetadata()` with the same path for every decorated member. */
|
|
19
|
+
static #pathHashes = new Map();
|
|
16
20
|
#metadataMap = new Map();
|
|
17
21
|
#idMap;
|
|
18
22
|
#classNameMap;
|
|
@@ -31,7 +35,7 @@ export class MetadataStorage {
|
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
static getMetadata(entity, path, target) {
|
|
34
|
-
const key = entity && path ? entity + '-' +
|
|
38
|
+
const key = entity && path ? entity + '-' + MetadataStorage.hashPath(path) : null;
|
|
35
39
|
// Key the registry by the class reference when available, so two classes minified
|
|
36
40
|
// to the same mangled name don't collide on the `className-path` key.
|
|
37
41
|
if (key && target) {
|
|
@@ -43,23 +47,36 @@ export class MetadataStorage {
|
|
|
43
47
|
}
|
|
44
48
|
// Keep the name-keyed entry in sync, the class-keyed metadata survives `MetadataStorage.clear()`.
|
|
45
49
|
MetadataStorage.#metadata[key] = target[MetadataStorage.META_SYMBOL];
|
|
50
|
+
MetadataStorage.#classNames.names?.add(target[MetadataStorage.META_SYMBOL].className);
|
|
46
51
|
return target[MetadataStorage.META_SYMBOL];
|
|
47
52
|
}
|
|
48
53
|
if (key && !MetadataStorage.#metadata[key]) {
|
|
49
|
-
|
|
54
|
+
const meta = new EntityMetadata({ className: entity, path });
|
|
55
|
+
MetadataStorage.#metadata[key] = meta;
|
|
56
|
+
MetadataStorage.#classNames.names?.add(meta.className);
|
|
50
57
|
}
|
|
51
58
|
if (key) {
|
|
52
59
|
return MetadataStorage.#metadata[key];
|
|
53
60
|
}
|
|
54
61
|
return MetadataStorage.#metadata;
|
|
55
62
|
}
|
|
63
|
+
static hashPath(path) {
|
|
64
|
+
let hash = MetadataStorage.#pathHashes.get(path);
|
|
65
|
+
if (hash === undefined) {
|
|
66
|
+
hash = Utils.hash(path);
|
|
67
|
+
MetadataStorage.#pathHashes.set(path, hash);
|
|
68
|
+
}
|
|
69
|
+
return hash;
|
|
70
|
+
}
|
|
56
71
|
/** Checks whether an entity with the given class name exists in the global metadata. */
|
|
57
72
|
static isKnownEntity(name) {
|
|
58
|
-
|
|
73
|
+
this.#classNames.names ??= new Set(Object.values(this.#metadata).map(meta => meta.className));
|
|
74
|
+
return this.#classNames.names.has(name);
|
|
59
75
|
}
|
|
60
76
|
/** Clears all entries from the global metadata registry. */
|
|
61
77
|
static clear() {
|
|
62
78
|
Object.keys(this.#metadata).forEach(k => delete this.#metadata[k]);
|
|
79
|
+
delete this.#classNames.names;
|
|
63
80
|
}
|
|
64
81
|
/** Returns the map of all registered entity metadata. */
|
|
65
82
|
getAll() {
|
|
@@ -118,12 +118,13 @@ export class MetadataValidator {
|
|
|
118
118
|
return Utils.className(p);
|
|
119
119
|
};
|
|
120
120
|
const pivotProps = new Map();
|
|
121
|
+
const discoveredNames = new Set(discovered.map(m => m.className));
|
|
121
122
|
// check for not discovered entities
|
|
122
123
|
discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
123
124
|
if (prop.kind !== ReferenceKind.SCALAR &&
|
|
124
125
|
!unwrap(prop.type)
|
|
125
126
|
.split(/ ?\| ?/)
|
|
126
|
-
.every(type =>
|
|
127
|
+
.every(type => discoveredNames.has(type))) {
|
|
127
128
|
throw MetadataError.fromUnknownEntity(prop.type, `${meta.className}.${prop.name}`);
|
|
128
129
|
}
|
|
129
130
|
if (prop.pivotEntity) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
|
-
"version": "7.2.6-dev.
|
|
3
|
+
"version": "7.2.6-dev.33",
|
|
4
4
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-mapper",
|
package/utils/Utils.js
CHANGED
|
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
|
|
|
153
153
|
/** Collection of general-purpose utility methods used throughout the ORM. */
|
|
154
154
|
export class Utils {
|
|
155
155
|
static PK_SEPARATOR = '~~~';
|
|
156
|
-
static #ORM_VERSION = '7.2.6-dev.
|
|
156
|
+
static #ORM_VERSION = '7.2.6-dev.33';
|
|
157
157
|
/** Default session variable name backing an RLS filter argument (`current_setting('mikro.<filter>.<arg>')`). */
|
|
158
158
|
static getRlsSettingName(filterName, argName) {
|
|
159
159
|
return `mikro.${filterName}.${argName}`;
|
|
@@ -651,14 +651,19 @@ export class Utils {
|
|
|
651
651
|
static isCollection(item) {
|
|
652
652
|
return Collection.isCollection(item);
|
|
653
653
|
}
|
|
654
|
-
// FNV-1a 64-bit
|
|
654
|
+
// FNV-1a 64-bit, computed on two uint32 halves instead of a BigInt. The prime is 2^40 + 0x1b3, so
|
|
655
|
+
// `h * prime mod 2^64` is `h * 0x1b3` plus the low half shifted up by 40 bits. Every partial product
|
|
656
|
+
// stays below 2^41, so the double arithmetic is exact.
|
|
655
657
|
static hash(data, length) {
|
|
656
|
-
let
|
|
658
|
+
let hi = 0xcbf29ce4;
|
|
659
|
+
let lo = 0x84222325;
|
|
657
660
|
for (let i = 0; i < data.length; i++) {
|
|
658
|
-
|
|
659
|
-
|
|
661
|
+
lo = (lo ^ data.charCodeAt(i)) >>> 0;
|
|
662
|
+
const loProduct = lo * 0x1b3;
|
|
663
|
+
hi = (((hi * 0x1b3) >>> 0) + Math.floor(loProduct / 0x100000000) + ((lo << 8) >>> 0)) >>> 0;
|
|
664
|
+
lo = loProduct >>> 0;
|
|
660
665
|
}
|
|
661
|
-
const hash =
|
|
666
|
+
const hash = hi.toString(16).padStart(8, '0') + lo.toString(16).padStart(8, '0');
|
|
662
667
|
if (length) {
|
|
663
668
|
return hash.substring(0, length);
|
|
664
669
|
}
|
package/utils/clone.js
CHANGED
|
@@ -22,8 +22,8 @@ function getPropertyDescriptor(obj, prop) {
|
|
|
22
22
|
}
|
|
23
23
|
const TypedArray = Object.getPrototypeOf(Uint8Array);
|
|
24
24
|
export function clone(parent, respectCustomCloneMethod = true) {
|
|
25
|
-
|
|
26
|
-
const
|
|
25
|
+
// already cloned objects, so shared references and cycles map to a single clone
|
|
26
|
+
const clones = new Map();
|
|
27
27
|
function _clone(parent) {
|
|
28
28
|
// cloning null always returns null
|
|
29
29
|
if (parent === null) {
|
|
@@ -92,12 +92,10 @@ export function clone(parent, respectCustomCloneMethod = true) {
|
|
|
92
92
|
proto = Object.getPrototypeOf(parent);
|
|
93
93
|
child = Object.create(proto);
|
|
94
94
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return allChildren[index];
|
|
95
|
+
if (clones.has(parent)) {
|
|
96
|
+
return clones.get(parent);
|
|
98
97
|
}
|
|
99
|
-
|
|
100
|
-
allChildren.push(child);
|
|
98
|
+
clones.set(parent, child);
|
|
101
99
|
if (parent instanceof Map) {
|
|
102
100
|
parent.forEach((value, key) => {
|
|
103
101
|
const keyChild = _clone(key);
|