@mikro-orm/core 7.1.0-dev.2 → 7.1.0-dev.20

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 (51) hide show
  1. package/EntityManager.d.ts +63 -11
  2. package/EntityManager.js +180 -21
  3. package/drivers/DatabaseDriver.d.ts +1 -0
  4. package/drivers/DatabaseDriver.js +3 -0
  5. package/drivers/IDatabaseDriver.d.ts +44 -1
  6. package/entity/BaseEntity.d.ts +68 -1
  7. package/entity/BaseEntity.js +18 -0
  8. package/entity/Collection.d.ts +6 -3
  9. package/entity/Collection.js +15 -4
  10. package/entity/EntityFactory.js +20 -1
  11. package/entity/EntityLoader.d.ts +3 -1
  12. package/entity/EntityLoader.js +39 -16
  13. package/entity/EntityRepository.d.ts +27 -9
  14. package/entity/EntityRepository.js +12 -0
  15. package/entity/Reference.d.ts +42 -1
  16. package/entity/Reference.js +9 -0
  17. package/entity/defineEntity.d.ts +84 -15
  18. package/entity/defineEntity.js +10 -6
  19. package/entity/utils.js +4 -5
  20. package/errors.d.ts +2 -0
  21. package/errors.js +4 -0
  22. package/index.d.ts +2 -2
  23. package/index.js +1 -1
  24. package/metadata/EntitySchema.js +3 -0
  25. package/metadata/MetadataDiscovery.d.ts +12 -0
  26. package/metadata/MetadataDiscovery.js +155 -10
  27. package/metadata/MetadataValidator.d.ts +24 -0
  28. package/metadata/MetadataValidator.js +201 -0
  29. package/metadata/types.d.ts +71 -3
  30. package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
  31. package/naming-strategy/NamingStrategy.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/platforms/Platform.d.ts +7 -0
  34. package/platforms/Platform.js +15 -1
  35. package/typings.d.ts +139 -10
  36. package/typings.js +21 -4
  37. package/utils/AbstractMigrator.d.ts +11 -1
  38. package/utils/AbstractMigrator.js +203 -0
  39. package/utils/Configuration.d.ts +7 -0
  40. package/utils/Configuration.js +1 -0
  41. package/utils/DataloaderUtils.d.ts +10 -1
  42. package/utils/DataloaderUtils.js +78 -0
  43. package/utils/QueryHelper.d.ts +16 -0
  44. package/utils/QueryHelper.js +15 -0
  45. package/utils/Utils.js +1 -1
  46. package/utils/fs-utils.d.ts +2 -0
  47. package/utils/fs-utils.js +7 -1
  48. package/utils/index.d.ts +1 -0
  49. package/utils/index.js +1 -0
  50. package/utils/partition-utils.d.ts +17 -0
  51. package/utils/partition-utils.js +79 -0
package/typings.d.ts CHANGED
@@ -11,7 +11,7 @@ import type { SerializationContext } from './serialization/SerializationContext.
11
11
  import type { SerializeOptions } from './serialization/EntitySerializer.js';
12
12
  import type { MetadataStorage } from './metadata/MetadataStorage.js';
13
13
  import type { EntitySchema } from './metadata/EntitySchema.js';
14
- import type { IndexColumnOptions } from './metadata/types.js';
14
+ import type { EntityPartitionBy, IndexColumnOptions } from './metadata/types.js';
15
15
  import type { Type, types } from './types/index.js';
16
16
  import type { Platform } from './platforms/Platform.js';
17
17
  import type { Configuration } from './utils/Configuration.js';
@@ -47,7 +47,7 @@ export type AsyncFunction<R = any, T = Dictionary> = (args: T) => Promise<T>;
47
47
  export type Compute<T> = {
48
48
  [K in keyof T]: T[K];
49
49
  } & {};
50
- type InternalKeys = 'EntityRepositoryType' | 'PrimaryKeyProp' | 'OptionalProps' | 'EagerProps' | 'HiddenProps' | '__selectedType' | '__loadedType';
50
+ type InternalKeys = 'EntityRepositoryType' | 'PrimaryKeyProp' | 'OptionalProps' | 'EagerProps' | 'HiddenProps' | 'IndexHints' | '__selectedType' | '__loadedType';
51
51
  /** Filters out function, symbol, and internal keys from an entity type. When `B = true`, also excludes scalar keys. */
52
52
  export type CleanKeys<T, K extends keyof T, B extends boolean = false> = T[K] & {} extends Function ? never : K extends symbol | InternalKeys ? never : B extends true ? T[K] & {} extends Scalar ? never : K : K;
53
53
  /** Extracts keys of `T` whose values are functions. */
@@ -113,7 +113,7 @@ type LoadedReferenceShape<T = any> = ReferenceShape & {
113
113
  * Using this instead of `Loadable<any>` in conditional type checks prevents
114
114
  * TypeScript from evaluating the full Collection/Reference interfaces.
115
115
  */
116
- type LoadableShape = CollectionShape | ReferenceShape | readonly any[];
116
+ type LoadableShape = CollectionShape | ReferenceShape | LazyRef.Brand<any> | readonly any[];
117
117
  /** Gets all keys from all members of a union type (distributes over the union). */
118
118
  export type UnionKeys<T> = T extends any ? keyof T : never;
119
119
  /** Gets the type of a property from all union members that have it (distributes over the union). */
@@ -150,6 +150,44 @@ export declare const EntityName: unique symbol;
150
150
  export type InferEntityName<T> = T extends {
151
151
  [EntityName]?: infer Name;
152
152
  } ? (Name extends string ? Name : never) : never;
153
+ /**
154
+ * Symbol used to declare index-to-column mappings on an entity type.
155
+ * For decorator entities, declare as a phantom property:
156
+ * ```typescript
157
+ * [IndexHints]?: { idx_email: 'email'; idx_name_age: 'name' | 'age' };
158
+ * ```
159
+ * For `defineEntity` entities, index hints are inferred automatically from
160
+ * named indexes (property-level `.index('name')` and entity-level `indexes`/`uniques`).
161
+ */
162
+ export declare const IndexHints: unique symbol;
163
+ /**
164
+ * Extracts the index hints map from an entity type. Returns `never` when no hints are declared.
165
+ * For decorator entities, `[IndexHints]` contains a pre-computed `{ idxName: 'prop' }` map.
166
+ * For `defineEntity` entities, `[IndexHints]` contains `[Properties]` (a tuple wrapping the
167
+ * raw property builders), which is lazily converted to the index map when first accessed.
168
+ */
169
+ export type ExtractIndexHints<T> = T extends {
170
+ [IndexHints]?: infer H;
171
+ } ? H extends [infer P extends Record<string, any>] ? InferPropertyIndexMap<P> : H : never;
172
+ /**
173
+ * Extracts `{ indexName: propertyKey }` from property builder options.
174
+ * Checks `.index('name')` and `.unique('name')` on each property in a single pass.
175
+ */
176
+ export type InferPropertyIndexMap<Properties extends Record<string, any>> = {
177
+ [K in keyof Properties as MaybeReturnType<Properties[K]> extends {
178
+ '~options': {
179
+ index: infer N extends string;
180
+ };
181
+ } ? N : MaybeReturnType<Properties[K]> extends {
182
+ '~options': {
183
+ unique: infer N extends string;
184
+ };
185
+ } ? N : never]: K & string;
186
+ };
187
+ /** Union of declared index names on an entity. Falls back to `string` when no `[IndexHints]` are declared. */
188
+ export type IndexName<T> = [ExtractIndexHints<T>] extends [never] ? string : (keyof ExtractIndexHints<T> & string) | (string & {});
189
+ /** Properties covered by the named index on entity T. Falls back to all entity keys when the index is unknown. */
190
+ export type IndexColumns<T, Name extends string> = ExtractIndexHints<T> extends Record<Name, infer Cols> ? Cols & string : EntityKey<T>;
153
191
  /**
154
192
  * Branded type that marks a property as optional in `em.create()`.
155
193
  * Use as a property type wrapper: `createdAt: Opt<Date>` instead of listing in `[OptionalProps]`.
@@ -328,6 +366,18 @@ export type ObjectQuery<T> = OperatorMap<T> & FilterObject<T>;
328
366
  * Accepts an object query, a primary key value, entity props with operators, or an array of filters.
329
367
  */
330
368
  export type FilterQuery<T> = ObjectQuery<T> | NonNullable<ExpandScalar<Primary<T>>> | NonNullable<EntityProps<T> & OperatorMap<T>> | FilterQuery<T>[];
369
+ /**
370
+ * `FilterQuery` restricted to only properties covered by the specified index(es).
371
+ * Used when `using` option is set in `FindOptions` to enforce type-safe index usage.
372
+ */
373
+ export type IndexFilterQuery<T, Using extends string> = [Using] extends [never] ? FilterQuery<T> : (OperatorMap<T> & {
374
+ -readonly [K in Extract<EntityKey<T>, IndexColumns<T, Using>>]?: ExpandQuery<ExpandProperty<FilterObjectProp<T, K>>> | ExpandQueryMerged<ExpandProperty<FilterObjectProp<T, K>>> | FilterValue<ExpandProperty<FilterObjectProp<T, K>>> | ElemMatchFilter<FilterObjectProp<T, K>> | null;
375
+ }) | NonNullable<ExpandScalar<Primary<T>>> | IndexFilterQuery<T, Using>[];
376
+ /** Replaces `where` and `using` on an options type with index-aware variants when `Using` is specified. */
377
+ export type WithUsingOptions<Opts, Entity, Using extends string> = Omit<Opts, 'where' | 'using'> & {
378
+ using?: Using | Using[];
379
+ where?: [Using] extends [never] ? FilterQuery<Entity> : IndexFilterQuery<Entity, Using>;
380
+ };
331
381
  /** Public interface for the entity wrapper, accessible via `wrap(entity)`. Provides helper methods for entity state management. */
332
382
  export interface IWrappedEntity<Entity extends object> {
333
383
  isInitialized(): boolean;
@@ -417,12 +467,12 @@ type NonArrayObject = object & {
417
467
  export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends {
418
468
  __runtime?: infer Runtime;
419
469
  __raw?: infer Raw;
420
- } ? C extends true ? Raw : Runtime : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends CollectionShape<infer U> ? U | U[] | EntityDataNested<U & object, C> | EntityDataNested<U & object, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
470
+ } ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? EntityDataNested<U, C> : T extends ReferenceShape<infer U> ? EntityDataNested<U, C> : T extends CollectionShape<infer U> ? U | U[] | EntityDataNested<U & object, C> | EntityDataNested<U & object, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
421
471
  /** Like `EntityDataProp` but used in `RequiredEntityData` context with required/optional key distinction. */
422
472
  export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : Exclude<T, null> extends RequiredNullable.Brand ? T | null : T extends Scalar ? T : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends {
423
473
  __runtime?: infer Runtime;
424
474
  __raw?: infer Raw;
425
- } ? C extends true ? Raw : Runtime : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends CollectionShape<infer U> ? U | U[] | RequiredEntityDataNested<U & object, O, C> | RequiredEntityDataNested<U & object, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
475
+ } ? C extends true ? Raw : Runtime : T extends LazyRef.Brand<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ReferenceShape<infer U> ? RequiredEntityDataNested<U, O, C> : T extends CollectionShape<infer U> ? U | U[] | RequiredEntityDataNested<U & object, O, C> | RequiredEntityDataNested<U & object, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
426
476
  /** Nested entity data shape for embedded or related entities within `EntityData`. */
427
477
  export type EntityDataNested<T, C extends boolean = false> = T extends undefined ? never : T extends any[] ? Readonly<T> : EntityData<T, C> | ExpandEntityProp<T, C>;
428
478
  type UnwrapScalarRef<T> = T extends ScalarReference<infer U> ? U : T;
@@ -471,6 +521,35 @@ type Relation<T> = {
471
521
  export type Rel<T> = T;
472
522
  /** Alias for `ScalarReference` (see {@apilink Ref}). */
473
523
  export type ScalarRef<T> = ScalarReference<T>;
524
+ /**
525
+ * Type-level marker for a to-one relation that is **direct at runtime** (no `Reference` wrapper) but
526
+ * **restricted at compile time** until narrowed via `Loaded<>`.
527
+ *
528
+ * Use as the property type on a `@ManyToOne`/`@OneToOne` relation that does **not** have `ref: true`:
529
+ *
530
+ * ```ts
531
+ * @ManyToOne(() => User)
532
+ * author!: LazyRef<User>;
533
+ * ```
534
+ *
535
+ * Semantics:
536
+ * - **Runtime**: identical to a plain direct relation — `article.author` is a `User` instance (stub or hydrated),
537
+ * `article.author instanceof User` is `true`, no `Reference` object is created.
538
+ * - **Type (unloaded view)**: only the primary key is accessible. Accessing other properties is a compile error.
539
+ * - **Type (loaded view)**: once the relation is in the populate hint of a `Loaded<Entity, 'author'>`, it narrows
540
+ * back to the full entity — no `.$` or `.get()` indirection needed.
541
+ *
542
+ * Note: the safety is purely compile-time. JS code or `as any` casts bypass it.
543
+ */
544
+ export type LazyRef<T extends object> = IsAny<T> extends true ? LazyRef.Brand<T> : {
545
+ [K in PrimaryProperty<T> & keyof T]: T[K];
546
+ } & LazyRef.Brand<T>;
547
+ export declare namespace LazyRef {
548
+ const __lazyRef: unique symbol;
549
+ interface Brand<T> {
550
+ [__lazyRef]?: (arg: T) => T;
551
+ }
552
+ }
474
553
  /** Alias for `Reference<T> & { id: number }` (see {@apilink Ref}). */
475
554
  export type EntityRef<T extends object> = true extends IsUnknown<PrimaryProperty<T>> ? Reference<T> : IsAny<T> extends true ? Reference<T> : {
476
555
  [K in PrimaryProperty<T> & keyof T]: T[K];
@@ -606,6 +685,8 @@ export type IndexCallback<T> = (columns: Record<PropertyName<T>, string>, table:
606
685
  export type FormulaCallback<T> = (columns: FormulaColumns<T>, table: FormulaTable) => string | Raw;
607
686
  /** Callback for CHECK constraint expressions. Receives column mappings and table info. */
608
687
  export type CheckCallback<T> = (columns: Record<PropertyName<T>, string>, table: SchemaTable) => string | Raw;
688
+ /** Callback for trigger body expressions. Receives column mappings and table info. */
689
+ export type TriggerCallback<T> = (columns: Record<PropertyName<T>, string>, table: SchemaTable) => string | Raw;
609
690
  /** Callback for generated (computed) column expressions. Receives column mappings and table info. */
610
691
  export type GeneratedColumnCallback<T> = (columns: Record<PropertyName<T>, string>, table: SchemaTable) => string | Raw;
611
692
  /** Definition of a CHECK constraint on a table or property. */
@@ -614,6 +695,23 @@ export interface CheckConstraint<T = any> {
614
695
  property?: string;
615
696
  expression: string | Raw | CheckCallback<T>;
616
697
  }
698
+ /** Definition of a database trigger on a table. */
699
+ export interface TriggerDef<T = any> {
700
+ /** Trigger name. Auto-generated if omitted. */
701
+ name?: string;
702
+ /** When the trigger fires relative to the event. */
703
+ timing: 'before' | 'after' | 'instead of';
704
+ /** Which DML events activate the trigger. */
705
+ events: ('insert' | 'update' | 'delete' | 'truncate')[];
706
+ /** Whether the trigger fires once per row or per statement. Defaults to `'row'`. */
707
+ forEach?: 'row' | 'statement';
708
+ /** SQL body of the trigger. Can be a string, Raw query, or callback receiving column name mappings. */
709
+ body?: string | Raw | TriggerCallback<T>;
710
+ /** Optional SQL WHEN condition for the trigger. */
711
+ when?: string;
712
+ /** Raw DDL escape hatch — full CREATE TRIGGER statement. Mutually exclusive with `body`. */
713
+ expression?: string;
714
+ }
617
715
  /** Branded string that accepts any string value while preserving autocompletion for known literals. */
618
716
  export type AnyString = string & {};
619
717
  /** Describes a single property (column, relation, or embedded) within an entity's metadata. */
@@ -776,6 +874,8 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
776
874
  materialized?: boolean;
777
875
  /** For materialized views, whether data is populated on creation. Defaults to true. */
778
876
  withData?: boolean;
877
+ /** PostgreSQL partitioning definition for this table. */
878
+ partitionBy?: EntityPartitionBy<Entity>;
779
879
  expression?: string | ((em: any, where: ObjectQuery<Entity>, options: FindOptions<Entity, any, any, any>, stream?: boolean) => MaybePromise<Raw | object | string>);
780
880
  discriminatorColumn?: EntityKey<Entity> | AnyString;
781
881
  discriminatorValue?: number | string;
@@ -814,6 +914,7 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
814
914
  type?: string;
815
915
  options?: Dictionary;
816
916
  expression?: string | IndexCallback<Entity>;
917
+ where?: string | FilterQuery<Entity>;
817
918
  columns?: IndexColumnOptions[];
818
919
  include?: EntityKey<Entity> | EntityKey<Entity>[];
819
920
  fillFactor?: number;
@@ -826,6 +927,7 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
826
927
  name?: string;
827
928
  options?: Dictionary;
828
929
  expression?: string | IndexCallback<Entity>;
930
+ where?: string | FilterQuery<Entity>;
829
931
  deferMode?: DeferMode | `${DeferMode}`;
830
932
  columns?: IndexColumnOptions[];
831
933
  include?: EntityKey<Entity> | EntityKey<Entity>[];
@@ -833,6 +935,7 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
833
935
  disabled?: boolean;
834
936
  }[];
835
937
  checks: CheckConstraint<Entity>[];
938
+ triggers: TriggerDef<Entity>[];
836
939
  repositoryClass?: string;
837
940
  repository: () => EntityClass<EntityRepository<any>>;
838
941
  hooks: {
@@ -853,6 +956,8 @@ export interface EntityMetadata<Entity = any, Class extends EntityCtor<Entity> =
853
956
  polymorphicDiscriminatorMap?: Dictionary<EntityClass>;
854
957
  /** Inheritance type: 'sti' (Single Table Inheritance) or 'tpt' (Table-Per-Type). Only set on root entities. */
855
958
  inheritanceType?: 'sti' | 'tpt';
959
+ /** Legacy alias populated from `EntityOptions.inheritance`; mirrored to `inheritanceType` during discovery. */
960
+ inheritance?: 'tpt';
856
961
  /** For TPT: direct parent entity metadata (the entity this one extends). */
857
962
  tptParent?: EntityMetadata;
858
963
  /** For TPT: direct child entities (entities that extend this one). */
@@ -1060,6 +1165,11 @@ export interface IMigrator {
1060
1165
  * Executes down migrations to the given point. Without parameter it will migrate one version down.
1061
1166
  */
1062
1167
  down(options?: string | string[] | Omit<MigrateOptions, 'from'>): Promise<MigrationInfo[]>;
1168
+ /**
1169
+ * Combines multiple executed migrations into a single migration file.
1170
+ * Concatenates source code without touching the database schema.
1171
+ */
1172
+ rollup(migrations?: string[]): Promise<MigrationResult>;
1063
1173
  /**
1064
1174
  * Registers event handler.
1065
1175
  */
@@ -1142,13 +1252,32 @@ export type PopulateOptions<T> = {
1142
1252
  children?: PopulateOptions<T[keyof T]>[];
1143
1253
  /** When true, ignores `mapToPk` on the property and returns full entity data instead of just PKs. */
1144
1254
  dataOnly?: boolean;
1255
+ /** Limit the number of items loaded per parent entity. Collections will be marked as partial and readonly. */
1256
+ limit?: number;
1257
+ /** Offset for per-parent limiting (used with `limit`). */
1258
+ offset?: number;
1259
+ /** Order by clause for per-parent limiting. Takes precedence over nested `FindOptions.orderBy`. */
1260
+ orderBy?: QueryOrderMap<T[keyof T]>;
1145
1261
  };
1146
1262
  /** Inline options that can be appended to populate hint strings (e.g., strategy, join type). */
1147
1263
  export type PopulateHintOptions = {
1148
1264
  strategy?: LoadStrategy.JOINED | LoadStrategy.SELECT_IN | 'joined' | 'select-in';
1149
1265
  joinType?: 'inner join' | 'left join';
1266
+ /** Limit the number of items loaded per parent entity. Collections will be marked as partial and readonly. */
1267
+ limit?: number;
1268
+ /** Offset for per-parent limiting (used with `limit`). */
1269
+ offset?: number;
1270
+ /** Order by clause for per-parent limiting. Takes precedence over nested `FindOptions.orderBy`. */
1271
+ orderBy?: QueryOrderMap<any>;
1150
1272
  };
1151
1273
  type ExtractType<T> = T extends CollectionShape<infer U> ? U : T extends ReferenceShape<infer U> ? U : T extends Ref<infer U> ? U : T extends readonly (infer U)[] ? U : T;
1274
+ /**
1275
+ * Like {@link ExtractType} but also unwraps {@link LazyRef}. Kept separate so {@link ExtractType}'s
1276
+ * hot path (used by `IsOptional`, `EntityData`, `RequiredEntityData`) doesn't pay the cost of the
1277
+ * extra branch — those contexts handle `LazyRef` in their own earlier branches (see
1278
+ * `EntityDataProp` / `RequiredEntityDataProp`) before delegating to `ExtractType`.
1279
+ */
1280
+ type ExtractTypeWithLazyRef<T> = T extends LazyRef.Brand<infer U> ? U : ExtractType<T>;
1152
1281
  type ExtractStringKeys<T> = {
1153
1282
  [K in keyof T]-?: CleanKeys<T, K>;
1154
1283
  }[keyof T] & {};
@@ -1156,8 +1285,8 @@ type ExtractStringKeys<T> = {
1156
1285
  * Extracts string keys from an entity type, handling Collection/Reference wrappers.
1157
1286
  * Simplified to just check `T extends object` since ExtractType handles the unwrapping.
1158
1287
  */
1159
- type StringKeys<T, E extends string = never> = T extends object ? ExtractStringKeys<ExtractType<T>> | E : never;
1160
- type GetStringKey<T, K extends StringKeys<T, string>, E extends string> = K extends keyof T ? ExtractType<T[K]> : K extends E ? keyof T : never;
1288
+ type StringKeys<T, E extends string = never> = T extends object ? ExtractStringKeys<ExtractTypeWithLazyRef<T>> | E : never;
1289
+ type GetStringKey<T, K extends StringKeys<T, string>, E extends string> = K extends keyof T ? ExtractTypeWithLazyRef<T[K]> : K extends E ? keyof T : never;
1161
1290
  type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
1162
1291
  type RelationKeys<T> = T extends object ? {
1163
1292
  [K in keyof T]-?: CleanKeys<T, K, true>;
@@ -1172,8 +1301,8 @@ export type UnboxArray<T> = T extends any[] ? ArrayElement<T> : T;
1172
1301
  /** Extracts the element type from an array type. */
1173
1302
  export type ArrayElement<ArrayType extends unknown[]> = ArrayType extends (infer ElementType)[] ? ElementType : never;
1174
1303
  /** Unwraps a property type from its wrapper (Reference, Collection, or array) to the inner entity type. */
1175
- export type ExpandProperty<T> = T extends ReferenceShape<infer U> ? NonNullable<U> : T extends CollectionShape<infer U> ? NonNullable<U> : T extends (infer U)[] ? NonNullable<U> : NonNullable<T>;
1176
- type LoadedLoadable<T, E extends object> = T extends CollectionShape ? LoadedCollection<E> : T extends ScalarReference<infer U> ? LoadedScalarReference<U> : T extends ReferenceShape ? T & LoadedReference<E> : T extends Scalar ? T : T extends (infer U)[] ? U extends Scalar ? T : E[] : E;
1304
+ export type ExpandProperty<T> = T extends LazyRef.Brand<infer U> ? NonNullable<U> : T extends ReferenceShape<infer U> ? NonNullable<U> : T extends CollectionShape<infer U> ? NonNullable<U> : T extends (infer U)[] ? NonNullable<U> : NonNullable<T>;
1305
+ type LoadedLoadable<T, E extends object> = T extends CollectionShape ? LoadedCollection<E> : T extends ScalarReference<infer U> ? LoadedScalarReference<U> : T extends LazyRef.Brand<any> ? E : T extends ReferenceShape ? T & LoadedReference<E> : T extends Scalar ? T : T extends (infer U)[] ? U extends Scalar ? T : E[] : E;
1177
1306
  type IsTrue<T> = IsNever<T> extends true ? false : T extends boolean ? (T extends true ? true : false) : false;
1178
1307
  type StringLiteral<T> = T extends string ? (string extends T ? never : T) : never;
1179
1308
  type Prefix<T, K> = K extends `${infer S}.${string}` ? S : K extends '*' ? keyof T : K;
@@ -1207,7 +1336,7 @@ export type MergeSelected<T, U, F extends string> = IsLoadedType<T> extends true
1207
1336
  export type MergeLoaded<T, U, P extends string, F extends string, E extends string, R extends boolean = false> = IsLoadedType<T> extends true ? T & Loaded<U, P, F, E> : Loaded<T, P, F, E>;
1208
1337
  /** Extracts the nullability modifiers (`null`, `undefined`, or both) from a type `T`. */
1209
1338
  export type AddOptional<T> = undefined | null extends T ? null | undefined : null extends T ? null : undefined extends T ? undefined : never;
1210
- type LoadedProp<T, L extends string = never, F extends string = '*', E extends string = never> = LoadedLoadable<T, Loaded<ExtractType<T>, L, F, E>>;
1339
+ type LoadedProp<T, L extends string = never, F extends string = '*', E extends string = never> = LoadedLoadable<T, Loaded<ExtractTypeWithLazyRef<T>, L, F, E>>;
1211
1340
  /** Extracts the eager-loaded property names declared via `[EagerProps]` as a string union. */
1212
1341
  export type AddEager<T> = ExtractEagerProps<T> & string;
1213
1342
  /** Combines an explicit populate hint `L` with the entity's eagerly loaded properties. */
package/typings.js CHANGED
@@ -4,7 +4,6 @@ import { EntityHelper } from './entity/EntityHelper.js';
4
4
  import { helper } from './entity/wrap.js';
5
5
  import { Utils } from './utils/Utils.js';
6
6
  import { EntityComparator } from './utils/EntityComparator.js';
7
- import { BaseEntity } from './entity/BaseEntity.js';
8
7
  /** Symbol used to declare a custom repository type on an entity class (e.g., `[EntityRepositoryType]?: BookRepository`). */
9
8
  export const EntityRepositoryType = Symbol('EntityRepositoryType');
10
9
  /** Symbol used to declare the primary key property name(s) on an entity (e.g., `[PrimaryKeyProp]?: 'id'`). */
@@ -22,6 +21,16 @@ export const Config = Symbol('Config');
22
21
  /** Symbol used to declare the entity name as a string literal type (used by `defineEntity`). */
23
22
  // eslint-disable-next-line @typescript-eslint/no-redeclare
24
23
  export const EntityName = Symbol('EntityName');
24
+ /**
25
+ * Symbol used to declare index-to-column mappings on an entity type.
26
+ * For decorator entities, declare as a phantom property:
27
+ * ```typescript
28
+ * [IndexHints]?: { idx_email: 'email'; idx_name_age: 'name' | 'age' };
29
+ * ```
30
+ * For `defineEntity` entities, index hints are inferred automatically from
31
+ * named indexes (property-level `.index('name')` and entity-level `indexes`/`uniques`).
32
+ */
33
+ export const IndexHints = Symbol('IndexHints');
25
34
  /**
26
35
  * Runtime metadata for an entity, holding its properties, relations, indexes, hooks, and more.
27
36
  * Created during metadata discovery and used throughout the ORM lifecycle.
@@ -39,12 +48,20 @@ export class EntityMetadata {
39
48
  this.indexes = [];
40
49
  this.uniques = [];
41
50
  this.checks = [];
51
+ this.triggers = [];
42
52
  this.referencingProperties = [];
43
53
  this.concurrencyCheckKeys = new Set();
44
54
  Object.assign(this, meta);
45
55
  const name = meta.className ?? meta.name;
46
56
  if (!this.class && name) {
47
- const Class = this.extends === BaseEntity ? { [name]: class extends BaseEntity {
57
+ let Parent;
58
+ if (typeof this.extends === 'function') {
59
+ Parent = this.extends;
60
+ }
61
+ else if (this.extends != null && typeof this.extends.class === 'function') {
62
+ Parent = this.extends.class;
63
+ }
64
+ const Class = Parent ? { [name]: class extends Parent {
48
65
  } }[name] : { [name]: class {
49
66
  } }[name];
50
67
  this.class = Class;
@@ -244,8 +261,8 @@ export class EntityMetadata {
244
261
  }, { __gettersDefined: { value: true, enumerable: false } });
245
262
  }
246
263
  initIndexes(prop) {
247
- const simpleIndex = this.indexes.find(index => index.properties === prop.name && !index.options && !index.type && !index.expression);
248
- const simpleUnique = this.uniques.find(index => index.properties === prop.name && !index.options);
264
+ const simpleIndex = this.indexes.find(index => index.properties === prop.name && !index.options && !index.type && !index.expression && index.where == null);
265
+ const simpleUnique = this.uniques.find(index => index.properties === prop.name && !index.options && !index.expression && index.where == null);
249
266
  const owner = prop.kind === ReferenceKind.MANY_TO_ONE;
250
267
  if (!prop.index && simpleIndex) {
251
268
  Utils.defaultValue(simpleIndex, 'name', true);
@@ -1,4 +1,4 @@
1
- import type { Constructor, IMigrationGenerator, IMigrationRunner, IMigrator, IMigratorStorage, MaybePromise, Migration, MigrationInfo, MigrationRow, MigratorEvent } from '../typings.js';
1
+ import type { Constructor, IMigrationGenerator, IMigrationRunner, IMigrator, IMigratorStorage, MaybePromise, Migration, MigrationInfo, MigrationResult, MigrationRow, MigratorEvent } from '../typings.js';
2
2
  import type { Transaction } from '../connections/Connection.js';
3
3
  import type { Configuration, MigrationsOptions } from './Configuration.js';
4
4
  import type { EntityManagerType, IDatabaseDriver } from '../drivers/IDatabaseDriver.js';
@@ -70,6 +70,16 @@ export declare abstract class AbstractMigrator<D extends IDatabaseDriver> implem
70
70
  * @inheritDoc
71
71
  */
72
72
  down(options?: string | string[] | Omit<MigrateOptions, 'from'>): Promise<MigrationInfo[]>;
73
+ /**
74
+ * @inheritDoc
75
+ */
76
+ rollup(migrations?: string[]): Promise<MigrationResult>;
77
+ /**
78
+ * Extracts the body of a method from migration source code using brace counting.
79
+ * Returns the raw lines between the opening and closing braces, or empty string if not found.
80
+ * @internal
81
+ */
82
+ private extractMethodBody;
73
83
  abstract getStorage(): IMigratorStorage;
74
84
  /**
75
85
  * @inheritDoc
@@ -63,6 +63,209 @@ export class AbstractMigrator {
63
63
  async down(options) {
64
64
  return this.runMigrations('down', options);
65
65
  }
66
+ /**
67
+ * @inheritDoc
68
+ */
69
+ async rollup(migrations) {
70
+ await this.init();
71
+ const { fs } = await import('@mikro-orm/core/fs-utils');
72
+ const all = await this.discoverMigrations();
73
+ const executedSet = new Set(await this.storage.executed());
74
+ let toRollup;
75
+ if (migrations && migrations.length > 0) {
76
+ const requested = new Set(migrations.map(m => this.getMigrationFilename(m)));
77
+ toRollup = all.filter(m => requested.has(m.name));
78
+ const found = new Set(toRollup.map(m => m.name));
79
+ const notFound = [...requested].filter(name => !found.has(name));
80
+ if (notFound.length > 0) {
81
+ throw new Error(`Migrations not found: ${notFound.join(', ')}`);
82
+ }
83
+ const notExecuted = toRollup.filter(m => !executedSet.has(m.name));
84
+ if (notExecuted.length > 0) {
85
+ throw new Error(`Cannot roll up migrations that have not been executed: ${notExecuted.map(m => m.name).join(', ')}`);
86
+ }
87
+ }
88
+ else {
89
+ toRollup = all.filter(m => executedSet.has(m.name));
90
+ }
91
+ if (toRollup.length < 2) {
92
+ throw new Error('At least 2 executed migrations are required for rollup');
93
+ }
94
+ const withoutPath = toRollup.filter(m => !m.path);
95
+ if (withoutPath.length > 0) {
96
+ throw new Error(`Cannot roll up migrations without file paths (class-based migrations): ${withoutPath.map(m => m.name).join(', ')}`);
97
+ }
98
+ const upBodies = [];
99
+ const downBodies = [];
100
+ const placeholder = `__mikro_orm_rollup_${Date.now()}__`;
101
+ for (const migration of toRollup) {
102
+ const source = await fs.readFile(migration.path);
103
+ const upBody = this.extractMethodBody(source, 'up');
104
+ const downBody = this.extractMethodBody(source, 'down');
105
+ if (upBody) {
106
+ upBodies.push(` // --- merged from ${migration.name} ---\n${upBody}`);
107
+ }
108
+ if (downBody) {
109
+ downBodies.unshift(` // --- merged from ${migration.name} ---\n${downBody}`);
110
+ }
111
+ }
112
+ const diff = {
113
+ up: [placeholder],
114
+ down: downBodies.length > 0 ? [placeholder] : [],
115
+ };
116
+ const [templateCode, fileName] = await this.generator.generate(diff);
117
+ const placeholderRe = new RegExp(`^.*${placeholder}.*$`, 'm');
118
+ let code = templateCode.replace(placeholderRe, upBodies.join('\n'));
119
+ if (downBodies.length > 0) {
120
+ code = code.replace(placeholderRe, downBodies.join('\n'));
121
+ }
122
+ await fs.writeFile(fs.normalizePath(this.absolutePath, fileName), code, { flush: true });
123
+ const updateStorage = async () => {
124
+ for (const migration of toRollup) {
125
+ await this.storage.unlogMigration({ name: migration.name });
126
+ }
127
+ await this.storage.logMigration({ name: fileName.replace(/\.[jt]s$/, '') });
128
+ };
129
+ if (this.options.transactional) {
130
+ await this.driver.getConnection().transactional(async (trx) => {
131
+ this.storage.setMasterMigration(trx);
132
+ try {
133
+ await updateStorage();
134
+ }
135
+ finally {
136
+ this.storage.unsetMasterMigration();
137
+ }
138
+ });
139
+ }
140
+ else {
141
+ await updateStorage();
142
+ }
143
+ await Promise.all(toRollup.map(migration => fs.unlink(migration.path)));
144
+ return { fileName, code, diff: { up: [], down: [] } };
145
+ }
146
+ /**
147
+ * Extracts the body of a method from migration source code using brace counting.
148
+ * Returns the raw lines between the opening and closing braces, or empty string if not found.
149
+ * @internal
150
+ */
151
+ extractMethodBody(source, methodName) {
152
+ const lines = source.split('\n');
153
+ // match method declarations, not occurrences in comments/strings — require preceding whitespace or keyword
154
+ const methodPattern = new RegExp(`^\\s+(?:override\\s+|async\\s+)*${methodName}\\s*\\(`);
155
+ let methodLine = -1;
156
+ for (let i = 0; i < lines.length; i++) {
157
+ if (methodPattern.test(lines[i])) {
158
+ methodLine = i;
159
+ break;
160
+ }
161
+ }
162
+ if (methodLine === -1) {
163
+ return '';
164
+ }
165
+ let braceCount = 0;
166
+ let bodyStart = -1;
167
+ let bodyEnd = -1;
168
+ let bodyStartCol = -1;
169
+ let bodyEndCol = -1;
170
+ let inBacktick = false;
171
+ let inBlockComment = false;
172
+ // stack tracks brace depth at which each template expression `${...}` was entered
173
+ const templateExprStack = [];
174
+ for (let i = methodLine; i < lines.length; i++) {
175
+ const line = lines[i];
176
+ for (let j = 0; j < line.length; j++) {
177
+ // handle multi-line block comments
178
+ if (inBlockComment) {
179
+ if (line[j] === '*' && j + 1 < line.length && line[j + 1] === '/') {
180
+ inBlockComment = false;
181
+ j++;
182
+ }
183
+ continue;
184
+ }
185
+ // handle multi-line template literals
186
+ if (inBacktick) {
187
+ if (line[j] === '\\') {
188
+ j++;
189
+ }
190
+ else if (line[j] === '`') {
191
+ inBacktick = false;
192
+ }
193
+ else if (line[j] === '$' && j + 1 < line.length && line[j + 1] === '{') {
194
+ // entering template expression — resume brace counting
195
+ templateExprStack.push(braceCount);
196
+ inBacktick = false;
197
+ j++; // skip the {
198
+ braceCount++;
199
+ }
200
+ continue;
201
+ }
202
+ const ch = line[j];
203
+ // single/double quoted strings (single-line only)
204
+ if (ch === "'" || ch === '"') {
205
+ const quote = ch;
206
+ j++;
207
+ while (j < line.length) {
208
+ if (line[j] === '\\') {
209
+ j++;
210
+ }
211
+ else if (line[j] === quote) {
212
+ break;
213
+ }
214
+ j++;
215
+ }
216
+ continue;
217
+ }
218
+ // template literal start
219
+ if (ch === '`') {
220
+ inBacktick = true;
221
+ continue;
222
+ }
223
+ // single-line comment
224
+ if (ch === '/' && j + 1 < line.length && line[j + 1] === '/') {
225
+ break;
226
+ }
227
+ // block comment start
228
+ if (ch === '/' && j + 1 < line.length && line[j + 1] === '*') {
229
+ inBlockComment = true;
230
+ j++;
231
+ continue;
232
+ }
233
+ if (ch === '{') {
234
+ if (braceCount === 0) {
235
+ bodyStart = i;
236
+ bodyStartCol = j + 1;
237
+ }
238
+ braceCount++;
239
+ }
240
+ else if (ch === '}') {
241
+ braceCount--;
242
+ // closing a template expression — re-enter backtick mode
243
+ if (templateExprStack.length > 0 && braceCount === templateExprStack[templateExprStack.length - 1]) {
244
+ templateExprStack.pop();
245
+ inBacktick = true;
246
+ continue;
247
+ }
248
+ if (braceCount === 0) {
249
+ bodyEnd = i;
250
+ bodyEndCol = j;
251
+ break;
252
+ }
253
+ }
254
+ }
255
+ if (bodyEnd !== -1) {
256
+ break;
257
+ }
258
+ }
259
+ if (bodyStart === -1 || bodyEnd === -1) {
260
+ return '';
261
+ }
262
+ // single-line method body: extract content between braces on the same line
263
+ if (bodyStart === bodyEnd) {
264
+ const content = lines[bodyStart].slice(bodyStartCol, bodyEndCol).trim();
265
+ return content ? ` ${content}` : '';
266
+ }
267
+ return lines.slice(bodyStart + 1, bodyEnd).join('\n');
268
+ }
66
269
  /**
67
270
  * @inheritDoc
68
271
  */
@@ -529,6 +529,13 @@ export interface Options<Driver extends IDatabaseDriver = IDatabaseDriver, EM ex
529
529
  * @default false
530
530
  */
531
531
  forceUndefined: boolean;
532
+ /**
533
+ * Initialize nullable properties to `null` (or `undefined` when `forceUndefined` is set)
534
+ * during `em.create()` when they are not provided in the data. Without this option,
535
+ * nullable properties remain `undefined` until the entity is loaded from the database.
536
+ * @default false
537
+ */
538
+ initNullableProperties: boolean;
532
539
  /**
533
540
  * Property `onCreate` hooks are normally executed during `flush` operation.
534
541
  * With this option, they will be processed early inside `em.create()` method.
@@ -62,6 +62,7 @@ const DEFAULTS = {
62
62
  upsertManaged: true,
63
63
  forceEntityConstructor: false,
64
64
  forceUndefined: false,
65
+ initNullableProperties: false,
65
66
  forceUtcTimezone: true,
66
67
  processOnCreateHooksEarly: true,
67
68
  ensureDatabase: true,
@@ -1,5 +1,5 @@
1
1
  import type { Constructor, Primary, Ref } from '../typings.js';
2
- import { Collection, type InitCollectionOptions } from '../entity/Collection.js';
2
+ import { Collection, type InitCollectionOptions, type LoadCountOptions } from '../entity/Collection.js';
3
3
  import { type EntityManager } from '../EntityManager.js';
4
4
  import { type LoadReferenceOptions } from '../entity/Reference.js';
5
5
  type BatchLoadFn<K, V> = (keys: readonly K[]) => PromiseLike<ArrayLike<V | Error>>;
@@ -44,6 +44,15 @@ export declare class DataloaderUtils {
44
44
  * makes one query per entity and maps each input collection to the corresponding result.
45
45
  */
46
46
  static getManyToManyColBatchLoadFn(em: EntityManager): BatchLoadFn<[Collection<any>, Omit<InitCollectionOptions<any, any>, 'dataloader'>?], any>;
47
+ /**
48
+ * Returns the count dataloader batchLoadFn, which aggregates `Collection.loadCount()` calls
49
+ * by entity and relation, issues a single grouped count query per entity+options combination
50
+ * via `em.countBy()`, and maps each input collection to the corresponding count.
51
+ *
52
+ * For 1:M relations, groups by the FK property on the target entity.
53
+ * For M:N relations, groups by the owner FK on the pivot entity.
54
+ */
55
+ static getCountBatchLoadFn(em: EntityManager): BatchLoadFn<[Collection<any>, Omit<LoadCountOptions<any>, 'dataloader' | 'refresh'>?], number>;
47
56
  static getDataLoader(): Promise<Constructor<{
48
57
  load: (...args: unknown[]) => Promise<unknown>;
49
58
  }>>;