@mikro-orm/core 7.0.0-dev.9 → 7.0.0-dev.90

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 (191) hide show
  1. package/EntityManager.d.ts +77 -48
  2. package/EntityManager.js +288 -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 +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 +26 -12
  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/events/EventSubscriber.d.ts +3 -1
  48. package/hydration/Hydrator.js +1 -2
  49. package/hydration/ObjectHydrator.d.ts +4 -4
  50. package/hydration/ObjectHydrator.js +35 -25
  51. package/index.d.ts +2 -2
  52. package/index.js +1 -2
  53. package/logging/DefaultLogger.d.ts +1 -1
  54. package/logging/DefaultLogger.js +1 -0
  55. package/logging/SimpleLogger.d.ts +1 -1
  56. package/logging/index.d.ts +1 -0
  57. package/logging/index.js +1 -0
  58. package/logging/inspect.d.ts +2 -0
  59. package/logging/inspect.js +16 -0
  60. package/metadata/EntitySchema.d.ts +9 -13
  61. package/metadata/EntitySchema.js +44 -26
  62. package/metadata/MetadataDiscovery.d.ts +6 -9
  63. package/metadata/MetadataDiscovery.js +165 -205
  64. package/metadata/MetadataProvider.d.ts +11 -2
  65. package/metadata/MetadataProvider.js +44 -2
  66. package/metadata/MetadataStorage.d.ts +1 -6
  67. package/metadata/MetadataStorage.js +6 -18
  68. package/metadata/MetadataValidator.d.ts +0 -7
  69. package/metadata/MetadataValidator.js +0 -10
  70. package/metadata/discover-entities.d.ts +5 -0
  71. package/metadata/discover-entities.js +40 -0
  72. package/metadata/index.d.ts +1 -1
  73. package/metadata/index.js +1 -1
  74. package/metadata/types.d.ts +480 -0
  75. package/metadata/types.js +1 -0
  76. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  77. package/naming-strategy/AbstractNamingStrategy.js +8 -2
  78. package/naming-strategy/NamingStrategy.d.ts +11 -1
  79. package/not-supported.d.ts +2 -0
  80. package/not-supported.js +4 -0
  81. package/package.json +18 -10
  82. package/platforms/ExceptionConverter.js +1 -1
  83. package/platforms/Platform.d.ts +6 -13
  84. package/platforms/Platform.js +15 -41
  85. package/serialization/EntitySerializer.d.ts +2 -0
  86. package/serialization/EntitySerializer.js +32 -14
  87. package/serialization/EntityTransformer.js +22 -12
  88. package/serialization/SerializationContext.js +16 -13
  89. package/types/ArrayType.d.ts +1 -1
  90. package/types/ArrayType.js +2 -3
  91. package/types/BigIntType.d.ts +8 -6
  92. package/types/BigIntType.js +1 -1
  93. package/types/BlobType.d.ts +0 -1
  94. package/types/BlobType.js +0 -3
  95. package/types/BooleanType.d.ts +2 -1
  96. package/types/BooleanType.js +3 -0
  97. package/types/DecimalType.d.ts +6 -4
  98. package/types/DecimalType.js +3 -3
  99. package/types/DoubleType.js +2 -2
  100. package/types/EnumArrayType.js +1 -2
  101. package/types/JsonType.d.ts +1 -1
  102. package/types/JsonType.js +7 -2
  103. package/types/TinyIntType.js +1 -1
  104. package/types/Type.d.ts +2 -4
  105. package/types/Type.js +3 -3
  106. package/types/Uint8ArrayType.d.ts +0 -1
  107. package/types/Uint8ArrayType.js +1 -4
  108. package/types/index.d.ts +1 -1
  109. package/typings.d.ts +109 -73
  110. package/typings.js +38 -35
  111. package/unit-of-work/ChangeSet.d.ts +0 -3
  112. package/unit-of-work/ChangeSet.js +2 -2
  113. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  114. package/unit-of-work/ChangeSetComputer.js +11 -9
  115. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  116. package/unit-of-work/ChangeSetPersister.js +51 -19
  117. package/unit-of-work/UnitOfWork.d.ts +8 -1
  118. package/unit-of-work/UnitOfWork.js +91 -49
  119. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  120. package/utils/AbstractSchemaGenerator.js +11 -9
  121. package/utils/Configuration.d.ts +757 -206
  122. package/utils/Configuration.js +140 -188
  123. package/utils/ConfigurationLoader.d.ts +1 -54
  124. package/utils/ConfigurationLoader.js +1 -352
  125. package/utils/Cursor.d.ts +0 -3
  126. package/utils/Cursor.js +6 -3
  127. package/utils/DataloaderUtils.d.ts +15 -5
  128. package/utils/DataloaderUtils.js +54 -8
  129. package/utils/EntityComparator.d.ts +8 -4
  130. package/utils/EntityComparator.js +52 -17
  131. package/utils/QueryHelper.d.ts +9 -1
  132. package/utils/QueryHelper.js +70 -9
  133. package/utils/RawQueryFragment.d.ts +36 -13
  134. package/utils/RawQueryFragment.js +36 -16
  135. package/utils/TransactionManager.d.ts +65 -0
  136. package/utils/TransactionManager.js +223 -0
  137. package/utils/Utils.d.ts +8 -97
  138. package/utils/Utils.js +82 -302
  139. package/utils/clone.js +2 -3
  140. package/utils/env-vars.d.ts +3 -0
  141. package/utils/env-vars.js +87 -0
  142. package/utils/fs-utils.d.ts +12 -0
  143. package/utils/fs-utils.js +97 -0
  144. package/utils/index.d.ts +2 -1
  145. package/utils/index.js +2 -1
  146. package/utils/upsert-utils.d.ts +7 -2
  147. package/utils/upsert-utils.js +55 -4
  148. package/decorators/Check.d.ts +0 -3
  149. package/decorators/Check.js +0 -13
  150. package/decorators/CreateRequestContext.d.ts +0 -3
  151. package/decorators/CreateRequestContext.js +0 -32
  152. package/decorators/Embeddable.d.ts +0 -8
  153. package/decorators/Embeddable.js +0 -11
  154. package/decorators/Embedded.d.ts +0 -18
  155. package/decorators/Embedded.js +0 -18
  156. package/decorators/Entity.d.ts +0 -18
  157. package/decorators/Entity.js +0 -12
  158. package/decorators/Enum.d.ts +0 -9
  159. package/decorators/Enum.js +0 -16
  160. package/decorators/Filter.d.ts +0 -2
  161. package/decorators/Filter.js +0 -8
  162. package/decorators/Formula.d.ts +0 -4
  163. package/decorators/Formula.js +0 -15
  164. package/decorators/Indexed.d.ts +0 -19
  165. package/decorators/Indexed.js +0 -20
  166. package/decorators/ManyToMany.d.ts +0 -40
  167. package/decorators/ManyToMany.js +0 -14
  168. package/decorators/ManyToOne.d.ts +0 -30
  169. package/decorators/ManyToOne.js +0 -14
  170. package/decorators/OneToMany.d.ts +0 -28
  171. package/decorators/OneToMany.js +0 -17
  172. package/decorators/OneToOne.d.ts +0 -24
  173. package/decorators/OneToOne.js +0 -7
  174. package/decorators/PrimaryKey.d.ts +0 -8
  175. package/decorators/PrimaryKey.js +0 -20
  176. package/decorators/Property.d.ts +0 -250
  177. package/decorators/Property.js +0 -32
  178. package/decorators/Transactional.d.ts +0 -13
  179. package/decorators/Transactional.js +0 -28
  180. package/decorators/hooks.d.ts +0 -16
  181. package/decorators/hooks.js +0 -47
  182. package/decorators/index.d.ts +0 -17
  183. package/decorators/index.js +0 -17
  184. package/entity/ArrayCollection.d.ts +0 -116
  185. package/entity/ArrayCollection.js +0 -402
  186. package/entity/EntityValidator.d.ts +0 -19
  187. package/entity/EntityValidator.js +0 -150
  188. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  189. package/metadata/ReflectMetadataProvider.js +0 -44
  190. package/utils/resolveContextProvider.d.ts +0 -10
  191. 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,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()) {
@@ -820,9 +779,16 @@ export class MetadataDiscovery {
820
779
  }
821
780
  return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
822
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
+ };
823
788
  const rootProperty = getRootProperty(embeddedProp);
824
789
  const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
825
790
  const object = isParentObject(embeddedProp);
791
+ const array = isParentArray(embeddedProp);
826
792
  this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
827
793
  // the prefix of the parent cannot be a boolean; it already passed here
828
794
  const prefix = this.getPrefix(embeddedProp, parentProperty);
@@ -835,7 +801,8 @@ export class MetadataDiscovery {
835
801
  meta.propertyOrder.set(name, (order += 0.01));
836
802
  embeddedProp.embeddedProps[prop.name] = meta.properties[name];
837
803
  meta.properties[name].persist ??= embeddedProp.persist;
838
- 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) {
839
806
  meta.properties[name].nullable = true;
840
807
  }
841
808
  if (meta.properties[name].fieldNames) {
@@ -873,6 +840,7 @@ export class MetadataDiscovery {
873
840
  meta.properties[name].persist = false; // only virtual as we store the whole object
874
841
  meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
875
842
  meta.properties[name].object = true;
843
+ this.initCustomType(meta, meta.properties[name], false, true);
876
844
  }
877
845
  this.initEmbeddables(meta, meta.properties[name], visited);
878
846
  }
@@ -941,7 +909,7 @@ export class MetadataDiscovery {
941
909
  newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
942
910
  }
943
911
  newProp.nullable = true;
944
- newProp.inherited = true;
912
+ newProp.inherited = !meta.root.properties[prop.name];
945
913
  meta.root.addProperty(newProp);
946
914
  });
947
915
  meta.collection = meta.root.collection;
@@ -966,7 +934,7 @@ export class MetadataDiscovery {
966
934
  }
967
935
  }
968
936
  initCheckConstraints(meta) {
969
- const map = this.createColumnMappingObject(meta);
937
+ const map = meta.createColumnMappingObject();
970
938
  for (const check of meta.checks) {
971
939
  const columns = check.property ? meta.properties[check.property].fieldNames : [];
972
940
  check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
@@ -976,7 +944,7 @@ export class MetadataDiscovery {
976
944
  }
977
945
  if (this.platform.usesEnumCheckConstraints() && !meta.embeddable) {
978
946
  for (const prop of meta.props) {
979
- 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')) {
980
948
  this.initFieldName(prop);
981
949
  meta.checks.push({
982
950
  name: this.namingStrategy.indexName(meta.tableName, prop.fieldNames, 'check'),
@@ -1001,35 +969,28 @@ export class MetadataDiscovery {
1001
969
  }
1002
970
  return;
1003
971
  }
1004
- const map = this.createColumnMappingObject(meta);
972
+ const map = meta.createColumnMappingObject();
1005
973
  if (prop.generated instanceof Function) {
1006
974
  prop.generated = prop.generated(map);
1007
975
  }
1008
976
  }
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) {
977
+ getDefaultVersionValue(meta, prop) {
1018
978
  if (typeof prop.defaultRaw !== 'undefined') {
1019
979
  return prop.defaultRaw;
1020
980
  }
1021
- /* v8 ignore next 3 */
981
+ /* v8 ignore next */
1022
982
  if (prop.default != null) {
1023
983
  return '' + this.platform.quoteVersionValue(prop.default, prop);
1024
984
  }
1025
- 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') {
1026
988
  prop.length ??= this.platform.getDefaultVersionLength();
1027
989
  return this.platform.getCurrentTimestampSQL(prop.length);
1028
990
  }
1029
991
  return '1';
1030
992
  }
1031
993
  inferDefaultValue(meta, prop) {
1032
- /* v8 ignore next 3 */
1033
994
  if (!meta.class) {
1034
995
  return;
1035
996
  }
@@ -1065,7 +1026,7 @@ export class MetadataDiscovery {
1065
1026
  prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
1066
1027
  return;
1067
1028
  }
1068
- if (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
1029
+ if (Array.isArray(prop.default) && prop.customType) {
1069
1030
  val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
1070
1031
  }
1071
1032
  prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
@@ -1093,13 +1054,13 @@ export class MetadataDiscovery {
1093
1054
  if (prop.version) {
1094
1055
  this.initDefaultValue(prop);
1095
1056
  meta.versionProperty = prop.name;
1096
- prop.defaultRaw = this.getDefaultVersionValue(prop);
1057
+ prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
1097
1058
  }
1098
1059
  if (prop.concurrencyCheck && !prop.primary) {
1099
1060
  meta.concurrencyCheckKeys.add(prop.name);
1100
1061
  }
1101
1062
  }
1102
- initCustomType(meta, prop) {
1063
+ initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
1103
1064
  // `prop.type` might be actually instance of custom type class
1104
1065
  if (Type.isMappedType(prop.type) && !prop.customType) {
1105
1066
  prop.customType = prop.type;
@@ -1107,47 +1068,70 @@ export class MetadataDiscovery {
1107
1068
  }
1108
1069
  // `prop.type` might also be custom type class (not instance), so `typeof MyType` will give us `function`, not `object`
1109
1070
  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;
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;
1112
1086
  }
1113
1087
  if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
1114
- prop.customType = new JsonType();
1088
+ prop.customType = new t.json();
1115
1089
  }
1116
1090
  if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
1117
- 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();
1118
1095
  }
1119
1096
  if (!prop.customType && prop.array && prop.items) {
1120
- 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();
1121
1102
  }
1122
1103
  // for number arrays we make sure to convert the items to numbers
1123
1104
  if (!prop.customType && prop.type === 'number[]') {
1124
- prop.customType = new ArrayType(i => +i);
1105
+ prop.customType = new t.array(i => +i);
1125
1106
  }
1126
1107
  // `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();
1108
+ if (!prop.customType && isArray) {
1109
+ prop.customType = new t.array();
1129
1110
  }
1130
1111
  if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
1131
- prop.customType = new BlobType();
1112
+ prop.customType = new t.blob();
1132
1113
  }
1133
1114
  if (!prop.customType && prop.type?.toLowerCase() === 'uint8array') {
1134
- prop.customType = new Uint8ArrayType();
1115
+ prop.customType = new t.uint8array();
1135
1116
  }
1136
1117
  const mappedType = this.getMappedType(prop);
1137
1118
  if (prop.fieldNames?.length === 1 && !prop.customType) {
1138
- [BigIntType, DoubleType, DecimalType, IntervalType, DateType]
1119
+ [t.bigint, t.double, t.decimal, t.interval, t.date]
1139
1120
  .filter(type => mappedType instanceof type)
1140
- .forEach(type => prop.customType = new type());
1121
+ .forEach((type) => prop.customType = new type());
1141
1122
  }
1142
1123
  if (prop.customType && !prop.columnTypes) {
1143
1124
  const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
1144
- 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)) {
1145
1126
  prop.runtimeType ??= mappedType.runtimeType;
1146
1127
  }
1147
1128
  else {
1148
1129
  prop.runtimeType ??= prop.customType.runtimeType;
1149
1130
  }
1150
1131
  }
1132
+ else if (prop.runtimeType === 'object') {
1133
+ prop.runtimeType = mappedType.runtimeType;
1134
+ }
1151
1135
  else {
1152
1136
  prop.runtimeType ??= mappedType.runtimeType;
1153
1137
  }
@@ -1158,11 +1142,11 @@ export class MetadataDiscovery {
1158
1142
  prop.columnTypes ??= [prop.customType.getColumnType(prop, this.platform)];
1159
1143
  prop.hasConvertToJSValueSQL = !!prop.customType.convertToJSValueSQL && prop.customType.convertToJSValueSQL('', this.platform) !== '';
1160
1144
  prop.hasConvertToDatabaseValueSQL = !!prop.customType.convertToDatabaseValueSQL && prop.customType.convertToDatabaseValueSQL('', this.platform) !== '';
1161
- 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())) {
1162
1146
  prop.customType.mode = prop.runtimeType.toLowerCase();
1163
1147
  }
1164
1148
  }
1165
- if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !prop.type?.toString().endsWith('[]')) {
1149
+ if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
1166
1150
  prop.type = prop.customType.name;
1167
1151
  }
1168
1152
  if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {
@@ -1179,7 +1163,7 @@ export class MetadataDiscovery {
1179
1163
  }
1180
1164
  }
1181
1165
  }
1182
- if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof UnknownType)) {
1166
+ if (prop.kind === ReferenceKind.SCALAR && !(mappedType instanceof t.unknown)) {
1183
1167
  if (!prop.columnTypes && prop.nativeEnumName && meta.schema !== this.platform.getDefaultSchemaName() && meta.schema && !prop.nativeEnumName.includes('.')) {
1184
1168
  prop.columnTypes = [`${meta.schema}.${prop.nativeEnumName}`];
1185
1169
  }
@@ -1222,7 +1206,7 @@ export class MetadataDiscovery {
1222
1206
  if (prop.kind === ReferenceKind.SCALAR) {
1223
1207
  const mappedType = this.getMappedType(prop);
1224
1208
  const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp', 'any', 'unknown'];
1225
- if (mappedType instanceof UnknownType
1209
+ if (mappedType instanceof t.unknown
1226
1210
  && !prop.columnTypes
1227
1211
  // it could be a runtime type from reflect-metadata
1228
1212
  && !SCALAR_TYPES.includes(prop.type)
@@ -1266,7 +1250,7 @@ export class MetadataDiscovery {
1266
1250
  t = 'enum';
1267
1251
  }
1268
1252
  else if (prop.enum) {
1269
- t = prop.items?.every(item => Utils.isString(item)) ? 'enum' : 'tinyint';
1253
+ t = prop.items?.every(item => typeof item === 'string') ? 'enum' : 'tinyint';
1270
1254
  }
1271
1255
  if (t === 'Date') {
1272
1256
  t = 'datetime';
@@ -1305,30 +1289,6 @@ export class MetadataDiscovery {
1305
1289
  prop.index ??= true;
1306
1290
  }
1307
1291
  }
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
1292
  shouldForceConstructorUsage(meta) {
1333
1293
  const forceConstructor = this.config.get('forceEntityConstructor');
1334
1294
  if (Array.isArray(forceConstructor)) {