@mikro-orm/core 7.0.0-dev.2 → 7.0.0-dev.21
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 +13 -5
- package/EntityManager.js +60 -40
- package/MikroORM.js +2 -2
- package/cache/FileCacheAdapter.js +2 -2
- package/decorators/Check.d.ts +2 -2
- package/decorators/CreateRequestContext.js +4 -1
- package/decorators/Embeddable.d.ts +5 -5
- package/decorators/Embeddable.js +1 -1
- package/decorators/Embedded.d.ts +6 -12
- package/decorators/Entity.d.ts +5 -5
- package/decorators/Entity.js +0 -1
- package/decorators/Enum.d.ts +1 -1
- package/decorators/Formula.d.ts +1 -2
- package/decorators/Indexed.d.ts +9 -7
- package/decorators/Indexed.js +1 -1
- package/decorators/ManyToMany.d.ts +2 -2
- package/decorators/ManyToOne.d.ts +4 -2
- package/decorators/OneToMany.d.ts +4 -4
- package/decorators/OneToOne.d.ts +3 -1
- package/decorators/PrimaryKey.d.ts +2 -3
- package/decorators/Property.d.ts +1 -1
- package/drivers/IDatabaseDriver.d.ts +4 -1
- package/entity/ArrayCollection.d.ts +1 -1
- package/entity/ArrayCollection.js +11 -4
- package/entity/Collection.d.ts +1 -2
- package/entity/Collection.js +14 -9
- package/entity/EntityFactory.js +4 -1
- package/entity/EntityHelper.js +3 -0
- package/entity/EntityLoader.d.ts +3 -2
- package/entity/EntityLoader.js +20 -6
- package/entity/Reference.d.ts +3 -7
- package/enums.d.ts +1 -1
- package/events/EventSubscriber.d.ts +3 -1
- package/exports.d.ts +24 -0
- package/exports.js +23 -0
- package/hydration/ObjectHydrator.js +1 -0
- package/index.d.ts +1 -1
- package/metadata/EntitySchema.d.ts +6 -4
- package/metadata/EntitySchema.js +23 -17
- package/metadata/MetadataDiscovery.js +25 -12
- package/metadata/MetadataValidator.js +4 -3
- package/package.json +7 -6
- package/types/BigIntType.d.ts +1 -0
- package/types/BigIntType.js +3 -0
- package/typings.d.ts +17 -10
- package/typings.js +3 -0
- package/unit-of-work/ChangeSetPersister.js +7 -1
- package/unit-of-work/UnitOfWork.js +25 -9
- package/utils/AbstractSchemaGenerator.js +5 -2
- package/utils/Configuration.d.ts +1 -1
- package/utils/Configuration.js +1 -1
- package/utils/Cursor.d.ts +3 -3
- package/utils/DataloaderUtils.d.ts +1 -1
- package/utils/DataloaderUtils.js +19 -5
- package/utils/EntityComparator.js +65 -49
- package/utils/QueryHelper.js +1 -1
- package/utils/RawQueryFragment.js +6 -1
- package/utils/Utils.d.ts +8 -2
- package/utils/Utils.js +26 -6
package/typings.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export type EntityKey<T = unknown, B extends boolean = false> = string & {
|
|
|
28
28
|
[K in keyof T]-?: CleanKeys<T, K, B> extends never ? never : K;
|
|
29
29
|
}[keyof T];
|
|
30
30
|
export type EntityValue<T> = T[EntityKey<T>];
|
|
31
|
+
export type EntityDataValue<T> = EntityData<T>[EntityKey<T>];
|
|
31
32
|
export type FilterKey<T> = keyof FilterQuery<T>;
|
|
32
33
|
export type AsyncFunction<R = any, T = Dictionary> = (args: T) => Promise<T>;
|
|
33
34
|
export type Compute<T> = {
|
|
@@ -176,7 +177,7 @@ export interface IWrappedEntityInternal<Entity extends object> extends IWrappedE
|
|
|
176
177
|
__touched: boolean;
|
|
177
178
|
__originalEntityData?: EntityData<Entity>;
|
|
178
179
|
__loadedProperties: Set<string>;
|
|
179
|
-
__identifier?: EntityIdentifier;
|
|
180
|
+
__identifier?: EntityIdentifier | EntityIdentifier[];
|
|
180
181
|
__managed: boolean;
|
|
181
182
|
__processing: boolean;
|
|
182
183
|
__schema?: string;
|
|
@@ -205,7 +206,7 @@ export type EntityName<T> = string | EntityClass<T> | EntitySchema<T, any> | {
|
|
|
205
206
|
};
|
|
206
207
|
export type GetRepository<Entity extends {
|
|
207
208
|
[k: PropertyKey]: any;
|
|
208
|
-
}, Fallback> = Entity[typeof EntityRepositoryType] extends EntityRepository<
|
|
209
|
+
}, Fallback> = Entity[typeof EntityRepositoryType] extends EntityRepository<any> | undefined ? NonNullable<Entity[typeof EntityRepositoryType]> : Fallback;
|
|
209
210
|
export type EntityDataPropValue<T> = T | Primary<T>;
|
|
210
211
|
type ExpandEntityProp<T, C extends boolean = false> = T extends Record<string, any> ? {
|
|
211
212
|
[K in keyof T as CleanKeys<T, K>]?: EntityDataProp<ExpandProperty<T[K]>, C> | EntityDataPropValue<ExpandProperty<T[K]>> | null;
|
|
@@ -216,14 +217,17 @@ type ExpandRequiredEntityPropObject<T, I = never, C extends boolean = false> = {
|
|
|
216
217
|
} & {
|
|
217
218
|
[K in keyof T as OptionalKeys<T, K, I>]?: RequiredEntityDataProp<ExpandProperty<T[K]>, T, C> | EntityDataPropValue<ExpandProperty<T[K]>> | null | undefined;
|
|
218
219
|
};
|
|
220
|
+
type NonArrayObject = object & {
|
|
221
|
+
[Symbol.iterator]?: never;
|
|
222
|
+
};
|
|
219
223
|
export type EntityDataProp<T, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends {
|
|
220
224
|
__runtime?: infer Runtime;
|
|
221
225
|
__raw?: infer Raw;
|
|
222
|
-
} ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? EntityDataNested<U, C> : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends Collection<infer U, any> ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : T extends readonly (infer U)[] ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
|
|
226
|
+
} ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? EntityDataNested<U, C> : T extends ScalarReference<infer U> ? EntityDataProp<U, C> : T extends Collection<infer U, any> ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | EntityDataNested<U, C> | EntityDataNested<U, C>[] : U[] | EntityDataNested<U, C>[] : EntityDataNested<T, C>;
|
|
223
227
|
export type RequiredEntityDataProp<T, O, C extends boolean> = T extends Date ? string | Date : T extends Scalar ? T : T extends {
|
|
224
228
|
__runtime?: infer Runtime;
|
|
225
229
|
__raw?: infer Raw;
|
|
226
|
-
} ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends Collection<infer U, any> ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : T extends readonly (infer U)[] ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
|
|
230
|
+
} ? (C extends true ? Raw : Runtime) : T extends Reference<infer U> ? RequiredEntityDataNested<U, O, C> : T extends ScalarReference<infer U> ? RequiredEntityDataProp<U, O, C> : T extends Collection<infer U, any> ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : T extends readonly (infer U)[] ? U extends NonArrayObject ? U | U[] | RequiredEntityDataNested<U, O, C> | RequiredEntityDataNested<U, O, C>[] : U[] | RequiredEntityDataNested<U, O, C>[] : RequiredEntityDataNested<T, O, C>;
|
|
227
231
|
export type EntityDataNested<T, C extends boolean = false> = T extends undefined ? never : T extends any[] ? Readonly<T> : EntityData<T, C> | ExpandEntityProp<T, C>;
|
|
228
232
|
type EntityDataItem<T, C extends boolean> = C extends false ? T | EntityDataProp<T, C> | null : EntityDataProp<T, C> | null;
|
|
229
233
|
export type RequiredEntityDataNested<T, O, C extends boolean> = T extends any[] ? Readonly<T> : RequiredEntityData<T, O> | ExpandRequiredEntityProp<T, O, C>;
|
|
@@ -240,7 +244,7 @@ type IsOptional<T, K extends keyof T, I> = T[K] extends Collection<any, any> ? t
|
|
|
240
244
|
type RequiredKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? CleanKeys<T, K> : never;
|
|
241
245
|
type OptionalKeys<T, K extends keyof T, I> = IsOptional<T, K, I> extends false ? never : CleanKeys<T, K>;
|
|
242
246
|
export type EntityData<T, C extends boolean = false> = {
|
|
243
|
-
[K in EntityKey<T>]?: EntityDataItem<T[K], C>;
|
|
247
|
+
[K in EntityKey<T>]?: EntityDataItem<T[K] & {}, C>;
|
|
244
248
|
};
|
|
245
249
|
export type RequiredEntityData<T, I = never, C extends boolean = false> = {
|
|
246
250
|
[K in keyof T as RequiredKeys<T, K, I>]: T[K] | RequiredEntityDataProp<T[K], T, C> | Primary<T[K]>;
|
|
@@ -282,7 +286,7 @@ type PrimaryOrObject<T, U, C extends TypeConfig> = PreferExplicitConfig<C, Extra
|
|
|
282
286
|
} : Primary<U>;
|
|
283
287
|
export type EntityDTOProp<E, T, C extends TypeConfig = never> = T extends Scalar ? T : T extends {
|
|
284
288
|
__serialized?: infer U;
|
|
285
|
-
} ? U : T extends LoadedReference<infer U> ? EntityDTO<U, C> : T extends Reference<infer U> ? PrimaryOrObject<E, U, C> : T extends ScalarReference<infer U> ? U : T extends LoadedCollection<infer U> ? EntityDTO<U, C>[] : T extends Collection<infer U> ? PrimaryOrObject<E, U, C>[] : T extends readonly (infer U)[] ? (T extends readonly any[] ? T : U[]) : T extends Relation<T> ? EntityDTO<T, C> : T;
|
|
289
|
+
} ? (IsUnknown<U> extends false ? U : T) : T extends LoadedReference<infer U> ? EntityDTO<U, C> : T extends Reference<infer U> ? PrimaryOrObject<E, U, C> : T extends ScalarReference<infer U> ? U : T extends LoadedCollection<infer U> ? EntityDTO<U, C>[] : T extends Collection<infer U> ? PrimaryOrObject<E, U, C>[] : T extends readonly (infer U)[] ? (T extends readonly any[] ? T : U[]) : T extends Relation<T> ? EntityDTO<T, C> : T;
|
|
286
290
|
type DTOProbablyOptionalProps<T> = NonNullable<NullableKeys<T, undefined>>;
|
|
287
291
|
type DTOIsOptional<T, K extends keyof T> = T[K] extends LoadedCollection<any> ? false : K extends PrimaryProperty<T> ? false : K extends DTOProbablyOptionalProps<T> ? true : false;
|
|
288
292
|
type DTORequiredKeys<T, K extends keyof T> = DTOIsOptional<T, K> extends false ? ExcludeHidden<T, K> & CleanKeys<T, K> : never;
|
|
@@ -292,7 +296,9 @@ export type EntityDTO<T, C extends TypeConfig = never> = {
|
|
|
292
296
|
} & {
|
|
293
297
|
[K in keyof T as DTOOptionalKeys<T, K>]?: EntityDTOProp<T, T[K], C> | AddOptional<T[K]>;
|
|
294
298
|
};
|
|
295
|
-
|
|
299
|
+
type TargetKeys<T> = T extends EntityClass<infer P> ? keyof P : keyof T;
|
|
300
|
+
type CheckKey<T> = IsUnknown<T> extends false ? TargetKeys<T> : string;
|
|
301
|
+
export type CheckCallback<T> = (columns: Record<CheckKey<T>, string>) => string;
|
|
296
302
|
export type GeneratedColumnCallback<T> = (columns: Record<keyof T, string>) => string;
|
|
297
303
|
export interface CheckConstraint<T = any> {
|
|
298
304
|
name?: string;
|
|
@@ -388,6 +394,7 @@ export interface EntityProperty<Owner = any, Target = any> {
|
|
|
388
394
|
optional?: boolean;
|
|
389
395
|
ignoreSchemaChanges?: ('type' | 'extra' | 'default')[];
|
|
390
396
|
deferMode?: DeferMode;
|
|
397
|
+
foreignKeyName?: string;
|
|
391
398
|
}
|
|
392
399
|
export declare class EntityMetadata<T = any> {
|
|
393
400
|
private static counter;
|
|
@@ -416,7 +423,7 @@ export interface EntityMetadata<T = any> {
|
|
|
416
423
|
schema?: string;
|
|
417
424
|
pivotTable?: boolean;
|
|
418
425
|
virtual?: boolean;
|
|
419
|
-
expression?: string | ((em: any, where:
|
|
426
|
+
expression?: string | ((em: any, where: ObjectQuery<T>, options: FindOptions<T, any, any, any>) => MaybePromise<RawQueryFragment | object | string>);
|
|
420
427
|
discriminatorColumn?: EntityKey<T> | AnyString;
|
|
421
428
|
discriminatorValue?: number | string;
|
|
422
429
|
discriminatorMap?: Dictionary<string>;
|
|
@@ -681,7 +688,7 @@ export interface MigrationObject {
|
|
|
681
688
|
}
|
|
682
689
|
export type FilterDef = {
|
|
683
690
|
name: string;
|
|
684
|
-
cond: Dictionary | ((args: Dictionary, type: 'read' | 'update' | 'delete', em: any, options?: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>) => Dictionary | Promise<Dictionary>);
|
|
691
|
+
cond: Dictionary | ((args: Dictionary, type: 'read' | 'update' | 'delete', em: any, options?: FindOptions<any, any, any, any> | FindOneOptions<any, any, any, any>, entityName?: EntityName<any>) => Dictionary | Promise<Dictionary>);
|
|
685
692
|
default?: boolean;
|
|
686
693
|
entity?: string[];
|
|
687
694
|
args?: boolean;
|
|
@@ -726,7 +733,7 @@ export type MergeSelected<T, U, F extends string> = T extends Loaded<infer TT, i
|
|
|
726
733
|
type MergeFields<F1 extends string, F2 extends string, P1, P2> = P1 | P2 extends '*' ? '*' : F1 | F2;
|
|
727
734
|
type MergeExcludes<F extends string, E extends string> = F extends E ? never : Exclude<E, F>;
|
|
728
735
|
export type MergeLoaded<T, U, P extends string, F extends string, E extends string, R extends boolean = false> = T extends Loaded<U, infer PP, infer FF, infer EE> ? string extends FF ? Loaded<T, P, F, AnyStringToNever<EE> | E> : string extends P ? Loaded<U, never, F | (FF & string), MergeExcludes<F | (FF & string), EE | E>> : Loaded<U, P | AnyStringToNever<PP>, MergeFields<F, AnyStringToNever<FF>, P, PP>, MergeExcludes<MergeFields<F, AnyStringToNever<FF>, P, PP>, (R extends true ? never : EE) | E>> : Loaded<T, P, F>;
|
|
729
|
-
type AddOptional<T> = undefined | null extends T ? null | undefined : null extends T ? null : undefined extends T ? undefined : never;
|
|
736
|
+
export type AddOptional<T> = undefined | null extends T ? null | undefined : null extends T ? null : undefined extends T ? undefined : never;
|
|
730
737
|
type LoadedProp<T, L extends string = never, F extends string = '*', E extends string = never> = LoadedLoadable<T, Loaded<ExtractType<T>, L, F, E>>;
|
|
731
738
|
export type AddEager<T> = ExtractEagerProps<T> & string;
|
|
732
739
|
export type ExpandHint<T, L extends string> = L | AddEager<T>;
|
package/typings.js
CHANGED
|
@@ -123,6 +123,9 @@ export class EntityMetadata {
|
|
|
123
123
|
const hydrator = wrapped.hydrator;
|
|
124
124
|
const entity = Reference.unwrapReference(val ?? wrapped.__data[prop.name]);
|
|
125
125
|
const old = Reference.unwrapReference(wrapped.__data[prop.name]);
|
|
126
|
+
if (old && old !== entity && prop.kind === ReferenceKind.MANY_TO_ONE && prop.inversedBy && old[prop.inversedBy]) {
|
|
127
|
+
old[prop.inversedBy].removeWithoutPropagation(this);
|
|
128
|
+
}
|
|
126
129
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
127
130
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
128
131
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
|
@@ -210,7 +210,9 @@ export class ChangeSetPersister {
|
|
|
210
210
|
// of using the raw value from db, we convert it back to the db value explicitly
|
|
211
211
|
value = prop.customType ? prop.customType.convertToDatabaseValue(insertId, this.platform, { mode: 'serialization' }) : value;
|
|
212
212
|
changeSet.payload[wrapped.__meta.primaryKeys[0]] = value;
|
|
213
|
-
wrapped.__identifier
|
|
213
|
+
if (wrapped.__identifier && !Array.isArray(wrapped.__identifier)) {
|
|
214
|
+
wrapped.__identifier.setValue(value);
|
|
215
|
+
}
|
|
214
216
|
}
|
|
215
217
|
/**
|
|
216
218
|
* Sets populate flag to new entities so they are serialized like if they were loaded from the db
|
|
@@ -338,6 +340,10 @@ export class ChangeSetPersister {
|
|
|
338
340
|
changeSet.payload[prop.name] = value.getValue();
|
|
339
341
|
return;
|
|
340
342
|
}
|
|
343
|
+
if (Array.isArray(value) && value.every(item => item instanceof EntityIdentifier)) {
|
|
344
|
+
changeSet.payload[prop.name] = value.map(item => item.getValue());
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
341
347
|
if (prop.kind === ReferenceKind.MANY_TO_MANY && Array.isArray(value)) {
|
|
342
348
|
changeSet.payload[prop.name] = value.map(val => val instanceof EntityIdentifier ? val.getValue() : val);
|
|
343
349
|
return;
|
|
@@ -542,13 +542,22 @@ export class UnitOfWork {
|
|
|
542
542
|
if (!wrapped || wrapped.__identifier || wrapped.hasPrimaryKey()) {
|
|
543
543
|
return;
|
|
544
544
|
}
|
|
545
|
-
const
|
|
546
|
-
|
|
547
|
-
|
|
545
|
+
const pks = wrapped.__meta.getPrimaryProps();
|
|
546
|
+
const idents = [];
|
|
547
|
+
for (const pk of pks) {
|
|
548
|
+
if (pk.kind === ReferenceKind.SCALAR) {
|
|
549
|
+
idents.push(new EntityIdentifier(entity[pk.name]));
|
|
550
|
+
}
|
|
551
|
+
else if (entity[pk.name]) {
|
|
552
|
+
this.initIdentifier(entity[pk.name]);
|
|
553
|
+
idents.push(helper(entity[pk.name])?.__identifier);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
if (pks.length === 1) {
|
|
557
|
+
wrapped.__identifier = idents[0];
|
|
548
558
|
}
|
|
549
|
-
else
|
|
550
|
-
|
|
551
|
-
wrapped.__identifier = helper(entity[pk.name])?.__identifier;
|
|
559
|
+
else {
|
|
560
|
+
wrapped.__identifier = idents;
|
|
552
561
|
}
|
|
553
562
|
}
|
|
554
563
|
processReference(parent, prop, kind, visited, processed, idx) {
|
|
@@ -600,8 +609,15 @@ export class UnitOfWork {
|
|
|
600
609
|
const diff = this.comparator.diffEntities(changeSet.name, copy, current);
|
|
601
610
|
Object.assign(changeSet.payload, diff);
|
|
602
611
|
const wrapped = helper(changeSet.entity);
|
|
603
|
-
if (wrapped.__identifier
|
|
604
|
-
|
|
612
|
+
if (wrapped.__identifier) {
|
|
613
|
+
const idents = Utils.asArray(wrapped.__identifier);
|
|
614
|
+
let i = 0;
|
|
615
|
+
for (const pk of wrapped.__meta.primaryKeys) {
|
|
616
|
+
if (diff[pk]) {
|
|
617
|
+
idents[i].setValue(diff[pk]);
|
|
618
|
+
}
|
|
619
|
+
i++;
|
|
620
|
+
}
|
|
605
621
|
}
|
|
606
622
|
}
|
|
607
623
|
postCommitCleanup() {
|
|
@@ -884,7 +900,7 @@ export class UnitOfWork {
|
|
|
884
900
|
collectionUpdates.push(coll);
|
|
885
901
|
}
|
|
886
902
|
}
|
|
887
|
-
await this.em.getDriver().syncCollections(collectionUpdates, { ctx });
|
|
903
|
+
await this.em.getDriver().syncCollections(collectionUpdates, { ctx, schema: this.em.schema });
|
|
888
904
|
for (const coll of this.collectionUpdates) {
|
|
889
905
|
coll.takeSnapshot();
|
|
890
906
|
}
|
|
@@ -32,7 +32,7 @@ export class AbstractSchemaGenerator {
|
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
34
|
await this.ensureDatabase();
|
|
35
|
-
await this.dropSchema();
|
|
35
|
+
await this.dropSchema(options);
|
|
36
36
|
}
|
|
37
37
|
if (options?.createSchema !== false) {
|
|
38
38
|
await this.createSchema(options);
|
|
@@ -103,7 +103,10 @@ export class AbstractSchemaGenerator {
|
|
|
103
103
|
}
|
|
104
104
|
return calc.sort()
|
|
105
105
|
.map(cls => this.metadata.find(cls))
|
|
106
|
-
.filter(meta =>
|
|
106
|
+
.filter(meta => {
|
|
107
|
+
const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
|
|
108
|
+
return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
|
|
109
|
+
});
|
|
107
110
|
}
|
|
108
111
|
notImplemented() {
|
|
109
112
|
throw new Error(`This method is not supported by ${this.driver.constructor.name} driver`);
|
package/utils/Configuration.d.ts
CHANGED
package/utils/Configuration.js
CHANGED
package/utils/Cursor.d.ts
CHANGED
|
@@ -49,13 +49,13 @@ import { type QueryOrder } from '../enums.js';
|
|
|
49
49
|
* }
|
|
50
50
|
* ```
|
|
51
51
|
*/
|
|
52
|
-
export declare class Cursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never> {
|
|
52
|
+
export declare class Cursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never, IncludeCount extends boolean = true> {
|
|
53
53
|
readonly items: Loaded<Entity, Hint, Fields, Excludes>[];
|
|
54
|
-
readonly totalCount: number;
|
|
54
|
+
readonly totalCount: IncludeCount extends true ? number : undefined;
|
|
55
55
|
readonly hasPrevPage: boolean;
|
|
56
56
|
readonly hasNextPage: boolean;
|
|
57
57
|
private readonly definition;
|
|
58
|
-
constructor(items: Loaded<Entity, Hint, Fields, Excludes>[], totalCount: number, options: FindByCursorOptions<Entity, Hint, Fields, Excludes>, meta: EntityMetadata<Entity>);
|
|
58
|
+
constructor(items: Loaded<Entity, Hint, Fields, Excludes>[], totalCount: IncludeCount extends true ? number : undefined, options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>, meta: EntityMetadata<Entity>);
|
|
59
59
|
get startCursor(): string | null;
|
|
60
60
|
get endCursor(): string | null;
|
|
61
61
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type DataLoader from 'dataloader';
|
|
1
2
|
import type { Primary, Ref } from '../typings.js';
|
|
2
3
|
import { Collection, type InitCollectionOptions } from '../entity/Collection.js';
|
|
3
4
|
import { type EntityManager } from '../EntityManager.js';
|
|
4
|
-
import type DataLoader from 'dataloader';
|
|
5
5
|
import { type LoadReferenceOptions } from '../entity/Reference.js';
|
|
6
6
|
export declare class DataloaderUtils {
|
|
7
7
|
/**
|
package/utils/DataloaderUtils.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Collection } from '../entity/Collection.js';
|
|
|
2
2
|
import { helper } from '../entity/wrap.js';
|
|
3
3
|
import { ReferenceKind } from '../enums.js';
|
|
4
4
|
import { Reference } from '../entity/Reference.js';
|
|
5
|
+
import { Utils } from './Utils.js';
|
|
5
6
|
export class DataloaderUtils {
|
|
6
7
|
/**
|
|
7
8
|
* Groups identified references by entity and returns a Map with the
|
|
@@ -118,7 +119,6 @@ export class DataloaderUtils {
|
|
|
118
119
|
// We need to populate the inverse side of the relationship in order to be able to later retrieve the PK(s) from its item(s)
|
|
119
120
|
populate: [
|
|
120
121
|
...(opts.populate === false ? [] : opts.populate ?? []),
|
|
121
|
-
...(opts.ref ? [':ref'] : []),
|
|
122
122
|
...Array.from(filterMap.keys()).filter(
|
|
123
123
|
// We need to do so only if the inverse side is a collection, because we can already retrieve the PK from a reference without having to load it
|
|
124
124
|
prop => em.getMetadata(className).properties[prop]?.ref !== true),
|
|
@@ -149,10 +149,6 @@ export class DataloaderUtils {
|
|
|
149
149
|
else if (target) {
|
|
150
150
|
return target === collection.owner;
|
|
151
151
|
}
|
|
152
|
-
// FIXME https://github.com/mikro-orm/mikro-orm/issues/6031
|
|
153
|
-
if (!target && collection.property.kind === ReferenceKind.MANY_TO_MANY) {
|
|
154
|
-
throw new Error(`Inverse side is required for M:N relations with dataloader: ${collection.owner.constructor.name}.${collection.property.name}`);
|
|
155
|
-
}
|
|
156
152
|
return false;
|
|
157
153
|
};
|
|
158
154
|
}
|
|
@@ -162,6 +158,24 @@ export class DataloaderUtils {
|
|
|
162
158
|
*/
|
|
163
159
|
static getColBatchLoadFn(em) {
|
|
164
160
|
return async (collsWithOpts) => {
|
|
161
|
+
const prop = collsWithOpts[0][0].property;
|
|
162
|
+
if (prop.kind === ReferenceKind.MANY_TO_MANY && em.getPlatform().usesPivotTable()) {
|
|
163
|
+
const options = {};
|
|
164
|
+
const wrap = (cond) => ({ [prop.name]: cond });
|
|
165
|
+
const orderBy = Utils.asArray(collsWithOpts[0][1]?.orderBy).map(o => wrap(o));
|
|
166
|
+
const populate = wrap(collsWithOpts[0][1]?.populate);
|
|
167
|
+
const owners = collsWithOpts.map(c => c[0].owner);
|
|
168
|
+
const $or = [];
|
|
169
|
+
// a bit of a hack, but we need to prefix the key, since we have only a column name, not a property name
|
|
170
|
+
const alias = em.config.getNamingStrategy().aliasName(prop.pivotEntity, 0);
|
|
171
|
+
const fk = `${alias}.${Utils.getPrimaryKeyHash(prop.joinColumns)}`;
|
|
172
|
+
for (const c of collsWithOpts) {
|
|
173
|
+
$or.push({ $and: [c[1]?.where ?? {}, { [fk]: c[0].owner }] });
|
|
174
|
+
options.refresh ??= c[1]?.refresh;
|
|
175
|
+
}
|
|
176
|
+
options.where = wrap({ $or });
|
|
177
|
+
return em.getEntityLoader().findChildrenFromPivotTable(owners, prop, options, orderBy, populate, collsWithOpts[0][1]?.ref);
|
|
178
|
+
}
|
|
165
179
|
const entitiesAndOptsMap = DataloaderUtils.groupInversedOrMappedKeysByEntityAndOpts(collsWithOpts);
|
|
166
180
|
const promises = DataloaderUtils.entitiesAndOptsMapToQueries(entitiesAndOptsMap, em);
|
|
167
181
|
const resultsMap = new Map(await Promise.all(promises));
|
|
@@ -74,7 +74,7 @@ export class EntityComparator {
|
|
|
74
74
|
lines.push(` if (entity${this.wrap(pk)} != null && (entity${this.wrap(pk)}.__entity || entity${this.wrap(pk)}.__reference)) {`);
|
|
75
75
|
lines.push(` const pk = entity${this.wrap(pk)}.__helper.getPrimaryKey();`);
|
|
76
76
|
if (meta.properties[pk].targetMeta.compositePK) {
|
|
77
|
-
lines.push(` if (typeof pk === 'object') {`);
|
|
77
|
+
lines.push(` if (typeof pk === 'object' && pk != null) {`);
|
|
78
78
|
lines.push(` return [`);
|
|
79
79
|
for (const childPK of meta.properties[pk].targetMeta.primaryKeys) {
|
|
80
80
|
lines.push(` pk${this.wrap(childPK)},`);
|
|
@@ -286,56 +286,72 @@ export class EntityComparator {
|
|
|
286
286
|
lines.push(`${padding} }`);
|
|
287
287
|
};
|
|
288
288
|
lines.push(` const mapped = {};`);
|
|
289
|
-
meta
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
289
|
+
const mapEntityProperties = (meta, padding = '') => {
|
|
290
|
+
for (const prop of meta.props) {
|
|
291
|
+
if (!prop.fieldNames) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (prop.targetMeta && prop.fieldNames.length > 1) {
|
|
295
|
+
lines.push(`${padding} if (${prop.fieldNames.map(field => `typeof ${this.propName(field)} === 'undefined'`).join(' && ')}) {`);
|
|
296
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} != null`).join(' && ')}) {`);
|
|
297
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.createCompositeKeyArray(prop)};`);
|
|
298
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`));
|
|
299
|
+
lines.push(`${padding} } else if (${prop.fieldNames.map(field => `${this.propName(field)} == null`).join(' && ')}) {\n ret${this.wrap(prop.name)} = null;`);
|
|
300
|
+
lines.push(...prop.fieldNames.map(field => `${padding} ${this.propName(field, 'mapped')} = true;`), ' }');
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (prop.embedded && (meta.embeddable || meta.properties[prop.embedded[0]].object)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (prop.runtimeType === 'boolean') {
|
|
307
|
+
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
308
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.propName(prop.fieldNames[0])} == null ? ${this.propName(prop.fieldNames[0])} : !!${this.propName(prop.fieldNames[0])};`);
|
|
309
|
+
lines.push(`${padding} ${this.propName(prop.fieldNames[0], 'mapped')} = true;`);
|
|
310
|
+
lines.push(`${padding} }`);
|
|
311
|
+
}
|
|
312
|
+
else if (prop.runtimeType === 'Date' && !this.platform.isNumericProperty(prop)) {
|
|
313
|
+
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
314
|
+
context.set('parseDate', (value) => this.platform.parseDate(value));
|
|
315
|
+
parseDate('ret' + this.wrap(prop.name), this.propName(prop.fieldNames[0]), padding);
|
|
316
|
+
lines.push(`${padding} ${this.propName(prop.fieldNames[0], 'mapped')} = true;`);
|
|
317
|
+
lines.push(`${padding} }`);
|
|
318
|
+
}
|
|
319
|
+
else if (prop.kind === ReferenceKind.EMBEDDED && (prop.object || meta.embeddable)) {
|
|
320
|
+
const idx = this.tmpIndex++;
|
|
321
|
+
context.set(`mapEmbeddedResult_${idx}`, (data) => {
|
|
322
|
+
const item = parseJsonSafe(data);
|
|
323
|
+
if (Array.isArray(item)) {
|
|
324
|
+
return item.map(row => row == null ? row : this.getResultMapper(prop.type)(row));
|
|
325
|
+
}
|
|
326
|
+
return item == null ? item : this.getResultMapper(prop.type)(item);
|
|
327
|
+
});
|
|
328
|
+
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
329
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.propName(prop.fieldNames[0])} == null ? ${this.propName(prop.fieldNames[0])} : mapEmbeddedResult_${idx}(${this.propName(prop.fieldNames[0])});`);
|
|
330
|
+
lines.push(`${padding} ${this.propName(prop.fieldNames[0], 'mapped')} = true;`);
|
|
331
|
+
lines.push(`${padding} }`);
|
|
332
|
+
}
|
|
333
|
+
else if (prop.kind !== ReferenceKind.EMBEDDED) {
|
|
334
|
+
lines.push(`${padding} if (typeof ${this.propName(prop.fieldNames[0])} !== 'undefined') {`);
|
|
335
|
+
lines.push(`${padding} ret${this.wrap(prop.name)} = ${this.propName(prop.fieldNames[0])};`);
|
|
336
|
+
lines.push(`${padding} ${this.propName(prop.fieldNames[0], 'mapped')} = true;`);
|
|
337
|
+
lines.push(`${padding} }`);
|
|
338
|
+
}
|
|
331
339
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
340
|
+
};
|
|
341
|
+
if (meta.polymorphs && meta.discriminatorColumn) {
|
|
342
|
+
for (const polymorph of meta.polymorphs) {
|
|
343
|
+
const first = polymorph === meta.polymorphs[0];
|
|
344
|
+
lines.push(` ${first ? '' : 'else '}if (${this.propName(meta.discriminatorColumn)} == '${polymorph.discriminatorValue}') {`);
|
|
345
|
+
mapEntityProperties(polymorph, ' ');
|
|
336
346
|
lines.push(` }`);
|
|
337
347
|
}
|
|
338
|
-
|
|
348
|
+
lines.push(` else {`);
|
|
349
|
+
mapEntityProperties(meta, ' ');
|
|
350
|
+
lines.push(` }`);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
mapEntityProperties(meta);
|
|
354
|
+
}
|
|
339
355
|
lines.push(` for (let k in result) { if (Object.hasOwn(result, k) && !mapped[k]) ret[k] = result[k]; }`);
|
|
340
356
|
const code = `// compiled mapper for entity ${meta.className}\n`
|
|
341
357
|
+ `return function(result) {\n const ret = {};\n${lines.join('\n')}\n return ret;\n}`;
|
|
@@ -591,7 +607,7 @@ export class EntityComparator {
|
|
|
591
607
|
* perf: used to generate list of comparable properties during discovery, so we speed up the runtime comparison
|
|
592
608
|
*/
|
|
593
609
|
static isComparable(prop, root) {
|
|
594
|
-
const virtual = prop.persist === false || prop.generated;
|
|
610
|
+
const virtual = prop.persist === false || (prop.generated && !prop.primary);
|
|
595
611
|
const inverse = prop.kind === ReferenceKind.ONE_TO_ONE && !prop.owner;
|
|
596
612
|
const discriminator = prop.name === root.discriminatorColumn;
|
|
597
613
|
const collection = prop.kind === ReferenceKind.ONE_TO_MANY || prop.kind === ReferenceKind.MANY_TO_MANY;
|
package/utils/QueryHelper.js
CHANGED
|
@@ -45,7 +45,7 @@ export class QueryHelper {
|
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
47
|
if (meta.primaryKeys.every(pk => pk in where) && Utils.getObjectKeysSize(where) === meta.primaryKeys.length) {
|
|
48
|
-
return !!key && !GroupOperator[key] && Object.keys(where).every(k => !Utils.isPlainObject(where[k]) || Object.keys(where[k]).every(v => {
|
|
48
|
+
return !!key && !GroupOperator[key] && key !== '$not' && Object.keys(where).every(k => !Utils.isPlainObject(where[k]) || Object.keys(where[k]).every(v => {
|
|
49
49
|
if (Utils.isOperator(v, false)) {
|
|
50
50
|
return false;
|
|
51
51
|
}
|
|
@@ -17,7 +17,12 @@ export class RawQueryFragment {
|
|
|
17
17
|
this.#key = `[raw]: ${this.sql} (#${RawQueryFragment.#index++})`;
|
|
18
18
|
}
|
|
19
19
|
as(alias) {
|
|
20
|
-
|
|
20
|
+
// TODO: to be removed in v7
|
|
21
|
+
/* istanbul ignore next */
|
|
22
|
+
if (alias.startsWith('`') || alias.startsWith('"')) {
|
|
23
|
+
return new RawQueryFragment(`${this.sql} as ${alias}`, this.params);
|
|
24
|
+
}
|
|
25
|
+
return new RawQueryFragment(`${this.sql} as ??`, [...this.params, alias]);
|
|
21
26
|
}
|
|
22
27
|
valueOf() {
|
|
23
28
|
throw new Error(`Trying to modify raw SQL fragment: '${this.sql}'`);
|
package/utils/Utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type GlobOptions } from 'tinyglobby';
|
|
2
2
|
import type { Dictionary, EntityData, EntityDictionary, EntityKey, EntityMetadata, EntityName, EntityProperty, IMetadataStorage, Primary } from '../typings.js';
|
|
3
3
|
import type { Collection } from '../entity/Collection.js';
|
|
4
4
|
import type { Platform } from '../platforms/Platform.js';
|
|
@@ -212,7 +212,7 @@ export declare class Utils {
|
|
|
212
212
|
static findDuplicates<T>(items: T[]): T[];
|
|
213
213
|
static removeDuplicates<T>(items: T[]): T[];
|
|
214
214
|
static randomInt(min: number, max: number): number;
|
|
215
|
-
static pathExists(path: string, options?:
|
|
215
|
+
static pathExists(path: string, options?: GlobOptions): Promise<boolean>;
|
|
216
216
|
/**
|
|
217
217
|
* Extracts all possible values of a TS enum. Works with both string and numeric enums.
|
|
218
218
|
*/
|
|
@@ -230,6 +230,12 @@ export declare class Utils {
|
|
|
230
230
|
* @param [from] Location to start the node resolution
|
|
231
231
|
*/
|
|
232
232
|
static requireFrom<T extends Dictionary>(id: string, from?: string): T;
|
|
233
|
+
/**
|
|
234
|
+
* Resolve path to a module.
|
|
235
|
+
* @param id The module to require
|
|
236
|
+
* @param [from] Location to start the node resolution
|
|
237
|
+
*/
|
|
238
|
+
static resolveModulePath(id: string, from?: string): string;
|
|
233
239
|
static dynamicImport<T = any>(id: string): Promise<T>;
|
|
234
240
|
static setDynamicImportProvider(provider: (id: string) => Promise<unknown>): void;
|
|
235
241
|
static ensureDir(path: string): void;
|
package/utils/Utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import
|
|
2
|
+
import { glob, isDynamicPattern } from 'tinyglobby';
|
|
3
3
|
import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:path';
|
|
4
4
|
import { platform } from 'node:os';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -625,8 +625,12 @@ export class Utils {
|
|
|
625
625
|
|| !!process.env.TS_JEST // check if ts-jest is used (works only with v27.0.4+)
|
|
626
626
|
|| !!process.env.VITEST // check if vitest is used
|
|
627
627
|
|| !!process.versions.bun // check if bun is used
|
|
628
|
-
|| process.argv.slice(1).some(arg => arg.
|
|
629
|
-
|| process.execArgv.some(arg =>
|
|
628
|
+
|| process.argv.slice(1).some(arg => arg.match(/\.([mc]?ts|tsx)$/)) // executing `.ts` file
|
|
629
|
+
|| process.execArgv.some(arg => {
|
|
630
|
+
return arg.includes('ts-node') // check for ts-node loader
|
|
631
|
+
|| arg.includes('@swc-node/register') // check for swc-node/register loader
|
|
632
|
+
|| arg.includes('node_modules/tsx/'); // check for tsx loader
|
|
633
|
+
});
|
|
630
634
|
}
|
|
631
635
|
/**
|
|
632
636
|
* Uses some dark magic to get source path to caller where decorator is used.
|
|
@@ -644,7 +648,8 @@ export class Utils {
|
|
|
644
648
|
// but those are also present in node, so we need to check this only if they weren't found.
|
|
645
649
|
if (line === -1) {
|
|
646
650
|
// here we handle bun which stack is different from nodejs so we search for reflect-metadata
|
|
647
|
-
|
|
651
|
+
// Different bun versions might have different stack traces. The "last index" works for both 1.2.6 and 1.2.7.
|
|
652
|
+
const reflectLine = stack.findLastIndex(line => Utils.normalizePath(line).includes('node_modules/reflect-metadata/Reflect.js'));
|
|
648
653
|
if (reflectLine === -1 || reflectLine + 2 >= stack.length || !stack[reflectLine + 1].includes('bun:wrap')) {
|
|
649
654
|
return name;
|
|
650
655
|
}
|
|
@@ -795,8 +800,8 @@ export class Utils {
|
|
|
795
800
|
return Math.round(Math.random() * (max - min)) + min;
|
|
796
801
|
}
|
|
797
802
|
static async pathExists(path, options = {}) {
|
|
798
|
-
if (
|
|
799
|
-
const found = await
|
|
803
|
+
if (isDynamicPattern(path)) {
|
|
804
|
+
const found = await glob(path, options);
|
|
800
805
|
return found.length > 0;
|
|
801
806
|
}
|
|
802
807
|
return this.pathExistsSync(path);
|
|
@@ -863,6 +868,21 @@ export class Utils {
|
|
|
863
868
|
}
|
|
864
869
|
return createRequire(resolve(from))(id);
|
|
865
870
|
}
|
|
871
|
+
/**
|
|
872
|
+
* Resolve path to a module.
|
|
873
|
+
* @param id The module to require
|
|
874
|
+
* @param [from] Location to start the node resolution
|
|
875
|
+
*/
|
|
876
|
+
static resolveModulePath(id, from = process.cwd()) {
|
|
877
|
+
if (!extname(from)) {
|
|
878
|
+
from = join(from, '__fake.js');
|
|
879
|
+
}
|
|
880
|
+
const path = Utils.normalizePath(createRequire(resolve(from)).resolve(id));
|
|
881
|
+
const parts = path.split('/');
|
|
882
|
+
const idx = parts.lastIndexOf(id) + 1;
|
|
883
|
+
parts.splice(idx, parts.length - idx);
|
|
884
|
+
return parts.join('/');
|
|
885
|
+
}
|
|
866
886
|
static async dynamicImport(id) {
|
|
867
887
|
/* v8 ignore next 7 */
|
|
868
888
|
if (platform() === 'win32') {
|