@mikro-orm/core 7.0.0-dev.8 → 7.0.0-dev.81

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 (181) hide show
  1. package/EntityManager.d.ts +85 -48
  2. package/EntityManager.js +300 -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 +6 -5
  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 +11 -7
  13. package/connections/Connection.js +16 -14
  14. package/drivers/DatabaseDriver.d.ts +11 -5
  15. package/drivers/DatabaseDriver.js +23 -11
  16. package/drivers/IDatabaseDriver.d.ts +27 -5
  17. package/entity/BaseEntity.d.ts +0 -1
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +98 -30
  20. package/entity/Collection.js +432 -93
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +15 -7
  23. package/entity/EntityFactory.d.ts +7 -0
  24. package/entity/EntityFactory.js +64 -41
  25. package/entity/EntityHelper.js +26 -9
  26. package/entity/EntityLoader.d.ts +5 -4
  27. package/entity/EntityLoader.js +73 -40
  28. package/entity/EntityRepository.d.ts +1 -1
  29. package/entity/Reference.d.ts +9 -7
  30. package/entity/Reference.js +33 -6
  31. package/entity/WrappedEntity.d.ts +2 -4
  32. package/entity/WrappedEntity.js +1 -5
  33. package/entity/defineEntity.d.ts +549 -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 +6 -2
  44. package/errors.js +14 -9
  45. package/events/EventSubscriber.d.ts +3 -1
  46. package/hydration/Hydrator.js +1 -2
  47. package/hydration/ObjectHydrator.d.ts +4 -4
  48. package/hydration/ObjectHydrator.js +36 -25
  49. package/index.d.ts +2 -2
  50. package/index.js +1 -2
  51. package/logging/DefaultLogger.d.ts +1 -1
  52. package/logging/SimpleLogger.d.ts +1 -1
  53. package/metadata/EntitySchema.d.ts +9 -13
  54. package/metadata/EntitySchema.js +44 -26
  55. package/metadata/MetadataDiscovery.d.ts +6 -9
  56. package/metadata/MetadataDiscovery.js +167 -206
  57. package/metadata/MetadataProvider.d.ts +11 -2
  58. package/metadata/MetadataProvider.js +44 -2
  59. package/metadata/MetadataStorage.d.ts +1 -6
  60. package/metadata/MetadataStorage.js +6 -18
  61. package/metadata/MetadataValidator.d.ts +0 -7
  62. package/metadata/MetadataValidator.js +4 -13
  63. package/metadata/discover-entities.d.ts +5 -0
  64. package/metadata/discover-entities.js +40 -0
  65. package/metadata/index.d.ts +1 -1
  66. package/metadata/index.js +1 -1
  67. package/metadata/types.d.ts +480 -0
  68. package/metadata/types.js +1 -0
  69. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  70. package/naming-strategy/AbstractNamingStrategy.js +8 -2
  71. package/naming-strategy/NamingStrategy.d.ts +11 -1
  72. package/not-supported.d.ts +2 -0
  73. package/not-supported.js +4 -0
  74. package/package.json +18 -10
  75. package/platforms/ExceptionConverter.js +1 -1
  76. package/platforms/Platform.d.ts +6 -10
  77. package/platforms/Platform.js +14 -39
  78. package/serialization/EntitySerializer.d.ts +2 -0
  79. package/serialization/EntitySerializer.js +32 -14
  80. package/serialization/EntityTransformer.js +22 -12
  81. package/serialization/SerializationContext.js +16 -13
  82. package/types/ArrayType.d.ts +1 -1
  83. package/types/ArrayType.js +2 -3
  84. package/types/BigIntType.d.ts +8 -6
  85. package/types/BigIntType.js +1 -1
  86. package/types/BlobType.d.ts +0 -1
  87. package/types/BlobType.js +0 -3
  88. package/types/BooleanType.d.ts +2 -1
  89. package/types/BooleanType.js +3 -0
  90. package/types/DecimalType.d.ts +6 -4
  91. package/types/DecimalType.js +3 -3
  92. package/types/DoubleType.js +2 -2
  93. package/types/JsonType.d.ts +1 -1
  94. package/types/JsonType.js +7 -2
  95. package/types/TinyIntType.js +1 -1
  96. package/types/Type.d.ts +2 -1
  97. package/types/Type.js +1 -1
  98. package/types/Uint8ArrayType.d.ts +0 -1
  99. package/types/Uint8ArrayType.js +1 -4
  100. package/types/index.d.ts +1 -1
  101. package/typings.d.ts +113 -77
  102. package/typings.js +41 -35
  103. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  104. package/unit-of-work/ChangeSetComputer.js +11 -9
  105. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  106. package/unit-of-work/ChangeSetPersister.js +58 -20
  107. package/unit-of-work/UnitOfWork.d.ts +8 -1
  108. package/unit-of-work/UnitOfWork.js +115 -57
  109. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  110. package/utils/AbstractSchemaGenerator.js +11 -9
  111. package/utils/Configuration.d.ts +757 -206
  112. package/utils/Configuration.js +139 -187
  113. package/utils/ConfigurationLoader.d.ts +1 -54
  114. package/utils/ConfigurationLoader.js +1 -352
  115. package/utils/Cursor.d.ts +3 -3
  116. package/utils/Cursor.js +4 -1
  117. package/utils/DataloaderUtils.d.ts +15 -5
  118. package/utils/DataloaderUtils.js +54 -8
  119. package/utils/EntityComparator.d.ts +8 -4
  120. package/utils/EntityComparator.js +111 -64
  121. package/utils/QueryHelper.d.ts +9 -1
  122. package/utils/QueryHelper.js +70 -9
  123. package/utils/RawQueryFragment.d.ts +36 -4
  124. package/utils/RawQueryFragment.js +35 -14
  125. package/utils/TransactionManager.d.ts +65 -0
  126. package/utils/TransactionManager.js +223 -0
  127. package/utils/Utils.d.ts +8 -97
  128. package/utils/Utils.js +88 -303
  129. package/utils/clone.js +2 -3
  130. package/utils/env-vars.d.ts +3 -0
  131. package/utils/env-vars.js +87 -0
  132. package/utils/fs-utils.d.ts +12 -0
  133. package/utils/fs-utils.js +96 -0
  134. package/utils/index.d.ts +2 -1
  135. package/utils/index.js +2 -1
  136. package/utils/upsert-utils.d.ts +7 -2
  137. package/utils/upsert-utils.js +55 -4
  138. package/decorators/Check.d.ts +0 -3
  139. package/decorators/Check.js +0 -13
  140. package/decorators/CreateRequestContext.d.ts +0 -3
  141. package/decorators/CreateRequestContext.js +0 -32
  142. package/decorators/Embeddable.d.ts +0 -8
  143. package/decorators/Embeddable.js +0 -11
  144. package/decorators/Embedded.d.ts +0 -18
  145. package/decorators/Embedded.js +0 -18
  146. package/decorators/Entity.d.ts +0 -18
  147. package/decorators/Entity.js +0 -12
  148. package/decorators/Enum.d.ts +0 -9
  149. package/decorators/Enum.js +0 -16
  150. package/decorators/Filter.d.ts +0 -2
  151. package/decorators/Filter.js +0 -8
  152. package/decorators/Formula.d.ts +0 -4
  153. package/decorators/Formula.js +0 -15
  154. package/decorators/Indexed.d.ts +0 -19
  155. package/decorators/Indexed.js +0 -20
  156. package/decorators/ManyToMany.d.ts +0 -40
  157. package/decorators/ManyToMany.js +0 -14
  158. package/decorators/ManyToOne.d.ts +0 -30
  159. package/decorators/ManyToOne.js +0 -14
  160. package/decorators/OneToMany.d.ts +0 -28
  161. package/decorators/OneToMany.js +0 -17
  162. package/decorators/OneToOne.d.ts +0 -24
  163. package/decorators/OneToOne.js +0 -7
  164. package/decorators/PrimaryKey.d.ts +0 -8
  165. package/decorators/PrimaryKey.js +0 -20
  166. package/decorators/Property.d.ts +0 -250
  167. package/decorators/Property.js +0 -32
  168. package/decorators/Transactional.d.ts +0 -13
  169. package/decorators/Transactional.js +0 -28
  170. package/decorators/hooks.d.ts +0 -16
  171. package/decorators/hooks.js +0 -47
  172. package/decorators/index.d.ts +0 -17
  173. package/decorators/index.js +0 -17
  174. package/entity/ArrayCollection.d.ts +0 -116
  175. package/entity/ArrayCollection.js +0 -402
  176. package/entity/EntityValidator.d.ts +0 -19
  177. package/entity/EntityValidator.js +0 -150
  178. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  179. package/metadata/ReflectMetadataProvider.js +0 -44
  180. package/utils/resolveContextProvider.d.ts +0 -10
  181. 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, 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,27 @@ 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
+ for (const entity of targets) {
157
+ if (typeof entity === 'string') {
158
+ const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
159
+ processed.push(...await discoverEntities(entity, { baseDir }));
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
+ return this.discoverReferences(processed);
149
166
  }
150
167
  discoverMissingTargets() {
151
168
  const unwrap = (type) => type
@@ -173,50 +190,20 @@ export class MetadataDiscovery {
173
190
  }
174
191
  tryDiscoverTargets(targets) {
175
192
  for (const target of targets) {
176
- if (typeof target === 'function' && target.name && !this.metadata.has(target.name)) {
177
- this.discoverReferences([target]);
193
+ const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
194
+ if (isDiscoverable && target.name && !this.metadata.has(target.name)) {
195
+ this.discoverReferences([target], false);
178
196
  this.discoverMissingTargets();
179
197
  }
180
198
  }
181
199
  }
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) {
200
+ discoverReferences(refs, validate = true) {
217
201
  const found = [];
218
202
  for (const entity of refs) {
219
- const schema = this.getSchema(this.prepare(entity));
203
+ if (typeof entity === 'string') {
204
+ throw new Error('Folder based discovery requires the async `MikroORM.init()` method.');
205
+ }
206
+ const schema = this.getSchema(entity);
220
207
  const meta = schema.init().meta;
221
208
  this.metadata.set(meta.className, meta);
222
209
  found.push(schema);
@@ -225,20 +212,27 @@ export class MetadataDiscovery {
225
212
  for (const meta of this.metadata) {
226
213
  let parent = meta.extends;
227
214
  if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.className)) {
228
- this.discoverReferences([parent]);
215
+ this.discoverReferences([parent], false);
229
216
  }
230
- /* v8 ignore next 3 */
217
+ if (typeof parent === 'function' && parent.name && !this.metadata.has(parent.name)) {
218
+ this.discoverReferences([parent], false);
219
+ }
220
+ /* v8 ignore next */
231
221
  if (!meta.class) {
232
222
  continue;
233
223
  }
234
224
  parent = Object.getPrototypeOf(meta.class);
235
225
  if (parent.name !== '' && !this.metadata.has(parent.name)) {
236
- this.discoverReferences([parent]);
226
+ this.discoverReferences([parent], false);
237
227
  }
238
228
  }
239
229
  for (const schema of found) {
240
230
  this.discoverEntity(schema);
241
231
  }
232
+ this.discoverMissingTargets();
233
+ if (validate) {
234
+ this.validateDiscovered(this.discovered);
235
+ }
242
236
  return this.discovered.filter(meta => found.find(m => m.name === meta.className));
243
237
  }
244
238
  reset(className) {
@@ -248,23 +242,13 @@ export class MetadataDiscovery {
248
242
  this.discovered.splice(exists, 1);
249
243
  }
250
244
  }
251
- prepare(entity) {
252
- /* v8 ignore next 3 */
253
- if ('schema' in entity && entity.schema instanceof EntitySchema) {
254
- return entity.schema;
255
- }
245
+ getSchema(entity) {
256
246
  if (EntitySchema.REGISTRY.has(entity)) {
257
- return EntitySchema.REGISTRY.get(entity);
247
+ entity = EntitySchema.REGISTRY.get(entity);
258
248
  }
259
- return entity;
260
- }
261
- getSchema(entity, filepath) {
262
249
  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;
250
+ const meta = Utils.copy(entity.meta, false);
251
+ return EntitySchema.fromMetadata(meta);
268
252
  }
269
253
  const path = entity[MetadataStorage.PATH_SYMBOL];
270
254
  if (path) {
@@ -279,16 +263,26 @@ export class MetadataDiscovery {
279
263
  schema.setClass(entity);
280
264
  return schema;
281
265
  }
282
- discoverEntity(schema, path) {
283
- this.logger.log('discovery', `- processing entity ${colors.cyan(schema.meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
266
+ getRootEntity(meta) {
267
+ const base = meta.extends && this.metadata.find(Utils.className(meta.extends));
268
+ if (!base || base === meta) { // make sure we do not fall into infinite loop
269
+ return meta;
270
+ }
271
+ const root = this.getRootEntity(base);
272
+ if (root.discriminatorColumn) {
273
+ return root;
274
+ }
275
+ return meta;
276
+ }
277
+ discoverEntity(schema) {
284
278
  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));
279
+ const path = meta.path;
280
+ this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
281
+ const root = this.getRootEntity(meta);
282
+ schema.meta.path = Utils.relativePath(meta.path, this.config.get('baseDir'));
283
+ const cache = this.metadataProvider.getCachedMetadata(meta, root);
288
284
  if (cache) {
289
285
  this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
290
- this.metadataProvider.loadFromCache(meta, cache);
291
- meta.root = root;
292
286
  this.discovered.push(meta);
293
287
  return;
294
288
  }
@@ -297,50 +291,18 @@ export class MetadataDiscovery {
297
291
  this.inferDefaultValue(meta, prop);
298
292
  }
299
293
  // 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);
294
+ this.metadataProvider.loadEntityMetadata(meta);
301
295
  if (!meta.collection && meta.name) {
302
296
  const entityName = root.discriminatorColumn ? root.name : meta.name;
303
297
  meta.collection = this.namingStrategy.classToTableName(entityName);
304
298
  }
305
- delete meta.root; // to allow caching (as root can contain cycles)
306
- this.saveToCache(meta);
299
+ this.metadataProvider.saveToCache(meta);
307
300
  meta.root = root;
308
301
  this.discovered.push(meta);
309
302
  }
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
303
  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
304
  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));
305
+ return Utils.defaultValue(prop, 'nullable', prop.optional || !prop.owner);
344
306
  }
345
307
  return Utils.defaultValue(prop, 'nullable', prop.optional);
346
308
  }
@@ -375,7 +337,7 @@ export class MetadataDiscovery {
375
337
  if (prop.joinColumns.length !== prop.columnTypes.length) {
376
338
  prop.columnTypes = prop.joinColumns.flatMap(field => {
377
339
  const matched = meta.props.find(p => p.fieldNames?.includes(field));
378
- /* v8 ignore next 3 */
340
+ /* v8 ignore next */
379
341
  if (!matched) {
380
342
  throw MetadataError.fromWrongForeignKey(meta, prop, 'columnTypes');
381
343
  }
@@ -497,10 +459,9 @@ export class MetadataDiscovery {
497
459
  }
498
460
  this.initOwnColumns(meta);
499
461
  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;
462
+ meta.serializedPrimaryKey ??= meta.props.find(prop => prop.serializedPrimaryKey)?.name;
463
+ if (meta.serializedPrimaryKey && meta.serializedPrimaryKey !== meta.primaryKeys[0]) {
464
+ meta.properties[meta.serializedPrimaryKey].persist ??= false;
504
465
  }
505
466
  if (this.platform.usesPivotTable()) {
506
467
  return Object.values(meta.properties)
@@ -539,7 +500,7 @@ export class MetadataDiscovery {
539
500
  }
540
501
  else if (fks.length >= 2) {
541
502
  [first, second] = fks;
542
- /* v8 ignore next 3 */
503
+ /* v8 ignore next */
543
504
  }
544
505
  else {
545
506
  return [];
@@ -618,7 +579,7 @@ export class MetadataDiscovery {
618
579
  }
619
580
  data.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.className, targetType + '_inverse', true, meta.className === targetType);
620
581
  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);
582
+ return this.metadata.set(data.className, EntitySchema.fromMetadata(data).init().meta);
622
583
  }
623
584
  defineFixedOrderProperty(prop, targetType) {
624
585
  const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
@@ -653,6 +614,7 @@ export class MetadataDiscovery {
653
614
  autoincrement: false,
654
615
  updateRule: prop.updateRule,
655
616
  deleteRule: prop.deleteRule,
617
+ createForeignKeyConstraint: prop.createForeignKeyConstraint,
656
618
  };
657
619
  if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
658
620
  ret.updateRule ??= 'no action';
@@ -739,12 +701,9 @@ export class MetadataDiscovery {
739
701
  Utils.keys(base.hooks).forEach(type => {
740
702
  meta.hooks[type] = Utils.unique([...base.hooks[type], ...(meta.hooks[type] || [])]);
741
703
  });
742
- if (meta.constructorParams.length === 0 && base.constructorParams.length > 0) {
704
+ if ((meta.constructorParams?.length ?? 0) === 0 && (base.constructorParams?.length ?? 0) > 0) {
743
705
  meta.constructorParams = [...base.constructorParams];
744
706
  }
745
- if (meta.toJsonParams.length === 0 && base.toJsonParams.length > 0) {
746
- meta.toJsonParams = [...base.toJsonParams];
747
- }
748
707
  return order;
749
708
  }
750
709
  initPolyEmbeddables(embeddedProp, discovered, visited = new Set()) {
@@ -765,6 +724,7 @@ export class MetadataDiscovery {
765
724
  delete prop.default;
766
725
  if (properties[prop.name] && properties[prop.name].type !== prop.type) {
767
726
  properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
727
+ properties[prop.name].runtimeType = 'any';
768
728
  return properties[prop.name];
769
729
  }
770
730
  return properties[prop.name] = prop;
@@ -819,9 +779,16 @@ export class MetadataDiscovery {
819
779
  }
820
780
  return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
821
781
  };
782
+ const isParentArray = (prop) => {
783
+ if (prop.array) {
784
+ return true;
785
+ }
786
+ return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
787
+ };
822
788
  const rootProperty = getRootProperty(embeddedProp);
823
789
  const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
824
790
  const object = isParentObject(embeddedProp);
791
+ const array = isParentArray(embeddedProp);
825
792
  this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
826
793
  // the prefix of the parent cannot be a boolean; it already passed here
827
794
  const prefix = this.getPrefix(embeddedProp, parentProperty);
@@ -834,7 +801,8 @@ export class MetadataDiscovery {
834
801
  meta.propertyOrder.set(name, (order += 0.01));
835
802
  embeddedProp.embeddedProps[prop.name] = meta.properties[name];
836
803
  meta.properties[name].persist ??= embeddedProp.persist;
837
- if (embeddedProp.nullable) {
804
+ const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
805
+ if (embeddedProp.nullable || refInArray) {
838
806
  meta.properties[name].nullable = true;
839
807
  }
840
808
  if (meta.properties[name].fieldNames) {
@@ -872,6 +840,7 @@ export class MetadataDiscovery {
872
840
  meta.properties[name].persist = false; // only virtual as we store the whole object
873
841
  meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
874
842
  meta.properties[name].object = true;
843
+ this.initCustomType(meta, meta.properties[name], false, true);
875
844
  }
876
845
  this.initEmbeddables(meta, meta.properties[name], visited);
877
846
  }
@@ -924,7 +893,7 @@ export class MetadataDiscovery {
924
893
  }
925
894
  let i = 1;
926
895
  Object.values(meta.properties).forEach(prop => {
927
- const newProp = Utils.copy(prop, false);
896
+ const newProp = { ...prop };
928
897
  if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
929
898
  const name = newProp.name;
930
899
  this.initFieldName(newProp, newProp.object);
@@ -940,7 +909,7 @@ export class MetadataDiscovery {
940
909
  newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
941
910
  }
942
911
  newProp.nullable = true;
943
- newProp.inherited = true;
912
+ newProp.inherited = !meta.root.properties[prop.name];
944
913
  meta.root.addProperty(newProp);
945
914
  });
946
915
  meta.collection = meta.root.collection;
@@ -965,7 +934,7 @@ export class MetadataDiscovery {
965
934
  }
966
935
  }
967
936
  initCheckConstraints(meta) {
968
- const map = this.createColumnMappingObject(meta);
937
+ const map = meta.createColumnMappingObject();
969
938
  for (const check of meta.checks) {
970
939
  const columns = check.property ? meta.properties[check.property].fieldNames : [];
971
940
  check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
@@ -975,7 +944,7 @@ export class MetadataDiscovery {
975
944
  }
976
945
  if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
977
946
  for (const prop of meta.props) {
978
- if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => Utils.isString(item))) {
947
+ if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
979
948
  this.initFieldName(prop);
980
949
  meta.checks.push({
981
950
  name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
@@ -1000,35 +969,28 @@ export class MetadataDiscovery {
1000
969
  }
1001
970
  return;
1002
971
  }
1003
- const map = this.createColumnMappingObject(meta);
972
+ const map = meta.createColumnMappingObject();
1004
973
  if (prop.generated instanceof Function) {
1005
974
  prop.generated = prop.generated(map);
1006
975
  }
1007
976
  }
1008
- createColumnMappingObject(meta) {
1009
- return Object.values(meta.properties).reduce((o, prop) => {
1010
- if (prop.fieldNames) {
1011
- o[prop.name] = prop.fieldNames[0];
1012
- }
1013
- return o;
1014
- }, {});
1015
- }
1016
- getDefaultVersionValue(prop) {
977
+ getDefaultVersionValue(meta, prop) {
1017
978
  if (typeof prop.defaultRaw !== 'undefined') {
1018
979
  return prop.defaultRaw;
1019
980
  }
1020
- /* v8 ignore next 3 */
981
+ /* v8 ignore next */
1021
982
  if (prop.default != null) {
1022
983
  return '' + this.platform.quoteVersionValue(prop.default, prop);
1023
984
  }
1024
- if (prop.type.toLowerCase() === 'date') {
985
+ this.initCustomType(meta, prop, true);
986
+ const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
987
+ if (type === 'Date') {
1025
988
  prop.length ??= this.platform.getDefaultVersionLength();
1026
989
  return this.platform.getCurrentTimestampSQL(prop.length);
1027
990
  }
1028
991
  return '1';
1029
992
  }
1030
993
  inferDefaultValue(meta, prop) {
1031
- /* v8 ignore next 3 */
1032
994
  if (!meta.class) {
1033
995
  return;
1034
996
  }
@@ -1064,7 +1026,7 @@ export class MetadataDiscovery {
1064
1026
  prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
1065
1027
  return;
1066
1028
  }
1067
- if (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
1029
+ if (Array.isArray(prop.default) && prop.customType) {
1068
1030
  val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
1069
1031
  }
1070
1032
  prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
@@ -1092,13 +1054,13 @@ export class MetadataDiscovery {
1092
1054
  if (prop.version) {
1093
1055
  this.initDefaultValue(prop);
1094
1056
  meta.versionProperty = prop.name;
1095
- prop.defaultRaw = this.getDefaultVersionValue(prop);
1057
+ prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
1096
1058
  }
1097
1059
  if (prop.concurrencyCheck && !prop.primary) {
1098
1060
  meta.concurrencyCheckKeys.add(prop.name);
1099
1061
  }
1100
1062
  }
1101
- initCustomType(meta, prop) {
1063
+ initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
1102
1064
  // `prop.type` might be actually instance of custom type class
1103
1065
  if (Type.isMappedType(prop.type) && !prop.customType) {
1104
1066
  prop.customType = prop.type;
@@ -1106,47 +1068,70 @@ export class MetadataDiscovery {
1106
1068
  }
1107
1069
  // `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
1108
1070
  if (typeof prop.type === 'function' && Type.isMappedType(prop.type.prototype) && !prop.customType) {
1109
- prop.customType = new prop.type();
1110
- prop.type = prop.customType.constructor.name;
1071
+ // if the type is an ORM defined mapped type without `ensureComparable: true`,
1072
+ // we use just the type name, to have more performant hydration code
1073
+ const type = Utils.keys(t).find(type => {
1074
+ return !Type.getType(t[type]).ensureComparable(meta, prop) && prop.type === t[type];
1075
+ });
1076
+ if (type) {
1077
+ prop.type = type === 'datetime' ? 'Date' : type;
1078
+ }
1079
+ else {
1080
+ prop.customType = new prop.type();
1081
+ prop.type = prop.customType.constructor.name;
1082
+ }
1083
+ }
1084
+ if (simple) {
1085
+ return;
1111
1086
  }
1112
1087
  if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
1113
- prop.customType = new JsonType();
1088
+ prop.customType = new t.json();
1114
1089
  }
1115
1090
  if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
1116
- prop.customType = new JsonType();
1091
+ prop.customType = new t.json();
1092
+ }
1093
+ if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
1094
+ prop.customType = new t.json();
1117
1095
  }
1118
1096
  if (!prop.customType && prop.array && prop.items) {
1119
- prop.customType = new EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
1097
+ prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
1098
+ }
1099
+ const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
1100
+ if (objectEmbeddable && !prop.customType && isArray) {
1101
+ prop.customType = new t.json();
1120
1102
  }
1121
1103
  // for number arrays we make sure to convert the items to numbers
1122
1104
  if (!prop.customType && prop.type === 'number[]') {
1123
- prop.customType = new ArrayType(i => +i);
1105
+ prop.customType = new t.array(i => +i);
1124
1106
  }
1125
1107
  // `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
1126
- if (!prop.customType && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1127
- prop.customType = new ArrayType();
1108
+ if (!prop.customType && isArray) {
1109
+ prop.customType = new t.array();
1128
1110
  }
1129
1111
  if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
1130
- prop.customType = new BlobType();
1112
+ prop.customType = new t.blob();
1131
1113
  }
1132
1114
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1133
- prop.customType = new Uint8ArrayType();
1115
+ prop.customType = new t.uint8array();
1134
1116
  }
1135
1117
  const mappedType = this.getMappedType(prop);
1136
1118
  if (prop.fieldNames?.length === 1 && !prop.customType) {
1137
- [BigIntType, DoubleType, DecimalType, IntervalType]
1119
+ [t.bigint, t.double, t.decimal, t.interval, t.date]
1138
1120
  .filter(type => mappedType instanceof type)
1139
- .forEach(type => prop.customType = new type());
1121
+ .forEach((type) => prop.customType = new type());
1140
1122
  }
1141
1123
  if (prop.customType && !prop.columnTypes) {
1142
1124
  const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
1143
- if (prop.customType.compareAsType() === 'any' && ![JsonType].some(t => prop.customType instanceof t)) {
1125
+ if (prop.customType.compareAsType() === 'any' && ![t.json].some(t => prop.customType instanceof t)) {
1144
1126
  prop.runtimeType ??= mappedType.runtimeType;
1145
1127
  }
1146
1128
  else {
1147
1129
  prop.runtimeType ??= prop.customType.runtimeType;
1148
1130
  }
1149
1131
  }
1132
+ else if (prop.runtimeType === 'object') {
1133
+ prop.runtimeType = mappedType.runtimeType;
1134
+ }
1150
1135
  else {
1151
1136
  prop.runtimeType ??= mappedType.runtimeType;
1152
1137
  }
@@ -1157,11 +1142,11 @@ export class MetadataDiscovery {
1157
1142
  prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
1158
1143
  prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
1159
1144
  prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
1160
- if (prop.customType instanceof BigIntType && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
1145
+ if (prop.customType instanceof t.bigint && ['string', 'bigint', 'number'].includes(prop.runtimeType.toLowerCase())) {
1161
1146
  prop.customType.mode = prop.runtimeType.toLowerCase();
1162
1147
  }
1163
1148
  }
1164
- if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !prop.type?.toString().endsWith('[]')) {
1149
+ if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
1165
1150
  prop.type = prop.customType.name;
1166
1151
  }
1167
1152
  if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
@@ -1178,7 +1163,7 @@ export class MetadataDiscovery {
1178
1163
  }
1179
1164
  }
1180
1165
  }
1181
- if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof UnknownType)) {
1166
+ if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
1182
1167
  if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
1183
1168
  prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
1184
1169
  }
@@ -1221,7 +1206,7 @@ export class MetadataDiscovery {
1221
1206
  if (prop.kind === ReferenceKind.SCALAR) {
1222
1207
  const mappedType = this.getMappedType(prop);
1223
1208
  const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
1224
- if (mappedType instanceof UnknownType
1209
+ if (mappedType instanceof t.unknown
1225
1210
  && !prop.columnTypes
1226
1211
  // it could be a runtime type from reflect-metadata
1227
1212
  && !SCALAR_TYPES.includes(prop.type)
@@ -1265,7 +1250,7 @@ export class MetadataDiscovery {
1265
1250
  t = 'enum';
1266
1251
  }
1267
1252
  else if (prop.enum) {
1268
- t = prop.items?.every(item => Utils.isString(item)) ? 'enum' : 'tinyint';
1253
+ t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
1269
1254
  }
1270
1255
  if (t === 'Date') {
1271
1256
  t = 'datetime';
@@ -1304,30 +1289,6 @@ export class MetadataDiscovery {
1304
1289
  prop.index ??= true;
1305
1290
  }
1306
1291
  }
1307
- async getEntityClassOrSchema(path, name) {
1308
- const exports = await Utils.dynamicImport(path);
1309
- const targets = Object.values(exports)
1310
- .filter(item => item instanceof EntitySchema || (item instanceof Function && MetadataStorage.isKnownEntity(item.name)));
1311
- // ignore class implementations that are linked from an EntitySchema
1312
- for (const item of targets) {
1313
- if (item instanceof EntitySchema) {
1314
- targets.forEach((item2, idx) => {
1315
- if (item.meta.class === item2) {
1316
- targets.splice(idx, 1);
1317
- }
1318
- });
1319
- }
1320
- }
1321
- if (targets.length > 0) {
1322
- return targets;
1323
- }
1324
- const target = exports.default ?? exports[name];
1325
- /* v8 ignore next 3 */
1326
- if (!target) {
1327
- throw MetadataError.entityNotFound(name, path.replace(this.config.get('baseDir'), '.'));
1328
- }
1329
- return [target];
1330
- }
1331
1292
  shouldForceConstructorUsage(meta) {
1332
1293
  const forceConstructor = this.config.get('forceEntityConstructor');
1333
1294
  if (Array.isArray(forceConstructor)) {