@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.
Files changed (123) hide show
  1. package/EntityManager.d.ts +85 -32
  2. package/EntityManager.js +281 -178
  3. package/MikroORM.d.ts +8 -8
  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 +5 -4
  8. package/connections/Connection.d.ts +11 -7
  9. package/connections/Connection.js +16 -13
  10. package/decorators/Embeddable.d.ts +2 -0
  11. package/decorators/Embedded.d.ts +5 -11
  12. package/decorators/Entity.d.ts +20 -3
  13. package/decorators/Indexed.d.ts +2 -2
  14. package/decorators/ManyToMany.d.ts +2 -0
  15. package/decorators/ManyToOne.d.ts +4 -0
  16. package/decorators/OneToOne.d.ts +4 -0
  17. package/decorators/Property.d.ts +53 -9
  18. package/decorators/Transactional.d.ts +3 -1
  19. package/decorators/Transactional.js +6 -3
  20. package/decorators/index.d.ts +1 -1
  21. package/drivers/DatabaseDriver.d.ts +11 -5
  22. package/drivers/DatabaseDriver.js +13 -4
  23. package/drivers/IDatabaseDriver.d.ts +29 -5
  24. package/entity/ArrayCollection.d.ts +6 -4
  25. package/entity/ArrayCollection.js +27 -12
  26. package/entity/BaseEntity.d.ts +0 -1
  27. package/entity/BaseEntity.js +0 -3
  28. package/entity/Collection.d.ts +3 -4
  29. package/entity/Collection.js +34 -17
  30. package/entity/EntityAssigner.d.ts +1 -1
  31. package/entity/EntityAssigner.js +9 -1
  32. package/entity/EntityFactory.d.ts +7 -0
  33. package/entity/EntityFactory.js +63 -40
  34. package/entity/EntityHelper.js +26 -9
  35. package/entity/EntityLoader.d.ts +5 -4
  36. package/entity/EntityLoader.js +69 -36
  37. package/entity/EntityRepository.d.ts +1 -1
  38. package/entity/EntityValidator.js +2 -2
  39. package/entity/Reference.d.ts +9 -7
  40. package/entity/Reference.js +32 -5
  41. package/entity/WrappedEntity.d.ts +0 -2
  42. package/entity/WrappedEntity.js +1 -5
  43. package/entity/defineEntity.d.ts +555 -0
  44. package/entity/defineEntity.js +529 -0
  45. package/entity/index.d.ts +2 -0
  46. package/entity/index.js +2 -0
  47. package/entity/utils.d.ts +7 -0
  48. package/entity/utils.js +15 -3
  49. package/enums.d.ts +18 -5
  50. package/enums.js +13 -0
  51. package/errors.d.ts +6 -1
  52. package/errors.js +14 -4
  53. package/events/EventSubscriber.d.ts +3 -1
  54. package/hydration/ObjectHydrator.d.ts +4 -4
  55. package/hydration/ObjectHydrator.js +35 -24
  56. package/index.d.ts +2 -1
  57. package/index.js +1 -1
  58. package/logging/DefaultLogger.d.ts +1 -1
  59. package/logging/SimpleLogger.d.ts +1 -1
  60. package/metadata/EntitySchema.d.ts +8 -4
  61. package/metadata/EntitySchema.js +41 -23
  62. package/metadata/MetadataDiscovery.d.ts +5 -7
  63. package/metadata/MetadataDiscovery.js +151 -159
  64. package/metadata/MetadataStorage.js +1 -1
  65. package/metadata/MetadataValidator.js +4 -3
  66. package/metadata/discover-entities.d.ts +5 -0
  67. package/metadata/discover-entities.js +39 -0
  68. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  69. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  70. package/naming-strategy/NamingStrategy.d.ts +11 -1
  71. package/package.json +14 -8
  72. package/platforms/Platform.d.ts +5 -8
  73. package/platforms/Platform.js +4 -17
  74. package/serialization/EntitySerializer.d.ts +2 -0
  75. package/serialization/EntitySerializer.js +29 -11
  76. package/serialization/EntityTransformer.js +22 -12
  77. package/serialization/SerializationContext.js +14 -11
  78. package/types/BigIntType.d.ts +9 -6
  79. package/types/BigIntType.js +3 -0
  80. package/types/BlobType.d.ts +0 -1
  81. package/types/BlobType.js +0 -3
  82. package/types/BooleanType.d.ts +2 -1
  83. package/types/BooleanType.js +3 -0
  84. package/types/DecimalType.d.ts +6 -4
  85. package/types/DecimalType.js +1 -1
  86. package/types/DoubleType.js +1 -1
  87. package/types/JsonType.d.ts +1 -1
  88. package/types/JsonType.js +7 -2
  89. package/types/Type.d.ts +2 -1
  90. package/types/Type.js +1 -1
  91. package/types/Uint8ArrayType.d.ts +0 -1
  92. package/types/Uint8ArrayType.js +0 -3
  93. package/types/index.d.ts +1 -1
  94. package/typings.d.ts +95 -52
  95. package/typings.js +31 -31
  96. package/unit-of-work/ChangeSetComputer.js +8 -3
  97. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  98. package/unit-of-work/ChangeSetPersister.js +37 -16
  99. package/unit-of-work/UnitOfWork.d.ts +8 -1
  100. package/unit-of-work/UnitOfWork.js +110 -53
  101. package/utils/AbstractSchemaGenerator.js +3 -1
  102. package/utils/Configuration.d.ts +201 -184
  103. package/utils/Configuration.js +143 -151
  104. package/utils/ConfigurationLoader.d.ts +9 -22
  105. package/utils/ConfigurationLoader.js +53 -76
  106. package/utils/Cursor.d.ts +3 -3
  107. package/utils/Cursor.js +3 -0
  108. package/utils/DataloaderUtils.d.ts +15 -5
  109. package/utils/DataloaderUtils.js +53 -7
  110. package/utils/EntityComparator.d.ts +8 -4
  111. package/utils/EntityComparator.js +105 -58
  112. package/utils/QueryHelper.d.ts +9 -1
  113. package/utils/QueryHelper.js +66 -5
  114. package/utils/RawQueryFragment.d.ts +36 -4
  115. package/utils/RawQueryFragment.js +34 -13
  116. package/utils/TransactionManager.d.ts +65 -0
  117. package/utils/TransactionManager.js +223 -0
  118. package/utils/Utils.d.ts +16 -31
  119. package/utils/Utils.js +129 -107
  120. package/utils/index.d.ts +1 -0
  121. package/utils/index.js +1 -0
  122. package/utils/upsert-utils.d.ts +7 -2
  123. package/utils/upsert-utils.js +52 -1
@@ -1,13 +1,12 @@
1
- import { basename, extname } from 'node:path';
2
- import globby from 'globby';
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 { ArrayType, BigIntType, BlobType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
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(preferTs = true) {
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.findEntities(preferTs, true);
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, sync = false) {
129
- this.discovered.length = 0;
130
- const options = this.config.get('discovery');
131
- const key = (preferTs && this.config.get('preferTs', Utils.detectTypeScriptSupport()) && this.config.get('entitiesTs').length > 0) ? 'entitiesTs' : 'entities';
132
- const paths = this.config.get(key).filter(item => Utils.isString(item));
133
- const refs = this.config.get(key).filter(item => !Utils.isString(item));
134
- if (paths.length > 0) {
135
- if (sync || options.requireEntitiesArray) {
136
- throw new Error(`[requireEntitiesArray] Explicit list of entities is required, please use the 'entities' option.`);
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(refs);
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
- if (typeof target === 'function' && target.name && !this.metadata.has(target.name)) {
177
- this.discoverReferences([target]);
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
- async discoverDirectories(paths) {
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
- const schema = this.getSchema(this.prepare(entity));
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
- prepare(entity) {
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
- return EntitySchema.REGISTRY.get(entity);
257
+ entity = EntitySchema.REGISTRY.get(entity);
258
258
  }
259
- return entity;
260
- }
261
- getSchema(entity, filepath) {
262
259
  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;
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, path) {
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(path || meta.path, this.config.get('baseDir'));
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 || prop.cascade.includes(Cascade.REMOVE) || prop.cascade.includes(Cascade.ALL));
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 = this.platform.getSerializedPrimaryKeyField(meta.primaryKeys[0]);
501
- const serializedPKProp = meta.properties[meta.serializedPrimaryKey];
502
- if (serializedPKProp && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
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.length === 0 && base.constructorParams.length > 0) {
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
- if (embeddedProp.nullable) {
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 = Utils.copy(prop, false);
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 = true;
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 = this.createColumnMappingObject(meta);
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 = this.createColumnMappingObject(meta);
1002
+ const map = meta.createColumnMappingObject();
1003
1003
  if (prop.generated instanceof Function) {
1004
1004
  prop.generated = prop.generated(map);
1005
1005
  }
1006
1006
  }
1007
- createColumnMappingObject(meta) {
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
- if (prop.type.toLowerCase() === 'date') {
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 (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
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
- prop.customType = new prop.type();
1109
- prop.type = prop.customType.constructor.name;
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 JsonType();
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 JsonType();
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 EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
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 ArrayType(i => +i);
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 && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1126
- prop.customType = new ArrayType();
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 BlobType();
1142
+ prop.customType = new t.blob();
1130
1143
  }
1131
1144
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1132
- prop.customType = new Uint8ArrayType();
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
- [BigIntType, DoubleType, DecimalType, IntervalType]
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' && ![JsonType].some(t => prop.customType instanceof t)) {
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 BigIntType && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
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 && !prop.type?.toString().endsWith('[]')) {
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 UnknownType)) {
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 UnknownType
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 (!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
  }
@@ -0,0 +1,5 @@
1
+ import { type Constructor } from '../typings.js';
2
+ import { EntitySchema } from './EntitySchema.js';
3
+ export declare function discoverEntities(paths: string | string[], options?: {
4
+ baseDir?: string;
5
+ }): Promise<Iterable<EntitySchema | Constructor>>;