@mikro-orm/core 6.3.14-dev.5 → 6.3.14-dev.50

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.
package/EntityManager.js CHANGED
@@ -623,7 +623,7 @@ class EntityManager {
623
623
  const convertCustomTypes = !utils_1.Utils.isEntity(data);
624
624
  if (utils_1.Utils.isEntity(data)) {
625
625
  entity = data;
626
- if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em) {
626
+ if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em && !this.config.get('upsertManaged')) {
627
627
  em.entityFactory.mergeData(meta, entity, data, { initialized: true });
628
628
  return entity;
629
629
  }
@@ -633,7 +633,7 @@ class EntityManager {
633
633
  else {
634
634
  data = utils_1.Utils.copy(utils_1.QueryHelper.processParams(data));
635
635
  where = utils_1.Utils.extractPK(data, meta);
636
- if (where) {
636
+ if (where && !this.config.get('upsertManaged')) {
637
637
  const exists = em.unitOfWork.getById(entityName, where, options.schema);
638
638
  if (exists) {
639
639
  return em.assign(exists, data);
@@ -763,7 +763,7 @@ class EntityManager {
763
763
  let where;
764
764
  if (utils_1.Utils.isEntity(row)) {
765
765
  const entity = row;
766
- if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em) {
766
+ if ((0, entity_1.helper)(entity).__managed && (0, entity_1.helper)(entity).__em === em && !this.config.get('upsertManaged')) {
767
767
  em.entityFactory.mergeData(meta, entity, row, { initialized: true });
768
768
  entities.set(entity, row);
769
769
  entitiesByData.set(row, entity);
@@ -775,7 +775,7 @@ class EntityManager {
775
775
  else {
776
776
  row = data[i] = utils_1.Utils.copy(utils_1.QueryHelper.processParams(row));
777
777
  where = utils_1.Utils.extractPK(row, meta);
778
- if (where) {
778
+ if (where && !this.config.get('upsertManaged')) {
779
779
  const exists = em.unitOfWork.getById(entityName, where, options.schema);
780
780
  if (exists) {
781
781
  em.assign(exists, row);
@@ -1,9 +1,3 @@
1
- import { MikroORM } from '../MikroORM';
2
- import { EntityManager } from '../EntityManager';
3
- import { EntityRepository } from '../entity/EntityRepository';
4
- export declare function CreateRequestContext<T>(getContext?: MikroORM | Promise<MikroORM> | ((type: T) => MikroORM | Promise<MikroORM> | EntityManager | EntityRepository<any> | {
5
- getEntityManager(): EntityManager;
6
- }), respectExistingContext?: boolean): MethodDecorator;
7
- export declare function EnsureRequestContext<T>(getContext?: MikroORM | Promise<MikroORM> | ((type: T) => MikroORM | Promise<MikroORM> | EntityManager | EntityRepository<any> | {
8
- getEntityManager(): EntityManager;
9
- })): MethodDecorator;
1
+ import type { ContextProvider } from '../typings';
2
+ export declare function CreateRequestContext<T extends object>(context?: ContextProvider<T>, respectExistingContext?: boolean): MethodDecorator;
3
+ export declare function EnsureRequestContext<T extends object>(context?: ContextProvider<T>): MethodDecorator;
@@ -2,59 +2,32 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CreateRequestContext = CreateRequestContext;
4
4
  exports.EnsureRequestContext = EnsureRequestContext;
5
- const MikroORM_1 = require("../MikroORM");
6
5
  const RequestContext_1 = require("../utils/RequestContext");
7
- const EntityManager_1 = require("../EntityManager");
8
- const EntityRepository_1 = require("../entity/EntityRepository");
6
+ const resolveContextProvider_1 = require("../utils/resolveContextProvider");
9
7
  const TransactionContext_1 = require("../utils/TransactionContext");
10
- function CreateRequestContext(getContext, respectExistingContext = false) {
8
+ function CreateRequestContext(context, respectExistingContext = false) {
11
9
  return function (target, propertyKey, descriptor) {
12
10
  const originalMethod = descriptor.value;
13
11
  descriptor.value = async function (...args) {
14
- // Otherwise, the outer tx context would be preferred.
15
- const txContext = TransactionContext_1.TransactionContext.currentTransactionContext();
16
- const provider = txContext ? TransactionContext_1.TransactionContext : RequestContext_1.RequestContext;
17
- let orm;
18
- let em;
19
- if (typeof getContext === 'function') {
20
- const context = await getContext(this);
21
- orm = context ?? await this.orm;
22
- em = context ?? await this.em;
23
- }
24
- else if (getContext) {
25
- orm = em = await getContext;
26
- }
27
- else {
28
- orm = await this.orm;
29
- em = await this.em;
30
- }
31
- const create = async (em) => {
32
- // reuse existing context if available for given respect `contextName`
33
- if (respectExistingContext && RequestContext_1.RequestContext.getEntityManager(em.name)) {
34
- return originalMethod.apply(this, args);
35
- }
36
- if (txContext) {
37
- em = em.fork({ useContext: true });
38
- }
39
- return await provider.create(em, () => {
40
- return originalMethod.apply(this, args);
41
- });
42
- };
43
- if (em instanceof EntityManager_1.EntityManager) {
44
- return await create(em);
45
- }
46
- if (orm instanceof EntityRepository_1.EntityRepository) {
47
- return await create(orm.getEntityManager());
48
- }
49
- if (!(orm instanceof MikroORM_1.MikroORM)) {
12
+ const em = await (0, resolveContextProvider_1.resolveContextProvider)(this, context);
13
+ if (!em) {
50
14
  const name = respectExistingContext ? 'EnsureRequestContext' : 'CreateRequestContext';
51
15
  throw new Error(`@${name}() decorator can only be applied to methods of classes with \`orm: MikroORM\` property, \`em: EntityManager\` property, or with a callback parameter like \`@${name}(() => orm)\` that returns one of those types. The parameter will contain a reference to current \`this\`. Returning an EntityRepository from it is also supported.`);
52
16
  }
53
- return await create(orm.em);
17
+ // reuse existing context if available for given respect `contextName`
18
+ if (respectExistingContext && RequestContext_1.RequestContext.getEntityManager(em.name)) {
19
+ return originalMethod.apply(this, args);
20
+ }
21
+ // Otherwise, the outer tx context would be preferred.
22
+ const txContext = TransactionContext_1.TransactionContext.currentTransactionContext();
23
+ const provider = txContext ? TransactionContext_1.TransactionContext : RequestContext_1.RequestContext;
24
+ return txContext
25
+ ? provider.create(em.fork({ useContext: true }), () => originalMethod.apply(this, args))
26
+ : provider.create(em, () => originalMethod.apply(this, args));
54
27
  };
55
28
  return descriptor;
56
29
  };
57
30
  }
58
- function EnsureRequestContext(getContext) {
59
- return CreateRequestContext(getContext, true);
31
+ function EnsureRequestContext(context) {
32
+ return CreateRequestContext(context, true);
60
33
  }
@@ -177,6 +177,19 @@ export interface PropertyOptions<Owner> {
177
177
  * ```
178
178
  */
179
179
  getter?: boolean;
180
+ /**
181
+ * When defining a property over a method (not a getter, a regular function), you can use this option to point
182
+ * to the method name.
183
+ *
184
+ * @example
185
+ * ```
186
+ * @Property({ getter: true })
187
+ * getFullName() {
188
+ * return this.firstName + this.lastName;
189
+ * }
190
+ * ```
191
+ */
192
+ getterName?: keyof Owner;
180
193
  /**
181
194
  * Set to define serialized primary key for MongoDB. (virtual)
182
195
  * Alias for `@SerializedPrimaryKey()` decorator.
@@ -0,0 +1,13 @@
1
+ import type { TransactionOptions } from '../enums';
2
+ import type { ContextProvider } from '../typings';
3
+ type TransactionalOptions<T> = TransactionOptions & {
4
+ context?: ContextProvider<T>;
5
+ };
6
+ /**
7
+ * This decorator wraps the method with `em.transactional()`, so you can provide `TransactionOptions` just like with `em.transactional()`.
8
+ * The difference is that you can specify the context in which the transaction begins by providing `context` option,
9
+ * and if omitted, the transaction will begin in the current context implicitly.
10
+ * It works on async functions and can be nested with `em.transactional()`.
11
+ */
12
+ export declare function Transactional<T extends object>(options?: TransactionalOptions<T>): MethodDecorator;
13
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Transactional = Transactional;
4
+ const RequestContext_1 = require("../utils/RequestContext");
5
+ const resolveContextProvider_1 = require("../utils/resolveContextProvider");
6
+ const TransactionContext_1 = require("../utils/TransactionContext");
7
+ /**
8
+ * This decorator wraps the method with `em.transactional()`, so you can provide `TransactionOptions` just like with `em.transactional()`.
9
+ * The difference is that you can specify the context in which the transaction begins by providing `context` option,
10
+ * and if omitted, the transaction will begin in the current context implicitly.
11
+ * It works on async functions and can be nested with `em.transactional()`.
12
+ */
13
+ function Transactional(options = {}) {
14
+ return function (target, propertyKey, descriptor) {
15
+ const originalMethod = descriptor.value;
16
+ if (originalMethod.constructor.name !== 'AsyncFunction') {
17
+ throw new Error('@Transactional() should be use with async functions');
18
+ }
19
+ descriptor.value = async function (...args) {
20
+ const { context, ...txOptions } = options;
21
+ const em = await (0, resolveContextProvider_1.resolveContextProvider)(this, context)
22
+ || TransactionContext_1.TransactionContext.getEntityManager()
23
+ || RequestContext_1.RequestContext.getEntityManager();
24
+ if (!em) {
25
+ throw new Error(`@Transactional() decorator can only be applied to methods of classes with \`orm: MikroORM\` property, \`em: EntityManager\` property, or with a callback parameter like \`@Transactional(() => orm)\` that returns one of those types. The parameter will contain a reference to current \`this\`. Returning an EntityRepository from it is also supported.`);
26
+ }
27
+ return em.transactional(() => originalMethod.apply(this, args), txOptions);
28
+ };
29
+ return descriptor;
30
+ };
31
+ }
@@ -14,3 +14,4 @@ export * from './Embedded';
14
14
  export * from './Filter';
15
15
  export * from './CreateRequestContext';
16
16
  export * from './hooks';
17
+ export * from './Transactional';
@@ -33,3 +33,4 @@ __exportStar(require("./Embedded"), exports);
33
33
  __exportStar(require("./Filter"), exports);
34
34
  __exportStar(require("./CreateRequestContext"), exports);
35
35
  __exportStar(require("./hooks"), exports);
36
+ __exportStar(require("./Transactional"), exports);
@@ -354,16 +354,11 @@ class DatabaseDriver {
354
354
  */
355
355
  getTableName(meta, options, quote = true) {
356
356
  const schema = this.getSchemaName(meta, options);
357
- if (schema) {
358
- if (quote) {
359
- return this.platform.quoteIdentifier(schema + '.' + meta.tableName);
360
- }
361
- return schema + '.' + meta.tableName;
362
- }
357
+ const tableName = schema ? `${schema}.${meta.tableName}` : meta.tableName;
363
358
  if (quote) {
364
- return this.platform.quoteIdentifier(meta.tableName);
359
+ return this.platform.quoteIdentifier(tableName);
365
360
  }
366
- return meta.tableName;
361
+ return tableName;
367
362
  }
368
363
  /**
369
364
  * @internal
@@ -14,7 +14,7 @@ export declare abstract class BaseEntity {
14
14
  toObject<Entity extends this = this>(ignoreFields: never[]): EntityDTO<Entity>;
15
15
  toObject<Entity extends this = this, Ignored extends EntityKey<Entity> = never>(ignoreFields: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
16
16
  toPOJO<Entity extends this = this>(): EntityDTO<Entity>;
17
- serialize<Entity extends this = this, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): EntityDTO<Loaded<Entity, Hint>>;
17
+ serialize<Entity extends this = this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): EntityDTO<Loaded<Naked, Hint>>;
18
18
  assign<Entity extends this, Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
19
19
  init<Entity extends this = this, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(options?: FindOneOptions<Entity, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
20
20
  getSchema(): string | undefined;
@@ -69,6 +69,12 @@ class EntityFactory {
69
69
  if (options.convertCustomTypes) {
70
70
  for (const prop of meta.props) {
71
71
  if (prop.customType?.ensureComparable(meta, prop) && data[prop.name]) {
72
+ if ([enums_1.ReferenceKind.ONE_TO_MANY, enums_1.ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
73
+ continue;
74
+ }
75
+ if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
76
+ data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta.primaryKeys, true);
77
+ }
72
78
  data[prop.name] = prop.customType.convertToDatabaseValue(data[prop.name], this.platform, { key: prop.name, mode: 'hydration' });
73
79
  }
74
80
  }
@@ -244,7 +250,7 @@ class EntityFactory {
244
250
  if (!Array.isArray(data) && meta.primaryKeys.some(pk => data[pk] == null)) {
245
251
  return undefined;
246
252
  }
247
- const pks = Utils_1.Utils.getOrderedPrimaryKeys(data, meta, this.platform, options.convertCustomTypes);
253
+ const pks = Utils_1.Utils.getOrderedPrimaryKeys(data, meta, this.platform);
248
254
  return this.unitOfWork.getById(meta.className, pks, schema);
249
255
  }
250
256
  processDiscriminatorColumn(meta, data) {
@@ -176,10 +176,15 @@ class EntityHelper {
176
176
  inverse.addWithoutPropagation(owner);
177
177
  (0, wrap_1.helper)(owner).__em?.getUnitOfWork().cancelOrphanRemoval(owner);
178
178
  }
179
- if (prop.kind === enums_1.ReferenceKind.ONE_TO_ONE && entity && (!prop.owner || (0, wrap_1.helper)(entity).__initialized)) {
179
+ if (prop.kind === enums_1.ReferenceKind.ONE_TO_ONE) {
180
180
  if ((value != null && Reference_1.Reference.unwrapReference(inverse) !== owner) ||
181
- (value == null && entity[prop2.name] != null)) {
182
- EntityHelper.propagateOneToOne(entity, owner, prop, prop2, value, old);
181
+ (value == null && entity?.[prop2.name] != null)) {
182
+ if (entity && (!prop.owner || (0, wrap_1.helper)(entity).__initialized)) {
183
+ EntityHelper.propagateOneToOne(entity, owner, prop, prop2, value, old);
184
+ }
185
+ if (old && prop.orphanRemoval) {
186
+ (0, wrap_1.helper)(old).__em?.getUnitOfWork().scheduleOrphanRemoval(old);
187
+ }
183
188
  }
184
189
  }
185
190
  }
@@ -203,9 +208,6 @@ class EntityHelper {
203
208
  else {
204
209
  entity[prop2.name] = Reference_1.Reference.wrapReference(owner, prop);
205
210
  }
206
- if (old && prop.orphanRemoval) {
207
- (0, wrap_1.helper)(old).__em?.getUnitOfWork().scheduleOrphanRemoval(old);
208
- }
209
211
  if (old?.[prop2.name] != null) {
210
212
  delete (0, wrap_1.helper)(old).__data[prop2.name];
211
213
  }
package/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * @module core
4
4
  */
5
- export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
5
+ export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings';
6
6
  export * from './enums';
7
7
  export * from './errors';
8
8
  export * from './exceptions';
package/index.mjs CHANGED
@@ -162,6 +162,7 @@ export const TimeType = mod.TimeType;
162
162
  export const TinyIntType = mod.TinyIntType;
163
163
  export const TransactionContext = mod.TransactionContext;
164
164
  export const TransactionEventBroadcaster = mod.TransactionEventBroadcaster;
165
+ export const Transactional = mod.Transactional;
165
166
  export const Type = mod.Type;
166
167
  export const Uint8ArrayType = mod.Uint8ArrayType;
167
168
  export const UnderscoreNamingStrategy = mod.UnderscoreNamingStrategy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.3.14-dev.5",
3
+ "version": "6.3.14-dev.50",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -64,7 +64,7 @@
64
64
  "esprima": "4.0.1",
65
65
  "fs-extra": "11.2.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.3.14-dev.5",
67
+ "mikro-orm": "6.3.14-dev.50",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
@@ -111,7 +111,7 @@ class EntitySerializer {
111
111
  const parts = prop.split('.');
112
112
  prop = parts[0];
113
113
  const wrapped = (0, wrap_1.helper)(entity);
114
- const property = wrapped.__meta.properties[prop];
114
+ const property = wrapped.__meta.properties[prop] ?? { name: prop };
115
115
  const serializer = property?.serializer;
116
116
  const value = entity[prop];
117
117
  // getter method
@@ -127,10 +127,10 @@ class EntitySerializer {
127
127
  return serializer(value);
128
128
  }
129
129
  if (Utils_1.Utils.isCollection(value)) {
130
- return this.processCollection(prop, entity, options);
130
+ return this.processCollection(property, entity, options);
131
131
  }
132
132
  if (Utils_1.Utils.isEntity(value, true)) {
133
- return this.processEntity(prop, entity, wrapped.__platform, options);
133
+ return this.processEntity(property, entity, wrapped.__platform, options);
134
134
  }
135
135
  if (Utils_1.Utils.isScalarReference(value)) {
136
136
  return value.unwrap();
@@ -158,17 +158,20 @@ class EntitySerializer {
158
158
  };
159
159
  }
160
160
  static processEntity(prop, entity, platform, options) {
161
- const child = Reference_1.Reference.unwrapReference(entity[prop]);
161
+ const child = Reference_1.Reference.unwrapReference(entity[prop.name]);
162
162
  const wrapped = (0, wrap_1.helper)(child);
163
- const populated = isPopulated(prop, options) && wrapped.isInitialized();
163
+ const populated = isPopulated(prop.name, options) && wrapped.isInitialized();
164
164
  const expand = populated || !wrapped.__managed;
165
165
  const meta = wrapped.__meta;
166
- const childOptions = this.extractChildOptions(options, prop);
166
+ const childOptions = this.extractChildOptions(options, prop.name);
167
167
  const visible = meta.primaryKeys.filter(prop => isVisible(meta, prop, childOptions));
168
168
  if (expand) {
169
169
  return this.serialize(child, childOptions);
170
170
  }
171
- const pk = wrapped.getPrimaryKey(true);
171
+ let pk = wrapped.getPrimaryKey();
172
+ if (prop.customType) {
173
+ pk = prop.customType.toJSON(pk, wrapped.__platform);
174
+ }
172
175
  if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
173
176
  return Utils_1.Utils.primaryKeyToObject(meta, pk, visible);
174
177
  }
@@ -182,20 +185,24 @@ class EntitySerializer {
182
185
  return platform.normalizePrimaryKey(pk);
183
186
  }
184
187
  static processCollection(prop, entity, options) {
185
- const col = entity[prop];
188
+ const col = entity[prop.name];
186
189
  if (!col.isInitialized()) {
187
190
  return undefined;
188
191
  }
189
192
  return col.getItems(false).map(item => {
190
- const populated = isPopulated(prop, options);
193
+ const populated = isPopulated(prop.name, options);
191
194
  const wrapped = (0, wrap_1.helper)(item);
192
195
  if (populated || !wrapped.__managed) {
193
- return this.serialize(item, this.extractChildOptions(options, prop));
196
+ return this.serialize(item, this.extractChildOptions(options, prop.name));
197
+ }
198
+ let pk = wrapped.getPrimaryKey();
199
+ if (prop.customType) {
200
+ pk = prop.customType.toJSON(pk, wrapped.__platform);
194
201
  }
195
202
  if (options.forceObject || wrapped.__config.get('serialization').forceObject) {
196
- return Utils_1.Utils.primaryKeyToObject(wrapped.__meta, wrapped.getPrimaryKey(true));
203
+ return Utils_1.Utils.primaryKeyToObject(wrapped.__meta, pk);
197
204
  }
198
- return (0, wrap_1.helper)(item).getPrimaryKey();
205
+ return pk;
199
206
  });
200
207
  }
201
208
  }
@@ -18,6 +18,9 @@ class EntityTransformer {
18
18
  }
19
19
  const wrapped = (0, wrap_1.helper)(entity);
20
20
  let contextCreated = false;
21
+ if (!wrapped) {
22
+ return entity;
23
+ }
21
24
  if (!wrapped.__serializationContext.root) {
22
25
  const root = new SerializationContext_1.SerializationContext(wrapped.__config, wrapped.__serializationContext.populate, wrapped.__serializationContext.fields, wrapped.__serializationContext.exclude);
23
26
  SerializationContext_1.SerializationContext.propagate(root, entity, isVisible);
@@ -108,7 +111,7 @@ class EntityTransformer {
108
111
  }
109
112
  static processProperty(prop, entity, raw, populated) {
110
113
  const wrapped = (0, wrap_1.helper)(entity);
111
- const property = wrapped.__meta.properties[prop];
114
+ const property = wrapped.__meta.properties[prop] ?? { name: prop };
112
115
  const serializer = property?.serializer;
113
116
  const value = entity[prop];
114
117
  // getter method
@@ -123,10 +126,10 @@ class EntityTransformer {
123
126
  return serializer(value);
124
127
  }
125
128
  if (Utils_1.Utils.isCollection(value)) {
126
- return EntityTransformer.processCollection(prop, entity, raw, populated);
129
+ return EntityTransformer.processCollection(property, entity, raw, populated);
127
130
  }
128
131
  if (Utils_1.Utils.isEntity(value, true)) {
129
- return EntityTransformer.processEntity(prop, entity, wrapped.__platform, raw, populated);
132
+ return EntityTransformer.processEntity(property, entity, wrapped.__platform, raw, populated);
130
133
  }
131
134
  if (Utils_1.Utils.isScalarReference(value)) {
132
135
  return value.unwrap();
@@ -151,7 +154,7 @@ class EntityTransformer {
151
154
  return value;
152
155
  }
153
156
  static processEntity(prop, entity, platform, raw, populated) {
154
- const child = entity[prop];
157
+ const child = entity[prop.name];
155
158
  const wrapped = (0, wrap_1.helper)(child);
156
159
  const meta = wrapped.__meta;
157
160
  const visible = meta.primaryKeys.filter(prop => isVisible(meta, prop));
@@ -170,7 +173,10 @@ class EntityTransformer {
170
173
  if (wrapped.isInitialized() && isPopulated() && child !== entity) {
171
174
  return (0, wrap_1.wrap)(child).toJSON();
172
175
  }
173
- const pk = wrapped.getPrimaryKey(true);
176
+ let pk = wrapped.getPrimaryKey();
177
+ if (prop.customType) {
178
+ pk = prop.customType.toJSON(pk, wrapped.__platform);
179
+ }
174
180
  if (wrapped.__config.get('serialization').forceObject) {
175
181
  return Utils_1.Utils.primaryKeyToObject(meta, pk, visible);
176
182
  }
@@ -184,7 +190,7 @@ class EntityTransformer {
184
190
  return platform.normalizePrimaryKey(pk);
185
191
  }
186
192
  static processCollection(prop, entity, raw, populated) {
187
- const col = entity[prop];
193
+ const col = entity[prop.name];
188
194
  if (raw && col.isInitialized(true)) {
189
195
  return col.map(item => (0, wrap_1.helper)(item).toPOJO());
190
196
  }
@@ -193,13 +199,18 @@ class EntityTransformer {
193
199
  }
194
200
  if (col.isInitialized()) {
195
201
  const wrapped = (0, wrap_1.helper)(entity);
196
- if (wrapped.__config.get('serialization').forceObject) {
197
- return col.map(item => {
198
- const wrapped = (0, wrap_1.helper)(item);
199
- return Utils_1.Utils.primaryKeyToObject(wrapped.__meta, wrapped.getPrimaryKey(true));
200
- });
201
- }
202
- return col.map(i => (0, wrap_1.helper)(i).getPrimaryKey(true));
202
+ const forceObject = wrapped.__config.get('serialization').forceObject;
203
+ return col.map(item => {
204
+ const wrapped = (0, wrap_1.helper)(item);
205
+ const pk = wrapped.getPrimaryKey();
206
+ if (prop.customType) {
207
+ return prop.customType.toJSON(pk, wrapped.__platform);
208
+ }
209
+ if (forceObject) {
210
+ return Utils_1.Utils.primaryKeyToObject(wrapped.__meta, pk);
211
+ }
212
+ return pk;
213
+ });
203
214
  }
204
215
  return undefined;
205
216
  }
@@ -8,6 +8,8 @@ export declare class DecimalType extends Type<string | number, string> {
8
8
  mode?: ("number" | "string") | undefined;
9
9
  constructor(mode?: ("number" | "string") | undefined);
10
10
  convertToJSValue(value: string): number | string;
11
+ compareValues(a: string, b: string): boolean;
12
+ private format;
11
13
  getColumnType(prop: EntityProperty, platform: Platform): string;
12
14
  compareAsType(): string;
13
15
  }
@@ -17,6 +17,17 @@ class DecimalType extends Type_1.Type {
17
17
  }
18
18
  return value;
19
19
  }
20
+ compareValues(a, b) {
21
+ return this.format(a) === this.format(b);
22
+ }
23
+ format(val) {
24
+ /* istanbul ignore next */
25
+ if (this.prop?.scale == null) {
26
+ return +val;
27
+ }
28
+ const base = Math.pow(10, this.prop.scale);
29
+ return Math.round((+val + Number.EPSILON) * base) / base;
30
+ }
20
31
  getColumnType(prop, platform) {
21
32
  return platform.getDecimalTypeDeclarationSQL(prop);
22
33
  }
package/typings.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Transaction } from './connections';
2
2
  import { type Cascade, type DeferMode, type EventType, type LoadStrategy, type PopulatePath, type QueryOrderMap, ReferenceKind } from './enums';
3
3
  import { type AssignOptions, type Collection, type EntityFactory, type EntityIdentifier, type EntityLoaderOptions, type EntityRepository, Reference, type ScalarReference } from './entity';
4
+ import type { MikroORM } from './MikroORM';
4
5
  import type { SerializationContext, SerializeOptions } from './serialization';
5
6
  import type { EntitySchema, MetadataStorage } from './metadata';
6
7
  import type { Type, types } from './types';
@@ -143,7 +144,7 @@ export interface IWrappedEntity<Entity extends object> {
143
144
  toObject<Ignored extends EntityKey<Entity>>(ignoreFields: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
144
145
  toJSON(...args: any[]): EntityDTO<Entity>;
145
146
  toPOJO(): EntityDTO<Entity>;
146
- serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): EntityDTO<Loaded<Entity, Hint>>;
147
+ serialize<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Naked, Hint, Exclude>): EntityDTO<Loaded<Naked, Hint>>;
147
148
  setSerializationContext<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
148
149
  assign<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked, Convert>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
149
150
  getSchema(): string | undefined;
@@ -626,11 +627,20 @@ export interface IMigrator {
626
627
  * Executes down migrations to the given point. Without parameter it will migrate one version down.
627
628
  */
628
629
  down(options?: string | string[] | MigrateOptions): Promise<UmzugMigration[]>;
630
+ /**
631
+ * Registers event handler.
632
+ */
633
+ on(event: MigratorEvent, listener: (event: UmzugMigration) => MaybePromise<void>): IMigrator;
634
+ /**
635
+ * Removes event handler.
636
+ */
637
+ off(event: MigratorEvent, listener: (event: UmzugMigration) => MaybePromise<void>): IMigrator;
629
638
  /**
630
639
  * @internal
631
640
  */
632
641
  getStorage(): IMigratorStorage;
633
642
  }
643
+ export type MigratorEvent = 'migrating' | 'migrated' | 'reverting' | 'reverted';
634
644
  export interface MigrationDiff {
635
645
  up: string[];
636
646
  down: string[];
@@ -786,4 +796,10 @@ export interface Seeder<T extends Dictionary = Dictionary> {
786
796
  }
787
797
  export type ConnectionType = 'read' | 'write';
788
798
  export type MetadataProcessor = (metadata: EntityMetadata[], platform: Platform) => MaybePromise<void>;
799
+ /**
800
+ * The type of context that the user intends to inject.
801
+ */
802
+ export type ContextProvider<T> = MaybePromise<MikroORM> | ((type: T) => MaybePromise<MikroORM | EntityManager | EntityRepository<any> | {
803
+ getEntityManager(): EntityManager;
804
+ }>);
789
805
  export {};
@@ -71,6 +71,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
71
71
  mergeEmbeddedProperties: true;
72
72
  };
73
73
  persistOnCreate: true;
74
+ upsertManaged: true;
74
75
  forceEntityConstructor: false;
75
76
  forceUndefined: false;
76
77
  ensureDatabase: true;
@@ -333,6 +334,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
333
334
  };
334
335
  assign: AssignOptions<boolean>;
335
336
  persistOnCreate: boolean;
337
+ upsertManaged: boolean;
336
338
  forceEntityConstructor: boolean | (Constructor<AnyEntity> | string)[];
337
339
  forceUndefined: boolean;
338
340
  forceUtcTimezone?: boolean;
@@ -65,6 +65,7 @@ class Configuration {
65
65
  mergeEmbeddedProperties: true,
66
66
  },
67
67
  persistOnCreate: true,
68
+ upsertManaged: true,
68
69
  forceEntityConstructor: false,
69
70
  forceUndefined: false,
70
71
  ensureDatabase: true,
@@ -23,7 +23,7 @@ export declare class DataloaderUtils {
23
23
  static groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts: readonly [Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?][]): Map<string, Map<string, Set<Primary<any>>>>;
24
24
  /**
25
25
  * Turn the entity+options map into actual queries.
26
- * The keys are the entity names + a strigified version of the options and the values are filter Maps which will be used as the values of an $or operator so we end up with a query per entity+opts.
26
+ * The keys are the entity names + a stringified version of the options and the values are filter Maps which will be used as the values of an $or operator so we end up with a query per entity+opts.
27
27
  * We must populate the inverse side of the relationship in order to be able to later retrieve the PK(s) from its item(s).
28
28
  * Together with the query the promises will also return the key which can be used to narrow down the results pertaining to a certain set of options.
29
29
  */
@@ -33,7 +33,7 @@ export declare class DataloaderUtils {
33
33
  * First checks if the Entity type matches, then retrieves the inverse side of the relationship
34
34
  * where the filtering will be done in order to match the target collection.
35
35
  */
36
- static getColFilter<T, S extends T>(collection: Collection<any, object>): (result: T) => result is S;
36
+ static getColFilter<T, S extends T>(collection: Collection<any>): (result: T) => result is S;
37
37
  /**
38
38
  * Returns the collection dataloader batchLoadFn, which aggregates collections by entity,
39
39
  * makes one query per entity and maps each input collection to the corresponding result.
@@ -4,6 +4,7 @@ exports.DataloaderUtils = void 0;
4
4
  const Collection_1 = require("../entity/Collection");
5
5
  const wrap_1 = require("../entity/wrap");
6
6
  const enums_1 = require("../enums");
7
+ const Reference_1 = require("../entity/Reference");
7
8
  class DataloaderUtils {
8
9
  /**
9
10
  * Groups identified references by entity and returns a Map with the
@@ -92,7 +93,7 @@ class DataloaderUtils {
92
93
  }
93
94
  /**
94
95
  * Turn the entity+options map into actual queries.
95
- * The keys are the entity names + a strigified version of the options and the values are filter Maps which will be used as the values of an $or operator so we end up with a query per entity+opts.
96
+ * The keys are the entity names + a stringified version of the options and the values are filter Maps which will be used as the values of an $or operator so we end up with a query per entity+opts.
96
97
  * We must populate the inverse side of the relationship in order to be able to later retrieve the PK(s) from its item(s).
97
98
  * Together with the query the promises will also return the key which can be used to narrow down the results pertaining to a certain set of options.
98
99
  */
@@ -139,20 +140,21 @@ class DataloaderUtils {
139
140
  // There is no need to check if Entity matches because we already matched the key which is entity+options.
140
141
  // This is the inverse side of the relationship where the filtering will be done in order to match the target collection
141
142
  // Either inversedBy or mappedBy exist because we already checked in groupInversedOrMappedKeysByEntity
142
- const refOrCol = result[(collection.property.inversedBy ?? collection.property.mappedBy)];
143
- if (refOrCol instanceof Collection_1.Collection) {
144
- // The inverse side is a Collection
145
- // We keep the result if any of PKs of the inverse side matches the PK of the collection owner
146
- for (const item of refOrCol.getItems()) {
147
- if ((0, wrap_1.helper)(item).getSerializedPrimaryKey() === (0, wrap_1.helper)(collection.owner).getSerializedPrimaryKey()) {
143
+ const inverseProp = collection.property.inversedBy ?? collection.property.mappedBy;
144
+ const target = Reference_1.Reference.unwrapReference(result[inverseProp]);
145
+ if (target instanceof Collection_1.Collection) {
146
+ for (const item of target) {
147
+ if (item === collection.owner) {
148
148
  return true;
149
149
  }
150
150
  }
151
151
  }
152
- else {
153
- // The inverse side is a Reference
154
- // We keep the result if the PK of the inverse side matches the PK of the collection owner
155
- return (0, wrap_1.helper)(refOrCol).getSerializedPrimaryKey() === (0, wrap_1.helper)(collection.owner).getSerializedPrimaryKey();
152
+ else if (target) {
153
+ return target === collection.owner;
154
+ }
155
+ // FIXME https://github.com/mikro-orm/mikro-orm/issues/6031
156
+ if (!target && collection.property.kind === enums_1.ReferenceKind.MANY_TO_MANY) {
157
+ throw new Error(`Inverse side is required for M:N relations with dataloader: ${collection.owner.constructor.name}.${collection.property.name}`);
156
158
  }
157
159
  return false;
158
160
  };
package/utils/clone.js CHANGED
@@ -75,7 +75,7 @@ function clone(parent, respectCustomCloneMethod = true) {
75
75
  child.lastIndex = parent.lastIndex;
76
76
  }
77
77
  }
78
- else if (parent instanceof Date || (typeof parent === 'object' && parent.constructor.name === 'Date')) {
78
+ else if (parent instanceof Date) {
79
79
  child = new Date(parent.getTime());
80
80
  }
81
81
  else if (Buffer.isBuffer(parent)) {
@@ -0,0 +1,10 @@
1
+ import { EntityManager } from '../EntityManager';
2
+ import { MikroORM } from '../MikroORM';
3
+ import type { ContextProvider, MaybePromise } from '../typings';
4
+ /**
5
+ * Find `EntityManager` in provided context, or else in instance's `orm` or `em` properties.
6
+ */
7
+ export declare function resolveContextProvider<T>(caller: T & {
8
+ orm?: MaybePromise<MikroORM>;
9
+ em?: MaybePromise<EntityManager>;
10
+ }, provider?: ContextProvider<T>): Promise<EntityManager | undefined>;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveContextProvider = resolveContextProvider;
4
+ const EntityRepository_1 = require("../entity/EntityRepository");
5
+ const EntityManager_1 = require("../EntityManager");
6
+ const MikroORM_1 = require("../MikroORM");
7
+ function getEntityManager(caller, context) {
8
+ if (context instanceof EntityManager_1.EntityManager) {
9
+ return context;
10
+ }
11
+ if (context instanceof EntityRepository_1.EntityRepository) {
12
+ return context.getEntityManager();
13
+ }
14
+ if (context instanceof MikroORM_1.MikroORM) {
15
+ return context.em;
16
+ }
17
+ if (caller.em instanceof EntityManager_1.EntityManager) {
18
+ return caller.em;
19
+ }
20
+ if (caller.orm instanceof MikroORM_1.MikroORM) {
21
+ return caller.orm.em;
22
+ }
23
+ return undefined;
24
+ }
25
+ /**
26
+ * Find `EntityManager` in provided context, or else in instance's `orm` or `em` properties.
27
+ */
28
+ async function resolveContextProvider(caller, provider) {
29
+ const context = typeof provider === 'function' ? await provider(caller) : await provider;
30
+ return getEntityManager({ orm: await caller.orm, em: await caller.em }, context);
31
+ }