@mikro-orm/core 7.0.0-dev.15 → 7.0.0-dev.150
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 +81 -55
- package/EntityManager.js +292 -262
- 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 +80 -35
- 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 +94 -29
- package/entity/Collection.js +434 -97
- 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 +5 -5
- package/entity/EntityLoader.js +80 -70
- package/entity/EntityRepository.d.ts +6 -2
- package/entity/EntityRepository.js +8 -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 +21 -5
- 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 +18 -22
- package/metadata/EntitySchema.js +50 -34
- package/metadata/MetadataDiscovery.d.ts +6 -10
- package/metadata/MetadataDiscovery.js +315 -304
- 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 +18 -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 +158 -113
- 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 +70 -34
- 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 +141 -98
- 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 +27 -11
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +64 -30
- package/utils/EntityComparator.d.ts +13 -9
- package/utils/EntityComparator.js +84 -42
- 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 +13 -120
- package/utils/Utils.js +99 -375
- 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 -12
- 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 -32
- 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 -26
- 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/exports.d.ts +0 -24
- package/exports.js +0 -23
- 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
|
-
import { Utils } from '../utils/Utils.js';
|
|
2
|
+
import { compareArrays, 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,123 +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);
|
|
235
|
+
}
|
|
236
|
+
if (typeof parent === 'function' && parent.name && !this.metadata.has(parent)) {
|
|
237
|
+
this.discoverReferences([parent], false);
|
|
229
238
|
}
|
|
230
|
-
/* v8 ignore next
|
|
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
|
-
if (filepath) {
|
|
264
|
-
// initialize global metadata for given entity
|
|
265
|
-
MetadataStorage.getMetadata(entity.meta.className, filepath);
|
|
266
|
-
}
|
|
267
269
|
const meta = Utils.copy(entity.meta, false);
|
|
268
270
|
return EntitySchema.fromMetadata(meta);
|
|
269
271
|
}
|
|
270
272
|
const path = entity[MetadataStorage.PATH_SYMBOL];
|
|
271
273
|
if (path) {
|
|
272
274
|
const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
|
|
273
|
-
meta.path =
|
|
274
|
-
this.metadata.set(entity
|
|
275
|
+
meta.path = path;
|
|
276
|
+
this.metadata.set(entity, meta);
|
|
275
277
|
}
|
|
276
|
-
const exists = this.metadata.has(entity
|
|
277
|
-
const meta = this.metadata.get(entity
|
|
278
|
+
const exists = this.metadata.has(entity);
|
|
279
|
+
const meta = this.metadata.get(entity, true);
|
|
278
280
|
meta.abstract ??= !(exists && meta.name);
|
|
279
281
|
const schema = EntitySchema.fromMetadata(meta);
|
|
280
282
|
schema.setClass(entity);
|
|
281
283
|
return schema;
|
|
282
284
|
}
|
|
283
|
-
|
|
284
|
-
|
|
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) {
|
|
285
297
|
const meta = schema.meta;
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
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);
|
|
289
303
|
if (cache) {
|
|
290
304
|
this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
291
|
-
this.metadataProvider.loadFromCache(meta, cache);
|
|
292
|
-
meta.root = root;
|
|
293
305
|
this.discovered.push(meta);
|
|
294
306
|
return;
|
|
295
307
|
}
|
|
@@ -298,50 +310,18 @@ export class MetadataDiscovery {
|
|
|
298
310
|
this.inferDefaultValue(meta, prop);
|
|
299
311
|
}
|
|
300
312
|
// if the definition is using EntitySchema we still want it to go through the metadata provider to validate no types are missing
|
|
301
|
-
this.metadataProvider.loadEntityMetadata(meta
|
|
302
|
-
if (!meta.
|
|
313
|
+
this.metadataProvider.loadEntityMetadata(meta);
|
|
314
|
+
if (!meta.tableName && meta.name) {
|
|
303
315
|
const entityName = root.discriminatorColumn ? root.name : meta.name;
|
|
304
|
-
meta.
|
|
316
|
+
meta.tableName = this.namingStrategy.classToTableName(entityName);
|
|
305
317
|
}
|
|
306
|
-
|
|
307
|
-
this.saveToCache(meta);
|
|
318
|
+
this.metadataProvider.saveToCache(meta);
|
|
308
319
|
meta.root = root;
|
|
309
320
|
this.discovered.push(meta);
|
|
310
321
|
}
|
|
311
|
-
saveToCache(meta) {
|
|
312
|
-
if (!this.metadataProvider.useCache()) {
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
const copy = Utils.copy(meta, false);
|
|
316
|
-
for (const prop of copy.props) {
|
|
317
|
-
if (Type.isMappedType(prop.type)) {
|
|
318
|
-
Reflect.deleteProperty(prop, 'type');
|
|
319
|
-
Reflect.deleteProperty(prop, 'customType');
|
|
320
|
-
}
|
|
321
|
-
if (prop.default) {
|
|
322
|
-
const raw = RawQueryFragment.getKnownFragment(prop.default);
|
|
323
|
-
if (raw) {
|
|
324
|
-
prop.defaultRaw ??= this.platform.formatQuery(raw.sql, raw.params);
|
|
325
|
-
Reflect.deleteProperty(prop, 'default');
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
Reflect.deleteProperty(prop, 'targetMeta');
|
|
329
|
-
}
|
|
330
|
-
[
|
|
331
|
-
'prototype', 'props', 'referencingProperties', 'propertyOrder', 'relations',
|
|
332
|
-
'concurrencyCheckKeys', 'checks',
|
|
333
|
-
].forEach(key => delete copy[key]);
|
|
334
|
-
// base entity without properties might not have path, but nothing to cache there
|
|
335
|
-
if (meta.path) {
|
|
336
|
-
this.cache.set(meta.className + extname(meta.path), copy, meta.path);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
322
|
initNullability(prop) {
|
|
340
|
-
if (prop.kind === ReferenceKind.MANY_TO_ONE) {
|
|
341
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
|
|
342
|
-
}
|
|
343
323
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
344
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner
|
|
324
|
+
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
|
|
345
325
|
}
|
|
346
326
|
return Utils.defaultValue(prop, 'nullable', prop.optional);
|
|
347
327
|
}
|
|
@@ -376,7 +356,7 @@ export class MetadataDiscovery {
|
|
|
376
356
|
if (prop.joinColumns.length !== prop.columnTypes.length) {
|
|
377
357
|
prop.columnTypes = prop.joinColumns.flatMap(field => {
|
|
378
358
|
const matched = meta.props.find(p => p.fieldNames?.includes(field));
|
|
379
|
-
/* v8 ignore next
|
|
359
|
+
/* v8 ignore next */
|
|
380
360
|
if (!matched) {
|
|
381
361
|
throw MetadataError.fromWrongForeignKey(meta, prop, 'columnTypes');
|
|
382
362
|
}
|
|
@@ -403,7 +383,7 @@ export class MetadataDiscovery {
|
|
|
403
383
|
}
|
|
404
384
|
}
|
|
405
385
|
initManyToOneFieldName(prop, name) {
|
|
406
|
-
const meta2 =
|
|
386
|
+
const meta2 = prop.targetMeta;
|
|
407
387
|
const ret = [];
|
|
408
388
|
for (const primaryKey of meta2.primaryKeys) {
|
|
409
389
|
this.initFieldName(meta2.properties[primaryKey]);
|
|
@@ -414,11 +394,11 @@ export class MetadataDiscovery {
|
|
|
414
394
|
return ret;
|
|
415
395
|
}
|
|
416
396
|
initManyToManyFieldName(prop, name) {
|
|
417
|
-
const meta2 =
|
|
397
|
+
const meta2 = prop.targetMeta;
|
|
418
398
|
return meta2.primaryKeys.map(() => this.namingStrategy.propertyToColumnName(name));
|
|
419
399
|
}
|
|
420
400
|
initManyToManyFields(meta, prop) {
|
|
421
|
-
const meta2 =
|
|
401
|
+
const meta2 = prop.targetMeta;
|
|
422
402
|
Utils.defaultValue(prop, 'fixedOrder', !!prop.fixedOrderColumn);
|
|
423
403
|
const pivotMeta = this.metadata.find(prop.pivotEntity);
|
|
424
404
|
const props = Object.values(pivotMeta?.properties ?? {});
|
|
@@ -439,26 +419,26 @@ export class MetadataDiscovery {
|
|
|
439
419
|
prop.inverseJoinColumns ??= second.fieldNames;
|
|
440
420
|
}
|
|
441
421
|
if (!prop.pivotTable && prop.owner && this.platform.usesPivotTable()) {
|
|
442
|
-
prop.pivotTable = this.namingStrategy.joinTableName(meta.
|
|
422
|
+
prop.pivotTable = this.namingStrategy.joinTableName(meta.className, meta2.tableName, prop.name, meta.tableName);
|
|
443
423
|
}
|
|
444
424
|
if (prop.mappedBy) {
|
|
445
425
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
446
426
|
this.initManyToManyFields(meta2, prop2);
|
|
447
427
|
prop.pivotTable = prop2.pivotTable;
|
|
448
|
-
prop.pivotEntity = prop2.pivotEntity
|
|
428
|
+
prop.pivotEntity = prop2.pivotEntity;
|
|
449
429
|
prop.fixedOrder = prop2.fixedOrder;
|
|
450
430
|
prop.fixedOrderColumn = prop2.fixedOrderColumn;
|
|
451
431
|
prop.joinColumns = prop2.inverseJoinColumns;
|
|
452
432
|
prop.inverseJoinColumns = prop2.joinColumns;
|
|
453
433
|
}
|
|
454
434
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
455
|
-
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));
|
|
456
436
|
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, meta2.root.className);
|
|
457
437
|
}
|
|
458
438
|
initManyToOneFields(prop) {
|
|
459
|
-
const meta2 =
|
|
439
|
+
const meta2 = prop.targetMeta;
|
|
460
440
|
const fieldNames = Utils.flatten(meta2.primaryKeys.map(primaryKey => meta2.properties[primaryKey].fieldNames));
|
|
461
|
-
Utils.defaultValue(prop, 'referencedTableName', meta2.
|
|
441
|
+
Utils.defaultValue(prop, 'referencedTableName', meta2.tableName);
|
|
462
442
|
if (!prop.joinColumns) {
|
|
463
443
|
prop.joinColumns = fieldNames.map(fieldName => this.namingStrategy.joinKeyColumnName(prop.name, fieldName, fieldNames.length > 1));
|
|
464
444
|
}
|
|
@@ -467,7 +447,7 @@ export class MetadataDiscovery {
|
|
|
467
447
|
}
|
|
468
448
|
}
|
|
469
449
|
initOneToManyFields(prop) {
|
|
470
|
-
const meta2 =
|
|
450
|
+
const meta2 = prop.targetMeta;
|
|
471
451
|
if (!prop.joinColumns) {
|
|
472
452
|
prop.joinColumns = [this.namingStrategy.joinColumnName(prop.name)];
|
|
473
453
|
}
|
|
@@ -485,7 +465,7 @@ export class MetadataDiscovery {
|
|
|
485
465
|
pks[0].deleteRule ??= 'cascade';
|
|
486
466
|
}
|
|
487
467
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
488
|
-
this.validator.validateEntityDefinition(this.metadata, meta.
|
|
468
|
+
this.validator.validateEntityDefinition(this.metadata, meta.class, this.config.get('discovery'));
|
|
489
469
|
for (const prop of Object.values(meta.properties)) {
|
|
490
470
|
this.initNullability(prop);
|
|
491
471
|
this.applyNamingStrategy(meta, prop);
|
|
@@ -498,15 +478,21 @@ export class MetadataDiscovery {
|
|
|
498
478
|
}
|
|
499
479
|
this.initOwnColumns(meta);
|
|
500
480
|
meta.simplePK = pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
501
|
-
meta.serializedPrimaryKey
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
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;
|
|
505
484
|
}
|
|
506
485
|
if (this.platform.usesPivotTable()) {
|
|
507
486
|
return Object.values(meta.properties)
|
|
508
487
|
.filter(prop => prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner && prop.pivotTable)
|
|
509
|
-
.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
|
+
});
|
|
510
496
|
}
|
|
511
497
|
return [];
|
|
512
498
|
}
|
|
@@ -523,8 +509,11 @@ export class MetadataDiscovery {
|
|
|
523
509
|
['mappedBy', 'inversedBy', 'pivotEntity'].forEach(type => {
|
|
524
510
|
const value = prop[type];
|
|
525
511
|
if (value instanceof Function) {
|
|
526
|
-
const meta2 = this.metadata.get(prop.
|
|
512
|
+
const meta2 = prop.targetMeta ?? this.metadata.get(prop.target);
|
|
527
513
|
prop[type] = value(meta2.properties)?.name;
|
|
514
|
+
if (type === 'pivotEntity' && value) {
|
|
515
|
+
prop[type] = value(meta2.properties);
|
|
516
|
+
}
|
|
528
517
|
if (prop[type] == null) {
|
|
529
518
|
throw MetadataError.fromWrongReference(meta, prop, type);
|
|
530
519
|
}
|
|
@@ -540,9 +529,9 @@ export class MetadataDiscovery {
|
|
|
540
529
|
}
|
|
541
530
|
else if (fks.length >= 2) {
|
|
542
531
|
[first, second] = fks;
|
|
543
|
-
/* v8 ignore next 3 */
|
|
544
532
|
}
|
|
545
533
|
else {
|
|
534
|
+
/* v8 ignore next */
|
|
546
535
|
return [];
|
|
547
536
|
}
|
|
548
537
|
// wrong FK order, first FK needs to point to the owning side
|
|
@@ -556,7 +545,9 @@ export class MetadataDiscovery {
|
|
|
556
545
|
return [first, second];
|
|
557
546
|
}
|
|
558
547
|
definePivotTableEntity(meta, prop) {
|
|
559
|
-
const pivotMeta =
|
|
548
|
+
const pivotMeta = prop.pivotEntity
|
|
549
|
+
? this.metadata.find(prop.pivotEntity)
|
|
550
|
+
: this.metadata.getByClassName(prop.pivotTable, false);
|
|
560
551
|
// ensure inverse side exists so we can join it when populating via pivot tables
|
|
561
552
|
if (!prop.inversedBy && prop.targetMeta) {
|
|
562
553
|
const inverseName = `${meta.className}_${prop.name}__inverse`;
|
|
@@ -565,6 +556,8 @@ export class MetadataDiscovery {
|
|
|
565
556
|
name: inverseName,
|
|
566
557
|
kind: ReferenceKind.MANY_TO_MANY,
|
|
567
558
|
type: meta.className,
|
|
559
|
+
target: meta.class,
|
|
560
|
+
targetMeta: meta,
|
|
568
561
|
mappedBy: prop.name,
|
|
569
562
|
pivotEntity: prop.pivotEntity,
|
|
570
563
|
pivotTable: prop.pivotTable,
|
|
@@ -573,55 +566,51 @@ export class MetadataDiscovery {
|
|
|
573
566
|
};
|
|
574
567
|
this.applyNamingStrategy(prop.targetMeta, inverseProp);
|
|
575
568
|
this.initCustomType(prop.targetMeta, inverseProp);
|
|
576
|
-
this.initRelation(inverseProp);
|
|
577
569
|
prop.targetMeta.properties[inverseName] = inverseProp;
|
|
578
570
|
}
|
|
579
571
|
if (pivotMeta) {
|
|
572
|
+
prop.pivotEntity = pivotMeta.class;
|
|
580
573
|
this.ensureCorrectFKOrderInPivotEntity(pivotMeta, prop);
|
|
581
574
|
return pivotMeta;
|
|
582
575
|
}
|
|
583
|
-
const exists = this.metadata.find(prop.pivotTable);
|
|
584
|
-
if (exists) {
|
|
585
|
-
prop.pivotEntity = exists.className;
|
|
586
|
-
return exists;
|
|
587
|
-
}
|
|
588
576
|
let tableName = prop.pivotTable;
|
|
589
577
|
let schemaName;
|
|
590
578
|
if (prop.pivotTable.includes('.')) {
|
|
591
579
|
[schemaName, tableName] = prop.pivotTable.split('.');
|
|
592
580
|
}
|
|
593
581
|
schemaName ??= meta.schema;
|
|
594
|
-
const
|
|
595
|
-
const
|
|
582
|
+
const targetMeta = prop.targetMeta;
|
|
583
|
+
const targetType = targetMeta.className;
|
|
584
|
+
const pivotMeta2 = new EntityMetadata({
|
|
596
585
|
name: prop.pivotTable,
|
|
597
586
|
className: prop.pivotTable,
|
|
598
587
|
collection: tableName,
|
|
599
588
|
schema: schemaName,
|
|
600
589
|
pivotTable: true,
|
|
601
590
|
});
|
|
602
|
-
prop.pivotEntity =
|
|
591
|
+
prop.pivotEntity = pivotMeta2.class;
|
|
603
592
|
if (prop.fixedOrder) {
|
|
604
|
-
const primaryProp = this.defineFixedOrderProperty(prop,
|
|
605
|
-
|
|
593
|
+
const primaryProp = this.defineFixedOrderProperty(prop, targetMeta);
|
|
594
|
+
pivotMeta2.properties[primaryProp.name] = primaryProp;
|
|
606
595
|
}
|
|
607
596
|
else {
|
|
608
|
-
|
|
597
|
+
pivotMeta2.compositePK = true;
|
|
609
598
|
}
|
|
610
599
|
// handle self-referenced m:n with same default field names
|
|
611
600
|
if (meta.className === targetType && prop.joinColumns.every((joinColumn, idx) => joinColumn === prop.inverseJoinColumns[idx])) {
|
|
612
|
-
prop.joinColumns = prop.referencedColumnNames.map(name => this.namingStrategy.joinKeyColumnName(meta.
|
|
613
|
-
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));
|
|
614
603
|
if (prop.inversedBy) {
|
|
615
|
-
const prop2 =
|
|
604
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
616
605
|
prop2.inverseJoinColumns = prop.joinColumns;
|
|
617
606
|
prop2.joinColumns = prop.inverseJoinColumns;
|
|
618
607
|
}
|
|
619
608
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
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);
|
|
623
612
|
}
|
|
624
|
-
defineFixedOrderProperty(prop,
|
|
613
|
+
defineFixedOrderProperty(prop, targetMeta) {
|
|
625
614
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
626
615
|
const primaryProp = {
|
|
627
616
|
name: pk,
|
|
@@ -635,7 +624,7 @@ export class MetadataDiscovery {
|
|
|
635
624
|
this.initColumnType(primaryProp);
|
|
636
625
|
prop.fixedOrderColumn = pk;
|
|
637
626
|
if (prop.inversedBy) {
|
|
638
|
-
const prop2 =
|
|
627
|
+
const prop2 = targetMeta.properties[prop.inversedBy];
|
|
639
628
|
prop2.fixedOrder = true;
|
|
640
629
|
prop2.fixedOrderColumn = pk;
|
|
641
630
|
}
|
|
@@ -644,7 +633,8 @@ export class MetadataDiscovery {
|
|
|
644
633
|
definePivotProperty(prop, name, type, inverse, owner, selfReferencing) {
|
|
645
634
|
const ret = {
|
|
646
635
|
name,
|
|
647
|
-
type,
|
|
636
|
+
type: Utils.className(type),
|
|
637
|
+
target: type,
|
|
648
638
|
kind: ReferenceKind.MANY_TO_ONE,
|
|
649
639
|
cascade: [Cascade.ALL],
|
|
650
640
|
fixedOrder: prop.fixedOrder,
|
|
@@ -654,6 +644,7 @@ export class MetadataDiscovery {
|
|
|
654
644
|
autoincrement: false,
|
|
655
645
|
updateRule: prop.updateRule,
|
|
656
646
|
deleteRule: prop.deleteRule,
|
|
647
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
657
648
|
};
|
|
658
649
|
if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
|
|
659
650
|
ret.updateRule ??= 'no action';
|
|
@@ -701,7 +692,7 @@ export class MetadataDiscovery {
|
|
|
701
692
|
Object.values(meta.properties)
|
|
702
693
|
.filter(prop => prop.kind !== ReferenceKind.SCALAR && !prop.owner && prop.mappedBy)
|
|
703
694
|
.forEach(prop => {
|
|
704
|
-
const meta2 =
|
|
695
|
+
const meta2 = prop.targetMeta;
|
|
705
696
|
const prop2 = meta2.properties[prop.mappedBy];
|
|
706
697
|
if (prop2 && !prop2.inversedBy) {
|
|
707
698
|
prop2.inversedBy = prop.name;
|
|
@@ -709,7 +700,7 @@ export class MetadataDiscovery {
|
|
|
709
700
|
});
|
|
710
701
|
}
|
|
711
702
|
defineBaseEntityProperties(meta) {
|
|
712
|
-
const base = meta.extends && this.metadata.get(
|
|
703
|
+
const base = meta.extends && this.metadata.get(meta.extends);
|
|
713
704
|
if (!base || base === meta) { // make sure we do not fall into infinite loop
|
|
714
705
|
return 0;
|
|
715
706
|
}
|
|
@@ -740,12 +731,9 @@ export class MetadataDiscovery {
|
|
|
740
731
|
Utils.keys(base.hooks).forEach(type => {
|
|
741
732
|
meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
|
|
742
733
|
});
|
|
743
|
-
if (meta.constructorParams
|
|
734
|
+
if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
|
|
744
735
|
meta.constructorParams = [...base.constructorParams];
|
|
745
736
|
}
|
|
746
|
-
if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
|
|
747
|
-
meta.toJsonParams = [...base.toJsonParams];
|
|
748
|
-
}
|
|
749
737
|
return order;
|
|
750
738
|
}
|
|
751
739
|
initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
|
|
@@ -821,22 +809,30 @@ export class MetadataDiscovery {
|
|
|
821
809
|
}
|
|
822
810
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
823
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
|
+
};
|
|
824
818
|
const rootProperty = getRootProperty(embeddedProp);
|
|
825
819
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
826
820
|
const object = isParentObject(embeddedProp);
|
|
821
|
+
const array = isParentArray(embeddedProp);
|
|
827
822
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
828
823
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
829
824
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
830
825
|
const glue = object ? '~' : '_';
|
|
831
826
|
for (const prop of Object.values(embeddable.properties)) {
|
|
832
827
|
const name = (embeddedProp.embeddedPath?.join(glue) ?? embeddedProp.fieldNames[0] + glue) + prop.name;
|
|
833
|
-
meta.properties[name] = Utils.copy(prop
|
|
828
|
+
meta.properties[name] = Utils.copy(prop);
|
|
834
829
|
meta.properties[name].name = name;
|
|
835
830
|
meta.properties[name].embedded = [embeddedProp.name, prop.name];
|
|
836
831
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
837
832
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
838
833
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
839
|
-
|
|
834
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
835
|
+
if (embeddedProp.nullable || refInArray) {
|
|
840
836
|
meta.properties[name].nullable = true;
|
|
841
837
|
}
|
|
842
838
|
if (meta.properties[name].fieldNames) {
|
|
@@ -866,14 +862,17 @@ export class MetadataDiscovery {
|
|
|
866
862
|
path = [embeddedProp.fieldNames[0]];
|
|
867
863
|
}
|
|
868
864
|
this.initFieldName(prop, true);
|
|
865
|
+
this.initRelation(prop);
|
|
869
866
|
path.push(prop.fieldNames[0]);
|
|
870
867
|
meta.properties[name].fieldNames = prop.fieldNames;
|
|
871
868
|
meta.properties[name].embeddedPath = path;
|
|
872
|
-
const
|
|
869
|
+
const targetProp = prop.targetMeta?.getPrimaryProp() ?? prop;
|
|
870
|
+
const fieldName = raw(this.platform.getSearchJsonPropertySQL(path.join('->'), targetProp.runtimeType ?? targetProp.type, true));
|
|
873
871
|
meta.properties[name].fieldNameRaw = fieldName.sql; // for querying in SQL drivers
|
|
874
872
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
875
873
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
876
874
|
meta.properties[name].object = true;
|
|
875
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
877
876
|
}
|
|
878
877
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
879
878
|
}
|
|
@@ -896,7 +895,7 @@ export class MetadataDiscovery {
|
|
|
896
895
|
}
|
|
897
896
|
initSingleTableInheritance(meta, metadata) {
|
|
898
897
|
if (meta.root !== meta && !meta.__processed) {
|
|
899
|
-
meta.root = metadata.find(m => m.
|
|
898
|
+
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
900
899
|
meta.root.__processed = true;
|
|
901
900
|
}
|
|
902
901
|
else {
|
|
@@ -905,17 +904,23 @@ export class MetadataDiscovery {
|
|
|
905
904
|
if (!meta.root.discriminatorColumn) {
|
|
906
905
|
return;
|
|
907
906
|
}
|
|
908
|
-
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 {
|
|
909
914
|
meta.root.discriminatorMap = {};
|
|
910
915
|
const children = metadata
|
|
911
|
-
.filter(m => m.root.
|
|
916
|
+
.filter(m => m.root.class === meta.root.class && !m.abstract)
|
|
912
917
|
.sort((a, b) => a.className.localeCompare(b.className));
|
|
913
918
|
for (const m of children) {
|
|
914
919
|
const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
|
|
915
|
-
meta.root.discriminatorMap[name] = m.
|
|
920
|
+
meta.root.discriminatorMap[name] = m.class;
|
|
916
921
|
}
|
|
917
922
|
}
|
|
918
|
-
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([,
|
|
923
|
+
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, cls]) => cls === meta.class)?.[0];
|
|
919
924
|
if (!meta.root.properties[meta.root.discriminatorColumn]) {
|
|
920
925
|
this.createDiscriminatorProperty(meta.root);
|
|
921
926
|
}
|
|
@@ -927,25 +932,44 @@ export class MetadataDiscovery {
|
|
|
927
932
|
let i = 1;
|
|
928
933
|
Object.values(meta.properties).forEach(prop => {
|
|
929
934
|
const newProp = { ...prop };
|
|
930
|
-
|
|
935
|
+
const rootProp = meta.root.properties[prop.name];
|
|
936
|
+
if (rootProp && (rootProp.type !== prop.type || (rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
931
937
|
const name = newProp.name;
|
|
932
938
|
this.initFieldName(newProp, newProp.object);
|
|
939
|
+
newProp.renamedFrom = name;
|
|
933
940
|
newProp.name = name + '_' + (i++);
|
|
934
941
|
meta.root.addProperty(newProp);
|
|
942
|
+
// Track all field variants and map discriminator values to field names
|
|
943
|
+
if (!rootProp.stiFieldNames) {
|
|
944
|
+
this.initFieldName(prop, prop.object);
|
|
945
|
+
this.initFieldName(rootProp, rootProp.object);
|
|
946
|
+
rootProp.stiFieldNames = [...rootProp.fieldNames];
|
|
947
|
+
rootProp.stiFieldNameMap = {};
|
|
948
|
+
// Find which discriminator owns the original fieldNames
|
|
949
|
+
for (const [discValue, childClass] of Object.entries(meta.root.discriminatorMap)) {
|
|
950
|
+
const childMeta = this.metadata.find(childClass);
|
|
951
|
+
if (childMeta?.properties[prop.name]?.fieldNames && compareArrays(childMeta.properties[prop.name].fieldNames, rootProp.fieldNames)) {
|
|
952
|
+
rootProp.stiFieldNameMap[discValue] = rootProp.fieldNames[0];
|
|
953
|
+
break;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
rootProp.stiFieldNameMap[meta.discriminatorValue] = prop.fieldNames[0];
|
|
958
|
+
rootProp.stiFieldNames.push(...prop.fieldNames);
|
|
935
959
|
newProp.nullable = true;
|
|
936
960
|
newProp.name = name;
|
|
937
961
|
newProp.hydrate = false;
|
|
938
962
|
newProp.inherited = true;
|
|
939
963
|
return;
|
|
940
964
|
}
|
|
941
|
-
if (prop.enum && prop.items &&
|
|
942
|
-
newProp.items = Utils.unique([...
|
|
965
|
+
if (prop.enum && prop.items && rootProp?.items) {
|
|
966
|
+
newProp.items = Utils.unique([...rootProp.items, ...prop.items]);
|
|
943
967
|
}
|
|
944
968
|
newProp.nullable = true;
|
|
945
|
-
newProp.inherited =
|
|
969
|
+
newProp.inherited = !rootProp;
|
|
946
970
|
meta.root.addProperty(newProp);
|
|
947
971
|
});
|
|
948
|
-
meta.
|
|
972
|
+
meta.tableName = meta.root.tableName;
|
|
949
973
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
950
974
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
951
975
|
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
@@ -967,7 +991,7 @@ export class MetadataDiscovery {
|
|
|
967
991
|
}
|
|
968
992
|
}
|
|
969
993
|
initCheckConstraints(meta) {
|
|
970
|
-
const map =
|
|
994
|
+
const map = meta.createColumnMappingObject();
|
|
971
995
|
for (const check of meta.checks) {
|
|
972
996
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
973
997
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -977,7 +1001,7 @@ export class MetadataDiscovery {
|
|
|
977
1001
|
}
|
|
978
1002
|
if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
|
|
979
1003
|
for (const prop of meta.props) {
|
|
980
|
-
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item =>
|
|
1004
|
+
if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
|
|
981
1005
|
this.initFieldName(prop);
|
|
982
1006
|
meta.checks.push({
|
|
983
1007
|
name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
|
|
@@ -1002,38 +1026,28 @@ export class MetadataDiscovery {
|
|
|
1002
1026
|
}
|
|
1003
1027
|
return;
|
|
1004
1028
|
}
|
|
1005
|
-
const map =
|
|
1029
|
+
const map = meta.createColumnMappingObject();
|
|
1006
1030
|
if (prop.generated instanceof Function) {
|
|
1007
1031
|
prop.generated = prop.generated(map);
|
|
1008
1032
|
}
|
|
1009
1033
|
}
|
|
1010
|
-
|
|
1011
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1012
|
-
if (prop.fieldNames) {
|
|
1013
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1014
|
-
}
|
|
1015
|
-
return o;
|
|
1016
|
-
}, {});
|
|
1017
|
-
}
|
|
1018
|
-
getDefaultVersionValue(prop) {
|
|
1034
|
+
getDefaultVersionValue(meta, prop) {
|
|
1019
1035
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1020
1036
|
return prop.defaultRaw;
|
|
1021
1037
|
}
|
|
1022
|
-
/* v8 ignore next
|
|
1038
|
+
/* v8 ignore next */
|
|
1023
1039
|
if (prop.default != null) {
|
|
1024
1040
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1025
1041
|
}
|
|
1026
|
-
|
|
1042
|
+
this.initCustomType(meta, prop, true);
|
|
1043
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1044
|
+
if (type === 'Date') {
|
|
1027
1045
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1028
1046
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1029
1047
|
}
|
|
1030
1048
|
return '1';
|
|
1031
1049
|
}
|
|
1032
1050
|
inferDefaultValue(meta, prop) {
|
|
1033
|
-
/* v8 ignore next 3 */
|
|
1034
|
-
if (!meta.class) {
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
1051
|
try {
|
|
1038
1052
|
// try to create two entity instances to detect the value is stable
|
|
1039
1053
|
const now = Date.now();
|
|
@@ -1061,12 +1075,12 @@ export class MetadataDiscovery {
|
|
|
1061
1075
|
return;
|
|
1062
1076
|
}
|
|
1063
1077
|
let val = prop.default;
|
|
1064
|
-
const raw =
|
|
1078
|
+
const raw = Raw.getKnownFragment(val);
|
|
1065
1079
|
if (raw) {
|
|
1066
1080
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1067
1081
|
return;
|
|
1068
1082
|
}
|
|
1069
|
-
if (
|
|
1083
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1070
1084
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1071
1085
|
}
|
|
1072
1086
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1094,13 +1108,13 @@ export class MetadataDiscovery {
|
|
|
1094
1108
|
if (prop.version) {
|
|
1095
1109
|
this.initDefaultValue(prop);
|
|
1096
1110
|
meta.versionProperty = prop.name;
|
|
1097
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1111
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1098
1112
|
}
|
|
1099
1113
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1100
1114
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1101
1115
|
}
|
|
1102
1116
|
}
|
|
1103
|
-
initCustomType(meta, prop) {
|
|
1117
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1104
1118
|
// `prop.type` might be actually instance of custom type class
|
|
1105
1119
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1106
1120
|
prop.customType = prop.type;
|
|
@@ -1108,41 +1122,61 @@ export class MetadataDiscovery {
|
|
|
1108
1122
|
}
|
|
1109
1123
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1110
1124
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1111
|
-
|
|
1112
|
-
|
|
1125
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1126
|
+
// we use just the type name, to have more performant hydration code
|
|
1127
|
+
const type = Utils.keys(t).find(type => {
|
|
1128
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1129
|
+
});
|
|
1130
|
+
if (type) {
|
|
1131
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
prop.customType = new prop.type();
|
|
1135
|
+
prop.type = prop.customType.constructor.name;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
if (simple) {
|
|
1139
|
+
return;
|
|
1113
1140
|
}
|
|
1114
1141
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1115
|
-
prop.customType = new
|
|
1142
|
+
prop.customType = new t.json();
|
|
1116
1143
|
}
|
|
1117
1144
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1118
|
-
prop.customType = new
|
|
1145
|
+
prop.customType = new t.json();
|
|
1146
|
+
}
|
|
1147
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1148
|
+
prop.customType = new t.json();
|
|
1119
1149
|
}
|
|
1120
1150
|
if (!prop.customType && prop.array && prop.items) {
|
|
1121
|
-
prop.customType = new
|
|
1151
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1152
|
+
}
|
|
1153
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1154
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1155
|
+
prop.customType = new t.json();
|
|
1122
1156
|
}
|
|
1123
1157
|
// for number arrays we make sure to convert the items to numbers
|
|
1124
1158
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1125
|
-
prop.customType = new
|
|
1159
|
+
prop.customType = new t.array(i => +i);
|
|
1126
1160
|
}
|
|
1127
1161
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1128
|
-
if (!prop.customType &&
|
|
1129
|
-
prop.customType = new
|
|
1162
|
+
if (!prop.customType && isArray) {
|
|
1163
|
+
prop.customType = new t.array();
|
|
1130
1164
|
}
|
|
1131
1165
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1132
|
-
prop.customType = new
|
|
1166
|
+
prop.customType = new t.blob();
|
|
1133
1167
|
}
|
|
1134
1168
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1135
|
-
prop.customType = new
|
|
1169
|
+
prop.customType = new t.uint8array();
|
|
1136
1170
|
}
|
|
1137
1171
|
const mappedType = this.getMappedType(prop);
|
|
1138
1172
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1139
|
-
[
|
|
1173
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1140
1174
|
.filter(type => mappedType instanceof type)
|
|
1141
|
-
.forEach(type => prop.customType = new type());
|
|
1175
|
+
.forEach((type) => prop.customType = new type());
|
|
1142
1176
|
}
|
|
1143
1177
|
if (prop.customType && !prop.columnTypes) {
|
|
1144
1178
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1145
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1179
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1146
1180
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1147
1181
|
}
|
|
1148
1182
|
else {
|
|
@@ -1162,16 +1196,16 @@ export class MetadataDiscovery {
|
|
|
1162
1196
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1163
1197
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1164
1198
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1165
|
-
if (prop.customType instanceof
|
|
1199
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1166
1200
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1167
1201
|
}
|
|
1168
1202
|
}
|
|
1169
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1203
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1170
1204
|
prop.type = prop.customType.name;
|
|
1171
1205
|
}
|
|
1172
|
-
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) &&
|
|
1206
|
+
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && prop.targetMeta.compositePK) {
|
|
1173
1207
|
prop.customTypes = [];
|
|
1174
|
-
for (const pk of
|
|
1208
|
+
for (const pk of prop.targetMeta.getPrimaryProps()) {
|
|
1175
1209
|
if (pk.customType) {
|
|
1176
1210
|
prop.customTypes.push(pk.customType);
|
|
1177
1211
|
prop.hasConvertToJSValueSQL ||= !!pk.customType.convertToJSValueSQL && pk.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
@@ -1183,7 +1217,7 @@ export class MetadataDiscovery {
|
|
|
1183
1217
|
}
|
|
1184
1218
|
}
|
|
1185
1219
|
}
|
|
1186
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1220
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1187
1221
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1188
1222
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1189
1223
|
}
|
|
@@ -1201,7 +1235,8 @@ export class MetadataDiscovery {
|
|
|
1201
1235
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1202
1236
|
return;
|
|
1203
1237
|
}
|
|
1204
|
-
|
|
1238
|
+
// 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
|
|
1239
|
+
const meta2 = this.metadata.find(prop.target) ?? this.metadata.getByClassName(prop.type);
|
|
1205
1240
|
prop.referencedPKs = meta2.primaryKeys;
|
|
1206
1241
|
prop.targetMeta = meta2;
|
|
1207
1242
|
if (!prop.formula && prop.persist === false && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.embedded) {
|
|
@@ -1210,7 +1245,7 @@ export class MetadataDiscovery {
|
|
|
1210
1245
|
}
|
|
1211
1246
|
initColumnType(prop) {
|
|
1212
1247
|
this.initUnsigned(prop);
|
|
1213
|
-
|
|
1248
|
+
prop.targetMeta?.getPrimaryProps().map(pk => {
|
|
1214
1249
|
prop.length ??= pk.length;
|
|
1215
1250
|
prop.precision ??= pk.precision;
|
|
1216
1251
|
prop.scale ??= pk.scale;
|
|
@@ -1226,8 +1261,7 @@ export class MetadataDiscovery {
|
|
|
1226
1261
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1227
1262
|
const mappedType = this.getMappedType(prop);
|
|
1228
1263
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1229
|
-
if (mappedType instanceof
|
|
1230
|
-
&& !prop.columnTypes
|
|
1264
|
+
if (mappedType instanceof t.unknown
|
|
1231
1265
|
// it could be a runtime type from reflect-metadata
|
|
1232
1266
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
1233
1267
|
// or it might be inferred via ts-morph to some generic type alias
|
|
@@ -1240,11 +1274,12 @@ export class MetadataDiscovery {
|
|
|
1240
1274
|
}
|
|
1241
1275
|
return;
|
|
1242
1276
|
}
|
|
1243
|
-
|
|
1277
|
+
/* v8 ignore next */
|
|
1278
|
+
if (prop.kind === ReferenceKind.EMBEDDED && prop.object) {
|
|
1244
1279
|
prop.columnTypes = [this.platform.getJsonDeclarationSQL()];
|
|
1245
1280
|
return;
|
|
1246
1281
|
}
|
|
1247
|
-
const targetMeta =
|
|
1282
|
+
const targetMeta = prop.targetMeta;
|
|
1248
1283
|
prop.columnTypes = [];
|
|
1249
1284
|
for (const pk of targetMeta.getPrimaryProps()) {
|
|
1250
1285
|
this.initCustomType(targetMeta, pk);
|
|
@@ -1270,7 +1305,7 @@ export class MetadataDiscovery {
|
|
|
1270
1305
|
t = 'enum';
|
|
1271
1306
|
}
|
|
1272
1307
|
else if (prop.enum) {
|
|
1273
|
-
t = prop.items?.every(item =>
|
|
1308
|
+
t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
|
|
1274
1309
|
}
|
|
1275
1310
|
if (t === 'Date') {
|
|
1276
1311
|
t = 'datetime';
|
|
@@ -1294,7 +1329,7 @@ export class MetadataDiscovery {
|
|
|
1294
1329
|
return;
|
|
1295
1330
|
}
|
|
1296
1331
|
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
|
|
1297
|
-
const meta2 =
|
|
1332
|
+
const meta2 = prop.targetMeta;
|
|
1298
1333
|
prop.unsigned = meta2.getPrimaryProps().some(pk => {
|
|
1299
1334
|
this.initUnsigned(pk);
|
|
1300
1335
|
return pk.unsigned;
|
|
@@ -1309,30 +1344,6 @@ export class MetadataDiscovery {
|
|
|
1309
1344
|
prop.index ??= true;
|
|
1310
1345
|
}
|
|
1311
1346
|
}
|
|
1312
|
-
async getEntityClassOrSchema(path, name) {
|
|
1313
|
-
const exports = await Utils.dynamicImport(path);
|
|
1314
|
-
const targets = Object.values(exports)
|
|
1315
|
-
.filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
|
|
1316
|
-
// ignore class implementations that are linked from an EntitySchema
|
|
1317
|
-
for (const item of targets) {
|
|
1318
|
-
if (item instanceof EntitySchema) {
|
|
1319
|
-
targets.forEach((item2, idx) => {
|
|
1320
|
-
if (item.meta.class === item2) {
|
|
1321
|
-
targets.splice(idx, 1);
|
|
1322
|
-
}
|
|
1323
|
-
});
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
if (targets.length > 0) {
|
|
1327
|
-
return targets;
|
|
1328
|
-
}
|
|
1329
|
-
const target = exports.default ?? exports[name];
|
|
1330
|
-
/* v8 ignore next 3 */
|
|
1331
|
-
if (!target) {
|
|
1332
|
-
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
|
|
1333
|
-
}
|
|
1334
|
-
return [target];
|
|
1335
|
-
}
|
|
1336
1347
|
shouldForceConstructorUsage(meta) {
|
|
1337
1348
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1338
1349
|
if (Array.isArray(forceConstructor)) {
|