@mikro-orm/core 7.0.0-dev.5 → 7.0.0-dev.50
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 -27
- package/EntityManager.js +287 -175
- package/MikroORM.d.ts +6 -6
- package/MikroORM.js +31 -74
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +6 -4
- package/connections/Connection.d.ts +9 -5
- package/connections/Connection.js +16 -13
- package/decorators/Embedded.d.ts +5 -11
- package/decorators/Entity.d.ts +18 -3
- package/decorators/Indexed.d.ts +2 -2
- package/decorators/ManyToMany.d.ts +2 -0
- package/decorators/ManyToOne.d.ts +4 -0
- package/decorators/OneToOne.d.ts +4 -0
- package/decorators/Property.d.ts +53 -9
- package/decorators/Transactional.d.ts +1 -0
- package/decorators/Transactional.js +3 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +10 -5
- package/drivers/DatabaseDriver.js +4 -4
- package/drivers/IDatabaseDriver.d.ts +28 -4
- package/entity/ArrayCollection.d.ts +6 -4
- package/entity/ArrayCollection.js +26 -9
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +3 -4
- package/entity/Collection.js +37 -17
- package/entity/EntityAssigner.d.ts +1 -1
- package/entity/EntityAssigner.js +9 -1
- package/entity/EntityFactory.d.ts +7 -0
- package/entity/EntityFactory.js +29 -11
- package/entity/EntityHelper.js +25 -8
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +69 -36
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +1 -1
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +30 -3
- package/entity/WrappedEntity.d.ts +0 -2
- package/entity/WrappedEntity.js +1 -5
- package/entity/defineEntity.d.ts +555 -0
- package/entity/defineEntity.js +529 -0
- package/entity/index.d.ts +2 -0
- package/entity/index.js +2 -0
- package/entity/utils.d.ts +7 -0
- package/entity/utils.js +15 -3
- package/enums.d.ts +16 -3
- package/enums.js +13 -0
- package/errors.d.ts +6 -1
- package/errors.js +14 -4
- package/events/EventSubscriber.d.ts +3 -1
- package/hydration/ObjectHydrator.d.ts +4 -4
- package/hydration/ObjectHydrator.js +35 -24
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/logging/DefaultLogger.d.ts +1 -1
- package/logging/SimpleLogger.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +8 -4
- package/metadata/EntitySchema.js +39 -19
- package/metadata/MetadataDiscovery.d.ts +4 -4
- package/metadata/MetadataDiscovery.js +139 -122
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
- package/naming-strategy/AbstractNamingStrategy.js +7 -1
- package/naming-strategy/NamingStrategy.d.ts +11 -1
- package/package.json +5 -5
- package/platforms/Platform.d.ts +5 -3
- package/platforms/Platform.js +4 -8
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +23 -5
- package/serialization/EntityTransformer.js +16 -6
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- package/types/BooleanType.d.ts +1 -1
- package/types/DecimalType.d.ts +6 -4
- package/types/DecimalType.js +1 -1
- package/types/DoubleType.js +1 -1
- package/types/JsonType.d.ts +1 -1
- package/types/JsonType.js +7 -2
- package/types/Type.d.ts +2 -1
- package/types/Type.js +1 -1
- package/types/index.d.ts +1 -1
- package/typings.d.ts +89 -49
- package/typings.js +31 -31
- package/unit-of-work/ChangeSetComputer.js +8 -3
- package/unit-of-work/ChangeSetPersister.d.ts +4 -2
- package/unit-of-work/ChangeSetPersister.js +37 -16
- package/unit-of-work/UnitOfWork.d.ts +8 -1
- package/unit-of-work/UnitOfWork.js +110 -53
- package/utils/AbstractSchemaGenerator.js +3 -1
- package/utils/Configuration.d.ts +29 -16
- package/utils/Configuration.js +17 -18
- package/utils/ConfigurationLoader.d.ts +9 -22
- package/utils/ConfigurationLoader.js +49 -72
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +7 -2
- package/utils/DataloaderUtils.js +38 -7
- package/utils/EntityComparator.d.ts +6 -2
- package/utils/EntityComparator.js +104 -58
- package/utils/QueryHelper.d.ts +9 -1
- package/utils/QueryHelper.js +66 -5
- package/utils/RawQueryFragment.d.ts +36 -4
- package/utils/RawQueryFragment.js +34 -13
- package/utils/TransactionManager.d.ts +65 -0
- package/utils/TransactionManager.js +223 -0
- package/utils/Utils.d.ts +13 -11
- package/utils/Utils.js +82 -55
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/upsert-utils.d.ts +7 -2
- package/utils/upsert-utils.js +52 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { basename, extname } from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import { EntityMetadata
|
|
2
|
+
import { glob } from 'tinyglobby';
|
|
3
|
+
import { EntityMetadata } from '../typings.js';
|
|
4
4
|
import { Utils } from '../utils/Utils.js';
|
|
5
5
|
import { MetadataValidator } from './MetadataValidator.js';
|
|
6
6
|
import { MetadataStorage } from './MetadataStorage.js';
|
|
7
7
|
import { EntitySchema } from './EntitySchema.js';
|
|
8
8
|
import { Cascade, ReferenceKind } from '../enums.js';
|
|
9
9
|
import { MetadataError } from '../errors.js';
|
|
10
|
-
import {
|
|
10
|
+
import { t, Type } from '../types/index.js';
|
|
11
11
|
import { colors } from '../logging/colors.js';
|
|
12
12
|
import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
13
13
|
export class MetadataDiscovery {
|
|
@@ -47,10 +47,10 @@ 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
51
|
const startTime = Date.now();
|
|
52
52
|
this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)} in sync mode`);
|
|
53
|
-
this.findEntities(
|
|
53
|
+
this.findEntities();
|
|
54
54
|
for (const meta of this.discovered) {
|
|
55
55
|
/* v8 ignore next */
|
|
56
56
|
void this.config.get('discovery').onMetadata?.(meta, this.platform);
|
|
@@ -63,6 +63,9 @@ export class MetadataDiscovery {
|
|
|
63
63
|
void this.config.get('discovery').afterDiscovered?.(storage, this.platform);
|
|
64
64
|
return storage;
|
|
65
65
|
}
|
|
66
|
+
validateDiscovered(metadata) {
|
|
67
|
+
return this.validator.validateDiscovered(metadata, this.config.get('discovery'));
|
|
68
|
+
}
|
|
66
69
|
mapDiscoveredEntities() {
|
|
67
70
|
const discovered = new MetadataStorage();
|
|
68
71
|
this.discovered
|
|
@@ -74,11 +77,41 @@ export class MetadataDiscovery {
|
|
|
74
77
|
});
|
|
75
78
|
return discovered;
|
|
76
79
|
}
|
|
80
|
+
initAccessors(meta) {
|
|
81
|
+
for (const prop of Object.values(meta.properties)) {
|
|
82
|
+
if (!prop.accessor || meta.properties[prop.accessor]) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
86
|
+
if (desc?.get || desc?.set) {
|
|
87
|
+
this.initFieldName(prop);
|
|
88
|
+
const accessor = prop.name;
|
|
89
|
+
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
90
|
+
if (prop.accessor === true) {
|
|
91
|
+
prop.getter = prop.setter = true;
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
prop.getter = prop.setter = false;
|
|
95
|
+
}
|
|
96
|
+
prop.accessor = accessor;
|
|
97
|
+
prop.serializedName ??= accessor;
|
|
98
|
+
Utils.renameKey(meta.properties, accessor, prop.name);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
const name = prop.name;
|
|
102
|
+
prop.name = prop.accessor;
|
|
103
|
+
this.initFieldName(prop);
|
|
104
|
+
prop.serializedName ??= prop.accessor;
|
|
105
|
+
prop.name = name;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
77
109
|
processDiscoveredEntities(discovered) {
|
|
78
110
|
for (const meta of discovered) {
|
|
79
111
|
let i = 1;
|
|
80
112
|
Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
|
|
81
113
|
Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
|
|
114
|
+
this.initAccessors(meta);
|
|
82
115
|
}
|
|
83
116
|
// ignore base entities (not annotated with @Entity)
|
|
84
117
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
@@ -101,10 +134,6 @@ export class MetadataDiscovery {
|
|
|
101
134
|
this.initDefaultValue(prop);
|
|
102
135
|
this.inferTypeFromDefault(prop);
|
|
103
136
|
this.initColumnType(prop);
|
|
104
|
-
// change tracking on scalars is used only for "auto" flushMode
|
|
105
|
-
if (this.config.get('flushMode') !== 'auto' && [ReferenceKind.SCALAR, ReferenceKind.EMBEDDED].includes(prop.kind)) {
|
|
106
|
-
prop.trackChanges = false;
|
|
107
|
-
}
|
|
108
137
|
}
|
|
109
138
|
}
|
|
110
139
|
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => this.initIndexes(meta, prop)));
|
|
@@ -128,24 +157,18 @@ export class MetadataDiscovery {
|
|
|
128
157
|
findEntities(preferTs, sync = false) {
|
|
129
158
|
this.discovered.length = 0;
|
|
130
159
|
const options = this.config.get('discovery');
|
|
131
|
-
const key = (preferTs && this.config.get('
|
|
160
|
+
const key = (preferTs && this.config.get('entitiesTs').length > 0) ? 'entitiesTs' : 'entities';
|
|
132
161
|
const paths = this.config.get(key).filter(item => Utils.isString(item));
|
|
133
162
|
const refs = this.config.get(key).filter(item => !Utils.isString(item));
|
|
134
163
|
if (paths.length > 0) {
|
|
135
164
|
if (sync || options.requireEntitiesArray) {
|
|
136
165
|
throw new Error(`[requireEntitiesArray] Explicit list of entities is required, please use the 'entities' option.`);
|
|
137
166
|
}
|
|
138
|
-
return this.discoverDirectories(paths).then(
|
|
139
|
-
this.discoverReferences(refs);
|
|
140
|
-
this.discoverMissingTargets();
|
|
141
|
-
this.validator.validateDiscovered(this.discovered, options);
|
|
142
|
-
return this.discovered;
|
|
167
|
+
return this.discoverDirectories(paths).then(targets => {
|
|
168
|
+
return this.discoverReferences([...targets, ...refs]);
|
|
143
169
|
});
|
|
144
170
|
}
|
|
145
|
-
this.discoverReferences(refs);
|
|
146
|
-
this.discoverMissingTargets();
|
|
147
|
-
this.validator.validateDiscovered(this.discovered, options);
|
|
148
|
-
return this.discovered;
|
|
171
|
+
return this.discoverReferences(refs);
|
|
149
172
|
}
|
|
150
173
|
discoverMissingTargets() {
|
|
151
174
|
const unwrap = (type) => type
|
|
@@ -173,50 +196,32 @@ export class MetadataDiscovery {
|
|
|
173
196
|
}
|
|
174
197
|
tryDiscoverTargets(targets) {
|
|
175
198
|
for (const target of targets) {
|
|
176
|
-
|
|
177
|
-
|
|
199
|
+
const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
|
|
200
|
+
if (isDiscoverable && target.name && !this.metadata.has(target.name)) {
|
|
201
|
+
this.discoverReferences([target], false);
|
|
178
202
|
this.discoverMissingTargets();
|
|
179
203
|
}
|
|
180
204
|
}
|
|
181
205
|
}
|
|
182
206
|
async discoverDirectories(paths) {
|
|
183
207
|
paths = paths.map(path => Utils.normalizePath(path));
|
|
184
|
-
const files = await
|
|
208
|
+
const files = await glob(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
|
|
185
209
|
this.logger.log('discovery', `- processing ${colors.cyan('' + files.length)} files`);
|
|
186
|
-
const found =
|
|
210
|
+
const found = new Map();
|
|
187
211
|
for (const filepath of files) {
|
|
188
212
|
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$/)) {
|
|
213
|
+
if (!filename.match(/\.[cm]?[jt]s$/) || filename.match(/\.d\.[cm]?ts/)) {
|
|
194
214
|
this.logger.log('discovery', `- ignoring file ${filename}`);
|
|
195
215
|
continue;
|
|
196
216
|
}
|
|
197
|
-
|
|
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);
|
|
217
|
+
await this.getEntityClassOrSchema(filepath, found);
|
|
214
218
|
}
|
|
219
|
+
return found.keys();
|
|
215
220
|
}
|
|
216
|
-
discoverReferences(refs) {
|
|
221
|
+
discoverReferences(refs, validate = true) {
|
|
217
222
|
const found = [];
|
|
218
223
|
for (const entity of refs) {
|
|
219
|
-
const schema = this.getSchema(
|
|
224
|
+
const schema = this.getSchema(entity);
|
|
220
225
|
const meta = schema.init().meta;
|
|
221
226
|
this.metadata.set(meta.className, meta);
|
|
222
227
|
found.push(schema);
|
|
@@ -225,7 +230,10 @@ export class MetadataDiscovery {
|
|
|
225
230
|
for (const meta of this.metadata) {
|
|
226
231
|
let parent = meta.extends;
|
|
227
232
|
if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.className)) {
|
|
228
|
-
this.discoverReferences([parent]);
|
|
233
|
+
this.discoverReferences([parent], false);
|
|
234
|
+
}
|
|
235
|
+
if (typeof parent === 'function' && parent.name && !this.metadata.has(parent.name)) {
|
|
236
|
+
this.discoverReferences([parent], false);
|
|
229
237
|
}
|
|
230
238
|
/* v8 ignore next 3 */
|
|
231
239
|
if (!meta.class) {
|
|
@@ -233,12 +241,16 @@ export class MetadataDiscovery {
|
|
|
233
241
|
}
|
|
234
242
|
parent = Object.getPrototypeOf(meta.class);
|
|
235
243
|
if (parent.name !== '' && !this.metadata.has(parent.name)) {
|
|
236
|
-
this.discoverReferences([parent]);
|
|
244
|
+
this.discoverReferences([parent], false);
|
|
237
245
|
}
|
|
238
246
|
}
|
|
239
247
|
for (const schema of found) {
|
|
240
248
|
this.discoverEntity(schema);
|
|
241
249
|
}
|
|
250
|
+
this.discoverMissingTargets();
|
|
251
|
+
if (validate) {
|
|
252
|
+
this.validateDiscovered(this.discovered);
|
|
253
|
+
}
|
|
242
254
|
return this.discovered.filter(meta => found.find(m => m.name === meta.className));
|
|
243
255
|
}
|
|
244
256
|
reset(className) {
|
|
@@ -248,23 +260,13 @@ export class MetadataDiscovery {
|
|
|
248
260
|
this.discovered.splice(exists, 1);
|
|
249
261
|
}
|
|
250
262
|
}
|
|
251
|
-
|
|
252
|
-
/* v8 ignore next 3 */
|
|
253
|
-
if ('schema' in entity && entity.schema instanceof EntitySchema) {
|
|
254
|
-
return entity.schema;
|
|
255
|
-
}
|
|
263
|
+
getSchema(entity) {
|
|
256
264
|
if (EntitySchema.REGISTRY.has(entity)) {
|
|
257
|
-
|
|
265
|
+
entity = EntitySchema.REGISTRY.get(entity);
|
|
258
266
|
}
|
|
259
|
-
return entity;
|
|
260
|
-
}
|
|
261
|
-
getSchema(entity, filepath) {
|
|
262
267
|
if (entity instanceof EntitySchema) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
MetadataStorage.getMetadata(entity.meta.className, filepath);
|
|
266
|
-
}
|
|
267
|
-
return entity;
|
|
268
|
+
const meta = Utils.copy(entity.meta, false);
|
|
269
|
+
return EntitySchema.fromMetadata(meta);
|
|
268
270
|
}
|
|
269
271
|
const path = entity[MetadataStorage.PATH_SYMBOL];
|
|
270
272
|
if (path) {
|
|
@@ -279,11 +281,12 @@ export class MetadataDiscovery {
|
|
|
279
281
|
schema.setClass(entity);
|
|
280
282
|
return schema;
|
|
281
283
|
}
|
|
282
|
-
discoverEntity(schema
|
|
283
|
-
this.logger.log('discovery', `- processing entity ${colors.cyan(schema.meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
284
|
+
discoverEntity(schema) {
|
|
284
285
|
const meta = schema.meta;
|
|
286
|
+
const path = meta.path;
|
|
287
|
+
this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
285
288
|
const root = Utils.getRootEntity(this.metadata, meta);
|
|
286
|
-
schema.meta.path = Utils.relativePath(
|
|
289
|
+
schema.meta.path = Utils.relativePath(meta.path, this.config.get('baseDir'));
|
|
287
290
|
const cache = this.metadataProvider.useCache() && meta.path && this.cache.get(meta.className + extname(meta.path));
|
|
288
291
|
if (cache) {
|
|
289
292
|
this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
@@ -336,11 +339,8 @@ export class MetadataDiscovery {
|
|
|
336
339
|
}
|
|
337
340
|
}
|
|
338
341
|
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
342
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
343
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner
|
|
343
|
+
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
|
|
344
344
|
}
|
|
345
345
|
return Utils.defaultValue(prop, 'nullable', prop.optional);
|
|
346
346
|
}
|
|
@@ -618,7 +618,7 @@ export class MetadataDiscovery {
|
|
|
618
618
|
}
|
|
619
619
|
data.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.className, targetType + '_inverse', true, meta.className === targetType);
|
|
620
620
|
data.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetType, meta.name + '_owner', false, meta.className === targetType);
|
|
621
|
-
return this.metadata.set(data.className, data);
|
|
621
|
+
return this.metadata.set(data.className, EntitySchema.fromMetadata(data).init().meta);
|
|
622
622
|
}
|
|
623
623
|
defineFixedOrderProperty(prop, targetType) {
|
|
624
624
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
@@ -653,6 +653,7 @@ export class MetadataDiscovery {
|
|
|
653
653
|
autoincrement: false,
|
|
654
654
|
updateRule: prop.updateRule,
|
|
655
655
|
deleteRule: prop.deleteRule,
|
|
656
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
656
657
|
};
|
|
657
658
|
if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
|
|
658
659
|
ret.updateRule ??= 'no action';
|
|
@@ -765,6 +766,7 @@ export class MetadataDiscovery {
|
|
|
765
766
|
delete prop.default;
|
|
766
767
|
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
767
768
|
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
769
|
+
properties[prop.name].runtimeType = 'any';
|
|
768
770
|
return properties[prop.name];
|
|
769
771
|
}
|
|
770
772
|
return properties[prop.name] = prop;
|
|
@@ -819,9 +821,16 @@ export class MetadataDiscovery {
|
|
|
819
821
|
}
|
|
820
822
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
821
823
|
};
|
|
824
|
+
const isParentArray = (prop) => {
|
|
825
|
+
if (prop.array) {
|
|
826
|
+
return true;
|
|
827
|
+
}
|
|
828
|
+
return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
|
|
829
|
+
};
|
|
822
830
|
const rootProperty = getRootProperty(embeddedProp);
|
|
823
831
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
824
832
|
const object = isParentObject(embeddedProp);
|
|
833
|
+
const array = isParentArray(embeddedProp);
|
|
825
834
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
826
835
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
827
836
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
@@ -834,7 +843,8 @@ export class MetadataDiscovery {
|
|
|
834
843
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
835
844
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
836
845
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
837
|
-
|
|
846
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
847
|
+
if (embeddedProp.nullable || refInArray) {
|
|
838
848
|
meta.properties[name].nullable = true;
|
|
839
849
|
}
|
|
840
850
|
if (meta.properties[name].fieldNames) {
|
|
@@ -872,6 +882,7 @@ export class MetadataDiscovery {
|
|
|
872
882
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
873
883
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
874
884
|
meta.properties[name].object = true;
|
|
885
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
875
886
|
}
|
|
876
887
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
877
888
|
}
|
|
@@ -905,11 +916,13 @@ export class MetadataDiscovery {
|
|
|
905
916
|
}
|
|
906
917
|
if (!meta.root.discriminatorMap) {
|
|
907
918
|
meta.root.discriminatorMap = {};
|
|
908
|
-
const children = metadata
|
|
909
|
-
|
|
919
|
+
const children = metadata
|
|
920
|
+
.filter(m => m.root.className === meta.root.className && !m.abstract)
|
|
921
|
+
.sort((a, b) => a.className.localeCompare(b.className));
|
|
922
|
+
for (const m of children) {
|
|
910
923
|
const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
|
|
911
924
|
meta.root.discriminatorMap[name] = m.className;
|
|
912
|
-
}
|
|
925
|
+
}
|
|
913
926
|
}
|
|
914
927
|
meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, className]) => className === meta.className)?.[0];
|
|
915
928
|
if (!meta.root.properties[meta.root.discriminatorColumn]) {
|
|
@@ -922,7 +935,7 @@ export class MetadataDiscovery {
|
|
|
922
935
|
}
|
|
923
936
|
let i = 1;
|
|
924
937
|
Object.values(meta.properties).forEach(prop => {
|
|
925
|
-
const newProp =
|
|
938
|
+
const newProp = { ...prop };
|
|
926
939
|
if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
|
|
927
940
|
const name = newProp.name;
|
|
928
941
|
this.initFieldName(newProp, newProp.object);
|
|
@@ -938,12 +951,13 @@ export class MetadataDiscovery {
|
|
|
938
951
|
newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
|
|
939
952
|
}
|
|
940
953
|
newProp.nullable = true;
|
|
941
|
-
newProp.inherited =
|
|
954
|
+
newProp.inherited = !meta.root.properties[prop.name];
|
|
942
955
|
meta.root.addProperty(newProp);
|
|
943
956
|
});
|
|
944
957
|
meta.collection = meta.root.collection;
|
|
945
958
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
946
959
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
960
|
+
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
947
961
|
}
|
|
948
962
|
createDiscriminatorProperty(meta) {
|
|
949
963
|
meta.addProperty({
|
|
@@ -962,7 +976,7 @@ export class MetadataDiscovery {
|
|
|
962
976
|
}
|
|
963
977
|
}
|
|
964
978
|
initCheckConstraints(meta) {
|
|
965
|
-
const map =
|
|
979
|
+
const map = meta.createColumnMappingObject();
|
|
966
980
|
for (const check of meta.checks) {
|
|
967
981
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
968
982
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -997,20 +1011,12 @@ export class MetadataDiscovery {
|
|
|
997
1011
|
}
|
|
998
1012
|
return;
|
|
999
1013
|
}
|
|
1000
|
-
const map =
|
|
1014
|
+
const map = meta.createColumnMappingObject();
|
|
1001
1015
|
if (prop.generated instanceof Function) {
|
|
1002
1016
|
prop.generated = prop.generated(map);
|
|
1003
1017
|
}
|
|
1004
1018
|
}
|
|
1005
|
-
|
|
1006
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1007
|
-
if (prop.fieldNames) {
|
|
1008
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1009
|
-
}
|
|
1010
|
-
return o;
|
|
1011
|
-
}, {});
|
|
1012
|
-
}
|
|
1013
|
-
getDefaultVersionValue(prop) {
|
|
1019
|
+
getDefaultVersionValue(meta, prop) {
|
|
1014
1020
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1015
1021
|
return prop.defaultRaw;
|
|
1016
1022
|
}
|
|
@@ -1018,14 +1024,15 @@ export class MetadataDiscovery {
|
|
|
1018
1024
|
if (prop.default != null) {
|
|
1019
1025
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1020
1026
|
}
|
|
1021
|
-
|
|
1027
|
+
this.initCustomType(meta, prop, true);
|
|
1028
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1029
|
+
if (type === 'Date') {
|
|
1022
1030
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1023
1031
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1024
1032
|
}
|
|
1025
1033
|
return '1';
|
|
1026
1034
|
}
|
|
1027
1035
|
inferDefaultValue(meta, prop) {
|
|
1028
|
-
/* v8 ignore next 3 */
|
|
1029
1036
|
if (!meta.class) {
|
|
1030
1037
|
return;
|
|
1031
1038
|
}
|
|
@@ -1061,7 +1068,7 @@ export class MetadataDiscovery {
|
|
|
1061
1068
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1062
1069
|
return;
|
|
1063
1070
|
}
|
|
1064
|
-
if (
|
|
1071
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1065
1072
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1066
1073
|
}
|
|
1067
1074
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1089,13 +1096,13 @@ export class MetadataDiscovery {
|
|
|
1089
1096
|
if (prop.version) {
|
|
1090
1097
|
this.initDefaultValue(prop);
|
|
1091
1098
|
meta.versionProperty = prop.name;
|
|
1092
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1099
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1093
1100
|
}
|
|
1094
1101
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1095
1102
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1096
1103
|
}
|
|
1097
1104
|
}
|
|
1098
|
-
initCustomType(meta, prop) {
|
|
1105
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1099
1106
|
// `prop.type` might be actually instance of custom type class
|
|
1100
1107
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1101
1108
|
prop.customType = prop.type;
|
|
@@ -1106,44 +1113,57 @@ export class MetadataDiscovery {
|
|
|
1106
1113
|
prop.customType = new prop.type();
|
|
1107
1114
|
prop.type = prop.customType.constructor.name;
|
|
1108
1115
|
}
|
|
1116
|
+
if (simple) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1109
1119
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1110
|
-
prop.customType = new
|
|
1120
|
+
prop.customType = new t.json();
|
|
1111
1121
|
}
|
|
1112
1122
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1113
|
-
prop.customType = new
|
|
1123
|
+
prop.customType = new t.json();
|
|
1124
|
+
}
|
|
1125
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1126
|
+
prop.customType = new t.json();
|
|
1114
1127
|
}
|
|
1115
1128
|
if (!prop.customType && prop.array && prop.items) {
|
|
1116
|
-
prop.customType = new
|
|
1129
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1130
|
+
}
|
|
1131
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1132
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1133
|
+
prop.customType = new t.json();
|
|
1117
1134
|
}
|
|
1118
1135
|
// for number arrays we make sure to convert the items to numbers
|
|
1119
1136
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1120
|
-
prop.customType = new
|
|
1137
|
+
prop.customType = new t.array(i => +i);
|
|
1121
1138
|
}
|
|
1122
1139
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1123
|
-
if (!prop.customType &&
|
|
1124
|
-
prop.customType = new
|
|
1140
|
+
if (!prop.customType && isArray) {
|
|
1141
|
+
prop.customType = new t.array();
|
|
1125
1142
|
}
|
|
1126
1143
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1127
|
-
prop.customType = new
|
|
1144
|
+
prop.customType = new t.blob();
|
|
1128
1145
|
}
|
|
1129
1146
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1130
|
-
prop.customType = new
|
|
1147
|
+
prop.customType = new t.uint8array();
|
|
1131
1148
|
}
|
|
1132
1149
|
const mappedType = this.getMappedType(prop);
|
|
1133
1150
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1134
|
-
[
|
|
1151
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1135
1152
|
.filter(type => mappedType instanceof type)
|
|
1136
|
-
.forEach(type => prop.customType = new type());
|
|
1153
|
+
.forEach((type) => prop.customType = new type());
|
|
1137
1154
|
}
|
|
1138
1155
|
if (prop.customType && !prop.columnTypes) {
|
|
1139
1156
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1140
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1157
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1141
1158
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1142
1159
|
}
|
|
1143
1160
|
else {
|
|
1144
1161
|
prop.runtimeType ??= prop.customType.runtimeType;
|
|
1145
1162
|
}
|
|
1146
1163
|
}
|
|
1164
|
+
else if (prop.runtimeType === 'object') {
|
|
1165
|
+
prop.runtimeType = mappedType.runtimeType;
|
|
1166
|
+
}
|
|
1147
1167
|
else {
|
|
1148
1168
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1149
1169
|
}
|
|
@@ -1154,11 +1174,11 @@ export class MetadataDiscovery {
|
|
|
1154
1174
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1155
1175
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1156
1176
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1157
|
-
if (prop.customType instanceof
|
|
1177
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1158
1178
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1159
1179
|
}
|
|
1160
1180
|
}
|
|
1161
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1181
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1162
1182
|
prop.type = prop.customType.name;
|
|
1163
1183
|
}
|
|
1164
1184
|
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
|
|
@@ -1175,7 +1195,7 @@ export class MetadataDiscovery {
|
|
|
1175
1195
|
}
|
|
1176
1196
|
}
|
|
1177
1197
|
}
|
|
1178
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1198
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1179
1199
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1180
1200
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1181
1201
|
}
|
|
@@ -1218,7 +1238,7 @@ export class MetadataDiscovery {
|
|
|
1218
1238
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1219
1239
|
const mappedType = this.getMappedType(prop);
|
|
1220
1240
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1221
|
-
if (mappedType instanceof
|
|
1241
|
+
if (mappedType instanceof t.unknown
|
|
1222
1242
|
&& !prop.columnTypes
|
|
1223
1243
|
// it could be a runtime type from reflect-metadata
|
|
1224
1244
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
@@ -1301,29 +1321,26 @@ export class MetadataDiscovery {
|
|
|
1301
1321
|
prop.index ??= true;
|
|
1302
1322
|
}
|
|
1303
1323
|
}
|
|
1304
|
-
async getEntityClassOrSchema(
|
|
1324
|
+
async getEntityClassOrSchema(filepath, allTargets) {
|
|
1325
|
+
const path = Utils.normalizePath(this.config.get('baseDir'), filepath);
|
|
1305
1326
|
const exports = await Utils.dynamicImport(path);
|
|
1306
|
-
const targets = Object.values(exports)
|
|
1307
|
-
.filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
|
|
1327
|
+
const targets = Object.values(exports);
|
|
1308
1328
|
// ignore class implementations that are linked from an EntitySchema
|
|
1309
1329
|
for (const item of targets) {
|
|
1310
1330
|
if (item instanceof EntitySchema) {
|
|
1311
|
-
|
|
1331
|
+
for (const item2 of targets) {
|
|
1312
1332
|
if (item.meta.class === item2) {
|
|
1313
|
-
targets.splice(
|
|
1333
|
+
targets.splice(targets.indexOf(item2), 1);
|
|
1314
1334
|
}
|
|
1315
|
-
}
|
|
1335
|
+
}
|
|
1316
1336
|
}
|
|
1317
1337
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
if (!target) {
|
|
1324
|
-
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
|
|
1338
|
+
for (const item of targets) {
|
|
1339
|
+
const validTarget = item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name));
|
|
1340
|
+
if (validTarget && !allTargets.has(item)) {
|
|
1341
|
+
allTargets.set(item, path);
|
|
1342
|
+
}
|
|
1325
1343
|
}
|
|
1326
|
-
return [target];
|
|
1327
1344
|
}
|
|
1328
1345
|
shouldForceConstructorUsage(meta) {
|
|
1329
1346
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
@@ -10,7 +10,7 @@ export class MetadataStorage {
|
|
|
10
10
|
this.metadata = Utils.copy(metadata, false);
|
|
11
11
|
}
|
|
12
12
|
static getMetadata(entity, path) {
|
|
13
|
-
const key = entity && path ? entity + '-' + Utils.hash(path) : null;
|
|
13
|
+
const key = entity && path ? entity + '-' + Utils.hash(path, undefined, 'sha256') : null;
|
|
14
14
|
if (key && !MetadataStorage.metadata[key]) {
|
|
15
15
|
MetadataStorage.metadata[key] = new EntityMetadata({ className: entity, path });
|
|
16
16
|
}
|
|
@@ -100,14 +100,15 @@ export class MetadataValidator {
|
|
|
100
100
|
if (!prop.type) {
|
|
101
101
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
102
102
|
}
|
|
103
|
+
const targetMeta = metadata.find(prop.type);
|
|
103
104
|
// references do have type of known entity
|
|
104
|
-
if (!
|
|
105
|
+
if (!targetMeta) {
|
|
105
106
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
106
107
|
}
|
|
107
|
-
if (
|
|
108
|
+
if (targetMeta.abstract && !targetMeta.discriminatorColumn && !targetMeta.embeddable) {
|
|
108
109
|
throw MetadataError.targetIsAbstract(meta, prop);
|
|
109
110
|
}
|
|
110
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false &&
|
|
111
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false && targetMeta.compositePK && options.checkNonPersistentCompositeProps) {
|
|
111
112
|
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -12,7 +12,11 @@ export declare abstract class AbstractNamingStrategy implements NamingStrategy {
|
|
|
12
12
|
/**
|
|
13
13
|
* @inheritDoc
|
|
14
14
|
*/
|
|
15
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
15
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* @inheritDoc
|
|
18
|
+
*/
|
|
19
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
16
20
|
/**
|
|
17
21
|
* @inheritDoc
|
|
18
22
|
*/
|
|
@@ -48,7 +48,13 @@ export class AbstractNamingStrategy {
|
|
|
48
48
|
* @inheritDoc
|
|
49
49
|
*/
|
|
50
50
|
getEnumClassName(columnName, tableName, schemaName) {
|
|
51
|
-
return this.getEntityName(`${tableName}_${columnName}
|
|
51
|
+
return this.getEntityName(tableName ? `${tableName}_${columnName}` : columnName, schemaName);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* @inheritDoc
|
|
55
|
+
*/
|
|
56
|
+
getEnumTypeName(columnName, tableName, schemaName) {
|
|
57
|
+
return 'T' + this.getEnumClassName(columnName, tableName, schemaName);
|
|
52
58
|
}
|
|
53
59
|
/**
|
|
54
60
|
* @inheritDoc
|
|
@@ -25,7 +25,17 @@ export interface NamingStrategy {
|
|
|
25
25
|
*
|
|
26
26
|
* @return A new class name that will be used for the enum.
|
|
27
27
|
*/
|
|
28
|
-
getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
|
|
28
|
+
getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Get an enum type name. Used with `enumType: 'dictionary'` and `enumType: 'union-type'` entity generator option.
|
|
31
|
+
*
|
|
32
|
+
* @param columnName The column name which has the enum.
|
|
33
|
+
* @param tableName The table name of the column.
|
|
34
|
+
* @param schemaName The schema name of the column.
|
|
35
|
+
*
|
|
36
|
+
* @return A new type name that will be used for the enum.
|
|
37
|
+
*/
|
|
38
|
+
getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
|
|
29
39
|
/**
|
|
30
40
|
* Get an enum option name for a given enum value.
|
|
31
41
|
*
|