@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.
Files changed (115) hide show
  1. package/EntityManager.d.ts +81 -27
  2. package/EntityManager.js +287 -175
  3. package/MikroORM.d.ts +6 -6
  4. package/MikroORM.js +31 -74
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +2 -1
  7. package/cache/FileCacheAdapter.js +6 -4
  8. package/connections/Connection.d.ts +9 -5
  9. package/connections/Connection.js +16 -13
  10. package/decorators/Embedded.d.ts +5 -11
  11. package/decorators/Entity.d.ts +18 -3
  12. package/decorators/Indexed.d.ts +2 -2
  13. package/decorators/ManyToMany.d.ts +2 -0
  14. package/decorators/ManyToOne.d.ts +4 -0
  15. package/decorators/OneToOne.d.ts +4 -0
  16. package/decorators/Property.d.ts +53 -9
  17. package/decorators/Transactional.d.ts +1 -0
  18. package/decorators/Transactional.js +3 -3
  19. package/decorators/index.d.ts +1 -1
  20. package/drivers/DatabaseDriver.d.ts +10 -5
  21. package/drivers/DatabaseDriver.js +4 -4
  22. package/drivers/IDatabaseDriver.d.ts +28 -4
  23. package/entity/ArrayCollection.d.ts +6 -4
  24. package/entity/ArrayCollection.js +26 -9
  25. package/entity/BaseEntity.d.ts +0 -1
  26. package/entity/BaseEntity.js +0 -3
  27. package/entity/Collection.d.ts +3 -4
  28. package/entity/Collection.js +37 -17
  29. package/entity/EntityAssigner.d.ts +1 -1
  30. package/entity/EntityAssigner.js +9 -1
  31. package/entity/EntityFactory.d.ts +7 -0
  32. package/entity/EntityFactory.js +29 -11
  33. package/entity/EntityHelper.js +25 -8
  34. package/entity/EntityLoader.d.ts +5 -4
  35. package/entity/EntityLoader.js +69 -36
  36. package/entity/EntityRepository.d.ts +1 -1
  37. package/entity/EntityValidator.js +1 -1
  38. package/entity/Reference.d.ts +9 -7
  39. package/entity/Reference.js +30 -3
  40. package/entity/WrappedEntity.d.ts +0 -2
  41. package/entity/WrappedEntity.js +1 -5
  42. package/entity/defineEntity.d.ts +555 -0
  43. package/entity/defineEntity.js +529 -0
  44. package/entity/index.d.ts +2 -0
  45. package/entity/index.js +2 -0
  46. package/entity/utils.d.ts +7 -0
  47. package/entity/utils.js +15 -3
  48. package/enums.d.ts +16 -3
  49. package/enums.js +13 -0
  50. package/errors.d.ts +6 -1
  51. package/errors.js +14 -4
  52. package/events/EventSubscriber.d.ts +3 -1
  53. package/hydration/ObjectHydrator.d.ts +4 -4
  54. package/hydration/ObjectHydrator.js +35 -24
  55. package/index.d.ts +2 -1
  56. package/index.js +1 -1
  57. package/logging/DefaultLogger.d.ts +1 -1
  58. package/logging/SimpleLogger.d.ts +1 -1
  59. package/metadata/EntitySchema.d.ts +8 -4
  60. package/metadata/EntitySchema.js +39 -19
  61. package/metadata/MetadataDiscovery.d.ts +4 -4
  62. package/metadata/MetadataDiscovery.js +139 -122
  63. package/metadata/MetadataStorage.js +1 -1
  64. package/metadata/MetadataValidator.js +4 -3
  65. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  66. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  67. package/naming-strategy/NamingStrategy.d.ts +11 -1
  68. package/package.json +5 -5
  69. package/platforms/Platform.d.ts +5 -3
  70. package/platforms/Platform.js +4 -8
  71. package/serialization/EntitySerializer.d.ts +2 -0
  72. package/serialization/EntitySerializer.js +23 -5
  73. package/serialization/EntityTransformer.js +16 -6
  74. package/serialization/SerializationContext.js +14 -11
  75. package/types/BigIntType.d.ts +9 -6
  76. package/types/BigIntType.js +3 -0
  77. package/types/BooleanType.d.ts +1 -1
  78. package/types/DecimalType.d.ts +6 -4
  79. package/types/DecimalType.js +1 -1
  80. package/types/DoubleType.js +1 -1
  81. package/types/JsonType.d.ts +1 -1
  82. package/types/JsonType.js +7 -2
  83. package/types/Type.d.ts +2 -1
  84. package/types/Type.js +1 -1
  85. package/types/index.d.ts +1 -1
  86. package/typings.d.ts +89 -49
  87. package/typings.js +31 -31
  88. package/unit-of-work/ChangeSetComputer.js +8 -3
  89. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  90. package/unit-of-work/ChangeSetPersister.js +37 -16
  91. package/unit-of-work/UnitOfWork.d.ts +8 -1
  92. package/unit-of-work/UnitOfWork.js +110 -53
  93. package/utils/AbstractSchemaGenerator.js +3 -1
  94. package/utils/Configuration.d.ts +29 -16
  95. package/utils/Configuration.js +17 -18
  96. package/utils/ConfigurationLoader.d.ts +9 -22
  97. package/utils/ConfigurationLoader.js +49 -72
  98. package/utils/Cursor.d.ts +3 -3
  99. package/utils/Cursor.js +3 -0
  100. package/utils/DataloaderUtils.d.ts +7 -2
  101. package/utils/DataloaderUtils.js +38 -7
  102. package/utils/EntityComparator.d.ts +6 -2
  103. package/utils/EntityComparator.js +104 -58
  104. package/utils/QueryHelper.d.ts +9 -1
  105. package/utils/QueryHelper.js +66 -5
  106. package/utils/RawQueryFragment.d.ts +36 -4
  107. package/utils/RawQueryFragment.js +34 -13
  108. package/utils/TransactionManager.d.ts +65 -0
  109. package/utils/TransactionManager.js +223 -0
  110. package/utils/Utils.d.ts +13 -11
  111. package/utils/Utils.js +82 -55
  112. package/utils/index.d.ts +1 -0
  113. package/utils/index.js +1 -0
  114. package/utils/upsert-utils.d.ts +7 -2
  115. package/utils/upsert-utils.js +52 -1
@@ -1,13 +1,13 @@
1
1
  import { basename, extname } from 'node:path';
2
- import globby from 'globby';
3
- import { EntityMetadata, } from '../typings.js';
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 { ArrayType, BigIntType, BlobType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
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(preferTs = true) {
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(preferTs, true);
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('preferTs', Utils.detectTypeScriptSupport()) && this.config.get('entitiesTs').length > 0) ? 'entitiesTs' : 'entities';
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
- if (typeof target === 'function' && target.name && !this.metadata.has(target.name)) {
177
- this.discoverReferences([target]);
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 globby(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
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
- 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);
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(this.prepare(entity));
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
- prepare(entity) {
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
- return EntitySchema.REGISTRY.get(entity);
265
+ entity = EntitySchema.REGISTRY.get(entity);
258
266
  }
259
- return entity;
260
- }
261
- getSchema(entity, filepath) {
262
267
  if (entity instanceof EntitySchema) {
263
- if (filepath) {
264
- // initialize global metadata for given entity
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, path) {
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(path || meta.path, this.config.get('baseDir'));
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 || prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
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
- if (embeddedProp.nullable) {
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.filter(m => m.root.className === meta.root.className && !m.abstract);
909
- children.forEach(m => {
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 = Utils.copy(prop, false);
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 = true;
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 = this.createColumnMappingObject(meta);
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 = this.createColumnMappingObject(meta);
1014
+ const map = meta.createColumnMappingObject();
1001
1015
  if (prop.generated instanceof Function) {
1002
1016
  prop.generated = prop.generated(map);
1003
1017
  }
1004
1018
  }
1005
- createColumnMappingObject(meta) {
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
- if (prop.type.toLowerCase() === 'date') {
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 (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
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 JsonType();
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 JsonType();
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 EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
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 ArrayType(i => +i);
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 && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1124
- prop.customType = new ArrayType();
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 BlobType();
1144
+ prop.customType = new t.blob();
1128
1145
  }
1129
1146
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1130
- prop.customType = new Uint8ArrayType();
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
- [BigIntType, DoubleType, DecimalType, IntervalType]
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' && ![JsonType].some(t => prop.customType instanceof t)) {
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 BigIntType && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
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 && !prop.type?.toString().endsWith('[]')) {
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 UnknownType)) {
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 UnknownType
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(path, name) {
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
- targets.forEach((item2, idx) => {
1331
+ for (const item2 of targets) {
1312
1332
  if (item.meta.class === item2) {
1313
- targets.splice(idx, 1);
1333
+ targets.splice(targets.indexOf(item2), 1);
1314
1334
  }
1315
- });
1335
+ }
1316
1336
  }
1317
1337
  }
1318
- if (targets.length > 0) {
1319
- return targets;
1320
- }
1321
- const target = exports.default ?? exports[name];
1322
- /* v8 ignore next 3 */
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 (!metadata.find(prop.type)) {
105
+ if (!targetMeta) {
105
106
  throw MetadataError.fromWrongTypeDefinition(meta, prop);
106
107
  }
107
- if (metadata.find(prop.type).abstract && !metadata.find(prop.type).discriminatorColumn) {
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 && metadata.find(prop.type).compositePK && options.checkNonPersistentCompositeProps) {
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}`, schemaName);
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
  *