@mikro-orm/core 7.0.0-dev.39 → 7.0.0-dev.40

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 (40) hide show
  1. package/EntityManager.d.ts +12 -9
  2. package/EntityManager.js +77 -52
  3. package/README.md +2 -0
  4. package/decorators/Property.d.ts +53 -3
  5. package/entity/Collection.js +3 -0
  6. package/entity/EntityFactory.d.ts +1 -0
  7. package/entity/EntityFactory.js +7 -3
  8. package/entity/EntityHelper.js +17 -2
  9. package/entity/EntityLoader.d.ts +3 -3
  10. package/entity/EntityLoader.js +20 -2
  11. package/entity/Reference.d.ts +1 -0
  12. package/entity/Reference.js +10 -4
  13. package/entity/defineEntity.d.ts +12 -8
  14. package/entity/defineEntity.js +9 -2
  15. package/hydration/ObjectHydrator.d.ts +4 -4
  16. package/hydration/ObjectHydrator.js +25 -22
  17. package/index.d.ts +1 -1
  18. package/metadata/EntitySchema.d.ts +2 -2
  19. package/metadata/MetadataDiscovery.d.ts +1 -0
  20. package/metadata/MetadataDiscovery.js +37 -3
  21. package/naming-strategy/AbstractNamingStrategy.d.ts +5 -1
  22. package/naming-strategy/AbstractNamingStrategy.js +7 -1
  23. package/naming-strategy/NamingStrategy.d.ts +11 -1
  24. package/package.json +2 -2
  25. package/platforms/Platform.js +1 -1
  26. package/serialization/EntitySerializer.js +1 -1
  27. package/serialization/EntityTransformer.js +1 -1
  28. package/serialization/SerializationContext.js +1 -1
  29. package/typings.d.ts +11 -4
  30. package/unit-of-work/ChangeSetPersister.js +16 -5
  31. package/unit-of-work/UnitOfWork.d.ts +6 -0
  32. package/unit-of-work/UnitOfWork.js +37 -23
  33. package/utils/Configuration.d.ts +4 -0
  34. package/utils/Configuration.js +10 -0
  35. package/utils/EntityComparator.js +11 -1
  36. package/utils/QueryHelper.d.ts +3 -1
  37. package/utils/QueryHelper.js +18 -0
  38. package/utils/RawQueryFragment.d.ts +2 -2
  39. package/utils/TransactionManager.js +0 -2
  40. package/utils/Utils.js +2 -2
@@ -2,9 +2,11 @@ import { inspect } from 'node:util';
2
2
  import { DataloaderType } from '../enums.js';
3
3
  import { helper, wrap } from './wrap.js';
4
4
  import { Utils } from '../utils/Utils.js';
5
+ import { QueryHelper } from '../utils/QueryHelper.js';
5
6
  import { NotFoundError } from '../errors.js';
6
7
  export class Reference {
7
8
  entity;
9
+ property;
8
10
  constructor(entity) {
9
11
  this.entity = entity;
10
12
  this.set(entity);
@@ -64,7 +66,9 @@ export class Reference {
64
66
  */
65
67
  static wrapReference(entity, prop) {
66
68
  if (entity && prop.ref && !Reference.isReference(entity)) {
67
- return Reference.create(entity);
69
+ const ref = Reference.create(entity);
70
+ ref.property = prop;
71
+ return ref;
68
72
  }
69
73
  return entity;
70
74
  }
@@ -84,6 +88,7 @@ export class Reference {
84
88
  if (!wrapped.__em) {
85
89
  return this.entity;
86
90
  }
91
+ options = { ...options, filters: QueryHelper.mergePropertyFilters(this.property?.filters, options.filters) };
87
92
  if (this.isInitialized() && !options.refresh && options.populate) {
88
93
  await wrapped.__em.populate(this.entity, options.populate, options);
89
94
  }
@@ -143,7 +148,7 @@ export class Reference {
143
148
  /** @ignore */
144
149
  [inspect.custom](depth = 2) {
145
150
  const object = { ...this };
146
- const hidden = ['meta'];
151
+ const hidden = ['meta', 'property'];
147
152
  hidden.forEach(k => delete object[k]);
148
153
  const ret = inspect(object, { depth });
149
154
  const wrapped = helper(this.entity);
@@ -183,12 +188,13 @@ export class ScalarReference {
183
188
  */
184
189
  async loadOrFail(options = {}) {
185
190
  const ret = await this.load(options);
186
- if (!ret) {
191
+ if (ret == null) {
187
192
  const wrapped = helper(this.entity);
188
193
  options.failHandler ??= wrapped.__em.config.get('findOneOrFailHandler');
189
194
  const entityName = this.entity.constructor.name;
190
195
  const where = wrapped.getPrimaryKey();
191
- throw new NotFoundError(`${entityName} (${where}) failed to load property '${this.property}'`);
196
+ const whereString = typeof where === 'object' ? inspect(where) : where;
197
+ throw new NotFoundError(`${entityName} (${whereString}) failed to load property '${this.property}'`);
192
198
  }
193
199
  return ret;
194
200
  }
@@ -6,14 +6,15 @@ import type { ManyToOneOptions } from '../decorators/ManyToOne.js';
6
6
  import type { OneToManyOptions } from '../decorators/OneToMany.js';
7
7
  import type { OneToOneOptions } from '../decorators/OneToOne.js';
8
8
  import type { ManyToManyOptions } from '../decorators/ManyToMany.js';
9
- import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType } from '../typings.js';
10
- import type { Reference, ScalarReference } from './Reference.js';
9
+ import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref } from '../typings.js';
10
+ import type { ScalarReference } from './Reference.js';
11
11
  import type { SerializeOptions } from '../serialization/EntitySerializer.js';
12
12
  import type { Cascade, DeferMode, EventType, LoadStrategy, QueryOrderMap } from '../enums.js';
13
13
  import type { IType, Type } from '../types/Type.js';
14
14
  import { types } from '../types/index.js';
15
15
  import type { Collection } from './Collection.js';
16
16
  import type { EventSubscriber } from '../events/EventSubscriber.js';
17
+ import type { FilterOptions } from '../drivers/IDatabaseDriver.js';
17
18
  export type UniversalPropertyKeys = keyof PropertyOptions<any> | keyof EnumOptions<any> | keyof EmbeddedOptions<any, any> | keyof ReferenceOptions<any, any> | keyof ManyToOneOptions<any, any> | keyof OneToManyOptions<any, any> | keyof OneToOneOptions<any, any> | keyof ManyToManyOptions<any, any>;
18
19
  type BuilderExtraKeys = '~options' | '~type' | '$type';
19
20
  type ExcludeKeys = 'entity' | 'items';
@@ -113,14 +114,12 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
113
114
  returning(returning?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
114
115
  /**
115
116
  * Automatically set the property value when entity gets created, executed during flush operation.
116
- * @param entity
117
117
  */
118
118
  onCreate(onCreate: (entity: any, em: EntityManager) => Value): Pick<UniversalPropertyOptionsBuilder<Value, Options & {
119
119
  onCreate: (...args: any[]) => any;
120
120
  }, IncludeKeys>, IncludeKeys>;
121
121
  /**
122
122
  * Automatically update the property value every time entity gets updated, executed during flush operation.
123
- * @param entity
124
123
  */
125
124
  onUpdate(onUpdate: (entity: any, em: EntityManager) => Value): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
126
125
  /**
@@ -137,6 +136,10 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
137
136
  defaultRaw(defaultRaw: string): Pick<UniversalPropertyOptionsBuilder<Value, Options & {
138
137
  defaultRaw: string;
139
138
  }, IncludeKeys>, IncludeKeys>;
139
+ /**
140
+ * Allow controlling `filters` option. This will be overridden with `em.fork` or `FindOptions` if provided.
141
+ */
142
+ filters(filters: FilterOptions): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
140
143
  /**
141
144
  * Set to map some SQL snippet for the entity.
142
145
  *
@@ -366,6 +369,7 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
366
369
  foreignKeyName(foreignKeyName: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
367
370
  /** Remove the entity when it gets disconnected from the relationship (see {@doclink cascading | Cascading}). */
368
371
  orphanRemoval(orphanRemoval?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
372
+ accessor(accessor?: string | boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
369
373
  }
370
374
  export interface EmptyOptions extends Partial<Record<UniversalPropertyKeys, unknown>> {
371
375
  }
@@ -420,7 +424,7 @@ declare const propertyBuilders: {
420
424
  export declare function defineEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, any>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never>(meta: Omit<Partial<EntityMetadata<InferEntityFromProperties<TProperties, TPK>>>, 'properties' | 'extends' | 'primaryKeys' | 'hooks'> & {
421
425
  name: TName;
422
426
  tableName?: TTableName;
423
- extends?: string | EntityName<TBase>;
427
+ extends?: EntityName<TBase>;
424
428
  properties: TProperties | ((properties: typeof propertyBuilders) => TProperties);
425
429
  primaryKeys?: TPK & InferPrimaryKey<TProperties>[];
426
430
  hooks?: DefineEntityHooks<InferEntityFromProperties<TProperties, TPK>>;
@@ -429,7 +433,7 @@ export declare function defineEntity<const TEntity = any, const TProperties exte
429
433
  class: EntityClass<TEntity>;
430
434
  className?: TClassName;
431
435
  tableName?: TTableName;
432
- extends?: string | EntityName<TBase>;
436
+ extends?: EntityName<TBase>;
433
437
  properties: TProperties | ((properties: typeof propertyBuilders) => TProperties);
434
438
  }): EntitySchemaWithMeta<TClassName, TTableName, TEntity, TBase, TProperties>;
435
439
  export declare namespace defineEntity {
@@ -518,10 +522,10 @@ type MaybeRelationRef<Value, Options> = Options extends {
518
522
  } ? Value : Options extends {
519
523
  ref: true;
520
524
  kind: '1:1';
521
- } ? Value extends object ? Reference<Value> : never : Options extends {
525
+ } ? Value extends object ? Ref<Value> : never : Options extends {
522
526
  ref: true;
523
527
  kind: 'm:1';
524
- } ? Value extends object ? Reference<Value> : never : Options extends {
528
+ } ? Value extends object ? Ref<Value> : never : Options extends {
525
529
  kind: '1:m';
526
530
  } ? Value extends object ? Collection<Value> : never : Options extends {
527
531
  kind: 'm:n';
@@ -96,14 +96,12 @@ export class UniversalPropertyOptionsBuilder {
96
96
  }
97
97
  /**
98
98
  * Automatically set the property value when entity gets created, executed during flush operation.
99
- * @param entity
100
99
  */
101
100
  onCreate(onCreate) {
102
101
  return this.assignOptions({ onCreate });
103
102
  }
104
103
  /**
105
104
  * Automatically update the property value every time entity gets updated, executed during flush operation.
106
- * @param entity
107
105
  */
108
106
  onUpdate(onUpdate) {
109
107
  return this.assignOptions({ onUpdate });
@@ -122,6 +120,12 @@ export class UniversalPropertyOptionsBuilder {
122
120
  defaultRaw(defaultRaw) {
123
121
  return this.assignOptions({ defaultRaw });
124
122
  }
123
+ /**
124
+ * Allow controlling `filters` option. This will be overridden with `em.fork` or `FindOptions` if provided.
125
+ */
126
+ filters(filters) {
127
+ return this.assignOptions({ filters });
128
+ }
125
129
  /**
126
130
  * Set to map some SQL snippet for the entity.
127
131
  *
@@ -445,6 +449,9 @@ export class UniversalPropertyOptionsBuilder {
445
449
  orphanRemoval(orphanRemoval = true) {
446
450
  return this.assignOptions({ orphanRemoval });
447
451
  }
452
+ accessor(accessor = true) {
453
+ return this.assignOptions({ accessor });
454
+ }
448
455
  }
449
456
  /** @internal */
450
457
  export class OneToManyOptionsBuilderOnlyMappedBy extends UniversalPropertyOptionsBuilder {
@@ -1,22 +1,22 @@
1
1
  import type { EntityData, EntityMetadata } from '../typings.js';
2
2
  import { Hydrator } from './Hydrator.js';
3
3
  import type { EntityFactory } from '../entity/EntityFactory.js';
4
- type EntityHydrator<T extends object> = (entity: T, data: EntityData<T>, factory: EntityFactory, newEntity: boolean, convertCustomTypes: boolean, schema?: string, parentSchema?: string) => void;
4
+ type EntityHydrator<T extends object> = (entity: T, data: EntityData<T>, factory: EntityFactory, newEntity: boolean, convertCustomTypes: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean) => void;
5
5
  export declare class ObjectHydrator extends Hydrator {
6
6
  private readonly hydrators;
7
7
  private tmpIndex;
8
8
  /**
9
9
  * @inheritDoc
10
10
  */
11
- hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
11
+ hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
12
12
  /**
13
13
  * @inheritDoc
14
14
  */
15
- hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
15
+ hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
16
16
  /**
17
17
  * @internal Highly performance-sensitive method.
18
18
  */
19
- getEntityHydrator<T extends object>(meta: EntityMetadata<T>, type: 'full' | 'reference'): EntityHydrator<T>;
19
+ getEntityHydrator<T extends object>(meta: EntityMetadata<T>, type: 'full' | 'reference', normalizeAccessors?: boolean): EntityHydrator<T>;
20
20
  private createCollectionItemMapper;
21
21
  private wrap;
22
22
  private safeKey;
@@ -6,37 +6,40 @@ import { ReferenceKind } from '../enums.js';
6
6
  import { RawQueryFragment } from '../utils/RawQueryFragment.js';
7
7
  export class ObjectHydrator extends Hydrator {
8
8
  hydrators = {
9
- full: new Map(),
10
- reference: new Map(),
9
+ 'full~true': new Map(),
10
+ 'full~false': new Map(),
11
+ 'reference~true': new Map(),
12
+ 'reference~false': new Map(),
11
13
  };
12
14
  tmpIndex = 0;
13
15
  /**
14
16
  * @inheritDoc
15
17
  */
16
- hydrate(entity, meta, data, factory, type, newEntity = false, convertCustomTypes = false, schema, parentSchema) {
17
- const hydrate = this.getEntityHydrator(meta, type);
18
+ hydrate(entity, meta, data, factory, type, newEntity = false, convertCustomTypes = false, schema, parentSchema, normalizeAccessors) {
19
+ const hydrate = this.getEntityHydrator(meta, type, normalizeAccessors);
18
20
  const running = this.running;
19
21
  // the running state is used to consider propagation as hydration, saving the values directly to the entity data,
20
22
  // but we don't want that for new entities, their propagation should result in entity updates when flushing
21
23
  this.running = !newEntity;
22
- Utils.callCompiledFunction(hydrate, entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema);
24
+ Utils.callCompiledFunction(hydrate, entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema, normalizeAccessors);
23
25
  this.running = running;
24
26
  }
25
27
  /**
26
28
  * @inheritDoc
27
29
  */
28
- hydrateReference(entity, meta, data, factory, convertCustomTypes = false, schema, parentSchema) {
29
- const hydrate = this.getEntityHydrator(meta, 'reference');
30
+ hydrateReference(entity, meta, data, factory, convertCustomTypes = false, schema, parentSchema, normalizeAccessors) {
31
+ const hydrate = this.getEntityHydrator(meta, 'reference', normalizeAccessors);
30
32
  const running = this.running;
31
33
  this.running = true;
32
- Utils.callCompiledFunction(hydrate, entity, data, factory, false, convertCustomTypes, schema, parentSchema);
34
+ Utils.callCompiledFunction(hydrate, entity, data, factory, false, convertCustomTypes, schema, parentSchema, normalizeAccessors);
33
35
  this.running = running;
34
36
  }
35
37
  /**
36
38
  * @internal Highly performance-sensitive method.
37
39
  */
38
- getEntityHydrator(meta, type) {
39
- const exists = this.hydrators[type].get(meta.className);
40
+ getEntityHydrator(meta, type, normalizeAccessors = false) {
41
+ const key = `${type}~${normalizeAccessors}`;
42
+ const exists = this.hydrators[key].get(meta.className);
40
43
  if (exists) {
41
44
  return exists;
42
45
  }
@@ -133,17 +136,17 @@ export class ObjectHydrator extends Hydrator {
133
136
  ret.push(` } else if (typeof data${dataKey} !== 'undefined') {`);
134
137
  ret.push(` if (isPrimaryKey(data${dataKey}, true)) {`);
135
138
  if (prop.ref) {
136
- ret.push(` entity${entityKey} = Reference.create(factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema }));`);
139
+ ret.push(` entity${entityKey} = Reference.create(factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, normalizeAccessors, schema }));`);
137
140
  }
138
141
  else {
139
- ret.push(` entity${entityKey} = factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, schema });`);
142
+ ret.push(` entity${entityKey} = factory.createReference('${prop.type}', data${dataKey}, { merge: true, convertCustomTypes, normalizeAccessors, schema });`);
140
143
  }
141
144
  ret.push(` } else if (data${dataKey} && typeof data${dataKey} === 'object') {`);
142
145
  if (prop.ref) {
143
- ret.push(` entity${entityKey} = Reference.create(factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema }));`);
146
+ ret.push(` entity${entityKey} = Reference.create(factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, normalizeAccessors, schema }));`);
144
147
  }
145
148
  else {
146
- ret.push(` entity${entityKey} = factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, schema });`);
149
+ ret.push(` entity${entityKey} = factory.${method}('${prop.type}', data${dataKey}, { initialized: true, merge: true, newEntity, convertCustomTypes, normalizeAccessors, schema });`);
147
150
  }
148
151
  ret.push(` }`);
149
152
  ret.push(` }`);
@@ -252,7 +255,7 @@ export class ObjectHydrator extends Hydrator {
252
255
  // weak comparison as we can have numbers that might have been converted to strings due to being object keys
253
256
  ret.push(` if (data${childDataKey} == '${childMeta.discriminatorValue}') {`);
254
257
  ret.push(` if (entity${entityKey} == null) {`);
255
- ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
258
+ ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes, normalizeAccessors });`);
256
259
  ret.push(` }`);
257
260
  meta.props
258
261
  .filter(p => p.embedded?.[0] === prop.name)
@@ -273,7 +276,7 @@ export class ObjectHydrator extends Hydrator {
273
276
  }
274
277
  else {
275
278
  ret.push(` if (entity${entityKey} == null) {`);
276
- ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
279
+ ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes, normalizeAccessors });`);
277
280
  ret.push(` }`);
278
281
  meta.props
279
282
  .filter(p => p.embedded?.[0] === prop.name)
@@ -311,7 +314,7 @@ export class ObjectHydrator extends Hydrator {
311
314
  };
312
315
  const hydrateProperty = (prop, object = prop.object, path = [prop.name], dataKey) => {
313
316
  const entityKey = path.map(k => this.wrap(k)).join('');
314
- dataKey = dataKey ?? (object ? entityKey : this.wrap(prop.name));
317
+ dataKey = dataKey ?? (object ? entityKey : this.wrap(normalizeAccessors ? (prop.accessor ?? prop.name) : prop.name));
315
318
  const ret = [];
316
319
  if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.mapToPk) {
317
320
  ret.push(...hydrateToOne(prop, dataKey, entityKey));
@@ -341,11 +344,11 @@ export class ObjectHydrator extends Hydrator {
341
344
  for (const prop of props) {
342
345
  lines.push(...hydrateProperty(prop));
343
346
  }
344
- const code = `// compiled hydrator for entity ${meta.className} (${type})\n`
345
- + `return function(entity, data, factory, newEntity, convertCustomTypes, schema) {\n`
347
+ const code = `// compiled hydrator for entity ${meta.className} (${type + normalizeAccessors ? ' normalized' : ''})\n`
348
+ + `return function(entity, data, factory, newEntity, convertCustomTypes, schema, parentSchema, normalizeAccessors) {\n`
346
349
  + `${lines.join('\n')}\n}`;
347
350
  const hydrator = Utils.createFunction(context, code);
348
- this.hydrators[type].set(meta.className, hydrator);
351
+ this.hydrators[key].set(meta.className, hydrator);
349
352
  return hydrator;
350
353
  }
351
354
  createCollectionItemMapper(prop) {
@@ -358,9 +361,9 @@ export class ObjectHydrator extends Hydrator {
358
361
  lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
359
362
  lines.push(` }`);
360
363
  }
361
- lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, merge: true });`);
364
+ lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, normalizeAccessors, merge: true });`);
362
365
  lines.push(` if (value && value.__entity) return value;`);
363
- lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, merge: true });`);
366
+ lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, normalizeAccessors, merge: true });`);
364
367
  lines.push(` }`);
365
368
  return lines;
366
369
  }
package/index.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * @module core
4
4
  */
5
5
  export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config } from './typings.js';
6
- export type { Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, } from './typings.js';
6
+ export type { Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, } from './typings.js';
7
7
  export * from './enums.js';
8
8
  export * from './errors.js';
9
9
  export * from './exceptions.js';
@@ -41,7 +41,7 @@ export type EntitySchemaMetadata<Entity, Base = never> = Omit<Partial<EntityMeta
41
41
  class: EntityClass<Entity>;
42
42
  name?: string;
43
43
  }) & {
44
- extends?: string | EntitySchema<Base>;
44
+ extends?: EntityName<Base>;
45
45
  } & {
46
46
  properties?: {
47
47
  [Key in keyof OmitBaseProps<Entity, Base> as CleanKeys<OmitBaseProps<Entity, Base>, Key>]-?: EntitySchemaProperty<ExpandProperty<NonNullable<Entity[Key]>>, Entity>;
@@ -71,7 +71,7 @@ export declare class EntitySchema<Entity = any, Base = never> {
71
71
  addIndex<Key extends string>(options: IndexOptions<Entity, Key>): void;
72
72
  addUnique<Key extends string>(options: UniqueOptions<Entity, Key>): void;
73
73
  setCustomRepository(repository: () => Constructor): void;
74
- setExtends(base: string | EntitySchema): void;
74
+ setExtends(base: EntityName<any>): void;
75
75
  setClass(proto: EntityClass<Entity>): void;
76
76
  get meta(): EntityMetadata<Entity>;
77
77
  get name(): EntityName<Entity>;
@@ -18,6 +18,7 @@ export declare class MetadataDiscovery {
18
18
  discover(preferTs?: boolean): Promise<MetadataStorage>;
19
19
  discoverSync(preferTs?: boolean): MetadataStorage;
20
20
  private mapDiscoveredEntities;
21
+ private initAccessors;
21
22
  processDiscoveredEntities(discovered: EntityMetadata[]): EntityMetadata[];
22
23
  private findEntities;
23
24
  private discoverMissingTargets;
@@ -74,11 +74,42 @@ export class MetadataDiscovery {
74
74
  });
75
75
  return discovered;
76
76
  }
77
+ initAccessors(meta) {
78
+ for (const prop of Object.values(meta.properties)) {
79
+ if (!prop.accessor || meta.properties[prop.accessor]) {
80
+ continue;
81
+ }
82
+ const desc = Object.getOwnPropertyDescriptor(meta.prototype, prop.name);
83
+ if (desc?.get || desc?.set) {
84
+ this.initFieldName(prop);
85
+ const accessor = prop.name;
86
+ prop.name = typeof prop.accessor === 'string' ? prop.accessor : prop.name;
87
+ if (prop.accessor === true) {
88
+ prop.getter = prop.setter = true;
89
+ }
90
+ else {
91
+ prop.getter = prop.setter = false;
92
+ }
93
+ prop.accessor = accessor;
94
+ prop.serializedName ??= accessor;
95
+ Utils.renameKey(meta.properties, accessor, prop.name);
96
+ }
97
+ else {
98
+ const name = prop.name;
99
+ prop.name = prop.accessor;
100
+ this.initFieldName(prop);
101
+ prop.serializedName ??= prop.accessor;
102
+ prop.name = name;
103
+ prop.trackChanges = false;
104
+ }
105
+ }
106
+ }
77
107
  processDiscoveredEntities(discovered) {
78
108
  for (const meta of discovered) {
79
109
  let i = 1;
80
110
  Object.values(meta.properties).forEach(prop => meta.propertyOrder.set(prop.name, i++));
81
111
  Object.values(meta.properties).forEach(prop => this.initPolyEmbeddables(prop, discovered));
112
+ this.initAccessors(meta);
82
113
  }
83
114
  // ignore base entities (not annotated with @Entity)
84
115
  const filtered = discovered.filter(meta => meta.root.name);
@@ -173,7 +204,8 @@ export class MetadataDiscovery {
173
204
  }
174
205
  tryDiscoverTargets(targets) {
175
206
  for (const target of targets) {
176
- if (typeof target === 'function' && target.name && !this.metadata.has(target.name)) {
207
+ const isDiscoverable = typeof target === 'function' || target instanceof EntitySchema;
208
+ if (isDiscoverable && target.name && !this.metadata.has(target.name)) {
177
209
  this.discoverReferences([target]);
178
210
  this.discoverMissingTargets();
179
211
  }
@@ -227,6 +259,9 @@ export class MetadataDiscovery {
227
259
  if (parent instanceof EntitySchema && !this.metadata.has(parent.meta.className)) {
228
260
  this.discoverReferences([parent]);
229
261
  }
262
+ if (typeof parent === 'function' && parent.name && !this.metadata.has(parent.name)) {
263
+ this.discoverReferences([parent]);
264
+ }
230
265
  /* v8 ignore next 3 */
231
266
  if (!meta.class) {
232
267
  continue;
@@ -952,7 +987,7 @@ export class MetadataDiscovery {
952
987
  newProp.items = Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
953
988
  }
954
989
  newProp.nullable = true;
955
- newProp.inherited = true;
990
+ newProp.inherited = !meta.root.properties[prop.name];
956
991
  meta.root.addProperty(newProp);
957
992
  });
958
993
  meta.collection = meta.root.collection;
@@ -1034,7 +1069,6 @@ export class MetadataDiscovery {
1034
1069
  return '1';
1035
1070
  }
1036
1071
  inferDefaultValue(meta, prop) {
1037
- /* v8 ignore next 3 */
1038
1072
  if (!meta.class) {
1039
1073
  return;
1040
1074
  }
@@ -12,7 +12,11 @@ export declare abstract class AbstractNamingStrategy implements NamingStrategy {
12
12
  /**
13
13
  * @inheritDoc
14
14
  */
15
- getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
15
+ getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
16
+ /**
17
+ * @inheritDoc
18
+ */
19
+ getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
16
20
  /**
17
21
  * @inheritDoc
18
22
  */
@@ -48,7 +48,13 @@ export class AbstractNamingStrategy {
48
48
  * @inheritDoc
49
49
  */
50
50
  getEnumClassName(columnName, tableName, schemaName) {
51
- return this.getEntityName(`${tableName}_${columnName}`, schemaName);
51
+ return this.getEntityName(tableName ? `${tableName}_${columnName}` : columnName, schemaName);
52
+ }
53
+ /**
54
+ * @inheritDoc
55
+ */
56
+ getEnumTypeName(columnName, tableName, schemaName) {
57
+ return 'T' + this.getEnumClassName(columnName, tableName, schemaName);
52
58
  }
53
59
  /**
54
60
  * @inheritDoc
@@ -25,7 +25,17 @@ export interface NamingStrategy {
25
25
  *
26
26
  * @return A new class name that will be used for the enum.
27
27
  */
28
- getEnumClassName(columnName: string, tableName: string, schemaName?: string): string;
28
+ getEnumClassName(columnName: string, tableName: string | undefined, schemaName?: string): string;
29
+ /**
30
+ * Get an enum type name. Used with `enumType: 'dictionary'` and `enumType: 'union-type'` entity generator option.
31
+ *
32
+ * @param columnName The column name which has the enum.
33
+ * @param tableName The table name of the column.
34
+ * @param schemaName The schema name of the column.
35
+ *
36
+ * @return A new type name that will be used for the enum.
37
+ */
38
+ getEnumTypeName(columnName: string, tableName: string | undefined, schemaName?: string): string;
29
39
  /**
30
40
  * Get an enum option name for a given enum value.
31
41
  *
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.39",
4
+ "version": "7.0.0-dev.40",
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",
@@ -54,7 +54,7 @@
54
54
  "dataloader": "2.2.3",
55
55
  "dotenv": "17.2.3",
56
56
  "esprima": "4.0.1",
57
- "mikro-orm": "7.0.0-dev.39",
57
+ "mikro-orm": "7.0.0-dev.40",
58
58
  "reflect-metadata": "0.2.2",
59
59
  "tinyglobby": "0.2.13"
60
60
  }
@@ -133,7 +133,7 @@ export class Platform {
133
133
  return true;
134
134
  }
135
135
  isBigIntProperty(prop) {
136
- return prop.columnTypes && prop.columnTypes[0] === 'bigint';
136
+ return prop.columnTypes?.[0] === 'bigint';
137
137
  }
138
138
  getDefaultSchemaName() {
139
139
  return undefined;
@@ -16,7 +16,7 @@ function isVisible(meta, propName, options) {
16
16
  return false;
17
17
  }
18
18
  const visible = prop && !(prop.hidden && !options.includeHidden);
19
- const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
19
+ const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
20
20
  return visible && !prefixed;
21
21
  }
22
22
  function isPopulated(propName, options) {
@@ -6,7 +6,7 @@ import { isRaw } from '../utils/RawQueryFragment.js';
6
6
  function isVisible(meta, propName, ignoreFields = []) {
7
7
  const prop = meta.properties[propName];
8
8
  const visible = prop && !prop.hidden;
9
- const prefixed = prop && !prop.primary && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
9
+ const prefixed = prop && !prop.primary && !prop.accessor && propName.startsWith('_'); // ignore prefixed properties, if it's not a PK
10
10
  return visible && !prefixed && !ignoreFields.includes(propName);
11
11
  }
12
12
  export class EntityTransformer {
@@ -37,7 +37,7 @@ export class SerializationContext {
37
37
  leave(entityName, prop) {
38
38
  const last = this.path.pop();
39
39
  /* v8 ignore next 3 */
40
- if (!last || last[0] !== entityName || last[1] !== prop) {
40
+ if (last?.[0] !== entityName || last[1] !== prop) {
41
41
  throw new Error(`Trying to leave wrong property: ${entityName}.${prop} instead of ${last?.join('.')}`);
42
42
  }
43
43
  }
package/typings.d.ts CHANGED
@@ -19,7 +19,7 @@ import type { RawQueryFragment } from './utils/RawQueryFragment.js';
19
19
  import type { EntityManager } from './EntityManager.js';
20
20
  import type { EmbeddedPrefixMode } from './decorators/Embedded.js';
21
21
  import type { EventSubscriber } from './events/EventSubscriber.js';
22
- import type { FindOneOptions, FindOptions, LoadHint } from './drivers/IDatabaseDriver.js';
22
+ import type { FilterOptions, FindOneOptions, FindOptions, LoadHint } from './drivers/IDatabaseDriver.js';
23
23
  export type Constructor<T = unknown> = new (...args: any[]) => T;
24
24
  export type Dictionary<T = any> = {
25
25
  [k: string]: T;
@@ -361,6 +361,7 @@ export interface EntityProperty<Owner = any, Target = any> {
361
361
  default?: string | number | boolean | null;
362
362
  defaultRaw?: string;
363
363
  formula?: (alias: string) => string;
364
+ filters?: FilterOptions;
364
365
  prefix?: string | boolean;
365
366
  prefixMode?: EmbeddedPrefixMode;
366
367
  embedded?: [EntityKey<Owner>, EntityKey<Owner>];
@@ -388,6 +389,7 @@ export interface EntityProperty<Owner = any, Target = any> {
388
389
  setter?: boolean;
389
390
  getter?: boolean;
390
391
  getterName?: keyof Owner;
392
+ accessor?: EntityKey<Owner>;
391
393
  cascade: Cascade[];
392
394
  orphanRemoval?: boolean;
393
395
  onCreate?: (entity: Owner, em: EntityManager) => any;
@@ -587,7 +589,9 @@ export interface GenerateOptions {
587
589
  undefinedDefaults?: boolean;
588
590
  bidirectionalRelations?: boolean;
589
591
  identifiedReferences?: boolean;
590
- entitySchema?: boolean;
592
+ entityDefinition?: 'decorators' | 'defineEntity' | 'entitySchema';
593
+ inferEntityType?: boolean;
594
+ enumMode?: 'ts-enum' | 'union-type' | 'dictionary';
591
595
  esmImport?: boolean;
592
596
  scalarTypeInDecorator?: boolean;
593
597
  scalarPropertiesForRelations?: 'always' | 'never' | 'smart';
@@ -602,6 +606,8 @@ export interface GenerateOptions {
602
606
  coreImportsPrefix?: string;
603
607
  onInitialMetadata?: MetadataProcessor;
604
608
  onProcessedMetadata?: MetadataProcessor;
609
+ /** @deprecated use `entityDefinition: 'entitySchema'` instead */
610
+ entitySchema?: boolean;
605
611
  }
606
612
  export interface IEntityGenerator {
607
613
  generate(options?: GenerateOptions): Promise<string[]>;
@@ -721,6 +727,7 @@ export type FilterDef = {
721
727
  default?: boolean;
722
728
  entity?: string[];
723
729
  args?: boolean;
730
+ strict?: boolean;
724
731
  };
725
732
  export type Populate<T, P extends string = never> = readonly AutoPath<T, P, `${PopulatePath}`>[] | false;
726
733
  export type PopulateOptions<T> = {
@@ -824,11 +831,11 @@ export interface IHydrator {
824
831
  * Hydrates the whole entity. This process handles custom type conversions, creating missing Collection instances,
825
832
  * mapping FKs to entity instances, as well as merging those entities.
826
833
  */
827
- hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
834
+ hydrate<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, type: 'full' | 'reference', newEntity?: boolean, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
828
835
  /**
829
836
  * Hydrates primary keys only
830
837
  */
831
- hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string): void;
838
+ hydrateReference<T extends object>(entity: T, meta: EntityMetadata<T>, data: EntityData<T>, factory: EntityFactory, convertCustomTypes?: boolean, schema?: string, parentSchema?: string, normalizeAccessors?: boolean): void;
832
839
  isRunning(): boolean;
833
840
  }
834
841
  export interface HydratorConstructor {