@mikro-orm/core 6.2.10-dev.99 → 6.3.1-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/EntityManager.js CHANGED
@@ -1218,6 +1218,7 @@ class EntityManager {
1218
1218
  ...options,
1219
1219
  };
1220
1220
  entityName = utils_1.Utils.className(entityName);
1221
+ await em.tryFlush(entityName, options);
1221
1222
  where = await em.processWhere(entityName, where, options, 'read');
1222
1223
  options.populate = await em.preparePopulate(entityName, options);
1223
1224
  em.validator.validateParams(where);
package/MikroORM.js CHANGED
@@ -43,7 +43,7 @@ class MikroORM {
43
43
  orm.config.set('allowGlobalContext', true);
44
44
  await orm.discoverEntities();
45
45
  orm.config.set('allowGlobalContext', allowGlobalContext);
46
- orm.driver.getPlatform().lookupExtensions(orm);
46
+ orm.driver.getPlatform().init(orm);
47
47
  if (orm.config.get('connect')) {
48
48
  await orm.connect();
49
49
  }
@@ -79,7 +79,7 @@ class MikroORM {
79
79
  orm.config.set('allowGlobalContext', true);
80
80
  orm.discoverEntitiesSync();
81
81
  orm.config.set('allowGlobalContext', allowGlobalContext);
82
- orm.driver.getPlatform().lookupExtensions(orm);
82
+ orm.driver.getPlatform().init(orm);
83
83
  for (const extension of orm.config.get('extensions')) {
84
84
  extension.register(orm);
85
85
  }
@@ -157,6 +157,7 @@ export interface CountOptions<T extends object, P extends string = never> {
157
157
  populate?: Populate<T, P>;
158
158
  ctx?: Transaction;
159
159
  connectionType?: ConnectionType;
160
+ flushMode?: FlushMode | `${FlushMode}`;
160
161
  /** sql only */
161
162
  indexHint?: string;
162
163
  /** sql only */
@@ -226,12 +226,27 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
226
226
  ret.push(` };`);
227
227
  }
228
228
  if (prop.targetMeta?.polymorphs) {
229
- prop.targetMeta.polymorphs.forEach(meta => {
229
+ prop.targetMeta.polymorphs.forEach(childMeta => {
230
230
  const childProp = prop.embeddedProps[prop.targetMeta.discriminatorColumn];
231
231
  const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
232
232
  // weak comparison as we can have numbers that might have been converted to strings due to being object keys
233
- ret.push(` if (data${childDataKey} == '${meta.discriminatorValue}' && entity${entityKey} == null) {`);
234
- ret.push(` entity${entityKey} = factory.createEmbeddable('${meta.className}', embeddedData, { newEntity, convertCustomTypes });`);
233
+ ret.push(` if (data${childDataKey} == '${childMeta.discriminatorValue}') {`);
234
+ ret.push(` if (entity${entityKey} == null) {`);
235
+ ret.push(` entity${entityKey} = factory.createEmbeddable('${childMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
236
+ ret.push(` }`);
237
+ meta.props
238
+ .filter(p => p.embedded?.[0] === prop.name)
239
+ .forEach(childProp => {
240
+ const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
241
+ const prop2 = childMeta.properties[childProp.embedded[1]];
242
+ const prop3 = {
243
+ ...prop2,
244
+ name: childProp.name,
245
+ embedded: childProp.embedded,
246
+ };
247
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
248
+ ret.push(...hydrateProperty(prop3, childProp.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
249
+ });
235
250
  ret.push(` }`);
236
251
  });
237
252
  }
@@ -239,14 +254,14 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
239
254
  ret.push(` if (entity${entityKey} == null) {`);
240
255
  ret.push(` entity${entityKey} = factory.createEmbeddable('${prop.targetMeta.className}', embeddedData, { newEntity, convertCustomTypes });`);
241
256
  ret.push(` }`);
257
+ meta.props
258
+ .filter(p => p.embedded?.[0] === prop.name)
259
+ .forEach(childProp => {
260
+ const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
261
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
262
+ ret.push(...hydrateProperty(childProp, prop.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
263
+ });
242
264
  }
243
- meta.props
244
- .filter(p => p.embedded?.[0] === prop.name)
245
- .forEach(childProp => {
246
- const childDataKey = prop.object ? dataKey + this.wrap(childProp.embedded[1]) : this.wrap(childProp.name);
247
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
248
- ret.push(...hydrateProperty(childProp, prop.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
249
- });
250
265
  /* istanbul ignore next */
251
266
  const nullVal = this.config.get('forceUndefined') ? 'undefined' : 'null';
252
267
  if (prop.object) {
@@ -316,19 +331,14 @@ class ObjectHydrator extends Hydrator_1.Hydrator {
316
331
  const meta = this.metadata.get(prop.type);
317
332
  const lines = [];
318
333
  lines.push(` const createCollectionItem_${this.safeKey(prop.name)} = (value, entity) => {`);
319
- const prop2 = prop.targetMeta?.properties[prop.mappedBy];
320
- if (prop2?.primary) {
334
+ const prop2 = prop.targetMeta.properties[prop.mappedBy];
335
+ if (prop.kind === enums_1.ReferenceKind.ONE_TO_MANY && prop2.primary) {
321
336
  lines.push(` if (typeof value === 'object' && value?.['${prop2.name}'] == null) {`);
322
337
  lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
323
338
  lines.push(` }`);
324
339
  }
325
340
  lines.push(` if (isPrimaryKey(value, ${meta.compositePK})) return factory.createReference('${prop.type}', value, { convertCustomTypes, schema, merge: true });`);
326
341
  lines.push(` if (value && value.__entity) return value;`);
327
- if (prop2 && !prop2.primary) {
328
- lines.push(` if (typeof value === 'object' && value?.['${prop2.name}'] == null) {`);
329
- lines.push(` value = { ...value, ['${prop2.name}']: Reference.wrapReference(entity, { ref: ${prop2.ref} }) };`);
330
- lines.push(` }`);
331
- }
332
342
  lines.push(` return factory.create('${prop.type}', value, { newEntity, convertCustomTypes, schema, merge: true });`);
333
343
  lines.push(` }`);
334
344
  return lines;
@@ -8,6 +8,11 @@ type TypeDef<Target> = {
8
8
  } | {
9
9
  entity: string | (() => string | EntityName<Target>);
10
10
  };
11
+ type EmbeddedTypeDef<Target> = {
12
+ type: TypeType;
13
+ } | {
14
+ entity: string | (() => string | EntityName<Target> | EntityName<Target>[]);
15
+ };
11
16
  export type EntitySchemaProperty<Target, Owner> = ({
12
17
  kind: ReferenceKind.MANY_TO_ONE | 'm:1';
13
18
  } & TypeDef<Target> & ManyToOneOptions<Owner, Target>) | ({
@@ -18,7 +23,7 @@ export type EntitySchemaProperty<Target, Owner> = ({
18
23
  kind: ReferenceKind.MANY_TO_MANY | 'm:n';
19
24
  } & TypeDef<Target> & ManyToManyOptions<Owner, Target>) | ({
20
25
  kind: ReferenceKind.EMBEDDED | 'embedded';
21
- } & TypeDef<Target> & EmbeddedOptions & PropertyOptions<Owner>) | ({
26
+ } & EmbeddedTypeDef<Target> & EmbeddedOptions & PropertyOptions<Owner>) | ({
22
27
  enum: true;
23
28
  } & EnumOptions<Owner>) | (TypeDef<Target> & PropertyOptions<Owner>);
24
29
  type OmitBaseProps<Entity, Base> = IsNever<Base> extends true ? Entity : Omit<Entity, keyof Base>;
@@ -721,6 +721,15 @@ class MetadataDiscovery {
721
721
  if (!embeddable && polymorphs.length > 0) {
722
722
  const properties = {};
723
723
  let discriminatorColumn;
724
+ const inlineProperties = (meta) => {
725
+ Object.values(meta.properties).forEach(prop => {
726
+ if (properties[prop.name] && properties[prop.name].type !== prop.type) {
727
+ properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
728
+ return properties[prop.name];
729
+ }
730
+ return properties[prop.name] = prop;
731
+ });
732
+ };
724
733
  const processExtensions = (meta) => {
725
734
  const parent = this.discovered.find(m => {
726
735
  return meta.extends && Utils_1.Utils.className(meta.extends) === m.className;
@@ -729,11 +738,11 @@ class MetadataDiscovery {
729
738
  return;
730
739
  }
731
740
  discriminatorColumn ??= parent.discriminatorColumn;
732
- Object.values(parent.properties).forEach(prop => properties[prop.name] = prop);
741
+ inlineProperties(parent);
733
742
  processExtensions(parent);
734
743
  };
735
744
  polymorphs.forEach(meta => {
736
- Object.values(meta.properties).forEach(prop => properties[prop.name] = prop);
745
+ inlineProperties(meta);
737
746
  processExtensions(meta);
738
747
  });
739
748
  const name = polymorphs.map(t => t.className).sort().join(' | ');
@@ -873,13 +882,21 @@ class MetadataDiscovery {
873
882
  if (meta.root === meta) {
874
883
  return;
875
884
  }
885
+ let i = 1;
876
886
  Object.values(meta.properties).forEach(prop => {
877
- const exists = meta.root.properties[prop.name];
887
+ if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
888
+ const name = prop.name;
889
+ this.initFieldName(prop, prop.object);
890
+ prop.name = name + '_' + (i++);
891
+ meta.root.addProperty(prop);
892
+ prop.nullable = true;
893
+ prop.name = name;
894
+ prop.hydrate = false;
895
+ return;
896
+ }
878
897
  prop = Utils_1.Utils.copy(prop, false);
879
898
  prop.nullable = true;
880
- if (!exists) {
881
- prop.inherited = true;
882
- }
899
+ prop.inherited = true;
883
900
  meta.root.addProperty(prop);
884
901
  });
885
902
  meta.collection = meta.root.collection;
@@ -964,7 +981,7 @@ class MetadataDiscovery {
964
981
  const entity1 = new meta.class();
965
982
  const entity2 = new meta.class();
966
983
  // we compare the two values by reference, this will discard things like `new Date()` or `Date.now()`
967
- if (this.config.get('discovery').inferDefaultValues && prop.default === undefined && entity1[prop.name] != null && entity1[prop.name] === entity2[prop.name] && entity1[prop.name] !== now) {
984
+ if (!meta.embeddable && this.config.get('discovery').inferDefaultValues && prop.default === undefined && entity1[prop.name] != null && entity1[prop.name] === entity2[prop.name] && entity1[prop.name] !== now) {
968
985
  prop.default ??= entity1[prop.name];
969
986
  }
970
987
  // if the default value is null, infer nullability
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.2.10-dev.99",
3
+ "version": "6.3.1-dev.0",
4
4
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
5
5
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -64,7 +64,7 @@
64
64
  "esprima": "4.0.1",
65
65
  "fs-extra": "11.2.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.2.10-dev.99",
67
+ "mikro-orm": "6.3.1-dev.0",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
@@ -160,6 +160,8 @@ export declare abstract class Platform {
160
160
  * Allows registering extensions of the driver automatically (e.g. `SchemaGenerator` extension in SQL drivers).
161
161
  */
162
162
  lookupExtensions(orm: MikroORM): void;
163
+ /** @internal */
164
+ init(orm: MikroORM): void;
163
165
  getExtension<T>(extensionName: string, extensionKey: string, moduleName: string, em: EntityManager): T;
164
166
  getSchemaGenerator(driver: IDatabaseDriver, em?: EntityManager): ISchemaGenerator;
165
167
  processDateProperty(value: unknown): string | number | Date;
@@ -320,6 +320,10 @@ class Platform {
320
320
  lookupExtensions(orm) {
321
321
  // no extensions by default
322
322
  }
323
+ /** @internal */
324
+ init(orm) {
325
+ this.lookupExtensions(orm);
326
+ }
323
327
  getExtension(extensionName, extensionKey, moduleName, em) {
324
328
  const extension = this.config.getExtension(extensionKey);
325
329
  if (extension) {
@@ -7,7 +7,6 @@ const Utils_1 = require("../utils/Utils");
7
7
  const enums_1 = require("../enums");
8
8
  const Reference_1 = require("../entity/Reference");
9
9
  const SerializationContext_1 = require("./SerializationContext");
10
- const RawQueryFragment_1 = require("../utils/RawQueryFragment");
11
10
  function isVisible(meta, propName, options) {
12
11
  const prop = meta.properties[propName];
13
12
  if (options.groups && prop?.groups) {
@@ -51,12 +50,13 @@ class EntitySerializer {
51
50
  if (!visited) {
52
51
  root.visited.add(entity);
53
52
  }
54
- [...keys]
55
- .filter(prop => isVisible(meta, prop, options))
56
- .map(prop => {
53
+ for (const prop of keys) {
54
+ if (!isVisible(meta, prop, options)) {
55
+ continue;
56
+ }
57
57
  const cycle = root.visit(meta.className, prop);
58
58
  if (cycle && visited) {
59
- return [prop, undefined];
59
+ continue;
60
60
  }
61
61
  const val = this.processProperty(prop, entity, options);
62
62
  if (!cycle) {
@@ -65,29 +65,36 @@ class EntitySerializer {
65
65
  if (options.skipNull && Utils_1.Utils.isPlainObject(val)) {
66
66
  Utils_1.Utils.dropUndefinedProperties(val, null);
67
67
  }
68
- if (val instanceof RawQueryFragment_1.RawQueryFragment) {
68
+ if (Utils_1.Utils.isRawSql(val)) {
69
69
  throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
70
70
  }
71
- return [prop, val];
72
- })
73
- .filter(([, value]) => typeof value !== 'undefined' && !(value === null && options.skipNull))
74
- .forEach(([prop, value]) => ret[this.propertyName(meta, prop, wrapped.__platform)] = value);
71
+ const visible = typeof val !== 'undefined' && !(val === null && options.skipNull);
72
+ if (visible) {
73
+ ret[this.propertyName(meta, prop, wrapped.__platform)] = val;
74
+ }
75
+ }
75
76
  if (contextCreated) {
76
77
  root.close();
77
78
  }
78
79
  if (!wrapped.isInitialized()) {
79
80
  return ret;
80
81
  }
81
- // decorated getters
82
- meta.props
83
- .filter(prop => prop.getter && prop.getterName === undefined && typeof entity[prop.name] !== 'undefined' && isVisible(meta, prop.name, options))
84
- // @ts-ignore
85
- .forEach(prop => ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.name, entity, options));
86
- // decorated get methods
87
- meta.props
88
- .filter(prop => prop.getterName && entity[prop.getterName] instanceof Function && isVisible(meta, prop.name, options))
89
- // @ts-ignore
90
- .forEach(prop => ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.getterName, entity, options));
82
+ for (const prop of meta.getterProps) {
83
+ // decorated get methods
84
+ if (prop.getterName != null) {
85
+ const visible = entity[prop.getterName] instanceof Function && isVisible(meta, prop.name, options);
86
+ if (visible) {
87
+ ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.getterName, entity, options);
88
+ }
89
+ }
90
+ else {
91
+ // decorated getters
92
+ const visible = typeof entity[prop.name] !== 'undefined' && isVisible(meta, prop.name, options);
93
+ if (visible) {
94
+ ret[this.propertyName(meta, prop.name, wrapped.__platform)] = this.processProperty(prop.name, entity, options);
95
+ }
96
+ }
97
+ }
91
98
  return ret;
92
99
  }
93
100
  static propertyName(meta, prop, platform) {
@@ -5,7 +5,6 @@ const wrap_1 = require("../entity/wrap");
5
5
  const Utils_1 = require("../utils/Utils");
6
6
  const enums_1 = require("../enums");
7
7
  const SerializationContext_1 = require("./SerializationContext");
8
- const RawQueryFragment_1 = require("../utils/RawQueryFragment");
9
8
  function isVisible(meta, propName, ignoreFields = []) {
10
9
  const prop = meta.properties[propName];
11
10
  const visible = prop && !prop.hidden;
@@ -38,49 +37,61 @@ class EntityTransformer {
38
37
  Utils_1.Utils.keys(entity).forEach(prop => keys.add(prop));
39
38
  }
40
39
  const visited = root.visited.has(entity);
40
+ const includePrimaryKeys = wrapped.__config.get('serialization').includePrimaryKeys;
41
41
  if (!visited) {
42
42
  root.visited.add(entity);
43
43
  }
44
- [...keys]
45
- .filter(prop => raw ? meta.properties[prop] : isVisible(meta, prop, ignoreFields))
46
- .map(prop => {
44
+ for (const prop of keys) {
45
+ const visible = raw ? meta.properties[prop] : isVisible(meta, prop, ignoreFields);
46
+ if (!visible) {
47
+ continue;
48
+ }
47
49
  const populated = root.isMarkedAsPopulated(meta.className, prop);
48
50
  const partiallyLoaded = root.isPartiallyLoaded(meta.className, prop);
49
- const isPrimary = wrapped.__config.get('serialization').includePrimaryKeys && meta.properties[prop].primary;
51
+ const isPrimary = includePrimaryKeys && meta.properties[prop].primary;
50
52
  if (!partiallyLoaded && !populated && !isPrimary) {
51
- return [prop, undefined];
53
+ continue;
52
54
  }
53
55
  const cycle = root.visit(meta.className, prop);
54
56
  if (cycle && visited) {
55
- return [prop, undefined];
57
+ continue;
56
58
  }
57
59
  const val = EntityTransformer.processProperty(prop, entity, raw, populated);
58
60
  if (!cycle) {
59
61
  root.leave(meta.className, prop);
60
62
  }
61
- if (val instanceof RawQueryFragment_1.RawQueryFragment) {
63
+ if (Utils_1.Utils.isRawSql(val)) {
62
64
  throw new Error(`Trying to serialize raw SQL fragment: '${val.sql}'`);
63
65
  }
64
- return [prop, val];
65
- })
66
- .filter(([, value]) => typeof value !== 'undefined')
67
- .forEach(([prop, value]) => ret[this.propertyName(meta, prop, wrapped.__platform, raw)] = value);
66
+ if (typeof val === 'undefined') {
67
+ continue;
68
+ }
69
+ ret[this.propertyName(meta, prop, wrapped.__platform, raw)] = val;
70
+ }
68
71
  if (!visited) {
69
72
  root.visited.delete(entity);
70
73
  }
71
74
  if (!wrapped.isInitialized() && wrapped.hasPrimaryKey()) {
72
75
  return ret;
73
76
  }
74
- // decorated getters
75
- meta.props
76
- .filter(prop => prop.getter && prop.getterName === undefined && !prop.hidden && typeof entity[prop.name] !== 'undefined')
77
- // @ts-ignore
78
- .forEach(prop => ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.name, entity, raw));
79
- // decorated get methods
80
- meta.props
81
- .filter(prop => prop.getterName && !prop.hidden && entity[prop.getterName] instanceof Function)
82
- // @ts-ignore
83
- .forEach(prop => ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.getterName, entity, raw));
77
+ for (const prop of meta.getterProps) {
78
+ // decorated get methods
79
+ if (prop.getterName != null) {
80
+ const visible = !prop.hidden && entity[prop.getterName] instanceof Function;
81
+ const populated = root.isMarkedAsPopulated(meta.className, prop.name);
82
+ if (visible) {
83
+ ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.getterName, entity, raw, populated);
84
+ }
85
+ }
86
+ else {
87
+ // decorated getters
88
+ const visible = !prop.hidden && typeof entity[prop.name] !== 'undefined';
89
+ const populated = root.isMarkedAsPopulated(meta.className, prop.name);
90
+ if (visible) {
91
+ ret[this.propertyName(meta, prop.name, wrapped.__platform, raw)] = this.processProperty(prop.name, entity, raw, populated);
92
+ }
93
+ }
94
+ }
84
95
  if (contextCreated) {
85
96
  root.close();
86
97
  }
@@ -45,9 +45,9 @@ class SerializationContext {
45
45
  }
46
46
  }
47
47
  close() {
48
- this.entities.forEach(entity => {
48
+ for (const entity of this.entities) {
49
49
  delete (0, wrap_1.helper)(entity).__serializationContext.root;
50
- });
50
+ }
51
51
  }
52
52
  /**
53
53
  * When initializing new context, we need to propagate it to the whole entity graph recursively.
@@ -55,20 +55,25 @@ class SerializationContext {
55
55
  static propagate(root, entity, isVisible) {
56
56
  root.register(entity);
57
57
  const meta = (0, wrap_1.helper)(entity).__meta;
58
- const items = [];
59
- Object.keys(entity)
60
- .filter(key => isVisible(meta, key))
61
- .forEach(key => {
62
- if (Utils_1.Utils.isEntity(entity[key], true)) {
63
- items.push(entity[key]);
58
+ for (const key of Object.keys(entity)) {
59
+ if (!isVisible(meta, key)) {
60
+ continue;
64
61
  }
65
- else if (Utils_1.Utils.isCollection(entity[key])) {
66
- items.push(...entity[key].getItems(false));
62
+ const target = entity[key];
63
+ if (Utils_1.Utils.isEntity(target, true)) {
64
+ if (!target.__helper.__serializationContext.root) {
65
+ this.propagate(root, target, isVisible);
66
+ }
67
+ continue;
67
68
  }
68
- });
69
- items
70
- .filter(item => !item.__helper.__serializationContext.root)
71
- .forEach(item => this.propagate(root, item, isVisible));
69
+ if (Utils_1.Utils.isCollection(target)) {
70
+ for (const item of target.getItems(false)) {
71
+ if (!item.__helper.__serializationContext.root) {
72
+ this.propagate(root, item, isVisible);
73
+ }
74
+ }
75
+ }
76
+ }
72
77
  }
73
78
  isMarkedAsPopulated(entityName, prop) {
74
79
  let populate = this.populate;
package/typings.d.ts CHANGED
@@ -223,7 +223,7 @@ type ExplicitlyOptionalProps<T> = (T extends {
223
223
  type NullableKeys<T, V = null> = {
224
224
  [K in keyof T]: V extends T[K] ? K : never;
225
225
  }[keyof T];
226
- type ProbablyOptionalProps<T> = PrimaryProperty<T> | ExplicitlyOptionalProps<T> | NonNullable<NullableKeys<T>>;
226
+ type ProbablyOptionalProps<T> = PrimaryProperty<T> | ExplicitlyOptionalProps<T> | NonNullable<NullableKeys<T, null | undefined>>;
227
227
  type IsOptional<T, K extends keyof T, I> = T[K] extends Collection<any, any> ? true : ExtractType<T[K]> extends I ? true : K extends ProbablyOptionalProps<T> ? true : false;
228
228
  type RequiredKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? CleanKeys<T, K> : never;
229
229
  type OptionalKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? never : CleanKeys<T, K>;
@@ -432,6 +432,7 @@ export interface EntityMetadata<T = any> {
432
432
  trackingProps: EntityProperty<T>[];
433
433
  hydrateProps: EntityProperty<T>[];
434
434
  uniqueProps: EntityProperty<T>[];
435
+ getterProps: EntityProperty<T>[];
435
436
  indexes: {
436
437
  properties: EntityKey<T> | EntityKey<T>[];
437
438
  name?: string;
package/typings.js CHANGED
@@ -63,6 +63,7 @@ class EntityMetadata {
63
63
  this.relations = this.props.filter(prop => typeof prop.kind !== 'undefined' && prop.kind !== enums_1.ReferenceKind.SCALAR && prop.kind !== enums_1.ReferenceKind.EMBEDDED);
64
64
  this.bidirectionalRelations = this.relations.filter(prop => prop.mappedBy || prop.inversedBy);
65
65
  this.uniqueProps = this.props.filter(prop => prop.unique);
66
+ this.getterProps = this.props.filter(prop => prop.getter);
66
67
  this.comparableProps = this.props.filter(prop => EntityComparator_1.EntityComparator.isComparable(prop, this));
67
68
  this.hydrateProps = this.props.filter(prop => {
68
69
  // `prop.userDefined` is either `undefined` or `false`
@@ -221,11 +221,11 @@ class EntityComparator {
221
221
  // traverse all parents, mapping my field name to each parent's field name until we reach the root
222
222
  for (let i = parents.length - 1; i >= 0; i--) {
223
223
  const parent = parents[i];
224
- // look for the field name in the parent's referencedColumnNames, except for M:N without a custom pivot entity
225
- // (in that case, the field names are the referenced PK's )
226
- const target = parent.kind === enums_1.ReferenceKind.MANY_TO_MANY && parent.pivotEntity === parent.pivotTable ?
227
- parent.referencedPKs : parent.referencedColumnNames;
228
- const idx = target.indexOf(fieldName);
224
+ // skip m:n since it does not represent any column directly
225
+ if (parent.pivotEntity) {
226
+ continue;
227
+ }
228
+ const idx = parent.referencedColumnNames.indexOf(fieldName);
229
229
  fieldName = parent.fieldNames[idx];
230
230
  }
231
231
  return this.propName(fieldName);
@@ -6,6 +6,7 @@ const Utils_1 = require("./Utils");
6
6
  const enums_1 = require("../enums");
7
7
  const JsonType_1 = require("../types/JsonType");
8
8
  const wrap_1 = require("../entity/wrap");
9
+ const RawQueryFragment_1 = require("./RawQueryFragment");
9
10
  class QueryHelper {
10
11
  static SUPPORTED_OPERATORS = ['>', '<', '<=', '>=', '!', '!='];
11
12
  static processParams(params) {
@@ -102,6 +103,10 @@ class QueryHelper {
102
103
  const prop = this.findProperty(key, options);
103
104
  const keys = prop?.joinColumns?.length ?? 0;
104
105
  const composite = keys > 1;
106
+ if (Array.isArray(value) && value.length === 0 && RawQueryFragment_1.RawQueryFragment.isKnownFragment(key)) {
107
+ o[key] = value;
108
+ return o;
109
+ }
105
110
  if (key in enums_1.GroupOperator) {
106
111
  o[key] = value.map((sub) => QueryHelper.processWhere({ ...options, where: sub, root }));
107
112
  return o;
@@ -70,7 +70,7 @@ export declare const ALIAS_REPLACEMENT_RE = "\\[::alias::\\]";
70
70
  * @Filter({ name: 'long', cond: () => ({ [raw('length(perex)')]: { $gt: 10000 } }) })
71
71
  * ```
72
72
  */
73
- export declare function raw<T extends object = any, R = any>(sql: EntityKey<T> | EntityKey<T>[] | AnyString | ((alias: string) => string) | RawQueryFragment, params?: unknown[] | Dictionary<unknown>): R;
73
+ 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;
74
74
  /**
75
75
  * Alternative to the `raw()` helper allowing to use it as a tagged template function for the simple cases.
76
76
  *
@@ -147,11 +147,12 @@ function raw(sql, params) {
147
147
  }
148
148
  if (typeof params === 'object' && !Array.isArray(params)) {
149
149
  const pairs = Object.entries(params);
150
- params = [];
150
+ const objectParams = [];
151
151
  for (const [key, value] of pairs) {
152
152
  sql = sql.replace(':' + key, '?');
153
- params.push(value);
153
+ objectParams.push(value);
154
154
  }
155
+ return new RawQueryFragment(sql, objectParams);
155
156
  }
156
157
  return new RawQueryFragment(sql, params);
157
158
  }