@mikro-orm/core 7.0.0-dev.21 → 7.0.0-dev.210

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 (211) hide show
  1. package/EntityManager.d.ts +99 -57
  2. package/EntityManager.js +302 -276
  3. package/MikroORM.d.ts +44 -35
  4. package/MikroORM.js +103 -143
  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 +25 -16
  15. package/drivers/DatabaseDriver.js +80 -35
  16. package/drivers/IDatabaseDriver.d.ts +44 -17
  17. package/entity/BaseEntity.d.ts +2 -2
  18. package/entity/BaseEntity.js +0 -3
  19. package/entity/Collection.d.ts +94 -29
  20. package/entity/Collection.js +434 -97
  21. package/entity/EntityAssigner.d.ts +1 -1
  22. package/entity/EntityAssigner.js +26 -18
  23. package/entity/EntityFactory.d.ts +13 -1
  24. package/entity/EntityFactory.js +84 -53
  25. package/entity/EntityHelper.d.ts +2 -2
  26. package/entity/EntityHelper.js +35 -15
  27. package/entity/EntityLoader.d.ts +6 -6
  28. package/entity/EntityLoader.js +119 -82
  29. package/entity/EntityRepository.d.ts +24 -4
  30. package/entity/EntityRepository.js +8 -2
  31. package/entity/Reference.d.ts +6 -5
  32. package/entity/Reference.js +34 -9
  33. package/entity/WrappedEntity.d.ts +2 -7
  34. package/entity/WrappedEntity.js +3 -8
  35. package/entity/defineEntity.d.ts +594 -0
  36. package/entity/defineEntity.js +533 -0
  37. package/entity/index.d.ts +3 -2
  38. package/entity/index.js +3 -2
  39. package/entity/utils.d.ts +7 -0
  40. package/entity/utils.js +16 -4
  41. package/entity/validators.d.ts +11 -0
  42. package/entity/validators.js +65 -0
  43. package/enums.d.ts +21 -5
  44. package/enums.js +15 -1
  45. package/errors.d.ts +23 -9
  46. package/errors.js +59 -21
  47. package/events/EventManager.d.ts +2 -1
  48. package/events/EventManager.js +19 -11
  49. package/hydration/Hydrator.js +1 -2
  50. package/hydration/ObjectHydrator.d.ts +4 -4
  51. package/hydration/ObjectHydrator.js +52 -33
  52. package/index.d.ts +2 -2
  53. package/index.js +1 -2
  54. package/logging/DefaultLogger.d.ts +1 -1
  55. package/logging/DefaultLogger.js +1 -0
  56. package/logging/SimpleLogger.d.ts +1 -1
  57. package/logging/colors.d.ts +1 -1
  58. package/logging/colors.js +7 -6
  59. package/logging/index.d.ts +1 -0
  60. package/logging/index.js +1 -0
  61. package/logging/inspect.d.ts +2 -0
  62. package/logging/inspect.js +11 -0
  63. package/metadata/EntitySchema.d.ts +40 -23
  64. package/metadata/EntitySchema.js +81 -34
  65. package/metadata/MetadataDiscovery.d.ts +7 -10
  66. package/metadata/MetadataDiscovery.js +391 -331
  67. package/metadata/MetadataProvider.d.ts +11 -2
  68. package/metadata/MetadataProvider.js +46 -2
  69. package/metadata/MetadataStorage.d.ts +13 -11
  70. package/metadata/MetadataStorage.js +70 -37
  71. package/metadata/MetadataValidator.d.ts +17 -9
  72. package/metadata/MetadataValidator.js +97 -40
  73. package/metadata/discover-entities.d.ts +5 -0
  74. package/metadata/discover-entities.js +40 -0
  75. package/metadata/index.d.ts +1 -1
  76. package/metadata/index.js +1 -1
  77. package/metadata/types.d.ts +502 -0
  78. package/metadata/types.js +1 -0
  79. package/naming-strategy/AbstractNamingStrategy.d.ts +12 -4
  80. package/naming-strategy/AbstractNamingStrategy.js +14 -2
  81. package/naming-strategy/EntityCaseNamingStrategy.d.ts +3 -3
  82. package/naming-strategy/EntityCaseNamingStrategy.js +6 -5
  83. package/naming-strategy/MongoNamingStrategy.d.ts +3 -3
  84. package/naming-strategy/MongoNamingStrategy.js +6 -6
  85. package/naming-strategy/NamingStrategy.d.ts +24 -4
  86. package/naming-strategy/UnderscoreNamingStrategy.d.ts +3 -3
  87. package/naming-strategy/UnderscoreNamingStrategy.js +6 -6
  88. package/not-supported.d.ts +2 -0
  89. package/not-supported.js +4 -0
  90. package/package.json +18 -11
  91. package/platforms/ExceptionConverter.js +1 -1
  92. package/platforms/Platform.d.ts +7 -13
  93. package/platforms/Platform.js +20 -43
  94. package/serialization/EntitySerializer.d.ts +5 -0
  95. package/serialization/EntitySerializer.js +47 -27
  96. package/serialization/EntityTransformer.js +28 -18
  97. package/serialization/SerializationContext.d.ts +6 -6
  98. package/serialization/SerializationContext.js +16 -13
  99. package/types/ArrayType.d.ts +1 -1
  100. package/types/ArrayType.js +2 -3
  101. package/types/BigIntType.d.ts +8 -6
  102. package/types/BigIntType.js +1 -1
  103. package/types/BlobType.d.ts +0 -1
  104. package/types/BlobType.js +0 -3
  105. package/types/BooleanType.d.ts +2 -1
  106. package/types/BooleanType.js +3 -0
  107. package/types/DecimalType.d.ts +6 -4
  108. package/types/DecimalType.js +3 -3
  109. package/types/DoubleType.js +2 -2
  110. package/types/EnumArrayType.js +1 -2
  111. package/types/JsonType.d.ts +1 -1
  112. package/types/JsonType.js +7 -2
  113. package/types/TinyIntType.js +1 -1
  114. package/types/Type.d.ts +2 -4
  115. package/types/Type.js +3 -3
  116. package/types/Uint8ArrayType.d.ts +0 -1
  117. package/types/Uint8ArrayType.js +1 -4
  118. package/types/index.d.ts +1 -1
  119. package/typings.d.ts +290 -137
  120. package/typings.js +59 -44
  121. package/unit-of-work/ChangeSet.d.ts +2 -6
  122. package/unit-of-work/ChangeSet.js +4 -5
  123. package/unit-of-work/ChangeSetComputer.d.ts +1 -3
  124. package/unit-of-work/ChangeSetComputer.js +26 -13
  125. package/unit-of-work/ChangeSetPersister.d.ts +5 -4
  126. package/unit-of-work/ChangeSetPersister.js +70 -34
  127. package/unit-of-work/CommitOrderCalculator.d.ts +12 -10
  128. package/unit-of-work/CommitOrderCalculator.js +13 -13
  129. package/unit-of-work/IdentityMap.d.ts +12 -0
  130. package/unit-of-work/IdentityMap.js +39 -1
  131. package/unit-of-work/UnitOfWork.d.ts +23 -3
  132. package/unit-of-work/UnitOfWork.js +175 -98
  133. package/utils/AbstractSchemaGenerator.d.ts +5 -5
  134. package/utils/AbstractSchemaGenerator.js +18 -16
  135. package/utils/AsyncContext.d.ts +6 -0
  136. package/utils/AsyncContext.js +42 -0
  137. package/utils/Configuration.d.ts +785 -207
  138. package/utils/Configuration.js +147 -190
  139. package/utils/ConfigurationLoader.d.ts +1 -54
  140. package/utils/ConfigurationLoader.js +1 -352
  141. package/utils/Cursor.d.ts +0 -3
  142. package/utils/Cursor.js +27 -11
  143. package/utils/DataloaderUtils.d.ts +15 -5
  144. package/utils/DataloaderUtils.js +64 -30
  145. package/utils/EntityComparator.d.ts +13 -9
  146. package/utils/EntityComparator.js +101 -42
  147. package/utils/QueryHelper.d.ts +14 -6
  148. package/utils/QueryHelper.js +87 -25
  149. package/utils/RawQueryFragment.d.ts +48 -25
  150. package/utils/RawQueryFragment.js +66 -70
  151. package/utils/RequestContext.js +2 -2
  152. package/utils/TransactionContext.js +2 -2
  153. package/utils/TransactionManager.d.ts +65 -0
  154. package/utils/TransactionManager.js +223 -0
  155. package/utils/Utils.d.ts +13 -126
  156. package/utils/Utils.js +100 -391
  157. package/utils/clone.js +8 -23
  158. package/utils/env-vars.d.ts +7 -0
  159. package/utils/env-vars.js +97 -0
  160. package/utils/fs-utils.d.ts +32 -0
  161. package/utils/fs-utils.js +178 -0
  162. package/utils/index.d.ts +2 -1
  163. package/utils/index.js +2 -1
  164. package/utils/upsert-utils.d.ts +9 -4
  165. package/utils/upsert-utils.js +55 -4
  166. package/decorators/Check.d.ts +0 -3
  167. package/decorators/Check.js +0 -13
  168. package/decorators/CreateRequestContext.d.ts +0 -3
  169. package/decorators/CreateRequestContext.js +0 -32
  170. package/decorators/Embeddable.d.ts +0 -8
  171. package/decorators/Embeddable.js +0 -11
  172. package/decorators/Embedded.d.ts +0 -12
  173. package/decorators/Embedded.js +0 -18
  174. package/decorators/Entity.d.ts +0 -18
  175. package/decorators/Entity.js +0 -12
  176. package/decorators/Enum.d.ts +0 -9
  177. package/decorators/Enum.js +0 -16
  178. package/decorators/Filter.d.ts +0 -2
  179. package/decorators/Filter.js +0 -8
  180. package/decorators/Formula.d.ts +0 -4
  181. package/decorators/Formula.js +0 -15
  182. package/decorators/Indexed.d.ts +0 -19
  183. package/decorators/Indexed.js +0 -20
  184. package/decorators/ManyToMany.d.ts +0 -40
  185. package/decorators/ManyToMany.js +0 -14
  186. package/decorators/ManyToOne.d.ts +0 -32
  187. package/decorators/ManyToOne.js +0 -14
  188. package/decorators/OneToMany.d.ts +0 -28
  189. package/decorators/OneToMany.js +0 -17
  190. package/decorators/OneToOne.d.ts +0 -26
  191. package/decorators/OneToOne.js +0 -7
  192. package/decorators/PrimaryKey.d.ts +0 -8
  193. package/decorators/PrimaryKey.js +0 -20
  194. package/decorators/Property.d.ts +0 -250
  195. package/decorators/Property.js +0 -32
  196. package/decorators/Transactional.d.ts +0 -13
  197. package/decorators/Transactional.js +0 -28
  198. package/decorators/hooks.d.ts +0 -16
  199. package/decorators/hooks.js +0 -47
  200. package/decorators/index.d.ts +0 -17
  201. package/decorators/index.js +0 -17
  202. package/entity/ArrayCollection.d.ts +0 -116
  203. package/entity/ArrayCollection.js +0 -402
  204. package/entity/EntityValidator.d.ts +0 -19
  205. package/entity/EntityValidator.js +0 -150
  206. package/exports.d.ts +0 -24
  207. package/exports.js +0 -23
  208. package/metadata/ReflectMetadataProvider.d.ts +0 -8
  209. package/metadata/ReflectMetadataProvider.js +0 -44
  210. package/utils/resolveContextProvider.d.ts +0 -10
  211. package/utils/resolveContextProvider.js +0 -28
package/typings.js CHANGED
@@ -3,6 +3,7 @@ import { Reference } from './entity/Reference.js';
3
3
  import { EntityHelper } from './entity/EntityHelper.js';
4
4
  import { Utils } from './utils/Utils.js';
5
5
  import { EntityComparator } from './utils/EntityComparator.js';
6
+ import { BaseEntity } from './entity/BaseEntity.js';
6
7
  export const EntityRepositoryType = Symbol('EntityRepositoryType');
7
8
  export const PrimaryKeyProp = Symbol('PrimaryKeyProp');
8
9
  export const OptionalProps = Symbol('OptionalProps');
@@ -25,38 +26,60 @@ export class EntityMetadata {
25
26
  this.referencingProperties = [];
26
27
  this.concurrencyCheckKeys = new Set();
27
28
  Object.assign(this, meta);
28
- }
29
- addProperty(prop, sync = true) {
30
- if (prop.pivotTable && !prop.pivotEntity) {
31
- prop.pivotEntity = prop.pivotTable;
29
+ const name = meta.className ?? meta.name;
30
+ if (!this.class && name) {
31
+ const Class = this.extends === BaseEntity
32
+ ? ({ [name]: class extends BaseEntity {
33
+ } })[name]
34
+ : ({ [name]: class {
35
+ } })[name];
36
+ this.class = Class;
32
37
  }
38
+ }
39
+ addProperty(prop) {
33
40
  this.properties[prop.name] = prop;
34
41
  this.propertyOrder.set(prop.name, this.props.length);
35
- /* v8 ignore next 3 */
36
- if (sync) {
37
- this.sync();
38
- }
42
+ this.sync();
39
43
  }
40
44
  removeProperty(name, sync = true) {
41
45
  delete this.properties[name];
42
46
  this.propertyOrder.delete(name);
43
- /* v8 ignore next 3 */
44
47
  if (sync) {
45
48
  this.sync();
46
49
  }
47
50
  }
48
- getPrimaryProps() {
49
- return this.primaryKeys.map(pk => this.properties[pk]);
51
+ getPrimaryProps(flatten = false) {
52
+ const pks = this.primaryKeys.map(pk => this.properties[pk]);
53
+ if (flatten) {
54
+ return pks.flatMap(pk => {
55
+ if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(pk.kind)) {
56
+ return pk.targetMeta.getPrimaryProps(true);
57
+ }
58
+ return [pk];
59
+ });
60
+ }
61
+ return pks;
50
62
  }
51
63
  getPrimaryProp() {
52
64
  return this.properties[this.primaryKeys[0]];
53
65
  }
66
+ createColumnMappingObject() {
67
+ return Object.values(this.properties).reduce((o, prop) => {
68
+ if (prop.fieldNames) {
69
+ o[prop.name] = prop.fieldNames[0];
70
+ }
71
+ return o;
72
+ }, {});
73
+ }
54
74
  get tableName() {
55
75
  return this.collection;
56
76
  }
57
77
  set tableName(name) {
58
78
  this.collection = name;
59
79
  }
80
+ get uniqueName() {
81
+ return this.tableName + '_' + this._id;
82
+ }
60
83
  sync(initIndexes = false, config) {
61
84
  this.root ??= this;
62
85
  const props = Object.values(this.properties).sort((a, b) => this.propertyOrder.get(a.name) - this.propertyOrder.get(b.name));
@@ -66,23 +89,32 @@ export class EntityMetadata {
66
89
  this.uniqueProps = this.props.filter(prop => prop.unique);
67
90
  this.getterProps = this.props.filter(prop => prop.getter);
68
91
  this.comparableProps = this.props.filter(prop => EntityComparator.isComparable(prop, this));
92
+ this.validateProps = this.props.filter(prop => {
93
+ if (prop.inherited || (prop.persist === false && prop.userDefined !== false)) {
94
+ return false;
95
+ }
96
+ return prop.kind === ReferenceKind.SCALAR && ['string', 'number', 'boolean', 'Date'].includes(prop.type);
97
+ });
69
98
  this.hydrateProps = this.props.filter(prop => {
70
99
  // `prop.userDefined` is either `undefined` or `false`
71
100
  const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
72
101
  // even if we don't have a setter, do not ignore value from database!
73
- const onlyGetter = prop.getter && !prop.setter;
102
+ const onlyGetter = prop.getter && !prop.setter && prop.persist === false;
74
103
  return !prop.inherited && prop.hydrate !== false && !discriminator && !prop.embedded && !onlyGetter;
75
104
  });
76
- this.trackingProps = this.hydrateProps
77
- .filter(prop => !prop.getter && !prop.setter && prop.trackChanges !== false)
78
- .filter(prop => ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind))
79
- .filter(prop => !prop.serializedPrimaryKey);
80
- this.selfReferencing = this.relations.some(prop => [this.className, this.root.className].includes(prop.targetMeta?.root.className ?? prop.type));
105
+ this.trackingProps = this.hydrateProps.filter(prop => {
106
+ return !prop.getter && !prop.setter && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind);
107
+ });
108
+ this.selfReferencing = this.relations.some(prop => {
109
+ return this.root.uniqueName === prop.targetMeta?.root.uniqueName;
110
+ });
81
111
  this.hasUniqueProps = this.uniques.length + this.uniqueProps.length > 0;
82
- this.virtual = !!this.expression;
112
+ // If `view` is set, this is a database view entity (not a virtual entity).
113
+ // Virtual entities evaluate expressions at query time, view entities create actual database views.
114
+ this.virtual = !!this.expression && !this.view;
83
115
  if (config) {
84
116
  for (const prop of this.props) {
85
- if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => Utils.isString(item))) {
117
+ if (prop.enum && !prop.nativeEnumName && prop.items?.every(item => typeof item === 'string')) {
86
118
  const name = config.getNamingStrategy().indexName(this.tableName, prop.fieldNames, 'check');
87
119
  const exists = this.checks.findIndex(check => check.name === name);
88
120
  if (exists !== -1) {
@@ -102,15 +134,14 @@ export class EntityMetadata {
102
134
  for (const hook of Utils.keys(this.hooks)) {
103
135
  this.hooks[hook] = Utils.removeDuplicates(this.hooks[hook]);
104
136
  }
105
- if (this.virtual) {
137
+ if (this.virtual || this.view) {
106
138
  this.readonly = true;
107
139
  }
108
140
  if (initIndexes && this.name) {
109
141
  this.props.forEach(prop => this.initIndexes(prop));
110
142
  }
111
143
  this.definedProperties = this.trackingProps.reduce((o, prop) => {
112
- const isCollection = [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind);
113
- const isReference = [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && (prop.inversedBy || prop.mappedBy) && !prop.mapToPk;
144
+ const isReference = (prop.inversedBy || prop.mappedBy) && !prop.mapToPk;
114
145
  if (isReference) {
115
146
  // eslint-disable-next-line @typescript-eslint/no-this-alias
116
147
  const meta = this;
@@ -129,10 +160,7 @@ export class EntityMetadata {
129
160
  wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
130
161
  // when propagation from inside hydration, we set the FK to the entity data immediately
131
162
  if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
132
- wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta.primaryKeys, true);
133
- }
134
- else {
135
- wrapped.__touched = !hydrator.isRunning();
163
+ wrapped.__originalEntityData[prop.name] = Utils.getPrimaryKeyValues(val, prop.targetMeta, true);
136
164
  }
137
165
  EntityHelper.propagate(meta, entity, this, prop, Reference.unwrapReference(val), old);
138
166
  },
@@ -140,23 +168,6 @@ export class EntityMetadata {
140
168
  configurable: true,
141
169
  };
142
170
  }
143
- if (prop.inherited || prop.primary || isCollection || prop.persist === false || prop.trackChanges === false || isReference || prop.embedded) {
144
- return o;
145
- }
146
- o[prop.name] = {
147
- get() {
148
- return this.__helper.__data[prop.name];
149
- },
150
- set(val) {
151
- if (typeof val === 'object' && !!val && '__raw' in val) {
152
- val.assign();
153
- }
154
- this.__helper.__data[prop.name] = val;
155
- this.__helper.__touched = !this.__helper.hydrator.isRunning();
156
- },
157
- enumerable: true,
158
- configurable: true,
159
- };
160
171
  return o;
161
172
  }, { __gettersDefined: { value: true, enumerable: false } });
162
173
  }
@@ -178,7 +189,7 @@ export class EntityMetadata {
178
189
  this.indexes.push({ properties: prop.name });
179
190
  prop.index = false;
180
191
  }
181
- /* v8 ignore next 4 */
192
+ /* v8 ignore next */
182
193
  if (owner && prop.fieldNames.length > 1 && prop.unique) {
183
194
  this.uniques.push({ properties: prop.name });
184
195
  prop.unique = false;
@@ -188,4 +199,8 @@ export class EntityMetadata {
188
199
  clone() {
189
200
  return this;
190
201
  }
202
+ /** @ignore */
203
+ [Symbol.for('nodejs.util.inspect.custom')]() {
204
+ return `[${this.constructor.name}<${this.className}>]`;
205
+ }
191
206
  }
@@ -1,4 +1,3 @@
1
- import { inspect } from 'node:util';
2
1
  import type { EntityData, EntityMetadata, EntityDictionary, Primary } from '../typings.js';
3
2
  export declare class ChangeSet<T extends object> {
4
3
  entity: T;
@@ -10,13 +9,10 @@ export declare class ChangeSet<T extends object> {
10
9
  constructor(entity: T, type: ChangeSetType, payload: EntityDictionary<T>, meta: EntityMetadata<T>);
11
10
  getPrimaryKey(object?: boolean): Primary<T> | null;
12
11
  getSerializedPrimaryKey(): string | null;
13
- /** @ignore */
14
- [inspect.custom](depth?: number): string;
15
12
  }
16
13
  export interface ChangeSet<T> {
17
- name: string;
18
- rootName: string;
19
- collection: string;
14
+ meta: EntityMetadata<T>;
15
+ rootMeta: EntityMetadata<T>;
20
16
  schema?: string;
21
17
  type: ChangeSetType;
22
18
  entity: T;
@@ -1,6 +1,6 @@
1
- import { inspect } from 'node:util';
2
1
  import { helper } from '../entity/wrap.js';
3
2
  import { Utils } from '../utils/Utils.js';
3
+ import { inspect } from '../logging/inspect.js';
4
4
  export class ChangeSet {
5
5
  entity;
6
6
  type;
@@ -13,9 +13,8 @@ export class ChangeSet {
13
13
  this.type = type;
14
14
  this.payload = payload;
15
15
  this.meta = meta;
16
- this.name = meta.className;
17
- this.rootName = meta.root.className;
18
- this.collection = meta.root.collection;
16
+ this.meta = meta;
17
+ this.rootMeta = meta.root;
19
18
  this.schema = helper(entity).__schema ?? meta.root.schema;
20
19
  }
21
20
  getPrimaryKey(object = false) {
@@ -46,7 +45,7 @@ export class ChangeSet {
46
45
  return this.serializedPrimaryKey;
47
46
  }
48
47
  /** @ignore */
49
- [inspect.custom](depth = 2) {
48
+ [Symbol.for('nodejs.util.inspect.custom')](depth = 2) {
50
49
  const object = { ...this };
51
50
  const hidden = ['meta', 'serializedPrimaryKey'];
52
51
  hidden.forEach(k => delete object[k]);
@@ -2,19 +2,17 @@ import { type Configuration } from '../utils/Configuration.js';
2
2
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
3
3
  import type { AnyEntity } from '../typings.js';
4
4
  import { ChangeSet } from './ChangeSet.js';
5
- import { type EntityValidator } from '../entity/EntityValidator.js';
6
5
  import { type Collection } from '../entity/Collection.js';
7
6
  import type { Platform } from '../platforms/Platform.js';
8
7
  import type { EntityManager } from '../EntityManager.js';
9
8
  export declare class ChangeSetComputer {
10
- private readonly validator;
11
9
  private readonly collectionUpdates;
12
10
  private readonly metadata;
13
11
  private readonly platform;
14
12
  private readonly config;
15
13
  private readonly em;
16
14
  private readonly comparator;
17
- constructor(validator: EntityValidator, collectionUpdates: Set<Collection<AnyEntity>>, metadata: MetadataStorage, platform: Platform, config: Configuration, em: EntityManager);
15
+ constructor(collectionUpdates: Set<Collection<AnyEntity>>, metadata: MetadataStorage, platform: Platform, config: Configuration, em: EntityManager);
18
16
  computeChangeSet<T extends object>(entity: T): ChangeSet<T> | null;
19
17
  /**
20
18
  * Traverses entity graph and executes `onCreate` and `onUpdate` methods, assigning the values to given properties.
@@ -1,17 +1,16 @@
1
1
  import { Utils } from '../utils/Utils.js';
2
2
  import { ChangeSet, ChangeSetType } from './ChangeSet.js';
3
3
  import { helper } from '../entity/wrap.js';
4
+ import { validateEntity } from '../entity/validators.js';
4
5
  import { ReferenceKind } from '../enums.js';
5
6
  export class ChangeSetComputer {
6
- validator;
7
7
  collectionUpdates;
8
8
  metadata;
9
9
  platform;
10
10
  config;
11
11
  em;
12
12
  comparator;
13
- constructor(validator, collectionUpdates, metadata, platform, config, em) {
14
- this.validator = validator;
13
+ constructor(collectionUpdates, metadata, platform, config, em) {
15
14
  this.collectionUpdates = collectionUpdates;
16
15
  this.metadata = metadata;
17
16
  this.platform = platform;
@@ -20,7 +19,7 @@ export class ChangeSetComputer {
20
19
  this.comparator = this.config.getComparator(this.metadata);
21
20
  }
22
21
  computeChangeSet(entity) {
23
- const meta = this.metadata.get(entity.constructor.name);
22
+ const meta = this.metadata.get(entity.constructor);
24
23
  if (meta.readonly) {
25
24
  return null;
26
25
  }
@@ -34,20 +33,21 @@ export class ChangeSetComputer {
34
33
  this.processPropertyInitializers(entity, prop, type, map);
35
34
  }
36
35
  }
37
- if (type === ChangeSetType.UPDATE && !wrapped.__initialized && !wrapped.isTouched()) {
38
- return null;
36
+ if (type === ChangeSetType.UPDATE && !wrapped.__initialized) {
37
+ const data = this.comparator.prepareEntity(entity);
38
+ if (Utils.equals(data, wrapped.__originalEntityData)) {
39
+ return null;
40
+ }
39
41
  }
40
42
  const changeSet = new ChangeSet(entity, type, this.computePayload(entity), meta);
41
43
  changeSet.originalEntity = wrapped.__originalEntityData;
42
- if (this.config.get('validate')) {
43
- this.validator.validate(changeSet.entity, changeSet.payload, meta);
44
- }
45
44
  for (const prop of meta.relations.filter(prop => prop.persist !== false || prop.userDefined === false)) {
46
45
  this.processProperty(changeSet, prop);
47
46
  }
48
47
  if (changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload)) {
49
48
  return null;
50
49
  }
50
+ validateEntity(changeSet.entity, meta);
51
51
  // Execute `onCreate` and `onUpdate` on properties recursively, saves `onUpdate` results
52
52
  // to the `map` as we want to apply those only if something else changed.
53
53
  if (type === ChangeSetType.UPDATE) {
@@ -91,7 +91,7 @@ export class ChangeSetComputer {
91
91
  computePayload(entity, ignoreUndefined = false) {
92
92
  const data = this.comparator.prepareEntity(entity);
93
93
  const wrapped = helper(entity);
94
- const entityName = wrapped.__meta.className;
94
+ const entityName = wrapped.__meta.class;
95
95
  const originalEntityData = wrapped.__originalEntityData;
96
96
  if (!wrapped.__initialized) {
97
97
  for (const prop of wrapped.__meta.primaryKeys) {
@@ -132,7 +132,16 @@ export class ChangeSetComputer {
132
132
  const targets = Utils.unwrapProperty(changeSet.entity, changeSet.meta, prop);
133
133
  targets.forEach(([target, idx]) => {
134
134
  if (!target.__helper.hasPrimaryKey()) {
135
- Utils.setPayloadProperty(changeSet.payload, this.metadata.find(changeSet.name), prop, target.__helper.__identifier, idx);
135
+ // When targetKey is set, use that property value instead of the PK identifier
136
+ let value = prop.targetKey ? target[prop.targetKey] : target.__helper.__identifier;
137
+ /* v8 ignore next */
138
+ if (prop.targetKey && prop.targetMeta) {
139
+ const targetProp = prop.targetMeta.properties[prop.targetKey];
140
+ if (targetProp?.customType) {
141
+ value = targetProp.customType.convertToDatabaseValue(value, this.platform, { mode: 'serialization' });
142
+ }
143
+ }
144
+ Utils.setPayloadProperty(changeSet.payload, changeSet.meta, prop, value, idx);
136
145
  }
137
146
  });
138
147
  }
@@ -141,9 +150,13 @@ export class ChangeSetComputer {
141
150
  if (!target.isDirty() && changeSet.type !== ChangeSetType.CREATE) {
142
151
  return;
143
152
  }
144
- this.collectionUpdates.add(target);
153
+ if (target.isDirty()) {
154
+ this.collectionUpdates.add(target);
155
+ }
145
156
  if (prop.owner && !this.platform.usesPivotTable()) {
146
- changeSet.payload[prop.name] = target.getItems(false).map((item) => item.__helper.__identifier ?? item.__helper.getPrimaryKey());
157
+ changeSet.payload[prop.name] = target.getItems(false).map((item) => {
158
+ return item.__helper.__identifier ?? item.__helper.getPrimaryKey();
159
+ });
147
160
  }
148
161
  }
149
162
  }
@@ -1,29 +1,30 @@
1
1
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
2
2
  import type { Dictionary, EntityDictionary, EntityMetadata, IHydrator } from '../typings.js';
3
3
  import { type EntityFactory } from '../entity/EntityFactory.js';
4
- import { type EntityValidator } from '../entity/EntityValidator.js';
5
4
  import { type ChangeSet } from './ChangeSet.js';
6
5
  import { type Configuration } from '../utils/Configuration.js';
7
6
  import type { DriverMethodOptions, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
7
+ import type { EntityManager } from '../EntityManager.js';
8
8
  export declare class ChangeSetPersister {
9
9
  private readonly driver;
10
10
  private readonly metadata;
11
11
  private readonly hydrator;
12
12
  private readonly factory;
13
- private readonly validator;
14
13
  private readonly config;
14
+ private readonly em;
15
15
  private readonly platform;
16
16
  private readonly comparator;
17
17
  private readonly usesReturningStatement;
18
- constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration);
18
+ constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, config: Configuration, em: EntityManager);
19
19
  executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
20
20
  executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
21
21
  executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
22
22
  private runForEachSchema;
23
+ private validateRequired;
23
24
  private processProperties;
24
25
  private persistNewEntity;
25
26
  private persistNewEntities;
26
- private propagateSchemaFromMetadata;
27
+ private prepareOptions;
27
28
  private persistNewEntitiesBatch;
28
29
  private persistManagedEntity;
29
30
  private persistManagedEntities;
@@ -3,25 +3,25 @@ import { helper } from '../entity/wrap.js';
3
3
  import { ChangeSetType } from './ChangeSet.js';
4
4
  import { isRaw } from '../utils/RawQueryFragment.js';
5
5
  import { Utils } from '../utils/Utils.js';
6
- import { OptimisticLockError } from '../errors.js';
6
+ import { OptimisticLockError, ValidationError } from '../errors.js';
7
7
  import { ReferenceKind } from '../enums.js';
8
8
  export class ChangeSetPersister {
9
9
  driver;
10
10
  metadata;
11
11
  hydrator;
12
12
  factory;
13
- validator;
14
13
  config;
14
+ em;
15
15
  platform;
16
16
  comparator;
17
17
  usesReturningStatement;
18
- constructor(driver, metadata, hydrator, factory, validator, config) {
18
+ constructor(driver, metadata, hydrator, factory, config, em) {
19
19
  this.driver = driver;
20
20
  this.metadata = metadata;
21
21
  this.hydrator = hydrator;
22
22
  this.factory = factory;
23
- this.validator = validator;
24
23
  this.config = config;
24
+ this.em = em;
25
25
  this.platform = this.driver.getPlatform();
26
26
  this.comparator = this.config.getComparator(this.metadata);
27
27
  this.usesReturningStatement = this.platform.usesReturningStatement() || this.platform.usesOutputStatement();
@@ -30,7 +30,7 @@ export class ChangeSetPersister {
30
30
  if (!withSchema) {
31
31
  return this.runForEachSchema(changeSets, 'executeInserts', options);
32
32
  }
33
- const meta = this.metadata.find(changeSets[0].name);
33
+ const meta = changeSets[0].meta;
34
34
  changeSets.forEach(changeSet => this.processProperties(changeSet));
35
35
  if (changeSets.length > 1 && this.config.get('useBatchInserts', this.platform.usesBatchInserts())) {
36
36
  return this.persistNewEntities(meta, changeSets, options);
@@ -43,9 +43,12 @@ export class ChangeSetPersister {
43
43
  if (!withSchema) {
44
44
  return this.runForEachSchema(changeSets, 'executeUpdates', options, batched);
45
45
  }
46
- const meta = this.metadata.find(changeSets[0].name);
46
+ const meta = changeSets[0].meta;
47
47
  changeSets.forEach(changeSet => this.processProperties(changeSet));
48
- if (batched && changeSets.length > 1 && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
48
+ // For STI with conflicting fieldNames (renamedFrom properties), we can't batch mixed child types
49
+ const hasSTIConflicts = meta.root.props.some(p => p.renamedFrom);
50
+ const hasMixedTypes = hasSTIConflicts && changeSets.some(cs => cs.meta.class !== meta.class);
51
+ if (batched && changeSets.length > 1 && !hasMixedTypes && this.config.get('useBatchUpdates', this.platform.usesBatchUpdates())) {
49
52
  return this.persistManagedEntities(meta, changeSets, options);
50
53
  }
51
54
  for (const changeSet of changeSets) {
@@ -62,8 +65,8 @@ export class ChangeSetPersister {
62
65
  for (let i = 0; i < changeSets.length; i += size) {
63
66
  const chunk = changeSets.slice(i, i + size);
64
67
  const pks = chunk.map(cs => cs.getPrimaryKey());
65
- options = this.propagateSchemaFromMetadata(meta, options);
66
- await this.driver.nativeDelete(meta.root.className, { [pk]: { $in: pks } }, options);
68
+ options = this.prepareOptions(meta, options);
69
+ await this.driver.nativeDelete(meta.root.class, { [pk]: { $in: pks } }, options);
67
70
  }
68
71
  }
69
72
  async runForEachSchema(changeSets, method, options, ...args) {
@@ -79,21 +82,41 @@ export class ChangeSetPersister {
79
82
  await this[method](group, ...args, options, true);
80
83
  }
81
84
  }
85
+ validateRequired(entity) {
86
+ const wrapped = helper(entity);
87
+ for (const prop of wrapped.__meta.props) {
88
+ if (!prop.nullable &&
89
+ !prop.autoincrement &&
90
+ !prop.default &&
91
+ !prop.defaultRaw &&
92
+ !prop.onCreate &&
93
+ !prop.generated &&
94
+ !prop.embedded &&
95
+ ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
96
+ prop.name !== wrapped.__meta.root.discriminatorColumn &&
97
+ prop.type !== 'ObjectId' &&
98
+ prop.persist !== false &&
99
+ entity[prop.name] == null) {
100
+ throw ValidationError.propertyRequired(entity, prop);
101
+ }
102
+ }
103
+ }
82
104
  processProperties(changeSet) {
83
- const meta = this.metadata.find(changeSet.name);
105
+ const meta = changeSet.meta;
84
106
  for (const prop of meta.relations) {
85
107
  this.processProperty(changeSet, prop);
86
108
  }
87
109
  if (changeSet.type === ChangeSetType.CREATE && this.config.get('validateRequired')) {
88
- this.validator.validateRequired(changeSet.entity);
110
+ this.validateRequired(changeSet.entity);
89
111
  }
90
112
  }
91
113
  async persistNewEntity(meta, changeSet, options) {
92
114
  const wrapped = helper(changeSet.entity);
93
- options = this.propagateSchemaFromMetadata(meta, options, {
115
+ options = this.prepareOptions(meta, options, {
94
116
  convertCustomTypes: false,
95
117
  });
96
- const res = await this.driver.nativeInsertMany(meta.className, [changeSet.payload], options);
118
+ // Use changeSet's own meta for STI entities to get correct field mappings
119
+ const res = await this.driver.nativeInsertMany(changeSet.meta.class, [changeSet.payload], options);
97
120
  if (!wrapped.hasPrimaryKey()) {
98
121
  this.mapPrimaryKey(meta, res.insertId, changeSet);
99
122
  }
@@ -116,19 +139,21 @@ export class ChangeSetPersister {
116
139
  }
117
140
  }
118
141
  }
119
- propagateSchemaFromMetadata(meta, options, additionalOptions) {
142
+ prepareOptions(meta, options, additionalOptions) {
143
+ const loggerContext = Utils.merge({ id: this.em._id }, this.em.getLoggerContext({ disableContextResolution: true }));
120
144
  return {
121
145
  ...options,
122
146
  ...additionalOptions,
123
147
  schema: options?.schema ?? meta.schema,
148
+ loggerContext,
124
149
  };
125
150
  }
126
151
  async persistNewEntitiesBatch(meta, changeSets, options) {
127
- options = this.propagateSchemaFromMetadata(meta, options, {
152
+ options = this.prepareOptions(meta, options, {
128
153
  convertCustomTypes: false,
129
154
  processCollections: false,
130
155
  });
131
- const res = await this.driver.nativeInsertMany(meta.className, changeSets.map(cs => cs.payload), options);
156
+ const res = await this.driver.nativeInsertMany(meta.class, changeSets.map(cs => cs.payload), options);
132
157
  for (let i = 0; i < changeSets.length; i++) {
133
158
  const changeSet = changeSets[i];
134
159
  const wrapped = helper(changeSet.entity);
@@ -146,7 +171,7 @@ export class ChangeSetPersister {
146
171
  }
147
172
  }
148
173
  async persistManagedEntity(changeSet, options) {
149
- const meta = this.metadata.find(changeSet.name);
174
+ const meta = changeSet.meta;
150
175
  const res = await this.updateEntity(meta, changeSet, options);
151
176
  this.checkOptimisticLock(meta, changeSet, res);
152
177
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
@@ -175,7 +200,7 @@ export class ChangeSetPersister {
175
200
  }
176
201
  async persistManagedEntitiesBatch(meta, changeSets, options) {
177
202
  await this.checkOptimisticLocks(meta, changeSets, options);
178
- options = this.propagateSchemaFromMetadata(meta, options, {
203
+ options = this.prepareOptions(meta, options, {
179
204
  convertCustomTypes: false,
180
205
  processCollections: false,
181
206
  });
@@ -187,7 +212,7 @@ export class ChangeSetPersister {
187
212
  cond.push(where);
188
213
  payload.push(changeSet.payload);
189
214
  }
190
- const res = await this.driver.nativeUpdateMany(meta.className, cond, payload, options);
215
+ const res = await this.driver.nativeUpdateMany(meta.class, cond, payload, options);
191
216
  const map = new Map();
192
217
  res.rows?.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
193
218
  for (const changeSet of changeSets) {
@@ -235,17 +260,17 @@ export class ChangeSetPersister {
235
260
  }
236
261
  async updateEntity(meta, changeSet, options) {
237
262
  const cond = changeSet.getPrimaryKey(true);
238
- options = this.propagateSchemaFromMetadata(meta, options, {
263
+ options = this.prepareOptions(meta, options, {
239
264
  convertCustomTypes: false,
240
265
  });
241
266
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSet.entity[meta.versionProperty] == null)) {
242
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
267
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
243
268
  }
244
269
  if (meta.versionProperty) {
245
270
  cond[meta.versionProperty] = this.platform.quoteVersionValue(changeSet.entity[meta.versionProperty], meta.properties[meta.versionProperty]);
246
271
  }
247
272
  this.checkConcurrencyKeys(meta, changeSet, cond);
248
- return this.driver.nativeUpdate(changeSet.name, cond, changeSet.payload, options);
273
+ return this.driver.nativeUpdate(changeSet.meta.class, cond, changeSet.payload, options);
249
274
  }
250
275
  async checkOptimisticLocks(meta, changeSets, options) {
251
276
  if (meta.concurrencyCheckKeys.size === 0 && (!meta.versionProperty || changeSets.every(cs => cs.entity[meta.versionProperty] == null))) {
@@ -262,10 +287,10 @@ export class ChangeSetPersister {
262
287
  return cond;
263
288
  });
264
289
  const primaryKeys = meta.primaryKeys.concat(...meta.concurrencyCheckKeys);
265
- options = this.propagateSchemaFromMetadata(meta, options, {
290
+ options = this.prepareOptions(meta, options, {
266
291
  fields: primaryKeys,
267
292
  });
268
- const res = await this.driver.find(meta.root.className, { $or }, options);
293
+ const res = await this.driver.find(meta.root.class, { $or }, options);
269
294
  if (res.length !== changeSets.length) {
270
295
  const compare = (a, b, keys) => keys.every(k => a[k] === b[k]);
271
296
  const entity = changeSets.find(cs => {
@@ -286,11 +311,22 @@ export class ChangeSetPersister {
286
311
  async reloadVersionValues(meta, changeSets, options) {
287
312
  const reloadProps = meta.versionProperty && !this.usesReturningStatement ? [meta.properties[meta.versionProperty]] : [];
288
313
  if (changeSets[0].type === ChangeSetType.CREATE) {
289
- // do not reload things that already had a runtime value
290
- meta.props
291
- .filter(prop => prop.persist !== false && (prop.autoincrement || prop.generated || prop.defaultRaw))
292
- .filter(prop => (changeSets[0].entity[prop.name] == null && prop.defaultRaw !== 'null') || isRaw(changeSets[0].entity[prop.name]))
293
- .forEach(prop => reloadProps.push(prop));
314
+ for (const prop of meta.props) {
315
+ if (prop.persist === false) {
316
+ continue;
317
+ }
318
+ if (isRaw(changeSets[0].entity[prop.name])) {
319
+ reloadProps.push(prop);
320
+ continue;
321
+ }
322
+ // do not reload things that already had a runtime value
323
+ if (changeSets[0].entity[prop.name] != null || prop.defaultRaw === 'null') {
324
+ continue;
325
+ }
326
+ if (prop.autoincrement || prop.generated || prop.defaultRaw) {
327
+ reloadProps.push(prop);
328
+ }
329
+ }
294
330
  }
295
331
  if (changeSets[0].type === ChangeSetType.UPDATE) {
296
332
  const returning = new Set();
@@ -321,12 +357,12 @@ export class ChangeSetPersister {
321
357
  }
322
358
  return val;
323
359
  });
324
- options = this.propagateSchemaFromMetadata(meta, options, {
360
+ options = this.prepareOptions(meta, options, {
325
361
  fields: Utils.unique(reloadProps.map(prop => prop.name)),
326
362
  });
327
- const data = await this.driver.find(meta.className, { [pk]: { $in: pks } }, options);
363
+ const data = await this.driver.find(meta.class, { [pk]: { $in: pks } }, options);
328
364
  const map = new Map();
329
- data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, true, this.platform, true), item));
365
+ data.forEach(item => map.set(Utils.getCompositeKeyHash(item, meta, false, this.platform, true), item));
330
366
  for (const changeSet of changeSets) {
331
367
  const data = map.get(helper(changeSet.entity).getSerializedPrimaryKey());
332
368
  this.hydrator.hydrate(changeSet.entity, meta, data, this.factory, 'full', false, true);
@@ -334,7 +370,7 @@ export class ChangeSetPersister {
334
370
  }
335
371
  }
336
372
  processProperty(changeSet, prop) {
337
- const meta = this.metadata.find(changeSet.name);
373
+ const meta = changeSet.meta;
338
374
  const value = changeSet.payload[prop.name]; // for inline embeddables
339
375
  if (value instanceof EntityIdentifier) {
340
376
  changeSet.payload[prop.name] = value.getValue();
@@ -367,7 +403,7 @@ export class ChangeSetPersister {
367
403
  if ((!this.usesReturningStatement && !upsert) || !row || !Utils.hasObjectKeys(row)) {
368
404
  return;
369
405
  }
370
- const mapped = this.comparator.mapResult(meta.className, row);
406
+ const mapped = this.comparator.mapResult(meta, row);
371
407
  if (entity) {
372
408
  this.hydrator.hydrate(entity, meta, mapped, this.factory, 'full', false, true);
373
409
  }