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