@mikro-orm/core 7.0.0-dev.33 → 7.0.0-dev.35

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.33",
4
+ "version": "7.0.0-dev.35",
5
5
  "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.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
@@ -54,7 +54,7 @@
54
54
  "dataloader": "2.2.3",
55
55
  "dotenv": "17.2.3",
56
56
  "esprima": "4.0.1",
57
- "mikro-orm": "7.0.0-dev.33",
57
+ "mikro-orm": "7.0.0-dev.35",
58
58
  "reflect-metadata": "0.2.2",
59
59
  "tinyglobby": "0.2.13"
60
60
  }
@@ -158,7 +158,7 @@ export declare abstract class Platform {
158
158
  getFullTextIndexExpression(indexName: string, schemaName: string | undefined, tableName: string, columns: SimpleColumnMeta[]): string;
159
159
  convertsJsonAutomatically(): boolean;
160
160
  convertJsonToDatabaseValue(value: unknown, context?: TransformContext): unknown;
161
- convertJsonToJSValue(value: unknown, prop: EntityProperty): unknown;
161
+ convertJsonToJSValue(value: unknown, context?: TransformContext): unknown;
162
162
  convertDateToJSValue(value: string | Date): string;
163
163
  convertIntervalToJSValue(value: string): unknown;
164
164
  convertIntervalToDatabaseValue(value: unknown): unknown;
@@ -195,7 +195,7 @@ export declare abstract class Platform {
195
195
  getDefaultPrimaryName(tableName: string, columns: string[]): string;
196
196
  supportsCustomPrimaryKeyNames(): boolean;
197
197
  isPopulated<T>(key: string, populate: readonly PopulateOptions<T>[] | boolean): boolean;
198
- shouldHaveColumn<T>(prop: EntityProperty<T>, populate: readonly PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean): boolean;
198
+ shouldHaveColumn<T>(prop: EntityProperty<T>, populate: readonly PopulateOptions<T>[] | boolean, exclude?: string[], includeFormulas?: boolean, ignoreInlineEmbeddables?: boolean): boolean;
199
199
  /**
200
200
  * Currently not supported due to how knex does complex sqlite diffing (always based on current schema)
201
201
  */
@@ -288,11 +288,7 @@ export class Platform {
288
288
  convertJsonToDatabaseValue(value, context) {
289
289
  return JSON.stringify(value);
290
290
  }
291
- convertJsonToJSValue(value, prop) {
292
- const isObjectEmbedded = prop.embedded && prop.object;
293
- if ((this.convertsJsonAutomatically() || isObjectEmbedded) && ['json', 'jsonb', this.getJsonDeclarationSQL()].includes(prop.columnTypes[0])) {
294
- return value;
295
- }
291
+ convertJsonToJSValue(value, context) {
296
292
  return parseJsonSafe(value);
297
293
  }
298
294
  convertDateToJSValue(value) {
@@ -455,7 +451,7 @@ export class Platform {
455
451
  isPopulated(key, populate) {
456
452
  return populate === true || (populate !== false && populate.some(p => p.field === key || p.all));
457
453
  }
458
- shouldHaveColumn(prop, populate, exclude, includeFormulas = true) {
454
+ shouldHaveColumn(prop, populate, exclude, includeFormulas = true, ignoreInlineEmbeddables = true) {
459
455
  if (exclude?.includes(prop.name)) {
460
456
  return false;
461
457
  }
@@ -475,7 +471,7 @@ export class Platform {
475
471
  return true;
476
472
  }
477
473
  if (prop.kind === ReferenceKind.EMBEDDED) {
478
- return !!prop.object;
474
+ return prop.object || ignoreInlineEmbeddables;
479
475
  }
480
476
  return prop.kind === ReferenceKind.ONE_TO_ONE && prop.owner;
481
477
  }
@@ -5,7 +5,7 @@ export declare class JsonType extends Type<unknown, string | null> {
5
5
  convertToDatabaseValue(value: unknown, platform: Platform, context?: TransformContext): string | null;
6
6
  convertToJSValueSQL(key: string, platform: Platform): string;
7
7
  convertToDatabaseValueSQL(key: string, platform: Platform): string;
8
- convertToJSValue(value: string | unknown, platform: Platform): unknown;
8
+ convertToJSValue(value: string | unknown, platform: Platform, context?: TransformContext): unknown;
9
9
  getColumnType(prop: EntityProperty, platform: Platform): string;
10
10
  ensureComparable<T extends object>(meta: EntityMetadata<T>, prop: EntityProperty<T>): boolean;
11
11
  compareAsType(): string;
package/types/JsonType.js CHANGED
@@ -12,8 +12,13 @@ export class JsonType extends Type {
12
12
  convertToDatabaseValueSQL(key, platform) {
13
13
  return key + platform.castColumn(this.prop);
14
14
  }
15
- convertToJSValue(value, platform) {
16
- return platform.convertJsonToJSValue(value, this.prop);
15
+ convertToJSValue(value, platform, context) {
16
+ const isJsonColumn = ['json', 'jsonb', platform.getJsonDeclarationSQL()].includes(this.prop.columnTypes[0]);
17
+ const isObjectEmbedded = this.prop.embedded && this.prop.object;
18
+ if ((platform.convertsJsonAutomatically() || isObjectEmbedded) && isJsonColumn && !context?.force) {
19
+ return value;
20
+ }
21
+ return platform.convertJsonToJSValue(value, context);
17
22
  }
18
23
  getColumnType(prop, platform) {
19
24
  return platform.getJsonDeclarationSQL();
package/types/Type.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { Platform } from '../platforms/Platform.js';
3
3
  import type { Constructor, EntityMetadata, EntityProperty } from '../typings.js';
4
4
  export interface TransformContext {
5
5
  fromQuery?: boolean;
6
+ force?: boolean;
6
7
  key?: string;
7
8
  mode?: 'hydration' | 'query' | 'query-data' | 'discovery' | 'serialization';
8
9
  }
@@ -23,7 +24,7 @@ export declare abstract class Type<JSType = string, DBType = JSType> {
23
24
  /**
24
25
  * Converts a value from its database representation to its JS representation of this type.
25
26
  */
26
- convertToJSValue(value: DBType, platform: Platform): JSType;
27
+ convertToJSValue(value: DBType, platform: Platform, context?: TransformContext): JSType;
27
28
  /**
28
29
  * Converts a value from its JS representation to its database representation of this type.
29
30
  */
package/types/Type.js CHANGED
@@ -13,7 +13,7 @@ export class Type {
13
13
  /**
14
14
  * Converts a value from its database representation to its JS representation of this type.
15
15
  */
16
- convertToJSValue(value, platform) {
16
+ convertToJSValue(value, platform, context) {
17
17
  return value;
18
18
  }
19
19
  /**
package/typings.d.ts CHANGED
@@ -91,7 +91,7 @@ type PrimaryPropToType<T, Keys extends (keyof T)[]> = {
91
91
  type ReadonlyPrimary<T> = T extends any[] ? Readonly<T> : T;
92
92
  export type Primary<T> = IsAny<T> extends true ? any : T extends {
93
93
  [PrimaryKeyProp]?: infer PK;
94
- } ? (PK extends keyof T ? ReadonlyPrimary<UnwrapPrimary<T[PK]>> : (PK extends (keyof T)[] ? ReadonlyPrimary<PrimaryPropToType<T, PK>> : PK)) : T extends {
94
+ } ? PK extends undefined ? Omit<T, typeof PrimaryKeyProp> : PK extends keyof T ? ReadonlyPrimary<UnwrapPrimary<T[PK]>> : PK extends (keyof T)[] ? ReadonlyPrimary<PrimaryPropToType<T, PK>> : PK : T extends {
95
95
  _id?: infer PK;
96
96
  } ? ReadonlyPrimary<PK> | string : T extends {
97
97
  id?: infer PK;
@@ -451,7 +451,7 @@ export interface EntityMetadata<T = any> {
451
451
  schema?: string;
452
452
  pivotTable?: boolean;
453
453
  virtual?: boolean;
454
- expression?: string | ((em: any, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>) => MaybePromise<RawQueryFragment | object | string>);
454
+ expression?: string | ((em: any, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>, stream?: boolean) => MaybePromise<RawQueryFragment | object | string>);
455
455
  discriminatorColumn?: EntityKey<T> | AnyString;
456
456
  discriminatorValue?: number | string;
457
457
  discriminatorMap?: Dictionary<string>;
@@ -99,6 +99,10 @@ export class UnitOfWork {
99
99
  }
100
100
  data[prop.name] = Utils.getPrimaryKeyValues(data[prop.name], prop.targetMeta, true);
101
101
  }
102
+ if (prop.hydrate === false && prop.customType?.ensureComparable(wrapped.__meta, prop)) {
103
+ const converted = prop.customType.convertToJSValue(data[key], this.platform, { key, mode: 'hydration', force: true });
104
+ data[key] = prop.customType.convertToDatabaseValue(converted, this.platform, { key, mode: 'hydration' });
105
+ }
102
106
  if (forceUndefined) {
103
107
  if (data[key] === null) {
104
108
  data[key] = undefined;
@@ -194,6 +198,9 @@ export class UnitOfWork {
194
198
  if (this.queuedActions.has(meta.className) || this.queuedActions.has(meta.root.className)) {
195
199
  return true;
196
200
  }
201
+ if (meta.discriminatorMap && Object.values(meta.discriminatorMap).some(v => this.queuedActions.has(v))) {
202
+ return true;
203
+ }
197
204
  for (const entity of this.identityMap.getStore(meta).values()) {
198
205
  if (helper(entity).__initialized && helper(entity).isTouched()) {
199
206
  return true;
@@ -146,6 +146,7 @@ export declare class Configuration<D extends IDatabaseDriver = IDatabaseDriver,
146
146
  fileName: (className: string) => string;
147
147
  };
148
148
  preferReadReplicas: true;
149
+ dynamicImportProvider: (id: string) => Promise<any>;
149
150
  };
150
151
  private readonly options;
151
152
  private readonly logger;
@@ -412,6 +413,7 @@ export interface MikroORMOptions<D extends IDatabaseDriver = IDatabaseDriver, EM
412
413
  };
413
414
  seeder: SeederOptions;
414
415
  preferReadReplicas: boolean;
416
+ dynamicImportProvider: (id: string) => Promise<unknown>;
415
417
  hashAlgorithm: 'md5' | 'sha256';
416
418
  }
417
419
  export type Options<D extends IDatabaseDriver = IDatabaseDriver, EM extends D[typeof EntityManagerType] & EntityManager = D[typeof EntityManagerType] & EntityManager> = Pick<MikroORMOptions<D, EM>, Exclude<keyof MikroORMOptions<D, EM>, keyof typeof Configuration.DEFAULTS>> & Partial<MikroORMOptions<D, EM>>;
@@ -134,6 +134,7 @@ export class Configuration {
134
134
  fileName: (className) => className,
135
135
  },
136
136
  preferReadReplicas: true,
137
+ dynamicImportProvider: /* v8 ignore next */ (id) => import(id),
137
138
  };
138
139
  options;
139
140
  logger;
@@ -142,6 +143,9 @@ export class Configuration {
142
143
  cache = new Map();
143
144
  extensions = new Map();
144
145
  constructor(options, validate = true) {
146
+ if (options.dynamicImportProvider) {
147
+ Utils.setDynamicImportProvider(options.dynamicImportProvider);
148
+ }
145
149
  this.options = Utils.mergeConfig({}, Configuration.DEFAULTS, options);
146
150
  this.options.baseDir = Utils.absolutePath(this.options.baseDir);
147
151
  this.options.preferTs ??= options.preferTs;
@@ -542,10 +542,7 @@ export class EntityComparator {
542
542
  context.set('compareObjects', compareObjects);
543
543
  context.set('equals', equals);
544
544
  for (const prop of meta.comparableProps) {
545
- // skip properties that are not hydrated
546
- if (prop.hydrate !== false) {
547
- lines.push(this.getPropertyComparator(prop, context));
548
- }
545
+ lines.push(this.getPropertyComparator(prop, context));
549
546
  }
550
547
  // also compare 1:1 inverse sides, important for `factory.mergeData`
551
548
  lines.push(`if (options?.includeInverseSides) {`);
package/utils/Utils.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare function equals(a: any, b: any): boolean;
15
15
  export declare function parseJsonSafe<T = unknown>(value: unknown): T;
16
16
  export declare class Utils {
17
17
  static readonly PK_SEPARATOR = "~~~";
18
+ static dynamicImportProvider: (id: string) => Promise<any>;
18
19
  /**
19
20
  * Checks if the argument is not undefined
20
21
  */
@@ -236,6 +237,7 @@ export declare class Utils {
236
237
  */
237
238
  static resolveModulePath(id: string, from?: string): string;
238
239
  static dynamicImport<T = any>(id: string): Promise<T>;
240
+ static setDynamicImportProvider(provider: (id: string) => Promise<unknown>): void;
239
241
  static ensureDir(path: string): void;
240
242
  static pathExistsSync(path: string): boolean;
241
243
  static readJSONSync(path: string): Dictionary;
package/utils/Utils.js CHANGED
@@ -131,6 +131,8 @@ export function parseJsonSafe(value) {
131
131
  }
132
132
  export class Utils {
133
133
  static PK_SEPARATOR = '~~~';
134
+ /* v8 ignore next */
135
+ static dynamicImportProvider = (id) => import(id);
134
136
  /**
135
137
  * Checks if the argument is not undefined
136
138
  */
@@ -907,7 +909,11 @@ export class Utils {
907
909
  static async dynamicImport(id) {
908
910
  /* v8 ignore next */
909
911
  const specifier = id.startsWith('file://') ? id : pathToFileURL(id).href;
910
- return import(specifier);
912
+ return this.dynamicImportProvider(specifier);
913
+ }
914
+ /* v8 ignore next 3 */
915
+ static setDynamicImportProvider(provider) {
916
+ this.dynamicImportProvider = provider;
911
917
  }
912
918
  static ensureDir(path) {
913
919
  if (!existsSync(path)) {
@@ -1,7 +1,12 @@
1
- import type { EntityData, EntityMetadata } from '../typings.js';
1
+ import type { EntityData, EntityMetadata, FilterQuery } from '../typings.js';
2
2
  import type { UpsertOptions } from '../drivers/IDatabaseDriver.js';
3
- import type { RawQueryFragment } from '../utils/RawQueryFragment.js';
3
+ import { type RawQueryFragment } from '../utils/RawQueryFragment.js';
4
4
  /** @internal */
5
5
  export declare function getOnConflictFields<T>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T>): (keyof T)[];
6
6
  /** @internal */
7
7
  export declare function getOnConflictReturningFields<T, P extends string>(meta: EntityMetadata<T> | undefined, data: EntityData<T>, uniqueFields: (keyof T)[] | RawQueryFragment, options: UpsertOptions<T, P>): (keyof T)[] | '*';
8
+ /** @internal */
9
+ export declare function getWhereCondition<T extends object>(meta: EntityMetadata<T>, onConflictFields: (keyof T)[] | RawQueryFragment | undefined, data: EntityData<T>, where: FilterQuery<T>): {
10
+ where: FilterQuery<T>;
11
+ propIndex: number | false;
12
+ };
@@ -1,3 +1,5 @@
1
+ import { isRaw } from '../utils/RawQueryFragment.js';
2
+ import { Utils } from './Utils.js';
1
3
  function expandEmbeddedProperties(prop, key) {
2
4
  if (prop.object) {
3
5
  return [prop.name];
@@ -96,3 +98,44 @@ export function getOnConflictReturningFields(meta, data, uniqueFields, options)
96
98
  }
97
99
  return keys.filter(key => !(key in data));
98
100
  }
101
+ function getPropertyValue(obj, key) {
102
+ if (key.indexOf('.') === -1) {
103
+ return obj[key];
104
+ }
105
+ const parts = key.split('.');
106
+ let curr = obj;
107
+ for (let i = 0; i < parts.length - 1; i++) {
108
+ curr[parts[i]] ??= {};
109
+ curr = curr[parts[i]];
110
+ }
111
+ return curr[parts[parts.length - 1]];
112
+ }
113
+ /** @internal */
114
+ export function getWhereCondition(meta, onConflictFields, data, where) {
115
+ const unique = onConflictFields ?? meta.props.filter(p => p.unique).map(p => p.name);
116
+ const propIndex = !isRaw(unique) && unique.findIndex(p => data[p] ?? data[p.substring(0, p.indexOf('.'))] != null);
117
+ if (onConflictFields || where == null) {
118
+ if (propIndex !== false && propIndex >= 0) {
119
+ let key = unique[propIndex];
120
+ if (key.includes('.')) {
121
+ const prop = meta.properties[key.substring(0, key.indexOf('.'))];
122
+ if (prop) {
123
+ key = `${prop.fieldNames[0]}${key.substring(key.indexOf('.'))}`;
124
+ }
125
+ }
126
+ where = { [key]: getPropertyValue(data, unique[propIndex]) };
127
+ }
128
+ else if (meta.uniques.length > 0) {
129
+ for (const u of meta.uniques) {
130
+ if (Utils.asArray(u.properties).every(p => data[p] != null)) {
131
+ where = Utils.asArray(u.properties).reduce((o, key) => {
132
+ o[key] = data[key];
133
+ return o;
134
+ }, {});
135
+ break;
136
+ }
137
+ }
138
+ }
139
+ }
140
+ return { where, propIndex };
141
+ }