@mikro-orm/core 7.0.0-dev.6 → 7.0.0-dev.60
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/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 +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 +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 +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 +40 -22
- 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 +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 +40 -20
- package/metadata/MetadataDiscovery.d.ts +5 -7
- package/metadata/MetadataDiscovery.js +150 -155
- 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 -7
- 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 +94 -50
- 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 +13 -12
- package/utils/Utils.js +106 -66
- 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';
|
|
@@ -765,6 +757,7 @@ export class MetadataDiscovery {
|
|
|
765
757
|
delete prop.default;
|
|
766
758
|
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
767
759
|
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
760
|
+
properties[prop.name].runtimeType = 'any';
|
|
768
761
|
return properties[prop.name];
|
|
769
762
|
}
|
|
770
763
|
return properties[prop.name] = prop;
|
|
@@ -819,9 +812,16 @@ export class MetadataDiscovery {
|
|
|
819
812
|
}
|
|
820
813
|
return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
|
|
821
814
|
};
|
|
815
|
+
const isParentArray = (prop) => {
|
|
816
|
+
if (prop.array) {
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
|
|
820
|
+
};
|
|
822
821
|
const rootProperty = getRootProperty(embeddedProp);
|
|
823
822
|
const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
|
|
824
823
|
const object = isParentObject(embeddedProp);
|
|
824
|
+
const array = isParentArray(embeddedProp);
|
|
825
825
|
this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
|
|
826
826
|
// the prefix of the parent cannot be a boolean; it already passed here
|
|
827
827
|
const prefix = this.getPrefix(embeddedProp, parentProperty);
|
|
@@ -834,7 +834,8 @@ export class MetadataDiscovery {
|
|
|
834
834
|
meta.propertyOrder.set(name, (order += 0.01));
|
|
835
835
|
embeddedProp.embeddedProps[prop.name] = meta.properties[name];
|
|
836
836
|
meta.properties[name].persist ??= embeddedProp.persist;
|
|
837
|
-
|
|
837
|
+
const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
|
|
838
|
+
if (embeddedProp.nullable || refInArray) {
|
|
838
839
|
meta.properties[name].nullable = true;
|
|
839
840
|
}
|
|
840
841
|
if (meta.properties[name].fieldNames) {
|
|
@@ -872,6 +873,7 @@ export class MetadataDiscovery {
|
|
|
872
873
|
meta.properties[name].persist = false; // only virtual as we store the whole object
|
|
873
874
|
meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
|
|
874
875
|
meta.properties[name].object = true;
|
|
876
|
+
this.initCustomType(meta, meta.properties[name], false, true);
|
|
875
877
|
}
|
|
876
878
|
this.initEmbeddables(meta, meta.properties[name], visited);
|
|
877
879
|
}
|
|
@@ -924,7 +926,7 @@ export class MetadataDiscovery {
|
|
|
924
926
|
}
|
|
925
927
|
let i = 1;
|
|
926
928
|
Object.values(meta.properties).forEach(prop => {
|
|
927
|
-
const newProp =
|
|
929
|
+
const newProp = { ...prop };
|
|
928
930
|
if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
|
|
929
931
|
const name = newProp.name;
|
|
930
932
|
this.initFieldName(newProp, newProp.object);
|
|
@@ -940,12 +942,13 @@ export class MetadataDiscovery {
|
|
|
940
942
|
newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
|
|
941
943
|
}
|
|
942
944
|
newProp.nullable = true;
|
|
943
|
-
newProp.inherited =
|
|
945
|
+
newProp.inherited = !meta.root.properties[prop.name];
|
|
944
946
|
meta.root.addProperty(newProp);
|
|
945
947
|
});
|
|
946
948
|
meta.collection = meta.root.collection;
|
|
947
949
|
meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
|
|
948
950
|
meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
|
|
951
|
+
meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
|
|
949
952
|
}
|
|
950
953
|
createDiscriminatorProperty(meta) {
|
|
951
954
|
meta.addProperty({
|
|
@@ -964,7 +967,7 @@ export class MetadataDiscovery {
|
|
|
964
967
|
}
|
|
965
968
|
}
|
|
966
969
|
initCheckConstraints(meta) {
|
|
967
|
-
const map =
|
|
970
|
+
const map = meta.createColumnMappingObject();
|
|
968
971
|
for (const check of meta.checks) {
|
|
969
972
|
const columns = check.property ? meta.properties[check.property].fieldNames : [];
|
|
970
973
|
check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
|
|
@@ -999,20 +1002,12 @@ export class MetadataDiscovery {
|
|
|
999
1002
|
}
|
|
1000
1003
|
return;
|
|
1001
1004
|
}
|
|
1002
|
-
const map =
|
|
1005
|
+
const map = meta.createColumnMappingObject();
|
|
1003
1006
|
if (prop.generated instanceof Function) {
|
|
1004
1007
|
prop.generated = prop.generated(map);
|
|
1005
1008
|
}
|
|
1006
1009
|
}
|
|
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) {
|
|
1010
|
+
getDefaultVersionValue(meta, prop) {
|
|
1016
1011
|
if (typeof prop.defaultRaw !== 'undefined') {
|
|
1017
1012
|
return prop.defaultRaw;
|
|
1018
1013
|
}
|
|
@@ -1020,14 +1015,15 @@ export class MetadataDiscovery {
|
|
|
1020
1015
|
if (prop.default != null) {
|
|
1021
1016
|
return '' + this.platform.quoteVersionValue(prop.default, prop);
|
|
1022
1017
|
}
|
|
1023
|
-
|
|
1018
|
+
this.initCustomType(meta, prop, true);
|
|
1019
|
+
const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
|
|
1020
|
+
if (type === 'Date') {
|
|
1024
1021
|
prop.length ??= this.platform.getDefaultVersionLength();
|
|
1025
1022
|
return this.platform.getCurrentTimestampSQL(prop.length);
|
|
1026
1023
|
}
|
|
1027
1024
|
return '1';
|
|
1028
1025
|
}
|
|
1029
1026
|
inferDefaultValue(meta, prop) {
|
|
1030
|
-
/* v8 ignore next 3 */
|
|
1031
1027
|
if (!meta.class) {
|
|
1032
1028
|
return;
|
|
1033
1029
|
}
|
|
@@ -1063,7 +1059,7 @@ export class MetadataDiscovery {
|
|
|
1063
1059
|
prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
|
|
1064
1060
|
return;
|
|
1065
1061
|
}
|
|
1066
|
-
if (
|
|
1062
|
+
if (Array.isArray(prop.default) && prop.customType) {
|
|
1067
1063
|
val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
|
|
1068
1064
|
}
|
|
1069
1065
|
prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
|
|
@@ -1091,13 +1087,13 @@ export class MetadataDiscovery {
|
|
|
1091
1087
|
if (prop.version) {
|
|
1092
1088
|
this.initDefaultValue(prop);
|
|
1093
1089
|
meta.versionProperty = prop.name;
|
|
1094
|
-
prop.defaultRaw = this.getDefaultVersionValue(prop);
|
|
1090
|
+
prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
|
|
1095
1091
|
}
|
|
1096
1092
|
if (prop.concurrencyCheck && !prop.primary) {
|
|
1097
1093
|
meta.concurrencyCheckKeys.add(prop.name);
|
|
1098
1094
|
}
|
|
1099
1095
|
}
|
|
1100
|
-
initCustomType(meta, prop) {
|
|
1096
|
+
initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
|
|
1101
1097
|
// `prop.type` might be actually instance of custom type class
|
|
1102
1098
|
if (Type.isMappedType(prop.type) && !prop.customType) {
|
|
1103
1099
|
prop.customType = prop.type;
|
|
@@ -1105,47 +1101,70 @@ export class MetadataDiscovery {
|
|
|
1105
1101
|
}
|
|
1106
1102
|
// `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
|
|
1107
1103
|
if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
|
|
1108
|
-
|
|
1109
|
-
|
|
1104
|
+
// if the type is an ORM defined mapped type without `ensureComparable: true`,
|
|
1105
|
+
// we use just the type name, to have more performant hydration code
|
|
1106
|
+
const type = Utils.keys(t).find(type => {
|
|
1107
|
+
return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
|
|
1108
|
+
});
|
|
1109
|
+
if (type) {
|
|
1110
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
prop.customType = new prop.type();
|
|
1114
|
+
prop.type = prop.customType.constructor.name;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
if (simple) {
|
|
1118
|
+
return;
|
|
1110
1119
|
}
|
|
1111
1120
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1112
|
-
prop.customType = new
|
|
1121
|
+
prop.customType = new t.json();
|
|
1113
1122
|
}
|
|
1114
1123
|
if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
|
|
1115
|
-
prop.customType = new
|
|
1124
|
+
prop.customType = new t.json();
|
|
1125
|
+
}
|
|
1126
|
+
if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
|
|
1127
|
+
prop.customType = new t.json();
|
|
1116
1128
|
}
|
|
1117
1129
|
if (!prop.customType && prop.array && prop.items) {
|
|
1118
|
-
prop.customType = new
|
|
1130
|
+
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1131
|
+
}
|
|
1132
|
+
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1133
|
+
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1134
|
+
prop.customType = new t.json();
|
|
1119
1135
|
}
|
|
1120
1136
|
// for number arrays we make sure to convert the items to numbers
|
|
1121
1137
|
if (!prop.customType && prop.type === 'number[]') {
|
|
1122
|
-
prop.customType = new
|
|
1138
|
+
prop.customType = new t.array(i => +i);
|
|
1123
1139
|
}
|
|
1124
1140
|
// `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
|
|
1125
|
-
if (!prop.customType &&
|
|
1126
|
-
prop.customType = new
|
|
1141
|
+
if (!prop.customType && isArray) {
|
|
1142
|
+
prop.customType = new t.array();
|
|
1127
1143
|
}
|
|
1128
1144
|
if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
|
|
1129
|
-
prop.customType = new
|
|
1145
|
+
prop.customType = new t.blob();
|
|
1130
1146
|
}
|
|
1131
1147
|
if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
|
|
1132
|
-
prop.customType = new
|
|
1148
|
+
prop.customType = new t.uint8array();
|
|
1133
1149
|
}
|
|
1134
1150
|
const mappedType = this.getMappedType(prop);
|
|
1135
1151
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1136
|
-
[
|
|
1152
|
+
[t.bigint, t.double, t.decimal, t.interval, t.date]
|
|
1137
1153
|
.filter(type => mappedType instanceof type)
|
|
1138
|
-
.forEach(type => prop.customType = new type());
|
|
1154
|
+
.forEach((type) => prop.customType = new type());
|
|
1139
1155
|
}
|
|
1140
1156
|
if (prop.customType && !prop.columnTypes) {
|
|
1141
1157
|
const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
|
|
1142
|
-
if (prop.customType.compareAsType() === 'any' && ![
|
|
1158
|
+
if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
|
|
1143
1159
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1144
1160
|
}
|
|
1145
1161
|
else {
|
|
1146
1162
|
prop.runtimeType ??= prop.customType.runtimeType;
|
|
1147
1163
|
}
|
|
1148
1164
|
}
|
|
1165
|
+
else if (prop.runtimeType === 'object') {
|
|
1166
|
+
prop.runtimeType = mappedType.runtimeType;
|
|
1167
|
+
}
|
|
1149
1168
|
else {
|
|
1150
1169
|
prop.runtimeType ??= mappedType.runtimeType;
|
|
1151
1170
|
}
|
|
@@ -1156,11 +1175,11 @@ export class MetadataDiscovery {
|
|
|
1156
1175
|
prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
|
|
1157
1176
|
prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
|
|
1158
1177
|
prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
|
|
1159
|
-
if (prop.customType instanceof
|
|
1178
|
+
if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
|
|
1160
1179
|
prop.customType.mode = prop.runtimeType.toLowerCase();
|
|
1161
1180
|
}
|
|
1162
1181
|
}
|
|
1163
|
-
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !
|
|
1182
|
+
if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
|
|
1164
1183
|
prop.type = prop.customType.name;
|
|
1165
1184
|
}
|
|
1166
1185
|
if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
|
|
@@ -1177,7 +1196,7 @@ export class MetadataDiscovery {
|
|
|
1177
1196
|
}
|
|
1178
1197
|
}
|
|
1179
1198
|
}
|
|
1180
|
-
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof
|
|
1199
|
+
if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
|
|
1181
1200
|
if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
|
|
1182
1201
|
prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
|
|
1183
1202
|
}
|
|
@@ -1220,7 +1239,7 @@ export class MetadataDiscovery {
|
|
|
1220
1239
|
if (prop.kind === ReferenceKind.SCALAR) {
|
|
1221
1240
|
const mappedType = this.getMappedType(prop);
|
|
1222
1241
|
const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
|
|
1223
|
-
if (mappedType instanceof
|
|
1242
|
+
if (mappedType instanceof t.unknown
|
|
1224
1243
|
&& !prop.columnTypes
|
|
1225
1244
|
// it could be a runtime type from reflect-metadata
|
|
1226
1245
|
&& !SCALAR_TYPES.includes(prop.type)
|
|
@@ -1303,30 +1322,6 @@ export class MetadataDiscovery {
|
|
|
1303
1322
|
prop.index ??= true;
|
|
1304
1323
|
}
|
|
1305
1324
|
}
|
|
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
1325
|
shouldForceConstructorUsage(meta) {
|
|
1331
1326
|
const forceConstructor = this.config.get('forceEntityConstructor');
|
|
1332
1327
|
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
|
}
|