@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.61
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EntityManager.d.ts +85 -32
- package/EntityManager.js +281 -178
- package/MikroORM.d.ts +8 -8
- package/MikroORM.js +31 -74
- package/README.md +3 -2
- package/cache/FileCacheAdapter.d.ts +2 -1
- package/cache/FileCacheAdapter.js +5 -4
- package/connections/Connection.d.ts +11 -7
- package/connections/Connection.js +16 -13
- package/decorators/Embeddable.d.ts +2 -0
- package/decorators/Embedded.d.ts +5 -11
- package/decorators/Entity.d.ts +20 -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 +3 -1
- package/decorators/Transactional.js +6 -3
- package/decorators/index.d.ts +1 -1
- package/drivers/DatabaseDriver.d.ts +11 -5
- package/drivers/DatabaseDriver.js +13 -4
- package/drivers/IDatabaseDriver.d.ts +29 -5
- package/entity/ArrayCollection.d.ts +6 -4
- package/entity/ArrayCollection.js +27 -12
- package/entity/BaseEntity.d.ts +0 -1
- package/entity/BaseEntity.js +0 -3
- package/entity/Collection.d.ts +3 -4
- package/entity/Collection.js +34 -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 +63 -40
- package/entity/EntityHelper.js +26 -9
- package/entity/EntityLoader.d.ts +5 -4
- package/entity/EntityLoader.js +69 -36
- package/entity/EntityRepository.d.ts +1 -1
- package/entity/EntityValidator.js +2 -2
- package/entity/Reference.d.ts +9 -7
- package/entity/Reference.js +32 -5
- 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 +18 -5
- 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 +41 -23
- package/metadata/MetadataDiscovery.d.ts +5 -7
- package/metadata/MetadataDiscovery.js +151 -159
- package/metadata/MetadataStorage.js +1 -1
- package/metadata/MetadataValidator.js +4 -3
- package/metadata/discover-entities.d.ts +5 -0
- package/metadata/discover-entities.js +39 -0
- 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 +14 -8
- package/platforms/Platform.d.ts +5 -8
- package/platforms/Platform.js +4 -17
- package/serialization/EntitySerializer.d.ts +2 -0
- package/serialization/EntitySerializer.js +29 -11
- package/serialization/EntityTransformer.js +22 -12
- package/serialization/SerializationContext.js +14 -11
- package/types/BigIntType.d.ts +9 -6
- package/types/BigIntType.js +3 -0
- 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 +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/Uint8ArrayType.d.ts +0 -1
- package/types/Uint8ArrayType.js +0 -3
- package/types/index.d.ts +1 -1
- package/typings.d.ts +95 -52
- 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 +201 -184
- package/utils/Configuration.js +143 -151
- package/utils/ConfigurationLoader.d.ts +9 -22
- package/utils/ConfigurationLoader.js +53 -76
- package/utils/Cursor.d.ts +3 -3
- package/utils/Cursor.js +3 -0
- package/utils/DataloaderUtils.d.ts +15 -5
- package/utils/DataloaderUtils.js +53 -7
- package/utils/EntityComparator.d.ts +8 -4
- package/utils/EntityComparator.js +105 -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 +16 -31
- package/utils/Utils.js +129 -107
- 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,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import { EntityMetadata, } from '../typings.js';
|
|
1
|
+
import { extname } from 'node:path';
|
|
2
|
+
import { EntityMetadata } from '../typings.js';
|
|
4
3
|
import { Utils } from '../utils/Utils.js';
|
|
5
4
|
import { MetadataValidator } from './MetadataValidator.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
11
|
import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
13
12
|
export class MetadataDiscovery {
|
|
@@ -32,6 +31,7 @@ export class MetadataDiscovery {
|
|
|
32
31
|
this.schemaHelper = this.platform.getSchemaHelper();
|
|
33
32
|
}
|
|
34
33
|
async discover(preferTs = true) {
|
|
34
|
+
this.discovered.length = 0;
|
|
35
35
|
const startTime = Date.now();
|
|
36
36
|
this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)}`);
|
|
37
37
|
await this.findEntities(preferTs);
|
|
@@ -47,10 +47,12 @@ 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
53
|
this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)} in sync mode`);
|
|
53
|
-
this.
|
|
54
|
+
const refs = this.config.get('entities');
|
|
55
|
+
this.discoverReferences(refs);
|
|
54
56
|
for (const meta of this.discovered) {
|
|
55
57
|
/* v8 ignore next */
|
|
56
58
|
void this.config.get('discovery').onMetadata?.(meta, this.platform);
|
|
@@ -63,6 +65,9 @@ export class MetadataDiscovery {
|
|
|
63
65
|
void this.config.get('discovery').afterDiscovered?.(storage, this.platform);
|
|
64
66
|
return storage;
|
|
65
67
|
}
|
|
68
|
+
validateDiscovered(metadata) {
|
|
69
|
+
return this.validator.validateDiscovered(metadata, this.config.get('discovery'));
|
|
70
|
+
}
|
|
66
71
|
mapDiscoveredEntities() {
|
|
67
72
|
const discovered = new MetadataStorage();
|
|
68
73
|
this.discovered
|
|
@@ -74,11 +79,41 @@ export class MetadataDiscovery {
|
|
|
74
79
|
});
|
|
75
80
|
return discovered;
|
|
76
81
|
}
|
|
82
|
+
initAccessors(meta) {
|
|
83
|
+
for (const prop of Object.values(meta.properties)) {
|
|
84
|
+
if (!prop.accessor || meta.properties[prop.accessor]) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
|
|
88
|
+
if (desc?.get || desc?.set) {
|
|
89
|
+
this.initFieldName(prop);
|
|
90
|
+
const accessor = prop.name;
|
|
91
|
+
prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
|
|
92
|
+
if (prop.accessor === true) {
|
|
93
|
+
prop.getter = prop.setter = true;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
prop.getter = prop.setter = false;
|
|
97
|
+
}
|
|
98
|
+
prop.accessor = accessor;
|
|
99
|
+
prop.serializedName ??= accessor;
|
|
100
|
+
Utils.renameKey(meta.properties, accessor, prop.name);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
const name = prop.name;
|
|
104
|
+
prop.name = prop.accessor;
|
|
105
|
+
this.initFieldName(prop);
|
|
106
|
+
prop.serializedName ??= prop.accessor;
|
|
107
|
+
prop.name = name;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
77
111
|
processDiscoveredEntities(discovered) {
|
|
78
112
|
for (const meta of discovered) {
|
|
79
113
|
let i = 1;
|
|
80
114
|
Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
|
|
81
115
|
Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
|
|
116
|
+
this.initAccessors(meta);
|
|
82
117
|
}
|
|
83
118
|
// ignore base entities (not annotated with @Entity)
|
|
84
119
|
const filtered = discovered.filter(meta => meta.root.name);
|
|
@@ -101,10 +136,6 @@ export class MetadataDiscovery {
|
|
|
101
136
|
this.initDefaultValue(prop);
|
|
102
137
|
this.inferTypeFromDefault(prop);
|
|
103
138
|
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
139
|
}
|
|
109
140
|
}
|
|
110
141
|
filtered.forEach(meta => Object.values(meta.properties).forEach(prop => this.initIndexes(meta, prop)));
|
|
@@ -125,27 +156,23 @@ export class MetadataDiscovery {
|
|
|
125
156
|
return meta;
|
|
126
157
|
});
|
|
127
158
|
}
|
|
128
|
-
findEntities(preferTs
|
|
129
|
-
this.
|
|
130
|
-
const
|
|
131
|
-
const
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
159
|
+
async findEntities(preferTs) {
|
|
160
|
+
const { entities, entitiesTs, baseDir } = this.config.getAll();
|
|
161
|
+
const targets = (preferTs && entitiesTs.length > 0) ? entitiesTs : entities;
|
|
162
|
+
const processed = [];
|
|
163
|
+
for (const entity of targets) {
|
|
164
|
+
if (typeof entity === 'string') {
|
|
165
|
+
if (this.config.get('discovery').requireEntitiesArray) {
|
|
166
|
+
throw new Error(`[requireEntitiesArray] Explicit list of entities is required, please use the 'entities' option.`);
|
|
167
|
+
}
|
|
168
|
+
const { discoverEntities } = await import('@mikro-orm/core/file-discovery' + '');
|
|
169
|
+
processed.push(...await discoverEntities(entity, { baseDir }));
|
|
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
|
-
this.discoverReferences(
|
|
146
|
-
this.discoverMissingTargets();
|
|
147
|
-
this.validator.validateDiscovered(this.discovered, options);
|
|
148
|
-
return this.discovered;
|
|
175
|
+
return this.discoverReferences(processed);
|
|
149
176
|
}
|
|
150
177
|
discoverMissingTargets() {
|
|
151
178
|
const unwrap = (type) => type
|
|
@@ -173,50 +200,20 @@ export class MetadataDiscovery {
|
|
|
173
200
|
}
|
|
174
201
|
tryDiscoverTargets(targets) {
|
|
175
202
|
for (const target of targets) {
|
|
176
|
-
|
|
177
|
-
|
|
203
|
+
const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
|
|
204
|
+
if (isDiscoverable && target.name && !this.metadata.has(target.name)) {
|
|
205
|
+
this.discoverReferences([target], false);
|
|
178
206
|
this.discoverMissingTargets();
|
|
179
207
|
}
|
|
180
208
|
}
|
|
181
209
|
}
|
|
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) {
|
|
210
|
+
discoverReferences(refs, validate = true) {
|
|
217
211
|
const found = [];
|
|
218
212
|
for (const entity of refs) {
|
|
219
|
-
|
|
213
|
+
if (typeof entity === 'string') {
|
|
214
|
+
throw new Error('Folder based discovery requires the async `MikroORM.init()` method.');
|
|
215
|
+
}
|
|
216
|
+
const schema = this.getSchema(entity);
|
|
220
217
|
const meta = schema.init().meta;
|
|
221
218
|
this.metadata.set(meta.className, meta);
|
|
222
219
|
found.push(schema);
|
|
@@ -225,7 +222,10 @@ export class MetadataDiscovery {
|
|
|
225
222
|
for (const meta of this.metadata) {
|
|
226
223
|
let parent = meta.extends;
|
|
227
224
|
if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.className)) {
|
|
228
|
-
this.discoverReferences([parent]);
|
|
225
|
+
this.discoverReferences([parent], false);
|
|
226
|
+
}
|
|
227
|
+
if (typeof parent === 'function' && parent.name && !this.metadata.has(parent.name)) {
|
|
228
|
+
this.discoverReferences([parent], false);
|
|
229
229
|
}
|
|
230
230
|
/* v8 ignore next 3 */
|
|
231
231
|
if (!meta.class) {
|
|
@@ -233,12 +233,16 @@ export class MetadataDiscovery {
|
|
|
233
233
|
}
|
|
234
234
|
parent = Object.getPrototypeOf(meta.class);
|
|
235
235
|
if (parent.name !== '' && !this.metadata.has(parent.name)) {
|
|
236
|
-
this.discoverReferences([parent]);
|
|
236
|
+
this.discoverReferences([parent], false);
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
for (const schema of found) {
|
|
240
240
|
this.discoverEntity(schema);
|
|
241
241
|
}
|
|
242
|
+
this.discoverMissingTargets();
|
|
243
|
+
if (validate) {
|
|
244
|
+
this.validateDiscovered(this.discovered);
|
|
245
|
+
}
|
|
242
246
|
return this.discovered.filter(meta => found.find(m => m.name === meta.className));
|
|
243
247
|
}
|
|
244
248
|
reset(className) {
|
|
@@ -248,23 +252,13 @@ export class MetadataDiscovery {
|
|
|
248
252
|
this.discovered.splice(exists, 1);
|
|
249
253
|
}
|
|
250
254
|
}
|
|
251
|
-
|
|
252
|
-
/* v8 ignore next 3 */
|
|
253
|
-
if ('schema' in entity && entity.schema instanceof EntitySchema) {
|
|
254
|
-
return entity.schema;
|
|
255
|
-
}
|
|
255
|
+
getSchema(entity) {
|
|
256
256
|
if (EntitySchema.REGISTRY.has(entity)) {
|
|
257
|
-
|
|
257
|
+
entity = EntitySchema.REGISTRY.get(entity);
|
|
258
258
|
}
|
|
259
|
-
return entity;
|
|
260
|
-
}
|
|
261
|
-
getSchema(entity, filepath) {
|
|
262
259
|
if (entity instanceof EntitySchema) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
MetadataStorage.getMetadata(entity.meta.className, filepath);
|
|
266
|
-
}
|
|
267
|
-
return entity;
|
|
260
|
+
const meta = Utils.copy(entity.meta, false);
|
|
261
|
+
return EntitySchema.fromMetadata(meta);
|
|
268
262
|
}
|
|
269
263
|
const path = entity[MetadataStorage.PATH_SYMBOL];
|
|
270
264
|
if (path) {
|
|
@@ -279,11 +273,12 @@ export class MetadataDiscovery {
|
|
|
279
273
|
schema.setClass(entity);
|
|
280
274
|
return schema;
|
|
281
275
|
}
|
|
282
|
-
discoverEntity(schema
|
|
283
|
-
this.logger.log('discovery', `- processing entity ${colors.cyan(schema.meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
276
|
+
discoverEntity(schema) {
|
|
284
277
|
const meta = schema.meta;
|
|
278
|
+
const path = meta.path;
|
|
279
|
+
this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
|
|
285
280
|
const root = Utils.getRootEntity(this.metadata, meta);
|
|
286
|
-
schema.meta.path = Utils.relativePath(
|
|
281
|
+
schema.meta.path = Utils.relativePath(meta.path, this.config.get('baseDir'));
|
|
287
282
|
const cache = this.metadataProvider.useCache() && meta.path && this.cache.get(meta.className + extname(meta.path));
|
|
288
283
|
if (cache) {
|
|
289
284
|
this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
|
|
@@ -336,11 +331,8 @@ export class MetadataDiscovery {
|
|
|
336
331
|
}
|
|
337
332
|
}
|
|
338
333
|
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
334
|
if (prop.kind === ReferenceKind.ONE_TO_ONE) {
|
|
343
|
-
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner
|
|
335
|
+
return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
|
|
344
336
|
}
|
|
345
337
|
return Utils.defaultValue(prop, 'nullable', prop.optional);
|
|
346
338
|
}
|
|
@@ -497,10 +489,9 @@ export class MetadataDiscovery {
|
|
|
497
489
|
}
|
|
498
490
|
this.initOwnColumns(meta);
|
|
499
491
|
meta.simplePK = pks.length === 1 && pks[0].kind === ReferenceKind.SCALAR && !pks[0].customType && pks[0].runtimeType !== 'Date';
|
|
500
|
-
meta.serializedPrimaryKey
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
serializedPKProp.persist = false;
|
|
492
|
+
meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
|
|
493
|
+
if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
|
|
494
|
+
meta.properties[meta.serializedPrimaryKey].persist ??= false;
|
|
504
495
|
}
|
|
505
496
|
if (this.platform.usesPivotTable()) {
|
|
506
497
|
return Object.values(meta.properties)
|
|
@@ -618,7 +609,7 @@ export class MetadataDiscovery {
|
|
|
618
609
|
}
|
|
619
610
|
data.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.className, targetType + '_inverse', true, meta.className === targetType);
|
|
620
611
|
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);
|
|
612
|
+
return this.metadata.set(data.className, EntitySchema.fromMetadata(data).init().meta);
|
|
622
613
|
}
|
|
623
614
|
defineFixedOrderProperty(prop, targetType) {
|
|
624
615
|
const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
|
|
@@ -653,6 +644,7 @@ export class MetadataDiscovery {
|
|
|
653
644
|
autoincrement: false,
|
|
654
645
|
updateRule: prop.updateRule,
|
|
655
646
|
deleteRule: prop.deleteRule,
|
|
647
|
+
createForeignKeyConstraint: prop.createForeignKeyConstraint,
|
|
656
648
|
};
|
|
657
649
|
if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
|
|
658
650
|
ret.updateRule ??= 'no action';
|
|
@@ -739,12 +731,9 @@ export class MetadataDiscovery {
|
|
|
739
731
|
Utils.keys(base.hooks).forEach(type => {
|
|
740
732
|
meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
|
|
741
733
|
});
|
|
742
|
-
if (meta.constructorParams
|
|
734
|
+
if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
|
|
743
735
|
meta.constructorParams = [...base.constructorParams];
|
|
744
736
|
}
|
|
745
|
-
if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
|
|
746
|
-
meta.toJsonParams = [...base.toJsonParams];
|
|
747
|
-
}
|
|
748
737
|
return order;
|
|
749
738
|
}
|
|
750
739
|
initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
|
|
@@ -765,6 +754,7 @@ export class MetadataDiscovery {
|
|
|
765
754
|
delete prop.default;
|
|
766
755
|
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
767
756
|
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
757
|
+
properties[prop.name].runtimeType = 'any';
|
|
768
758
|
return properties[prop.name];
|
|
769
759
|
}
|
|
770
760
|
return properties[prop.name] = prop;
|
|
@@ -819,9 +809,16 @@ export class MetadataDiscovery {
|
|
|
819
809
|
}
|
|
820
810
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
821
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
|
+
};
|
|
822
818
|
const rootProperty = getRootProperty(embeddedProp);
|
|
823
819
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
824
820
|
const object = isParentObject(embeddedProp);
|
|
821
|
+
const array = isParentArray(embeddedProp);
|
|
825
822
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
826
823
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
827
824
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
@@ -834,7 +831,8 @@ export class MetadataDiscovery {
|
|
|
834
831
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
835
832
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
836
833
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
837
|
-
|
|
834
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
835
|
+
if (embeddedProp.nullable || refInArray) {
|
|
838
836
|
meta.properties[name].nullable = true;
|
|
839
837
|
}
|
|
840
838
|
if (meta.properties[name].fieldNames) {
|
|
@@ -872,6 +870,7 @@ export class MetadataDiscovery {
|
|
|
872
870
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
873
871
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
874
872
|
meta.properties[name].object = true;
|
|
873
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
875
874
|
}
|
|
876
875
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
877
876
|
}
|
|
@@ -924,7 +923,7 @@ export class MetadataDiscovery {
|
|
|
924
923
|
}
|
|
925
924
|
let i = 1;
|
|
926
925
|
Object.values(meta.properties).forEach(prop => {
|
|
927
|
-
const newProp =
|
|
926
|
+
const newProp = { ...prop };
|
|
928
927
|
if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
|
|
929
928
|
const name = newProp.name;
|
|
930
929
|
this.initFieldName(newProp, newProp.object);
|
|
@@ -940,12 +939,13 @@ export class MetadataDiscovery {
|
|
|
940
939
|
newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
|
|
941
940
|
}
|
|
942
941
|
newProp.nullable = true;
|
|
943
|
-
newProp.inherited =
|
|
942
|
+
newProp.inherited = !meta.root.properties[prop.name];
|
|
944
943
|
meta.root.addProperty(newProp);
|
|
945
944
|
});
|
|
946
945
|
meta.collection = meta.root.collection;
|
|
947
946
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
948
947
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
948
|
+
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
949
949
|
}
|
|
950
950
|
createDiscriminatorProperty(meta) {
|
|
951
951
|
meta.addProperty({
|
|
@@ -964,7 +964,7 @@ export class MetadataDiscovery {
|
|
|
964
964
|
}
|
|
965
965
|
}
|
|
966
966
|
initCheckConstraints(meta) {
|
|
967
|
-
const map =
|
|
967
|
+
const map = meta.createColumnMappingObject();
|
|
968
968
|
for (const check of meta.checks) {
|
|
969
969
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
970
970
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -999,20 +999,12 @@ export class MetadataDiscovery {
|
|
|
999
999
|
}
|
|
1000
1000
|
return;
|
|
1001
1001
|
}
|
|
1002
|
-
const map =
|
|
1002
|
+
const map = meta.createColumnMappingObject();
|
|
1003
1003
|
if (prop.generated instanceof Function) {
|
|
1004
1004
|
prop.generated = prop.generated(map);
|
|
1005
1005
|
}
|
|
1006
1006
|
}
|
|
1007
|
-
|
|
1008
|
-
return Object.values(meta.properties).reduce((o, prop) => {
|
|
1009
|
-
if (prop.fieldNames) {
|
|
1010
|
-
o[prop.name] = prop.fieldNames[0];
|
|
1011
|
-
}
|
|
1012
|
-
return o;
|
|
1013
|
-
}, {});
|
|
1014
|
-
}
|
|
1015
|
-
getDefaultVersionValue(prop) {
|
|
1007
|
+
getDefaultVersionValue(meta, prop) {
|
|
1016
1008
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1017
1009
|
return prop.defaultRaw;
|
|
1018
1010
|
}
|
|
@@ -1020,14 +1012,15 @@ export class MetadataDiscovery {
|
|
|
1020
1012
|
if (prop.default != null) {
|
|
1021
1013
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1022
1014
|
}
|
|
1023
|
-
|
|
1015
|
+
this.initCustomType(meta, prop, true);
|
|
1016
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1017
|
+
if (type === 'Date') {
|
|
1024
1018
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1025
1019
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1026
1020
|
}
|
|
1027
1021
|
return '1';
|
|
1028
1022
|
}
|
|
1029
1023
|
inferDefaultValue(meta, prop) {
|
|
1030
|
-
/* v8 ignore next 3 */
|
|
1031
1024
|
if (!meta.class) {
|
|
1032
1025
|
return;
|
|
1033
1026
|
}
|
|
@@ -1063,7 +1056,7 @@ export class MetadataDiscovery {
|
|
|
1063
1056
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1064
1057
|
return;
|
|
1065
1058
|
}
|
|
1066
|
-
if (
|
|
1059
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1067
1060
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1068
1061
|
}
|
|
1069
1062
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1091,13 +1084,13 @@ export class MetadataDiscovery {
|
|
|
1091
1084
|
if (prop.version) {
|
|
1092
1085
|
this.initDefaultValue(prop);
|
|
1093
1086
|
meta.versionProperty = prop.name;
|
|
1094
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1087
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1095
1088
|
}
|
|
1096
1089
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1097
1090
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1098
1091
|
}
|
|
1099
1092
|
}
|
|
1100
|
-
initCustomType(meta, prop) {
|
|
1093
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1101
1094
|
// `prop.type` might be actually instance of custom type class
|
|
1102
1095
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1103
1096
|
prop.customType = prop.type;
|
|
@@ -1105,47 +1098,70 @@ export class MetadataDiscovery {
|
|
|
1105
1098
|
}
|
|
1106
1099
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1107
1100
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1101
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1102
|
+
// we use just the type name, to have more performant hydration code
|
|
1103
|
+
const type = Utils.keys(t).find(type => {
|
|
1104
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1105
|
+
});
|
|
1106
|
+
if (type) {
|
|
1107
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1108
|
+
}
|
|
1109
|
+
else {
|
|
1110
|
+
prop.customType = new prop.type();
|
|
1111
|
+
prop.type = prop.customType.constructor.name;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (simple) {
|
|
1115
|
+
return;
|
|
1110
1116
|
}
|
|
1111
1117
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1112
|
-
prop.customType = new
|
|
1118
|
+
prop.customType = new t.json();
|
|
1113
1119
|
}
|
|
1114
1120
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1115
|
-
prop.customType = new
|
|
1121
|
+
prop.customType = new t.json();
|
|
1122
|
+
}
|
|
1123
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1124
|
+
prop.customType = new t.json();
|
|
1116
1125
|
}
|
|
1117
1126
|
if (!prop.customType && prop.array && prop.items) {
|
|
1118
|
-
prop.customType = new
|
|
1127
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1128
|
+
}
|
|
1129
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1130
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1131
|
+
prop.customType = new t.json();
|
|
1119
1132
|
}
|
|
1120
1133
|
// for number arrays we make sure to convert the items to numbers
|
|
1121
1134
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1122
|
-
prop.customType = new
|
|
1135
|
+
prop.customType = new t.array(i => +i);
|
|
1123
1136
|
}
|
|
1124
1137
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1125
|
-
if (!prop.customType &&
|
|
1126
|
-
prop.customType = new
|
|
1138
|
+
if (!prop.customType && isArray) {
|
|
1139
|
+
prop.customType = new t.array();
|
|
1127
1140
|
}
|
|
1128
1141
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1129
|
-
prop.customType = new
|
|
1142
|
+
prop.customType = new t.blob();
|
|
1130
1143
|
}
|
|
1131
1144
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1132
|
-
prop.customType = new
|
|
1145
|
+
prop.customType = new t.uint8array();
|
|
1133
1146
|
}
|
|
1134
1147
|
const mappedType = this.getMappedType(prop);
|
|
1135
1148
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1136
|
-
[
|
|
1149
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1137
1150
|
.filter(type => mappedType instanceof type)
|
|
1138
|
-
.forEach(type => prop.customType = new type());
|
|
1151
|
+
.forEach((type) => prop.customType = new type());
|
|
1139
1152
|
}
|
|
1140
1153
|
if (prop.customType && !prop.columnTypes) {
|
|
1141
1154
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1142
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1155
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1143
1156
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1144
1157
|
}
|
|
1145
1158
|
else {
|
|
1146
1159
|
prop.runtimeType ??= prop.customType.runtimeType;
|
|
1147
1160
|
}
|
|
1148
1161
|
}
|
|
1162
|
+
else if (prop.runtimeType === 'object') {
|
|
1163
|
+
prop.runtimeType = mappedType.runtimeType;
|
|
1164
|
+
}
|
|
1149
1165
|
else {
|
|
1150
1166
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1151
1167
|
}
|
|
@@ -1156,11 +1172,11 @@ export class MetadataDiscovery {
|
|
|
1156
1172
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1157
1173
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1158
1174
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1159
|
-
if (prop.customType instanceof
|
|
1175
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1160
1176
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1161
1177
|
}
|
|
1162
1178
|
}
|
|
1163
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1179
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1164
1180
|
prop.type = prop.customType.name;
|
|
1165
1181
|
}
|
|
1166
1182
|
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
|
|
@@ -1177,7 +1193,7 @@ export class MetadataDiscovery {
|
|
|
1177
1193
|
}
|
|
1178
1194
|
}
|
|
1179
1195
|
}
|
|
1180
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1196
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1181
1197
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1182
1198
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1183
1199
|
}
|
|
@@ -1220,7 +1236,7 @@ export class MetadataDiscovery {
|
|
|
1220
1236
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1221
1237
|
const mappedType = this.getMappedType(prop);
|
|
1222
1238
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1223
|
-
if (mappedType instanceof
|
|
1239
|
+
if (mappedType instanceof t.unknown
|
|
1224
1240
|
&& !prop.columnTypes
|
|
1225
1241
|
// it could be a runtime type from reflect-metadata
|
|
1226
1242
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
@@ -1303,30 +1319,6 @@ export class MetadataDiscovery {
|
|
|
1303
1319
|
prop.index ??= true;
|
|
1304
1320
|
}
|
|
1305
1321
|
}
|
|
1306
|
-
async getEntityClassOrSchema(path, name) {
|
|
1307
|
-
const exports = await Utils.dynamicImport(path);
|
|
1308
|
-
const targets = Object.values(exports)
|
|
1309
|
-
.filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
|
|
1310
|
-
// ignore class implementations that are linked from an EntitySchema
|
|
1311
|
-
for (const item of targets) {
|
|
1312
|
-
if (item instanceof EntitySchema) {
|
|
1313
|
-
targets.forEach((item2, idx) => {
|
|
1314
|
-
if (item.meta.class === item2) {
|
|
1315
|
-
targets.splice(idx, 1);
|
|
1316
|
-
}
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
if (targets.length > 0) {
|
|
1321
|
-
return targets;
|
|
1322
|
-
}
|
|
1323
|
-
const target = exports.default ?? exports[name];
|
|
1324
|
-
/* v8 ignore next 3 */
|
|
1325
|
-
if (!target) {
|
|
1326
|
-
throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
|
|
1327
|
-
}
|
|
1328
|
-
return [target];
|
|
1329
|
-
}
|
|
1330
1322
|
shouldForceConstructorUsage(meta) {
|
|
1331
1323
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1332
1324
|
if (Array.isArray(forceConstructor)) {
|
|
@@ -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
|
}
|