@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.
- package/EntityManager.d.ts +63 -11
- package/EntityManager.js +180 -21
- package/drivers/DatabaseDriver.d.ts +1 -0
- package/drivers/DatabaseDriver.js +3 -0
- package/drivers/IDatabaseDriver.d.ts +44 -1
- package/entity/BaseEntity.d.ts +68 -1
- package/entity/BaseEntity.js +18 -0
- package/entity/Collection.d.ts +6 -3
- package/entity/Collection.js +15 -4
- package/entity/EntityFactory.js +20 -1
- package/entity/EntityLoader.d.ts +3 -1
- package/entity/EntityLoader.js +39 -16
- package/entity/EntityRepository.d.ts +27 -9
- package/entity/EntityRepository.js +12 -0
- package/entity/Reference.d.ts +42 -1
- package/entity/Reference.js +9 -0
- package/entity/defineEntity.d.ts +84 -15
- package/entity/defineEntity.js +10 -6
- package/entity/utils.js +4 -5
- package/errors.d.ts +2 -0
- package/errors.js +4 -0
- package/index.d.ts +2 -2
- package/index.js +1 -1
- package/metadata/EntitySchema.js +3 -0
- package/metadata/MetadataDiscovery.d.ts +12 -0
- package/metadata/MetadataDiscovery.js +155 -10
- package/metadata/MetadataValidator.d.ts +24 -0
- package/metadata/MetadataValidator.js +201 -0
- package/metadata/types.d.ts +71 -3
- package/naming-strategy/AbstractNamingStrategy.d.ts +1 -1
- package/naming-strategy/NamingStrategy.d.ts +1 -1
- package/package.json +1 -1
- package/platforms/Platform.d.ts +7 -0
- package/platforms/Platform.js +15 -1
- package/typings.d.ts +139 -10
- package/typings.js +21 -4
- package/utils/AbstractMigrator.d.ts +11 -1
- package/utils/AbstractMigrator.js +203 -0
- package/utils/Configuration.d.ts +7 -0
- package/utils/Configuration.js +1 -0
- package/utils/DataloaderUtils.d.ts +10 -1
- package/utils/DataloaderUtils.js +78 -0
- package/utils/QueryHelper.d.ts +16 -0
- package/utils/QueryHelper.js +15 -0
- package/utils/Utils.js +1 -1
- package/utils/fs-utils.d.ts +2 -0
- package/utils/fs-utils.js +7 -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/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, FormulaCallback, EntityCtor, IsNever, IWrappedEntity, DefineConfig, Config, MaybePromise } 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
|
}>;
|
|
@@ -104,8 +118,16 @@ export interface PropertyChain<Value, Options> {
|
|
|
104
118
|
customOrder(...customOrder: string[] | number[] | boolean[]): PropertyChain<Value, Options>;
|
|
105
119
|
extra(extra: string): PropertyChain<Value, Options>;
|
|
106
120
|
ignoreSchemaChanges(...ignoreSchemaChanges: ('type' | 'extra' | 'default')[]): PropertyChain<Value, Options>;
|
|
107
|
-
index
|
|
108
|
-
|
|
121
|
+
/** Explicitly specify index on a property. When a string name is passed, it enables type-safe `using` in `FindOptions`. */
|
|
122
|
+
index<N extends string>(name: N): PropertyChain<Value, Omit<Options, 'index'> & {
|
|
123
|
+
index: N;
|
|
124
|
+
}>;
|
|
125
|
+
index(index?: boolean): PropertyChain<Value, Options>;
|
|
126
|
+
/** Set column as unique. When a string name is passed, it enables type-safe `using` in `FindOptions`. (SQL only) */
|
|
127
|
+
unique<N extends string>(name: N): PropertyChain<Value, Omit<Options, 'unique'> & {
|
|
128
|
+
unique: N;
|
|
129
|
+
}>;
|
|
130
|
+
unique(unique?: boolean): PropertyChain<Value, Options>;
|
|
109
131
|
comment(comment: string): PropertyChain<Value, Options>;
|
|
110
132
|
accessor(accessor?: string | boolean): PropertyChain<Value, Options>;
|
|
111
133
|
eager(eager?: boolean): HasKind<Options, 'm:1' | '1:m' | '1:1' | 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -117,7 +139,9 @@ export interface PropertyChain<Value, Options> {
|
|
|
117
139
|
owner(): HasKind<Options, '1:1' | 'm:n'> extends true ? PropertyChain<Value, Omit<Options, 'owner'> & {
|
|
118
140
|
owner: true;
|
|
119
141
|
}> : never;
|
|
120
|
-
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ?
|
|
142
|
+
mapToPk(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
143
|
+
lazyRef: true;
|
|
144
|
+
} ? never : PropertyChain<Value, Omit<Options, 'mapToPk'> & {
|
|
121
145
|
mapToPk: true;
|
|
122
146
|
}> : never;
|
|
123
147
|
orphanRemoval(orphanRemoval?: boolean): HasKind<Options, '1:m' | '1:1'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -127,9 +151,13 @@ export interface PropertyChain<Value, Options> {
|
|
|
127
151
|
pivotEntity(pivotEntity: () => EntityName): HasKind<Options, 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
128
152
|
fixedOrder(fixedOrder?: boolean): HasKind<Options, 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
129
153
|
fixedOrderColumn(fixedOrderColumn: string): HasKind<Options, 'm:n'> extends true ? PropertyChain<Value, Options> : never;
|
|
130
|
-
array():
|
|
154
|
+
array(): Options extends {
|
|
155
|
+
kind: infer K extends string;
|
|
156
|
+
} ? K extends 'embedded' | 'enum' ? PropertyChain<Value, Omit<Options, 'array'> & {
|
|
131
157
|
array: true;
|
|
132
|
-
}> : never
|
|
158
|
+
}> : never : PropertyChain<Value, Omit<Options, 'array'> & {
|
|
159
|
+
array: true;
|
|
160
|
+
}>;
|
|
133
161
|
prefix(prefix: string | boolean): HasKind<Options, 'embedded'> extends true ? PropertyChain<Value, Options> : never;
|
|
134
162
|
prefixMode(prefixMode: EmbeddedPrefixMode): HasKind<Options, 'embedded'> extends true ? PropertyChain<Value, Options> : never;
|
|
135
163
|
object(object?: boolean): HasKind<Options, 'embedded'> extends true ? PropertyChain<Value, Options> : never;
|
|
@@ -313,9 +341,24 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
313
341
|
/**
|
|
314
342
|
* 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.
|
|
315
343
|
*/
|
|
316
|
-
ref():
|
|
344
|
+
ref(): Options extends {
|
|
345
|
+
lazyRef: true;
|
|
346
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'ref'> & {
|
|
317
347
|
ref: true;
|
|
318
348
|
}, IncludeKeys>;
|
|
349
|
+
/**
|
|
350
|
+
* Mark a to-one relation (`m:1`/`1:1`) as a {@apilink LazyRef} — the runtime value stays a plain entity
|
|
351
|
+
* (no `Reference` wrapper) but the TypeScript type restricts access to the primary key until `Loaded<>`
|
|
352
|
+
* narrows it. Type-level only; does not set `ref: true`. Not compatible with `.ref()` or `.mapToPk()` —
|
|
353
|
+
* those chains are rejected in both directions at compile time.
|
|
354
|
+
*/
|
|
355
|
+
lazyRef(): HasKind<Options, 'm:1' | '1:1'> extends true ? Options extends {
|
|
356
|
+
ref: true;
|
|
357
|
+
} | {
|
|
358
|
+
mapToPk: true;
|
|
359
|
+
} ? never : UniversalPropertyOptionsBuilder<Value, Omit<Options, 'lazyRef'> & {
|
|
360
|
+
lazyRef: true;
|
|
361
|
+
}, IncludeKeys> : never;
|
|
319
362
|
/**
|
|
320
363
|
* Set to true to omit the property when {@link https://mikro-orm.io/docs/serializing Serializing}.
|
|
321
364
|
*/
|
|
@@ -334,12 +377,20 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
334
377
|
concurrencyCheck(concurrencyCheck?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
335
378
|
/**
|
|
336
379
|
* Explicitly specify index on a property.
|
|
380
|
+
* When a string name is passed, it is captured as a literal type for use with the `using` option in `FindOptions`.
|
|
337
381
|
*/
|
|
338
|
-
index
|
|
382
|
+
index<N extends string>(name: N): Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'index'> & {
|
|
383
|
+
index: N;
|
|
384
|
+
}, IncludeKeys>, IncludeKeys>;
|
|
385
|
+
index(index?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
339
386
|
/**
|
|
340
387
|
* Set column as unique for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
388
|
+
* When a string name is passed, it is captured as a literal type for use with the `using` option in `FindOptions`.
|
|
341
389
|
*/
|
|
342
|
-
unique
|
|
390
|
+
unique<N extends string>(name: N): Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'unique'> & {
|
|
391
|
+
unique: N;
|
|
392
|
+
}, IncludeKeys>, IncludeKeys>;
|
|
393
|
+
unique(unique?: boolean): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
343
394
|
/**
|
|
344
395
|
* Specify column with check constraints. (Postgres driver only)
|
|
345
396
|
*
|
|
@@ -492,7 +543,9 @@ export declare class UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys
|
|
|
492
543
|
/** What to do when the reference to the target entity gets updated. */
|
|
493
544
|
updateRule(updateRule: 'cascade' | 'no action' | 'set null' | 'set default' | AnyString): Pick<UniversalPropertyOptionsBuilder<Value, Options, IncludeKeys>, IncludeKeys>;
|
|
494
545
|
/** Map this relation to the primary key value instead of an entity. */
|
|
495
|
-
mapToPk():
|
|
546
|
+
mapToPk(): Options extends {
|
|
547
|
+
lazyRef: true;
|
|
548
|
+
} ? never : Pick<UniversalPropertyOptionsBuilder<Value, Omit<Options, 'mapToPk'> & {
|
|
496
549
|
mapToPk: true;
|
|
497
550
|
}, IncludeKeys>, IncludeKeys>;
|
|
498
551
|
/** 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. */
|
|
@@ -536,7 +589,7 @@ export type PropertyBuilders = {
|
|
|
536
589
|
datetime: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.datetime>, EmptyOptions, IncludeKeysForProperty>;
|
|
537
590
|
time: (length?: number) => UniversalPropertyOptionsBuilder<InferPropertyValueType<typeof types.time>, EmptyOptions, IncludeKeysForProperty>;
|
|
538
591
|
type: <T extends PropertyValueType>(type: T) => UniversalPropertyOptionsBuilder<InferPropertyValueType<T>, EmptyOptions, IncludeKeysForProperty>;
|
|
539
|
-
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>;
|
|
592
|
+
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>;
|
|
540
593
|
embedded: <Target extends EntityTarget | EntityTarget[]>(target: Target) => PropertyChain<InferEntity<Target extends (infer T)[] ? T : Target>, EmptyOptions & {
|
|
541
594
|
kind: 'embedded';
|
|
542
595
|
}>;
|
|
@@ -556,7 +609,7 @@ export type PropertyBuilders = {
|
|
|
556
609
|
/** Own keys + base entity keys (when TBase is not `never`). Guards against `keyof never = string | number | symbol`. */
|
|
557
610
|
type AllKeys<TProperties, TBase> = keyof TProperties | (IsNever<TBase> extends true ? never : keyof TBase);
|
|
558
611
|
/** Metadata descriptor for `defineEntity()`, combining entity options with property definitions. */
|
|
559
|
-
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' | 'repository' | 'filters' | 'orderBy'> {
|
|
612
|
+
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'> {
|
|
560
613
|
name: TName;
|
|
561
614
|
tableName?: TTableName;
|
|
562
615
|
extends?: {
|
|
@@ -591,6 +644,7 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
591
644
|
type?: string;
|
|
592
645
|
options?: Dictionary;
|
|
593
646
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
647
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
594
648
|
columns?: IndexColumnOptions[];
|
|
595
649
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
596
650
|
fillFactor?: number;
|
|
@@ -603,12 +657,22 @@ export interface EntityMetadataWithProperties<TName extends string, TTableName e
|
|
|
603
657
|
name?: string;
|
|
604
658
|
options?: Dictionary;
|
|
605
659
|
expression?: string | IndexCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
660
|
+
where?: string | FilterQuery<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
606
661
|
deferMode?: DeferMode | `${DeferMode}`;
|
|
607
662
|
columns?: IndexColumnOptions[];
|
|
608
663
|
include?: NoInfer<AllKeys<TProperties, TBase>> | NoInfer<AllKeys<TProperties, TBase>>[];
|
|
609
664
|
fillFactor?: number;
|
|
610
665
|
disabled?: boolean;
|
|
611
666
|
}[];
|
|
667
|
+
triggers?: {
|
|
668
|
+
name?: string;
|
|
669
|
+
timing: 'before' | 'after' | 'instead of';
|
|
670
|
+
events: ('insert' | 'update' | 'delete' | 'truncate')[];
|
|
671
|
+
forEach?: 'row' | 'statement';
|
|
672
|
+
body?: string | Raw | TriggerCallback<InferEntityFromProperties<TProperties, TPK, TBase>>;
|
|
673
|
+
when?: string;
|
|
674
|
+
expression?: string;
|
|
675
|
+
}[];
|
|
612
676
|
}
|
|
613
677
|
/** Defines an entity schema using property builders, with full type inference from the property definitions. */
|
|
614
678
|
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>;
|
|
@@ -665,7 +729,9 @@ export type InferEntityFromProperties<Properties extends Record<string, any>, PK
|
|
|
665
729
|
[Config]?: DefineConfig<{
|
|
666
730
|
forceObject: true;
|
|
667
731
|
}>;
|
|
668
|
-
} : {})
|
|
732
|
+
} : {}) & {
|
|
733
|
+
[IndexHints]?: [Properties];
|
|
734
|
+
};
|
|
669
735
|
type InferCombinedPrimaryKey<Properties extends Record<string, any>, PK, Base> = PK extends undefined ? CombinePrimaryKeys<InferPrimaryKey<Properties>, ExtractBasePrimaryKey<Base>> : PK;
|
|
670
736
|
type ExtractBasePrimaryKey<Base> = Base extends {
|
|
671
737
|
[PrimaryKeyProp]?: infer BasePK;
|
|
@@ -707,6 +773,9 @@ type MaybeNullable<Value, Options> = Options extends {
|
|
|
707
773
|
type MaybeRelationRef<Value, Options> = Options extends {
|
|
708
774
|
mapToPk: true;
|
|
709
775
|
} ? Value : Options extends {
|
|
776
|
+
lazyRef: true;
|
|
777
|
+
kind: '1:1' | 'm:1';
|
|
778
|
+
} ? Value extends object ? LazyRef<Value> : never : Options extends {
|
|
710
779
|
ref: true;
|
|
711
780
|
kind: '1:1';
|
|
712
781
|
} ? 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
|
*/
|
|
@@ -191,15 +201,9 @@ export class UniversalPropertyOptionsBuilder {
|
|
|
191
201
|
concurrencyCheck(concurrencyCheck = true) {
|
|
192
202
|
return this.assignOptions({ concurrencyCheck });
|
|
193
203
|
}
|
|
194
|
-
/**
|
|
195
|
-
* Explicitly specify index on a property.
|
|
196
|
-
*/
|
|
197
204
|
index(index = true) {
|
|
198
205
|
return this.assignOptions({ index });
|
|
199
206
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Set column as unique for {@link https://mikro-orm.io/docs/schema-generator Schema Generator}. (SQL only)
|
|
202
|
-
*/
|
|
203
207
|
unique(unique = true) {
|
|
204
208
|
return this.assignOptions({ unique });
|
|
205
209
|
}
|
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/errors.d.ts
CHANGED
|
@@ -74,6 +74,8 @@ export declare class MetadataError<T extends AnyEntity = AnyEntity> extends Vali
|
|
|
74
74
|
static viewEntityWithoutExpression(meta: EntityMetadata): MetadataError;
|
|
75
75
|
static mixedInheritanceStrategies(root: EntityMetadata, child: EntityMetadata): MetadataError;
|
|
76
76
|
static tptNotSupportedByDriver(meta: EntityMetadata): MetadataError;
|
|
77
|
+
/** Thrown when database triggers are defined on an entity using a driver that does not support them. */
|
|
78
|
+
static triggersNotSupportedByDriver(meta: EntityMetadata): MetadataError;
|
|
77
79
|
private static fromMessage;
|
|
78
80
|
}
|
|
79
81
|
/** Error thrown when an entity lookup fails to find the expected result. */
|
package/errors.js
CHANGED
|
@@ -243,6 +243,10 @@ export class MetadataError extends ValidationError {
|
|
|
243
243
|
static tptNotSupportedByDriver(meta) {
|
|
244
244
|
return new MetadataError(`Entity ${meta.className} uses TPT (Table-Per-Type) inheritance which is not supported by the current driver. TPT requires SQL JOINs and is only available with SQL drivers.`);
|
|
245
245
|
}
|
|
246
|
+
/** Thrown when database triggers are defined on an entity using a driver that does not support them. */
|
|
247
|
+
static triggersNotSupportedByDriver(meta) {
|
|
248
|
+
return new MetadataError(`Entity ${meta.className} defines database triggers which are not supported by the current driver. Triggers are only available with SQL drivers.`);
|
|
249
|
+
}
|
|
246
250
|
static fromMessage(meta, prop, message) {
|
|
247
251
|
return new MetadataError(`${meta.className}.${prop.name} ${message}`);
|
|
248
252
|
}
|
package/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
|
-
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, } 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, IMigrationRunner, IEntityGenerator, ISeedManager, SeederObject, IMigratorStorage, RequiredEntityData, CheckCallback, 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, GeneratedColumnCallback, FilterDef, EntityCtor, Subquery, PopulateHintOptions, Prefixes, } from './typings.js';
|
|
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, 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, 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/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
* @module core
|
|
4
4
|
*/
|
|
5
|
-
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, } from './typings.js';
|
|
5
|
+
export { EntityMetadata, PrimaryKeyProp, EntityRepositoryType, OptionalProps, EagerProps, HiddenProps, Config, EntityName, IndexHints, } from './typings.js';
|
|
6
6
|
export * from './enums.js';
|
|
7
7
|
export * from './errors.js';
|
|
8
8
|
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);
|
|
@@ -54,6 +54,16 @@ export declare class MetadataDiscovery {
|
|
|
54
54
|
* Define properties for a polymorphic pivot table.
|
|
55
55
|
*/
|
|
56
56
|
private definePolymorphicPivotProperties;
|
|
57
|
+
/**
|
|
58
|
+
* Mirror of definePolymorphicPivotProperties for union-target M:N
|
|
59
|
+
* (e.g. Post.attachments -> Image | Video via shared pivot with a target-side discriminator).
|
|
60
|
+
*
|
|
61
|
+
* Pivot shape:
|
|
62
|
+
* (owner_fk..., discriminator_column, target_fk...)
|
|
63
|
+
* - owner side is a normal M:1 to the single owner entity
|
|
64
|
+
* - target side is a discriminator column + per-target-type virtual M:1 relations
|
|
65
|
+
*/
|
|
66
|
+
private defineUnionTargetPolymorphicPivotProperties;
|
|
57
67
|
/**
|
|
58
68
|
* Create a virtual M:1 relation from pivot to a polymorphic owner entity.
|
|
59
69
|
* This enables single-query join loading for inverse-side polymorphic M:N.
|
|
@@ -66,6 +76,7 @@ export declare class MetadataDiscovery {
|
|
|
66
76
|
private initPolyEmbeddables;
|
|
67
77
|
private initPolymorphicRelation;
|
|
68
78
|
private initEmbeddables;
|
|
79
|
+
private sameRelationTargetRoot;
|
|
69
80
|
private initSingleTableInheritance;
|
|
70
81
|
/**
|
|
71
82
|
* First pass of TPT initialization: sets up hierarchy relationships
|
|
@@ -105,6 +116,7 @@ export declare class MetadataDiscovery {
|
|
|
105
116
|
private initAutoincrement;
|
|
106
117
|
private createSchemaTable;
|
|
107
118
|
private initCheckConstraints;
|
|
119
|
+
private initTriggers;
|
|
108
120
|
private initGeneratedColumn;
|
|
109
121
|
private getDefaultVersionValue;
|
|
110
122
|
private inferDefaultValue;
|
|
@@ -159,6 +159,7 @@ export class MetadataDiscovery {
|
|
|
159
159
|
forEachProp((m, p) => this.initGeneratedColumn(m, p));
|
|
160
160
|
filtered.forEach(meta => this.initAutoincrement(meta)); // once again after we init custom types
|
|
161
161
|
filtered.forEach(meta => this.initCheckConstraints(meta));
|
|
162
|
+
filtered.forEach(meta => this.initTriggers(meta));
|
|
162
163
|
forEachProp((_m, p) => {
|
|
163
164
|
this.initDefaultValue(p);
|
|
164
165
|
this.inferTypeFromDefault(p);
|
|
@@ -479,17 +480,28 @@ export class MetadataDiscovery {
|
|
|
479
480
|
prop.polymorphic = prop2.polymorphic;
|
|
480
481
|
prop.discriminator = prop2.discriminator;
|
|
481
482
|
prop.discriminatorColumn = prop2.discriminatorColumn;
|
|
482
|
-
|
|
483
|
+
// For a union-target pivot each inverse side sits on one specific target class, so its
|
|
484
|
+
// discriminator value is that class's tableName. For Rails-style, prop2 has a single fixed value.
|
|
485
|
+
prop.discriminatorValue = QueryHelper.isUnionTargetPolymorphic(prop2) ? meta.tableName : prop2.discriminatorValue;
|
|
483
486
|
}
|
|
484
487
|
prop.referencedColumnNames ??= Utils.flatten(meta.primaryKeys.map(primaryKey => meta.properties[primaryKey].fieldNames));
|
|
485
|
-
//
|
|
486
|
-
|
|
488
|
+
// Union-target polymorphic M:N: owner side is fixed (real FK), target side uses discriminator-derived names.
|
|
489
|
+
const isUnionTargetMN = QueryHelper.isUnionTargetPolymorphic(prop);
|
|
490
|
+
if (prop.polymorphic && prop.discriminator && !isUnionTargetMN) {
|
|
491
|
+
// Rails-style: owner side is polymorphic, uses discriminator base name (e.g. taggable_id instead of post_id)
|
|
487
492
|
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, referencedColumnName, prop.referencedColumnNames.length > 1));
|
|
488
493
|
}
|
|
489
494
|
else {
|
|
490
495
|
prop.joinColumns ??= prop.referencedColumnNames.map(referencedColumnName => this.#namingStrategy.joinKeyColumnName(meta.root.className, referencedColumnName, meta.compositePK));
|
|
491
496
|
}
|
|
492
|
-
|
|
497
|
+
if (isUnionTargetMN) {
|
|
498
|
+
// Target side uses discriminator base name (e.g. attachable_id — shared across Image/Video)
|
|
499
|
+
const targetPkCols = Utils.flatten(meta2.primaryKeys.map(pk => meta2.properties[pk].fieldNames));
|
|
500
|
+
prop.inverseJoinColumns ??= targetPkCols.map(fieldName => this.#namingStrategy.joinKeyColumnName(prop.discriminator, fieldName, targetPkCols.length > 1));
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
prop.inverseJoinColumns ??= this.initManyToOneFieldName(prop, meta2.root.className);
|
|
504
|
+
}
|
|
493
505
|
}
|
|
494
506
|
isExplicitTableName(meta) {
|
|
495
507
|
return meta.tableName !== this.#namingStrategy.classToTableName(meta.className);
|
|
@@ -556,6 +568,12 @@ export class MetadataDiscovery {
|
|
|
556
568
|
}
|
|
557
569
|
}
|
|
558
570
|
meta.forceConstructor ??= this.shouldForceConstructorUsage(meta);
|
|
571
|
+
// Fail fast when the platform rejects the metadata, so users see the platform-specific
|
|
572
|
+
// error (e.g. "SqlitePlatform does not support partitioned tables") instead of a
|
|
573
|
+
// downstream core validator message that assumes partitioning is supported.
|
|
574
|
+
if (meta.partitionBy && !this.#platform.supportsPartitionedTables()) {
|
|
575
|
+
this.#platform.validateMetadata(meta);
|
|
576
|
+
}
|
|
559
577
|
this.#validator.validateEntityDefinition(this.#metadata, meta.class, this.#config.get('discovery'));
|
|
560
578
|
for (const prop of Object.values(meta.properties)) {
|
|
561
579
|
this.initNullability(prop);
|
|
@@ -583,6 +601,24 @@ export class MetadataDiscovery {
|
|
|
583
601
|
if (prop.inversedBy) {
|
|
584
602
|
prop.targetMeta.properties[prop.inversedBy].pivotEntity = pivotMeta.class;
|
|
585
603
|
}
|
|
604
|
+
// Propagate pivotEntity to ALL inverse collections using mappedBy pointing at this
|
|
605
|
+
// owner prop. Covers three cases:
|
|
606
|
+
// - regular inverse (Tag.posts mappedBy Post.tags) — handled by inversedBy above
|
|
607
|
+
// - union-target inverse (Image.posts mappedBy Post.attachments) — on each polymorph target
|
|
608
|
+
// - merged inverse (Tag.owners mappedBy [Post,Video].tags) — union collection on the target
|
|
609
|
+
const inverseCandidates = QueryHelper.isUnionTargetPolymorphic(prop)
|
|
610
|
+
? prop.polymorphTargets
|
|
611
|
+
: [prop.targetMeta];
|
|
612
|
+
for (const targetMeta of inverseCandidates) {
|
|
613
|
+
for (const inverseProp of Object.values(targetMeta.properties)) {
|
|
614
|
+
if (inverseProp.kind === ReferenceKind.MANY_TO_MANY &&
|
|
615
|
+
inverseProp.mappedBy === prop.name &&
|
|
616
|
+
!inverseProp.pivotEntity) {
|
|
617
|
+
inverseProp.pivotEntity = pivotMeta.class;
|
|
618
|
+
inverseProp.pivotTable = pivotMeta.tableName;
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
586
622
|
return pivotMeta;
|
|
587
623
|
});
|
|
588
624
|
}
|
|
@@ -721,8 +757,12 @@ export class MetadataDiscovery {
|
|
|
721
757
|
}
|
|
722
758
|
}
|
|
723
759
|
}
|
|
724
|
-
//
|
|
725
|
-
if (prop.
|
|
760
|
+
// Union-target polymorphic M:N: discriminator + target FK share the pivot across multiple target types
|
|
761
|
+
if (prop.discriminatorColumn && QueryHelper.isUnionTargetPolymorphic(prop)) {
|
|
762
|
+
this.defineUnionTargetPolymorphicPivotProperties(pivotMeta2, meta, prop);
|
|
763
|
+
}
|
|
764
|
+
else if (prop.polymorphic && prop.discriminatorColumn) {
|
|
765
|
+
// Rails-style polymorphic M:N: multiple owners share the pivot, single target type
|
|
726
766
|
this.definePolymorphicPivotProperties(pivotMeta2, meta, prop, targetMeta);
|
|
727
767
|
}
|
|
728
768
|
else {
|
|
@@ -809,6 +849,33 @@ export class MetadataDiscovery {
|
|
|
809
849
|
pivotMeta.polymorphicDiscriminatorMap ??= {};
|
|
810
850
|
pivotMeta.polymorphicDiscriminatorMap[prop.discriminatorValue] = meta.class;
|
|
811
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* Mirror of definePolymorphicPivotProperties for union-target M:N
|
|
854
|
+
* (e.g. Post.attachments -> Image | Video via shared pivot with a target-side discriminator).
|
|
855
|
+
*
|
|
856
|
+
* Pivot shape:
|
|
857
|
+
* (owner_fk..., discriminator_column, target_fk...)
|
|
858
|
+
* - owner side is a normal M:1 to the single owner entity
|
|
859
|
+
* - target side is a discriminator column + per-target-type virtual M:1 relations
|
|
860
|
+
*/
|
|
861
|
+
defineUnionTargetPolymorphicPivotProperties(pivotMeta, meta, prop) {
|
|
862
|
+
const discriminatorColumn = prop.discriminatorColumn;
|
|
863
|
+
const targets = prop.polymorphTargets;
|
|
864
|
+
pivotMeta.properties[meta.name + '_owner'] = this.definePivotProperty(prop, meta.name + '_owner', meta.class, prop.discriminator, true, false);
|
|
865
|
+
const discriminatorProp = this.createPivotScalarProperty(discriminatorColumn, [this.#platform.getVarcharTypeDeclarationSQL(prop)], [discriminatorColumn], { type: 'string', primary: true, nullable: false });
|
|
866
|
+
this.initFieldName(discriminatorProp);
|
|
867
|
+
pivotMeta.properties[discriminatorColumn] = discriminatorProp;
|
|
868
|
+
const firstTargetColumnTypes = this.getPrimaryKeyColumnTypes(targets[0]);
|
|
869
|
+
pivotMeta.properties[prop.discriminator] = this.createPivotScalarProperty(prop.discriminator, firstTargetColumnTypes, [...prop.inverseJoinColumns], { type: targets[0].className, primary: true, nullable: false });
|
|
870
|
+
pivotMeta.polymorphicDiscriminatorMap ??= {};
|
|
871
|
+
for (const targetMeta of targets) {
|
|
872
|
+
const relationName = `${prop.discriminator}_${targetMeta.tableName}`;
|
|
873
|
+
const relation = this.definePolymorphicOwnerRelation(prop, relationName, targetMeta);
|
|
874
|
+
relation.joinColumns = relation.fieldNames = relation.ownColumns = [...prop.inverseJoinColumns];
|
|
875
|
+
pivotMeta.properties[relationName] = relation;
|
|
876
|
+
pivotMeta.polymorphicDiscriminatorMap[targetMeta.tableName] = targetMeta.class;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
812
879
|
/**
|
|
813
880
|
* Create a virtual M:1 relation from pivot to a polymorphic owner entity.
|
|
814
881
|
* This enables single-query join loading for inverse-side polymorphic M:N.
|
|
@@ -961,6 +1028,7 @@ export class MetadataDiscovery {
|
|
|
961
1028
|
meta.indexes = Utils.unique([...base.indexes, ...meta.indexes]);
|
|
962
1029
|
meta.uniques = Utils.unique([...base.uniques, ...meta.uniques]);
|
|
963
1030
|
meta.checks = Utils.unique([...base.checks, ...meta.checks]);
|
|
1031
|
+
meta.triggers = Utils.unique([...base.triggers, ...meta.triggers]);
|
|
964
1032
|
const pks = Object.values(meta.properties)
|
|
965
1033
|
.filter(p => p.primary)
|
|
966
1034
|
.map(p => p.name);
|
|
@@ -1045,11 +1113,15 @@ export class MetadataDiscovery {
|
|
|
1045
1113
|
prop.discriminatorColumn ??= this.#namingStrategy.discriminatorColumnName(prop.discriminator);
|
|
1046
1114
|
prop.createForeignKeyConstraint = false;
|
|
1047
1115
|
const isToOne = [ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind);
|
|
1048
|
-
|
|
1116
|
+
const isUnionTargetMN = prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(prop.target);
|
|
1117
|
+
if (isToOne || isUnionTargetMN) {
|
|
1049
1118
|
const types = prop.type.split(/ ?\| ?/);
|
|
1050
1119
|
prop.polymorphTargets = discovered.filter(m => types.includes(m.className) && !m.embeddable);
|
|
1051
1120
|
prop.targetMeta = prop.polymorphTargets[0];
|
|
1052
1121
|
prop.referencedPKs = prop.targetMeta?.primaryKeys;
|
|
1122
|
+
if (isUnionTargetMN && prop.polymorphTargets.length < 2) {
|
|
1123
|
+
throw new MetadataError(`${meta.className}.${prop.name} union-target polymorphic M:N requires at least two target entity types; use a regular M:N relation for a single target.`);
|
|
1124
|
+
}
|
|
1053
1125
|
}
|
|
1054
1126
|
if (prop.discriminatorMap) {
|
|
1055
1127
|
const normalizedMap = {};
|
|
@@ -1065,7 +1137,7 @@ export class MetadataDiscovery {
|
|
|
1065
1137
|
}
|
|
1066
1138
|
prop.discriminatorMap = normalizedMap;
|
|
1067
1139
|
}
|
|
1068
|
-
else if (isToOne) {
|
|
1140
|
+
else if (isToOne || isUnionTargetMN) {
|
|
1069
1141
|
prop.discriminatorMap = {};
|
|
1070
1142
|
const tableNameToTarget = new Map();
|
|
1071
1143
|
for (const target of prop.polymorphTargets) {
|
|
@@ -1187,6 +1259,16 @@ export class MetadataDiscovery {
|
|
|
1187
1259
|
});
|
|
1188
1260
|
}
|
|
1189
1261
|
}
|
|
1262
|
+
sameRelationTargetRoot(rootProp, prop) {
|
|
1263
|
+
if (!rootProp || rootProp.kind !== prop.kind) {
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
if (prop.kind !== ReferenceKind.MANY_TO_ONE && prop.kind !== ReferenceKind.ONE_TO_ONE) {
|
|
1267
|
+
return false;
|
|
1268
|
+
}
|
|
1269
|
+
const aRoot = this.#metadata.getByClassName(prop.type, false)?.root;
|
|
1270
|
+
return aRoot != null && aRoot === this.#metadata.getByClassName(rootProp.type, false)?.root;
|
|
1271
|
+
}
|
|
1190
1272
|
initSingleTableInheritance(meta, metadata) {
|
|
1191
1273
|
if (meta.root !== meta && !meta.__processed) {
|
|
1192
1274
|
meta.root = metadata.find(m => m.class === meta.root.class);
|
|
@@ -1227,11 +1309,16 @@ export class MetadataDiscovery {
|
|
|
1227
1309
|
Object.values(meta.properties).forEach(prop => {
|
|
1228
1310
|
const newProp = { ...prop };
|
|
1229
1311
|
const rootProp = meta.root.properties[prop.name];
|
|
1312
|
+
// A child that narrows a relation to a subclass of the root's declared
|
|
1313
|
+
// target (same STI hierarchy) shares the FK column with the root; treat
|
|
1314
|
+
// that as matching so the rename branch below doesn't run (which would
|
|
1315
|
+
// crash — `targetMeta` is only populated later, in `initRelation`).
|
|
1316
|
+
const narrowedRelationOverride = rootProp != null && rootProp.type !== prop.type && this.sameRelationTargetRoot(rootProp, prop);
|
|
1230
1317
|
// stiMerged is set during inlineProperties when a property was merged
|
|
1231
1318
|
// from multiple polymorphic variants with different types. The flag is
|
|
1232
1319
|
// cleared implicitly when the first child claims the root property via
|
|
1233
1320
|
// addProperty below, so subsequent children correctly trigger renaming.
|
|
1234
|
-
const typesMatch = rootProp?.type === prop.type || rootProp?.stiMerged === true;
|
|
1321
|
+
const typesMatch = rootProp?.type === prop.type || rootProp?.stiMerged === true || narrowedRelationOverride;
|
|
1235
1322
|
if (rootProp &&
|
|
1236
1323
|
(!typesMatch ||
|
|
1237
1324
|
(rootProp.fieldNames && prop.fieldNames && !compareArrays(rootProp.fieldNames, prop.fieldNames)))) {
|
|
@@ -1281,6 +1368,12 @@ export class MetadataDiscovery {
|
|
|
1281
1368
|
}
|
|
1282
1369
|
newProp.nullable = true;
|
|
1283
1370
|
newProp.inherited = !rootProp;
|
|
1371
|
+
// For narrowed relation overrides, keep the root's declaration intact so
|
|
1372
|
+
// the full target union (e.g. `Food`) is preserved for populates from the
|
|
1373
|
+
// abstract root — otherwise the last child processed would win.
|
|
1374
|
+
if (narrowedRelationOverride) {
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1284
1377
|
meta.root.addProperty(newProp);
|
|
1285
1378
|
});
|
|
1286
1379
|
meta.tableName = meta.root.tableName;
|
|
@@ -1520,6 +1613,27 @@ export class MetadataDiscovery {
|
|
|
1520
1613
|
}
|
|
1521
1614
|
}
|
|
1522
1615
|
}
|
|
1616
|
+
initTriggers(meta) {
|
|
1617
|
+
if (meta.triggers.length === 0) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const columns = meta.createSchemaColumnMappingObject();
|
|
1621
|
+
const table = this.createSchemaTable(meta);
|
|
1622
|
+
for (const trigger of meta.triggers) {
|
|
1623
|
+
if (trigger.body && trigger.expression) {
|
|
1624
|
+
throw new MetadataError(`Trigger "${trigger.name ?? '(unnamed)'}" on entity ${meta.className} defines both 'body' and 'expression'. Use one or the other.`);
|
|
1625
|
+
}
|
|
1626
|
+
if (!trigger.body && !trigger.expression) {
|
|
1627
|
+
throw new MetadataError(`Trigger "${trigger.name ?? '(unnamed)'}" on entity ${meta.className} must define either 'body' or 'expression'.`);
|
|
1628
|
+
}
|
|
1629
|
+
trigger.name ??= this.#namingStrategy.indexName(meta.tableName, trigger.events, 'trigger');
|
|
1630
|
+
trigger.forEach ??= 'row';
|
|
1631
|
+
if (trigger.body instanceof Function) {
|
|
1632
|
+
trigger.body = trigger.body(columns, table);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
meta.hasTriggers = true;
|
|
1636
|
+
}
|
|
1523
1637
|
initGeneratedColumn(meta, prop) {
|
|
1524
1638
|
if (!prop.generated && prop.columnTypes) {
|
|
1525
1639
|
const match = /(.*) generated always as (.*)/i.exec(prop.columnTypes[0]);
|
|
@@ -1646,7 +1760,14 @@ export class MetadataDiscovery {
|
|
|
1646
1760
|
return (!Type.getType(t[type]).ensureComparable(meta, prop) && (prop.type === t[type] || brand === type));
|
|
1647
1761
|
});
|
|
1648
1762
|
if (type) {
|
|
1649
|
-
prop.
|
|
1763
|
+
if (prop.array) {
|
|
1764
|
+
// built-in type + array: true — force-create instance for ArrayType wrapping
|
|
1765
|
+
prop.customType = new prop.type();
|
|
1766
|
+
prop.type = prop.customType.constructor.name;
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
prop.type = type === 'datetime' ? 'Date' : type;
|
|
1770
|
+
}
|
|
1650
1771
|
}
|
|
1651
1772
|
else {
|
|
1652
1773
|
prop.customType = new prop.type();
|
|
@@ -1656,6 +1777,30 @@ export class MetadataDiscovery {
|
|
|
1656
1777
|
if (simple) {
|
|
1657
1778
|
return;
|
|
1658
1779
|
}
|
|
1780
|
+
if (prop.array &&
|
|
1781
|
+
prop.customType &&
|
|
1782
|
+
!(prop.customType instanceof t.array) &&
|
|
1783
|
+
!(prop.customType instanceof t.enumArray) &&
|
|
1784
|
+
prop.kind === ReferenceKind.SCALAR) {
|
|
1785
|
+
const innerType = prop.customType;
|
|
1786
|
+
innerType.platform = this.#platform;
|
|
1787
|
+
innerType.meta = meta;
|
|
1788
|
+
innerType.prop = prop;
|
|
1789
|
+
if (!prop.columnTypes) {
|
|
1790
|
+
const arrayDecl = this.#platform.getArrayDeclarationSQL();
|
|
1791
|
+
if (arrayDecl.endsWith('[]')) {
|
|
1792
|
+
// native array support (e.g. postgres) — use inner type's column type with [] suffix
|
|
1793
|
+
prop.columnTypes = [innerType.getColumnType(prop, this.#platform) + '[]'];
|
|
1794
|
+
}
|
|
1795
|
+
else {
|
|
1796
|
+
// non-native (e.g. mysql, sqlite) — store as text/clob
|
|
1797
|
+
prop.columnTypes = [arrayDecl];
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
const platform = this.#platform;
|
|
1801
|
+
prop.customType = new t.array(v => innerType.convertToJSValue(v, platform), v => innerType.convertToDatabaseValue(v, platform));
|
|
1802
|
+
prop.type = prop.customType.constructor.name;
|
|
1803
|
+
}
|
|
1659
1804
|
if (!prop.customType && ['json', 'jsonb'].includes(prop.type?.toLowerCase())) {
|
|
1660
1805
|
prop.customType = new t.json();
|
|
1661
1806
|
}
|