@mikro-orm/core 7.0.9-dev.0 → 7.0.9-dev.10

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.
@@ -1,4 +1,4 @@
1
- import type { EntityDTO, EntityKey, EntityProperty, FilterQuery, IPrimaryKey, Loaded, LoadedCollection, Populate, Primary } from '../typings.js';
1
+ import { type EntityDTO, type EntityKey, type EntityProperty, type FilterQuery, type IPrimaryKey, type Loaded, type LoadedCollection, type Populate, type Primary, CollectionBrand } from '../typings.js';
2
2
  import { Reference } from './Reference.js';
3
3
  import type { Transaction } from '../connections/Connection.js';
4
4
  import type { CountOptions, FindOptions } from '../drivers/IDatabaseDriver.js';
@@ -17,6 +17,7 @@ export declare class Collection<T extends object, O extends object = object> {
17
17
  #private;
18
18
  readonly owner: O;
19
19
  [k: number]: T;
20
+ readonly [CollectionBrand]: true;
20
21
  constructor(owner: O, items?: T[], initialized?: boolean);
21
22
  /**
22
23
  * Creates new Collection instance, assigns it to the owning entity and sets the items to it (propagating them to their inverse sides)
@@ -167,9 +167,11 @@ export class EntityFactory {
167
167
  return diff2[key] === undefined;
168
168
  })
169
169
  .forEach(key => delete diff2[key]);
170
- // but always add collection properties and formulas if they are part of the `data`
170
+ // but always add collection properties, formulas, and generated columns if they are part of the `data`,
171
+ // as these are excluded from `comparableProps` and won't appear in the diff
171
172
  Utils.keys(data)
172
173
  .filter(key => meta.properties[key]?.formula ||
174
+ (meta.properties[key]?.generated && !meta.properties[key]?.primary) ||
173
175
  [ReferenceKind.ONE_TO_MANY, ReferenceKind.MANY_TO_MANY].includes(meta.properties[key]?.kind))
174
176
  .forEach(key => (diff2[key] = data[key]));
175
177
  // rehydrated with the new values, skip those changed by user
@@ -1267,6 +1267,18 @@ export class MetadataDiscovery {
1267
1267
  if (prop.enum && prop.items && rootProp?.items) {
1268
1268
  newProp.items = Utils.unique([...rootProp.items, ...prop.items]);
1269
1269
  }
1270
+ // When multiple STI children share the same unique relation (OneToOne/ManyToOne),
1271
+ // replace the simple unique with a composite one including the discriminator column
1272
+ // so different subtypes can independently reference the same target row.
1273
+ if (rootProp?.unique &&
1274
+ newProp.unique &&
1275
+ [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(newProp.kind)) {
1276
+ newProp.unique = false;
1277
+ rootProp.unique = false;
1278
+ meta.root.uniques.push({
1279
+ properties: [prop.name, meta.root.discriminatorColumn],
1280
+ });
1281
+ }
1270
1282
  newProp.nullable = true;
1271
1283
  newProp.inherited = !rootProp;
1272
1284
  meta.root.addProperty(newProp);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.0.9-dev.0",
3
+ "version": "7.0.9-dev.10",
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",
package/typings.d.ts CHANGED
@@ -74,7 +74,7 @@ export type MaybePromise<T> = T | Promise<T>;
74
74
  */
75
75
  type CollectionShape<T = any> = {
76
76
  [k: number]: T;
77
- readonly owner: object;
77
+ readonly [CollectionBrand]: true;
78
78
  };
79
79
  /**
80
80
  * Structural type for matching LoadedCollection (extends CollectionShape with `$` property).
@@ -134,6 +134,8 @@ export type DeepPartial<T> = T & {
134
134
  export declare const EntityRepositoryType: unique symbol;
135
135
  /** Symbol used to declare the primary key property name(s) on an entity (e.g., `[PrimaryKeyProp]?: 'id'`). */
136
136
  export declare const PrimaryKeyProp: unique symbol;
137
+ /** Symbol used as a brand on `CollectionShape` to prevent false structural matches with entities that have properties like `owner`. */
138
+ export declare const CollectionBrand: unique symbol;
137
139
  /** Symbol used to declare which properties are optional in `em.create()` (e.g., `[OptionalProps]?: 'createdAt'`). */
138
140
  export declare const OptionalProps: unique symbol;
139
141
  /** Symbol used to declare which relation properties should be eagerly loaded (e.g., `[EagerProps]?: 'author'`). */
package/typings.js CHANGED
@@ -9,6 +9,8 @@ import { BaseEntity } from './entity/BaseEntity.js';
9
9
  export const EntityRepositoryType = Symbol('EntityRepositoryType');
10
10
  /** Symbol used to declare the primary key property name(s) on an entity (e.g., `[PrimaryKeyProp]?: 'id'`). */
11
11
  export const PrimaryKeyProp = Symbol('PrimaryKeyProp');
12
+ /** Symbol used as a brand on `CollectionShape` to prevent false structural matches with entities that have properties like `owner`. */
13
+ export const CollectionBrand = Symbol('CollectionBrand');
12
14
  /** Symbol used to declare which properties are optional in `em.create()` (e.g., `[OptionalProps]?: 'createdAt'`). */
13
15
  export const OptionalProps = Symbol('OptionalProps');
14
16
  /** Symbol used to declare which relation properties should be eagerly loaded (e.g., `[EagerProps]?: 'author'`). */
@@ -24,6 +24,12 @@ export declare class ChangeSetPersister {
24
24
  private checkConcurrencyKeys;
25
25
  private persistManagedEntitiesBatch;
26
26
  private mapPrimaryKey;
27
+ /**
28
+ * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
29
+ * with the real values now present on the entity. This is needed because `mapPrimaryKey`
30
+ * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
31
+ */
32
+ private syncCompositeIdentifiers;
27
33
  /**
28
34
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
29
35
  */
@@ -132,6 +132,7 @@ export class ChangeSetPersister {
132
132
  this.mapPrimaryKey(meta, res.insertId ?? res.row?.[meta.primaryKeys[0]], changeSet);
133
133
  }
134
134
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.row, meta);
135
+ this.syncCompositeIdentifiers(changeSet);
135
136
  this.markAsPopulated(changeSet, meta);
136
137
  wrapped.__initialized = true;
137
138
  wrapped.__managed = true;
@@ -178,6 +179,7 @@ export class ChangeSetPersister {
178
179
  if (res.rows) {
179
180
  this.mapReturnedValues(changeSet.entity, changeSet.payload, res.rows[i], meta);
180
181
  }
182
+ this.syncCompositeIdentifiers(changeSet);
181
183
  this.markAsPopulated(changeSet, meta);
182
184
  wrapped.__initialized = true;
183
185
  wrapped.__managed = true;
@@ -255,6 +257,24 @@ export class ChangeSetPersister {
255
257
  wrapped.__identifier.setValue(value);
256
258
  }
257
259
  }
260
+ /**
261
+ * After INSERT + hydration, sync all EntityIdentifier placeholders in composite PK arrays
262
+ * with the real values now present on the entity. This is needed because `mapPrimaryKey`
263
+ * only handles the first PK column, but any scalar PK in a composite key may be auto-generated.
264
+ */
265
+ syncCompositeIdentifiers(changeSet) {
266
+ const wrapped = helper(changeSet.entity);
267
+ if (!Array.isArray(wrapped.__identifier)) {
268
+ return;
269
+ }
270
+ const pks = changeSet.meta.getPrimaryProps();
271
+ for (let i = 0; i < pks.length; i++) {
272
+ const ident = wrapped.__identifier[i];
273
+ if (ident instanceof EntityIdentifier && pks[i].kind === ReferenceKind.SCALAR) {
274
+ ident.setValue(changeSet.entity[pks[i].name]);
275
+ }
276
+ }
277
+ }
258
278
  /**
259
279
  * Sets populate flag to new entities so they are serialized like if they were loaded from the db
260
280
  */
@@ -413,8 +433,8 @@ export class ChangeSetPersister {
413
433
  }
414
434
  return;
415
435
  }
416
- if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
417
- changeSet.payload[prop.name] = value.map(item => item.getValue());
436
+ if (Array.isArray(value) && value.some(item => item instanceof EntityIdentifier)) {
437
+ changeSet.payload[prop.name] = value.map(item => (item instanceof EntityIdentifier ? item.getValue() : item));
418
438
  return;
419
439
  }
420
440
  if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
package/utils/Utils.js CHANGED
@@ -132,7 +132,7 @@ export function parseJsonSafe(value) {
132
132
  /** Collection of general-purpose utility methods used throughout the ORM. */
133
133
  export class Utils {
134
134
  static PK_SEPARATOR = '~~~';
135
- static #ORM_VERSION = '7.0.9-dev.0';
135
+ static #ORM_VERSION = '7.0.9-dev.10';
136
136
  /**
137
137
  * Checks if the argument is instance of `Object`. Returns false for arrays.
138
138
  */