@mikro-orm/core 6.5.10-dev.2 → 6.5.10-dev.21

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.
@@ -4,7 +4,7 @@ import { type Configuration, Cursor } from './utils';
4
4
  import { type AssignOptions, EntityFactory, EntityLoader, type EntityLoaderOptions, type EntityRepository, EntityValidator, Reference } from './entity';
5
5
  import { UnitOfWork } from './unit-of-work';
6
6
  import type { CountOptions, DeleteOptions, FindAllOptions, FindByCursorOptions, FindOneOptions, FindOneOrFailOptions, FindOptions, GetReferenceOptions, IDatabaseDriver, LockOptions, NativeInsertUpdateOptions, UpdateOptions, UpsertManyOptions, UpsertOptions } from './drivers';
7
- import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
7
+ import type { AnyEntity, AnyString, ArrayElement, AutoPath, ConnectionType, Dictionary, EntityData, EntityDictionary, EntityDTO, EntityMetadata, EntityName, FilterDef, FilterQuery, FromEntityType, GetRepository, IHydrator, IsSubset, Loaded, MaybePromise, MergeLoaded, MergeSelected, NoInfer, ObjectQuery, Primary, Ref, RequiredEntityData, UnboxArray } from './typings';
8
8
  import { FlushMode, LockMode, PopulatePath, type TransactionOptions } from './enums';
9
9
  import type { MetadataStorage } from './metadata';
10
10
  import type { Transaction } from './connections';
@@ -83,19 +83,19 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
83
83
  /**
84
84
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
85
85
  */
86
- addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], enabled?: boolean): void;
86
+ addFilter<T1>(name: string, cond: FilterQuery<T1> | ((args: Dictionary) => MaybePromise<FilterQuery<T1>>), entityName?: EntityName<T1> | [EntityName<T1>], options?: boolean | Partial<FilterDef>): void;
87
87
  /**
88
88
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
89
89
  */
90
- addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], enabled?: boolean): void;
90
+ addFilter<T1, T2>(name: string, cond: FilterQuery<T1 | T2> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2>>), entityName?: [EntityName<T1>, EntityName<T2>], options?: boolean | Partial<FilterDef>): void;
91
91
  /**
92
92
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
93
93
  */
94
- addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], enabled?: boolean): void;
94
+ addFilter<T1, T2, T3>(name: string, cond: FilterQuery<T1 | T2 | T3> | ((args: Dictionary) => MaybePromise<FilterQuery<T1 | T2 | T3>>), entityName?: [EntityName<T1>, EntityName<T2>, EntityName<T3>], options?: boolean | Partial<FilterDef>): void;
95
95
  /**
96
96
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
97
97
  */
98
- addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], enabled?: boolean): void;
98
+ addFilter(name: string, cond: Dictionary | ((args: Dictionary) => MaybePromise<FilterQuery<AnyEntity>>), entityName?: EntityName<AnyEntity> | EntityName<AnyEntity>[], options?: boolean | Partial<FilterDef>): void;
99
99
  /**
100
100
  * Sets filter parameter values globally inside context defined by this entity manager.
101
101
  * If you want to set shared value for all contexts, be sure to use the root entity manager.
@@ -119,11 +119,14 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
119
119
  protected processWhere<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entityName: string, where: FilterQuery<Entity>, options: FindOptions<Entity, Hint, Fields, Excludes> | FindOneOptions<Entity, Hint, Fields, Excludes>, type: 'read' | 'update' | 'delete'): Promise<FilterQuery<Entity>>;
120
120
  protected applyDiscriminatorCondition<Entity extends object>(entityName: string, where: FilterQuery<Entity>): FilterQuery<Entity>;
121
121
  protected createPopulateWhere<Entity extends object>(cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any> | CountOptions<Entity, any>): ObjectQuery<Entity>;
122
- protected getJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>, cond: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): Promise<ObjectQuery<Entity>>;
122
+ protected getJoinedFilters<Entity extends object>(meta: EntityMetadata<Entity>, options: FindOptions<Entity, any, any, any> | FindOneOptions<Entity, any, any, any>): Promise<ObjectQuery<Entity> | undefined>;
123
123
  /**
124
124
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
125
125
  */
126
- protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>): Promise<void>;
126
+ protected autoJoinRefsForFilters<T extends object>(meta: EntityMetadata<T>, options: FindOptions<T, any, any, any> | FindOneOptions<T, any, any, any>, parent?: {
127
+ className: string;
128
+ propName: string;
129
+ }): Promise<void>;
127
130
  /**
128
131
  * @internal
129
132
  */
package/EntityManager.js CHANGED
@@ -143,7 +143,7 @@ class EntityManager {
143
143
  // save the original hint value so we know it was infer/all
144
144
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
145
145
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
146
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
146
+ options.populateFilter = await this.getJoinedFilters(meta, options);
147
147
  const results = await em.driver.find(entityName, where, { ctx: em.transactionContext, em, ...options });
148
148
  if (results.length === 0) {
149
149
  await em.storeCache(options.cache, cached, []);
@@ -197,8 +197,8 @@ class EntityManager {
197
197
  /**
198
198
  * Registers global filter to this entity manager. Global filters are enabled by default (unless disabled via last parameter).
199
199
  */
200
- addFilter(name, cond, entityName, enabled = true) {
201
- const options = { name, cond, default: enabled };
200
+ addFilter(name, cond, entityName, options = true) {
201
+ options = typeof options === 'object' ? { name, cond, default: true, ...options } : { name, cond, default: options };
202
202
  if (entityName) {
203
203
  options.entity = utils_1.Utils.asArray(entityName).map(n => utils_1.Utils.className(n));
204
204
  }
@@ -277,29 +277,37 @@ class EntityManager {
277
277
  }
278
278
  return ret;
279
279
  }
280
- async getJoinedFilters(meta, cond, options) {
280
+ async getJoinedFilters(meta, options) {
281
+ if (!this.config.get('filtersOnRelations') || !options.populate) {
282
+ return undefined;
283
+ }
281
284
  const ret = {};
282
- if (options.populate) {
283
- for (const hint of options.populate) {
284
- const field = hint.field.split(':')[0];
285
- const prop = meta.properties[field];
286
- const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
287
- const joined = strategy === enums_1.LoadStrategy.JOINED && prop.kind !== enums_1.ReferenceKind.SCALAR;
288
- if (!joined && !hint.filter) {
289
- continue;
290
- }
291
- const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', { ...options, populate: hint.children });
292
- const where2 = await this.getJoinedFilters(prop.targetMeta, {}, { ...options, populate: hint.children, populateWhere: enums_1.PopulateHint.ALL });
293
- if (utils_1.Utils.hasObjectKeys(where)) {
294
- ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
285
+ for (const hint of options.populate) {
286
+ const field = hint.field.split(':')[0];
287
+ const prop = meta.properties[field];
288
+ const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
289
+ const joined = strategy === enums_1.LoadStrategy.JOINED && prop.kind !== enums_1.ReferenceKind.SCALAR;
290
+ if (!joined && !hint.filter) {
291
+ continue;
292
+ }
293
+ const where = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', {
294
+ ...options,
295
+ populate: hint.children,
296
+ });
297
+ const where2 = await this.getJoinedFilters(prop.targetMeta, {
298
+ ...options,
299
+ populate: hint.children,
300
+ populateWhere: enums_1.PopulateHint.ALL,
301
+ });
302
+ if (utils_1.Utils.hasObjectKeys(where)) {
303
+ ret[field] = ret[field] ? { $and: [where, ret[field]] } : where;
304
+ }
305
+ if (where2 && utils_1.Utils.hasObjectKeys(where2)) {
306
+ if (ret[field]) {
307
+ utils_1.Utils.merge(ret[field], where2);
295
308
  }
296
- if (utils_1.Utils.hasObjectKeys(where2)) {
297
- if (ret[field]) {
298
- utils_1.Utils.merge(ret[field], where2);
299
- }
300
- else {
301
- ret[field] = where2;
302
- }
309
+ else {
310
+ ret[field] = where2;
303
311
  }
304
312
  }
305
313
  }
@@ -308,29 +316,29 @@ class EntityManager {
308
316
  /**
309
317
  * When filters are active on M:1 or 1:1 relations, we need to ref join them eagerly as they might affect the FK value.
310
318
  */
311
- async autoJoinRefsForFilters(meta, options) {
312
- if (!meta || !this.config.get('autoJoinRefsForFilters')) {
319
+ async autoJoinRefsForFilters(meta, options, parent) {
320
+ if (!meta || !this.config.get('autoJoinRefsForFilters') || options.filters === false) {
313
321
  return;
314
322
  }
315
- const props = meta.relations.filter(prop => {
316
- return !prop.object && [enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
317
- && ((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)));
318
- });
319
323
  const ret = options.populate;
320
- for (const prop of props) {
324
+ for (const prop of meta.relations) {
325
+ if (prop.object
326
+ || ![enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind)
327
+ || !((options.fields?.length ?? 0) === 0 || options.fields?.some(f => prop.name === f || prop.name.startsWith(`${String(f)}.`)))
328
+ || (parent?.className === prop.targetMeta.root.className && parent.propName === prop.inversedBy)) {
329
+ continue;
330
+ }
321
331
  const cond = await this.applyFilters(prop.type, {}, options.filters ?? {}, 'read', options);
322
332
  if (!utils_1.Utils.isEmpty(cond)) {
323
333
  const populated = options.populate.filter(({ field }) => field.split(':')[0] === prop.name);
324
334
  let found = false;
325
- if (populated.length > 0) {
326
- for (const hint of populated) {
327
- if (!hint.all) {
328
- hint.filter = true;
329
- found = true;
330
- }
331
- else if (hint.field === `${prop.name}:ref`) {
332
- found = true;
333
- }
335
+ for (const hint of populated) {
336
+ if (!hint.all) {
337
+ hint.filter = true;
338
+ }
339
+ const strategy = (0, utils_2.getLoadingStrategy)(prop.strategy || hint.strategy || options.strategy || this.config.get('loadStrategy'), prop.kind);
340
+ if (hint.field === `${prop.name}:ref` || (hint.filter && strategy === enums_1.LoadStrategy.JOINED)) {
341
+ found = true;
334
342
  }
335
343
  }
336
344
  if (!found) {
@@ -338,6 +346,14 @@ class EntityManager {
338
346
  }
339
347
  }
340
348
  }
349
+ for (const hint of ret) {
350
+ const [field, ref] = hint.field.split(':');
351
+ const prop = meta?.properties[field];
352
+ if (prop && !ref) {
353
+ hint.children ??= [];
354
+ await this.autoJoinRefsForFilters(prop.targetMeta, { ...options, populate: hint.children }, { className: meta.root.className, propName: prop.name });
355
+ }
356
+ }
341
357
  }
342
358
  /**
343
359
  * @internal
@@ -376,13 +392,17 @@ class EntityManager {
376
392
  else {
377
393
  cond = filter.cond;
378
394
  }
379
- ret.push(utils_1.QueryHelper.processWhere({
395
+ cond = utils_1.QueryHelper.processWhere({
380
396
  where: cond,
381
397
  entityName,
382
398
  metadata: this.metadata,
383
399
  platform: this.driver.getPlatform(),
384
400
  aliased: type === 'read',
385
- }));
401
+ });
402
+ if (filter.strict) {
403
+ Object.defineProperty(cond, '__strict', { value: filter.strict, enumerable: false });
404
+ }
405
+ ret.push(cond);
386
406
  }
387
407
  const conds = [...ret, where].filter(c => utils_1.Utils.hasObjectKeys(c));
388
408
  return conds.length > 1 ? { $and: conds } : conds[0];
@@ -568,7 +588,7 @@ class EntityManager {
568
588
  // save the original hint value so we know it was infer/all
569
589
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
570
590
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
571
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
591
+ options.populateFilter = await this.getJoinedFilters(meta, options);
572
592
  const data = await em.driver.findOne(entityName, where, {
573
593
  ctx: em.transactionContext,
574
594
  em,
@@ -1253,7 +1273,7 @@ class EntityManager {
1253
1273
  const meta = em.metadata.find(entityName);
1254
1274
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
1255
1275
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
1256
- options.populateFilter = await this.getJoinedFilters(meta, { ...where }, options);
1276
+ options.populateFilter = await this.getJoinedFilters(meta, options);
1257
1277
  em.validator.validateParams(where);
1258
1278
  delete options.orderBy;
1259
1279
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
@@ -1387,7 +1407,7 @@ class EntityManager {
1387
1407
  const em = this.getContext();
1388
1408
  em.prepareOptions(options);
1389
1409
  const entityName = arr[0].constructor.name;
1390
- const preparedPopulate = await em.preparePopulate(entityName, { populate: populate }, options.validate);
1410
+ const preparedPopulate = await em.preparePopulate(entityName, { populate: populate, filters: options.filters }, options.validate);
1391
1411
  await em.entityLoader.populate(entityName, arr, preparedPopulate, options);
1392
1412
  return entities;
1393
1413
  }
@@ -160,6 +160,7 @@ class EntityFactory {
160
160
  this.create(prop.type, data[prop.name], options); // we can ignore the value, we just care about the `mergeData` call
161
161
  }
162
162
  });
163
+ this.unitOfWork.normalizeEntityData(meta, originalEntityData);
163
164
  (0, wrap_1.helper)(entity).__touched = false;
164
165
  }
165
166
  createReference(entityName, id, options = {}) {
@@ -213,7 +213,7 @@ class EntityLoader {
213
213
  }
214
214
  }
215
215
  async findChildren(entities, prop, populate, options, ref) {
216
- const children = this.getChildReferences(entities, prop, options, ref);
216
+ const children = Utils_1.Utils.unique(this.getChildReferences(entities, prop, options, ref));
217
217
  const meta = prop.targetMeta;
218
218
  let fk = Utils_1.Utils.getPrimaryKeyHash(meta.primaryKeys);
219
219
  let schema = options.schema;
@@ -269,6 +269,24 @@ class EntityLoader {
269
269
  // @ts-ignore not a public option, will be propagated to the populate call
270
270
  visited: options.visited,
271
271
  });
272
+ if ([enums_1.ReferenceKind.ONE_TO_ONE, enums_1.ReferenceKind.MANY_TO_ONE].includes(prop.kind) && items.length !== children.length) {
273
+ const nullVal = this.em.config.get('forceUndefined') ? undefined : null;
274
+ const itemsMap = new Set();
275
+ const childrenMap = new Set();
276
+ for (const item of items) {
277
+ itemsMap.add((0, wrap_1.helper)(item).getSerializedPrimaryKey());
278
+ }
279
+ for (const child of children) {
280
+ childrenMap.add((0, wrap_1.helper)(child).getSerializedPrimaryKey());
281
+ }
282
+ for (const entity of entities) {
283
+ const key = (0, wrap_1.helper)(entity[prop.name] ?? {})?.getSerializedPrimaryKey();
284
+ if (childrenMap.has(key) && !itemsMap.has(key)) {
285
+ entity[prop.name] = nullVal;
286
+ (0, wrap_1.helper)(entity).__originalEntityData[prop.name] = null;
287
+ }
288
+ }
289
+ }
272
290
  for (const item of items) {
273
291
  if (ref && !(0, wrap_1.helper)(item).__onLoadFired) {
274
292
  (0, wrap_1.helper)(item).__initialized = false;
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, 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, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, 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, RequiredNullable, } 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, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, } from './typings';
6
6
  export * from './enums';
7
7
  export * from './errors';
8
8
  export * from './exceptions';
@@ -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
  */
@@ -51,7 +51,13 @@ class AbstractNamingStrategy {
51
51
  * @inheritDoc
52
52
  */
53
53
  getEnumClassName(columnName, tableName, schemaName) {
54
- return this.getEntityName(`${tableName}_${columnName}`, schemaName);
54
+ return this.getEntityName(tableName ? `${tableName}_${columnName}` : columnName, schemaName);
55
+ }
56
+ /**
57
+ * @inheritDoc
58
+ */
59
+ getEnumTypeName(columnName, tableName, schemaName) {
60
+ return 'T' + this.getEnumClassName(columnName, tableName, schemaName);
55
61
  }
56
62
  /**
57
63
  * @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,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.5.10-dev.2",
3
+ "version": "6.5.10-dev.21",
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.3.2",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.5.10-dev.2",
67
+ "mikro-orm": "6.5.10-dev.21",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
package/typings.d.ts CHANGED
@@ -577,7 +577,9 @@ export interface GenerateOptions {
577
577
  undefinedDefaults?: boolean;
578
578
  bidirectionalRelations?: boolean;
579
579
  identifiedReferences?: boolean;
580
- entitySchema?: boolean;
580
+ entityDefinition?: 'decorators' | 'defineEntity' | 'entitySchema';
581
+ inferEntityType?: boolean;
582
+ enumMode?: 'ts-enum' | 'union-type' | 'dictionary';
581
583
  esmImport?: boolean;
582
584
  scalarTypeInDecorator?: boolean;
583
585
  scalarPropertiesForRelations?: 'always' | 'never' | 'smart';
@@ -592,6 +594,8 @@ export interface GenerateOptions {
592
594
  coreImportsPrefix?: string;
593
595
  onInitialMetadata?: MetadataProcessor;
594
596
  onProcessedMetadata?: MetadataProcessor;
597
+ /** @deprecated use `entityDefinition: 'entitySchema'` instead */
598
+ entitySchema?: boolean;
595
599
  }
596
600
  export interface IEntityGenerator {
597
601
  generate(options?: GenerateOptions): Promise<string[]>;
@@ -710,6 +714,7 @@ export type FilterDef = {
710
714
  default?: boolean;
711
715
  entity?: string[];
712
716
  args?: boolean;
717
+ strict?: boolean;
713
718
  };
714
719
  export type Populate<T, P extends string = never> = readonly AutoPath<T, P, `${PopulatePath}`>[] | false;
715
720
  export type PopulateOptions<T> = {
@@ -27,6 +27,12 @@ export declare class UnitOfWork {
27
27
  private working;
28
28
  constructor(em: EntityManager);
29
29
  merge<T extends object>(entity: T, visited?: Set<AnyEntity>): void;
30
+ /**
31
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
32
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
33
+ * @internal
34
+ */
35
+ normalizeEntityData<T extends object>(meta: EntityMetadata<T>, data: EntityData<T>): void;
30
36
  /**
31
37
  * @internal
32
38
  */
@@ -64,6 +64,40 @@ class UnitOfWork {
64
64
  }
65
65
  this.cascade(entity, enums_1.Cascade.MERGE, visited ?? new Set());
66
66
  }
67
+ /**
68
+ * Entity data can wary in its shape, e.g. we might get a deep relation graph with joined strategy, but for diffing,
69
+ * we need to normalize the shape, so relation values are only raw FKs. This method handles that.
70
+ * @internal
71
+ */
72
+ normalizeEntityData(meta, data) {
73
+ const forceUndefined = this.em.config.get('forceUndefined');
74
+ for (const key of Utils_1.Utils.keys(data)) {
75
+ const prop = meta.properties[key];
76
+ if (!prop) {
77
+ continue;
78
+ }
79
+ if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
80
+ data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
81
+ }
82
+ else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
83
+ for (const p of prop.targetMeta.props) {
84
+ /* istanbul ignore next */
85
+ const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
86
+ data[prefix + p.name] = data[prop.name][p.name];
87
+ }
88
+ data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
89
+ }
90
+ if (prop.hydrate === false && prop.customType?.ensureComparable(meta, prop)) {
91
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
92
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
93
+ }
94
+ if (forceUndefined) {
95
+ if (data[key] === null) {
96
+ data[key] = undefined;
97
+ }
98
+ }
99
+ }
100
+ }
67
101
  /**
68
102
  * @internal
69
103
  */
@@ -81,31 +115,11 @@ class UnitOfWork {
81
115
  wrapped.__em ??= this.em;
82
116
  wrapped.__managed = true;
83
117
  if (data && (options?.refresh || !wrapped.__originalEntityData)) {
118
+ this.normalizeEntityData(wrapped.__meta, data);
84
119
  for (const key of Utils_1.Utils.keys(data)) {
85
120
  const prop = wrapped.__meta.properties[key];
86
- if (!prop) {
87
- continue;
88
- }
89
- wrapped.__loadedProperties.add(key);
90
- if ([enums_1.ReferenceKind.MANY_TO_ONE, enums_1.ReferenceKind.ONE_TO_ONE].includes(prop.kind) && Utils_1.Utils.isPlainObject(data[prop.name])) {
91
- data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
92
- }
93
- else if (prop.kind === enums_1.ReferenceKind.EMBEDDED && !prop.object && Utils_1.Utils.isPlainObject(data[prop.name])) {
94
- for (const p of prop.targetMeta.props) {
95
- /* istanbul ignore next */
96
- const prefix = prop.prefix === false ? '' : prop.prefix === true ? prop.name + '_' : prop.prefix;
97
- data[prefix + p.name] = data[prop.name][p.name];
98
- }
99
- data[prop.name] = Utils_1.Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
100
- }
101
- if (prop.hydrate === false && prop.customType?.ensureComparable(wrapped.__meta, prop)) {
102
- const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
103
- data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
104
- }
105
- if (forceUndefined) {
106
- if (data[key] === null) {
107
- data[key] = undefined;
108
- }
121
+ if (prop) {
122
+ wrapped.__loadedProperties.add(key);
109
123
  }
110
124
  }
111
125
  wrapped.__originalEntityData = data;
@@ -61,6 +61,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
61
61
  onQuery: (sql: string) => string;
62
62
  autoJoinOneToOneOwner: true;
63
63
  autoJoinRefsForFilters: true;
64
+ filtersOnRelations: true;
64
65
  propagationOnPrototype: true;
65
66
  populateAfterFlush: true;
66
67
  serialization: {
@@ -117,6 +118,8 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
117
118
  identifiedReferences: false;
118
119
  scalarTypeInDecorator: false;
119
120
  scalarPropertiesForRelations: "never";
121
+ entityDefinition: "decorators";
122
+ enumMode: "ts-enum";
120
123
  fileName: (className: string) => string;
121
124
  onlyPurePivotTables: false;
122
125
  outputPurePivotTables: false;
@@ -337,6 +340,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
337
340
  onQuery: (sql: string, params: unknown[]) => string;
338
341
  autoJoinOneToOneOwner: boolean;
339
342
  autoJoinRefsForFilters: boolean;
343
+ filtersOnRelations: boolean;
340
344
  propagationOnPrototype: boolean;
341
345
  populateAfterFlush: boolean;
342
346
  serialization: {
@@ -55,6 +55,7 @@ class Configuration {
55
55
  onQuery: sql => sql,
56
56
  autoJoinOneToOneOwner: true,
57
57
  autoJoinRefsForFilters: true,
58
+ filtersOnRelations: true,
58
59
  propagationOnPrototype: true,
59
60
  populateAfterFlush: true,
60
61
  serialization: {
@@ -111,6 +112,8 @@ class Configuration {
111
112
  identifiedReferences: false,
112
113
  scalarTypeInDecorator: false,
113
114
  scalarPropertiesForRelations: 'never',
115
+ entityDefinition: 'decorators',
116
+ enumMode: 'ts-enum',
114
117
  fileName: (className) => className,
115
118
  onlyPurePivotTables: false,
116
119
  outputPurePivotTables: false,
@@ -339,6 +342,9 @@ class Configuration {
339
342
  Object.keys(this.options.filters).forEach(key => {
340
343
  this.options.filters[key].default ??= true;
341
344
  });
345
+ if (!this.options.filtersOnRelations) {
346
+ this.options.autoJoinRefsForFilters ??= false;
347
+ }
342
348
  this.options.subscribers = Utils_1.Utils.unique(this.options.subscribers).map(subscriber => {
343
349
  return subscriber.constructor.name === 'Function' ? new subscriber() : subscriber;
344
350
  });
@@ -350,6 +356,9 @@ class Configuration {
350
356
  sync() {
351
357
  process.env.MIKRO_ORM_COLORS = '' + this.options.colors;
352
358
  this.options.tsNode = this.options.preferTs;
359
+ if (this.options.entityGenerator.entitySchema) {
360
+ this.options.entityGenerator.entityDefinition = 'entitySchema';
361
+ }
353
362
  this.logger.setDebugMode(this.options.debug);
354
363
  }
355
364
  /**
@@ -89,7 +89,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
89
89
  * export class Author { ... }
90
90
  * ```
91
91
  */
92
- export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): R;
92
+ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: readonly unknown[] | Dictionary<unknown>): NoInfer<R>;
93
93
  /**
94
94
  * Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
95
95
  *
@@ -106,7 +106,7 @@ export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> |
106
106
  */
107
107
  export declare function sql(sql: readonly string[], ...values: unknown[]): any;
108
108
  export declare namespace sql {
109
- var ref: <T extends object>(...keys: string[]) => RawQueryFragment;
109
+ var ref: <T extends object>(...keys: string[]) => NoInfer<RawQueryFragment>;
110
110
  var now: (length?: number) => string;
111
111
  var lower: <T extends object>(key: string | ((alias: string) => string)) => string;
112
112
  var upper: <T extends object>(key: string | ((alias: string) => string)) => string;
package/utils/Utils.js CHANGED
@@ -717,11 +717,11 @@ class Utils {
717
717
  return simple;
718
718
  }
719
719
  const objectType = Object.prototype.toString.call(value);
720
- const type = objectType.match(/\[object (\w+)]/)[1];
720
+ const type = objectType.match(/^\[object (.+)]$/)[1];
721
721
  if (type === 'Uint8Array') {
722
722
  return 'Buffer';
723
723
  }
724
- return ['Date', 'Buffer', 'RegExp'].includes(type) ? type : type.toLowerCase();
724
+ return type;
725
725
  }
726
726
  /**
727
727
  * Checks whether the value is POJO (e.g. `{ foo: 'bar' }`, and not instance of `Foo`)