@mikro-orm/core 7.0.0-dev.27 → 7.0.0-dev.29

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.
@@ -270,6 +270,29 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
270
270
  upsertMany<Entity extends object, Fields extends string = any>(entityNameOrEntity: EntityName<Entity> | Entity[], data?: (EntityData<Entity> | NoInfer<Entity>)[], options?: UpsertManyOptions<Entity, Fields>): Promise<Entity[]>;
271
271
  /**
272
272
  * Runs your callback wrapped inside a database transaction.
273
+ *
274
+ * If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
275
+ * can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
276
+ * should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
277
+ * method automatically creates an async context for the transaction.
278
+ *
279
+ * **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
280
+ * `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
281
+ * between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
282
+ * the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
283
+ *
284
+ * **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
285
+ * parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
286
+ * and then call this method on the fork.
287
+ *
288
+ * **Example:**
289
+ * ```ts
290
+ * await em.transactional(async (em) => {
291
+ * const author = new Author('Jon');
292
+ * em.persist(author);
293
+ * // flush is called automatically at the end of the callback
294
+ * });
295
+ * ```
273
296
  */
274
297
  transactional<T>(cb: (em: this) => T | Promise<T>, options?: TransactionOptions): Promise<T>;
275
298
  /**
@@ -562,6 +585,8 @@ export interface MergeOptions {
562
585
  schema?: string;
563
586
  disableContextResolution?: boolean;
564
587
  keepIdentity?: boolean;
588
+ validate?: boolean;
589
+ cascade?: boolean; /** @default true */
565
590
  }
566
591
  export interface ForkOptions {
567
592
  /** do we want a clear identity map? defaults to true */
package/EntityManager.js CHANGED
@@ -506,18 +506,24 @@ export class EntityManager {
506
506
  flushMode: FlushMode.COMMIT,
507
507
  });
508
508
  const em = this.getContext();
509
- if (reloaded) {
510
- for (const e of fork.unitOfWork.getIdentityMap()) {
511
- const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
512
- const data = this.comparator.prepareEntity(e);
513
- em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, helper(e).serialize({ ignoreSerializers: true, includeHidden: true }), em.entityFactory, 'full', false, true);
514
- helper(ref).__originalEntityData = data;
515
- }
516
- }
517
- else {
509
+ if (!reloaded) {
518
510
  em.unitOfWork.unsetIdentity(entity);
511
+ return null;
519
512
  }
520
- return reloaded ? entity : reloaded;
513
+ let found = false;
514
+ for (const e of fork.unitOfWork.getIdentityMap()) {
515
+ const ref = em.getReference(e.constructor.name, helper(e).getPrimaryKey());
516
+ const data = helper(e).serialize({ ignoreSerializers: true, includeHidden: true });
517
+ em.config.getHydrator(this.metadata).hydrate(ref, helper(ref).__meta, data, em.entityFactory, 'full', false, true);
518
+ helper(ref).__originalEntityData = this.comparator.prepareEntity(e);
519
+ found ||= ref === entity;
520
+ }
521
+ if (!found) {
522
+ const data = helper(reloaded).serialize({ ignoreSerializers: true, includeHidden: true });
523
+ em.config.getHydrator(this.metadata).hydrate(entity, helper(entity).__meta, data, em.entityFactory, 'full', false, true);
524
+ helper(entity).__originalEntityData = this.comparator.prepareEntity(reloaded);
525
+ }
526
+ return entity;
521
527
  }
522
528
  /**
523
529
  * Finds first entity matching your `where` query.
@@ -979,6 +985,29 @@ export class EntityManager {
979
985
  }
980
986
  /**
981
987
  * Runs your callback wrapped inside a database transaction.
988
+ *
989
+ * If a transaction is already active, a new savepoint (nested transaction) will be created by default. This behavior
990
+ * can be controlled via the `propagation` option. Use the provided EntityManager instance for all operations that
991
+ * should be part of the transaction. You can safely use a global EntityManager instance from a DI container, as this
992
+ * method automatically creates an async context for the transaction.
993
+ *
994
+ * **Concurrency note:** When running multiple transactions concurrently (e.g. in parallel requests or jobs), use the
995
+ * `clear: true` option. This ensures the callback runs in a clear fork of the EntityManager, providing full isolation
996
+ * between concurrent transactional handlers. Using `clear: true` is an alternative to forking explicitly and calling
997
+ * the method on the new fork – it already provides the necessary isolation for safe concurrent usage.
998
+ *
999
+ * **Propagation note:** Changes made within a transaction (whether top-level or nested) are always propagated to the
1000
+ * parent context, unless the parent context is a global one. If you want to avoid that, fork the EntityManager first
1001
+ * and then call this method on the fork.
1002
+ *
1003
+ * **Example:**
1004
+ * ```ts
1005
+ * await em.transactional(async (em) => {
1006
+ * const author = new Author('Jon');
1007
+ * em.persist(author);
1008
+ * // flush is called automatically at the end of the callback
1009
+ * });
1010
+ * ```
982
1011
  */
983
1012
  async transactional(cb, options = {}) {
984
1013
  const em = this.getContext(false);
@@ -1171,8 +1200,12 @@ export class EntityManager {
1171
1200
  }
1172
1201
  const em = options.disableContextResolution ? this : this.getContext();
1173
1202
  options.schema ??= em._schema;
1203
+ options.validate ??= true;
1204
+ options.cascade ??= true;
1174
1205
  entityName = Utils.className(entityName);
1175
- em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1206
+ if (options.validate) {
1207
+ em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1208
+ }
1176
1209
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1177
1210
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
1178
1211
  return entity;
@@ -1181,12 +1214,16 @@ export class EntityManager {
1181
1214
  const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1182
1215
  const dataIsEntity = Utils.isEntity(data);
1183
1216
  if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
1184
- em.entityFactory.mergeData(meta, entity, helper(data).__originalEntityData, { initialized: true, merge: true, ...options });
1217
+ helper(entity).__data = helper(data).__data;
1218
+ helper(entity).__originalEntityData = helper(data).__originalEntityData;
1185
1219
  return entity;
1186
1220
  }
1187
1221
  entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
1188
- em.validator.validate(entity, data, childMeta ?? meta);
1189
- em.unitOfWork.merge(entity);
1222
+ if (options.validate) {
1223
+ em.validator.validate(entity, data, childMeta ?? meta);
1224
+ }
1225
+ const visited = options.cascade ? undefined : new Set([entity]);
1226
+ em.unitOfWork.merge(entity, visited);
1190
1227
  return entity;
1191
1228
  }
1192
1229
  /**
package/MikroORM.js CHANGED
@@ -35,7 +35,6 @@ export class MikroORM {
35
35
  }
36
36
  }
37
37
  options = Utils.mergeConfig(options, env);
38
- ConfigurationLoader.commonJSCompat(options);
39
38
  if ('DRIVER' in this && !options.driver) {
40
39
  options.driver = this.DRIVER;
41
40
  }
@@ -3,12 +3,13 @@ export declare class FileCacheAdapter implements SyncCacheAdapter {
3
3
  private readonly options;
4
4
  private readonly baseDir;
5
5
  private readonly pretty;
6
+ private readonly hashAlgorithm;
6
7
  private readonly VERSION;
7
8
  private cache;
8
9
  constructor(options: {
9
10
  cacheDir: string;
10
11
  combined?: boolean | string;
11
- }, baseDir: string, pretty?: boolean);
12
+ }, baseDir: string, pretty?: boolean, hashAlgorithm?: 'md5' | 'sha256');
12
13
  /**
13
14
  * @inheritDoc
14
15
  */
@@ -5,12 +5,14 @@ export class FileCacheAdapter {
5
5
  options;
6
6
  baseDir;
7
7
  pretty;
8
+ hashAlgorithm;
8
9
  VERSION = Utils.getORMVersion();
9
10
  cache = {};
10
- constructor(options, baseDir, pretty = false) {
11
+ constructor(options, baseDir, pretty = false, hashAlgorithm = 'md5') {
11
12
  this.options = options;
12
13
  this.baseDir = baseDir;
13
14
  this.pretty = pretty;
15
+ this.hashAlgorithm = hashAlgorithm;
14
16
  }
15
17
  /**
16
18
  * @inheritDoc
@@ -77,6 +79,6 @@ export class FileCacheAdapter {
77
79
  return null;
78
80
  }
79
81
  const contents = readFileSync(origin);
80
- return Utils.hash(contents.toString() + this.VERSION);
82
+ return Utils.hash(contents.toString() + this.VERSION, undefined, this.hashAlgorithm);
81
83
  }
82
84
  }
@@ -466,23 +466,20 @@ export class EntityLoader {
466
466
  }
467
467
  getChildReferences(entities, prop, options, ref) {
468
468
  const filtered = this.filterCollections(entities, prop.name, options, ref);
469
- const children = [];
470
469
  if (prop.kind === ReferenceKind.ONE_TO_MANY) {
471
- children.push(...filtered.map(e => e[prop.name].owner));
470
+ return filtered.map(e => e[prop.name].owner);
472
471
  }
473
- else if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner) {
474
- children.push(...filtered.reduce((a, b) => {
472
+ if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner) {
473
+ return filtered.reduce((a, b) => {
475
474
  a.push(...b[prop.name].getItems());
476
475
  return a;
477
- }, []));
478
- }
479
- else if (prop.kind === ReferenceKind.MANY_TO_MANY) { // inverse side
480
- children.push(...filtered);
476
+ }, []);
481
477
  }
482
- else { // MANY_TO_ONE or ONE_TO_ONE
483
- children.push(...this.filterReferences(entities, prop.name, options, ref));
478
+ if (prop.kind === ReferenceKind.MANY_TO_MANY) { // inverse side
479
+ return filtered;
484
480
  }
485
- return children;
481
+ // MANY_TO_ONE or ONE_TO_ONE
482
+ return this.filterReferences(entities, prop.name, options, ref);
486
483
  }
487
484
  filterCollections(entities, field, options, ref) {
488
485
  if (options.refresh) {
@@ -11,7 +11,7 @@ export class EntityValidator {
11
11
  }
12
12
  validate(entity, payload, meta) {
13
13
  meta.props.forEach(prop => {
14
- if (prop.inherited) {
14
+ if (prop.inherited || (prop.persist === false && prop.userDefined !== false)) {
15
15
  return;
16
16
  }
17
17
  if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
@@ -73,7 +73,7 @@ export declare class PropertyOptionsBuilder<Value> {
73
73
  /**
74
74
  * Explicitly specify the auto increment of the primary key.
75
75
  */
76
- autoincrement(autoincrement?: boolean): PropertyOptionsBuilder<Value>;
76
+ autoincrement<T extends boolean = true>(autoincrement?: T): PropertyOptionsBuilder<T extends true ? Opt<Value> : Value>;
77
77
  /**
78
78
  * Add the property to the `returning` statement.
79
79
  */
@@ -443,6 +443,9 @@ export declare class OneToOneOptionsBuilder<TargetValue extends object> extends
443
443
  deferMode(deferMode: DeferMode | `${DeferMode}`): OneToOneOptionsBuilder<TargetValue>;
444
444
  }
445
445
  declare const propertyBuilders: {
446
+ bigint: <Mode extends "bigint" | "number" | "string" = "bigint">(mode?: Mode) => PropertyOptionsBuilder<(Mode extends "bigint" ? bigint : Mode extends "number" ? number : string) & {}>;
447
+ array: <T = string>(toJsValue?: (i: string) => T, toDbValue?: (i: T) => string) => PropertyOptionsBuilder<T[]>;
448
+ decimal: <Mode extends "number" | "string" = "string">(mode?: Mode) => PropertyOptionsBuilder<NonNullable<Mode extends "number" ? number : string>>;
446
449
  json: <T>() => PropertyOptionsBuilder<T>;
447
450
  formula: <T>(formula: string | ((alias: string) => string)) => PropertyOptionsBuilder<T>;
448
451
  type: <T extends PropertyValueType>(type: T) => PropertyOptionsBuilder<InferPropertyValueType<T>>;
@@ -455,10 +458,8 @@ declare const propertyBuilders: {
455
458
  date: () => PropertyOptionsBuilder<string>;
456
459
  time: () => PropertyOptionsBuilder<any>;
457
460
  datetime: () => PropertyOptionsBuilder<Date>;
458
- bigint: () => PropertyOptionsBuilder<NonNullable<string | number | bigint | null | undefined>>;
459
461
  blob: () => PropertyOptionsBuilder<NonNullable<Uint8Array<ArrayBufferLike> | Buffer<ArrayBufferLike> | null>>;
460
462
  uint8array: () => PropertyOptionsBuilder<Uint8Array<ArrayBufferLike>>;
461
- array: () => PropertyOptionsBuilder<unknown[]>;
462
463
  enumArray: () => PropertyOptionsBuilder<(string | number)[]>;
463
464
  integer: () => PropertyOptionsBuilder<number>;
464
465
  smallint: () => PropertyOptionsBuilder<number>;
@@ -467,7 +468,6 @@ declare const propertyBuilders: {
467
468
  float: () => PropertyOptionsBuilder<number>;
468
469
  double: () => PropertyOptionsBuilder<NonNullable<string | number>>;
469
470
  boolean: () => PropertyOptionsBuilder<NonNullable<boolean | null | undefined>>;
470
- decimal: () => PropertyOptionsBuilder<NonNullable<string | number>>;
471
471
  character: () => PropertyOptionsBuilder<string>;
472
472
  string: () => PropertyOptionsBuilder<string>;
473
473
  uuid: () => PropertyOptionsBuilder<string>;
@@ -481,6 +481,9 @@ export declare function defineEntity<Properties extends Record<string, any>>(met
481
481
  }): EntitySchema<InferEntityFromProperties<Properties>, never>;
482
482
  export declare namespace defineEntity {
483
483
  var properties: {
484
+ bigint: <Mode extends "bigint" | "number" | "string" = "bigint">(mode?: Mode) => PropertyOptionsBuilder<(Mode extends "bigint" ? bigint : Mode extends "number" ? number : string) & {}>;
485
+ array: <T = string>(toJsValue?: (i: string) => T, toDbValue?: (i: T) => string) => PropertyOptionsBuilder<T[]>;
486
+ decimal: <Mode extends "number" | "string" = "string">(mode?: Mode) => PropertyOptionsBuilder<NonNullable<Mode extends "number" ? number : string>>;
484
487
  json: <T>() => PropertyOptionsBuilder<T>;
485
488
  formula: <T>(formula: string | ((alias: string) => string)) => PropertyOptionsBuilder<T>;
486
489
  type: <T extends PropertyValueType>(type: T) => PropertyOptionsBuilder<InferPropertyValueType<T>>;
@@ -493,10 +496,8 @@ export declare namespace defineEntity {
493
496
  date: () => PropertyOptionsBuilder<string>;
494
497
  time: () => PropertyOptionsBuilder<any>;
495
498
  datetime: () => PropertyOptionsBuilder<Date>;
496
- bigint: () => PropertyOptionsBuilder<NonNullable<string | number | bigint | null | undefined>>;
497
499
  blob: () => PropertyOptionsBuilder<NonNullable<Uint8Array<ArrayBufferLike> | Buffer<ArrayBufferLike> | null>>;
498
500
  uint8array: () => PropertyOptionsBuilder<Uint8Array<ArrayBufferLike>>;
499
- array: () => PropertyOptionsBuilder<unknown[]>;
500
501
  enumArray: () => PropertyOptionsBuilder<(string | number)[]>;
501
502
  integer: () => PropertyOptionsBuilder<number>;
502
503
  smallint: () => PropertyOptionsBuilder<number>;
@@ -505,7 +506,6 @@ export declare namespace defineEntity {
505
506
  float: () => PropertyOptionsBuilder<number>;
506
507
  double: () => PropertyOptionsBuilder<NonNullable<string | number>>;
507
508
  boolean: () => PropertyOptionsBuilder<NonNullable<boolean | null | undefined>>;
508
- decimal: () => PropertyOptionsBuilder<NonNullable<string | number>>;
509
509
  character: () => PropertyOptionsBuilder<string>;
510
510
  string: () => PropertyOptionsBuilder<string>;
511
511
  uuid: () => PropertyOptionsBuilder<string>;
@@ -622,6 +622,9 @@ function createPropertyBuilders(options) {
622
622
  }
623
623
  const propertyBuilders = {
624
624
  ...createPropertyBuilders(types),
625
+ bigint: (mode) => new PropertyOptionsBuilder({ type: new types.bigint(mode) }),
626
+ array: (toJsValue = i => i, toDbValue = i => i) => new PropertyOptionsBuilder({ type: new types.array(toJsValue, toDbValue) }),
627
+ decimal: (mode) => new PropertyOptionsBuilder({ type: new types.decimal(mode) }),
625
628
  json: () => new PropertyOptionsBuilder({ type: types.json }),
626
629
  formula: (formula) => new PropertyOptionsBuilder({ formula }),
627
630
  type: (type) => new PropertyOptionsBuilder({ type }),
@@ -71,7 +71,14 @@ export class ObjectHydrator extends Hydrator {
71
71
  ret.push(` const oldValue_${idx} = entity${entityKey};`);
72
72
  }
73
73
  ret.push(` if (data${dataKey} === null) {`);
74
- ret.push(` entity${entityKey} = ${nullVal};`);
74
+ if (prop.ref) {
75
+ ret.push(` entity${entityKey} = new ScalarReference();`);
76
+ ret.push(` entity${entityKey}.bind(entity, '${prop.name}');`);
77
+ ret.push(` entity${entityKey}.set(${nullVal});`);
78
+ }
79
+ else {
80
+ ret.push(` entity${entityKey} = ${nullVal};`);
81
+ }
75
82
  ret.push(` } else if (typeof data${dataKey} !== 'undefined') {`);
76
83
  if (prop.customType) {
77
84
  registerCustomType(prop, convertorKey, 'convertToJSValue', context);
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * @module core
4
4
  */
5
- export { PrimaryKeyProp, EntityMetadata, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, } from './typings.js';
5
+ export { PrimaryKeyProp, EntityMetadata, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Opt, Hidden, Config, DefineConfig, RequiredNullable, } from './typings.js';
6
6
  export * from './enums.js';
7
7
  export * from './errors.js';
8
8
  export * from './exceptions.js';
@@ -1152,7 +1152,7 @@ export class MetadataDiscovery {
1152
1152
  if (prop.fieldNames?.length === 1 && !prop.customType) {
1153
1153
  [BigIntType, DoubleType, DecimalType, IntervalType, DateType]
1154
1154
  .filter(type => mappedType instanceof type)
1155
- .forEach(type => prop.customType = new type());
1155
+ .forEach((type) => prop.customType = new type());
1156
1156
  }
1157
1157
  if (prop.customType && !prop.columnTypes) {
1158
1158
  const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
@@ -10,7 +10,7 @@ export class MetadataStorage {
10
10
  this.metadata = Utils.copy(metadata, false);
11
11
  }
12
12
  static getMetadata(entity, path) {
13
- const key = entity && path ? entity + '-' + Utils.hash(path) : null;
13
+ const key = entity && path ? entity + '-' + Utils.hash(path, undefined, 'sha256') : null;
14
14
  if (key && !MetadataStorage.metadata[key]) {
15
15
  MetadataStorage.metadata[key] = new EntityMetadata({ className: entity, path });
16
16
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.27",
4
+ "version": "7.0.0-dev.29",
5
5
  "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.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
@@ -52,9 +52,9 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "dataloader": "2.2.3",
55
- "dotenv": "17.2.2",
55
+ "dotenv": "17.2.3",
56
56
  "esprima": "4.0.1",
57
- "mikro-orm": "7.0.0-dev.27",
57
+ "mikro-orm": "7.0.0-dev.29",
58
58
  "reflect-metadata": "0.2.2",
59
59
  "tinyglobby": "0.2.13"
60
60
  }
@@ -5,13 +5,15 @@ import type { EntityProperty } from '../typings.js';
5
5
  * This type will automatically convert string values returned from the database to native JS bigints (default)
6
6
  * or numbers (safe only for values up to `Number.MAX_SAFE_INTEGER`), or strings, depending on the `mode`.
7
7
  */
8
- export declare class BigIntType extends Type<string | bigint | number | null | undefined, string | null | undefined> {
9
- mode?: "bigint" | "number" | "string" | undefined;
10
- constructor(mode?: "bigint" | "number" | "string" | undefined);
11
- convertToDatabaseValue(value: string | bigint | null | undefined): string | null | undefined;
12
- convertToJSValue(value: string | bigint | null | undefined): bigint | number | string | null | undefined;
13
- toJSON(value: string | bigint | null | undefined): string | bigint | null | undefined;
8
+ export declare class BigIntType<Mode extends 'bigint' | 'number' | 'string' = 'bigint'> extends Type<JSTypeByMode<Mode> | null | undefined, string | null | undefined> {
9
+ mode?: Mode | undefined;
10
+ constructor(mode?: Mode | undefined);
11
+ convertToDatabaseValue(value: JSTypeByMode<Mode> | null | undefined): string | null | undefined;
12
+ convertToJSValue(value: string | bigint | null | undefined): JSTypeByMode<Mode> | null | undefined;
13
+ toJSON(value: JSTypeByMode<Mode> | null | undefined): JSTypeByMode<Mode> | null | undefined;
14
14
  getColumnType(prop: EntityProperty, platform: Platform): string;
15
15
  compareAsType(): string;
16
16
  compareValues(a: string, b: string): boolean;
17
17
  }
18
+ type JSTypeByMode<Mode extends 'bigint' | 'number' | 'string'> = Mode extends 'bigint' ? bigint : Mode extends 'number' ? number : string;
19
+ export {};
@@ -4,12 +4,14 @@ import type { EntityProperty } from '../typings.js';
4
4
  /**
5
5
  * Type that maps an SQL DECIMAL to a JS string or number.
6
6
  */
7
- export declare class DecimalType extends Type<string | number, string> {
8
- mode?: "number" | "string" | undefined;
9
- constructor(mode?: "number" | "string" | undefined);
10
- convertToJSValue(value: string): number | string;
7
+ export declare class DecimalType<Mode extends 'number' | 'string' = 'string'> extends Type<JSTypeByMode<Mode>, string> {
8
+ mode?: Mode | undefined;
9
+ constructor(mode?: Mode | undefined);
10
+ convertToJSValue(value: string): JSTypeByMode<Mode>;
11
11
  compareValues(a: string, b: string): boolean;
12
12
  private format;
13
13
  getColumnType(prop: EntityProperty, platform: Platform): string;
14
14
  compareAsType(): string;
15
15
  }
16
+ type JSTypeByMode<Mode extends 'number' | 'string'> = Mode extends 'number' ? number : string;
17
+ export {};
package/typings.d.ts CHANGED
@@ -52,22 +52,34 @@ export declare const OptionalProps: unique symbol;
52
52
  export declare const EagerProps: unique symbol;
53
53
  export declare const HiddenProps: unique symbol;
54
54
  export declare const Config: unique symbol;
55
- declare const __optional: unique symbol;
56
- declare const __requiredNullable: unique symbol;
57
- declare const __hidden: unique symbol;
58
- declare const __config: unique symbol;
59
- export type Opt<T = unknown> = T & {
60
- [__optional]?: 1;
61
- };
62
- export type RequiredNullable<T = never> = (T & {
63
- [__requiredNullable]?: 1;
64
- }) | null;
65
- export type Hidden<T = unknown> = T & {
66
- [__hidden]?: 1;
67
- };
68
- export type DefineConfig<T extends TypeConfig> = T & {
69
- [__config]?: 1;
70
- };
55
+ export type Opt<T = unknown> = T & Opt.Brand;
56
+ export declare namespace Opt {
57
+ const __optional: unique symbol;
58
+ interface Brand {
59
+ [__optional]?: 1;
60
+ }
61
+ }
62
+ export type RequiredNullable<T = never> = (T & RequiredNullable.Brand) | null;
63
+ export declare namespace RequiredNullable {
64
+ const __requiredNullable: unique symbol;
65
+ interface Brand {
66
+ [__requiredNullable]?: 1;
67
+ }
68
+ }
69
+ export type Hidden<T = unknown> = T & Hidden.Brand;
70
+ export declare namespace Hidden {
71
+ const __hidden: unique symbol;
72
+ interface Brand {
73
+ [__hidden]?: 1;
74
+ }
75
+ }
76
+ export type DefineConfig<T extends TypeConfig> = T & DefineConfig.Brand;
77
+ export declare namespace DefineConfig {
78
+ const __config: unique symbol;
79
+ interface Brand {
80
+ [__config]?: 1;
81
+ }
82
+ }
71
83
  export type CleanTypeConfig<T> = Compute<Pick<T, Extract<keyof T, keyof TypeConfig>>>;
72
84
  export interface TypeConfig {
73
85
  forceObject?: boolean;
@@ -82,9 +94,9 @@ export type Primary<T> = IsAny<T> extends true ? any : T extends {
82
94
  } ? (PK extends keyof T ? ReadonlyPrimary<UnwrapPrimary<T[PK]>> : (PK extends (keyof T)[] ? ReadonlyPrimary<PrimaryPropToType<T, PK>> : PK)) : T extends {
83
95
  _id?: infer PK;
84
96
  } ? ReadonlyPrimary<PK> | string : T extends {
85
- uuid?: infer PK;
86
- } ? ReadonlyPrimary<PK> : T extends {
87
97
  id?: infer PK;
98
+ } ? ReadonlyPrimary<PK> : T extends {
99
+ uuid?: infer PK;
88
100
  } ? ReadonlyPrimary<PK> : T;
89
101
  /** @internal */
90
102
  export type PrimaryProperty<T> = T extends {
@@ -94,10 +106,10 @@ export type PrimaryProperty<T> = T extends {
94
106
  } ? (T extends {
95
107
  id?: any;
96
108
  } ? 'id' | '_id' : '_id') : T extends {
97
- uuid?: any;
98
- } ? 'uuid' : T extends {
99
109
  id?: any;
100
- } ? 'id' : never;
110
+ } ? 'id' : T extends {
111
+ uuid?: any;
112
+ } ? 'uuid' : never;
101
113
  export type IPrimaryKeyValue = number | string | bigint | Date | {
102
114
  toHexString(): string;
103
115
  };
@@ -229,9 +241,7 @@ export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Dat
229
241
  __runtime?: infer Runtime;
230
242
  __raw?: infer Raw;
231
243
  } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? EntityDataNested<U, C> : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends Collection<infer U, any> ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
232
- export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends {
233
- [__requiredNullable]?: 1;
234
- } ? T | null : T extends Scalar ? T : T extends {
244
+ export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends RequiredNullable.Brand ? T | null : T extends Scalar ? T : T extends {
235
245
  __runtime?: infer Runtime;
236
246
  __raw?: infer Raw;
237
247
  } ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends Collection<infer U, any> ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
@@ -247,9 +257,7 @@ type NullableKeys<T, V = null> = {
247
257
  [K in keyof T]: V extends T[K] ? K : never;
248
258
  }[keyof T];
249
259
  type RequiredNullableKeys<T> = {
250
- [K in keyof T]: Exclude<T[K], null> extends {
251
- [__requiredNullable]?: 1;
252
- } ? K : never;
260
+ [K in keyof T]: Exclude<T[K], null> extends RequiredNullable.Brand ? K : never;
253
261
  }[keyof T];
254
262
  type ProbablyOptionalProps<T> = PrimaryProperty<T> | ExplicitlyOptionalProps<T> | Exclude<NonNullable<NullableKeys<T, null | undefined>>, RequiredNullableKeys<T>>;
255
263
  type IsOptional<T, K extends keyof T, I> = T[K] extends Collection<any, any> ? true : ExtractType<T[K]> extends I ? true : K extends ProbablyOptionalProps<T> ? true : false;
@@ -82,6 +82,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
82
82
  ensureDatabase: true;
83
83
  ensureIndexes: false;
84
84
  batchSize: number;
85
+ hashAlgorithm: "md5";
85
86
  debug: false;
86
87
  ignoreDeprecations: false;
87
88
  verbose: false;
@@ -104,6 +105,8 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
104
105
  disableForeignKeys: false;
105
106
  createForeignKeyConstraints: true;
106
107
  ignoreSchema: never[];
108
+ skipTables: never[];
109
+ skipColumns: {};
107
110
  };
108
111
  embeddables: {
109
112
  prefixMode: "relative";
@@ -143,7 +146,6 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
143
146
  fileName: (className: string) => string;
144
147
  };
145
148
  preferReadReplicas: true;
146
- dynamicImportProvider: (id: string) => Promise<any>;
147
149
  };
148
150
  private readonly options;
149
151
  private readonly logger;
@@ -380,6 +382,8 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
380
382
  disableForeignKeys?: boolean;
381
383
  createForeignKeyConstraints?: boolean;
382
384
  ignoreSchema?: string[];
385
+ skipTables?: (string | RegExp)[];
386
+ skipColumns?: Dictionary<(string | RegExp)[]>;
383
387
  managementDbName?: string;
384
388
  };
385
389
  embeddables: {
@@ -408,6 +412,6 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
408
412
  };
409
413
  seeder: SeederOptions;
410
414
  preferReadReplicas: boolean;
411
- dynamicImportProvider: (id: string) => Promise<unknown>;
415
+ hashAlgorithm: 'md5' | 'sha256';
412
416
  }
413
417
  export type Options<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager = D[typeof EntityManagerType] & EntityManager> = Pick<MikroORMOptions<D, EM>, Exclude<keyof MikroORMOptions<D, EM>, keyof typeof Configuration.DEFAULTS>> & Partial<MikroORMOptions<D, EM>>;
@@ -72,6 +72,7 @@ export class Configuration {
72
72
  ensureDatabase: true,
73
73
  ensureIndexes: false,
74
74
  batchSize: 300,
75
+ hashAlgorithm: 'md5',
75
76
  debug: false,
76
77
  ignoreDeprecations: false,
77
78
  verbose: false,
@@ -94,6 +95,8 @@ export class Configuration {
94
95
  disableForeignKeys: false,
95
96
  createForeignKeyConstraints: true,
96
97
  ignoreSchema: [],
98
+ skipTables: [],
99
+ skipColumns: {},
97
100
  },
98
101
  embeddables: {
99
102
  prefixMode: 'relative',
@@ -131,7 +134,6 @@ export class Configuration {
131
134
  fileName: (className) => className,
132
135
  },
133
136
  preferReadReplicas: true,
134
- dynamicImportProvider: /* v8 ignore next */ (id) => import(id),
135
137
  };
136
138
  options;
137
139
  logger;
@@ -140,9 +142,6 @@ export class Configuration {
140
142
  cache = new Map();
141
143
  extensions = new Map();
142
144
  constructor(options, validate = true) {
143
- if (options.dynamicImportProvider) {
144
- Utils.setDynamicImportProvider(options.dynamicImportProvider);
145
- }
146
145
  this.options = Utils.mergeConfig({}, Configuration.DEFAULTS, options);
147
146
  this.options.baseDir = Utils.absolutePath(this.options.baseDir);
148
147
  this.options.preferTs ??= options.preferTs;
@@ -269,7 +268,7 @@ export class Configuration {
269
268
  * Gets instance of metadata CacheAdapter. (cached)
270
269
  */
271
270
  getMetadataCacheAdapter() {
272
- return this.getCachedService(this.options.metadataCache.adapter, this.options.metadataCache.options, this.options.baseDir, this.options.metadataCache.pretty);
271
+ return this.getCachedService(this.options.metadataCache.adapter, this.options.metadataCache.options, this.options.baseDir, this.options.metadataCache.pretty, this.options.hashAlgorithm);
273
272
  }
274
273
  /**
275
274
  * Gets instance of CacheAdapter for result cache. (cached)
@@ -40,8 +40,6 @@ export declare class ConfigurationLoader {
40
40
  static loadEnvironmentVars<D extends IDatabaseDriver>(): Promise<Partial<Options<D>>>;
41
41
  static loadEnvironmentVarsSync<D extends IDatabaseDriver>(): Partial<Options<D>>;
42
42
  static getORMPackages(): Set<string>;
43
- /** @internal */
44
- static commonJSCompat(options: Partial<Options>): void;
45
43
  static getORMPackageVersion(name: string): string | undefined;
46
44
  static checkPackageVersion(): string;
47
45
  }
@@ -1,7 +1,5 @@
1
1
  import dotenv from 'dotenv';
2
2
  import { realpathSync } from 'node:fs';
3
- import { platform } from 'node:os';
4
- import { fileURLToPath } from 'node:url';
5
3
  import { colors } from '../logging/colors.js';
6
4
  import { Configuration } from './Configuration.js';
7
5
  import { Utils } from './Utils.js';
@@ -12,7 +10,6 @@ export class ConfigurationLoader {
12
10
  static async getConfiguration(contextName = 'default', paths = ConfigurationLoader.getConfigPaths(), options = {}) {
13
11
  // Backwards compatibility layer
14
12
  if (typeof contextName === 'boolean' || !Array.isArray(paths)) {
15
- this.commonJSCompat(options);
16
13
  this.registerDotenv(options);
17
14
  const configPathFromArg = ConfigurationLoader.configPathsFromArg();
18
15
  const configPaths = configPathFromArg ?? (Array.isArray(paths) ? paths : ConfigurationLoader.getConfigPaths());
@@ -300,25 +297,6 @@ export class ConfigurationLoader {
300
297
  ...Object.keys(pkg.devDependencies ?? {}),
301
298
  ]);
302
299
  }
303
- /** @internal */
304
- static commonJSCompat(options) {
305
- if (this.isESM()) {
306
- return;
307
- }
308
- /* v8 ignore next 11 */
309
- options.dynamicImportProvider ??= id => {
310
- if (platform() === 'win32') {
311
- try {
312
- id = fileURLToPath(id);
313
- }
314
- catch {
315
- // ignore
316
- }
317
- }
318
- return Utils.requireFrom(id);
319
- };
320
- Utils.setDynamicImportProvider(options.dynamicImportProvider);
321
- }
322
300
  static getORMPackageVersion(name) {
323
301
  try {
324
302
  const pkg = Utils.requireFrom(`${name}/package.json`);
@@ -562,7 +562,9 @@ export class EntityComparator {
562
562
  return comparator;
563
563
  }
564
564
  getGenericComparator(prop, cond) {
565
- return ` if (current${prop} == null && last${prop} == null) {\n\n` +
565
+ return ` if (current${prop} === null && last${prop} === undefined) {\n` +
566
+ ` diff${prop} = current${prop};\n` +
567
+ ` } else if (current${prop} == null && last${prop} == null) {\n\n` +
566
568
  ` } else if ((current${prop} != null && last${prop} == null) || (current${prop} == null && last${prop} != null)) {\n` +
567
569
  ` diff${prop} = current${prop};\n` +
568
570
  ` } else if (${cond}) {\n` +
@@ -3,6 +3,7 @@ import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcast
3
3
  import { TransactionContext } from '../utils/TransactionContext.js';
4
4
  import { ChangeSetType } from '../unit-of-work/ChangeSet.js';
5
5
  import { TransactionStateError } from '../errors.js';
6
+ import { helper } from '../entity/wrap.js';
6
7
  /**
7
8
  * Manages transaction lifecycle and propagation for EntityManager.
8
9
  */
@@ -149,8 +150,28 @@ export class TransactionManager {
149
150
  * Merges entities from fork to parent EntityManager.
150
151
  */
151
152
  mergeEntitiesToParent(fork, parent) {
153
+ const parentUoW = parent.getUnitOfWork(false);
154
+ // perf: if parent is empty, we can just move all entities from the fork to skip the `em.merge` overhead
155
+ if (parentUoW.getIdentityMap().keys().length === 0) {
156
+ for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
157
+ parentUoW.getIdentityMap().store(entity);
158
+ helper(entity).__em = parent;
159
+ }
160
+ return;
161
+ }
152
162
  for (const entity of fork.getUnitOfWork(false).getIdentityMap()) {
153
- parent.merge(entity, { disableContextResolution: true, keepIdentity: true, refresh: true });
163
+ const wrapped = helper(entity);
164
+ const meta = wrapped.__meta;
165
+ // eslint-disable-next-line dot-notation
166
+ const parentEntity = parentUoW.getById(meta.className, wrapped.getPrimaryKey(), parent['_schema'], true);
167
+ if (parentEntity && parentEntity !== entity) {
168
+ const parentWrapped = helper(parentEntity);
169
+ parentWrapped.__data = helper(entity).__data;
170
+ parentWrapped.__originalEntityData = helper(entity).__originalEntityData;
171
+ }
172
+ else {
173
+ parentUoW.merge(entity, new Set([entity]));
174
+ }
154
175
  }
155
176
  }
156
177
  /**
package/utils/Utils.d.ts CHANGED
@@ -15,7 +15,6 @@ export declare function equals(a: any, b: any): boolean;
15
15
  export declare function parseJsonSafe<T = unknown>(value: unknown): T;
16
16
  export declare class Utils {
17
17
  static readonly PK_SEPARATOR = "~~~";
18
- static dynamicImportProvider: (id: string) => Promise<any>;
19
18
  /**
20
19
  * Checks if the argument is not undefined
21
20
  */
@@ -206,7 +205,7 @@ export declare class Utils {
206
205
  * If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
207
206
  */
208
207
  static absolutePath(path: string, baseDir?: string): string;
209
- static hash(data: string, length?: number): string;
208
+ static hash(data: string, length?: number, algorithm?: 'md5' | 'sha256'): string;
210
209
  static runIfNotEmpty(clause: () => any, data: any): void;
211
210
  static defaultValue<T extends Dictionary>(prop: T, option: keyof T, defaultValue: any): void;
212
211
  static findDuplicates<T>(items: T[]): T[];
@@ -237,7 +236,6 @@ export declare class Utils {
237
236
  */
238
237
  static resolveModulePath(id: string, from?: string): string;
239
238
  static dynamicImport<T = any>(id: string): Promise<T>;
240
- static setDynamicImportProvider(provider: (id: string) => Promise<unknown>): void;
241
239
  static ensureDir(path: string): void;
242
240
  static pathExistsSync(path: string): boolean;
243
241
  static readJSONSync(path: string): Dictionary;
package/utils/Utils.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { glob, isDynamicPattern } from 'tinyglobby';
3
3
  import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:path';
4
- import { platform } from 'node:os';
5
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
6
5
  import { existsSync, mkdirSync, readFileSync } from 'node:fs';
7
6
  import { createHash } from 'node:crypto';
@@ -132,8 +131,6 @@ export function parseJsonSafe(value) {
132
131
  }
133
132
  export class Utils {
134
133
  static PK_SEPARATOR = '~~~';
135
- /* v8 ignore next */
136
- static dynamicImportProvider = (id) => import(id);
137
134
  /**
138
135
  * Checks if the argument is not undefined
139
136
  */
@@ -789,8 +786,9 @@ export class Utils {
789
786
  }
790
787
  return Utils.normalizePath(path);
791
788
  }
792
- static hash(data, length) {
793
- const hash = createHash('md5').update(data).digest('hex');
789
+ static hash(data, length, algorithm) {
790
+ const hashAlgorithm = algorithm || 'sha256';
791
+ const hash = createHash(hashAlgorithm).update(data).digest('hex');
794
792
  if (length) {
795
793
  return hash.substring(0, length);
796
794
  }
@@ -907,21 +905,9 @@ export class Utils {
907
905
  return parts.join('/');
908
906
  }
909
907
  static async dynamicImport(id) {
910
- /* v8 ignore next 7 */
911
- if (platform() === 'win32') {
912
- try {
913
- id = pathToFileURL(id).toString();
914
- }
915
- catch {
916
- // ignore
917
- }
918
- }
919
908
  /* v8 ignore next */
920
- return this.dynamicImportProvider(id);
921
- }
922
- /* v8 ignore next 3 */
923
- static setDynamicImportProvider(provider) {
924
- this.dynamicImportProvider = provider;
909
+ const specifier = id.startsWith('file://') ? id : pathToFileURL(id).href;
910
+ return import(specifier);
925
911
  }
926
912
  static ensureDir(path) {
927
913
  if (!existsSync(path)) {