@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.
- package/EntityManager.d.ts +1 -0
- package/EntityManager.js +3 -2
- package/entity/Collection.js +3 -1
- package/entity/EntityLoader.js +16 -0
- package/entity/Reference.js +4 -2
- package/metadata/MetadataDiscovery.js +6 -1
- package/package.json +1 -1
- package/utils/QueryHelper.js +4 -1
- package/utils/RawQueryFragment.d.ts +2 -2
- package/utils/RawQueryFragment.js +53 -16
- package/utils/TransactionManager.js +4 -0
- package/utils/Utils.js +11 -2
package/EntityManager.d.ts
CHANGED
|
@@ -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
|
-
...
|
|
1314
|
+
...rest,
|
|
1314
1315
|
});
|
|
1315
1316
|
}
|
|
1316
1317
|
/**
|
package/entity/Collection.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/entity/EntityLoader.js
CHANGED
|
@@ -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);
|
package/entity/Reference.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|
package/utils/QueryHelper.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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' &&
|
|
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
|
-
|
|
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
|
|
90
|
+
if (isRaw(key)) {
|
|
62
91
|
return key;
|
|
63
92
|
}
|
|
64
93
|
if (typeof key !== 'symbol') {
|
|
65
94
|
return;
|
|
66
95
|
}
|
|
67
|
-
return
|
|
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
|
|
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'
|
|
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.
|
|
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
|
*/
|