@mikro-orm/core 7.0.0-dev.11 → 7.0.0-dev.110

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 (196) hide show
  1. package/EntityManager.d.ts +77 -48
  2. package/EntityManager.js +285 -225
  3. package/MikroORM.d.ts +40 -31
  4. package/MikroORM.js +98 -137
  5. package/README.md +3 -2
  6. package/cache/FileCacheAdapter.d.ts +1 -1
  7. package/cache/FileCacheAdapter.js +8 -7
  8. package/cache/GeneratedCacheAdapter.d.ts +0 -1
  9. package/cache/GeneratedCacheAdapter.js +0 -2
  10. package/cache/index.d.ts +0 -1
  11. package/cache/index.js +0 -1
  12. package/connections/Connection.d.ts +16 -7
  13. package/connections/Connection.js +23 -14
  14. package/drivers/DatabaseDriver.d.ts +14 -5
  15. package/drivers/DatabaseDriver.js +28 -11
  16. package/drivers/IDatabaseDriver.d.ts +25 -4
  17. package/entity/BaseEntity.d.ts +0 -1
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +95 -30
  20. package/entity/Collection.js +432 -93
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +17 -9
  23. package/entity/EntityFactory.d.ts +7 -0
  24. package/entity/EntityFactory.js +63 -41
  25. package/entity/EntityHelper.js +28 -13
  26. package/entity/EntityLoader.d.ts +5 -4
  27. package/entity/EntityLoader.js +63 -38
  28. package/entity/EntityRepository.d.ts +1 -1
  29. package/entity/Reference.d.ts +6 -5
  30. package/entity/Reference.js +34 -9
  31. package/entity/WrappedEntity.d.ts +2 -7
  32. package/entity/WrappedEntity.js +2 -7
  33. package/entity/defineEntity.d.ts +568 -0
  34. package/entity/defineEntity.js +529 -0
  35. package/entity/index.d.ts +3 -2
  36. package/entity/index.js +3 -2
  37. package/entity/utils.d.ts +7 -0
  38. package/entity/utils.js +16 -4
  39. package/entity/validators.d.ts +11 -0
  40. package/entity/validators.js +65 -0
  41. package/enums.d.ts +21 -6
  42. package/enums.js +14 -1
  43. package/errors.d.ts +10 -2
  44. package/errors.js +29 -10
  45. package/events/EventManager.d.ts +2 -1
  46. package/events/EventManager.js +19 -11
  47. package/hydration/Hydrator.js +1 -2
  48. package/hydration/ObjectHydrator.d.ts +4 -4
  49. package/hydration/ObjectHydrator.js +35 -25
  50. package/index.d.ts +2 -2
  51. package/index.js +1 -2
  52. package/logging/DefaultLogger.d.ts +1 -1
  53. package/logging/DefaultLogger.js +1 -0
  54. package/logging/SimpleLogger.d.ts +1 -1
  55. package/logging/colors.d.ts +1 -1
  56. package/logging/colors.js +7 -6
  57. package/logging/index.d.ts +1 -0
  58. package/logging/index.js +1 -0
  59. package/logging/inspect.d.ts +2 -0
  60. package/logging/inspect.js +11 -0
  61. package/metadata/EntitySchema.d.ts +9 -13
  62. package/metadata/EntitySchema.js +44 -26
  63. package/metadata/MetadataDiscovery.d.ts +6 -9
  64. package/metadata/MetadataDiscovery.js +170 -206
  65. package/metadata/MetadataProvider.d.ts +11 -2
  66. package/metadata/MetadataProvider.js +44 -2
  67. package/metadata/MetadataStorage.d.ts +1 -6
  68. package/metadata/MetadataStorage.js +6 -18
  69. package/metadata/MetadataValidator.d.ts +0 -7
  70. package/metadata/MetadataValidator.js +0 -10
  71. package/metadata/discover-entities.d.ts +5 -0
  72. package/metadata/discover-entities.js +40 -0
  73. package/metadata/index.d.ts +1 -1
  74. package/metadata/index.js +1 -1
  75. package/metadata/types.d.ts +480 -0
  76. package/metadata/types.js +1 -0
  77. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  78. package/naming-strategy/AbstractNamingStrategy.js +8 -2
  79. package/naming-strategy/NamingStrategy.d.ts +11 -1
  80. package/not-supported.d.ts +2 -0
  81. package/not-supported.js +4 -0
  82. package/package.json +18 -10
  83. package/platforms/ExceptionConverter.js +1 -1
  84. package/platforms/Platform.d.ts +6 -13
  85. package/platforms/Platform.js +15 -41
  86. package/serialization/EntitySerializer.d.ts +2 -0
  87. package/serialization/EntitySerializer.js +32 -14
  88. package/serialization/EntityTransformer.js +22 -12
  89. package/serialization/SerializationContext.js +16 -13
  90. package/types/ArrayType.d.ts +1 -1
  91. package/types/ArrayType.js +2 -3
  92. package/types/BigIntType.d.ts +8 -6
  93. package/types/BigIntType.js +1 -1
  94. package/types/BlobType.d.ts +0 -1
  95. package/types/BlobType.js +0 -3
  96. package/types/BooleanType.d.ts +2 -1
  97. package/types/BooleanType.js +3 -0
  98. package/types/DecimalType.d.ts +6 -4
  99. package/types/DecimalType.js +3 -3
  100. package/types/DoubleType.js +2 -2
  101. package/types/EnumArrayType.js +1 -2
  102. package/types/JsonType.d.ts +1 -1
  103. package/types/JsonType.js +7 -2
  104. package/types/TinyIntType.js +1 -1
  105. package/types/Type.d.ts +2 -4
  106. package/types/Type.js +3 -3
  107. package/types/Uint8ArrayType.d.ts +0 -1
  108. package/types/Uint8ArrayType.js +1 -4
  109. package/types/index.d.ts +1 -1
  110. package/typings.d.ts +109 -73
  111. package/typings.js +38 -35
  112. package/unit-of-work/ChangeSet.d.ts +0 -3
  113. package/unit-of-work/ChangeSet.js +2 -2
  114. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  115. package/unit-of-work/ChangeSetComputer.js +11 -9
  116. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  117. package/unit-of-work/ChangeSetPersister.js +51 -19
  118. package/unit-of-work/UnitOfWork.d.ts +8 -1
  119. package/unit-of-work/UnitOfWork.js +93 -51
  120. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  121. package/utils/AbstractSchemaGenerator.js +11 -9
  122. package/utils/AsyncContext.d.ts +6 -0
  123. package/utils/AsyncContext.js +42 -0
  124. package/utils/Configuration.d.ts +757 -206
  125. package/utils/Configuration.js +145 -190
  126. package/utils/ConfigurationLoader.d.ts +1 -54
  127. package/utils/ConfigurationLoader.js +1 -352
  128. package/utils/Cursor.d.ts +0 -3
  129. package/utils/Cursor.js +6 -3
  130. package/utils/DataloaderUtils.d.ts +15 -5
  131. package/utils/DataloaderUtils.js +54 -8
  132. package/utils/EntityComparator.d.ts +8 -4
  133. package/utils/EntityComparator.js +52 -17
  134. package/utils/QueryHelper.d.ts +9 -1
  135. package/utils/QueryHelper.js +70 -9
  136. package/utils/RawQueryFragment.d.ts +36 -13
  137. package/utils/RawQueryFragment.js +38 -18
  138. package/utils/RequestContext.js +2 -2
  139. package/utils/TransactionContext.js +2 -2
  140. package/utils/TransactionManager.d.ts +65 -0
  141. package/utils/TransactionManager.js +223 -0
  142. package/utils/Utils.d.ts +9 -118
  143. package/utils/Utils.js +88 -372
  144. package/utils/clone.js +2 -3
  145. package/utils/env-vars.d.ts +7 -0
  146. package/utils/env-vars.js +98 -0
  147. package/utils/fs-utils.d.ts +32 -0
  148. package/utils/fs-utils.js +178 -0
  149. package/utils/index.d.ts +2 -1
  150. package/utils/index.js +2 -1
  151. package/utils/upsert-utils.d.ts +7 -2
  152. package/utils/upsert-utils.js +55 -4
  153. package/decorators/Check.d.ts +0 -3
  154. package/decorators/Check.js +0 -13
  155. package/decorators/CreateRequestContext.d.ts +0 -3
  156. package/decorators/CreateRequestContext.js +0 -32
  157. package/decorators/Embeddable.d.ts +0 -8
  158. package/decorators/Embeddable.js +0 -11
  159. package/decorators/Embedded.d.ts +0 -18
  160. package/decorators/Embedded.js +0 -18
  161. package/decorators/Entity.d.ts +0 -18
  162. package/decorators/Entity.js +0 -12
  163. package/decorators/Enum.d.ts +0 -9
  164. package/decorators/Enum.js +0 -16
  165. package/decorators/Filter.d.ts +0 -2
  166. package/decorators/Filter.js +0 -8
  167. package/decorators/Formula.d.ts +0 -4
  168. package/decorators/Formula.js +0 -15
  169. package/decorators/Indexed.d.ts +0 -19
  170. package/decorators/Indexed.js +0 -20
  171. package/decorators/ManyToMany.d.ts +0 -40
  172. package/decorators/ManyToMany.js +0 -14
  173. package/decorators/ManyToOne.d.ts +0 -30
  174. package/decorators/ManyToOne.js +0 -14
  175. package/decorators/OneToMany.d.ts +0 -28
  176. package/decorators/OneToMany.js +0 -17
  177. package/decorators/OneToOne.d.ts +0 -24
  178. package/decorators/OneToOne.js +0 -7
  179. package/decorators/PrimaryKey.d.ts +0 -8
  180. package/decorators/PrimaryKey.js +0 -20
  181. package/decorators/Property.d.ts +0 -250
  182. package/decorators/Property.js +0 -32
  183. package/decorators/Transactional.d.ts +0 -13
  184. package/decorators/Transactional.js +0 -28
  185. package/decorators/hooks.d.ts +0 -16
  186. package/decorators/hooks.js +0 -47
  187. package/decorators/index.d.ts +0 -17
  188. package/decorators/index.js +0 -17
  189. package/entity/ArrayCollection.d.ts +0 -116
  190. package/entity/ArrayCollection.js +0 -402
  191. package/entity/EntityValidator.d.ts +0 -19
  192. package/entity/EntityValidator.js +0 -150
  193. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  194. package/metadata/ReflectMetadataProvider.js +0 -44
  195. package/utils/resolveContextProvider.d.ts +0 -10
  196. package/utils/resolveContextProvider.js +0 -28
@@ -1,5 +1,3 @@
1
- import { basename, extname } from 'node:path';
2
- import globby from 'globby';
3
1
  import { EntityMetadata, } from '../typings.js';
4
2
  import { Utils } from '../utils/Utils.js';
5
3
  import { MetadataValidator } from './MetadataValidator.js';
@@ -7,7 +5,7 @@ import { MetadataStorage } from './MetadataStorage.js';
7
5
  import { EntitySchema } from './EntitySchema.js';
8
6
  import { Cascade, ReferenceKind } from '../enums.js';
9
7
  import { MetadataError } from '../errors.js';
10
- import { ArrayType, BigIntType, BlobType, DateType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
8
+ import { t, Type } from '../types/index.js';
11
9
  import { colors } from '../logging/colors.js';
12
10
  import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
13
11
  export class MetadataDiscovery {
@@ -16,7 +14,6 @@ export class MetadataDiscovery {
16
14
  config;
17
15
  namingStrategy;
18
16
  metadataProvider;
19
- cache;
20
17
  logger;
21
18
  schemaHelper;
22
19
  validator = new MetadataValidator();
@@ -27,11 +24,11 @@ export class MetadataDiscovery {
27
24
  this.config = config;
28
25
  this.namingStrategy = this.config.getNamingStrategy();
29
26
  this.metadataProvider = this.config.getMetadataProvider();
30
- this.cache = this.config.getMetadataCacheAdapter();
31
27
  this.logger = this.config.getLogger();
32
28
  this.schemaHelper = this.platform.getSchemaHelper();
33
29
  }
34
30
  async discover(preferTs = true) {
31
+ this.discovered.length = 0;
35
32
  const startTime = Date.now();
36
33
  this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)}`);
37
34
  await this.findEntities(preferTs);
@@ -47,10 +44,12 @@ export class MetadataDiscovery {
47
44
  await this.config.get('discovery').afterDiscovered?.(storage, this.platform);
48
45
  return storage;
49
46
  }
50
- discoverSync(preferTs = true) {
47
+ discoverSync() {
48
+ this.discovered.length = 0;
51
49
  const startTime = Date.now();
52
50
  this.logger.log('discovery', `ORM entity discovery started, using ${colors.cyan(this.metadataProvider.constructor.name)} in sync mode`);
53
- this.findEntities(preferTs, true);
51
+ const refs = this.config.get('entities');
52
+ this.discoverReferences(refs);
54
53
  for (const meta of this.discovered) {
55
54
  /* v8 ignore next */
56
55
  void this.config.get('discovery').onMetadata?.(meta, this.platform);
@@ -63,6 +62,9 @@ export class MetadataDiscovery {
63
62
  void this.config.get('discovery').afterDiscovered?.(storage, this.platform);
64
63
  return storage;
65
64
  }
65
+ validateDiscovered(metadata) {
66
+ return this.validator.validateDiscovered(metadata, this.config.get('discovery'));
67
+ }
66
68
  mapDiscoveredEntities() {
67
69
  const discovered = new MetadataStorage();
68
70
  this.discovered
@@ -74,11 +76,41 @@ export class MetadataDiscovery {
74
76
  });
75
77
  return discovered;
76
78
  }
79
+ initAccessors(meta) {
80
+ for (const prop of Object.values(meta.properties)) {
81
+ if (!prop.accessor || meta.properties[prop.accessor]) {
82
+ continue;
83
+ }
84
+ const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
85
+ if (desc?.get || desc?.set) {
86
+ this.initFieldName(prop);
87
+ const accessor = prop.name;
88
+ prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
89
+ if (prop.accessor === true) {
90
+ prop.getter = prop.setter = true;
91
+ }
92
+ else {
93
+ prop.getter = prop.setter = false;
94
+ }
95
+ prop.accessor = accessor;
96
+ prop.serializedName ??= accessor;
97
+ Utils.renameKey(meta.properties, accessor, prop.name);
98
+ }
99
+ else {
100
+ const name = prop.name;
101
+ prop.name = prop.accessor;
102
+ this.initFieldName(prop);
103
+ prop.serializedName ??= prop.accessor;
104
+ prop.name = name;
105
+ }
106
+ }
107
+ }
77
108
  processDiscoveredEntities(discovered) {
78
109
  for (const meta of discovered) {
79
110
  let i = 1;
80
111
  Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
81
112
  Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
113
+ this.initAccessors(meta);
82
114
  }
83
115
  // ignore base entities (not annotated with @Entity)
84
116
  const filtered = discovered.filter(meta => meta.root.name);
@@ -101,10 +133,6 @@ export class MetadataDiscovery {
101
133
  this.initDefaultValue(prop);
102
134
  this.inferTypeFromDefault(prop);
103
135
  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
136
  }
109
137
  }
110
138
  filtered.forEach(meta => Object.values(meta.properties).forEach(prop => this.initIndexes(meta, prop)));
@@ -114,38 +142,31 @@ export class MetadataDiscovery {
114
142
  discovered.push(...this.processEntity(meta));
115
143
  }
116
144
  discovered.forEach(meta => meta.sync(true));
117
- const combinedCachePath = this.cache.combine?.();
118
- // override the path in the options, so we can log it from the CLI in `cache:generate` command
119
- if (combinedCachePath) {
120
- this.config.get('metadataCache').combined = combinedCachePath;
121
- }
145
+ this.metadataProvider.combineCache();
122
146
  return discovered.map(meta => {
123
147
  meta = this.metadata.get(meta.className);
124
148
  meta.sync(true);
125
149
  return meta;
126
150
  });
127
151
  }
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.`);
152
+ async findEntities(preferTs) {
153
+ const { entities, entitiesTs, baseDir } = this.config.getAll();
154
+ const targets = (preferTs && entitiesTs.length > 0) ? entitiesTs : entities;
155
+ const processed = [];
156
+ const paths = [];
157
+ for (const entity of targets) {
158
+ if (typeof entity === 'string') {
159
+ paths.push(entity);
160
+ }
161
+ else {
162
+ processed.push(entity);
137
163
  }
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
164
  }
145
- this.discoverReferences(refs);
146
- this.discoverMissingTargets();
147
- this.validator.validateDiscovered(this.discovered, options);
148
- return this.discovered;
165
+ if (paths.length > 0) {
166
+ const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
167
+ processed.push(...await discoverEntities(paths, { baseDir }));
168
+ }
169
+ return this.discoverReferences(processed);
149
170
  }
150
171
  discoverMissingTargets() {
151
172
  const unwrap = (type) => type
@@ -173,50 +194,20 @@ export class MetadataDiscovery {
173
194
  }
174
195
  tryDiscoverTargets(targets) {
175
196
  for (const target of targets) {
176
- if (typeof target === 'function' && target.name && !this.metadata.has(target.name)) {
177
- this.discoverReferences([target]);
197
+ const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
198
+ if (isDiscoverable && target.name && !this.metadata.has(target.name)) {
199
+ this.discoverReferences([target], false);
178
200
  this.discoverMissingTargets();
179
201
  }
180
202
  }
181
203
  }
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) {
204
+ discoverReferences(refs, validate = true) {
217
205
  const found = [];
218
206
  for (const entity of refs) {
219
- const schema = this.getSchema(this.prepare(entity));
207
+ if (typeof entity === 'string') {
208
+ throw new Error('Folder based discovery requires the async `MikroORM.init()` method.');
209
+ }
210
+ const schema = this.getSchema(entity);
220
211
  const meta = schema.init().meta;
221
212
  this.metadata.set(meta.className, meta);
222
213
  found.push(schema);
@@ -225,20 +216,27 @@ export class MetadataDiscovery {
225
216
  for (const meta of this.metadata) {
226
217
  let parent = meta.extends;
227
218
  if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.className)) {
228
- this.discoverReferences([parent]);
219
+ this.discoverReferences([parent], false);
220
+ }
221
+ if (typeof parent === 'function' && parent.name && !this.metadata.has(parent.name)) {
222
+ this.discoverReferences([parent], false);
229
223
  }
230
- /* v8 ignore next 3 */
224
+ /* v8 ignore next */
231
225
  if (!meta.class) {
232
226
  continue;
233
227
  }
234
228
  parent = Object.getPrototypeOf(meta.class);
235
229
  if (parent.name !== '' && !this.metadata.has(parent.name)) {
236
- this.discoverReferences([parent]);
230
+ this.discoverReferences([parent], false);
237
231
  }
238
232
  }
239
233
  for (const schema of found) {
240
234
  this.discoverEntity(schema);
241
235
  }
236
+ this.discoverMissingTargets();
237
+ if (validate) {
238
+ this.validateDiscovered(this.discovered);
239
+ }
242
240
  return this.discovered.filter(meta => found.find(m => m.name === meta.className));
243
241
  }
244
242
  reset(className) {
@@ -248,28 +246,18 @@ export class MetadataDiscovery {
248
246
  this.discovered.splice(exists, 1);
249
247
  }
250
248
  }
251
- prepare(entity) {
252
- /* v8 ignore next 3 */
253
- if ('schema' in entity && entity.schema instanceof EntitySchema) {
254
- return entity.schema;
255
- }
249
+ getSchema(entity) {
256
250
  if (EntitySchema.REGISTRY.has(entity)) {
257
- return EntitySchema.REGISTRY.get(entity);
251
+ entity = EntitySchema.REGISTRY.get(entity);
258
252
  }
259
- return entity;
260
- }
261
- getSchema(entity, filepath) {
262
253
  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;
254
+ const meta = Utils.copy(entity.meta, false);
255
+ return EntitySchema.fromMetadata(meta);
268
256
  }
269
257
  const path = entity[MetadataStorage.PATH_SYMBOL];
270
258
  if (path) {
271
259
  const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
272
- meta.path = Utils.relativePath(path, this.config.get('baseDir'));
260
+ meta.path = path;
273
261
  this.metadata.set(entity.name, meta);
274
262
  }
275
263
  const exists = this.metadata.has(entity.name);
@@ -279,16 +267,26 @@ export class MetadataDiscovery {
279
267
  schema.setClass(entity);
280
268
  return schema;
281
269
  }
282
- discoverEntity(schema, path) {
283
- this.logger.log('discovery', `- processing entity ${colors.cyan(schema.meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
270
+ getRootEntity(meta) {
271
+ const base = meta.extends && this.metadata.find(Utils.className(meta.extends));
272
+ if (!base || base === meta) { // make sure we do not fall into infinite loop
273
+ return meta;
274
+ }
275
+ const root = this.getRootEntity(base);
276
+ if (root.discriminatorColumn) {
277
+ return root;
278
+ }
279
+ return meta;
280
+ }
281
+ discoverEntity(schema) {
284
282
  const meta = schema.meta;
285
- const root = Utils.getRootEntity(this.metadata, meta);
286
- schema.meta.path = Utils.relativePath(path || meta.path, this.config.get('baseDir'));
287
- const cache = this.metadataProvider.useCache() && meta.path && this.cache.get(meta.className + extname(meta.path));
283
+ const path = meta.path;
284
+ this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
285
+ const root = this.getRootEntity(meta);
286
+ schema.meta.path = meta.path;
287
+ const cache = this.metadataProvider.getCachedMetadata(meta, root);
288
288
  if (cache) {
289
289
  this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
290
- this.metadataProvider.loadFromCache(meta, cache);
291
- meta.root = root;
292
290
  this.discovered.push(meta);
293
291
  return;
294
292
  }
@@ -297,50 +295,18 @@ export class MetadataDiscovery {
297
295
  this.inferDefaultValue(meta, prop);
298
296
  }
299
297
  // if the definition is using EntitySchema we still want it to go through the metadata provider to validate no types are missing
300
- this.metadataProvider.loadEntityMetadata(meta, meta.className);
298
+ this.metadataProvider.loadEntityMetadata(meta);
301
299
  if (!meta.collection && meta.name) {
302
300
  const entityName = root.discriminatorColumn ? root.name : meta.name;
303
301
  meta.collection = this.namingStrategy.classToTableName(entityName);
304
302
  }
305
- delete meta.root; // to allow caching (as root can contain cycles)
306
- this.saveToCache(meta);
303
+ this.metadataProvider.saveToCache(meta);
307
304
  meta.root = root;
308
305
  this.discovered.push(meta);
309
306
  }
310
- saveToCache(meta) {
311
- if (!this.metadataProvider.useCache()) {
312
- return;
313
- }
314
- const copy = Utils.copy(meta, false);
315
- for (const prop of copy.props) {
316
- if (Type.isMappedType(prop.type)) {
317
- Reflect.deleteProperty(prop, 'type');
318
- Reflect.deleteProperty(prop, 'customType');
319
- }
320
- if (prop.default) {
321
- const raw = RawQueryFragment.getKnownFragment(prop.default);
322
- if (raw) {
323
- prop.defaultRaw ??= this.platform.formatQuery(raw.sql, raw.params);
324
- Reflect.deleteProperty(prop, 'default');
325
- }
326
- }
327
- Reflect.deleteProperty(prop, 'targetMeta');
328
- }
329
- [
330
- 'prototype', 'props', 'referencingProperties', 'propertyOrder', 'relations',
331
- 'concurrencyCheckKeys', 'checks',
332
- ].forEach(key => delete copy[key]);
333
- // base entity without properties might not have path, but nothing to cache there
334
- if (meta.path) {
335
- this.cache.set(meta.className + extname(meta.path), copy, meta.path);
336
- }
337
- }
338
307
  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
308
  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));
309
+ return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
344
310
  }
345
311
  return Utils.defaultValue(prop, 'nullable', prop.optional);
346
312
  }
@@ -375,7 +341,7 @@ export class MetadataDiscovery {
375
341
  if (prop.joinColumns.length !== prop.columnTypes.length) {
376
342
  prop.columnTypes = prop.joinColumns.flatMap(field => {
377
343
  const matched = meta.props.find(p => p.fieldNames?.includes(field));
378
- /* v8 ignore next 3 */
344
+ /* v8 ignore next */
379
345
  if (!matched) {
380
346
  throw MetadataError.fromWrongForeignKey(meta, prop, 'columnTypes');
381
347
  }
@@ -497,10 +463,9 @@ export class MetadataDiscovery {
497
463
  }
498
464
  this.initOwnColumns(meta);
499
465
  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;
466
+ meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
467
+ if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
468
+ meta.properties[meta.serializedPrimaryKey].persist ??= false;
504
469
  }
505
470
  if (this.platform.usesPivotTable()) {
506
471
  return Object.values(meta.properties)
@@ -539,7 +504,7 @@ export class MetadataDiscovery {
539
504
  }
540
505
  else if (fks.length >= 2) {
541
506
  [first, second] = fks;
542
- /* v8 ignore next 3 */
507
+ /* v8 ignore next */
543
508
  }
544
509
  else {
545
510
  return [];
@@ -618,7 +583,7 @@ export class MetadataDiscovery {
618
583
  }
619
584
  data.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.className, targetType + '_inverse', true, meta.className === targetType);
620
585
  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);
586
+ return this.metadata.set(data.className, EntitySchema.fromMetadata(data).init().meta);
622
587
  }
623
588
  defineFixedOrderProperty(prop, targetType) {
624
589
  const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
@@ -653,6 +618,7 @@ export class MetadataDiscovery {
653
618
  autoincrement: false,
654
619
  updateRule: prop.updateRule,
655
620
  deleteRule: prop.deleteRule,
621
+ createForeignKeyConstraint: prop.createForeignKeyConstraint,
656
622
  };
657
623
  if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
658
624
  ret.updateRule ??= 'no action';
@@ -739,12 +705,9 @@ export class MetadataDiscovery {
739
705
  Utils.keys(base.hooks).forEach(type => {
740
706
  meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
741
707
  });
742
- if (meta.constructorParams.length === 0 && base.constructorParams.length > 0) {
708
+ if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
743
709
  meta.constructorParams = [...base.constructorParams];
744
710
  }
745
- if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
746
- meta.toJsonParams = [...base.toJsonParams];
747
- }
748
711
  return order;
749
712
  }
750
713
  initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
@@ -820,9 +783,16 @@ export class MetadataDiscovery {
820
783
  }
821
784
  return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
822
785
  };
786
+ const isParentArray = (prop) => {
787
+ if (prop.array) {
788
+ return true;
789
+ }
790
+ return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
791
+ };
823
792
  const rootProperty = getRootProperty(embeddedProp);
824
793
  const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
825
794
  const object = isParentObject(embeddedProp);
795
+ const array = isParentArray(embeddedProp);
826
796
  this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
827
797
  // the prefix of the parent cannot be a boolean; it already passed here
828
798
  const prefix = this.getPrefix(embeddedProp, parentProperty);
@@ -835,7 +805,8 @@ export class MetadataDiscovery {
835
805
  meta.propertyOrder.set(name, (order += 0.01));
836
806
  embeddedProp.embeddedProps[prop.name] = meta.properties[name];
837
807
  meta.properties[name].persist ??= embeddedProp.persist;
838
- if (embeddedProp.nullable) {
808
+ const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
809
+ if (embeddedProp.nullable || refInArray) {
839
810
  meta.properties[name].nullable = true;
840
811
  }
841
812
  if (meta.properties[name].fieldNames) {
@@ -873,6 +844,7 @@ export class MetadataDiscovery {
873
844
  meta.properties[name].persist = false; // only virtual as we store the whole object
874
845
  meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
875
846
  meta.properties[name].object = true;
847
+ this.initCustomType(meta, meta.properties[name], false, true);
876
848
  }
877
849
  this.initEmbeddables(meta, meta.properties[name], visited);
878
850
  }
@@ -941,7 +913,7 @@ export class MetadataDiscovery {
941
913
  newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
942
914
  }
943
915
  newProp.nullable = true;
944
- newProp.inherited = true;
916
+ newProp.inherited = !meta.root.properties[prop.name];
945
917
  meta.root.addProperty(newProp);
946
918
  });
947
919
  meta.collection = meta.root.collection;
@@ -966,7 +938,7 @@ export class MetadataDiscovery {
966
938
  }
967
939
  }
968
940
  initCheckConstraints(meta) {
969
- const map = this.createColumnMappingObject(meta);
941
+ const map = meta.createColumnMappingObject();
970
942
  for (const check of meta.checks) {
971
943
  const columns = check.property ? meta.properties[check.property].fieldNames : [];
972
944
  check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
@@ -976,7 +948,7 @@ export class MetadataDiscovery {
976
948
  }
977
949
  if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
978
950
  for (const prop of meta.props) {
979
- if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => Utils.isString(item))) {
951
+ if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
980
952
  this.initFieldName(prop);
981
953
  meta.checks.push({
982
954
  name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
@@ -1001,35 +973,28 @@ export class MetadataDiscovery {
1001
973
  }
1002
974
  return;
1003
975
  }
1004
- const map = this.createColumnMappingObject(meta);
976
+ const map = meta.createColumnMappingObject();
1005
977
  if (prop.generated instanceof Function) {
1006
978
  prop.generated = prop.generated(map);
1007
979
  }
1008
980
  }
1009
- createColumnMappingObject(meta) {
1010
- return Object.values(meta.properties).reduce((o, prop) => {
1011
- if (prop.fieldNames) {
1012
- o[prop.name] = prop.fieldNames[0];
1013
- }
1014
- return o;
1015
- }, {});
1016
- }
1017
- getDefaultVersionValue(prop) {
981
+ getDefaultVersionValue(meta, prop) {
1018
982
  if (typeof prop.defaultRaw !== 'undefined') {
1019
983
  return prop.defaultRaw;
1020
984
  }
1021
- /* v8 ignore next 3 */
985
+ /* v8 ignore next */
1022
986
  if (prop.default != null) {
1023
987
  return '' + this.platform.quoteVersionValue(prop.default, prop);
1024
988
  }
1025
- if (prop.type.toLowerCase() === 'date') {
989
+ this.initCustomType(meta, prop, true);
990
+ const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
991
+ if (type === 'Date') {
1026
992
  prop.length ??= this.platform.getDefaultVersionLength();
1027
993
  return this.platform.getCurrentTimestampSQL(prop.length);
1028
994
  }
1029
995
  return '1';
1030
996
  }
1031
997
  inferDefaultValue(meta, prop) {
1032
- /* v8 ignore next 3 */
1033
998
  if (!meta.class) {
1034
999
  return;
1035
1000
  }
@@ -1065,7 +1030,7 @@ export class MetadataDiscovery {
1065
1030
  prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
1066
1031
  return;
1067
1032
  }
1068
- if (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
1033
+ if (Array.isArray(prop.default) && prop.customType) {
1069
1034
  val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
1070
1035
  }
1071
1036
  prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
@@ -1093,13 +1058,13 @@ export class MetadataDiscovery {
1093
1058
  if (prop.version) {
1094
1059
  this.initDefaultValue(prop);
1095
1060
  meta.versionProperty = prop.name;
1096
- prop.defaultRaw = this.getDefaultVersionValue(prop);
1061
+ prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
1097
1062
  }
1098
1063
  if (prop.concurrencyCheck && !prop.primary) {
1099
1064
  meta.concurrencyCheckKeys.add(prop.name);
1100
1065
  }
1101
1066
  }
1102
- initCustomType(meta, prop) {
1067
+ initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
1103
1068
  // `prop.type` might be actually instance of custom type class
1104
1069
  if (Type.isMappedType(prop.type) && !prop.customType) {
1105
1070
  prop.customType = prop.type;
@@ -1107,47 +1072,70 @@ export class MetadataDiscovery {
1107
1072
  }
1108
1073
  // `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
1109
1074
  if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
1110
- prop.customType = new prop.type();
1111
- prop.type = prop.customType.constructor.name;
1075
+ // if the type is an ORM defined mapped type without `ensureComparable: true`,
1076
+ // we use just the type name, to have more performant hydration code
1077
+ const type = Utils.keys(t).find(type => {
1078
+ return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
1079
+ });
1080
+ if (type) {
1081
+ prop.type = type === 'datetime' ? 'Date' : type;
1082
+ }
1083
+ else {
1084
+ prop.customType = new prop.type();
1085
+ prop.type = prop.customType.constructor.name;
1086
+ }
1087
+ }
1088
+ if (simple) {
1089
+ return;
1112
1090
  }
1113
1091
  if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
1114
- prop.customType = new JsonType();
1092
+ prop.customType = new t.json();
1115
1093
  }
1116
1094
  if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
1117
- prop.customType = new JsonType();
1095
+ prop.customType = new t.json();
1096
+ }
1097
+ if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
1098
+ prop.customType = new t.json();
1118
1099
  }
1119
1100
  if (!prop.customType && prop.array && prop.items) {
1120
- prop.customType = new EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
1101
+ prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
1102
+ }
1103
+ const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
1104
+ if (objectEmbeddable && !prop.customType && isArray) {
1105
+ prop.customType = new t.json();
1121
1106
  }
1122
1107
  // for number arrays we make sure to convert the items to numbers
1123
1108
  if (!prop.customType && prop.type === 'number[]') {
1124
- prop.customType = new ArrayType(i => +i);
1109
+ prop.customType = new t.array(i => +i);
1125
1110
  }
1126
1111
  // `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
1127
- if (!prop.customType && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1128
- prop.customType = new ArrayType();
1112
+ if (!prop.customType && isArray) {
1113
+ prop.customType = new t.array();
1129
1114
  }
1130
1115
  if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
1131
- prop.customType = new BlobType();
1116
+ prop.customType = new t.blob();
1132
1117
  }
1133
1118
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1134
- prop.customType = new Uint8ArrayType();
1119
+ prop.customType = new t.uint8array();
1135
1120
  }
1136
1121
  const mappedType = this.getMappedType(prop);
1137
1122
  if (prop.fieldNames?.length === 1 && !prop.customType) {
1138
- [BigIntType, DoubleType, DecimalType, IntervalType, DateType]
1123
+ [t.bigint, t.double, t.decimal, t.interval, t.date]
1139
1124
  .filter(type => mappedType instanceof type)
1140
- .forEach(type => prop.customType = new type());
1125
+ .forEach((type) => prop.customType = new type());
1141
1126
  }
1142
1127
  if (prop.customType && !prop.columnTypes) {
1143
1128
  const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
1144
- if (prop.customType.compareAsType() === 'any' && ![JsonType].some(t => prop.customType instanceof t)) {
1129
+ if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
1145
1130
  prop.runtimeType ??= mappedType.runtimeType;
1146
1131
  }
1147
1132
  else {
1148
1133
  prop.runtimeType ??= prop.customType.runtimeType;
1149
1134
  }
1150
1135
  }
1136
+ else if (prop.runtimeType === 'object') {
1137
+ prop.runtimeType = mappedType.runtimeType;
1138
+ }
1151
1139
  else {
1152
1140
  prop.runtimeType ??= mappedType.runtimeType;
1153
1141
  }
@@ -1158,11 +1146,11 @@ export class MetadataDiscovery {
1158
1146
  prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
1159
1147
  prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
1160
1148
  prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
1161
- if (prop.customType instanceof BigIntType && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
1149
+ if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
1162
1150
  prop.customType.mode = prop.runtimeType.toLowerCase();
1163
1151
  }
1164
1152
  }
1165
- if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !prop.type?.toString().endsWith('[]')) {
1153
+ if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
1166
1154
  prop.type = prop.customType.name;
1167
1155
  }
1168
1156
  if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
@@ -1179,7 +1167,7 @@ export class MetadataDiscovery {
1179
1167
  }
1180
1168
  }
1181
1169
  }
1182
- if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof UnknownType)) {
1170
+ if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
1183
1171
  if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
1184
1172
  prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
1185
1173
  }
@@ -1222,7 +1210,7 @@ export class MetadataDiscovery {
1222
1210
  if (prop.kind === ReferenceKind.SCALAR) {
1223
1211
  const mappedType = this.getMappedType(prop);
1224
1212
  const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
1225
- if (mappedType instanceof UnknownType
1213
+ if (mappedType instanceof t.unknown
1226
1214
  && !prop.columnTypes
1227
1215
  // it could be a runtime type from reflect-metadata
1228
1216
  && !SCALAR_TYPES.includes(prop.type)
@@ -1266,7 +1254,7 @@ export class MetadataDiscovery {
1266
1254
  t = 'enum';
1267
1255
  }
1268
1256
  else if (prop.enum) {
1269
- t = prop.items?.every(item => Utils.isString(item)) ? 'enum' : 'tinyint';
1257
+ t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
1270
1258
  }
1271
1259
  if (t === 'Date') {
1272
1260
  t = 'datetime';
@@ -1305,30 +1293,6 @@ export class MetadataDiscovery {
1305
1293
  prop.index ??= true;
1306
1294
  }
1307
1295
  }
1308
- async getEntityClassOrSchema(path, name) {
1309
- const exports = await Utils.dynamicImport(path);
1310
- const targets = Object.values(exports)
1311
- .filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
1312
- // ignore class implementations that are linked from an EntitySchema
1313
- for (const item of targets) {
1314
- if (item instanceof EntitySchema) {
1315
- targets.forEach((item2, idx) => {
1316
- if (item.meta.class === item2) {
1317
- targets.splice(idx, 1);
1318
- }
1319
- });
1320
- }
1321
- }
1322
- if (targets.length > 0) {
1323
- return targets;
1324
- }
1325
- const target = exports.default ?? exports[name];
1326
- /* v8 ignore next 3 */
1327
- if (!target) {
1328
- throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
1329
- }
1330
- return [target];
1331
- }
1332
1296
  shouldForceConstructorUsage(meta) {
1333
1297
  const forceConstructor = this.config.get('forceEntityConstructor');
1334
1298
  if (Array.isArray(forceConstructor)) {