@mikro-orm/core 7.0.0-dev.3 → 7.0.0-dev.30

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.
Files changed (103) hide show
  1. package/EntityManager.d.ts +50 -7
  2. package/EntityManager.js +141 -97
  3. package/MikroORM.js +0 -1
  4. package/README.md +1 -2
  5. package/cache/FileCacheAdapter.d.ts +2 -1
  6. package/cache/FileCacheAdapter.js +6 -4
  7. package/connections/Connection.d.ts +4 -2
  8. package/connections/Connection.js +2 -2
  9. package/decorators/Check.d.ts +2 -2
  10. package/decorators/Embeddable.d.ts +5 -5
  11. package/decorators/Embeddable.js +1 -1
  12. package/decorators/Embedded.d.ts +6 -12
  13. package/decorators/Entity.d.ts +20 -5
  14. package/decorators/Entity.js +0 -1
  15. package/decorators/Enum.d.ts +1 -1
  16. package/decorators/Formula.d.ts +1 -2
  17. package/decorators/Indexed.d.ts +10 -8
  18. package/decorators/Indexed.js +1 -1
  19. package/decorators/ManyToMany.d.ts +4 -2
  20. package/decorators/ManyToOne.d.ts +6 -2
  21. package/decorators/OneToMany.d.ts +4 -4
  22. package/decorators/OneToOne.d.ts +5 -1
  23. package/decorators/PrimaryKey.d.ts +2 -3
  24. package/decorators/Property.d.ts +1 -1
  25. package/decorators/Transactional.d.ts +1 -0
  26. package/decorators/Transactional.js +3 -3
  27. package/drivers/IDatabaseDriver.d.ts +8 -1
  28. package/entity/ArrayCollection.d.ts +4 -2
  29. package/entity/ArrayCollection.js +18 -6
  30. package/entity/Collection.d.ts +1 -2
  31. package/entity/Collection.js +16 -10
  32. package/entity/EntityAssigner.d.ts +1 -1
  33. package/entity/EntityAssigner.js +9 -1
  34. package/entity/EntityFactory.d.ts +6 -0
  35. package/entity/EntityFactory.js +21 -7
  36. package/entity/EntityHelper.js +8 -1
  37. package/entity/EntityLoader.d.ts +3 -2
  38. package/entity/EntityLoader.js +54 -35
  39. package/entity/EntityRepository.d.ts +1 -1
  40. package/entity/EntityValidator.js +1 -1
  41. package/entity/Reference.d.ts +8 -7
  42. package/entity/Reference.js +22 -1
  43. package/entity/WrappedEntity.js +1 -1
  44. package/entity/defineEntity.d.ts +537 -0
  45. package/entity/defineEntity.js +693 -0
  46. package/entity/index.d.ts +2 -0
  47. package/entity/index.js +2 -0
  48. package/entity/utils.d.ts +7 -0
  49. package/entity/utils.js +15 -3
  50. package/enums.d.ts +16 -3
  51. package/enums.js +13 -0
  52. package/errors.d.ts +6 -0
  53. package/errors.js +14 -0
  54. package/events/EventSubscriber.d.ts +3 -1
  55. package/hydration/ObjectHydrator.js +10 -2
  56. package/index.d.ts +1 -1
  57. package/index.js +1 -1
  58. package/metadata/EntitySchema.d.ts +6 -4
  59. package/metadata/EntitySchema.js +33 -19
  60. package/metadata/MetadataDiscovery.d.ts +0 -1
  61. package/metadata/MetadataDiscovery.js +51 -29
  62. package/metadata/MetadataStorage.js +1 -1
  63. package/metadata/MetadataValidator.js +4 -3
  64. package/package.json +5 -5
  65. package/platforms/Platform.d.ts +3 -1
  66. package/serialization/EntitySerializer.d.ts +2 -0
  67. package/serialization/EntitySerializer.js +1 -1
  68. package/serialization/SerializationContext.js +13 -10
  69. package/types/BigIntType.d.ts +9 -6
  70. package/types/BigIntType.js +3 -0
  71. package/types/BooleanType.d.ts +1 -1
  72. package/types/DecimalType.d.ts +6 -4
  73. package/types/DecimalType.js +1 -1
  74. package/types/DoubleType.js +1 -1
  75. package/typings.d.ts +72 -35
  76. package/typings.js +24 -4
  77. package/unit-of-work/ChangeSetComputer.js +3 -1
  78. package/unit-of-work/ChangeSetPersister.d.ts +4 -2
  79. package/unit-of-work/ChangeSetPersister.js +21 -11
  80. package/unit-of-work/UnitOfWork.d.ts +2 -1
  81. package/unit-of-work/UnitOfWork.js +71 -24
  82. package/utils/AbstractSchemaGenerator.js +5 -2
  83. package/utils/Configuration.d.ts +15 -5
  84. package/utils/Configuration.js +7 -7
  85. package/utils/ConfigurationLoader.d.ts +0 -2
  86. package/utils/ConfigurationLoader.js +2 -24
  87. package/utils/Cursor.d.ts +3 -3
  88. package/utils/Cursor.js +3 -0
  89. package/utils/DataloaderUtils.d.ts +7 -2
  90. package/utils/DataloaderUtils.js +38 -7
  91. package/utils/EntityComparator.d.ts +6 -2
  92. package/utils/EntityComparator.js +98 -59
  93. package/utils/QueryHelper.d.ts +6 -0
  94. package/utils/QueryHelper.js +48 -5
  95. package/utils/RawQueryFragment.d.ts +34 -0
  96. package/utils/RawQueryFragment.js +40 -1
  97. package/utils/TransactionManager.d.ts +65 -0
  98. package/utils/TransactionManager.js +220 -0
  99. package/utils/Utils.d.ts +11 -7
  100. package/utils/Utils.js +67 -33
  101. package/utils/index.d.ts +1 -0
  102. package/utils/index.js +1 -0
  103. package/utils/upsert-utils.js +9 -1
package/entity/index.d.ts CHANGED
@@ -11,3 +11,5 @@ export * from './Reference.js';
11
11
  export * from './BaseEntity.js';
12
12
  export * from './WrappedEntity.js';
13
13
  export * from './wrap.js';
14
+ export * from './defineEntity.js';
15
+ export * from './utils.js';
package/entity/index.js CHANGED
@@ -11,3 +11,5 @@ export * from './Reference.js';
11
11
  export * from './BaseEntity.js';
12
12
  export * from './WrappedEntity.js';
13
13
  export * from './wrap.js';
14
+ export * from './defineEntity.js';
15
+ export * from './utils.js';
package/entity/utils.d.ts CHANGED
@@ -1,5 +1,12 @@
1
1
  import type { EntityMetadata, PopulateOptions } from '../typings.js';
2
+ import { LoadStrategy, ReferenceKind } from '../enums.js';
2
3
  /**
3
4
  * @internal
4
5
  */
5
6
  export declare function expandDotPaths<Entity>(meta: EntityMetadata<Entity>, populate?: readonly (string | PopulateOptions<Entity>)[], normalized?: boolean): PopulateOptions<Entity>[];
7
+ /**
8
+ * Returns the loading strategy based on the provided hint.
9
+ * If `BALANCED` strategy is used, it will return JOINED if the property is a to-one relation.
10
+ * @internal
11
+ */
12
+ export declare function getLoadingStrategy(strategy: LoadStrategy | `${LoadStrategy}`, kind: ReferenceKind): LoadStrategy.SELECT_IN | LoadStrategy.JOINED;
package/entity/utils.js CHANGED
@@ -1,5 +1,5 @@
1
- import { PopulatePath, ReferenceKind } from '../enums.js';
2
- import { Utils } from '@mikro-orm/core';
1
+ import { LoadStrategy, PopulatePath, ReferenceKind } from '../enums.js';
2
+ import { Utils } from '../utils/Utils.js';
3
3
  /**
4
4
  * Expands `books.perex` like populate to use `children` array instead of the dot syntax
5
5
  */
@@ -34,7 +34,6 @@ export function expandDotPaths(meta, populate, normalized = false) {
34
34
  p.field = f;
35
35
  p.children ??= [];
36
36
  const prop = meta.properties[p.field];
37
- p.strategy ??= prop.strategy;
38
37
  if (parts[0] === PopulatePath.ALL) {
39
38
  prop.targetMeta.props
40
39
  .filter(prop => prop.lazy || prop.kind !== ReferenceKind.SCALAR)
@@ -55,3 +54,16 @@ export function expandDotPaths(meta, populate, normalized = false) {
55
54
  }
56
55
  return ret;
57
56
  }
57
+ /**
58
+ * Returns the loading strategy based on the provided hint.
59
+ * If `BALANCED` strategy is used, it will return JOINED if the property is a to-one relation.
60
+ * @internal
61
+ */
62
+ export function getLoadingStrategy(strategy, kind) {
63
+ if (strategy === LoadStrategy.BALANCED) {
64
+ return [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(kind)
65
+ ? LoadStrategy.JOINED
66
+ : LoadStrategy.SELECT_IN;
67
+ }
68
+ return strategy;
69
+ }
package/enums.d.ts CHANGED
@@ -66,7 +66,7 @@ export declare enum QueryOrderNumeric {
66
66
  ASC = 1,
67
67
  DESC = -1
68
68
  }
69
- export type QueryOrderKeysFlat = QueryOrder | QueryOrderNumeric | keyof typeof QueryOrder;
69
+ export type QueryOrderKeysFlat = QueryOrder | QueryOrderNumeric | `${QueryOrder}`;
70
70
  export type QueryOrderKeys<T> = QueryOrderKeysFlat | QueryOrderMap<T>;
71
71
  export type QueryOrderMap<T> = {
72
72
  [K in EntityKey<T>]?: QueryOrderKeys<ExpandProperty<T[K]>>;
@@ -85,7 +85,9 @@ export declare enum QueryFlag {
85
85
  INCLUDE_LAZY_FORMULAS = "INCLUDE_LAZY_FORMULAS",
86
86
  AUTO_JOIN_ONE_TO_ONE_OWNER = "AUTO_JOIN_ONE_TO_ONE_OWNER",
87
87
  INFER_POPULATE = "INFER_POPULATE",
88
- IDENTITY_INSERT = "IDENTITY_INSERT"
88
+ DISABLE_NESTED_INNER_JOIN = "DISABLE_NESTED_INNER_JOIN",
89
+ IDENTITY_INSERT = "IDENTITY_INSERT",// mssql only
90
+ OUTPUT_TABLE = "OUTPUT_TABLE"
89
91
  }
90
92
  export declare const SCALAR_TYPES: string[];
91
93
  export declare enum ReferenceKind {
@@ -108,7 +110,8 @@ export declare enum Cascade {
108
110
  }
109
111
  export declare enum LoadStrategy {
110
112
  SELECT_IN = "select-in",
111
- JOINED = "joined"
113
+ JOINED = "joined",
114
+ BALANCED = "balanced"
112
115
  }
113
116
  export declare enum DataloaderType {
114
117
  NONE = 0,
@@ -156,8 +159,18 @@ export declare enum EventType {
156
159
  }
157
160
  export declare const EventTypeMap: Record<EventType, number>;
158
161
  export type TransactionEventType = EventType.beforeTransactionStart | EventType.afterTransactionStart | EventType.beforeTransactionCommit | EventType.afterTransactionCommit | EventType.beforeTransactionRollback | EventType.afterTransactionRollback;
162
+ export declare enum TransactionPropagation {
163
+ REQUIRED = "required",
164
+ REQUIRES_NEW = "requires_new",
165
+ NESTED = "nested",
166
+ NOT_SUPPORTED = "not_supported",
167
+ SUPPORTS = "supports",
168
+ MANDATORY = "mandatory",
169
+ NEVER = "never"
170
+ }
159
171
  export interface TransactionOptions {
160
172
  ctx?: Transaction;
173
+ propagation?: TransactionPropagation;
161
174
  isolationLevel?: IsolationLevel;
162
175
  readOnly?: boolean;
163
176
  clear?: boolean;
package/enums.js CHANGED
@@ -95,7 +95,9 @@ export var QueryFlag;
95
95
  QueryFlag["INCLUDE_LAZY_FORMULAS"] = "INCLUDE_LAZY_FORMULAS";
96
96
  QueryFlag["AUTO_JOIN_ONE_TO_ONE_OWNER"] = "AUTO_JOIN_ONE_TO_ONE_OWNER";
97
97
  QueryFlag["INFER_POPULATE"] = "INFER_POPULATE";
98
+ QueryFlag["DISABLE_NESTED_INNER_JOIN"] = "DISABLE_NESTED_INNER_JOIN";
98
99
  QueryFlag["IDENTITY_INSERT"] = "IDENTITY_INSERT";
100
+ QueryFlag["OUTPUT_TABLE"] = "OUTPUT_TABLE";
99
101
  })(QueryFlag || (QueryFlag = {}));
100
102
  export const SCALAR_TYPES = ['string', 'number', 'boolean', 'bigint', 'Date', 'Buffer', 'RegExp'];
101
103
  export var ReferenceKind;
@@ -122,6 +124,7 @@ export var LoadStrategy;
122
124
  (function (LoadStrategy) {
123
125
  LoadStrategy["SELECT_IN"] = "select-in";
124
126
  LoadStrategy["JOINED"] = "joined";
127
+ LoadStrategy["BALANCED"] = "balanced";
125
128
  })(LoadStrategy || (LoadStrategy = {}));
126
129
  export var DataloaderType;
127
130
  (function (DataloaderType) {
@@ -175,6 +178,16 @@ export const EventTypeMap = Object.keys(EventType).reduce((a, b, i) => {
175
178
  a[b] = i;
176
179
  return a;
177
180
  }, {});
181
+ export var TransactionPropagation;
182
+ (function (TransactionPropagation) {
183
+ TransactionPropagation["REQUIRED"] = "required";
184
+ TransactionPropagation["REQUIRES_NEW"] = "requires_new";
185
+ TransactionPropagation["NESTED"] = "nested";
186
+ TransactionPropagation["NOT_SUPPORTED"] = "not_supported";
187
+ TransactionPropagation["SUPPORTS"] = "supports";
188
+ TransactionPropagation["MANDATORY"] = "mandatory";
189
+ TransactionPropagation["NEVER"] = "never";
190
+ })(TransactionPropagation || (TransactionPropagation = {}));
178
191
  export class PlainObject {
179
192
  }
180
193
  export var DeferMode;
package/errors.d.ts CHANGED
@@ -24,6 +24,7 @@ export declare class ValidationError<T extends AnyEntity = AnyEntity> extends Er
24
24
  static cannotCommit(): ValidationError;
25
25
  static cannotUseGlobalContext(): ValidationError;
26
26
  static cannotUseOperatorsInsideEmbeddables(className: string, propName: string, payload: unknown): ValidationError;
27
+ static cannotUseGroupOperatorsInsideScalars(className: string, propName: string, payload: unknown): ValidationError;
27
28
  static invalidEmbeddableQuery(className: string, propName: string, embeddableType: string): ValidationError;
28
29
  }
29
30
  export declare class CursorError<T extends AnyEntity = AnyEntity> extends ValidationError<T> {
@@ -66,3 +67,8 @@ export declare class NotFoundError<T extends AnyEntity = AnyEntity> extends Vali
66
67
  static findOneFailed(name: string, where: Dictionary | IPrimaryKey): NotFoundError;
67
68
  static findExactlyOneFailed(name: string, where: Dictionary | IPrimaryKey): NotFoundError;
68
69
  }
70
+ export declare class TransactionStateError extends ValidationError {
71
+ static requiredTransactionNotFound(propagation: string): TransactionStateError;
72
+ static transactionNotAllowed(propagation: string): TransactionStateError;
73
+ static invalidPropagation(propagation: string): TransactionStateError;
74
+ }
package/errors.js CHANGED
@@ -94,6 +94,9 @@ export class ValidationError extends Error {
94
94
  static cannotUseOperatorsInsideEmbeddables(className, propName, payload) {
95
95
  return new ValidationError(`Using operators inside embeddables is not allowed, move the operator above. (property: ${className}.${propName}, payload: ${inspect(payload)})`);
96
96
  }
97
+ static cannotUseGroupOperatorsInsideScalars(className, propName, payload) {
98
+ return new ValidationError(`Using group operators ($and/$or) inside scalar properties is not allowed, move the operator above. (property: ${className}.${propName}, payload: ${inspect(payload)})`);
99
+ }
97
100
  static invalidEmbeddableQuery(className, propName, embeddableType) {
98
101
  return new ValidationError(`Invalid query for entity '${className}', property '${propName}' does not exist in embeddable '${embeddableType}'`);
99
102
  }
@@ -220,3 +223,14 @@ export class NotFoundError extends ValidationError {
220
223
  return new NotFoundError(`Wrong number of ${name} entities found for query ${inspect(where)}, expected exactly one`);
221
224
  }
222
225
  }
226
+ export class TransactionStateError extends ValidationError {
227
+ static requiredTransactionNotFound(propagation) {
228
+ return new TransactionStateError(`No existing transaction found for transaction marked with propagation "${propagation}"`);
229
+ }
230
+ static transactionNotAllowed(propagation) {
231
+ return new TransactionStateError(`Existing transaction found for transaction marked with propagation "${propagation}"`);
232
+ }
233
+ static invalidPropagation(propagation) {
234
+ return new TransactionStateError(`Unsupported transaction propagation type: ${propagation}`);
235
+ }
236
+ }
@@ -13,7 +13,9 @@ export interface FlushEventArgs extends Omit<EventArgs<any>, 'entity' | 'changeS
13
13
  uow: UnitOfWork;
14
14
  }
15
15
  export interface TransactionEventArgs extends Omit<EventArgs<any>, 'entity' | 'meta' | 'changeSet'> {
16
- transaction?: Transaction;
16
+ transaction?: Transaction & {
17
+ savepointName?: string;
18
+ };
17
19
  uow?: UnitOfWork;
18
20
  }
19
21
  export interface EventSubscriber<T = any> {
@@ -63,7 +63,7 @@ export class ObjectHydrator extends Hydrator {
63
63
  const ret = [];
64
64
  const idx = this.tmpIndex++;
65
65
  const nullVal = this.config.get('forceUndefined') ? 'undefined' : 'null';
66
- if (prop.getter && !prop.setter) {
66
+ if (prop.getter && !prop.setter && prop.persist === false) {
67
67
  return [];
68
68
  }
69
69
  if (prop.ref) {
@@ -71,7 +71,14 @@ export class ObjectHydrator extends Hydrator {
71
71
  ret.push(` const oldValue_${idx} = entity${entityKey};`);
72
72
  }
73
73
  ret.push(` if (data${dataKey} === null) {`);
74
- ret.push(` entity${entityKey} = ${nullVal};`);
74
+ if (prop.ref) {
75
+ ret.push(` entity${entityKey} = new ScalarReference();`);
76
+ ret.push(` entity${entityKey}.bind(entity, '${prop.name}');`);
77
+ ret.push(` entity${entityKey}.set(${nullVal});`);
78
+ }
79
+ else {
80
+ ret.push(` entity${entityKey} = ${nullVal};`);
81
+ }
75
82
  ret.push(` } else if (typeof data${dataKey} !== 'undefined') {`);
76
83
  if (prop.customType) {
77
84
  registerCustomType(prop, convertorKey, 'convertToJSValue', context);
@@ -256,6 +263,7 @@ export class ObjectHydrator extends Hydrator {
256
263
  ...prop2,
257
264
  name: childProp.name,
258
265
  embedded: childProp.embedded,
266
+ embeddedProps: childProp.embeddedProps,
259
267
  };
260
268
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
261
269
  ret.push(...hydrateProperty(prop3, childProp.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
package/index.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * @module core
4
4
  */
5
- export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, } from './typings.js';
5
+ export { Constructor, ConnectionType, Dictionary, PrimaryKeyProp, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, EntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, EntityMetadata, QBFilterQuery, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, EntityRepositoryType, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, MigrationDiff, GenerateOptions, FilterObject, IEntityGenerator, ISeedManager, EntityClassGroup, OptionalProps, EagerProps, HiddenProps, RequiredEntityData, CheckCallback, IndexCallback, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, UmzugMigration, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, Opt, EntityType, FromEntityType, Selected, IsSubset, NoInfer, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, Hidden, FilterValue, MergeLoaded, MergeSelected, Config, DefineConfig, TypeConfig, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, } from './typings.js';
6
6
  export * from './enums.js';
7
7
  export * from './errors.js';
8
8
  export * from './exceptions.js';
package/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * @module core
4
4
  */
5
- export { PrimaryKeyProp, EntityMetadata, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, } from './typings.js';
5
+ export { PrimaryKeyProp, EntityMetadata, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Opt, Hidden, Config, DefineConfig, RequiredNullable, } from './typings.js';
6
6
  export * from './enums.js';
7
7
  export * from './errors.js';
8
8
  export * from './exceptions.js';
@@ -31,7 +31,7 @@ export type EntitySchemaProperty<Target, Owner> = ({
31
31
  kind: ReferenceKind.MANY_TO_MANY | 'm:n';
32
32
  } & TypeDef<Target> & ManyToManyOptions<Owner, Target>) | ({
33
33
  kind: ReferenceKind.EMBEDDED | 'embedded';
34
- } & EmbeddedTypeDef<Target> & EmbeddedOptions & PropertyOptions<Owner>) | ({
34
+ } & EmbeddedTypeDef<Target> & EmbeddedOptions<Owner, Target> & PropertyOptions<Owner>) | ({
35
35
  enum: true;
36
36
  } & EnumOptions<Owner>) | (TypeDef<Target> & PropertyOptions<Owner>);
37
37
  type OmitBaseProps<Entity, Base> = IsNever<Base> extends true ? Entity : Omit<Entity, keyof Base>;
@@ -63,13 +63,13 @@ export declare class EntitySchema<Entity = any, Base = never> {
63
63
  addVersion(name: EntityKey<Entity>, type: TypeType, options?: PropertyOptions<Entity>): void;
64
64
  addPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: PrimaryKeyOptions<Entity>): void;
65
65
  addSerializedPrimaryKey(name: EntityKey<Entity>, type: TypeType, options?: SerializedPrimaryKeyOptions<Entity>): void;
66
- addEmbedded<Target = AnyEntity>(name: EntityKey<Entity>, options: EmbeddedOptions): void;
66
+ addEmbedded<Target = AnyEntity>(name: EntityKey<Entity>, options: EmbeddedOptions<Entity, Target>): void;
67
67
  addManyToOne<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: ManyToOneOptions<Entity, Target>): void;
68
68
  addManyToMany<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: ManyToManyOptions<Entity, Target>): void;
69
69
  addOneToMany<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: OneToManyOptions<Entity, Target>): void;
70
70
  addOneToOne<Target = AnyEntity>(name: EntityKey<Entity>, type: TypeType, options: OneToOneOptions<Entity, Target>): void;
71
- addIndex(options: IndexOptions<Entity>): void;
72
- addUnique(options: UniqueOptions<Entity>): void;
71
+ addIndex<Key extends string>(options: IndexOptions<Entity, Key>): void;
72
+ addUnique<Key extends string>(options: UniqueOptions<Entity, Key>): void;
73
73
  setCustomRepository(repository: () => Constructor): void;
74
74
  setExtends(base: string | EntitySchema): void;
75
75
  setClass(proto: EntityClass<Entity>): void;
@@ -83,5 +83,7 @@ export declare class EntitySchema<Entity = any, Base = never> {
83
83
  private initPrimaryKeys;
84
84
  private normalizeType;
85
85
  private createProperty;
86
+ private rename;
87
+ private renameCompositeOptions;
86
88
  }
87
89
  export {};
@@ -34,23 +34,7 @@ export class EntitySchema {
34
34
  return schema;
35
35
  }
36
36
  addProperty(name, type, options = {}) {
37
- const rename = (data, from, to) => {
38
- if (from in options && !(to in options)) {
39
- // @ts-ignore
40
- options[to] = [options[from]];
41
- // @ts-ignore
42
- delete options[from];
43
- }
44
- };
45
- if (name !== options.name) {
46
- Utils.renameKey(options, 'name', 'fieldName');
47
- }
48
- rename(options, 'fieldName', 'fieldNames');
49
- rename(options, 'ref', 'ref');
50
- rename(options, 'joinColumn', 'joinColumns');
51
- rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
52
- rename(options, 'referenceColumnName', 'referencedColumnNames');
53
- rename(options, 'columnType', 'columnTypes');
37
+ this.renameCompositeOptions(name, options);
54
38
  const prop = { name, kind: ReferenceKind.SCALAR, ...options, type: this.normalizeType(options, type) };
55
39
  if (type && Type.isMappedType(type.prototype)) {
56
40
  prop.type = type;
@@ -94,6 +78,7 @@ export class EntitySchema {
94
78
  this.addProperty(name, type, options);
95
79
  }
96
80
  addEmbedded(name, options) {
81
+ this.renameCompositeOptions(name, options);
97
82
  Utils.defaultValue(options, 'prefix', true);
98
83
  if (options.array) {
99
84
  options.object = true; // force object mode for arrays
@@ -114,6 +99,8 @@ export class EntitySchema {
114
99
  if (prop.fieldNames && !prop.joinColumns) {
115
100
  prop.joinColumns = prop.fieldNames;
116
101
  }
102
+ // By default, the foreign key constraint is created on the relation
103
+ Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
117
104
  this.addProperty(name, type, prop);
118
105
  }
119
106
  addManyToMany(name, type, options) {
@@ -123,6 +110,8 @@ export class EntitySchema {
123
110
  }
124
111
  if (options.owner) {
125
112
  Utils.renameKey(options, 'mappedBy', 'inversedBy');
113
+ // By default, the foreign key constraint is created on the relation
114
+ Utils.defaultValue(options, 'createForeignKeyConstraint', true);
126
115
  }
127
116
  const prop = this.createProperty(ReferenceKind.MANY_TO_MANY, options);
128
117
  this.addProperty(name, type, prop);
@@ -135,8 +124,12 @@ export class EntitySchema {
135
124
  const prop = this.createProperty(ReferenceKind.ONE_TO_ONE, options);
136
125
  Utils.defaultValue(prop, 'owner', !!prop.inversedBy || !prop.mappedBy);
137
126
  Utils.defaultValue(prop, 'unique', prop.owner);
138
- if (prop.owner && options.mappedBy) {
139
- Utils.renameKey(prop, 'mappedBy', 'inversedBy');
127
+ if (prop.owner) {
128
+ if (options.mappedBy) {
129
+ Utils.renameKey(prop, 'mappedBy', 'inversedBy');
130
+ }
131
+ // By default, the foreign key constraint is created on the relation
132
+ Utils.defaultValue(prop, 'createForeignKeyConstraint', true);
140
133
  }
141
134
  if (prop.joinColumns && !prop.fieldNames) {
142
135
  prop.fieldNames = prop.joinColumns;
@@ -289,4 +282,25 @@ export class EntitySchema {
289
282
  ...options,
290
283
  };
291
284
  }
285
+ rename(data, from, to) {
286
+ if (from in data && !(to in data)) {
287
+ // @ts-ignore
288
+ data[to] = [data[from]];
289
+ // @ts-ignore
290
+ delete data[from];
291
+ }
292
+ }
293
+ renameCompositeOptions(name, options = {}) {
294
+ if (name !== options.name && !options.fieldNames) {
295
+ Utils.renameKey(options, 'name', 'fieldName');
296
+ }
297
+ else if (options.name && (options.fieldNames?.length ?? 0) > 1) {
298
+ delete options.name;
299
+ }
300
+ this.rename(options, 'fieldName', 'fieldNames');
301
+ this.rename(options, 'joinColumn', 'joinColumns');
302
+ this.rename(options, 'inverseJoinColumn', 'inverseJoinColumns');
303
+ this.rename(options, 'referenceColumnName', 'referencedColumnNames');
304
+ this.rename(options, 'columnType', 'columnTypes');
305
+ }
292
306
  }
@@ -54,7 +54,6 @@ export declare class MetadataDiscovery {
54
54
  private initAutoincrement;
55
55
  private initCheckConstraints;
56
56
  private initGeneratedColumn;
57
- private createColumnMappingObject;
58
57
  private getDefaultVersionValue;
59
58
  private inferDefaultValue;
60
59
  private initDefaultValue;
@@ -1,5 +1,5 @@
1
1
  import { basename, extname } from 'node:path';
2
- import globby from 'globby';
2
+ import { glob } from 'tinyglobby';
3
3
  import { EntityMetadata, } from '../typings.js';
4
4
  import { Utils } from '../utils/Utils.js';
5
5
  import { MetadataValidator } from './MetadataValidator.js';
@@ -7,7 +7,7 @@ import { MetadataStorage } from './MetadataStorage.js';
7
7
  import { EntitySchema } from './EntitySchema.js';
8
8
  import { Cascade, ReferenceKind } from '../enums.js';
9
9
  import { MetadataError } from '../errors.js';
10
- import { ArrayType, BigIntType, BlobType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
10
+ import { ArrayType, BigIntType, BlobType, DateType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
11
11
  import { colors } from '../logging/colors.js';
12
12
  import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
13
13
  export class MetadataDiscovery {
@@ -181,7 +181,7 @@ export class MetadataDiscovery {
181
181
  }
182
182
  async discoverDirectories(paths) {
183
183
  paths = paths.map(path => Utils.normalizePath(path));
184
- const files = await globby(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
184
+ const files = await glob(paths, { cwd: Utils.normalizePath(this.config.get('baseDir')) });
185
185
  this.logger.log('discovery', `- processing ${colors.cyan('' + files.length)} files`);
186
186
  const found = [];
187
187
  for (const filepath of files) {
@@ -264,7 +264,8 @@ export class MetadataDiscovery {
264
264
  // initialize global metadata for given entity
265
265
  MetadataStorage.getMetadata(entity.meta.className, filepath);
266
266
  }
267
- return entity;
267
+ const meta = Utils.copy(entity.meta, false);
268
+ return EntitySchema.fromMetadata(meta);
268
269
  }
269
270
  const path = entity[MetadataStorage.PATH_SYMBOL];
270
271
  if (path) {
@@ -618,7 +619,7 @@ export class MetadataDiscovery {
618
619
  }
619
620
  data.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.className, targetType + '_inverse', true, meta.className === targetType);
620
621
  data.properties[targetType + '_inverse'] = this.definePivotProperty(prop, targetType + '_inverse', targetType, meta.name + '_owner', false, meta.className === targetType);
621
- return this.metadata.set(data.className, data);
622
+ return this.metadata.set(data.className, EntitySchema.fromMetadata(data).init().meta);
622
623
  }
623
624
  defineFixedOrderProperty(prop, targetType) {
624
625
  const pk = prop.fixedOrderColumn || this.namingStrategy.referenceColumnName();
@@ -653,6 +654,7 @@ export class MetadataDiscovery {
653
654
  autoincrement: false,
654
655
  updateRule: prop.updateRule,
655
656
  deleteRule: prop.deleteRule,
657
+ createForeignKeyConstraint: prop.createForeignKeyConstraint,
656
658
  };
657
659
  if (selfReferencing && !this.platform.supportsMultipleCascadePaths()) {
658
660
  ret.updateRule ??= 'no action';
@@ -765,6 +767,7 @@ export class MetadataDiscovery {
765
767
  delete prop.default;
766
768
  if (properties[prop.name] && properties[prop.name].type !== prop.type) {
767
769
  properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
770
+ properties[prop.name].runtimeType = 'any';
768
771
  return properties[prop.name];
769
772
  }
770
773
  return properties[prop.name] = prop;
@@ -819,9 +822,16 @@ export class MetadataDiscovery {
819
822
  }
820
823
  return prop.embedded ? isParentObject(meta.properties[prop.embedded[0]]) : false;
821
824
  };
825
+ const isParentArray = (prop) => {
826
+ if (prop.array) {
827
+ return true;
828
+ }
829
+ return prop.embedded ? isParentArray(meta.properties[prop.embedded[0]]) : false;
830
+ };
822
831
  const rootProperty = getRootProperty(embeddedProp);
823
832
  const parentProperty = meta.properties[embeddedProp.embedded?.[0] ?? ''];
824
833
  const object = isParentObject(embeddedProp);
834
+ const array = isParentArray(embeddedProp);
825
835
  this.initFieldName(embeddedProp, rootProperty !== embeddedProp && object);
826
836
  // the prefix of the parent cannot be a boolean; it already passed here
827
837
  const prefix = this.getPrefix(embeddedProp, parentProperty);
@@ -834,7 +844,8 @@ export class MetadataDiscovery {
834
844
  meta.propertyOrder.set(name, (order += 0.01));
835
845
  embeddedProp.embeddedProps[prop.name] = meta.properties[name];
836
846
  meta.properties[name].persist ??= embeddedProp.persist;
837
- if (embeddedProp.nullable) {
847
+ const refInArray = array && [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.owner;
848
+ if (embeddedProp.nullable || refInArray) {
838
849
  meta.properties[name].nullable = true;
839
850
  }
840
851
  if (meta.properties[name].fieldNames) {
@@ -872,6 +883,7 @@ export class MetadataDiscovery {
872
883
  meta.properties[name].persist = false; // only virtual as we store the whole object
873
884
  meta.properties[name].userDefined = false; // mark this as a generated/internal property, so we can distinguish from user-defined non-persist properties
874
885
  meta.properties[name].object = true;
886
+ this.initCustomType(meta, meta.properties[name], false, true);
875
887
  }
876
888
  this.initEmbeddables(meta, meta.properties[name], visited);
877
889
  }
@@ -905,11 +917,13 @@ export class MetadataDiscovery {
905
917
  }
906
918
  if (!meta.root.discriminatorMap) {
907
919
  meta.root.discriminatorMap = {};
908
- const children = metadata.filter(m => m.root.className === meta.root.className && !m.abstract);
909
- children.forEach(m => {
920
+ const children = metadata
921
+ .filter(m => m.root.className === meta.root.className && !m.abstract)
922
+ .sort((a, b) => a.className.localeCompare(b.className));
923
+ for (const m of children) {
910
924
  const name = m.discriminatorValue ?? this.namingStrategy.classToTableName(m.className);
911
925
  meta.root.discriminatorMap[name] = m.className;
912
- });
926
+ }
913
927
  }
914
928
  meta.discriminatorValue = Object.entries(meta.root.discriminatorMap).find(([, className]) => className === meta.className)?.[0];
915
929
  if (!meta.root.properties[meta.root.discriminatorColumn]) {
@@ -922,7 +936,7 @@ export class MetadataDiscovery {
922
936
  }
923
937
  let i = 1;
924
938
  Object.values(meta.properties).forEach(prop => {
925
- const newProp = Utils.copy(prop, false);
939
+ const newProp = { ...prop };
926
940
  if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
927
941
  const name = newProp.name;
928
942
  this.initFieldName(newProp, newProp.object);
@@ -944,6 +958,7 @@ export class MetadataDiscovery {
944
958
  meta.collection = meta.root.collection;
945
959
  meta.root.indexes = Utils.unique([...meta.root.indexes, ...meta.indexes]);
946
960
  meta.root.uniques = Utils.unique([...meta.root.uniques, ...meta.uniques]);
961
+ meta.root.checks = Utils.unique([...meta.root.checks, ...meta.checks]);
947
962
  }
948
963
  createDiscriminatorProperty(meta) {
949
964
  meta.addProperty({
@@ -962,7 +977,7 @@ export class MetadataDiscovery {
962
977
  }
963
978
  }
964
979
  initCheckConstraints(meta) {
965
- const map = this.createColumnMappingObject(meta);
980
+ const map = meta.createColumnMappingObject();
966
981
  for (const check of meta.checks) {
967
982
  const columns = check.property ? meta.properties[check.property].fieldNames : [];
968
983
  check.name ??= this.namingStrategy.indexName(meta.tableName, columns, 'check');
@@ -997,20 +1012,12 @@ export class MetadataDiscovery {
997
1012
  }
998
1013
  return;
999
1014
  }
1000
- const map = this.createColumnMappingObject(meta);
1015
+ const map = meta.createColumnMappingObject();
1001
1016
  if (prop.generated instanceof Function) {
1002
1017
  prop.generated = prop.generated(map);
1003
1018
  }
1004
1019
  }
1005
- createColumnMappingObject(meta) {
1006
- return Object.values(meta.properties).reduce((o, prop) => {
1007
- if (prop.fieldNames) {
1008
- o[prop.name] = prop.fieldNames[0];
1009
- }
1010
- return o;
1011
- }, {});
1012
- }
1013
- getDefaultVersionValue(prop) {
1020
+ getDefaultVersionValue(meta, prop) {
1014
1021
  if (typeof prop.defaultRaw !== 'undefined') {
1015
1022
  return prop.defaultRaw;
1016
1023
  }
@@ -1018,7 +1025,9 @@ export class MetadataDiscovery {
1018
1025
  if (prop.default != null) {
1019
1026
  return '' + this.platform.quoteVersionValue(prop.default, prop);
1020
1027
  }
1021
- if (prop.type.toLowerCase() === 'date') {
1028
+ this.initCustomType(meta, prop, true);
1029
+ const type = prop.customType?.runtimeType ?? prop.runtimeType ?? prop.type;
1030
+ if (type === 'Date') {
1022
1031
  prop.length ??= this.platform.getDefaultVersionLength();
1023
1032
  return this.platform.getCurrentTimestampSQL(prop.length);
1024
1033
  }
@@ -1061,7 +1070,7 @@ export class MetadataDiscovery {
1061
1070
  prop.defaultRaw = this.platform.formatQuery(raw.sql, raw.params);
1062
1071
  return;
1063
1072
  }
1064
- if (prop.customType instanceof ArrayType && Array.isArray(prop.default)) {
1073
+ if (Array.isArray(prop.default) && prop.customType) {
1065
1074
  val = prop.customType.convertToDatabaseValue(prop.default, this.platform);
1066
1075
  }
1067
1076
  prop.defaultRaw = typeof val === 'string' ? `'${val}'` : '' + val;
@@ -1089,13 +1098,13 @@ export class MetadataDiscovery {
1089
1098
  if (prop.version) {
1090
1099
  this.initDefaultValue(prop);
1091
1100
  meta.versionProperty = prop.name;
1092
- prop.defaultRaw = this.getDefaultVersionValue(prop);
1101
+ prop.defaultRaw = this.getDefaultVersionValue(meta, prop);
1093
1102
  }
1094
1103
  if (prop.concurrencyCheck && !prop.primary) {
1095
1104
  meta.concurrencyCheckKeys.add(prop.name);
1096
1105
  }
1097
1106
  }
1098
- initCustomType(meta, prop) {
1107
+ initCustomType(meta, prop, simple = false, objectEmbeddable = false) {
1099
1108
  // `prop.type` might be actually instance of custom type class
1100
1109
  if (Type.isMappedType(prop.type) && !prop.customType) {
1101
1110
  prop.customType = prop.type;
@@ -1106,21 +1115,31 @@ export class MetadataDiscovery {
1106
1115
  prop.customType = new prop.type();
1107
1116
  prop.type = prop.customType.constructor.name;
1108
1117
  }
1118
+ if (simple) {
1119
+ return;
1120
+ }
1109
1121
  if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
1110
1122
  prop.customType = new JsonType();
1111
1123
  }
1112
1124
  if (prop.kind === ReferenceKind.SCALAR && !prop.customType && prop.columnTypes && ['json', 'jsonb'].includes(prop.columnTypes[0])) {
1113
1125
  prop.customType = new JsonType();
1114
1126
  }
1127
+ if (prop.kind === ReferenceKind.EMBEDDED && !prop.customType && (prop.object || prop.array)) {
1128
+ prop.customType = new JsonType();
1129
+ }
1115
1130
  if (!prop.customType && prop.array && prop.items) {
1116
1131
  prop.customType = new EnumArrayType(`${meta.className}.${prop.name}`, prop.items);
1117
1132
  }
1133
+ const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
1134
+ if (objectEmbeddable && !prop.customType && isArray) {
1135
+ prop.customType = new JsonType();
1136
+ }
1118
1137
  // for number arrays we make sure to convert the items to numbers
1119
1138
  if (!prop.customType && prop.type === 'number[]') {
1120
1139
  prop.customType = new ArrayType(i => +i);
1121
1140
  }
1122
1141
  // `string[]` can be returned via ts-morph, while reflect metadata will give us just `array`
1123
- if (!prop.customType && (prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]'))) {
1142
+ if (!prop.customType && isArray) {
1124
1143
  prop.customType = new ArrayType();
1125
1144
  }
1126
1145
  if (!prop.customType && prop.type?.toLowerCase() === 'buffer') {
@@ -1131,9 +1150,9 @@ export class MetadataDiscovery {
1131
1150
  }
1132
1151
  const mappedType = this.getMappedType(prop);
1133
1152
  if (prop.fieldNames?.length === 1 && !prop.customType) {
1134
- [BigIntType, DoubleType, DecimalType, IntervalType]
1153
+ [BigIntType, DoubleType, DecimalType, IntervalType, DateType]
1135
1154
  .filter(type => mappedType instanceof type)
1136
- .forEach(type => prop.customType = new type());
1155
+ .forEach((type) => prop.customType = new type());
1137
1156
  }
1138
1157
  if (prop.customType && !prop.columnTypes) {
1139
1158
  const mappedType = this.getMappedType({ columnTypes: [prop.customType.getColumnType(prop, this.platform)] });
@@ -1144,6 +1163,9 @@ export class MetadataDiscovery {
1144
1163
  prop.runtimeType ??= prop.customType.runtimeType;
1145
1164
  }
1146
1165
  }
1166
+ else if (prop.runtimeType === 'object') {
1167
+ prop.runtimeType = mappedType.runtimeType;
1168
+ }
1147
1169
  else {
1148
1170
  prop.runtimeType ??= mappedType.runtimeType;
1149
1171
  }
@@ -1158,7 +1180,7 @@ export class MetadataDiscovery {
1158
1180
  prop.customType.mode = prop.runtimeType.toLowerCase();
1159
1181
  }
1160
1182
  }
1161
- if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !prop.type?.toString().endsWith('[]')) {
1183
+ if (Type.isMappedType(prop.customType) && prop.kind === ReferenceKind.SCALAR && !isArray) {
1162
1184
  prop.type = prop.customType.name;
1163
1185
  }
1164
1186
  if (!prop.customType && [ReferenceKind.ONE_TO_ONE, ReferenceKind.MANY_TO_ONE].includes(prop.kind) && this.metadata.get(prop.type).compositePK) {