@mikro-orm/core 7.1.0-dev.9 → 7.1.0
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/EntityManager.d.ts +47 -3
- package/EntityManager.js +150 -23
- package/README.md +2 -1
- package/connections/Connection.d.ts +67 -1
- package/connections/Connection.js +64 -0
- package/drivers/IDatabaseDriver.d.ts +20 -6
- package/entity/BaseEntity.d.ts +68 -1
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +1 -1
- package/entity/Collection.js +4 -1
- package/entity/EntityAssigner.js +8 -0
- package/entity/EntityFactory.js +5 -3
- package/entity/EntityLoader.d.ts +8 -1
- package/entity/EntityLoader.js +97 -29
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/defineEntity.d.ts +69 -16
- package/entity/defineEntity.js +17 -0
- package/entity/utils.js +4 -5
- package/enums.d.ts +8 -1
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.js +18 -0
- package/metadata/MetadataDiscovery.d.ts +1 -0
- package/metadata/MetadataDiscovery.js +79 -15
- package/metadata/MetadataValidator.d.ts +26 -0
- package/metadata/MetadataValidator.js +228 -1
- package/metadata/Routine.d.ts +74 -0
- package/metadata/Routine.js +140 -0
- package/metadata/index.d.ts +1 -0
- package/metadata/index.js +1 -0
- package/metadata/types.d.ts +75 -8
- package/package.json +1 -1
- package/platforms/Platform.d.ts +13 -3
- package/platforms/Platform.js +51 -6
- package/serialization/EntitySerializer.js +4 -3
- package/typings.d.ts +323 -21
- package/typings.js +32 -12
- package/unit-of-work/UnitOfWork.js +43 -5
- package/utils/AbstractMigrator.d.ts +9 -4
- package/utils/AbstractMigrator.js +60 -28
- package/utils/AbstractSchemaGenerator.d.ts +1 -1
- package/utils/AbstractSchemaGenerator.js +4 -1
- package/utils/Configuration.d.ts +30 -0
- package/utils/Configuration.js +45 -0
- package/utils/EntityComparator.js +1 -1
- package/utils/TransactionManager.js +2 -0
- package/utils/Utils.js +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/partition-utils.d.ts +17 -0
- package/utils/partition-utils.js +79 -0
- package/utils/upsert-utils.d.ts +2 -0
- package/utils/upsert-utils.js +26 -1
package/entity/defineEntity.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EntityManager } from '../EntityManager.js';
|
|
2
2
|
import type { ColumnType, PropertyOptions, ReferenceOptions, EnumOptions, EmbeddedOptions, ManyToOneOptions, OneToManyOptions, OneToOneOptions, ManyToManyOptions, IndexColumnOptions } from '../metadata/types.js';
|
|
3
|
-
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, EntityRepositoryType, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref, IndexCallback, TriggerCallback, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise, IndexHints } from '../typings.js';
|
|
3
|
+
import type { AnyString, GeneratedColumnCallback, Constructor, CheckCallback, FilterQuery, EntityName, Dictionary, EntityMetadata, PrimaryKeyProp, EntityRepositoryType, Hidden, Opt, Primary, EntityClass, EntitySchemaWithMeta, InferEntity, MaybeReturnType, Ref, LazyRef, IndexCallback, TriggerCallback, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise, IndexHints } from '../typings.js';
|
|
4
4
|
import type { Raw } from '../utils/RawQueryFragment.js';
|
|
5
5
|
import type { ScalarReference } from './Reference.js';
|
|
6
6
|
import type { SerializeOptions } from '../serialization/EntitySerializer.js';
|
|
@@ -12,7 +12,7 @@ import type { Collection } from './Collection.js';
|
|
|
12
12
|
import type { FilterOptions, FindOptions, FindOneOptions } from '../drivers/IDatabaseDriver.js';
|
|
13
13
|
/** Union of all option keys supported across all property definition types (scalar, enum, embedded, relations). */
|
|
14
14
|
export type UniversalPropertyKeys = keyof PropertyOptions<any> | keyof EnumOptions<any> | keyof EmbeddedOptions<any, any> | keyof ReferenceOptions<any, any> | keyof ManyToOneOptions<any, any> | keyof OneToManyOptions<any, any> | keyof OneToOneOptions<any, any> | keyof ManyToManyOptions<any, any>;
|
|
15
|
-
type BuilderExtraKeys = '~options' | '~type' | '$type' | 'strictNullable';
|
|
15
|
+
type BuilderExtraKeys = '~options' | '~type' | '$type' | 'strictNullable' | 'lazyRef';
|
|
16
16
|
type ExcludeKeys = 'entity' | 'items';
|
|
17
17
|
type BuilderKeys = Exclude<UniversalPropertyKeys, ExcludeKeys> | BuilderExtraKeys;
|
|
18
18
|
type IncludeKeysForProperty = Exclude<keyof PropertyOptions<any>, ExcludeKeys> | BuilderExtraKeys;
|
|
@@ -36,9 +36,23 @@ export interface PropertyChain<Value, Options> {
|
|
|
36
36
|
nullable: true;
|
|
37
37
|
strictNullable: true;
|
|
38
38
|
}>;
|
|
39
|
-
ref():
|
|
39
|
+
ref(): Options extends {
|
|
40
|
+
lazyRef: true;
|
|
41
|
+
} ? never : PropertyChain<Value, Omit<Options, 'ref'> & {
|
|
40
42
|
ref: true;
|
|
41
43
|
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — direct entity at runtime, type-restricted
|
|
46
|
+
* until `Loaded<>` narrows it. Type-level only, no `ref: true` wrapping. Not compatible with `.ref()` or
|
|
47
|
+
* `.mapToPk()` — those chains are rejected in both directions at compile time.
|
|
48
|
+
*/
|
|
49
|
+
lazyRef(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
50
|
+
ref: true;
|
|
51
|
+
} | {
|
|
52
|
+
mapToPk: true;
|
|
53
|
+
} ? never : PropertyChain<Value, Omit<Options, 'lazyRef'> & {
|
|
54
|
+
lazyRef: true;
|
|
55
|
+
}> : never;
|
|
42
56
|
primary(): PropertyChain<Value, Omit<Options, 'primary'> & {
|
|
43
57
|
primary: true;
|
|
44
58
|
}>;
|
|
@@ -103,7 +117,7 @@ export interface PropertyChain<Value, Options> {
|
|
|
103
117
|
groups(...groups: string[]): PropertyChain<Value, Options>;
|
|
104
118
|
customOrder(...customOrder: string[] | number[] | boolean[]): PropertyChain<Value, Options>;
|
|
105
119
|
extra(extra: string): PropertyChain<Value, Options>;
|
|
106
|
-
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default')[]): PropertyChain<Value, Options>;
|
|
120
|
+
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default' | 'collation')[]): PropertyChain<Value, Options>;
|
|
107
121
|
/** Explicitly specify index on a property. When a string name is passed, it enables type-safe `using` in `FindOptions`. */
|
|
108
122
|
index<N extends string>(name: N): PropertyChain<Value, Omit<Options, 'index'> & {
|
|
109
123
|
index: N;
|
|
@@ -115,6 +129,7 @@ export interface PropertyChain<Value, Options> {
|
|
|
115
129
|
}>;
|
|
116
130
|
unique(unique?: boolean): PropertyChain<Value, Options>;
|
|
117
131
|
comment(comment: string): PropertyChain<Value, Options>;
|
|
132
|
+
collation(collation: string): PropertyChain<Value, Options>;
|
|
118
133
|
accessor(accessor?: string | boolean): PropertyChain<Value, Options>;
|
|
119
134
|
eager(eager?: boolean): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
120
135
|
cascade(...cascade: Cascade[]): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -125,7 +140,9 @@ export interface PropertyChain<Value, Options> {
|
|
|
125
140
|
owner(): HasKind<Options, '1:1' | 'm:n'> extends true ? PropertyChain<Value, Omit<Options, 'owner'> & {
|
|
126
141
|
owner: true;
|
|
127
142
|
}> : never;
|
|
128
|
-
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ?
|
|
143
|
+
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
144
|
+
lazyRef: true;
|
|
145
|
+
} ? never : PropertyChain<Value, Omit<Options, 'mapToPk'> & {
|
|
129
146
|
mapToPk: true;
|
|
130
147
|
}> : never;
|
|
131
148
|
orphanRemoval(orphanRemoval?: boolean): HasKind<Options, '1:m' | '1:1'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -325,9 +342,24 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
325
342
|
/**
|
|
326
343
|
* Enable `ScalarReference` wrapper for lazy values. Use this in combination with `lazy: true` to have a type-safe accessor object in place of the value.
|
|
327
344
|
*/
|
|
328
|
-
ref():
|
|
345
|
+
ref(): Options extends {
|
|
346
|
+
lazyRef: true;
|
|
347
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'ref'> & {
|
|
329
348
|
ref: true;
|
|
330
349
|
}, IncludeKeys>;
|
|
350
|
+
/**
|
|
351
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — the runtime value stays a plain entity
|
|
352
|
+
* (no `Reference` wrapper) but the TypeScript type restricts access to the primary key until `Loaded<>`
|
|
353
|
+
* narrows it. Type-level only; does not set `ref: true`. Not compatible with `.ref()` or `.mapToPk()` —
|
|
354
|
+
* those chains are rejected in both directions at compile time.
|
|
355
|
+
*/
|
|
356
|
+
lazyRef(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
357
|
+
ref: true;
|
|
358
|
+
} | {
|
|
359
|
+
mapToPk: true;
|
|
360
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'lazyRef'> & {
|
|
361
|
+
lazyRef: true;
|
|
362
|
+
}, IncludeKeys> : never;
|
|
331
363
|
/**
|
|
332
364
|
* Set to true to omit the property when {@link https://mikro-orm.io/docs/serializing Serializing}.
|
|
333
365
|
*/
|
|
@@ -447,6 +479,11 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
447
479
|
* Specify comment of column for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
448
480
|
*/
|
|
449
481
|
comment(comment: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
482
|
+
/**
|
|
483
|
+
* Specify column-level collation for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
484
|
+
* Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
|
|
485
|
+
*/
|
|
486
|
+
collation(collation: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
450
487
|
/** mysql only */
|
|
451
488
|
extra(extra: string): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
452
489
|
/**
|
|
@@ -454,7 +491,7 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
454
491
|
*
|
|
455
492
|
* @see https://mikro-orm.io/docs/defining-entities#sql-generated-columns
|
|
456
493
|
*/
|
|
457
|
-
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default')[]): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
494
|
+
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default' | 'collation')[]): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
458
495
|
array(): Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'array'> & {
|
|
459
496
|
array: true;
|
|
460
497
|
}, IncludeKeys>, IncludeKeys>;
|
|
@@ -512,7 +549,9 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
512
549
|
/** What to do when the reference to the target entity gets updated. */
|
|
513
550
|
updateRule(updateRule: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
514
551
|
/** Map this relation to the primary key value instead of an entity. */
|
|
515
|
-
mapToPk():
|
|
552
|
+
mapToPk(): Options extends {
|
|
553
|
+
lazyRef: true;
|
|
554
|
+
} ? never : Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'mapToPk'> & {
|
|
516
555
|
mapToPk: true;
|
|
517
556
|
}, IncludeKeys>, IncludeKeys>;
|
|
518
557
|
/** Set the constraint type. Immediate constraints are checked for each statement, while deferred ones are only checked at the end of the transaction. Only for postgres unique constraints. */
|
|
@@ -556,7 +595,7 @@ export type PropertyBuilders = {
|
|
|
556
595
|
datetime: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.datetime>, EmptyOptions, IncludeKeysForProperty>;
|
|
557
596
|
time: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.time>, EmptyOptions, IncludeKeysForProperty>;
|
|
558
597
|
type: <T extends PropertyValueType>(type: T) => UniversalPropertyOptionsBuilder<InferPropertyValueType<T>, EmptyOptions, IncludeKeysForProperty>;
|
|
559
|
-
enum: <const T extends readonly (number | string)[] | (() => Dictionary)>(items?: T) => UniversalPropertyOptionsBuilder<T extends () => Dictionary ? ValueOf<ReturnType<T>> : T extends readonly (infer Value)[] ? Value : T, EmptyOptions, IncludeKeysForEnumOptions>;
|
|
598
|
+
enum: <const T extends readonly (number | string)[] | Dictionary | (() => Dictionary)>(items?: T) => UniversalPropertyOptionsBuilder<T extends () => Dictionary ? ValueOf<ReturnType<T>> : T extends readonly (infer Value)[] ? Value : T extends Dictionary ? ValueOf<T> : T, EmptyOptions, IncludeKeysForEnumOptions>;
|
|
560
599
|
embedded: <Target extends EntityTarget | EntityTarget[]>(target: Target) => PropertyChain<InferEntity<Target extends (infer T)[] ? T : Target>, EmptyOptions & {
|
|
561
600
|
kind: 'embedded';
|
|
562
601
|
}>;
|
|
@@ -576,12 +615,15 @@ export type PropertyBuilders = {
|
|
|
576
615
|
/** Own keys + base entity keys (when TBase is not `never`). Guards against `keyof never = string | number | symbol`. */
|
|
577
616
|
type AllKeys<TProperties, TBase> = keyof TProperties | (IsNever<TBase> extends true ? never : keyof TBase);
|
|
578
617
|
/** Metadata descriptor for `defineEntity()`, combining entity options with property definitions. */
|
|
579
|
-
export interface EntityMetadataWithProperties<TName extends string, TTableName extends string, TProperties extends Record<string, any>, TPK extends (keyof TProperties)[] | undefined = undefined, TBase = never, TRepository = never, TForceObject extends boolean = false> extends Omit<Partial<EntityMetadata<InferEntityFromProperties<TProperties, TPK, TBase, TRepository>>>, 'properties' | 'extends' | 'primaryKeys' | 'hooks' | 'discriminatorColumn' | 'versionProperty' | 'concurrencyCheckKeys' | 'serializedPrimaryKey' | 'indexes' | 'uniques' | 'triggers' | 'repository' | 'filters' | 'orderBy'> {
|
|
618
|
+
export interface EntityMetadataWithProperties<TName extends string, TTableName extends string, TProperties extends Record<string, any>, TPK extends (keyof TProperties)[] | undefined = undefined, TBase = never, TRepository = never, TForceObject extends boolean = false, TDiscriminatorColumn extends string | undefined = undefined, TDiscriminatorValue extends string | number | undefined = undefined, TBaseDiscriminatorColumn extends string | undefined = undefined> extends Omit<Partial<EntityMetadata<InferEntityFromProperties<TProperties, TPK, TBase, TRepository>>>, 'properties' | 'extends' | 'primaryKeys' | 'hooks' | 'discriminator' | 'discriminatorColumn' | 'discriminatorValue' | 'versionProperty' | 'concurrencyCheckKeys' | 'serializedPrimaryKey' | 'indexes' | 'uniques' | 'triggers' | 'repository' | 'filters' | 'orderBy'> {
|
|
580
619
|
name: TName;
|
|
581
620
|
tableName?: TTableName;
|
|
582
621
|
extends?: {
|
|
583
622
|
'~entity': TBase;
|
|
584
|
-
|
|
623
|
+
'~discriminatorColumn'?: TBaseDiscriminatorColumn;
|
|
624
|
+
} | (EntityCtor<TBase> & {
|
|
625
|
+
'~discriminatorColumn'?: TBaseDiscriminatorColumn;
|
|
626
|
+
});
|
|
585
627
|
properties: TProperties | ((properties: PropertyBuilders) => TProperties);
|
|
586
628
|
primaryKeys?: TPK & InferPrimaryKeyConstraint<TProperties>[];
|
|
587
629
|
hooks?: DefineEntityHooks;
|
|
@@ -601,7 +643,10 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
601
643
|
} | {
|
|
602
644
|
[K in Extract<AllKeys<TProperties, TBase>, string>]?: QueryOrderKeysFlat;
|
|
603
645
|
}[];
|
|
604
|
-
|
|
646
|
+
discriminator?: TDiscriminatorColumn;
|
|
647
|
+
/** Alias for `discriminator`. */
|
|
648
|
+
discriminatorColumn?: TDiscriminatorColumn;
|
|
649
|
+
discriminatorValue?: TDiscriminatorValue;
|
|
605
650
|
versionProperty?: AllKeys<TProperties, TBase>;
|
|
606
651
|
concurrencyCheckKeys?: Set<AllKeys<TProperties, TBase>>;
|
|
607
652
|
serializedPrimaryKey?: AllKeys<TProperties, TBase>;
|
|
@@ -611,6 +656,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
611
656
|
type?: string;
|
|
612
657
|
options?: Dictionary;
|
|
613
658
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
659
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
614
660
|
columns?: IndexColumnOptions[];
|
|
615
661
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
616
662
|
fillFactor?: number;
|
|
@@ -623,6 +669,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
623
669
|
name?: string;
|
|
624
670
|
options?: Dictionary;
|
|
625
671
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
672
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
626
673
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
627
674
|
columns?: IndexColumnOptions[];
|
|
628
675
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
@@ -640,7 +687,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
640
687
|
}[];
|
|
641
688
|
}
|
|
642
689
|
/** Defines an entity schema using property builders, with full type inference from the property definitions. */
|
|
643
|
-
export declare function defineEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, any>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject>): EntitySchemaWithMeta<TName, TTableName, InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject>, TBase, TProperties>;
|
|
690
|
+
export declare function defineEntity<const TName extends string, const TTableName extends string, const TProperties extends Record<string, any>, const TPK extends (keyof TProperties)[] | undefined = undefined, const TBase = never, const TRepository = never, const TForceObject extends boolean = false, const TDiscriminatorColumn extends string | undefined = undefined, const TDiscriminatorValue extends string | number | undefined = undefined, const TBaseDiscriminatorColumn extends string | undefined = undefined>(meta: EntityMetadataWithProperties<TName, TTableName, TProperties, TPK, TBase, TRepository, TForceObject, TDiscriminatorColumn, TDiscriminatorValue, TBaseDiscriminatorColumn>): EntitySchemaWithMeta<TName, TTableName, InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject, TBaseDiscriminatorColumn, TDiscriminatorValue>, TBase, TProperties, EntityCtor<InferEntityFromProperties<TProperties, TPK, TBase, TRepository, TForceObject, TBaseDiscriminatorColumn, TDiscriminatorValue>>, TDiscriminatorColumn>;
|
|
644
691
|
/** Defines an entity schema for an existing class, combining the class with property builders. */
|
|
645
692
|
export declare function defineEntity<const TEntity = any, const TProperties extends Record<string, any> = Record<string, any>, const TClassName extends string = string, const TTableName extends string = string, const TBase = never, const TClass extends EntityCtor = EntityCtor<TEntity>>(meta: Omit<Partial<EntityMetadata<TEntity>>, 'properties' | 'extends' | 'className' | 'tableName' | 'hooks'> & {
|
|
646
693
|
class: TClass;
|
|
@@ -676,7 +723,7 @@ type InferJSType<T> = T extends typeof Type<infer TValue, any> ? NonNullable<TVa
|
|
|
676
723
|
type InferColumnType<T extends string> = T extends 'int' | 'int4' | 'integer' | 'bigint' | 'int8' | 'int2' | 'tinyint' | 'smallint' | 'mediumint' ? number : T extends 'double' | 'double precision' | 'real' | 'float8' | 'decimal' | 'numeric' | 'float' | 'float4' ? number : T extends 'datetime' | 'time' | 'time with time zone' | 'timestamp' | 'timestamp with time zone' | 'timetz' | 'timestamptz' | 'date' | 'interval' ? Date : T extends 'ObjectId' | 'objectId' | 'character varying' | 'varchar' | 'char' | 'character' | 'uuid' | 'text' | 'tinytext' | 'mediumtext' | 'longtext' | 'enum' ? string : T extends 'boolean' | 'bool' | 'bit' ? boolean : T extends 'blob' | 'tinyblob' | 'mediumblob' | 'longblob' | 'bytea' ? Buffer : T extends 'point' | 'line' | 'lseg' | 'box' | 'circle' | 'path' | 'polygon' | 'geometry' ? number[] : T extends 'tsvector' | 'tsquery' ? string[] : T extends 'json' | 'jsonb' ? any : any;
|
|
677
724
|
type BaseEntityMethodKeys = 'toObject' | 'toPOJO' | 'serialize' | 'assign' | 'populate' | 'init' | 'toReference';
|
|
678
725
|
/** Infers the entity type from a `defineEntity()` properties map, resolving builders, base classes, and primary keys. */
|
|
679
|
-
export type InferEntityFromProperties<Properties extends Record<string, any>, PK extends (keyof Properties)[] | undefined = undefined, Base = never, Repository = never, ForceObject extends boolean = false> = (IsNever<Base> extends true ? {} : Base extends {
|
|
726
|
+
export type InferEntityFromProperties<Properties extends Record<string, any>, PK extends (keyof Properties)[] | undefined = undefined, Base = never, Repository = never, ForceObject extends boolean = false, BaseDiscriminatorColumn extends string | undefined = undefined, DiscriminatorValue extends string | number | undefined = undefined> = (IsNever<Base> extends true ? {} : Base extends {
|
|
680
727
|
toObject(...args: any[]): any;
|
|
681
728
|
} ? Pick<IWrappedEntity<{
|
|
682
729
|
-readonly [K in keyof Properties]: InferBuilderValue<MaybeReturnType<Properties[K]>>;
|
|
@@ -684,19 +731,22 @@ export type InferEntityFromProperties<Properties extends Record<string, any>, PK
|
|
|
684
731
|
[PrimaryKeyProp]?: InferCombinedPrimaryKey<Properties, PK, Base>;
|
|
685
732
|
} & (IsNever<Repository> extends true ? {} : {
|
|
686
733
|
[EntityRepositoryType]?: Repository extends Constructor<infer R> ? R : Repository;
|
|
687
|
-
}) & Omit<Base, typeof PrimaryKeyProp>>, BaseEntityMethodKeys> : {}) & {
|
|
734
|
+
}) & NarrowDiscriminator<Omit<Base, typeof PrimaryKeyProp>, BaseDiscriminatorColumn, DiscriminatorValue>>, BaseEntityMethodKeys> : {}) & {
|
|
688
735
|
-readonly [K in keyof Properties]: InferBuilderValue<MaybeReturnType<Properties[K]>>;
|
|
689
736
|
} & {
|
|
690
737
|
[PrimaryKeyProp]?: InferCombinedPrimaryKey<Properties, PK, Base>;
|
|
691
738
|
} & (IsNever<Repository> extends true ? {} : {
|
|
692
739
|
[EntityRepositoryType]?: Repository extends Constructor<infer R> ? R : Repository;
|
|
693
|
-
}) & (IsNever<Base> extends true ? {} : Omit<Base, typeof PrimaryKeyProp>) & (ForceObject extends true ? {
|
|
740
|
+
}) & (IsNever<Base> extends true ? {} : NarrowDiscriminator<Omit<Base, typeof PrimaryKeyProp>, BaseDiscriminatorColumn, DiscriminatorValue>) & (ForceObject extends true ? {
|
|
694
741
|
[Config]?: DefineConfig<{
|
|
695
742
|
forceObject: true;
|
|
696
743
|
}>;
|
|
697
744
|
} : {}) & {
|
|
698
745
|
[IndexHints]?: [Properties];
|
|
699
746
|
};
|
|
747
|
+
type NarrowDiscriminator<Base, DiscColumn extends string | undefined, DiscValue> = DiscColumn extends string ? DiscColumn extends keyof Base ? DiscValue extends string | number ? Omit<Base, DiscColumn> & {
|
|
748
|
+
[K in DiscColumn]: DiscValue;
|
|
749
|
+
} : Base : Base : Base;
|
|
700
750
|
type InferCombinedPrimaryKey<Properties extends Record<string, any>, PK, Base> = PK extends undefined ? CombinePrimaryKeys<InferPrimaryKey<Properties>, ExtractBasePrimaryKey<Base>> : PK;
|
|
701
751
|
type ExtractBasePrimaryKey<Base> = Base extends {
|
|
702
752
|
[PrimaryKeyProp]?: infer BasePK;
|
|
@@ -738,6 +788,9 @@ type MaybeNullable<Value, Options> = Options extends {
|
|
|
738
788
|
type MaybeRelationRef<Value, Options> = Options extends {
|
|
739
789
|
mapToPk: true;
|
|
740
790
|
} ? Value : Options extends {
|
|
791
|
+
lazyRef: true;
|
|
792
|
+
kind: '1:1' | 'm:1';
|
|
793
|
+
} ? Value extends object ? LazyRef<Value> : never : Options extends {
|
|
741
794
|
ref: true;
|
|
742
795
|
kind: '1:1';
|
|
743
796
|
} ? Value extends object ? Ref<Value> : never : Options extends {
|
package/entity/defineEntity.js
CHANGED
|
@@ -173,6 +173,16 @@ export class UniversalPropertyOptionsBuilder {
|
|
|
173
173
|
ref() {
|
|
174
174
|
return this.assignOptions({ ref: true });
|
|
175
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — the runtime value stays a plain entity
|
|
178
|
+
* (no `Reference` wrapper) but the TypeScript type restricts access to the primary key until `Loaded<>`
|
|
179
|
+
* narrows it. Type-level only; does not set `ref: true`. Not compatible with `.ref()` or `.mapToPk()` —
|
|
180
|
+
* those chains are rejected in both directions at compile time.
|
|
181
|
+
*/
|
|
182
|
+
lazyRef() {
|
|
183
|
+
// purely type-level — no runtime metadata change
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
176
186
|
/**
|
|
177
187
|
* Set to true to omit the property when {@link https://mikro-orm.io/docs/serializing Serializing}.
|
|
178
188
|
*/
|
|
@@ -306,6 +316,13 @@ export class UniversalPropertyOptionsBuilder {
|
|
|
306
316
|
comment(comment) {
|
|
307
317
|
return this.assignOptions({ comment });
|
|
308
318
|
}
|
|
319
|
+
/**
|
|
320
|
+
* Specify column-level collation for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
321
|
+
* Emits a `COLLATE` clause on the column definition; the accepted collation names are dialect-specific.
|
|
322
|
+
*/
|
|
323
|
+
collation(collation) {
|
|
324
|
+
return this.assignOptions({ collation });
|
|
325
|
+
}
|
|
309
326
|
/** mysql only */
|
|
310
327
|
extra(extra) {
|
|
311
328
|
return this.assignOptions({ extra });
|
package/entity/utils.js
CHANGED
|
@@ -94,11 +94,10 @@ export function applyPopulateHints(populate, hints) {
|
|
|
94
94
|
if (!entry) {
|
|
95
95
|
continue;
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
entry.joinType = hint.joinType;
|
|
97
|
+
for (const key of Object.keys(hint)) {
|
|
98
|
+
if (hint[key] != null) {
|
|
99
|
+
entry[key] = hint[key];
|
|
100
|
+
}
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
}
|
package/enums.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EntityKey, ExpandProperty } from './typings.js';
|
|
2
|
-
import type { Transaction } from './connections/Connection.js';
|
|
2
|
+
import type { InflightQueryAbortStrategy, Transaction } from './connections/Connection.js';
|
|
3
3
|
import type { LogContext } from './logging/Logger.js';
|
|
4
4
|
/** Controls when the `EntityManager` flushes pending changes to the database. */
|
|
5
5
|
export declare enum FlushMode {
|
|
@@ -307,6 +307,13 @@ export interface TransactionOptions {
|
|
|
307
307
|
flushMode?: FlushMode | `${FlushMode}`;
|
|
308
308
|
ignoreNestedTransactions?: boolean;
|
|
309
309
|
loggerContext?: LogContext;
|
|
310
|
+
/**
|
|
311
|
+
* `AbortSignal` cancelling every query within the transaction (including the implicit flush).
|
|
312
|
+
* Cancelling mid-transaction triggers a rollback once the in-flight query settles.
|
|
313
|
+
*/
|
|
314
|
+
signal?: AbortSignal;
|
|
315
|
+
/** Cancellation strategy paired with {@link signal}. */
|
|
316
|
+
inflightQueryAbortStrategy?: InflightQueryAbortStrategy;
|
|
310
317
|
}
|
|
311
318
|
export declare abstract class PlainObject {
|
|
312
319
|
}
|
package/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
5
|
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, IndexHints, } from './typings.js';
|
|
6
|
-
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaTable, SchemaTable, SchemaColumns, SimpleColumnMeta, Rel, Ref, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
6
|
+
export type { CompiledFunctions, Constructor, ConnectionType, Dictionary, Primary, IPrimaryKey, ObjectQuery, FilterQuery, IWrappedEntity, InferEntityName, EntityData, Highlighter, MaybePromise, AnyEntity, EntityClass, EntityProperty, PopulateOptions, Populate, Loaded, New, LoadedReference, LoadedCollection, IMigrator, IMigrationGenerator, MigratorEvent, GetRepository, MigrationObject, DeepPartial, PrimaryProperty, Cast, IsUnknown, EntityDictionary, EntityDTO, EntityDTOFlat, EntityDTOProp, SerializeDTO, MigrationDiff, GenerateOptions, FilterObject, IndexFilterQuery, ExtractIndexHints, IndexName, IndexColumns, WithUsingOptions, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, TriggerCallback, IndexCallback, FormulaCallback, FormulaColumns, FormulaTable, SchemaTable, SchemaColumns, SchemaColumnRef, EntityDataPropValue, SimpleColumnMeta, Rel, Ref, LazyRef, ScalarRef, EntityRef, ISchemaGenerator, MigrationInfo, MigrateOptions, MigrationResult, MigrationRow, EntityKey, EntityValue, EntityDataValue, FilterKey, EntityType, FromEntityType, Selected, IsSubset, EntityProps, ExpandProperty, ExpandScalar, FilterItemValue, ExpandQuery, Scalar, ExpandHint, FilterValue, MergeLoaded, MergeSelected, TypeConfig, AnyString, ClearDatabaseOptions, CreateSchemaOptions, EnsureDatabaseOptions, UpdateSchemaOptions, DropSchemaOptions, RefreshDatabaseOptions, AutoPath, UnboxArray, MetadataProcessor, ImportsResolver, RequiredNullable, DefineConfig, Opt, Hidden, EntitySchemaWithMeta, InferEntity, CheckConstraint, TriggerDef, RoutineReturns, RoutineBodyCallback, RoutineJsBody, RoutineIgnoreField, RoutineParamConfig, RoutineConfig, RoutineRuntimeType, RoutineArgs, RoutineReturn, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
7
7
|
export * from './enums.js';
|
|
8
8
|
export * from './errors.js';
|
|
9
9
|
export * from './exceptions.js';
|
package/metadata/EntitySchema.js
CHANGED
|
@@ -85,6 +85,9 @@ export class EntitySchema {
|
|
|
85
85
|
if (options.items instanceof Function) {
|
|
86
86
|
options.items = Utils.extractEnumValues(options.items());
|
|
87
87
|
}
|
|
88
|
+
else if (options.items && !Array.isArray(options.items) && typeof options.items === 'object') {
|
|
89
|
+
options.items = Utils.extractEnumValues(options.items);
|
|
90
|
+
}
|
|
88
91
|
// enum arrays are simple numeric/string arrays, the constraint is enforced in the custom type only
|
|
89
92
|
if (options.array && !options.type) {
|
|
90
93
|
options.type = new EnumArrayType(`${this._meta.className}.${name}`, options.items);
|
|
@@ -251,6 +254,21 @@ export class EntitySchema {
|
|
|
251
254
|
return this;
|
|
252
255
|
}
|
|
253
256
|
this.setClass(this._meta.class);
|
|
257
|
+
// `discriminator` is the property name on the entity / embeddable; `discriminatorColumn` is an
|
|
258
|
+
// optional column-name override on that property. For backward compat, when only
|
|
259
|
+
// `discriminatorColumn` is given (legacy), it is treated as the property name. Once normalized,
|
|
260
|
+
// `meta.discriminatorColumn` always holds the property name (so existing readers keep working),
|
|
261
|
+
// and `meta.discriminatorFieldName` carries the explicit column override (if any).
|
|
262
|
+
if (this._meta.discriminator &&
|
|
263
|
+
this._meta.discriminatorColumn &&
|
|
264
|
+
this._meta.discriminator !== this._meta.discriminatorColumn) {
|
|
265
|
+
this._meta.discriminatorFieldName = this._meta.discriminatorColumn;
|
|
266
|
+
this._meta.discriminatorColumn = this._meta.discriminator;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
this._meta.discriminatorColumn ??= this._meta.discriminator;
|
|
270
|
+
this._meta.discriminator ??= this._meta.discriminatorColumn;
|
|
271
|
+
}
|
|
254
272
|
// Abstract TPT entities keep their name because they have their own table
|
|
255
273
|
const isTPT = this._meta.inheritance === 'tpt' || this.isPartOfTPTHierarchy();
|
|
256
274
|
if (this._meta.abstract && !this._meta.discriminatorColumn && !isTPT) {
|
|
@@ -76,6 +76,7 @@ export declare class MetadataDiscovery {
|
|
|
76
76
|
private initPolyEmbeddables;
|
|
77
77
|
private initPolymorphicRelation;
|
|
78
78
|
private initEmbeddables;
|
|
79
|
+
private sameRelationTargetRoot;
|
|
79
80
|
private initSingleTableInheritance;
|
|
80
81
|
/**
|
|
81
82
|
* First pass of TPT initialization: sets up hierarchy relationships
|
|
@@ -114,7 +114,12 @@ export class MetadataDiscovery {
|
|
|
114
114
|
}
|
|
115
115
|
else {
|
|
116
116
|
const name = prop.name;
|
|
117
|
-
|
|
117
|
+
// For to-one relations, only swap to the accessor for the documented
|
|
118
|
+
// backing-field convention (a `_`-prefixed property like `_draft` paired
|
|
119
|
+
// with `accessor: 'draft'`). The non-prefixed sibling-helper pattern
|
|
120
|
+
// (`author` + `accessor: 'authorId'`) keeps the property name as the
|
|
121
|
+
// canonical FK column source, so this stays non-breaking.
|
|
122
|
+
if (prop.kind === ReferenceKind.SCALAR || prop.kind === ReferenceKind.EMBEDDED || name.startsWith('_')) {
|
|
118
123
|
prop.name = prop.accessor;
|
|
119
124
|
}
|
|
120
125
|
this.initRelation(prop);
|
|
@@ -568,6 +573,12 @@ export class MetadataDiscovery {
|
|
|
568
573
|
}
|
|
569
574
|
}
|
|
570
575
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
576
|
+
// Fail fast when the platform rejects the metadata, so users see the platform-specific
|
|
577
|
+
// error (e.g. "SqlitePlatform does not support partitioned tables") instead of a
|
|
578
|
+
// downstream core validator message that assumes partitioning is supported.
|
|
579
|
+
if (meta.partitionBy && !this.#platform.supportsPartitionedTables()) {
|
|
580
|
+
this.#platform.validateMetadata(meta);
|
|
581
|
+
}
|
|
571
582
|
this.#validator.validateEntityDefinition(this.#metadata, meta.class, this.#config.get('discovery'));
|
|
572
583
|
for (const prop of Object.values(meta.properties)) {
|
|
573
584
|
this.initNullability(prop);
|
|
@@ -594,6 +605,10 @@ export class MetadataDiscovery {
|
|
|
594
605
|
prop.pivotEntity = pivotMeta.class;
|
|
595
606
|
if (prop.inversedBy) {
|
|
596
607
|
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
608
|
+
const targetRoot = prop.targetMeta.root;
|
|
609
|
+
if (targetRoot !== prop.targetMeta && targetRoot.properties[prop.inversedBy]) {
|
|
610
|
+
targetRoot.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
611
|
+
}
|
|
597
612
|
}
|
|
598
613
|
// Propagate pivotEntity to ALL inverse collections using mappedBy pointing at this
|
|
599
614
|
// owner prop. Covers three cases:
|
|
@@ -1019,10 +1034,14 @@ export class MetadataDiscovery {
|
|
|
1019
1034
|
meta.propertyOrder.set(prop.name, (order += 0.01));
|
|
1020
1035
|
});
|
|
1021
1036
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
meta.
|
|
1025
|
-
|
|
1037
|
+
// TPT children have their own tables that don't contain the parent's columns,
|
|
1038
|
+
// so propagating parent indexes/uniques/checks/triggers would target missing columns.
|
|
1039
|
+
if (meta.inheritanceType !== 'tpt' || !meta.tptParent) {
|
|
1040
|
+
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes]);
|
|
1041
|
+
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques]);
|
|
1042
|
+
meta.checks = Utils.unique([...base.checks, ...meta.checks]);
|
|
1043
|
+
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers]);
|
|
1044
|
+
}
|
|
1026
1045
|
const pks = Object.values(meta.properties)
|
|
1027
1046
|
.filter(p => p.primary)
|
|
1028
1047
|
.map(p => p.name);
|
|
@@ -1253,6 +1272,19 @@ export class MetadataDiscovery {
|
|
|
1253
1272
|
});
|
|
1254
1273
|
}
|
|
1255
1274
|
}
|
|
1275
|
+
sameRelationTargetRoot(rootProp, prop) {
|
|
1276
|
+
if (!rootProp || rootProp.kind !== prop.kind) {
|
|
1277
|
+
return false;
|
|
1278
|
+
}
|
|
1279
|
+
if (prop.kind !== ReferenceKind.MANY_TO_ONE &&
|
|
1280
|
+
prop.kind !== ReferenceKind.ONE_TO_ONE &&
|
|
1281
|
+
prop.kind !== ReferenceKind.ONE_TO_MANY &&
|
|
1282
|
+
prop.kind !== ReferenceKind.MANY_TO_MANY) {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
const aRoot = this.#metadata.getByClassName(prop.type, false)?.root;
|
|
1286
|
+
return aRoot != null && aRoot === this.#metadata.getByClassName(rootProp.type, false)?.root;
|
|
1287
|
+
}
|
|
1256
1288
|
initSingleTableInheritance(meta, metadata) {
|
|
1257
1289
|
if (meta.root !== meta && !meta.__processed) {
|
|
1258
1290
|
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
@@ -1293,12 +1325,24 @@ export class MetadataDiscovery {
|
|
|
1293
1325
|
Object.values(meta.properties).forEach(prop => {
|
|
1294
1326
|
const newProp = { ...prop };
|
|
1295
1327
|
const rootProp = meta.root.properties[prop.name];
|
|
1328
|
+
// A child that narrows a relation to a subclass of the root's declared
|
|
1329
|
+
// target (same STI hierarchy) shares the FK column with the root; treat
|
|
1330
|
+
// that as matching so the rename branch below doesn't run (which would
|
|
1331
|
+
// crash — `targetMeta` is only populated later, in `initRelation`).
|
|
1332
|
+
const narrowedRelationOverride = rootProp != null && rootProp.type !== prop.type && this.sameRelationTargetRoot(rootProp, prop);
|
|
1296
1333
|
// stiMerged is set during inlineProperties when a property was merged
|
|
1297
1334
|
// from multiple polymorphic variants with different types. The flag is
|
|
1298
1335
|
// cleared implicitly when the first child claims the root property via
|
|
1299
1336
|
// addProperty below, so subsequent children correctly trigger renaming.
|
|
1300
|
-
const typesMatch = rootProp?.type === prop.type || rootProp?.stiMerged === true;
|
|
1337
|
+
const typesMatch = rootProp?.type === prop.type || rootProp?.stiMerged === true || narrowedRelationOverride;
|
|
1338
|
+
// Inverse-side relations (1:m, inverse m:n) have no FK column on this
|
|
1339
|
+
// entity, so the rename branch — which exists to disambiguate physical
|
|
1340
|
+
// columns shared across STI children — doesn't apply. Skipping it also
|
|
1341
|
+
// avoids crashing on `[...rootProp.fieldNames]` since fieldNames is
|
|
1342
|
+
// never populated for inverse-side properties.
|
|
1343
|
+
const isInverseSideRelation = prop.kind === ReferenceKind.ONE_TO_MANY || (prop.kind === ReferenceKind.MANY_TO_MANY && !prop.owner);
|
|
1301
1344
|
if (rootProp &&
|
|
1345
|
+
!isInverseSideRelation &&
|
|
1302
1346
|
(!typesMatch ||
|
|
1303
1347
|
(rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
1304
1348
|
const name = newProp.name;
|
|
@@ -1347,6 +1391,20 @@ export class MetadataDiscovery {
|
|
|
1347
1391
|
}
|
|
1348
1392
|
newProp.nullable = true;
|
|
1349
1393
|
newProp.inherited = !rootProp;
|
|
1394
|
+
// For narrowed relation overrides, keep the root's declaration intact so
|
|
1395
|
+
// the full target union (e.g. `Food`) is preserved for populates from the
|
|
1396
|
+
// abstract root — otherwise the last child processed would win.
|
|
1397
|
+
if (narrowedRelationOverride) {
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
// STI siblings can declare the same property name on inverse-side relations
|
|
1401
|
+
// pointing to disjoint targets (e.g. `Dog.items → DogItem` and
|
|
1402
|
+
// `Cat.items → CatItem` with no shared base). There's no physical column
|
|
1403
|
+
// to manage at root, and propagating would clobber the first child's
|
|
1404
|
+
// declaration; each child keeps its local typed property instead.
|
|
1405
|
+
if (isInverseSideRelation && rootProp && rootProp.type !== prop.type) {
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1350
1408
|
meta.root.addProperty(newProp);
|
|
1351
1409
|
});
|
|
1352
1410
|
meta.tableName = meta.root.tableName;
|
|
@@ -1362,10 +1420,10 @@ export class MetadataDiscovery {
|
|
|
1362
1420
|
if (meta.root !== meta) {
|
|
1363
1421
|
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
1364
1422
|
}
|
|
1365
|
-
|
|
1366
|
-
if (inheritance === 'tpt' && meta.root
|
|
1367
|
-
meta.inheritanceType = 'tpt';
|
|
1368
|
-
meta.tptChildren
|
|
1423
|
+
// init the root eagerly so children iterated before the root (e.g. alphabetical glob order) still get linked
|
|
1424
|
+
if (meta.root.inheritance === 'tpt' && meta.root.inheritanceType !== 'tpt') {
|
|
1425
|
+
meta.root.inheritanceType = 'tpt';
|
|
1426
|
+
meta.root.tptChildren ??= [];
|
|
1369
1427
|
}
|
|
1370
1428
|
if (meta.root.inheritanceType !== 'tpt') {
|
|
1371
1429
|
return;
|
|
@@ -1524,13 +1582,18 @@ export class MetadataDiscovery {
|
|
|
1524
1582
|
});
|
|
1525
1583
|
}
|
|
1526
1584
|
createDiscriminatorProperty(meta) {
|
|
1527
|
-
|
|
1585
|
+
const prop = {
|
|
1528
1586
|
name: meta.discriminatorColumn,
|
|
1529
1587
|
type: 'string',
|
|
1530
1588
|
enum: true,
|
|
1531
1589
|
kind: ReferenceKind.SCALAR,
|
|
1532
1590
|
userDefined: false,
|
|
1533
|
-
}
|
|
1591
|
+
};
|
|
1592
|
+
// honor the column-name override set via `discriminatorColumn` when `discriminator` is also given
|
|
1593
|
+
if (meta.discriminatorFieldName) {
|
|
1594
|
+
prop.fieldName = meta.discriminatorFieldName;
|
|
1595
|
+
}
|
|
1596
|
+
meta.addProperty(prop);
|
|
1534
1597
|
}
|
|
1535
1598
|
initAutoincrement(meta) {
|
|
1536
1599
|
const pks = meta.getPrimaryProps();
|
|
@@ -1553,7 +1616,7 @@ export class MetadataDiscovery {
|
|
|
1553
1616
|
const table = this.createSchemaTable(meta);
|
|
1554
1617
|
for (const check of meta.checks) {
|
|
1555
1618
|
const fieldNames = check.property ? meta.properties[check.property].fieldNames : [];
|
|
1556
|
-
check.name ??= this.#
|
|
1619
|
+
check.name ??= this.#platform.getIndexName(meta.tableName, fieldNames, 'check');
|
|
1557
1620
|
if (check.expression instanceof Function) {
|
|
1558
1621
|
check.expression = check.expression(columns, table);
|
|
1559
1622
|
}
|
|
@@ -1578,7 +1641,7 @@ export class MetadataDiscovery {
|
|
|
1578
1641
|
}
|
|
1579
1642
|
if (expression) {
|
|
1580
1643
|
meta.checks.push({
|
|
1581
|
-
name: this.#
|
|
1644
|
+
name: this.#platform.getIndexName(meta.tableName, prop.fieldNames, 'check'),
|
|
1582
1645
|
property: prop.name,
|
|
1583
1646
|
expression,
|
|
1584
1647
|
});
|
|
@@ -1754,6 +1817,7 @@ export class MetadataDiscovery {
|
|
|
1754
1817
|
prop.customType &&
|
|
1755
1818
|
!(prop.customType instanceof t.array) &&
|
|
1756
1819
|
!(prop.customType instanceof t.enumArray) &&
|
|
1820
|
+
!(prop.customType instanceof t.json) &&
|
|
1757
1821
|
prop.kind === ReferenceKind.SCALAR) {
|
|
1758
1822
|
const innerType = prop.customType;
|
|
1759
1823
|
innerType.platform = this.#platform;
|
|
@@ -1789,7 +1853,7 @@ export class MetadataDiscovery {
|
|
|
1789
1853
|
if (!prop.customType && prop.array && prop.items) {
|
|
1790
1854
|
prop.customType = new t.enumArray(`${meta.className}.${prop.name}`, prop.items);
|
|
1791
1855
|
}
|
|
1792
|
-
const isArray = prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1856
|
+
const isArray = prop.array || prop.type?.toLowerCase() === 'array' || prop.type?.toString().endsWith('[]');
|
|
1793
1857
|
if (objectEmbeddable && !prop.customType && isArray) {
|
|
1794
1858
|
prop.customType = new t.json();
|
|
1795
1859
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { EntityMetadata, EntityName } from '../typings.js';
|
|
2
|
+
import type { Routine } from './Routine.js';
|
|
2
3
|
import { type MetadataDiscoveryOptions } from '../utils/Configuration.js';
|
|
3
4
|
import type { MetadataStorage } from './MetadataStorage.js';
|
|
4
5
|
/**
|
|
@@ -6,7 +7,32 @@ import type { MetadataStorage } from './MetadataStorage.js';
|
|
|
6
7
|
*/
|
|
7
8
|
export declare class MetadataValidator {
|
|
8
9
|
validateEntityDefinition<T>(metadata: MetadataStorage, name: EntityName<T>, options: MetadataDiscoveryOptions): void;
|
|
10
|
+
validateRoutineDefinition(routine: Routine): void;
|
|
9
11
|
validateDiscovered(discovered: EntityMetadata[], options: MetadataDiscoveryOptions): void;
|
|
12
|
+
private validatePartitioning;
|
|
13
|
+
/**
|
|
14
|
+
* Find the first partition name whose normalized form (case-folded for unquoted segments,
|
|
15
|
+
* quoted segments preserved) has already been seen. Returns the offending name in its
|
|
16
|
+
* original form for the error message.
|
|
17
|
+
*/
|
|
18
|
+
private findDuplicatePartitionName;
|
|
19
|
+
/**
|
|
20
|
+
* Partition names may be bare (`child`), schema-qualified (`schema.child`), or use quoted
|
|
21
|
+
* identifiers (`"my.schema"."child"`). Reject anything with more than one unquoted `.`.
|
|
22
|
+
*/
|
|
23
|
+
private hasValidPartitionName;
|
|
24
|
+
private hasPartitionExpression;
|
|
25
|
+
private validatePartitionKeyConstraints;
|
|
26
|
+
/**
|
|
27
|
+
* Returns the list of physical field names that a partition expression references, or
|
|
28
|
+
* `undefined` when the expression is opaque (callback, or raw SQL like `date_trunc('day', x)`
|
|
29
|
+
* that we cannot statically parse). Opaque expressions intentionally bypass the primary-key /
|
|
30
|
+
* unique-constraint coverage checks — users are trusted to ensure the referenced columns are
|
|
31
|
+
* part of the partition key, since PostgreSQL will surface the violation at DDL execution.
|
|
32
|
+
*/
|
|
33
|
+
private getPartitionKeyFields;
|
|
34
|
+
private resolvePartitionKeyField;
|
|
35
|
+
private getConstraintFields;
|
|
10
36
|
private validateReference;
|
|
11
37
|
private validateTargetKey;
|
|
12
38
|
/**
|