@mikro-orm/core 6.3.4-dev.1 → 6.3.4-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.
@@ -68,12 +68,14 @@ export interface FindAllOptions<T, P extends string = never, F extends string =
68
68
  where?: FilterQuery<T>;
69
69
  }
70
70
  export type FilterOptions = Dictionary<boolean | Dictionary> | string[] | boolean;
71
- export interface FindOptions<Entity, Hint extends string = never, Fields extends string = PopulatePath.ALL, Excludes extends string = never> {
71
+ export interface LoadHint<Entity, Hint extends string = never, Fields extends string = PopulatePath.ALL, Excludes extends string = never> {
72
72
  populate?: Populate<Entity, Hint>;
73
- populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
74
- populateOrderBy?: OrderDefinition<Entity>;
75
73
  fields?: readonly AutoPath<Entity, Fields, `${PopulatePath.ALL}`>[];
76
74
  exclude?: readonly AutoPath<Entity, Excludes>[];
75
+ }
76
+ export interface FindOptions<Entity, Hint extends string = never, Fields extends string = PopulatePath.ALL, Excludes extends string = never> extends LoadHint<Entity, Hint, Fields, Excludes> {
77
+ populateWhere?: ObjectQuery<Entity> | PopulateHint | `${PopulateHint}`;
78
+ populateOrderBy?: OrderDefinition<Entity>;
77
79
  orderBy?: OrderDefinition<Entity>;
78
80
  cache?: boolean | number | [string, number];
79
81
  limit?: number;
@@ -24,6 +24,7 @@ export declare class Collection<T extends object, O extends object = object> ext
24
24
  * Returns the Collection instance (itself), works the same as `Reference.load()`.
25
25
  */
26
26
  load<TT extends T, P extends string = never>(options?: InitCollectionOptions<TT, P>): Promise<LoadedCollection<Loaded<TT, P>>>;
27
+ private setSerializationContext;
27
28
  /**
28
29
  * Initializes the collection and returns the items
29
30
  */
@@ -37,12 +37,20 @@ class Collection extends ArrayCollection_1.ArrayCollection {
37
37
  if (this.isInitialized(true) && !options.refresh) {
38
38
  const em = this.getEntityManager(this.items, false);
39
39
  await em?.populate(this.items, options.populate, options);
40
+ this.setSerializationContext(options);
40
41
  }
41
42
  else {
42
43
  await this.init({ refresh: false, ...options });
43
44
  }
44
45
  return this;
45
46
  }
47
+ setSerializationContext(options) {
48
+ (0, wrap_1.helper)(this.owner).setSerializationContext({
49
+ populate: Array.isArray(options.populate)
50
+ ? options.populate.map(hint => `${this.property.name}.${hint}`)
51
+ : options.populate ?? [this.property.name],
52
+ });
53
+ }
46
54
  /**
47
55
  * Initializes the collection and returns the items
48
56
  */
@@ -86,6 +94,7 @@ class Collection extends ArrayCollection_1.ArrayCollection {
86
94
  }
87
95
  if (options.store) {
88
96
  this.hydrate(items, true);
97
+ this.setSerializationContext(options);
89
98
  this.populated();
90
99
  this.readonly = true;
91
100
  }
@@ -33,16 +33,11 @@ export declare class EntityLoader {
33
33
  populate<Entity extends object, Fields extends string = PopulatePath.ALL>(entityName: string, entities: Entity[], populate: PopulateOptions<Entity>[] | boolean, options: EntityLoaderOptions<Entity, Fields>): Promise<void>;
34
34
  normalizePopulate<Entity>(entityName: string, populate: (PopulateOptions<Entity> | boolean)[] | PopulateOptions<Entity> | boolean, strategy?: LoadStrategy, lookup?: boolean): PopulateOptions<Entity>[];
35
35
  private setSerializationContext;
36
- private expandDotPaths;
37
36
  /**
38
37
  * Merge multiple populates for the same entity with different children. Also skips `*` fields, those can come from
39
38
  * partial loading hints (`fields`) that are used to infer the `populate` hint if missing.
40
39
  */
41
40
  private mergeNestedPopulate;
42
- /**
43
- * Expands `books.perex` like populate to use `children` array instead of the dot syntax
44
- */
45
- private expandNestedPopulate;
46
41
  /**
47
42
  * preload everything in one call (this will update already existing references in IM)
48
43
  */
@@ -8,6 +8,7 @@ const enums_1 = require("../enums");
8
8
  const Reference_1 = require("./Reference");
9
9
  const wrap_1 = require("./wrap");
10
10
  const RawQueryFragment_1 = require("../utils/RawQueryFragment");
11
+ const utils_1 = require("./utils");
11
12
  class EntityLoader {
12
13
  em;
13
14
  metadata;
@@ -64,52 +65,24 @@ class EntityLoader {
64
65
  normalized = this.lookupAllRelationships(entityName);
65
66
  }
66
67
  // convert nested `field` with dot syntax to PopulateOptions with `children` array
67
- this.expandDotPaths(normalized, meta);
68
+ (0, utils_1.expandDotPaths)(meta, normalized, true);
68
69
  if (lookup && populate !== false) {
69
70
  normalized = this.lookupEagerLoadedRelationships(entityName, normalized, strategy);
70
71
  // convert nested `field` with dot syntax produced by eager relations
71
- this.expandDotPaths(normalized, meta);
72
+ (0, utils_1.expandDotPaths)(meta, normalized, true);
72
73
  }
73
74
  // merge same fields
74
75
  return this.mergeNestedPopulate(normalized);
75
76
  }
76
77
  setSerializationContext(entities, populate, options) {
77
- const exclude = options.exclude ?? [];
78
78
  for (const entity of entities) {
79
- const context = (0, wrap_1.helper)(entity).__serializationContext;
80
- context.populate = context.populate ? context.populate.concat(populate) : populate;
81
- context.exclude = context.exclude ? context.exclude.concat(exclude) : exclude;
82
- if (context.fields && options.fields) {
83
- options.fields.forEach(f => context.fields.add(f));
84
- }
85
- else if (options.fields) {
86
- context.fields = new Set(options.fields);
87
- }
88
- else {
89
- context.fields = new Set([enums_1.PopulatePath.ALL]);
90
- }
79
+ (0, wrap_1.helper)(entity).setSerializationContext({
80
+ populate,
81
+ fields: options.fields,
82
+ exclude: options.exclude,
83
+ });
91
84
  }
92
85
  }
93
- expandDotPaths(normalized, meta) {
94
- normalized.forEach(p => {
95
- if (!p.field.includes('.')) {
96
- return;
97
- }
98
- const [f, ...parts] = p.field.split('.');
99
- p.field = f;
100
- p.children ??= [];
101
- const prop = meta.properties[f];
102
- p.strategy ??= prop.strategy;
103
- if (parts[0] === enums_1.PopulatePath.ALL) {
104
- prop.targetMeta.props
105
- .filter(prop => prop.lazy || prop.kind !== enums_1.ReferenceKind.SCALAR)
106
- .forEach(prop => p.children.push({ field: prop.name, strategy: p.strategy }));
107
- }
108
- else {
109
- p.children.push(this.expandNestedPopulate(prop.type, parts, p.strategy, p.all));
110
- }
111
- });
112
- }
113
86
  /**
114
87
  * Merge multiple populates for the same entity with different children. Also skips `*` fields, those can come from
115
88
  * partial loading hints (`fields`) that are used to infer the `populate` hint if missing.
@@ -138,19 +111,6 @@ class EntityLoader {
138
111
  return item;
139
112
  });
140
113
  }
141
- /**
142
- * Expands `books.perex` like populate to use `children` array instead of the dot syntax
143
- */
144
- expandNestedPopulate(entityName, parts, strategy, all) {
145
- const meta = this.metadata.find(entityName);
146
- const field = parts.shift();
147
- const prop = meta.properties[field];
148
- const ret = { field, strategy, all };
149
- if (parts.length > 0) {
150
- ret.children = [this.expandNestedPopulate(prop.type, parts, strategy)];
151
- }
152
- return ret;
153
- }
154
114
  /**
155
115
  * preload everything in one call (this will update already existing references in IM)
156
116
  */
@@ -404,6 +364,9 @@ class EntityLoader {
404
364
  const where = options.where;
405
365
  const subCond = Utils_1.Utils.isPlainObject(where[prop.name]) ? where[prop.name] : {};
406
366
  const meta2 = this.metadata.find(prop.type);
367
+ if (!meta2) {
368
+ return {};
369
+ }
407
370
  const pk = Utils_1.Utils.getPrimaryKeyHash(meta2.primaryKeys);
408
371
  ['$and', '$or'].forEach(op => {
409
372
  if (where[op]) {
@@ -8,7 +8,7 @@ import type { EntityLoaderOptions } from './EntityLoader';
8
8
  import type { EntityIdentifier } from './EntityIdentifier';
9
9
  import type { SerializationContext } from '../serialization/SerializationContext';
10
10
  import { type SerializeOptions } from '../serialization/EntitySerializer';
11
- import type { FindOneOptions } from '../drivers/IDatabaseDriver';
11
+ import type { FindOneOptions, LoadHint } from '../drivers/IDatabaseDriver';
12
12
  export declare class WrappedEntity<Entity extends object> {
13
13
  __initialized: boolean;
14
14
  __touched: boolean;
@@ -17,14 +17,15 @@ export declare class WrappedEntity<Entity extends object> {
17
17
  __onLoadFired?: boolean;
18
18
  __schema?: string;
19
19
  __em?: EntityManager;
20
+ __loadedProperties: Set<string>;
21
+ __data: Dictionary;
22
+ __processing: boolean;
20
23
  __serializationContext: {
21
24
  root?: SerializationContext<Entity>;
22
25
  populate?: PopulateOptions<Entity>[];
23
- fields?: string[];
26
+ fields?: Set<string>;
27
+ exclude?: readonly string[];
24
28
  };
25
- __loadedProperties: Set<string>;
26
- __data: Dictionary;
27
- __processing: boolean;
28
29
  /** stores last known primary key, as its current state might be broken due to propagation/orphan removal, but we need to know the PK to be able t remove the entity */
29
30
  __pk?: Primary<Entity>;
30
31
  /** holds the reference wrapper instance (if created), so we can maintain the identity on reference wrappers too */
@@ -43,6 +44,7 @@ export declare class WrappedEntity<Entity extends object> {
43
44
  isTouched(): boolean;
44
45
  isManaged(): boolean;
45
46
  populated(populated?: boolean | undefined): void;
47
+ setSerializationContext<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
46
48
  toReference(): Ref<Entity> & LoadedReference<Loaded<Entity, AddEager<Entity>>>;
47
49
  toObject<Ignored extends EntityKey<Entity> = never>(ignoreFields?: Ignored[]): Omit<EntityDTO<Entity>, Ignored>;
48
50
  serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): EntityDTO<Loaded<Entity, Hint>>;
@@ -9,6 +9,7 @@ const Utils_1 = require("../utils/Utils");
9
9
  const errors_1 = require("../errors");
10
10
  const wrap_1 = require("./wrap");
11
11
  const EntitySerializer_1 = require("../serialization/EntitySerializer");
12
+ const utils_1 = require("./utils");
12
13
  class WrappedEntity {
13
14
  constructor(entity, hydrator, pkGetter, pkSerializer, pkGetterConverted) {
14
15
  this.entity = entity;
@@ -35,6 +36,22 @@ class WrappedEntity {
35
36
  populated(populated = true) {
36
37
  this.__populated = populated;
37
38
  }
39
+ setSerializationContext(options) {
40
+ const exclude = options.exclude ?? [];
41
+ const context = this.__serializationContext;
42
+ const populate = (0, utils_1.expandDotPaths)(this.__meta, options.populate);
43
+ context.populate = context.populate ? context.populate.concat(populate) : populate;
44
+ context.exclude = context.exclude ? context.exclude.concat(exclude) : exclude;
45
+ if (context.fields && options.fields) {
46
+ options.fields.forEach(f => context.fields.add(f));
47
+ }
48
+ else if (options.fields) {
49
+ context.fields = new Set(options.fields);
50
+ }
51
+ else {
52
+ context.fields = new Set(['*']);
53
+ }
54
+ }
38
55
  toReference() {
39
56
  this.__reference ??= new Reference_1.Reference(this.entity);
40
57
  return this.__reference;
@@ -0,0 +1,5 @@
1
+ import type { EntityMetadata, PopulateOptions } from '../typings';
2
+ /**
3
+ * @internal
4
+ */
5
+ export declare function expandDotPaths<Entity>(meta: EntityMetadata<Entity>, populate?: readonly (string | PopulateOptions<Entity>)[], normalized?: boolean): PopulateOptions<Entity>[];
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.expandDotPaths = expandDotPaths;
4
+ const enums_1 = require("../enums");
5
+ const core_1 = require("@mikro-orm/core");
6
+ /**
7
+ * Expands `books.perex` like populate to use `children` array instead of the dot syntax
8
+ */
9
+ function expandNestedPopulate(parentProp, parts, strategy, all) {
10
+ const meta = parentProp.targetMeta;
11
+ const field = parts.shift();
12
+ const prop = meta.properties[field];
13
+ const ret = { field, strategy, all };
14
+ if (parts.length > 0) {
15
+ ret.children = [expandNestedPopulate(prop, parts, strategy)];
16
+ }
17
+ return ret;
18
+ }
19
+ /**
20
+ * @internal
21
+ */
22
+ function expandDotPaths(meta, populate, normalized = false) {
23
+ const ret = normalized ? populate : core_1.Utils.asArray(populate).map(field => {
24
+ if (typeof field === 'string') {
25
+ return { field };
26
+ }
27
+ /* istanbul ignore next */
28
+ return typeof field === 'boolean' || field.field === enums_1.PopulatePath.ALL
29
+ ? { all: !!field, field: meta.primaryKeys[0] }
30
+ : field;
31
+ });
32
+ for (const p of ret) {
33
+ if (!p.field.includes('.')) {
34
+ continue;
35
+ }
36
+ const [f, ...parts] = p.field.split('.');
37
+ p.field = f;
38
+ p.children ??= [];
39
+ const prop = meta.properties[p.field];
40
+ p.strategy ??= prop.strategy;
41
+ if (parts[0] === enums_1.PopulatePath.ALL) {
42
+ prop.targetMeta.props
43
+ .filter(prop => prop.lazy || prop.kind !== enums_1.ReferenceKind.SCALAR)
44
+ .forEach(prop => p.children.push({ field: prop.name, strategy: p.strategy }));
45
+ }
46
+ else if (prop.kind === enums_1.ReferenceKind.EMBEDDED) {
47
+ ret.push({
48
+ ...p,
49
+ field: Object.values(prop.embeddedProps).find(c => c.embedded[1] === parts[0])?.name,
50
+ children: [],
51
+ });
52
+ p.children.push(expandNestedPopulate(prop, parts, p.strategy, p.all));
53
+ }
54
+ else {
55
+ p.children.push(expandNestedPopulate(prop, parts, p.strategy, p.all));
56
+ }
57
+ }
58
+ return ret;
59
+ }
@@ -896,10 +896,13 @@ class MetadataDiscovery {
896
896
  prop.hydrate = false;
897
897
  return;
898
898
  }
899
- prop = Utils_1.Utils.copy(prop, false);
900
- prop.nullable = true;
901
- prop.inherited = true;
902
- meta.root.addProperty(prop);
899
+ const newProp = Utils_1.Utils.copy(prop, false);
900
+ if (prop.enum && prop.items && meta.root.properties[prop.name]?.items) {
901
+ newProp.items = Utils_1.Utils.unique([...meta.root.properties[prop.name].items, ...prop.items]);
902
+ }
903
+ newProp.nullable = true;
904
+ newProp.inherited = true;
905
+ meta.root.addProperty(newProp);
903
906
  });
904
907
  meta.collection = meta.root.collection;
905
908
  meta.root.indexes = Utils_1.Utils.unique([...meta.root.indexes, ...meta.indexes]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
- "version": "6.3.4-dev.1",
3
+ "version": "6.3.4-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
  "main": "index.js",
6
6
  "module": "index.mjs",
@@ -64,7 +64,7 @@
64
64
  "esprima": "4.0.1",
65
65
  "fs-extra": "11.2.0",
66
66
  "globby": "11.1.0",
67
- "mikro-orm": "6.3.4-dev.1",
67
+ "mikro-orm": "6.3.4-dev.10",
68
68
  "reflect-metadata": "0.2.2"
69
69
  }
70
70
  }
package/typings.d.ts CHANGED
@@ -8,7 +8,7 @@ import type { Platform } from './platforms';
8
8
  import type { Configuration } from './utils';
9
9
  import type { EntityManager } from './EntityManager';
10
10
  import type { EventSubscriber } from './events';
11
- import type { FindOneOptions, FindOptions } from './drivers';
11
+ import type { FindOneOptions, FindOptions, LoadHint } from './drivers';
12
12
  export type Constructor<T = unknown> = new (...args: any[]) => T;
13
13
  export type Dictionary<T = any> = {
14
14
  [k: string]: T;
@@ -135,7 +135,7 @@ export interface IWrappedEntity<Entity extends object> {
135
135
  isManaged(): boolean;
136
136
  populated(populated?: boolean): void;
137
137
  populate<Hint extends string = never>(populate: AutoPath<Entity, Hint>[] | false, options?: EntityLoaderOptions<Entity>): Promise<Loaded<Entity, Hint>>;
138
- init<Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(options?: FindOneOptions<Entity, Hint, Fields, Excludes>): Promise<Loaded<Entity, Hint, Fields, Excludes> | null>;
138
+ init<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options?: FindOneOptions<Entity, Hint, Fields, Exclude>): Promise<Loaded<Entity, Hint, Fields, Exclude> | null>;
139
139
  toReference(): Ref<Entity> & LoadedReference<Loaded<Entity, AddEager<Entity>>>;
140
140
  toObject(): EntityDTO<Entity>;
141
141
  toObject(ignoreFields: never[]): EntityDTO<Entity>;
@@ -143,6 +143,7 @@ export interface IWrappedEntity<Entity extends object> {
143
143
  toJSON(...args: any[]): EntityDTO<Entity>;
144
144
  toPOJO(): EntityDTO<Entity>;
145
145
  serialize<Hint extends string = never, Exclude extends string = never>(options?: SerializeOptions<Entity, Hint, Exclude>): EntityDTO<Loaded<Entity, Hint>>;
146
+ setSerializationContext<Hint extends string = never, Fields extends string = '*', Exclude extends string = never>(options: LoadHint<Entity, Hint, Fields, Exclude>): void;
146
147
  assign<Naked extends FromEntityType<Entity> = FromEntityType<Entity>, Convert extends boolean = false, Data extends EntityData<Naked, Convert> | Partial<EntityDTO<Naked>> = EntityData<Naked, Convert> | Partial<EntityDTO<Naked>>>(data: Data & IsSubset<EntityData<Naked, Convert>, Data>, options?: AssignOptions<Convert>): MergeSelected<Entity, Naked, keyof Data & string>;
147
148
  getSchema(): string | undefined;
148
149
  setSchema(schema?: string): void;