@mikro-orm/core 7.0.0-dev.74 → 7.0.0-dev.76

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.
@@ -3,7 +3,6 @@ import { type Configuration } from './utils/Configuration.js';
3
3
  import { Cursor } from './utils/Cursor.js';
4
4
  import { EntityFactory } from './entity/EntityFactory.js';
5
5
  import { type AssignOptions } from './entity/EntityAssigner.js';
6
- import { EntityValidator } from './entity/EntityValidator.js';
7
6
  import { type EntityRepository } from './entity/EntityRepository.js';
8
7
  import { EntityLoader, type EntityLoaderOptions } from './entity/EntityLoader.js';
9
8
  import { Reference } from './entity/Reference.js';
@@ -33,7 +32,6 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
33
32
  readonly global = false;
34
33
  readonly name: string;
35
34
  private readonly loaders;
36
- private readonly validator;
37
35
  private readonly repositoryMap;
38
36
  private readonly entityLoader;
39
37
  protected readonly comparator: EntityComparator;
@@ -72,10 +70,6 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
72
70
  * Shortcut for `em.getRepository()`.
73
71
  */
74
72
  repo<Entity extends object, Repository extends EntityRepository<Entity> = EntityRepository<Entity>>(entityName: EntityName<Entity>): GetRepository<Entity, Repository>;
75
- /**
76
- * Gets EntityValidator instance
77
- */
78
- getValidator(): EntityValidator;
79
73
  /**
80
74
  * Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
81
75
  */
package/EntityManager.js CHANGED
@@ -8,7 +8,7 @@ import { TransactionContext } from './utils/TransactionContext.js';
8
8
  import { isRaw, RawQueryFragment } from './utils/RawQueryFragment.js';
9
9
  import { EntityFactory } from './entity/EntityFactory.js';
10
10
  import { EntityAssigner } from './entity/EntityAssigner.js';
11
- import { EntityValidator } from './entity/EntityValidator.js';
11
+ import { validateEmptyWhere, validateParams, validatePrimaryKey, validateProperty } from './entity/validators.js';
12
12
  import { EntityLoader } from './entity/EntityLoader.js';
13
13
  import { Reference } from './entity/Reference.js';
14
14
  import { helper } from './entity/wrap.js';
@@ -36,7 +36,6 @@ export class EntityManager {
36
36
  global = false;
37
37
  name;
38
38
  loaders = {};
39
- validator;
40
39
  repositoryMap = {};
41
40
  entityLoader;
42
41
  comparator;
@@ -61,7 +60,6 @@ export class EntityManager {
61
60
  this.eventManager = eventManager;
62
61
  this.entityLoader = new EntityLoader(this);
63
62
  this.name = this.config.get('contextName');
64
- this.validator = new EntityValidator(this.config.get('strict'));
65
63
  this.comparator = this.config.getComparator(this.metadata);
66
64
  this.resultCache = this.config.getResultCacheAdapter();
67
65
  this.disableTransactions = this.config.get('disableTransactions');
@@ -105,12 +103,6 @@ export class EntityManager {
105
103
  repo(entityName) {
106
104
  return this.getRepository(entityName);
107
105
  }
108
- /**
109
- * Gets EntityValidator instance
110
- */
111
- getValidator() {
112
- return this.validator;
113
- }
114
106
  /**
115
107
  * Finds all entities matching your `where` query. You can pass additional options via the `options` parameter.
116
108
  */
@@ -127,7 +119,7 @@ export class EntityManager {
127
119
  await em.tryFlush(entityName, options);
128
120
  entityName = Utils.className(entityName);
129
121
  where = await em.processWhere(entityName, where, options, 'read');
130
- em.validator.validateParams(where);
122
+ validateParams(where);
131
123
  options.orderBy = options.orderBy || {};
132
124
  options.populate = await em.preparePopulate(entityName, options);
133
125
  const populate = options.populate;
@@ -203,7 +195,7 @@ export class EntityManager {
203
195
  await em.tryFlush(entityName, options);
204
196
  entityName = Utils.className(entityName);
205
197
  const where = await em.processWhere(entityName, options.where ?? {}, options, 'read');
206
- em.validator.validateParams(where);
198
+ validateParams(where);
207
199
  options.orderBy = options.orderBy || {};
208
200
  options.populate = await em.preparePopulate(entityName, options);
209
201
  const meta = this.metadata.get(entityName);
@@ -625,13 +617,13 @@ export class EntityManager {
625
617
  await em.tryFlush(entityName, options);
626
618
  const meta = em.metadata.get(entityName);
627
619
  where = await em.processWhere(entityName, where, options, 'read');
628
- em.validator.validateEmptyWhere(where);
620
+ validateEmptyWhere(where);
629
621
  em.checkLockRequirements(options.lockMode, meta);
630
622
  const isOptimisticLocking = options.lockMode == null || options.lockMode === LockMode.OPTIMISTIC;
631
623
  if (entity && !em.shouldRefresh(meta, entity, options) && isOptimisticLocking) {
632
624
  return em.lockAndPopulate(meta, entity, where, options);
633
625
  }
634
- em.validator.validateParams(where);
626
+ validateParams(where);
635
627
  options.populate = await em.preparePopulate(entityName, options);
636
628
  const cacheKey = em.cacheKey(entityName, options, 'em.findOne', where);
637
629
  const cached = await em.tryCache(entityName, options.cache, cacheKey, options.refresh, true);
@@ -763,7 +755,7 @@ export class EntityManager {
763
755
  }
764
756
  where = getWhereCondition(meta, options.onConflictFields, data, where).where;
765
757
  data = QueryHelper.processObjectParams(data);
766
- em.validator.validateParams(data, 'insert data');
758
+ validateParams(data, 'insert data');
767
759
  if (em.eventManager.hasListeners(EventType.beforeUpsert, meta)) {
768
760
  await em.eventManager.dispatchEvent(EventType.beforeUpsert, { entity: data, em, meta }, meta);
769
761
  }
@@ -915,7 +907,7 @@ export class EntityManager {
915
907
  platform: this.getPlatform(),
916
908
  });
917
909
  row = QueryHelper.processObjectParams(row);
918
- em.validator.validateParams(row, 'insert data');
910
+ validateParams(row, 'insert data');
919
911
  allData.push(row);
920
912
  allWhere.push(where);
921
913
  }
@@ -1144,7 +1136,7 @@ export class EntityManager {
1144
1136
  return cs.getPrimaryKey();
1145
1137
  }
1146
1138
  data = QueryHelper.processObjectParams(data);
1147
- em.validator.validateParams(data, 'insert data');
1139
+ validateParams(data, 'insert data');
1148
1140
  const res = await em.driver.nativeInsert(entityName, data, { ctx: em.transactionContext, ...options });
1149
1141
  return res.insertId;
1150
1142
  }
@@ -1184,7 +1176,7 @@ export class EntityManager {
1184
1176
  return css.map(cs => cs.getPrimaryKey());
1185
1177
  }
1186
1178
  data = data.map(row => QueryHelper.processObjectParams(row));
1187
- data.forEach(row => em.validator.validateParams(row, 'insert data'));
1179
+ data.forEach(row => validateParams(row, 'insert data'));
1188
1180
  const res = await em.driver.nativeInsertMany(entityName, data, { ctx: em.transactionContext, ...options });
1189
1181
  if (res.insertedIds) {
1190
1182
  return res.insertedIds;
@@ -1200,8 +1192,8 @@ export class EntityManager {
1200
1192
  entityName = Utils.className(entityName);
1201
1193
  data = QueryHelper.processObjectParams(data);
1202
1194
  where = await em.processWhere(entityName, where, { ...options, convertCustomTypes: false }, 'update');
1203
- em.validator.validateParams(data, 'update data');
1204
- em.validator.validateParams(where, 'update condition');
1195
+ validateParams(data, 'update data');
1196
+ validateParams(where, 'update condition');
1205
1197
  const res = await em.driver.nativeUpdate(entityName, where, data, { ctx: em.transactionContext, ...options });
1206
1198
  return res.affectedRows;
1207
1199
  }
@@ -1213,7 +1205,7 @@ export class EntityManager {
1213
1205
  em.prepareOptions(options);
1214
1206
  entityName = Utils.className(entityName);
1215
1207
  where = await em.processWhere(entityName, where, options, 'delete');
1216
- em.validator.validateParams(where, 'delete condition');
1208
+ validateParams(where, 'delete condition');
1217
1209
  const res = await em.driver.nativeDelete(entityName, where, { ctx: em.transactionContext, ...options });
1218
1210
  return res.affectedRows;
1219
1211
  }
@@ -1224,15 +1216,17 @@ export class EntityManager {
1224
1216
  entityName = Utils.className(entityName);
1225
1217
  const meta = this.metadata.get(entityName);
1226
1218
  const data = this.driver.mapResult(result, meta);
1227
- Object.keys(data).forEach(k => {
1219
+ for (const k of Object.keys(data)) {
1228
1220
  const prop = meta.properties[k];
1229
- if (prop && prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.includes(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1230
- data[k] = this.validator.validateProperty(prop, data[k], data);
1221
+ if (prop?.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && !prop.customType && (prop.setter || !prop.getter)) {
1222
+ validateProperty(prop, data[k], data);
1231
1223
  }
1232
- });
1224
+ }
1233
1225
  return this.merge(entityName, data, {
1234
1226
  convertCustomTypes: true,
1235
- refresh: true, ...options,
1227
+ refresh: true,
1228
+ validate: false,
1229
+ ...options,
1236
1230
  });
1237
1231
  }
1238
1232
  /**
@@ -1248,15 +1242,11 @@ export class EntityManager {
1248
1242
  options.validate ??= true;
1249
1243
  options.cascade ??= true;
1250
1244
  entityName = Utils.className(entityName);
1251
- if (options.validate) {
1252
- em.validator.validatePrimaryKey(data, em.metadata.get(entityName));
1253
- }
1245
+ validatePrimaryKey(data, em.metadata.get(entityName));
1254
1246
  let entity = em.unitOfWork.tryGetById(entityName, data, options.schema, false);
1255
1247
  if (entity && helper(entity).__managed && helper(entity).__initialized && !options.refresh) {
1256
1248
  return entity;
1257
1249
  }
1258
- const meta = em.metadata.find(entityName);
1259
- const childMeta = em.metadata.getByDiscriminatorColumn(meta, data);
1260
1250
  const dataIsEntity = Utils.isEntity(data);
1261
1251
  if (options.keepIdentity && entity && dataIsEntity && entity !== data) {
1262
1252
  helper(entity).__data = helper(data).__data;
@@ -1264,9 +1254,6 @@ export class EntityManager {
1264
1254
  return entity;
1265
1255
  }
1266
1256
  entity = dataIsEntity ? data : em.entityFactory.create(entityName, data, { merge: true, ...options });
1267
- if (options.validate) {
1268
- em.validator.validate(entity, data, childMeta ?? meta);
1269
- }
1270
1257
  const visited = options.cascade ? undefined : new Set([entity]);
1271
1258
  em.unitOfWork.merge(entity, visited);
1272
1259
  return entity;
@@ -1344,7 +1331,7 @@ export class EntityManager {
1344
1331
  options._populateWhere = options.populateWhere ?? this.config.get('populateWhere');
1345
1332
  options.populateWhere = this.createPopulateWhere({ ...where }, options);
1346
1333
  options.populateFilter = await this.getJoinedFilters(meta, options);
1347
- em.validator.validateParams(where);
1334
+ validateParams(where);
1348
1335
  delete options.orderBy;
1349
1336
  const cacheKey = em.cacheKey(entityName, options, 'em.count', where);
1350
1337
  const cached = await em.tryCache(entityName, options.cache, cacheKey);
@@ -3,10 +3,9 @@ import { Collection } from './Collection.js';
3
3
  import { Utils } from '../utils/Utils.js';
4
4
  import { Reference } from './Reference.js';
5
5
  import { ReferenceKind, SCALAR_TYPES } from '../enums.js';
6
- import { EntityValidator } from './EntityValidator.js';
6
+ import { validateProperty } from './validators.js';
7
7
  import { helper, wrap } from './wrap.js';
8
8
  import { EntityHelper } from './EntityHelper.js';
9
- const validator = new EntityValidator(false);
10
9
  export class EntityAssigner {
11
10
  static assign(entity, data, options = {}) {
12
11
  let opts = options;
@@ -94,8 +93,9 @@ export class EntityAssigner {
94
93
  }
95
94
  return EntityAssigner.assignReference(entity, value, prop, options.em, options);
96
95
  }
97
- if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.includes(prop.runtimeType) && (prop.setter || !prop.getter)) {
98
- return entity[prop.name] = validator.validateProperty(prop, value, entity);
96
+ if (prop.kind === ReferenceKind.SCALAR && SCALAR_TYPES.has(prop.runtimeType) && (prop.setter || !prop.getter)) {
97
+ validateProperty(prop, value, entity);
98
+ return entity[prop.name] = value;
99
99
  }
100
100
  if (prop.kind === ReferenceKind.EMBEDDED && EntityAssigner.validateEM(options.em)) {
101
101
  return EntityAssigner.assignEmbeddable(entity, value, prop, options.em, options);
package/entity/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './EntityRepository.js';
2
2
  export * from './EntityIdentifier.js';
3
- export * from './EntityValidator.js';
4
3
  export * from './EntityAssigner.js';
5
4
  export * from './EntityHelper.js';
6
5
  export * from './EntityFactory.js';
@@ -9,6 +8,7 @@ export * from './EntityLoader.js';
9
8
  export * from './Reference.js';
10
9
  export * from './BaseEntity.js';
11
10
  export * from './WrappedEntity.js';
11
+ export * from './validators.js';
12
12
  export * from './wrap.js';
13
13
  export * from './defineEntity.js';
14
14
  export * from './utils.js';
package/entity/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export * from './EntityRepository.js';
2
2
  export * from './EntityIdentifier.js';
3
- export * from './EntityValidator.js';
4
3
  export * from './EntityAssigner.js';
5
4
  export * from './EntityHelper.js';
6
5
  export * from './EntityFactory.js';
@@ -9,6 +8,7 @@ export * from './EntityLoader.js';
9
8
  export * from './Reference.js';
10
9
  export * from './BaseEntity.js';
11
10
  export * from './WrappedEntity.js';
11
+ export * from './validators.js';
12
12
  export * from './wrap.js';
13
13
  export * from './defineEntity.js';
14
14
  export * from './utils.js';
@@ -0,0 +1,11 @@
1
+ import type { EntityData, EntityMetadata, EntityProperty, FilterQuery } from '../typings.js';
2
+ /** @internal */
3
+ export declare function validateProperty<T extends object>(prop: EntityProperty, givenValue: any, entity: T): void;
4
+ /** @internal */
5
+ export declare function validateEntity<T extends object>(entity: T, meta: EntityMetadata<T>): void;
6
+ /** @internal */
7
+ export declare function validateParams(params: any, type?: string, field?: string): void;
8
+ /** @internal */
9
+ export declare function validatePrimaryKey<T>(entity: EntityData<T>, meta: EntityMetadata<T>): void;
10
+ /** @internal */
11
+ export declare function validateEmptyWhere<T>(where: FilterQuery<T>): void;
@@ -0,0 +1,61 @@
1
+ import { Utils } from '../utils/Utils.js';
2
+ import { ValidationError } from '../errors.js';
3
+ import { isRaw } from '../utils/RawQueryFragment.js';
4
+ import { SCALAR_TYPES } from '../enums.js';
5
+ /** @internal */
6
+ export function validateProperty(prop, givenValue, entity) {
7
+ if (givenValue == null || isRaw(givenValue)) {
8
+ return;
9
+ }
10
+ const expectedType = prop.runtimeType;
11
+ const propName = prop.embedded ? prop.name.replace(/~/g, '.') : prop.name;
12
+ const givenType = Utils.getObjectType(givenValue);
13
+ if (prop.enum && prop.items) {
14
+ /* v8 ignore next 3 */
15
+ if (!prop.items.some(it => it === givenValue)) {
16
+ throw ValidationError.fromWrongPropertyType(entity, propName, expectedType, givenType, givenValue);
17
+ }
18
+ }
19
+ else {
20
+ if (givenType !== expectedType && SCALAR_TYPES.has(expectedType)) {
21
+ throw ValidationError.fromWrongPropertyType(entity, propName, expectedType, givenType, givenValue);
22
+ }
23
+ }
24
+ }
25
+ function getValue(o, prop) {
26
+ if (prop.embedded && prop.embedded[0] in o) {
27
+ return o[prop.embedded[0]]?.[prop.embedded[1]];
28
+ }
29
+ return o[prop.name];
30
+ }
31
+ /** @internal */
32
+ export function validateEntity(entity, meta) {
33
+ for (const prop of meta.validateProps) {
34
+ validateProperty(prop, getValue(entity, prop), entity);
35
+ }
36
+ }
37
+ /** @internal */
38
+ export function validateParams(params, type = 'search condition', field) {
39
+ if (Utils.isPrimaryKey(params) || Utils.isEntity(params)) {
40
+ return;
41
+ }
42
+ if (Array.isArray(params)) {
43
+ return params.forEach(item => validateParams(item, type, field));
44
+ }
45
+ if (Utils.isPlainObject(params)) {
46
+ Object.keys(params).forEach(k => validateParams(params[k], type, k));
47
+ }
48
+ }
49
+ /** @internal */
50
+ export function validatePrimaryKey(entity, meta) {
51
+ const pkExists = meta.primaryKeys.every(pk => entity[pk] != null) || (meta.serializedPrimaryKey && entity[meta.serializedPrimaryKey] != null);
52
+ if (!entity || !pkExists) {
53
+ throw ValidationError.fromMergeWithoutPK(meta);
54
+ }
55
+ }
56
+ /** @internal */
57
+ export function validateEmptyWhere(where) {
58
+ if (Utils.isEmpty(where)) {
59
+ throw new Error(`You cannot call 'EntityManager.findOne()' with empty 'where' parameter`);
60
+ }
61
+ }
package/enums.d.ts CHANGED
@@ -89,7 +89,7 @@ export declare enum QueryFlag {
89
89
  IDENTITY_INSERT = "IDENTITY_INSERT",// mssql only
90
90
  OUTPUT_TABLE = "OUTPUT_TABLE"
91
91
  }
92
- export declare const SCALAR_TYPES: string[];
92
+ export declare const SCALAR_TYPES: Set<string>;
93
93
  export declare enum ReferenceKind {
94
94
  SCALAR = "scalar",
95
95
  ONE_TO_ONE = "1:1",
package/enums.js CHANGED
@@ -99,7 +99,7 @@ export var QueryFlag;
99
99
  QueryFlag["IDENTITY_INSERT"] = "IDENTITY_INSERT";
100
100
  QueryFlag["OUTPUT_TABLE"] = "OUTPUT_TABLE";
101
101
  })(QueryFlag || (QueryFlag = {}));
102
- export const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp'];
102
+ export const SCALAR_TYPES = new Set(['string', 'number', 'boolean', 'bigint', 'Uint8Array', 'Date', 'Buffer', 'RegExp']);
103
103
  export var ReferenceKind;
104
104
  (function (ReferenceKind) {
105
105
  ReferenceKind["SCALAR"] = "scalar";
package/errors.d.ts CHANGED
@@ -8,7 +8,6 @@ export declare class ValidationError<T extends AnyEntity = AnyEntity> extends Er
8
8
  getEntity(): AnyEntity | undefined;
9
9
  static fromWrongPropertyType(entity: AnyEntity, property: string, expectedType: string, givenType: string, givenValue: string): ValidationError;
10
10
  static fromWrongRepositoryType(entityName: string, repoType: string, method: string): ValidationError;
11
- static fromCollectionNotInitialized(entity: AnyEntity, prop: EntityProperty): ValidationError;
12
11
  static fromMergeWithoutPK(meta: EntityMetadata): ValidationError;
13
12
  static transactionRequired(): ValidationError;
14
13
  static entityNotManaged(entity: AnyEntity): ValidationError;
package/errors.js CHANGED
@@ -23,11 +23,6 @@ export class ValidationError extends Error {
23
23
  const msg = `Trying to use EntityRepository.${method}() with '${entityName}' entity while the repository is of type '${repoType}'`;
24
24
  return new ValidationError(msg);
25
25
  }
26
- static fromCollectionNotInitialized(entity, prop) {
27
- const entityName = entity.constructor.name;
28
- const msg = `${entityName}.${prop.name} is not initialized, define it as '${prop.name} = new Collection<${prop.type}>(this);'`;
29
- return new ValidationError(msg);
30
- }
31
26
  static fromMergeWithoutPK(meta) {
32
27
  return new ValidationError(`You cannot merge entity '${meta.className}' without identifier!`);
33
28
  }
@@ -162,7 +162,7 @@ export class MetadataDiscovery {
162
162
  const processed = [];
163
163
  for (const entity of targets) {
164
164
  if (typeof entity === 'string') {
165
- const { discoverEntities } = await import('@mikro-orm/core/file-discovery' + '');
165
+ const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
166
166
  processed.push(...await discoverEntities(entity, { baseDir }));
167
167
  }
168
168
  else {
@@ -1,4 +1,4 @@
1
- import { EntityMetadata, type Dictionary, type EntityData, type EntityName } from '../typings.js';
1
+ import { EntityMetadata, type Dictionary, type EntityName } from '../typings.js';
2
2
  import type { EntityManager } from '../EntityManager.js';
3
3
  export declare class MetadataStorage {
4
4
  static readonly PATH_SYMBOL: unique symbol;
@@ -10,7 +10,6 @@ export declare class MetadataStorage {
10
10
  static isKnownEntity(name: string): boolean;
11
11
  static clear(): void;
12
12
  getAll(): Dictionary<EntityMetadata>;
13
- getByDiscriminatorColumn<T>(meta: EntityMetadata<T>, data: EntityData<T>): EntityMetadata<T> | undefined;
14
13
  get<T = any>(entityName: EntityName<T>, init?: boolean, validate?: boolean): EntityMetadata<T>;
15
14
  find<T = any>(entityName: EntityName<T>): EntityMetadata<T> | undefined;
16
15
  has(entity: string): boolean;
@@ -33,14 +33,6 @@ export class MetadataStorage {
33
33
  getAll() {
34
34
  return this.metadata;
35
35
  }
36
- getByDiscriminatorColumn(meta, data) {
37
- const value = data[meta.root.discriminatorColumn];
38
- if (!value) {
39
- return undefined;
40
- }
41
- const type = meta.root.discriminatorMap[value];
42
- return this.metadata[type];
43
- }
44
36
  get(entityName, init = false, validate = true) {
45
37
  entityName = Utils.className(entityName);
46
38
  if (validate && !init && !this.has(entityName)) {
@@ -0,0 +1 @@
1
+ export declare function discoverEntities(): void;
@@ -0,0 +1,3 @@
1
+ export function discoverEntities() {
2
+ throw new Error('File discovery is not supported in this environment.');
3
+ }
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.74",
4
+ "version": "7.0.0-dev.76",
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",
8
8
  ".": "./index.js",
9
- "./file-discovery": "./metadata/discover-entities.js"
9
+ "./file-discovery": {
10
+ "node": "./metadata/discover-entities.js",
11
+ "browser": "./not-supported.js"
12
+ }
10
13
  },
11
14
  "repository": {
12
15
  "type": "git",
package/typings.d.ts CHANGED
@@ -472,6 +472,7 @@ export interface EntityMetadata<T = any> {
472
472
  comparableProps: EntityProperty<T>[];
473
473
  trackingProps: EntityProperty<T>[];
474
474
  hydrateProps: EntityProperty<T>[];
475
+ validateProps: EntityProperty<T>[];
475
476
  uniqueProps: EntityProperty<T>[];
476
477
  getterProps: EntityProperty<T>[];
477
478
  indexes: {
package/typings.js CHANGED
@@ -83,6 +83,12 @@ export class EntityMetadata {
83
83
  this.uniqueProps = this.props.filter(prop => prop.unique);
84
84
  this.getterProps = this.props.filter(prop => prop.getter);
85
85
  this.comparableProps = this.props.filter(prop => EntityComparator.isComparable(prop, this));
86
+ this.validateProps = this.props.filter(prop => {
87
+ if (prop.inherited || (prop.persist === false && prop.userDefined !== false)) {
88
+ return false;
89
+ }
90
+ return prop.kind === ReferenceKind.SCALAR && ['string', 'number', 'boolean', 'Date'].includes(prop.type);
91
+ });
86
92
  this.hydrateProps = this.props.filter(prop => {
87
93
  // `prop.userDefined` is either `undefined` or `false`
88
94
  const discriminator = this.root.discriminatorColumn === prop.name && prop.userDefined === false;
@@ -2,19 +2,17 @@ import { type Configuration } from '../utils/Configuration.js';
2
2
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
3
3
  import type { AnyEntity } from '../typings.js';
4
4
  import { ChangeSet } from './ChangeSet.js';
5
- import { type EntityValidator } from '../entity/EntityValidator.js';
6
5
  import { type Collection } from '../entity/Collection.js';
7
6
  import type { Platform } from '../platforms/Platform.js';
8
7
  import type { EntityManager } from '../EntityManager.js';
9
8
  export declare class ChangeSetComputer {
10
- private readonly validator;
11
9
  private readonly collectionUpdates;
12
10
  private readonly metadata;
13
11
  private readonly platform;
14
12
  private readonly config;
15
13
  private readonly em;
16
14
  private readonly comparator;
17
- constructor(validator: EntityValidator, collectionUpdates: Set<Collection<AnyEntity>>, metadata: MetadataStorage, platform: Platform, config: Configuration, em: EntityManager);
15
+ constructor(collectionUpdates: Set<Collection<AnyEntity>>, metadata: MetadataStorage, platform: Platform, config: Configuration, em: EntityManager);
18
16
  computeChangeSet<T extends object>(entity: T): ChangeSet<T> | null;
19
17
  /**
20
18
  * Traverses entity graph and executes `onCreate` and `onUpdate` methods, assigning the values to given properties.
@@ -1,17 +1,16 @@
1
1
  import { Utils } from '../utils/Utils.js';
2
2
  import { ChangeSet, ChangeSetType } from './ChangeSet.js';
3
3
  import { helper } from '../entity/wrap.js';
4
+ import { validateEntity } from '../entity/validators.js';
4
5
  import { ReferenceKind } from '../enums.js';
5
6
  export class ChangeSetComputer {
6
- validator;
7
7
  collectionUpdates;
8
8
  metadata;
9
9
  platform;
10
10
  config;
11
11
  em;
12
12
  comparator;
13
- constructor(validator, collectionUpdates, metadata, platform, config, em) {
14
- this.validator = validator;
13
+ constructor(collectionUpdates, metadata, platform, config, em) {
15
14
  this.collectionUpdates = collectionUpdates;
16
15
  this.metadata = metadata;
17
16
  this.platform = platform;
@@ -42,15 +41,13 @@ export class ChangeSetComputer {
42
41
  }
43
42
  const changeSet = new ChangeSet(entity, type, this.computePayload(entity), meta);
44
43
  changeSet.originalEntity = wrapped.__originalEntityData;
45
- if (this.config.get('validate')) {
46
- this.validator.validate(changeSet.entity, changeSet.payload, meta);
47
- }
48
44
  for (const prop of meta.relations.filter(prop => prop.persist !== false || prop.userDefined === false)) {
49
45
  this.processProperty(changeSet, prop);
50
46
  }
51
47
  if (changeSet.type === ChangeSetType.UPDATE && !Utils.hasObjectKeys(changeSet.payload)) {
52
48
  return null;
53
49
  }
50
+ validateEntity(changeSet.entity, meta);
54
51
  // Execute `onCreate` and `onUpdate` on properties recursively, saves `onUpdate` results
55
52
  // to the `map` as we want to apply those only if something else changed.
56
53
  if (type === ChangeSetType.UPDATE) {
@@ -1,7 +1,6 @@
1
1
  import type { MetadataStorage } from '../metadata/MetadataStorage.js';
2
2
  import type { Dictionary, EntityDictionary, EntityMetadata, IHydrator } from '../typings.js';
3
3
  import { type EntityFactory } from '../entity/EntityFactory.js';
4
- import { type EntityValidator } from '../entity/EntityValidator.js';
5
4
  import { type ChangeSet } from './ChangeSet.js';
6
5
  import { type Configuration } from '../utils/Configuration.js';
7
6
  import type { DriverMethodOptions, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
@@ -11,17 +10,17 @@ export declare class ChangeSetPersister {
11
10
  private readonly metadata;
12
11
  private readonly hydrator;
13
12
  private readonly factory;
14
- private readonly validator;
15
13
  private readonly config;
16
14
  private readonly em;
17
15
  private readonly platform;
18
16
  private readonly comparator;
19
17
  private readonly usesReturningStatement;
20
- constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, validator: EntityValidator, config: Configuration, em: EntityManager);
18
+ constructor(driver: IDatabaseDriver, metadata: MetadataStorage, hydrator: IHydrator, factory: EntityFactory, config: Configuration, em: EntityManager);
21
19
  executeInserts<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
22
20
  executeUpdates<T extends object>(changeSets: ChangeSet<T>[], batched: boolean, options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
23
21
  executeDeletes<T extends object>(changeSets: ChangeSet<T>[], options?: DriverMethodOptions, withSchema?: boolean): Promise<void>;
24
22
  private runForEachSchema;
23
+ private validateRequired;
25
24
  private processProperties;
26
25
  private persistNewEntity;
27
26
  private persistNewEntities;
@@ -3,25 +3,23 @@ import { helper } from '../entity/wrap.js';
3
3
  import { ChangeSetType } from './ChangeSet.js';
4
4
  import { isRaw } from '../utils/RawQueryFragment.js';
5
5
  import { Utils } from '../utils/Utils.js';
6
- import { OptimisticLockError } from '../errors.js';
6
+ import { OptimisticLockError, ValidationError } from '../errors.js';
7
7
  import { ReferenceKind } from '../enums.js';
8
8
  export class ChangeSetPersister {
9
9
  driver;
10
10
  metadata;
11
11
  hydrator;
12
12
  factory;
13
- validator;
14
13
  config;
15
14
  em;
16
15
  platform;
17
16
  comparator;
18
17
  usesReturningStatement;
19
- constructor(driver, metadata, hydrator, factory, validator, config, em) {
18
+ constructor(driver, metadata, hydrator, factory, config, em) {
20
19
  this.driver = driver;
21
20
  this.metadata = metadata;
22
21
  this.hydrator = hydrator;
23
22
  this.factory = factory;
24
- this.validator = validator;
25
23
  this.config = config;
26
24
  this.em = em;
27
25
  this.platform = this.driver.getPlatform();
@@ -81,13 +79,32 @@ export class ChangeSetPersister {
81
79
  await this[method](group, ...args, options, true);
82
80
  }
83
81
  }
82
+ validateRequired(entity) {
83
+ const wrapped = helper(entity);
84
+ for (const prop of wrapped.__meta.props) {
85
+ if (!prop.nullable &&
86
+ !prop.autoincrement &&
87
+ !prop.default &&
88
+ !prop.defaultRaw &&
89
+ !prop.onCreate &&
90
+ !prop.generated &&
91
+ !prop.embedded &&
92
+ ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
93
+ prop.name !== wrapped.__meta.root.discriminatorColumn &&
94
+ prop.type !== 'ObjectId' &&
95
+ prop.persist !== false &&
96
+ entity[prop.name] == null) {
97
+ throw ValidationError.propertyRequired(entity, prop);
98
+ }
99
+ }
100
+ }
84
101
  processProperties(changeSet) {
85
102
  const meta = this.metadata.find(changeSet.name);
86
103
  for (const prop of meta.relations) {
87
104
  this.processProperty(changeSet, prop);
88
105
  }
89
106
  if (changeSet.type === ChangeSetType.CREATE && this.config.get('validateRequired')) {
90
- this.validator.validateRequired(changeSet.entity);
107
+ this.validateRequired(changeSet.entity);
91
108
  }
92
109
  }
93
110
  async persistNewEntity(meta, changeSet, options) {
@@ -42,8 +42,8 @@ export class UnitOfWork {
42
42
  this.identityMap = new IdentityMap(this.platform.getDefaultSchemaName());
43
43
  this.eventManager = this.em.getEventManager();
44
44
  this.comparator = this.em.getComparator();
45
- this.changeSetComputer = new ChangeSetComputer(this.em.getValidator(), this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
46
- this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.getValidator(), this.em.config, this.em);
45
+ this.changeSetComputer = new ChangeSetComputer(this.collectionUpdates, this.metadata, this.platform, this.em.config, this.em);
46
+ this.changeSetPersister = new ChangeSetPersister(this.em.getDriver(), this.metadata, this.em.config.getHydrator(this.metadata), this.em.getEntityFactory(), this.em.config, this.em);
47
47
  }
48
48
  merge(entity, visited) {
49
49
  const wrapped = helper(entity);
@@ -738,20 +738,6 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
738
738
  * @see https://mikro-orm.io/docs/read-connections
739
739
  */
740
740
  replicas?: ConnectionOptions[];
741
- /**
742
- * Enable strict mode which disables automatic data type conversion.
743
- * In strict mode, the ORM throws errors instead of fixing wrong data types.
744
- * @default false
745
- * @see https://mikro-orm.io/docs/property-validation
746
- */
747
- strict?: boolean;
748
- /**
749
- * Enable runtime property validation before persisting.
750
- * Has performance implications and is usually not needed.
751
- * @default false
752
- * @see https://mikro-orm.io/docs/property-validation
753
- */
754
- validate?: boolean;
755
741
  /**
756
742
  * Validate that required properties are set on new entities before insert.
757
743
  * @default true
package/utils/clone.js CHANGED
@@ -3,7 +3,6 @@
3
3
  * clone `EventEmitter`s to get around https://github.com/mikro-orm/mikro-orm/issues/2748
4
4
  * @internal
5
5
  */
6
- import { EventEmitter } from 'node:events';
7
6
  import { RawQueryFragment } from './RawQueryFragment.js';
8
7
  /**
9
8
  * Get the property descriptor of a property on an object or its prototype chain.
@@ -34,7 +33,7 @@ export function clone(parent, respectCustomCloneMethod = true) {
34
33
  if (raw && respectCustomCloneMethod) {
35
34
  return raw.clone();
36
35
  }
37
- if (typeof parent !== 'object' || parent instanceof EventEmitter) {
36
+ if (typeof parent !== 'object') {
38
37
  return parent;
39
38
  }
40
39
  if (respectCustomCloneMethod && 'clone' in parent && typeof parent.clone === 'function') {
@@ -1,19 +0,0 @@
1
- import type { EntityData, EntityMetadata, EntityProperty, FilterQuery } from '../typings.js';
2
- export declare class EntityValidator {
3
- private strict;
4
- KNOWN_TYPES: Set<string>;
5
- constructor(strict: boolean);
6
- validate<T extends object>(entity: T, payload: any, meta: EntityMetadata<T>): void;
7
- validateRequired<T extends object>(entity: T): void;
8
- validateProperty<T extends object>(prop: EntityProperty, givenValue: any, entity: T): any;
9
- validateParams(params: any, type?: string, field?: string): void;
10
- validatePrimaryKey<T>(entity: EntityData<T>, meta: EntityMetadata<T>): void;
11
- validateEmptyWhere<T>(where: FilterQuery<T>): void;
12
- private getValue;
13
- private setValue;
14
- private validateCollection;
15
- private fixTypes;
16
- private fixDateType;
17
- private fixNumberType;
18
- private fixBooleanType;
19
- }
@@ -1,150 +0,0 @@
1
- import { ReferenceKind } from '../enums.js';
2
- import { Utils } from '../utils/Utils.js';
3
- import { ValidationError } from '../errors.js';
4
- import { helper } from './wrap.js';
5
- import { RawQueryFragment } from '../utils/RawQueryFragment.js';
6
- export class EntityValidator {
7
- strict;
8
- KNOWN_TYPES = new Set(['string', 'number', 'boolean', 'bigint', 'Uint8Array', 'Date', 'Buffer', 'RegExp']);
9
- constructor(strict) {
10
- this.strict = strict;
11
- }
12
- validate(entity, payload, meta) {
13
- meta.props.forEach(prop => {
14
- if (prop.inherited || (prop.persist === false && prop.userDefined !== false)) {
15
- return;
16
- }
17
- if ([ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind)) {
18
- this.validateCollection(entity, prop);
19
- }
20
- const SCALAR_TYPES = ['string', 'number', 'boolean', 'Date'];
21
- if (prop.kind !== ReferenceKind.SCALAR || !SCALAR_TYPES.includes(prop.type)) {
22
- return;
23
- }
24
- const newValue = this.validateProperty(prop, this.getValue(payload, prop), entity);
25
- if (this.getValue(payload, prop) === newValue) {
26
- return;
27
- }
28
- this.setValue(payload, prop, newValue);
29
- if (entity[prop.name]) {
30
- entity[prop.name] = payload[prop.name];
31
- }
32
- });
33
- }
34
- validateRequired(entity) {
35
- const wrapped = helper(entity);
36
- for (const prop of wrapped.__meta.props) {
37
- if (!prop.nullable &&
38
- !prop.autoincrement &&
39
- !prop.default &&
40
- !prop.defaultRaw &&
41
- !prop.onCreate &&
42
- !prop.generated &&
43
- !prop.embedded &&
44
- ![ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(prop.kind) &&
45
- prop.name !== wrapped.__meta.root.discriminatorColumn &&
46
- prop.type !== 'ObjectId' &&
47
- prop.persist !== false &&
48
- entity[prop.name] == null) {
49
- throw ValidationError.propertyRequired(entity, prop);
50
- }
51
- }
52
- }
53
- validateProperty(prop, givenValue, entity) {
54
- if (givenValue == null || givenValue instanceof RawQueryFragment) {
55
- return givenValue;
56
- }
57
- const expectedType = prop.runtimeType;
58
- const propName = prop.embedded ? prop.name.replace(/~/g, '.') : prop.name;
59
- let givenType = Utils.getObjectType(givenValue);
60
- let ret = givenValue;
61
- if (!this.strict) {
62
- ret = this.fixTypes(expectedType, givenType, givenValue);
63
- givenType = Utils.getObjectType(ret);
64
- }
65
- if (prop.enum && prop.items) {
66
- /* v8 ignore next 3 */
67
- if (!prop.items.some(it => it === givenValue)) {
68
- throw ValidationError.fromWrongPropertyType(entity, propName, expectedType, givenType, givenValue);
69
- }
70
- }
71
- else {
72
- if (givenType !== expectedType && this.KNOWN_TYPES.has(expectedType)) {
73
- throw ValidationError.fromWrongPropertyType(entity, propName, expectedType, givenType, givenValue);
74
- }
75
- }
76
- return ret;
77
- }
78
- validateParams(params, type = 'search condition', field) {
79
- if (Utils.isPrimaryKey(params) || Utils.isEntity(params)) {
80
- return;
81
- }
82
- if (Array.isArray(params)) {
83
- return params.forEach(item => this.validateParams(item, type, field));
84
- }
85
- if (Utils.isPlainObject(params)) {
86
- Object.keys(params).forEach(k => {
87
- this.validateParams(params[k], type, k);
88
- });
89
- }
90
- }
91
- validatePrimaryKey(entity, meta) {
92
- const pkExists = meta.primaryKeys.every(pk => entity[pk] != null) || (meta.serializedPrimaryKey && entity[meta.serializedPrimaryKey] != null);
93
- if (!entity || !pkExists) {
94
- throw ValidationError.fromMergeWithoutPK(meta);
95
- }
96
- }
97
- validateEmptyWhere(where) {
98
- if (Utils.isEmpty(where)) {
99
- throw new Error(`You cannot call 'EntityManager.findOne()' with empty 'where' parameter`);
100
- }
101
- }
102
- getValue(o, prop) {
103
- if (prop.embedded && prop.embedded[0] in o) {
104
- return o[prop.embedded[0]]?.[prop.embedded[1]];
105
- }
106
- return o[prop.name];
107
- }
108
- setValue(o, prop, v) {
109
- /* v8 ignore next 3 */
110
- if (prop.embedded && prop.embedded[0] in o) {
111
- return o[prop.embedded[0]][prop.embedded[1]] = v;
112
- }
113
- o[prop.name] = v;
114
- }
115
- validateCollection(entity, prop) {
116
- if (prop.hydrate !== false && helper(entity).__initialized && !entity[prop.name]) {
117
- throw ValidationError.fromCollectionNotInitialized(entity, prop);
118
- }
119
- }
120
- fixTypes(expectedType, givenType, givenValue) {
121
- if (expectedType === 'Date' && ['string', 'number'].includes(givenType)) {
122
- givenValue = this.fixDateType(givenValue);
123
- }
124
- if (expectedType === 'number' && givenType === 'string') {
125
- givenValue = this.fixNumberType(givenValue);
126
- }
127
- if (expectedType === 'boolean' && givenType === 'number') {
128
- givenValue = this.fixBooleanType(givenValue);
129
- }
130
- return givenValue;
131
- }
132
- fixDateType(givenValue) {
133
- let date;
134
- if (typeof givenValue === 'string' && givenValue.match(/^-?\d+(\.\d+)?$/)) {
135
- date = new Date(+givenValue);
136
- }
137
- else {
138
- date = new Date(givenValue);
139
- }
140
- return date.toString() !== 'Invalid Date' ? date : givenValue;
141
- }
142
- fixNumberType(givenValue) {
143
- const num = +givenValue;
144
- return '' + num === givenValue ? num : givenValue;
145
- }
146
- fixBooleanType(givenValue) {
147
- const bool = !!givenValue;
148
- return +bool === givenValue ? bool : givenValue;
149
- }
150
- }