@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.
Files changed (122) 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/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 +3 -1
  18. package/decorators/Transactional.js +6 -3
  19. package/decorators/index.d.ts +1 -1
  20. package/drivers/DatabaseDriver.d.ts +11 -5
  21. package/drivers/DatabaseDriver.js +13 -4
  22. package/drivers/IDatabaseDriver.d.ts +29 -5
  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 +34 -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 +40 -22
  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 +2 -2
  38. package/entity/Reference.d.ts +9 -7
  39. package/entity/Reference.js +32 -5
  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 +18 -5
  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 +40 -20
  61. package/metadata/MetadataDiscovery.d.ts +5 -7
  62. package/metadata/MetadataDiscovery.js +150 -155
  63. package/metadata/MetadataStorage.js +1 -1
  64. package/metadata/MetadataValidator.js +4 -3
  65. package/metadata/discover-entities.d.ts +5 -0
  66. package/metadata/discover-entities.js +39 -0
  67. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  68. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  69. package/naming-strategy/NamingStrategy.d.ts +11 -1
  70. package/package.json +14 -7
  71. package/platforms/Platform.d.ts +5 -8
  72. package/platforms/Platform.js +4 -17
  73. package/serialization/EntitySerializer.d.ts +2 -0
  74. package/serialization/EntitySerializer.js +29 -11
  75. package/serialization/EntityTransformer.js +22 -12
  76. package/serialization/SerializationContext.js +14 -11
  77. package/types/BigIntType.d.ts +9 -6
  78. package/types/BigIntType.js +3 -0
  79. package/types/BlobType.d.ts +0 -1
  80. package/types/BlobType.js +0 -3
  81. package/types/BooleanType.d.ts +2 -1
  82. package/types/BooleanType.js +3 -0
  83. package/types/DecimalType.d.ts +6 -4
  84. package/types/DecimalType.js +1 -1
  85. package/types/DoubleType.js +1 -1
  86. package/types/JsonType.d.ts +1 -1
  87. package/types/JsonType.js +7 -2
  88. package/types/Type.d.ts +2 -1
  89. package/types/Type.js +1 -1
  90. package/types/Uint8ArrayType.d.ts +0 -1
  91. package/types/Uint8ArrayType.js +0 -3
  92. package/types/index.d.ts +1 -1
  93. package/typings.d.ts +94 -50
  94. package/typings.js +31 -31
  95. package/unit-of-work/ChangeSetComputer.js +8 -3
  96. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  97. package/unit-of-work/ChangeSetPersister.js +37 -16
  98. package/unit-of-work/UnitOfWork.d.ts +8 -1
  99. package/unit-of-work/UnitOfWork.js +110 -53
  100. package/utils/AbstractSchemaGenerator.js +3 -1
  101. package/utils/Configuration.d.ts +201 -184
  102. package/utils/Configuration.js +143 -151
  103. package/utils/ConfigurationLoader.d.ts +9 -22
  104. package/utils/ConfigurationLoader.js +53 -76
  105. package/utils/Cursor.d.ts +3 -3
  106. package/utils/Cursor.js +3 -0
  107. package/utils/DataloaderUtils.d.ts +15 -5
  108. package/utils/DataloaderUtils.js +53 -7
  109. package/utils/EntityComparator.d.ts +8 -4
  110. package/utils/EntityComparator.js +105 -58
  111. package/utils/QueryHelper.d.ts +9 -1
  112. package/utils/QueryHelper.js +66 -5
  113. package/utils/RawQueryFragment.d.ts +36 -4
  114. package/utils/RawQueryFragment.js +34 -13
  115. package/utils/TransactionManager.d.ts +65 -0
  116. package/utils/TransactionManager.js +223 -0
  117. package/utils/Utils.d.ts +13 -12
  118. package/utils/Utils.js +106 -66
  119. package/utils/index.d.ts +1 -0
  120. package/utils/index.js +1 -0
  121. package/utils/upsert-utils.d.ts +7 -2
  122. 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';
@@ -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
- if (embeddedProp.nullable) {
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 = Utils.copy(prop, false);
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 = true;
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 = this.createColumnMappingObject(meta);
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 = this.createColumnMappingObject(meta);
1005
+ const map = meta.createColumnMappingObject();
1003
1006
  if (prop.generated instanceof Function) {
1004
1007
  prop.generated = prop.generated(map);
1005
1008
  }
1006
1009
  }
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) {
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
- if (prop.type.toLowerCase() === 'date') {
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 (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
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
- prop.customType = new prop.type();
1109
- prop.type = prop.customType.constructor.name;
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 JsonType();
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 JsonType();
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 EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
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 ArrayType(i => +i);
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 && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1126
- prop.customType = new ArrayType();
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 BlobType();
1145
+ prop.customType = new t.blob();
1130
1146
  }
1131
1147
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1132
- prop.customType = new Uint8ArrayType();
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
- [BigIntType, DoubleType, DecimalType, IntervalType]
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' && ![JsonType].some(t => prop.customType instanceof t)) {
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 BigIntType && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
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 && !prop.type?.toString().endsWith('[]')) {
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 UnknownType)) {
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 UnknownType
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 (!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>>;