@mikro-orm/core 7.1.11-dev.1 → 7.1.11-dev.3

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,9 +1,10 @@
1
1
  /** Interface for async-capable cache storage used by result cache and metadata cache. */
2
2
  export interface CacheAdapter {
3
3
  /**
4
- * Gets the items under `name` key from the cache.
4
+ * Gets the items under `name` key from the cache. When `origin` is provided, adapters that
5
+ * track the entry origin should ignore entries cached from a different source file.
5
6
  */
6
- get<T = any>(name: string): T | Promise<T | undefined> | undefined;
7
+ get<T = any>(name: string, origin?: string): T | Promise<T | undefined> | undefined;
7
8
  /**
8
9
  * Sets the item to the cache. `origin` is used for cache invalidation and should reflect the change in data.
9
10
  */
@@ -24,9 +25,10 @@ export interface CacheAdapter {
24
25
  /** Synchronous variant of CacheAdapter, used for metadata cache where async access is not needed. */
25
26
  export interface SyncCacheAdapter extends CacheAdapter {
26
27
  /**
27
- * Gets the items under `name` key from the cache.
28
+ * Gets the items under `name` key from the cache. When `origin` is provided, adapters that
29
+ * track the entry origin should ignore entries cached from a different source file.
28
30
  */
29
- get<T = any>(name: string): T | undefined;
31
+ get<T = any>(name: string, origin?: string): T | undefined;
30
32
  /**
31
33
  * Sets the item to the cache. `origin` is used for cache invalidation and should reflect the change in data.
32
34
  */
@@ -8,7 +8,7 @@ export declare class FileCacheAdapter implements SyncCacheAdapter {
8
8
  /**
9
9
  * @inheritDoc
10
10
  */
11
- get(name: string): any;
11
+ get(name: string, origin?: string): any;
12
12
  /**
13
13
  * @inheritDoc
14
14
  */
@@ -16,12 +16,17 @@ export class FileCacheAdapter {
16
16
  /**
17
17
  * @inheritDoc
18
18
  */
19
- get(name) {
19
+ get(name, origin) {
20
20
  const path = this.path(name);
21
21
  if (!existsSync(path)) {
22
22
  return null;
23
23
  }
24
24
  const payload = fs.readJSONSync(path);
25
+ // Two classes with the same name share the cache key, ignore entries cached
26
+ // from a different source file.
27
+ if (origin && fs.absolutePath(payload.origin, this.#baseDir) !== fs.absolutePath(origin, this.#baseDir)) {
28
+ return null;
29
+ }
25
30
  const hash = this.getHash(payload.origin);
26
31
  if (!hash || payload.hash !== hash) {
27
32
  return null;
@@ -4,6 +4,8 @@ import { Cascade, ReferenceKind } from '../enums.js';
4
4
  import { Type } from '../types/Type.js';
5
5
  import { Utils } from '../utils/Utils.js';
6
6
  import { EnumArrayType } from '../types/EnumArrayType.js';
7
+ // mirrors `MetadataStorage.META_SYMBOL`, we can't import it here due to a module cycle
8
+ const META_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.META_SYMBOL');
7
9
  /** Class-less entity definition that provides a programmatic API for defining entities without decorators. */
8
10
  export class EntitySchema {
9
11
  /**
@@ -220,8 +222,9 @@ export class EntitySchema {
220
222
  // Only set extends if the parent is NOT the auto-generated class for this same entity.
221
223
  // When the user extends the auto-generated class (from defineEntity without a class option)
222
224
  // and registers their custom class via setClass, we don't want to discover the
223
- // auto-generated class as a separate parent entity.
224
- if (base !== BaseEntity && base.name !== this._meta.className) {
225
+ // auto-generated class as a separate parent entity. A parent carrying its own decorator
226
+ // metadata is a real base class even when a minifier mangles it to the same name as the child.
227
+ if (base !== BaseEntity && (base.name !== this._meta.className || Object.hasOwn(base, META_SYMBOL))) {
225
228
  this._meta.extends ??= base.name ? base : undefined;
226
229
  }
227
230
  }
@@ -212,6 +212,12 @@ export class MetadataDiscovery {
212
212
  .replace(/Array<(.*)>/, '$1') // unwrap array
213
213
  .replace(/\[]$/, '') // remove array suffix
214
214
  .replace(/\((.*)\)/, '$1'); // unwrap union types
215
+ // Names can be ambiguous when a minifier mangles two classes to the same name,
216
+ // so class references also need to be checked by identity.
217
+ const discoveredByIdentity = (target) => {
218
+ const cls = EntitySchema.is(target) ? target.meta.class : target;
219
+ return typeof cls !== 'function' || this.#discovered.some(m => m.class === cls);
220
+ };
215
221
  const missing = [];
216
222
  this.#discovered.forEach(meta => Object.values(meta.properties).forEach(prop => {
217
223
  if (prop.kind === ReferenceKind.MANY_TO_MANY && prop.pivotEntity) {
@@ -219,7 +225,7 @@ export class MetadataDiscovery {
219
225
  const target = typeof pivotEntity === 'function' && !pivotEntity.prototype
220
226
  ? pivotEntity()
221
227
  : pivotEntity;
222
- if (!this.#discovered.find(m => m.className === Utils.className(target))) {
228
+ if (!this.#discovered.find(m => m.className === Utils.className(target)) || !discoveredByIdentity(target)) {
223
229
  missing.push(target);
224
230
  }
225
231
  }
@@ -227,7 +233,8 @@ export class MetadataDiscovery {
227
233
  const target = typeof prop.entity === 'function' && !prop.entity.prototype ? prop.entity() : prop.type;
228
234
  if (!unwrap(prop.type)
229
235
  .split(/ ?\| ?/)
230
- .every(type => this.#discovered.find(m => m.className === type))) {
236
+ .every(type => this.#discovered.find(m => m.className === type)) ||
237
+ !Utils.asArray(target).every(discoveredByIdentity)) {
231
238
  missing.push(...Utils.asArray(target));
232
239
  }
233
240
  }
@@ -275,9 +282,11 @@ export class MetadataDiscovery {
275
282
  continue;
276
283
  }
277
284
  parent = Object.getPrototypeOf(meta.class);
278
- // Skip if parent is the auto-generated base class for the same entity (from setClass usage)
285
+ // Skip if parent is the auto-generated base class for the same entity (from setClass usage).
286
+ // A parent carrying its own decorator metadata is a real base class even when a minifier
287
+ // mangles it to the same name as the child.
279
288
  if (parent.name !== '' &&
280
- parent.name !== meta.className &&
289
+ (parent.name !== meta.className || Object.hasOwn(parent, MetadataStorage.META_SYMBOL)) &&
281
290
  !this.#metadata.has(parent) &&
282
291
  parent !== BaseEntity) {
283
292
  this.discoverReferences([parent], false);
@@ -311,7 +320,12 @@ export class MetadataDiscovery {
311
320
  const cls = entity;
312
321
  const path = cls[MetadataStorage.PATH_SYMBOL];
313
322
  if (path) {
314
- const meta = Utils.copy(MetadataStorage.getMetadata(cls.name, path), false);
323
+ // Prefer the metadata stored on the class reference, the `className-path` key can
324
+ // collide when a minifier mangles two classes to the same name.
325
+ const stored = Object.hasOwn(cls, MetadataStorage.META_SYMBOL)
326
+ ? cls[MetadataStorage.META_SYMBOL]
327
+ : MetadataStorage.getMetadata(cls.name, path);
328
+ const meta = Utils.copy(stored, false);
315
329
  meta.path = path;
316
330
  this.#metadata.set(cls, meta);
317
331
  }
@@ -1184,7 +1198,11 @@ export class MetadataDiscovery {
1184
1198
  return;
1185
1199
  }
1186
1200
  visited.add(embeddedProp);
1187
- const embeddable = this.#discovered.find(m => m.name === embeddedProp.type);
1201
+ // Prefer resolution via the class reference, the name can be ambiguous when a minifier
1202
+ // mangles two classes to the same name. Only named metadata counts, an auto-discovered
1203
+ // class without the `@Embeddable()` decorator should still fail as unknown below.
1204
+ const embeddable = this.#discovered.find(m => m.name && m.class === embeddedProp.target) ??
1205
+ this.#discovered.find(m => m.name === embeddedProp.type);
1188
1206
  if (!embeddable) {
1189
1207
  throw MetadataError.fromUnknownEntity(embeddedProp.type, `${meta.className}.${embeddedProp.name}`);
1190
1208
  }
@@ -92,7 +92,7 @@ export class MetadataProvider {
92
92
  if (!this.useCache()) {
93
93
  return undefined;
94
94
  }
95
- const cache = meta.path && this.config.getMetadataCacheAdapter().get(this.getCacheKey(meta));
95
+ const cache = meta.path && this.config.getMetadataCacheAdapter().get(this.getCacheKey(meta), meta.path);
96
96
  if (cache) {
97
97
  this.loadFromCache(meta, cache);
98
98
  meta.root = root;
@@ -1,13 +1,14 @@
1
- import { type Dictionary, EntityMetadata, type EntityName } from '../typings.js';
1
+ import { type Dictionary, type EntityCtor, EntityMetadata, type EntityName } from '../typings.js';
2
2
  import type { EntityManager } from '../EntityManager.js';
3
3
  /** Registry that stores and provides access to entity metadata by class, name, or id. */
4
4
  export declare class MetadataStorage {
5
5
  #private;
6
6
  static readonly PATH_SYMBOL: unique symbol;
7
+ static readonly META_SYMBOL: unique symbol;
7
8
  constructor(metadata?: Dictionary<EntityMetadata>);
8
- /** Returns the global metadata dictionary, or a specific entry by entity name and path. */
9
+ /** Returns the global metadata dictionary, or a specific entry by entity name and path (keyed by the class reference when `target` is provided). */
9
10
  static getMetadata(): Dictionary<EntityMetadata>;
10
- static getMetadata<T = any>(entity: string, path: string): EntityMetadata<T>;
11
+ static getMetadata<T = any>(entity: string, path: string, target?: EntityCtor): EntityMetadata<T>;
11
12
  /** Checks whether an entity with the given class name exists in the global metadata. */
12
13
  static isKnownEntity(name: string): boolean;
13
14
  /** Clears all entries from the global metadata registry. */
@@ -11,6 +11,7 @@ function getGlobalStorage(namespace) {
11
11
  /** Registry that stores and provides access to entity metadata by class, name, or id. */
12
12
  export class MetadataStorage {
13
13
  static PATH_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.PATH_SYMBOL');
14
+ static META_SYMBOL = Symbol.for('@mikro-orm/core/MetadataStorage.META_SYMBOL');
14
15
  static #metadata = getGlobalStorage('metadata');
15
16
  #metadataMap = new Map();
16
17
  #idMap;
@@ -26,8 +27,21 @@ export class MetadataStorage {
26
27
  this.#metadataMap.set(meta.class, meta);
27
28
  }
28
29
  }
29
- static getMetadata(entity, path) {
30
+ static getMetadata(entity, path, target) {
30
31
  const key = entity && path ? entity + '-' + Utils.hash(path) : null;
32
+ // Key the registry by the class reference when available, so two classes minified
33
+ // to the same mangled name don't collide on the `className-path` key.
34
+ if (key && target) {
35
+ if (!Object.hasOwn(target, MetadataStorage.META_SYMBOL)) {
36
+ Object.defineProperty(target, MetadataStorage.META_SYMBOL, {
37
+ value: new EntityMetadata({ className: entity, path }),
38
+ writable: true,
39
+ });
40
+ }
41
+ // Keep the name-keyed entry in sync, the class-keyed metadata survives `MetadataStorage.clear()`.
42
+ MetadataStorage.#metadata[key] = target[MetadataStorage.META_SYMBOL];
43
+ return target[MetadataStorage.META_SYMBOL];
44
+ }
31
45
  if (key && !MetadataStorage.#metadata[key]) {
32
46
  MetadataStorage.#metadata[key] = new EntityMetadata({ className: entity, path });
33
47
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "7.1.11-dev.1",
3
+ "version": "7.1.11-dev.3",
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/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.11-dev.1';
156
+ static #ORM_VERSION = '7.1.11-dev.3';
157
157
  /**
158
158
  * Checks if the argument is instance of `Object`. Returns false for arrays.
159
159
  */