@mikro-orm/core 7.1.15-dev.1 → 7.1.15-dev.11

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.
@@ -229,9 +229,10 @@ export class EntityLoader {
229
229
  }
230
230
  toPopulate.push(entity);
231
231
  }
232
- else if (refValue == null && !helper(entity).__loadedProperties.has(prop.name)) {
232
+ else if (refValue == null && !prop.object && !helper(entity).__loadedProperties.has(prop.name)) {
233
233
  // FK columns weren't loaded (partial loading) — need to re-fetch them.
234
234
  // If the property IS in __loadedProperties, the FK was loaded and is genuinely null.
235
+ // Object-embedded virtual props are skipped — they are populated via the embeddable instance.
235
236
  needsFkLoad.push(entity);
236
237
  }
237
238
  }
@@ -112,7 +112,7 @@ export interface PropertyChain<Value, Options> {
112
112
  getter(getter?: boolean): PropertyChain<Value, Options>;
113
113
  getterName(getterName: string): PropertyChain<Value, Options>;
114
114
  serializedPrimaryKey(serializedPrimaryKey?: boolean): PropertyChain<Value, Options>;
115
- serializer(serializer: (value: Value, options?: SerializeOptions<any>) => any): PropertyChain<Value, Options>;
115
+ serializer(serializer: (value: SerializerValue<Value, Options>, options?: SerializeOptions<any>) => any): PropertyChain<Value, Options>;
116
116
  serializedName(serializedName: string): PropertyChain<Value, Options>;
117
117
  groups(...groups: string[]): PropertyChain<Value, Options>;
118
118
  customOrder(...customOrder: string[] | number[] | boolean[]): PropertyChain<Value, Options>;
@@ -795,6 +795,8 @@ type InferBuilderValue<Builder> = Builder extends {
795
795
  type MaybeArray<Value, Options> = Options extends {
796
796
  array: true;
797
797
  } ? Value[] : Value;
798
+ /** The runtime value passed to a custom serializer — the property value as stored on the entity (e.g. `Collection`/`Ref` for relations). Skips `MaybeMapToPk`, as its `Primary<Value>` branch is too costly for the type checker. */
799
+ type SerializerValue<Value, Options> = MaybeNullable<MaybeRelationRef<MaybeArray<Value, Options>, Options>, Options>;
798
800
  type MaybeMapToPk<Value, Options> = Options extends {
799
801
  mapToPk: true;
800
802
  } ? Primary<Value> : Value;
@@ -475,7 +475,7 @@ export class UniversalPropertyOptionsBuilder {
475
475
  export class OneToManyOptionsBuilderOnlyMappedBy extends UniversalPropertyOptionsBuilder {
476
476
  /** Point to the owning side property name. */
477
477
  mappedBy(mappedBy) {
478
- return new UniversalPropertyOptionsBuilder({ ...this['~options'], mappedBy });
478
+ return this.assignOptions({ mappedBy });
479
479
  }
480
480
  }
481
481
  function createPropertyBuilders(options) {
@@ -19,6 +19,8 @@ export declare class ObjectHydrator extends Hydrator {
19
19
  getEntityHydrator<T extends object>(meta: EntityMetadata<T>, type: 'full' | 'reference', normalizeAccessors?: boolean): EntityHydrator<T>;
20
20
  private createCollectionItemMapper;
21
21
  private wrap;
22
+ /** Renders a key as a single-quoted JS string literal, safe to embed in generated code. */
23
+ private quote;
22
24
  private safeKey;
23
25
  }
24
26
  export {};
@@ -87,7 +87,7 @@ export class ObjectHydrator extends Hydrator {
87
87
  ret.push(` if (data${dataKey} === null) {`);
88
88
  if (prop.ref) {
89
89
  ret.push(` entity${entityKey} = new ScalarReference();`);
90
- ret.push(` entity${entityKey}.bind(entity, '${prop.name}');`);
90
+ ret.push(` entity${entityKey}.bind(entity, ${this.quote(prop.name)});`);
91
91
  ret.push(` entity${entityKey}.set(${nullVal});`);
92
92
  }
93
93
  else {
@@ -127,14 +127,14 @@ export class ObjectHydrator extends Hydrator {
127
127
  if (prop.ref) {
128
128
  ret.push(` const value = isScalarReference(entity${entityKey}) ? entity${entityKey}.unwrap() : entity${entityKey};`);
129
129
  ret.push(` entity${entityKey} = oldValue_${idx} ?? new ScalarReference(value);`);
130
- ret.push(` entity${entityKey}.bind(entity, '${prop.name}');`);
130
+ ret.push(` entity${entityKey}.bind(entity, ${this.quote(prop.name)});`);
131
131
  ret.push(` entity${entityKey}.set(value);`);
132
132
  }
133
133
  ret.push(` }`);
134
134
  if (prop.ref) {
135
135
  ret.push(` if (!entity${entityKey}) {`);
136
136
  ret.push(` entity${entityKey} = new ScalarReference();`);
137
- ret.push(` entity${entityKey}.bind(entity, '${prop.name}');`);
137
+ ret.push(` entity${entityKey}.bind(entity, ${this.quote(prop.name)});`);
138
138
  ret.push(` }`);
139
139
  }
140
140
  return ret;
@@ -220,7 +220,7 @@ export class ObjectHydrator extends Hydrator {
220
220
  ret.push(` }`);
221
221
  ret.push(` if (Array.isArray(data${dataKey})) {`);
222
222
  ret.push(` const items = data${dataKey}.map(value => createCollectionItem_${this.safeKey(prop.name)}(value, entity));`);
223
- ret.push(` const coll = Collection.create(entity, '${prop.name}', items, newEntity);`);
223
+ ret.push(` const coll = Collection.create(entity, ${this.quote(prop.name)}, items, newEntity);`);
224
224
  ret.push(` if (newEntity) {`);
225
225
  ret.push(` coll.setDirty();`);
226
226
  ret.push(` } else {`);
@@ -231,11 +231,11 @@ export class ObjectHydrator extends Hydrator {
231
231
  if (!this.platform.usesPivotTable() && prop.owner && prop.kind === ReferenceKind.MANY_TO_MANY) {
232
232
  ret.push(` } else if (!entity${entityKey} && Array.isArray(data${dataKey})) {`);
233
233
  const items = this.platform.usesPivotTable() || !prop.owner ? 'undefined' : '[]';
234
- ret.push(` const coll = Collection.create(entity, '${prop.name}', ${items}, !!data${dataKey} || newEntity);`);
234
+ ret.push(` const coll = Collection.create(entity, ${this.quote(prop.name)}, ${items}, !!data${dataKey} || newEntity);`);
235
235
  ret.push(` coll.setDirty(false);`);
236
236
  }
237
237
  ret.push(` } else if (!entity${entityKey}) {`);
238
- ret.push(` const coll = Collection.create(entity, '${prop.name}', undefined, newEntity);`);
238
+ ret.push(` const coll = Collection.create(entity, ${this.quote(prop.name)}, undefined, newEntity);`);
239
239
  ret.push(` coll.setDirty(false);`);
240
240
  ret.push(` }`);
241
241
  return ret;
@@ -431,10 +431,14 @@ export class ObjectHydrator extends Hydrator {
431
431
  return lines;
432
432
  }
433
433
  wrap(key) {
434
- if (/^\[.*]$/.exec(key)) {
434
+ if (/^\[idx_\d+]$/.exec(key)) {
435
435
  return key;
436
436
  }
437
- return /^\w+$/.exec(key) ? `.${key}` : `['${key}']`;
437
+ return /^\w+$/.exec(key) ? `.${key}` : `[${this.quote(key)}]`;
438
+ }
439
+ /** Renders a key as a single-quoted JS string literal, safe to embed in generated code. */
440
+ quote(key) {
441
+ return `'${key.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
438
442
  }
439
443
  safeKey(key) {
440
444
  return key.replace(/\W/g, '_');
@@ -441,7 +441,18 @@ export class MetadataDiscovery {
441
441
  if (prop.kind === ReferenceKind.SCALAR || prop.kind === ReferenceKind.EMBEDDED) {
442
442
  prop.fieldNames = [this.#namingStrategy.propertyToColumnName(prop.name, object)];
443
443
  }
444
- else if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && !prop.polymorphic) {
444
+ else if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.polymorphic) {
445
+ if (prop.targetMeta) {
446
+ // same layout as `initManyToOneFields` builds later: `[discriminatorColumn, ...fkIdColumns]`
447
+ const pkFields = prop.targetMeta.getPrimaryProps().flatMap(pk => {
448
+ this.initFieldName(pk);
449
+ return pk.fieldNames;
450
+ });
451
+ const idColumns = pkFields.map(fieldName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, fieldName, pkFields.length > 1));
452
+ prop.fieldNames = [prop.discriminatorColumn, ...idColumns];
453
+ }
454
+ }
455
+ else if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind)) {
445
456
  prop.fieldNames = this.initManyToOneFieldName(prop, prop.name);
446
457
  }
447
458
  else if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.owner) {
@@ -1246,8 +1257,17 @@ export class MetadataDiscovery {
1246
1257
  if (embeddedProp.nullable || refInArray) {
1247
1258
  meta.properties[name].nullable = true;
1248
1259
  }
1260
+ // polymorphic relations derive their column names from the discriminator, so prefix it too
1261
+ if (meta.properties[name].polymorphic && !object) {
1262
+ meta.properties[name].discriminator = prefix + meta.properties[name].discriminator;
1263
+ meta.properties[name].discriminatorColumn = prefix + meta.properties[name].discriminatorColumn;
1264
+ }
1249
1265
  if (meta.properties[name].fieldNames) {
1250
- meta.properties[name].fieldNames[0] = prefix + meta.properties[name].fieldNames[0];
1266
+ const { fieldNames, polymorphic } = meta.properties[name];
1267
+ // polymorphic `fieldNames` hold `[discriminatorColumn, ...fkIdColumns]`, so prefix all of them
1268
+ for (let i = 0; i < (polymorphic ? fieldNames.length : 1); i++) {
1269
+ fieldNames[i] = prefix + fieldNames[i];
1270
+ }
1251
1271
  }
1252
1272
  else {
1253
1273
  const name2 = meta.properties[name].name;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.15-dev.1",
3
+ "version": "7.1.15-dev.11",
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
  "keywords": [
6
6
  "data-mapper",
@@ -80,6 +80,8 @@ export declare class EntityComparator {
80
80
  private getGenericComparator;
81
81
  private getPropertyComparator;
82
82
  private wrap;
83
+ /** Renders a key as a single-quoted JS string literal, safe to embed in generated code. */
84
+ private quote;
83
85
  private safeKey;
84
86
  /**
85
87
  * Sets the toArray helper in the context if not already set.
@@ -587,7 +587,7 @@ export class EntityComparator {
587
587
  }
588
588
  }
589
589
  else if (prop.polymorphic) {
590
- const discriminatorMapKey = `discriminatorMapReverse_${prop.name}`;
590
+ const discriminatorMapKey = `discriminatorMapReverse_${this.safeKey(prop.name)}`;
591
591
  const reverseMap = new Map();
592
592
  for (const [key, value] of Object.entries(prop.discriminatorMap)) {
593
593
  reverseMap.set(value, key);
@@ -758,10 +758,14 @@ export class EntityComparator {
758
758
  return this.getGenericComparator(this.wrap(prop.name), `!equals(last${this.wrap(prop.name)}, current${this.wrap(prop.name)})`);
759
759
  }
760
760
  wrap(key) {
761
- if (/^\[.*]$/.exec(key)) {
761
+ if (/^\[idx_\d+]$/.exec(key)) {
762
762
  return key;
763
763
  }
764
- return /^\w+$/.exec(key) ? `.${key}` : `['${key}']`;
764
+ return /^\w+$/.exec(key) ? `.${key}` : `[${this.quote(key)}]`;
765
+ }
766
+ /** Renders a key as a single-quoted JS string literal, safe to embed in generated code. */
767
+ quote(key) {
768
+ return `'${key.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
765
769
  }
766
770
  safeKey(key) {
767
771
  return key.replace(/\W/g, '_');
package/utils/Utils.js CHANGED
@@ -153,7 +153,7 @@ export function parseJsonSafe(value) {
153
153
  /** Collection of general-purpose utility methods used throughout the ORM. */
154
154
  export class Utils {
155
155
  static PK_SEPARATOR = '~~~';
156
- static #ORM_VERSION = '7.1.15-dev.1';
156
+ static #ORM_VERSION = '7.1.15-dev.11';
157
157
  /**
158
158
  * Checks if the argument is instance of `Object`. Returns false for arrays.
159
159
  */