@mikro-orm/core 7.0.10-dev.9 → 7.0.11-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.
@@ -324,6 +324,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
324
324
  */
325
325
  map<Entity extends object>(entityName: EntityName<Entity>, result: EntityDictionary<Entity>, options?: {
326
326
  schema?: string;
327
+ mapped?: boolean;
327
328
  }): Entity;
328
329
  /**
329
330
  * Merges given entity to this EntityManager so it becomes managed. You can force refreshing of existing entities
package/EntityManager.js CHANGED
@@ -1295,8 +1295,9 @@ export class EntityManager {
1295
1295
  * Maps raw database result to an entity and merges it to this EntityManager.
1296
1296
  */
1297
1297
  map(entityName, result, options = {}) {
1298
+ const { mapped, ...rest } = options;
1298
1299
  const meta = this.metadata.get(entityName);
1299
- const data = this.driver.mapResult(result, meta);
1300
+ const data = (mapped ? result : this.driver.mapResult(result, meta));
1300
1301
  for (const k of Object.keys(data)) {
1301
1302
  const prop = meta.properties[k];
1302
1303
  if (prop?.kind === ReferenceKind.SCALAR &&
@@ -1310,7 +1311,7 @@ export class EntityManager {
1310
1311
  convertCustomTypes: true,
1311
1312
  refresh: true,
1312
1313
  validate: false,
1313
- ...options,
1314
+ ...rest,
1314
1315
  });
1315
1316
  }
1316
1317
  /**
@@ -5,7 +5,9 @@ import { Reference } from './Reference.js';
5
5
  import { helper, wrap } from './wrap.js';
6
6
  import { QueryHelper } from '../utils/QueryHelper.js';
7
7
  import { inspect } from '../logging/inspect.js';
8
- const collectionSymbol = Symbol('Collection');
8
+ // Globally registered so the marker survives the CJS/ESM dual-package hazard
9
+ // (see entitySymbol rationale in EntityHelper.ts and #7515/#7534).
10
+ const collectionSymbol = Symbol.for('@mikro-orm/core/Collection');
9
11
  /** Represents a to-many relation (1:m or m:n) as an iterable, managed collection of entities. */
10
12
  export class Collection {
11
13
  owner;
@@ -489,6 +489,22 @@ export class EntityLoader {
489
489
  }
490
490
  }
491
491
  if (populated.length === 0 && !populate.children) {
492
+ // Populate child-specific relations for TPT entities already in the identity map (GH #7529).
493
+ // Pass a sentinel hint so the existing TPT child handling in `populate()` does the real work.
494
+ if (populate.all && prop.targetMeta?.inheritanceType === 'tpt') {
495
+ const visited = options.visited;
496
+ const pending = Utils.unique(children).filter(c => helper(c).__meta !== prop.targetMeta && !visited.has(c));
497
+ if (pending.length > 0) {
498
+ const hint = [
499
+ { field: prop.targetMeta.primaryKeys[0], strategy: LoadStrategy.SELECT_IN, all: true },
500
+ ];
501
+ await this.populate(prop.targetMeta.class, pending, hint, {
502
+ ...options,
503
+ lookup: false,
504
+ validate: false,
505
+ });
506
+ }
507
+ }
492
508
  return;
493
509
  }
494
510
  const fields = this.buildFields(options.fields, prop);
@@ -4,8 +4,10 @@ import { Utils } from '../utils/Utils.js';
4
4
  import { QueryHelper } from '../utils/QueryHelper.js';
5
5
  import { NotFoundError } from '../errors.js';
6
6
  import { inspect } from '../logging/inspect.js';
7
- const referenceSymbol = Symbol('Reference');
8
- const scalarReferenceSymbol = Symbol('ScalarReference');
7
+ // Globally registered so the markers survive the CJS/ESM dual-package hazard
8
+ // (see entitySymbol rationale in EntityHelper.ts and #7515/#7534).
9
+ const referenceSymbol = Symbol.for('@mikro-orm/core/Reference');
10
+ const scalarReferenceSymbol = Symbol.for('@mikro-orm/core/ScalarReference');
9
11
  /** Wrapper around an entity that provides lazy loading capabilities and identity-preserving reference semantics. */
10
12
  export class Reference {
11
13
  entity;
@@ -1493,7 +1493,12 @@ export class MetadataDiscovery {
1493
1493
  }
1494
1494
  }
1495
1495
  if (this.#platform.usesEnumCheckConstraints() && !meta.embeddable) {
1496
- for (const prop of meta.props) {
1496
+ // Iterate `meta.properties` rather than `meta.props` — by this point in
1497
+ // the discovery pipeline `initEmbeddables` has added flattened embedded
1498
+ // properties to `meta.properties`, but `meta.sync()` has not yet been
1499
+ // called, so `meta.props` is still the pre-flatten array and would miss
1500
+ // enum properties that live inside embeddables.
1501
+ for (const prop of Object.values(meta.properties)) {
1497
1502
  if (prop.persist === false || prop.nativeEnumName || !prop.items?.every(item => typeof item === 'string')) {
1498
1503
  continue;
1499
1504
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.0.10-dev.9",
3
+ "version": "7.0.11-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
  "keywords": [
6
6
  "data-mapper",
@@ -106,7 +106,10 @@ export class QueryHelper {
106
106
  Object.keys(where).every(k => !Utils.isPlainObject(where[k]) ||
107
107
  Object.keys(where[k]).every(v => {
108
108
  if (Utils.isOperator(v, false)) {
109
- return true;
109
+ // multi-value operators (e.g. `$in`/`$nin`) cannot be inlined into a composite
110
+ // PK tuple — `getPrimaryKeyValues` would flatten the operator's array alongside
111
+ // the sibling PK values, producing a malformed `(col1, col2) IN ((a, b))` clause
112
+ return !meta.compositePK || !Array.isArray(where[k][v]);
110
113
  }
111
114
  if (meta.properties[k].primary &&
112
115
  [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(meta.properties[k].kind)) {
@@ -4,6 +4,8 @@ declare const rawFragmentSymbolBrand: unique symbol;
4
4
  export type RawQueryFragmentSymbol = symbol & {
5
5
  readonly [rawFragmentSymbolBrand]: true;
6
6
  };
7
+ /** Checks whether the given value is a `RawQueryFragment` instance. */
8
+ export declare function isRaw(value: unknown): value is RawQueryFragment;
7
9
  /** Represents a raw SQL fragment with optional parameters, usable as both a value and an object key via Symbol coercion. */
8
10
  export declare class RawQueryFragment<Alias extends string = string> {
9
11
  #private;
@@ -30,8 +32,6 @@ export declare class RawQueryFragment<Alias extends string = string> {
30
32
  static getKnownFragment(key: unknown): RawQueryFragment | undefined;
31
33
  }
32
34
  export { RawQueryFragment as Raw };
33
- /** Checks whether the given value is a `RawQueryFragment` instance. */
34
- export declare function isRaw(value: unknown): value is RawQueryFragment;
35
35
  /** @internal */
36
36
  export declare const ALIAS_REPLACEMENT = "[::alias::]";
37
37
  /** @internal */
@@ -1,21 +1,53 @@
1
1
  import { Utils } from './Utils.js';
2
- const rawSymbol = Symbol('RawQueryFragment');
2
+ // Brand lives on the prototype so JSON payloads — whose proto is
3
+ // `Object.prototype` — cannot forge it. String key, not a `Symbol.for(...)`,
4
+ // so each CJS/ESM copy of this module independently installs it on its own
5
+ // prototype without publishing a global key for the marker that controls raw
6
+ // SQL assembly. The string is namespaced so it does not collide with property
7
+ // names users might independently install on their own prototypes.
8
+ const RAW_FRAGMENT_BRAND = '__mikroOrmRawFragment';
9
+ // Back-references from a fragment's symbol key to the fragment itself, shared
10
+ // across CJS/ESM module copies via globalThis: when one copy creates a fragment
11
+ // via `raw('…')` and stores its symbol in a where-clause object key, the other
12
+ // copy still needs to recover the original fragment to assemble SQL.
13
+ const REGISTRY_KEY = Symbol.for('@mikro-orm/core/RawQueryFragment.references');
14
+ const rawQueryReferences = (globalThis[REGISTRY_KEY] ??=
15
+ new WeakMap());
16
+ /** Checks whether the given value is a `RawQueryFragment` instance. */
17
+ export function isRaw(value) {
18
+ if (value == null || typeof value !== 'object') {
19
+ return false;
20
+ }
21
+ // Fast path: intra-module instances and their subclasses.
22
+ // eslint-disable-next-line no-use-before-define
23
+ if (value instanceof RawQueryFragment) {
24
+ return true;
25
+ }
26
+ // Walk the prototype chain starting from the *prototype* (not the value) so
27
+ // own-property spoofing from JSON payloads cannot forge the brand. Stop at
28
+ // `Object.prototype`, which is never branded — that bails plain objects,
29
+ // JSON payloads, and built-ins like `Date`/`Array`/`Map` at depth 1.
30
+ for (let p = Object.getPrototypeOf(value); p != null && p !== Object.prototype; p = Object.getPrototypeOf(p)) {
31
+ if (Object.hasOwn(p, RAW_FRAGMENT_BRAND)) {
32
+ return true;
33
+ }
34
+ }
35
+ return false;
36
+ }
3
37
  /** Represents a raw SQL fragment with optional parameters, usable as both a value and an object key via Symbol coercion. */
4
38
  export class RawQueryFragment {
5
39
  sql;
6
40
  params;
7
- static #rawQueryReferences = new WeakMap();
8
41
  #key;
9
42
  constructor(sql, params = []) {
10
43
  this.sql = sql;
11
44
  this.params = params;
12
- Object.defineProperty(this, rawSymbol, { value: true, enumerable: false });
13
45
  }
14
46
  /** Returns a unique symbol key for this fragment, creating and caching it on first access. */
15
47
  get key() {
16
48
  if (!this.#key) {
17
49
  this.#key = Symbol(this.toJSON());
18
- RawQueryFragment.#rawQueryReferences.set(this.#key, this);
50
+ rawQueryReferences.set(this.#key, this);
19
51
  }
20
52
  return this.#key;
21
53
  }
@@ -42,7 +74,7 @@ export class RawQueryFragment {
42
74
  }
43
75
  /** Checks whether the given value is a symbol that maps to a known raw query fragment. */
44
76
  static isKnownFragmentSymbol(key) {
45
- return typeof key === 'symbol' && this.#rawQueryReferences.has(key);
77
+ return typeof key === 'symbol' && rawQueryReferences.has(key);
46
78
  }
47
79
  /** Checks whether an object has any symbol keys that are known raw query fragments. */
48
80
  static hasObjectFragments(object) {
@@ -51,20 +83,17 @@ export class RawQueryFragment {
51
83
  }
52
84
  /** Checks whether the given value is a RawQueryFragment instance or a known fragment symbol. */
53
85
  static isKnownFragment(key) {
54
- if (key instanceof RawQueryFragment) {
55
- return true;
56
- }
57
- return this.isKnownFragmentSymbol(key);
86
+ return isRaw(key) || this.isKnownFragmentSymbol(key);
58
87
  }
59
88
  /** Retrieves the RawQueryFragment associated with the given key (instance or symbol). */
60
89
  static getKnownFragment(key) {
61
- if (key instanceof RawQueryFragment) {
90
+ if (isRaw(key)) {
62
91
  return key;
63
92
  }
64
93
  if (typeof key !== 'symbol') {
65
94
  return;
66
95
  }
67
- return this.#rawQueryReferences.get(key);
96
+ return rawQueryReferences.get(key);
68
97
  }
69
98
  /** @ignore */
70
99
  /* v8 ignore next */
@@ -75,11 +104,19 @@ export class RawQueryFragment {
75
104
  return { sql: this.sql };
76
105
  }
77
106
  }
107
+ // Non-enumerable so the brand is skipped by JSON/Object.keys/for-in, and locked
108
+ // down (non-writable, non-configurable) so in-process code cannot delete or
109
+ // overwrite it and thereby silently disable `isRaw` recognition for every
110
+ // fragment in the process. Subclasses don't need mutability here — they inherit
111
+ // the brand via the prototype chain, and sibling-copy classes install their own
112
+ // brand on their own prototype (a different object), so lockdown only blocks
113
+ // tampering with the canonical marker.
114
+ Object.defineProperty(RawQueryFragment.prototype, RAW_FRAGMENT_BRAND, {
115
+ value: true,
116
+ writable: false,
117
+ configurable: false,
118
+ });
78
119
  export { RawQueryFragment as Raw };
79
- /** Checks whether the given value is a `RawQueryFragment` instance. */
80
- export function isRaw(value) {
81
- return typeof value === 'object' && value !== null && Object.hasOwn(value, rawSymbol);
82
- }
83
120
  /** @internal */
84
121
  export const ALIAS_REPLACEMENT = '[::alias::]';
85
122
  /** @internal */
@@ -140,7 +177,7 @@ export const ALIAS_REPLACEMENT_RE = '\\[::alias::\\]';
140
177
  * ```
141
178
  */
142
179
  export function raw(sql, params) {
143
- if (sql instanceof RawQueryFragment) {
180
+ if (isRaw(sql)) {
144
181
  return sql;
145
182
  }
146
183
  if (sql instanceof Function) {
@@ -163,6 +163,10 @@ export class TransactionManager {
163
163
  const parentEntity = parentUoW.getById(meta.class, wrapped.getPrimaryKey(), parent.schema, true);
164
164
  if (parentEntity && parentEntity !== entity) {
165
165
  const parentWrapped = helper(parentEntity);
166
+ // Don't overwrite a fully-loaded entity with an uninitialized reference
167
+ if (!wrapped.__initialized && parentWrapped.__initialized) {
168
+ continue;
169
+ }
166
170
  parentWrapped.__data = wrapped.__data;
167
171
  parentWrapped.__originalEntityData = wrapped.__originalEntityData;
168
172
  for (const prop of meta.hydrateProps) {
package/utils/Utils.js CHANGED
@@ -22,13 +22,22 @@ export function compareObjects(a, b) {
22
22
  if (a === b || (a == null && b == null)) {
23
23
  return true;
24
24
  }
25
- if (!a || !b || typeof a !== 'object' || typeof b !== 'object' || !compareConstructors(a, b)) {
25
+ if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
26
26
  return false;
27
27
  }
28
+ // Raw fragments are compared by `sql` + `params` *before* the constructor
29
+ // check, so that two fragments carrying the same SQL but constructed by
30
+ // different CJS/ESM copies of this module (different classes, different
31
+ // prototypes) still compare as equal. Without this, the dual-package hazard
32
+ // would produce spurious change-set diffs when a raw fragment is used as a
33
+ // property value.
28
34
  if (isRaw(a) && isRaw(b)) {
29
35
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
30
36
  return a.sql === b.sql && compareArrays(a.params, b.params);
31
37
  }
38
+ if (!compareConstructors(a, b)) {
39
+ return false;
40
+ }
32
41
  if (a instanceof Date && b instanceof Date) {
33
42
  const timeA = a.getTime();
34
43
  const timeB = b.getTime();
@@ -132,7 +141,7 @@ export function parseJsonSafe(value) {
132
141
  /** Collection of general-purpose utility methods used throughout the ORM. */
133
142
  export class Utils {
134
143
  static PK_SEPARATOR = '~~~';
135
- static #ORM_VERSION = '7.0.10-dev.9';
144
+ static #ORM_VERSION = '7.0.11-dev.0';
136
145
  /**
137
146
  * Checks if the argument is instance of `Object`. Returns false for arrays.
138
147
  */