@mikro-orm/core 7.0.0-dev.13 → 7.0.0-dev.130
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 +333 -294
- 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 +40 -24
- package/drivers/IDatabaseDriver.d.ts +38 -17
- package/entity/BaseEntity.d.ts +2 -2
- 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 +35 -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 +575 -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 +22 -6
- package/enums.js +15 -1
- package/errors.d.ts +18 -9
- package/errors.js +44 -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 +22 -24
- package/metadata/EntitySchema.js +73 -51
- package/metadata/MetadataDiscovery.d.ts +6 -10
- package/metadata/MetadataDiscovery.js +292 -299
- 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 +11 -9
- package/metadata/MetadataValidator.js +50 -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 +157 -112
- package/typings.js +55 -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 +754 -207
- package/utils/Configuration.js +146 -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
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { basename, extname } from 'node:path';
|
|
2
|
-
import globby from 'globby';
|
|
3
1
|
import { EntityMetadata, } from '../typings.js';
|
|
4
2
|
import { Utils } from '../utils/Utils.js';
|
|
5
3
|
import { MetadataValidator } from './MetadataValidator.js';
|
|
@@ -7,16 +5,16 @@ import { MetadataStorage } from './MetadataStorage.js';
|
|
|
7
5
|
import { EntitySchema } from './EntitySchema.js';
|
|
8
6
|
import { Cascade, ReferenceKind } from '../enums.js';
|
|
9
7
|
import { MetadataError } from '../errors.js';
|
|
10
|
-
import {
|
|
8
|
+
import { t, Type } from '../types/index.js';
|
|
11
9
|
import { colors } from '../logging/colors.js';
|
|
12
|
-
import { raw,
|
|
10
|
+
import { raw, Raw } from '../utils/RawQueryFragment.js';
|
|
11
|
+
import { BaseEntity } from '../entity/BaseEntity.js';
|
|
13
12
|
export class MetadataDiscovery {
|
|
14
13
|
metadata;
|
|
15
14
|
platform;
|
|
16
15
|
config;
|
|
17
16
|
namingStrategy;
|
|
18
17
|
metadataProvider;
|
|
19
|
-
cache;
|
|
20
18
|
logger;
|
|
21
19
|
schemaHelper;
|
|
22
20
|
validator = new MetadataValidator();
|
|
@@ -27,11 +25,11 @@ export class MetadataDiscovery {
|
|
|
27
25
|
this.config = config;
|
|
28
26
|
this.namingStrategy = this.config.getNamingStrategy();
|
|
29
27
|
this.metadataProvider = this.config.getMetadataProvider();
|
|
30
|
-
this.cache = this.config.getMetadataCacheAdapter();
|
|
31
28
|
this.logger = this.config.getLogger();
|
|
32
29
|
this.schemaHelper = this.platform.getSchemaHelper();
|
|
33
30
|
}
|
|
34
31
|
async discover(preferTs = true) {
|
|
32
|
+
this.discovered.length = 0;
|
|
35
33
|
const startTime = Date.now();
|
|
36
34
|
this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)}`);
|
|
37
35
|
await this.findEntities(preferTs);
|
|
@@ -47,10 +45,12 @@ export class MetadataDiscovery {
|
|
|
47
45
|
await this.config.get('discovery').afterDiscovered?.(storage, this.platform);
|
|
48
46
|
return storage;
|
|
49
47
|
}
|
|
50
|
-
discoverSync(
|
|
48
|
+
discoverSync() {
|
|
49
|
+
this.discovered.length = 0;
|
|
51
50
|
const startTime = Date.now();
|
|
52
51
|
this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)} in sync mode`);
|
|
53
|
-
this.
|
|
52
|
+
const refs = this.config.get('entities');
|
|
53
|
+
this.discoverReferences(refs);
|
|
54
54
|
for (const meta of this.discovered) {
|
|
55
55
|
/* v8 ignore next */
|
|
56
56
|
void this.config.get('discovery').onMetadata?.(meta, this.platform);
|
|
@@ -70,15 +70,48 @@ export class MetadataDiscovery {
|
|
|
70
70
|
.sort((a, b) => b.root.name.localeCompare(a.root.name))
|
|
71
71
|
.forEach(meta => {
|
|
72
72
|
this.platform.validateMetadata(meta);
|
|
73
|
-
discovered.set(meta.
|
|
73
|
+
discovered.set(meta.class, meta);
|
|
74
74
|
});
|
|
75
|
+
for (const meta of discovered) {
|
|
76
|
+
meta.root = discovered.get(meta.root.class);
|
|
77
|
+
}
|
|
75
78
|
return discovered;
|
|
76
79
|
}
|
|
80
|
+
initAccessors(meta) {
|
|
81
|
+
for (const prop of Object.values(meta.properties)) {
|
|
82
|
+
if (!prop.accessor || meta.properties[prop.accessor]) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
86
|
+
if (desc?.get || desc?.set) {
|
|
87
|
+
this.initFieldName(prop);
|
|
88
|
+
const accessor = prop.name;
|
|
89
|
+
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
90
|
+
if (prop.accessor === true) {
|
|
91
|
+
prop.getter = prop.setter = true;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
prop.getter = prop.setter = false;
|
|
95
|
+
}
|
|
96
|
+
prop.accessor = accessor;
|
|
97
|
+
prop.serializedName ??= accessor;
|
|
98
|
+
Utils.renameKey(meta.properties, accessor, prop.name);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const name = prop.name;
|
|
102
|
+
prop.name = prop.accessor;
|
|
103
|
+
this.initFieldName(prop);
|
|
104
|
+
prop.serializedName ??= prop.accessor;
|
|
105
|
+
prop.name = name;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
77
109
|
processDiscoveredEntities(discovered) {
|
|
78
110
|
for (const meta of discovered) {
|
|
79
111
|
let i = 1;
|
|
80
112
|
Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
|
|
81
113
|
Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
|
|
114
|
+
this.initAccessors(meta);
|
|
82
115
|
}
|
|
83
116
|
// ignore base entities (not annotated with @Entity)
|
|
84
117
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
@@ -86,66 +119,61 @@ export class MetadataDiscovery {
|
|
|
86
119
|
filtered.sort((a, b) => !a.embeddable === !b.embeddable ? 0 : (a.embeddable ? 1 : -1));
|
|
87
120
|
filtered.forEach(meta => this.initSingleTableInheritance(meta, filtered));
|
|
88
121
|
filtered.forEach(meta => this.defineBaseEntityProperties(meta));
|
|
89
|
-
filtered.forEach(meta =>
|
|
122
|
+
filtered.forEach(meta => {
|
|
123
|
+
const newMeta = EntitySchema.fromMetadata(meta).init().meta;
|
|
124
|
+
return this.metadata.set(newMeta.class, newMeta);
|
|
125
|
+
});
|
|
90
126
|
filtered.forEach(meta => this.initAutoincrement(meta));
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
127
|
+
const forEachProp = (cb) => {
|
|
128
|
+
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => cb(meta, prop)));
|
|
129
|
+
};
|
|
130
|
+
forEachProp((m, p) => this.initFactoryField(m, p));
|
|
131
|
+
forEachProp((_m, p) => this.initRelation(p));
|
|
132
|
+
forEachProp((m, p) => this.initEmbeddables(m, p));
|
|
133
|
+
forEachProp((_m, p) => this.initFieldName(p));
|
|
134
|
+
forEachProp((m, p) => this.initVersionProperty(m, p));
|
|
135
|
+
forEachProp((m, p) => this.initCustomType(m, p));
|
|
136
|
+
forEachProp((m, p) => this.initGeneratedColumn(m, p));
|
|
97
137
|
filtered.forEach(meta => this.initAutoincrement(meta)); // once again after we init custom types
|
|
98
138
|
filtered.forEach(meta => this.initCheckConstraints(meta));
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
prop.trackChanges = false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => this.initIndexes(meta, prop)));
|
|
139
|
+
forEachProp((_m, p) => {
|
|
140
|
+
this.initDefaultValue(p);
|
|
141
|
+
this.inferTypeFromDefault(p);
|
|
142
|
+
this.initRelation(p);
|
|
143
|
+
this.initColumnType(p);
|
|
144
|
+
});
|
|
145
|
+
forEachProp((m, p) => this.initIndexes(m, p));
|
|
111
146
|
filtered.forEach(meta => this.autoWireBidirectionalProperties(meta));
|
|
112
|
-
filtered.forEach(meta => this.findReferencingProperties(meta, filtered));
|
|
113
147
|
for (const meta of filtered) {
|
|
114
148
|
discovered.push(...this.processEntity(meta));
|
|
115
149
|
}
|
|
116
150
|
discovered.forEach(meta => meta.sync(true));
|
|
117
|
-
|
|
118
|
-
// override the path in the options, so we can log it from the CLI in `cache:generate` command
|
|
119
|
-
if (combinedCachePath) {
|
|
120
|
-
this.config.get('metadataCache').combined = combinedCachePath;
|
|
121
|
-
}
|
|
151
|
+
this.metadataProvider.combineCache();
|
|
122
152
|
return discovered.map(meta => {
|
|
123
|
-
meta = this.metadata.get(meta.
|
|
153
|
+
meta = this.metadata.get(meta.class);
|
|
124
154
|
meta.sync(true);
|
|
155
|
+
this.findReferencingProperties(meta, filtered);
|
|
125
156
|
return meta;
|
|
126
157
|
});
|
|
127
158
|
}
|
|
128
|
-
findEntities(preferTs
|
|
129
|
-
this.
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const paths =
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
159
|
+
async findEntities(preferTs) {
|
|
160
|
+
const { entities, entitiesTs, baseDir } = this.config.getAll();
|
|
161
|
+
const targets = (preferTs && entitiesTs.length > 0) ? entitiesTs : entities;
|
|
162
|
+
const processed = [];
|
|
163
|
+
const paths = [];
|
|
164
|
+
for (const entity of targets) {
|
|
165
|
+
if (typeof entity === 'string') {
|
|
166
|
+
paths.push(entity);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
processed.push(entity);
|
|
137
170
|
}
|
|
138
|
-
return this.discoverDirectories(paths).then(() => {
|
|
139
|
-
this.discoverReferences(refs);
|
|
140
|
-
this.discoverMissingTargets();
|
|
141
|
-
this.validator.validateDiscovered(this.discovered, options);
|
|
142
|
-
return this.discovered;
|
|
143
|
-
});
|
|
144
171
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
if (paths.length > 0) {
|
|
173
|
+
const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
|
|
174
|
+
processed.push(...await discoverEntities(paths, { baseDir }));
|
|
175
|
+
}
|
|
176
|
+
return this.discoverReferences(processed);
|
|
149
177
|
}
|
|
150
178
|
discoverMissingTargets() {
|
|
151
179
|
const unwrap = (type) => type
|
|
@@ -154,17 +182,22 @@ export class MetadataDiscovery {
|
|
|
154
182
|
.replace(/\((.*)\)/, '$1'); // unwrap union types
|
|
155
183
|
const missing = [];
|
|
156
184
|
this.discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
|
|
157
|
-
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
185
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity) {
|
|
186
|
+
const pivotEntity = prop.pivotEntity;
|
|
187
|
+
const target = typeof pivotEntity === 'function' && !pivotEntity.prototype
|
|
188
|
+
? pivotEntity()
|
|
189
|
+
: pivotEntity;
|
|
190
|
+
if (!this.discovered.find(m => m.className === Utils.className(target))) {
|
|
191
|
+
missing.push(target);
|
|
192
|
+
}
|
|
162
193
|
}
|
|
163
|
-
if (prop.kind !== ReferenceKind.SCALAR
|
|
164
|
-
const target = typeof prop.entity === 'function'
|
|
194
|
+
if (prop.kind !== ReferenceKind.SCALAR) {
|
|
195
|
+
const target = typeof prop.entity === 'function' && !prop.entity.prototype
|
|
165
196
|
? prop.entity()
|
|
166
197
|
: prop.type;
|
|
167
|
-
|
|
198
|
+
if (!unwrap(prop.type).split(/ ?\| ?/).every(type => this.discovered.find(m => m.className === type))) {
|
|
199
|
+
missing.push(...Utils.asArray(target));
|
|
200
|
+
}
|
|
168
201
|
}
|
|
169
202
|
}));
|
|
170
203
|
if (missing.length > 0) {
|
|
@@ -173,122 +206,99 @@ export class MetadataDiscovery {
|
|
|
173
206
|
}
|
|
174
207
|
tryDiscoverTargets(targets) {
|
|
175
208
|
for (const target of targets) {
|
|
176
|
-
|
|
177
|
-
|
|
209
|
+
const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
|
|
210
|
+
if (isDiscoverable && target.name && !this.metadata.has(target)) {
|
|
211
|
+
this.discoverReferences([target], false);
|
|
178
212
|
this.discoverMissingTargets();
|
|
179
213
|
}
|
|
180
214
|
}
|
|
181
215
|
}
|
|
182
|
-
|
|
183
|
-
paths = paths.map(path => Utils.normalizePath(path));
|
|
184
|
-
const files = await globby(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
|
|
185
|
-
this.logger.log('discovery', `- processing ${colors.cyan('' + files.length)} files`);
|
|
186
|
-
const found = [];
|
|
187
|
-
for (const filepath of files) {
|
|
188
|
-
const filename = basename(filepath);
|
|
189
|
-
if (!filename.match(/\.[cm]?[jt]s$/) ||
|
|
190
|
-
filename.endsWith('.js.map') ||
|
|
191
|
-
filename.match(/\.d\.[cm]?ts/) ||
|
|
192
|
-
filename.startsWith('.') ||
|
|
193
|
-
filename.match(/index\.[cm]?[jt]s$/)) {
|
|
194
|
-
this.logger.log('discovery', `- ignoring file ${filename}`);
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const name = this.namingStrategy.getClassName(filename);
|
|
198
|
-
const path = Utils.normalizePath(this.config.get('baseDir'), filepath);
|
|
199
|
-
const targets = await this.getEntityClassOrSchema(path, name);
|
|
200
|
-
for (const target of targets) {
|
|
201
|
-
if (!(target instanceof Function) && !(target instanceof EntitySchema)) {
|
|
202
|
-
this.logger.log('discovery', `- ignoring file ${filename}`);
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
const entity = this.prepare(target);
|
|
206
|
-
const schema = this.getSchema(entity, path);
|
|
207
|
-
const meta = schema.init().meta;
|
|
208
|
-
this.metadata.set(meta.className, meta);
|
|
209
|
-
found.push([schema, path]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
for (const [schema, path] of found) {
|
|
213
|
-
this.discoverEntity(schema, path);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
discoverReferences(refs) {
|
|
216
|
+
discoverReferences(refs, validate = true) {
|
|
217
217
|
const found = [];
|
|
218
218
|
for (const entity of refs) {
|
|
219
|
-
|
|
219
|
+
if (typeof entity === 'string') {
|
|
220
|
+
throw new Error('Folder based discovery requires the async `MikroORM.init()` method.');
|
|
221
|
+
}
|
|
222
|
+
const schema = this.getSchema(entity);
|
|
220
223
|
const meta = schema.init().meta;
|
|
221
|
-
this.metadata.set(meta.
|
|
224
|
+
this.metadata.set(meta.class, meta);
|
|
222
225
|
found.push(schema);
|
|
223
226
|
}
|
|
224
227
|
// discover parents (base entities) automatically
|
|
225
228
|
for (const meta of this.metadata) {
|
|
226
229
|
let parent = meta.extends;
|
|
227
|
-
if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.
|
|
228
|
-
this.discoverReferences([parent]);
|
|
230
|
+
if (parent instanceof EntitySchema && !this.metadata.has(parent.init().meta.class)) {
|
|
231
|
+
this.discoverReferences([parent], false);
|
|
229
232
|
}
|
|
230
|
-
|
|
233
|
+
if (typeof parent === 'function' && parent.name && !this.metadata.has(parent)) {
|
|
234
|
+
this.discoverReferences([parent], false);
|
|
235
|
+
}
|
|
236
|
+
/* v8 ignore next */
|
|
231
237
|
if (!meta.class) {
|
|
232
238
|
continue;
|
|
233
239
|
}
|
|
234
240
|
parent = Object.getPrototypeOf(meta.class);
|
|
235
|
-
if (parent.name !== '' && !this.metadata.has(parent
|
|
236
|
-
this.discoverReferences([parent]);
|
|
241
|
+
if (parent.name !== '' && !this.metadata.has(parent) && parent !== BaseEntity) {
|
|
242
|
+
this.discoverReferences([parent], false);
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
245
|
for (const schema of found) {
|
|
240
246
|
this.discoverEntity(schema);
|
|
241
247
|
}
|
|
248
|
+
this.discoverMissingTargets();
|
|
249
|
+
if (validate) {
|
|
250
|
+
this.validator.validateDiscovered(this.discovered, this.config.get('discovery'));
|
|
251
|
+
}
|
|
242
252
|
return this.discovered.filter(meta => found.find(m => m.name === meta.className));
|
|
243
253
|
}
|
|
244
|
-
reset(
|
|
245
|
-
const exists = this.discovered.findIndex(m => m.className === className);
|
|
254
|
+
reset(entityName) {
|
|
255
|
+
const exists = this.discovered.findIndex(m => m.class === entityName || m.className === Utils.className(entityName));
|
|
246
256
|
if (exists !== -1) {
|
|
247
|
-
this.metadata.reset(this.discovered[exists].
|
|
257
|
+
this.metadata.reset(this.discovered[exists].class);
|
|
248
258
|
this.discovered.splice(exists, 1);
|
|
249
259
|
}
|
|
250
260
|
}
|
|
251
|
-
|
|
252
|
-
/* v8 ignore next 3 */
|
|
253
|
-
if ('schema' in entity && entity.schema instanceof EntitySchema) {
|
|
254
|
-
return entity.schema;
|
|
255
|
-
}
|
|
261
|
+
getSchema(entity) {
|
|
256
262
|
if (EntitySchema.REGISTRY.has(entity)) {
|
|
257
|
-
|
|
263
|
+
entity = EntitySchema.REGISTRY.get(entity);
|
|
258
264
|
}
|
|
259
|
-
return entity;
|
|
260
|
-
}
|
|
261
|
-
getSchema(entity, filepath) {
|
|
262
265
|
if (entity instanceof EntitySchema) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
MetadataStorage.getMetadata(entity.meta.className, filepath);
|
|
266
|
-
}
|
|
267
|
-
return entity;
|
|
266
|
+
const meta = Utils.copy(entity.meta, false);
|
|
267
|
+
return EntitySchema.fromMetadata(meta);
|
|
268
268
|
}
|
|
269
269
|
const path = entity[MetadataStorage.PATH_SYMBOL];
|
|
270
270
|
if (path) {
|
|
271
271
|
const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
|
|
272
|
-
meta.path =
|
|
273
|
-
this.metadata.set(entity
|
|
272
|
+
meta.path = path;
|
|
273
|
+
this.metadata.set(entity, meta);
|
|
274
274
|
}
|
|
275
|
-
const exists = this.metadata.has(entity
|
|
276
|
-
const meta = this.metadata.get(entity
|
|
275
|
+
const exists = this.metadata.has(entity);
|
|
276
|
+
const meta = this.metadata.get(entity, true);
|
|
277
277
|
meta.abstract ??= !(exists && meta.name);
|
|
278
278
|
const schema = EntitySchema.fromMetadata(meta);
|
|
279
279
|
schema.setClass(entity);
|
|
280
280
|
return schema;
|
|
281
281
|
}
|
|
282
|
-
|
|
283
|
-
|
|
282
|
+
getRootEntity(meta) {
|
|
283
|
+
const base = meta.extends && this.metadata.find(meta.extends);
|
|
284
|
+
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
285
|
+
return meta;
|
|
286
|
+
}
|
|
287
|
+
const root = this.getRootEntity(base);
|
|
288
|
+
if (root.discriminatorColumn) {
|
|
289
|
+
return root;
|
|
290
|
+
}
|
|
291
|
+
return meta;
|
|
292
|
+
}
|
|
293
|
+
discoverEntity(schema) {
|
|
284
294
|
const meta = schema.meta;
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
const
|
|
295
|
+
const path = meta.path;
|
|
296
|
+
this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
297
|
+
const root = this.getRootEntity(meta);
|
|
298
|
+
schema.meta.path = meta.path;
|
|
299
|
+
const cache = this.metadataProvider.getCachedMetadata(meta, root);
|
|
288
300
|
if (cache) {
|
|
289
301
|
this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
290
|
-
this.metadataProvider.loadFromCache(meta, cache);
|
|
291
|
-
meta.root = root;
|
|
292
302
|
this.discovered.push(meta);
|
|
293
303
|
return;
|
|
294
304
|
}
|
|
@@ -297,50 +307,18 @@ export class MetadataDiscovery {
|
|
|
297
307
|
this.inferDefaultValue(meta, prop);
|
|
298
308
|
}
|
|
299
309
|
// if the definition is using EntitySchema we still want it to go through the metadata provider to validate no types are missing
|
|
300
|
-
this.metadataProvider.loadEntityMetadata(meta
|
|
301
|
-
if (!meta.
|
|
310
|
+
this.metadataProvider.loadEntityMetadata(meta);
|
|
311
|
+
if (!meta.tableName && meta.name) {
|
|
302
312
|
const entityName = root.discriminatorColumn ? root.name : meta.name;
|
|
303
|
-
meta.
|
|
313
|
+
meta.tableName = this.namingStrategy.classToTableName(entityName);
|
|
304
314
|
}
|
|
305
|
-
|
|
306
|
-
this.saveToCache(meta);
|
|
315
|
+
this.metadataProvider.saveToCache(meta);
|
|
307
316
|
meta.root = root;
|
|
308
317
|
this.discovered.push(meta);
|
|
309
318
|
}
|
|
310
|
-
saveToCache(meta) {
|
|
311
|
-
if (!this.metadataProvider.useCache()) {
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
const copy = Utils.copy(meta, false);
|
|
315
|
-
for (const prop of copy.props) {
|
|
316
|
-
if (Type.isMappedType(prop.type)) {
|
|
317
|
-
Reflect.deleteProperty(prop, 'type');
|
|
318
|
-
Reflect.deleteProperty(prop, 'customType');
|
|
319
|
-
}
|
|
320
|
-
if (prop.default) {
|
|
321
|
-
const raw = RawQueryFragment.getKnownFragment(prop.default);
|
|
322
|
-
if (raw) {
|
|
323
|
-
prop.defaultRaw ??= this.platform.formatQuery(raw.sql, raw.params);
|
|
324
|
-
Reflect.deleteProperty(prop, 'default');
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
Reflect.deleteProperty(prop, 'targetMeta');
|
|
328
|
-
}
|
|
329
|
-
[
|
|
330
|
-
'prototype', 'props', 'referencingProperties', 'propertyOrder', 'relations',
|
|
331
|
-
'concurrencyCheckKeys', 'checks',
|
|
332
|
-
].forEach(key => delete copy[key]);
|
|
333
|
-
// base entity without properties might not have path, but nothing to cache there
|
|
334
|
-
if (meta.path) {
|
|
335
|
-
this.cache.set(meta.className + extname(meta.path), copy, meta.path);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
319
|
initNullability(prop) {
|
|
339
|
-
if (prop.kind === ReferenceKind.MANY_TO_ONE) {
|
|
340
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
|
|
341
|
-
}
|
|
342
320
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
343
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner
|
|
321
|
+
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
|
|
344
322
|
}
|
|
345
323
|
return Utils.defaultValue(prop, 'nullable', prop.optional);
|
|
346
324
|
}
|
|
@@ -375,7 +353,7 @@ export class MetadataDiscovery {
|
|
|
375
353
|
if (prop.joinColumns.length !== prop.columnTypes.length) {
|
|
376
354
|
prop.columnTypes = prop.joinColumns.flatMap(field => {
|
|
377
355
|
const matched = meta.props.find(p => p.fieldNames?.includes(field));
|
|
378
|
-
/* v8 ignore next
|
|
356
|
+
/* v8 ignore next */
|
|
379
357
|
if (!matched) {
|
|
380
358
|
throw MetadataError.fromWrongForeignKey(meta, prop, 'columnTypes');
|
|
381
359
|
}
|
|
@@ -402,7 +380,7 @@ export class MetadataDiscovery {
|
|
|
402
380
|
}
|
|
403
381
|
}
|
|
404
382
|
initManyToOneFieldName(prop, name) {
|
|
405
|
-
const meta2 =
|
|
383
|
+
const meta2 = prop.targetMeta;
|
|
406
384
|
const ret = [];
|
|
407
385
|
for (const primaryKey of meta2.primaryKeys) {
|
|
408
386
|
this.initFieldName(meta2.properties[primaryKey]);
|
|
@@ -413,11 +391,11 @@ export class MetadataDiscovery {
|
|
|
413
391
|
return ret;
|
|
414
392
|
}
|
|
415
393
|
initManyToManyFieldName(prop, name) {
|
|
416
|
-
const meta2 =
|
|
394
|
+
const meta2 = prop.targetMeta;
|
|
417
395
|
return meta2.primaryKeys.map(() => this.namingStrategy.propertyToColumnName(name));
|
|
418
396
|
}
|
|
419
397
|
initManyToManyFields(meta, prop) {
|
|
420
|
-
const meta2 =
|
|
398
|
+
const meta2 = prop.targetMeta;
|
|
421
399
|
Utils.defaultValue(prop, 'fixedOrder', !!prop.fixedOrderColumn);
|
|
422
400
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
423
401
|
const props = Object.values(pivotMeta?.properties ?? {});
|
|
@@ -438,26 +416,26 @@ export class MetadataDiscovery {
|
|
|
438
416
|
prop.inverseJoinColumns ??= second.fieldNames;
|
|
439
417
|
}
|
|
440
418
|
if (!prop.pivotTable && prop.owner && this.platform.usesPivotTable()) {
|
|
441
|
-
prop.pivotTable = this.namingStrategy.joinTableName(meta.
|
|
419
|
+
prop.pivotTable = this.namingStrategy.joinTableName(meta.className, meta2.tableName, prop.name, meta.tableName);
|
|
442
420
|
}
|
|
443
421
|
if (prop.mappedBy) {
|
|
444
422
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
445
423
|
this.initManyToManyFields(meta2, prop2);
|
|
446
424
|
prop.pivotTable = prop2.pivotTable;
|
|
447
|
-
prop.pivotEntity = prop2.pivotEntity
|
|
425
|
+
prop.pivotEntity = prop2.pivotEntity;
|
|
448
426
|
prop.fixedOrder = prop2.fixedOrder;
|
|
449
427
|
prop.fixedOrderColumn = prop2.fixedOrderColumn;
|
|
450
428
|
prop.joinColumns = prop2.inverseJoinColumns;
|
|
451
429
|
prop.inverseJoinColumns = prop2.joinColumns;
|
|
452
430
|
}
|
|
453
431
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
454
|
-
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK));
|
|
432
|
+
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK, meta.root.tableName));
|
|
455
433
|
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, meta2.root.className);
|
|
456
434
|
}
|
|
457
435
|
initManyToOneFields(prop) {
|
|
458
|
-
const meta2 =
|
|
436
|
+
const meta2 = prop.targetMeta;
|
|
459
437
|
const fieldNames = Utils.flatten(meta2.primaryKeys.map(primaryKey => meta2.properties[primaryKey].fieldNames));
|
|
460
|
-
Utils.defaultValue(prop, 'referencedTableName', meta2.
|
|
438
|
+
Utils.defaultValue(prop, 'referencedTableName', meta2.tableName);
|
|
461
439
|
if (!prop.joinColumns) {
|
|
462
440
|
prop.joinColumns = fieldNames.map(fieldName => this.namingStrategy.joinKeyColumnName(prop.name, fieldName, fieldNames.length > 1));
|
|
463
441
|
}
|
|
@@ -466,7 +444,7 @@ export class MetadataDiscovery {
|
|
|
466
444
|
}
|
|
467
445
|
}
|
|
468
446
|
initOneToManyFields(prop) {
|
|
469
|
-
const meta2 =
|
|
447
|
+
const meta2 = prop.targetMeta;
|
|
470
448
|
if (!prop.joinColumns) {
|
|
471
449
|
prop.joinColumns = [this.namingStrategy.joinColumnName(prop.name)];
|
|
472
450
|
}
|
|
@@ -484,7 +462,7 @@ export class MetadataDiscovery {
|
|
|
484
462
|
pks[0].deleteRule ??= 'cascade';
|
|
485
463
|
}
|
|
486
464
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
487
|
-
this.validator.validateEntityDefinition(this.metadata, meta.
|
|
465
|
+
this.validator.validateEntityDefinition(this.metadata, meta.class, this.config.get('discovery'));
|
|
488
466
|
for (const prop of Object.values(meta.properties)) {
|
|
489
467
|
this.initNullability(prop);
|
|
490
468
|
this.applyNamingStrategy(meta, prop);
|
|
@@ -497,15 +475,21 @@ export class MetadataDiscovery {
|
|
|
497
475
|
}
|
|
498
476
|
this.initOwnColumns(meta);
|
|
499
477
|
meta.simplePK = pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
500
|
-
meta.serializedPrimaryKey
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
serializedPKProp.persist = false;
|
|
478
|
+
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
479
|
+
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
480
|
+
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
504
481
|
}
|
|
505
482
|
if (this.platform.usesPivotTable()) {
|
|
506
483
|
return Object.values(meta.properties)
|
|
507
484
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
508
|
-
.map(prop =>
|
|
485
|
+
.map(prop => {
|
|
486
|
+
const pivotMeta = this.definePivotTableEntity(meta, prop);
|
|
487
|
+
prop.pivotEntity = pivotMeta.class;
|
|
488
|
+
if (prop.inversedBy) {
|
|
489
|
+
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
490
|
+
}
|
|
491
|
+
return pivotMeta;
|
|
492
|
+
});
|
|
509
493
|
}
|
|
510
494
|
return [];
|
|
511
495
|
}
|
|
@@ -522,8 +506,11 @@ export class MetadataDiscovery {
|
|
|
522
506
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
523
507
|
const value = prop[type];
|
|
524
508
|
if (value instanceof Function) {
|
|
525
|
-
const meta2 = this.metadata.get(prop.
|
|
509
|
+
const meta2 = prop.targetMeta ?? this.metadata.get(prop.target);
|
|
526
510
|
prop[type] = value(meta2.properties)?.name;
|
|
511
|
+
if (type === 'pivotEntity' && value) {
|
|
512
|
+
prop[type] = value(meta2.properties);
|
|
513
|
+
}
|
|
527
514
|
if (prop[type] == null) {
|
|
528
515
|
throw MetadataError.fromWrongReference(meta, prop, type);
|
|
529
516
|
}
|
|
@@ -539,9 +526,9 @@ export class MetadataDiscovery {
|
|
|
539
526
|
}
|
|
540
527
|
else if (fks.length >= 2) {
|
|
541
528
|
[first, second] = fks;
|
|
542
|
-
/* v8 ignore next 3 */
|
|
543
529
|
}
|
|
544
530
|
else {
|
|
531
|
+
/* v8 ignore next */
|
|
545
532
|
return [];
|
|
546
533
|
}
|
|
547
534
|
// wrong FK order, first FK needs to point to the owning side
|
|
@@ -555,7 +542,9 @@ export class MetadataDiscovery {
|
|
|
555
542
|
return [first, second];
|
|
556
543
|
}
|
|
557
544
|
definePivotTableEntity(meta, prop) {
|
|
558
|
-
const pivotMeta =
|
|
545
|
+
const pivotMeta = prop.pivotEntity
|
|
546
|
+
? this.metadata.find(prop.pivotEntity)
|
|
547
|
+
: this.metadata.getByClassName(prop.pivotTable, false);
|
|
559
548
|
// ensure inverse side exists so we can join it when populating via pivot tables
|
|
560
549
|
if (!prop.inversedBy && prop.targetMeta) {
|
|
561
550
|
const inverseName = `${meta.className}_${prop.name}__inverse`;
|
|
@@ -564,6 +553,8 @@ export class MetadataDiscovery {
|
|
|
564
553
|
name: inverseName,
|
|
565
554
|
kind: ReferenceKind.MANY_TO_MANY,
|
|
566
555
|
type: meta.className,
|
|
556
|
+
target: meta.class,
|
|
557
|
+
targetMeta: meta,
|
|
567
558
|
mappedBy: prop.name,
|
|
568
559
|
pivotEntity: prop.pivotEntity,
|
|
569
560
|
pivotTable: prop.pivotTable,
|
|
@@ -572,55 +563,51 @@ export class MetadataDiscovery {
|
|
|
572
563
|
};
|
|
573
564
|
this.applyNamingStrategy(prop.targetMeta, inverseProp);
|
|
574
565
|
this.initCustomType(prop.targetMeta, inverseProp);
|
|
575
|
-
this.initRelation(inverseProp);
|
|
576
566
|
prop.targetMeta.properties[inverseName] = inverseProp;
|
|
577
567
|
}
|
|
578
568
|
if (pivotMeta) {
|
|
569
|
+
prop.pivotEntity = pivotMeta.class;
|
|
579
570
|
this.ensureCorrectFKOrderInPivotEntity(pivotMeta, prop);
|
|
580
571
|
return pivotMeta;
|
|
581
572
|
}
|
|
582
|
-
const exists = this.metadata.find(prop.pivotTable);
|
|
583
|
-
if (exists) {
|
|
584
|
-
prop.pivotEntity = exists.className;
|
|
585
|
-
return exists;
|
|
586
|
-
}
|
|
587
573
|
let tableName = prop.pivotTable;
|
|
588
574
|
let schemaName;
|
|
589
575
|
if (prop.pivotTable.includes('.')) {
|
|
590
576
|
[schemaName, tableName] = prop.pivotTable.split('.');
|
|
591
577
|
}
|
|
592
578
|
schemaName ??= meta.schema;
|
|
593
|
-
const
|
|
594
|
-
const
|
|
579
|
+
const targetMeta = prop.targetMeta;
|
|
580
|
+
const targetType = targetMeta.className;
|
|
581
|
+
const pivotMeta2 = new EntityMetadata({
|
|
595
582
|
name: prop.pivotTable,
|
|
596
583
|
className: prop.pivotTable,
|
|
597
584
|
collection: tableName,
|
|
598
585
|
schema: schemaName,
|
|
599
586
|
pivotTable: true,
|
|
600
587
|
});
|
|
601
|
-
prop.pivotEntity =
|
|
588
|
+
prop.pivotEntity = pivotMeta2.class;
|
|
602
589
|
if (prop.fixedOrder) {
|
|
603
|
-
const primaryProp = this.defineFixedOrderProperty(prop,
|
|
604
|
-
|
|
590
|
+
const primaryProp = this.defineFixedOrderProperty(prop, targetMeta);
|
|
591
|
+
pivotMeta2.properties[primaryProp.name] = primaryProp;
|
|
605
592
|
}
|
|
606
593
|
else {
|
|
607
|
-
|
|
594
|
+
pivotMeta2.compositePK = true;
|
|
608
595
|
}
|
|
609
596
|
// handle self-referenced m:n with same default field names
|
|
610
597
|
if (meta.className === targetType && prop.joinColumns.every((joinColumn, idx) => joinColumn === prop.inverseJoinColumns[idx])) {
|
|
611
|
-
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.
|
|
612
|
-
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.
|
|
598
|
+
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_1', name, meta.compositePK));
|
|
599
|
+
prop.inverseJoinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.tableName + '_2', name, meta.compositePK));
|
|
613
600
|
if (prop.inversedBy) {
|
|
614
|
-
const prop2 =
|
|
601
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
615
602
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
616
603
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
617
604
|
}
|
|
618
605
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
return this.metadata.set(
|
|
606
|
+
pivotMeta2.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, targetType + '_inverse', true, meta.className === targetType);
|
|
607
|
+
pivotMeta2.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetMeta.class, meta.name + '_owner', false, meta.className === targetType);
|
|
608
|
+
return this.metadata.set(pivotMeta2.class, EntitySchema.fromMetadata(pivotMeta2).init().meta);
|
|
622
609
|
}
|
|
623
|
-
defineFixedOrderProperty(prop,
|
|
610
|
+
defineFixedOrderProperty(prop, targetMeta) {
|
|
624
611
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
625
612
|
const primaryProp = {
|
|
626
613
|
name: pk,
|
|
@@ -634,7 +621,7 @@ export class MetadataDiscovery {
|
|
|
634
621
|
this.initColumnType(primaryProp);
|
|
635
622
|
prop.fixedOrderColumn = pk;
|
|
636
623
|
if (prop.inversedBy) {
|
|
637
|
-
const prop2 =
|
|
624
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
638
625
|
prop2.fixedOrder = true;
|
|
639
626
|
prop2.fixedOrderColumn = pk;
|
|
640
627
|
}
|
|
@@ -643,7 +630,8 @@ export class MetadataDiscovery {
|
|
|
643
630
|
definePivotProperty(prop, name, type, inverse, owner, selfReferencing) {
|
|
644
631
|
const ret = {
|
|
645
632
|
name,
|
|
646
|
-
type,
|
|
633
|
+
type: Utils.className(type),
|
|
634
|
+
target: type,
|
|
647
635
|
kind: ReferenceKind.MANY_TO_ONE,
|
|
648
636
|
cascade: [Cascade.ALL],
|
|
649
637
|
fixedOrder: prop.fixedOrder,
|
|
@@ -653,6 +641,7 @@ export class MetadataDiscovery {
|
|
|
653
641
|
autoincrement: false,
|
|
654
642
|
updateRule: prop.updateRule,
|
|
655
643
|
deleteRule: prop.deleteRule,
|
|
644
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
656
645
|
};
|
|
657
646
|
if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
|
|
658
647
|
ret.updateRule ??= 'no action';
|
|
@@ -700,7 +689,7 @@ export class MetadataDiscovery {
|
|
|
700
689
|
Object.values(meta.properties)
|
|
701
690
|
.filter(prop => prop.kind !== ReferenceKind.SCALAR && !prop.owner && prop.mappedBy)
|
|
702
691
|
.forEach(prop => {
|
|
703
|
-
const meta2 =
|
|
692
|
+
const meta2 = prop.targetMeta;
|
|
704
693
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
705
694
|
if (prop2 && !prop2.inversedBy) {
|
|
706
695
|
prop2.inversedBy = prop.name;
|
|
@@ -708,7 +697,7 @@ export class MetadataDiscovery {
|
|
|
708
697
|
});
|
|
709
698
|
}
|
|
710
699
|
defineBaseEntityProperties(meta) {
|
|
711
|
-
const base = meta.extends && this.metadata.get(
|
|
700
|
+
const base = meta.extends && this.metadata.get(meta.extends);
|
|
712
701
|
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
713
702
|
return 0;
|
|
714
703
|
}
|
|
@@ -739,12 +728,9 @@ export class MetadataDiscovery {
|
|
|
739
728
|
Utils.keys(base.hooks).forEach(type => {
|
|
740
729
|
meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
|
|
741
730
|
});
|
|
742
|
-
if (meta.constructorParams
|
|
731
|
+
if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
|
|
743
732
|
meta.constructorParams = [...base.constructorParams];
|
|
744
733
|
}
|
|
745
|
-
if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
|
|
746
|
-
meta.toJsonParams = [...base.toJsonParams];
|
|
747
|
-
}
|
|
748
734
|
return order;
|
|
749
735
|
}
|
|
750
736
|
initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
|
|
@@ -820,22 +806,30 @@ export class MetadataDiscovery {
|
|
|
820
806
|
}
|
|
821
807
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
822
808
|
};
|
|
809
|
+
const isParentArray = (prop) => {
|
|
810
|
+
if (prop.array) {
|
|
811
|
+
return true;
|
|
812
|
+
}
|
|
813
|
+
return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
|
|
814
|
+
};
|
|
823
815
|
const rootProperty = getRootProperty(embeddedProp);
|
|
824
816
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
825
817
|
const object = isParentObject(embeddedProp);
|
|
818
|
+
const array = isParentArray(embeddedProp);
|
|
826
819
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
827
820
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
828
821
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
829
822
|
const glue = object ? '~' : '_';
|
|
830
823
|
for (const prop of Object.values(embeddable.properties)) {
|
|
831
824
|
const name = (embeddedProp.embeddedPath?.join(glue) ?? embeddedProp.fieldNames[0] + glue) + prop.name;
|
|
832
|
-
meta.properties[name] = Utils.copy(prop
|
|
825
|
+
meta.properties[name] = Utils.copy(prop);
|
|
833
826
|
meta.properties[name].name = name;
|
|
834
827
|
meta.properties[name].embedded = [embeddedProp.name, prop.name];
|
|
835
828
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
836
829
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
837
830
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
838
|
-
|
|
831
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
832
|
+
if (embeddedProp.nullable || refInArray) {
|
|
839
833
|
meta.properties[name].nullable = true;
|
|
840
834
|
}
|
|
841
835
|
if (meta.properties[name].fieldNames) {
|
|
@@ -865,14 +859,17 @@ export class MetadataDiscovery {
|
|
|
865
859
|
path = [embeddedProp.fieldNames[0]];
|
|
866
860
|
}
|
|
867
861
|
this.initFieldName(prop, true);
|
|
862
|
+
this.initRelation(prop);
|
|
868
863
|
path.push(prop.fieldNames[0]);
|
|
869
864
|
meta.properties[name].fieldNames = prop.fieldNames;
|
|
870
865
|
meta.properties[name].embeddedPath = path;
|
|
871
|
-
const
|
|
866
|
+
const targetProp = prop.targetMeta?.getPrimaryProp() ?? prop;
|
|
867
|
+
const fieldName = raw(this.platform.getSearchJsonPropertySQL(path.join('->'), targetProp.runtimeType ?? targetProp.type, true));
|
|
872
868
|
meta.properties[name].fieldNameRaw = fieldName.sql; // for querying in SQL drivers
|
|
873
869
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
874
870
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
875
871
|
meta.properties[name].object = true;
|
|
872
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
876
873
|
}
|
|
877
874
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
878
875
|
}
|
|
@@ -895,7 +892,7 @@ export class MetadataDiscovery {
|
|
|
895
892
|
}
|
|
896
893
|
initSingleTableInheritance(meta, metadata) {
|
|
897
894
|
if (meta.root !== meta && !meta.__processed) {
|
|
898
|
-
meta.root = metadata.find(m => m.
|
|
895
|
+
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
899
896
|
meta.root.__processed = true;
|
|
900
897
|
}
|
|
901
898
|
else {
|
|
@@ -904,17 +901,23 @@ export class MetadataDiscovery {
|
|
|
904
901
|
if (!meta.root.discriminatorColumn) {
|
|
905
902
|
return;
|
|
906
903
|
}
|
|
907
|
-
if (
|
|
904
|
+
if (meta.root.discriminatorMap) {
|
|
905
|
+
const map = meta.root.discriminatorMap;
|
|
906
|
+
Object.keys(map)
|
|
907
|
+
.filter(key => typeof map[key] === 'string')
|
|
908
|
+
.forEach(key => map[key] = this.metadata.getByClassName(map[key]).class);
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
908
911
|
meta.root.discriminatorMap = {};
|
|
909
912
|
const children = metadata
|
|
910
|
-
.filter(m => m.root.
|
|
913
|
+
.filter(m => m.root.class === meta.root.class && !m.abstract)
|
|
911
914
|
.sort((a, b) => a.className.localeCompare(b.className));
|
|
912
915
|
for (const m of children) {
|
|
913
916
|
const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
|
|
914
|
-
meta.root.discriminatorMap[name] = m.
|
|
917
|
+
meta.root.discriminatorMap[name] = m.class;
|
|
915
918
|
}
|
|
916
919
|
}
|
|
917
|
-
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([,
|
|
920
|
+
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, cls]) => cls === meta.class)?.[0];
|
|
918
921
|
if (!meta.root.properties[meta.root.discriminatorColumn]) {
|
|
919
922
|
this.createDiscriminatorProperty(meta.root);
|
|
920
923
|
}
|
|
@@ -941,10 +944,10 @@ export class MetadataDiscovery {
|
|
|
941
944
|
newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
|
|
942
945
|
}
|
|
943
946
|
newProp.nullable = true;
|
|
944
|
-
newProp.inherited =
|
|
947
|
+
newProp.inherited = !meta.root.properties[prop.name];
|
|
945
948
|
meta.root.addProperty(newProp);
|
|
946
949
|
});
|
|
947
|
-
meta.
|
|
950
|
+
meta.tableName = meta.root.tableName;
|
|
948
951
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
949
952
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
950
953
|
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
@@ -966,7 +969,7 @@ export class MetadataDiscovery {
|
|
|
966
969
|
}
|
|
967
970
|
}
|
|
968
971
|
initCheckConstraints(meta) {
|
|
969
|
-
const map =
|
|
972
|
+
const map = meta.createColumnMappingObject();
|
|
970
973
|
for (const check of meta.checks) {
|
|
971
974
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
972
975
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -976,7 +979,7 @@ export class MetadataDiscovery {
|
|
|
976
979
|
}
|
|
977
980
|
if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
978
981
|
for (const prop of meta.props) {
|
|
979
|
-
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item =>
|
|
982
|
+
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
|
|
980
983
|
this.initFieldName(prop);
|
|
981
984
|
meta.checks.push({
|
|
982
985
|
name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
@@ -1001,38 +1004,28 @@ export class MetadataDiscovery {
|
|
|
1001
1004
|
}
|
|
1002
1005
|
return;
|
|
1003
1006
|
}
|
|
1004
|
-
const map =
|
|
1007
|
+
const map = meta.createColumnMappingObject();
|
|
1005
1008
|
if (prop.generated instanceof Function) {
|
|
1006
1009
|
prop.generated = prop.generated(map);
|
|
1007
1010
|
}
|
|
1008
1011
|
}
|
|
1009
|
-
|
|
1010
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1011
|
-
if (prop.fieldNames) {
|
|
1012
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1013
|
-
}
|
|
1014
|
-
return o;
|
|
1015
|
-
}, {});
|
|
1016
|
-
}
|
|
1017
|
-
getDefaultVersionValue(prop) {
|
|
1012
|
+
getDefaultVersionValue(meta, prop) {
|
|
1018
1013
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1019
1014
|
return prop.defaultRaw;
|
|
1020
1015
|
}
|
|
1021
|
-
/* v8 ignore next
|
|
1016
|
+
/* v8 ignore next */
|
|
1022
1017
|
if (prop.default != null) {
|
|
1023
1018
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1024
1019
|
}
|
|
1025
|
-
|
|
1020
|
+
this.initCustomType(meta, prop, true);
|
|
1021
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1022
|
+
if (type === 'Date') {
|
|
1026
1023
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1027
1024
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1028
1025
|
}
|
|
1029
1026
|
return '1';
|
|
1030
1027
|
}
|
|
1031
1028
|
inferDefaultValue(meta, prop) {
|
|
1032
|
-
/* v8 ignore next 3 */
|
|
1033
|
-
if (!meta.class) {
|
|
1034
|
-
return;
|
|
1035
|
-
}
|
|
1036
1029
|
try {
|
|
1037
1030
|
// try to create two entity instances to detect the value is stable
|
|
1038
1031
|
const now = Date.now();
|
|
@@ -1060,12 +1053,12 @@ export class MetadataDiscovery {
|
|
|
1060
1053
|
return;
|
|
1061
1054
|
}
|
|
1062
1055
|
let val = prop.default;
|
|
1063
|
-
const raw =
|
|
1056
|
+
const raw = Raw.getKnownFragment(val);
|
|
1064
1057
|
if (raw) {
|
|
1065
1058
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1066
1059
|
return;
|
|
1067
1060
|
}
|
|
1068
|
-
if (
|
|
1061
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1069
1062
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1070
1063
|
}
|
|
1071
1064
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1093,13 +1086,13 @@ export class MetadataDiscovery {
|
|
|
1093
1086
|
if (prop.version) {
|
|
1094
1087
|
this.initDefaultValue(prop);
|
|
1095
1088
|
meta.versionProperty = prop.name;
|
|
1096
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1089
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1097
1090
|
}
|
|
1098
1091
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1099
1092
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1100
1093
|
}
|
|
1101
1094
|
}
|
|
1102
|
-
initCustomType(meta, prop) {
|
|
1095
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1103
1096
|
// `prop.type` might be actually instance of custom type class
|
|
1104
1097
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1105
1098
|
prop.customType = prop.type;
|
|
@@ -1107,47 +1100,70 @@ export class MetadataDiscovery {
|
|
|
1107
1100
|
}
|
|
1108
1101
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1109
1102
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1110
|
-
|
|
1111
|
-
|
|
1103
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1104
|
+
// we use just the type name, to have more performant hydration code
|
|
1105
|
+
const type = Utils.keys(t).find(type => {
|
|
1106
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1107
|
+
});
|
|
1108
|
+
if (type) {
|
|
1109
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1110
|
+
}
|
|
1111
|
+
else {
|
|
1112
|
+
prop.customType = new prop.type();
|
|
1113
|
+
prop.type = prop.customType.constructor.name;
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (simple) {
|
|
1117
|
+
return;
|
|
1112
1118
|
}
|
|
1113
1119
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1114
|
-
prop.customType = new
|
|
1120
|
+
prop.customType = new t.json();
|
|
1115
1121
|
}
|
|
1116
1122
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1117
|
-
prop.customType = new
|
|
1123
|
+
prop.customType = new t.json();
|
|
1124
|
+
}
|
|
1125
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1126
|
+
prop.customType = new t.json();
|
|
1118
1127
|
}
|
|
1119
1128
|
if (!prop.customType && prop.array && prop.items) {
|
|
1120
|
-
prop.customType = new
|
|
1129
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1130
|
+
}
|
|
1131
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1132
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1133
|
+
prop.customType = new t.json();
|
|
1121
1134
|
}
|
|
1122
1135
|
// for number arrays we make sure to convert the items to numbers
|
|
1123
1136
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1124
|
-
prop.customType = new
|
|
1137
|
+
prop.customType = new t.array(i => +i);
|
|
1125
1138
|
}
|
|
1126
1139
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1127
|
-
if (!prop.customType &&
|
|
1128
|
-
prop.customType = new
|
|
1140
|
+
if (!prop.customType && isArray) {
|
|
1141
|
+
prop.customType = new t.array();
|
|
1129
1142
|
}
|
|
1130
1143
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1131
|
-
prop.customType = new
|
|
1144
|
+
prop.customType = new t.blob();
|
|
1132
1145
|
}
|
|
1133
1146
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1134
|
-
prop.customType = new
|
|
1147
|
+
prop.customType = new t.uint8array();
|
|
1135
1148
|
}
|
|
1136
1149
|
const mappedType = this.getMappedType(prop);
|
|
1137
1150
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1138
|
-
[
|
|
1151
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1139
1152
|
.filter(type => mappedType instanceof type)
|
|
1140
|
-
.forEach(type => prop.customType = new type());
|
|
1153
|
+
.forEach((type) => prop.customType = new type());
|
|
1141
1154
|
}
|
|
1142
1155
|
if (prop.customType && !prop.columnTypes) {
|
|
1143
1156
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1144
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1157
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1145
1158
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1146
1159
|
}
|
|
1147
1160
|
else {
|
|
1148
1161
|
prop.runtimeType ??= prop.customType.runtimeType;
|
|
1149
1162
|
}
|
|
1150
1163
|
}
|
|
1164
|
+
else if (prop.runtimeType === 'object') {
|
|
1165
|
+
prop.runtimeType = mappedType.runtimeType;
|
|
1166
|
+
}
|
|
1151
1167
|
else {
|
|
1152
1168
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1153
1169
|
}
|
|
@@ -1158,16 +1174,16 @@ export class MetadataDiscovery {
|
|
|
1158
1174
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1159
1175
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1160
1176
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1161
|
-
if (prop.customType instanceof
|
|
1177
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1162
1178
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1163
1179
|
}
|
|
1164
1180
|
}
|
|
1165
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1181
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1166
1182
|
prop.type = prop.customType.name;
|
|
1167
1183
|
}
|
|
1168
|
-
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1184
|
+
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && prop.targetMeta.compositePK) {
|
|
1169
1185
|
prop.customTypes = [];
|
|
1170
|
-
for (const pk of
|
|
1186
|
+
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1171
1187
|
if (pk.customType) {
|
|
1172
1188
|
prop.customTypes.push(pk.customType);
|
|
1173
1189
|
prop.hasConvertToJSValueSQL ||= !!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
@@ -1179,7 +1195,7 @@ export class MetadataDiscovery {
|
|
|
1179
1195
|
}
|
|
1180
1196
|
}
|
|
1181
1197
|
}
|
|
1182
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1198
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1183
1199
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1184
1200
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1185
1201
|
}
|
|
@@ -1197,7 +1213,8 @@ export class MetadataDiscovery {
|
|
|
1197
1213
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1198
1214
|
return;
|
|
1199
1215
|
}
|
|
1200
|
-
|
|
1216
|
+
// when the target is a polymorphic embedded entity, `prop.target` is an array of classes, we need to get the metadata by the type name instead
|
|
1217
|
+
const meta2 = this.metadata.find(prop.target) ?? this.metadata.getByClassName(prop.type);
|
|
1201
1218
|
prop.referencedPKs = meta2.primaryKeys;
|
|
1202
1219
|
prop.targetMeta = meta2;
|
|
1203
1220
|
if (!prop.formula && prop.persist === false && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.embedded) {
|
|
@@ -1206,7 +1223,7 @@ export class MetadataDiscovery {
|
|
|
1206
1223
|
}
|
|
1207
1224
|
initColumnType(prop) {
|
|
1208
1225
|
this.initUnsigned(prop);
|
|
1209
|
-
|
|
1226
|
+
prop.targetMeta?.getPrimaryProps().map(pk => {
|
|
1210
1227
|
prop.length ??= pk.length;
|
|
1211
1228
|
prop.precision ??= pk.precision;
|
|
1212
1229
|
prop.scale ??= pk.scale;
|
|
@@ -1222,8 +1239,7 @@ export class MetadataDiscovery {
|
|
|
1222
1239
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1223
1240
|
const mappedType = this.getMappedType(prop);
|
|
1224
1241
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1225
|
-
if (mappedType instanceof
|
|
1226
|
-
&& !prop.columnTypes
|
|
1242
|
+
if (mappedType instanceof t.unknown
|
|
1227
1243
|
// it could be a runtime type from reflect-metadata
|
|
1228
1244
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
1229
1245
|
// or it might be inferred via ts-morph to some generic type alias
|
|
@@ -1236,11 +1252,12 @@ export class MetadataDiscovery {
|
|
|
1236
1252
|
}
|
|
1237
1253
|
return;
|
|
1238
1254
|
}
|
|
1239
|
-
|
|
1255
|
+
/* v8 ignore next */
|
|
1256
|
+
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
1240
1257
|
prop.columnTypes = [this.platform.getJsonDeclarationSQL()];
|
|
1241
1258
|
return;
|
|
1242
1259
|
}
|
|
1243
|
-
const targetMeta =
|
|
1260
|
+
const targetMeta = prop.targetMeta;
|
|
1244
1261
|
prop.columnTypes = [];
|
|
1245
1262
|
for (const pk of targetMeta.getPrimaryProps()) {
|
|
1246
1263
|
this.initCustomType(targetMeta, pk);
|
|
@@ -1266,7 +1283,7 @@ export class MetadataDiscovery {
|
|
|
1266
1283
|
t = 'enum';
|
|
1267
1284
|
}
|
|
1268
1285
|
else if (prop.enum) {
|
|
1269
|
-
t = prop.items?.every(item =>
|
|
1286
|
+
t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
|
|
1270
1287
|
}
|
|
1271
1288
|
if (t === 'Date') {
|
|
1272
1289
|
t = 'datetime';
|
|
@@ -1290,7 +1307,7 @@ export class MetadataDiscovery {
|
|
|
1290
1307
|
return;
|
|
1291
1308
|
}
|
|
1292
1309
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1293
|
-
const meta2 =
|
|
1310
|
+
const meta2 = prop.targetMeta;
|
|
1294
1311
|
prop.unsigned = meta2.getPrimaryProps().some(pk => {
|
|
1295
1312
|
this.initUnsigned(pk);
|
|
1296
1313
|
return pk.unsigned;
|
|
@@ -1305,30 +1322,6 @@ export class MetadataDiscovery {
|
|
|
1305
1322
|
prop.index ??= true;
|
|
1306
1323
|
}
|
|
1307
1324
|
}
|
|
1308
|
-
async getEntityClassOrSchema(path, name) {
|
|
1309
|
-
const exports = await Utils.dynamicImport(path);
|
|
1310
|
-
const targets = Object.values(exports)
|
|
1311
|
-
.filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
|
|
1312
|
-
// ignore class implementations that are linked from an EntitySchema
|
|
1313
|
-
for (const item of targets) {
|
|
1314
|
-
if (item instanceof EntitySchema) {
|
|
1315
|
-
targets.forEach((item2, idx) => {
|
|
1316
|
-
if (item.meta.class === item2) {
|
|
1317
|
-
targets.splice(idx, 1);
|
|
1318
|
-
}
|
|
1319
|
-
});
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
if (targets.length > 0) {
|
|
1323
|
-
return targets;
|
|
1324
|
-
}
|
|
1325
|
-
const target = exports.default ?? exports[name];
|
|
1326
|
-
/* v8 ignore next 3 */
|
|
1327
|
-
if (!target) {
|
|
1328
|
-
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
|
|
1329
|
-
}
|
|
1330
|
-
return [target];
|
|
1331
|
-
}
|
|
1332
1325
|
shouldForceConstructorUsage(meta) {
|
|
1333
1326
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1334
1327
|
if (Array.isArray(forceConstructor)) {
|