@mikro-orm/core 7.0.0-dev.8 → 7.0.0-dev.9
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 +7 -3
- package/EntityManager.js +8 -2
- package/drivers/IDatabaseDriver.d.ts +2 -1
- package/entity/EntityFactory.js +1 -0
- package/entity/EntityHelper.js +3 -0
- package/entity/EntityLoader.js +11 -3
- package/entity/Reference.d.ts +3 -7
- package/hydration/ObjectHydrator.js +1 -0
- package/metadata/MetadataDiscovery.js +4 -3
- package/metadata/MetadataValidator.js +4 -3
- package/package.json +3 -3
- package/typings.d.ts +4 -4
- package/typings.js +3 -0
- package/unit-of-work/ChangeSetPersister.js +7 -1
- package/unit-of-work/UnitOfWork.js +24 -8
- package/utils/Cursor.d.ts +3 -3
- package/utils/EntityComparator.js +63 -47
- package/utils/Utils.js +8 -3
package/EntityManager.d.ts
CHANGED
|
@@ -171,6 +171,10 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
171
171
|
* });
|
|
172
172
|
* ```
|
|
173
173
|
*
|
|
174
|
+
* The options also support an `includeCount` (true by default) option. If set to false, the `totalCount` is not
|
|
175
|
+
* returned as part of the cursor. This is useful for performance reason, when you don't care about the total number
|
|
176
|
+
* of pages.
|
|
177
|
+
*
|
|
174
178
|
* The `Cursor` object provides the following interface:
|
|
175
179
|
*
|
|
176
180
|
* ```ts
|
|
@@ -180,7 +184,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
180
184
|
* User { ... },
|
|
181
185
|
* User { ... },
|
|
182
186
|
* ],
|
|
183
|
-
* totalCount: 50,
|
|
187
|
+
* totalCount: 50, // not included if `includeCount: false`
|
|
184
188
|
* startCursor: 'WzRd',
|
|
185
189
|
* endCursor: 'WzZd',
|
|
186
190
|
* hasPrevPage: true,
|
|
@@ -188,7 +192,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
188
192
|
* }
|
|
189
193
|
* ```
|
|
190
194
|
*/
|
|
191
|
-
findByCursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity>>, options: FindByCursorOptions<Entity, Hint, Fields, Excludes>): Promise<Cursor<Entity, Hint, Fields, Excludes>>;
|
|
195
|
+
findByCursor<Entity extends object, Hint extends string = never, Fields extends string = '*', Excludes extends string = never, IncludeCount extends boolean = true>(entityName: EntityName<Entity>, where: FilterQuery<NoInfer<Entity>>, options: FindByCursorOptions<Entity, Hint, Fields, Excludes, IncludeCount>): Promise<Cursor<Entity, Hint, Fields, Excludes, IncludeCount>>;
|
|
192
196
|
/**
|
|
193
197
|
* Refreshes the persistent state of an entity from the database, overriding any local changes that have not yet been
|
|
194
198
|
* persisted. Returns the same entity instance (same object reference), but re-hydrated. If the entity is no longer
|
|
@@ -419,7 +423,7 @@ export declare class EntityManager<Driver extends IDatabaseDriver = IDatabaseDri
|
|
|
419
423
|
/**
|
|
420
424
|
* Loads specified relations in batch. This will execute one query for each relation, that will populate it on all the specified entities.
|
|
421
425
|
*/
|
|
422
|
-
populate<Entity extends object, Naked extends FromEntityType<UnboxArray<Entity>> = FromEntityType<UnboxArray<Entity>>, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entities: Entity, populate: AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Entity extends object[] ? MergeLoaded<ArrayElement<Entity>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Entity, Naked, Hint, Fields, Excludes>>;
|
|
426
|
+
populate<Entity extends object, Naked extends FromEntityType<UnboxArray<Entity>> = FromEntityType<UnboxArray<Entity>>, Hint extends string = never, Fields extends string = '*', Excludes extends string = never>(entities: Entity, populate: readonly AutoPath<Naked, Hint, PopulatePath.ALL>[] | false, options?: EntityLoaderOptions<Naked, Fields, Excludes>): Promise<Entity extends object[] ? MergeLoaded<ArrayElement<Entity>, Naked, Hint, Fields, Excludes>[] : MergeLoaded<Entity, Naked, Hint, Fields, Excludes>>;
|
|
423
427
|
/**
|
|
424
428
|
* Returns new EntityManager instance with its own identity map
|
|
425
429
|
*/
|
package/EntityManager.js
CHANGED
|
@@ -433,6 +433,10 @@ export class EntityManager {
|
|
|
433
433
|
* });
|
|
434
434
|
* ```
|
|
435
435
|
*
|
|
436
|
+
* The options also support an `includeCount` (true by default) option. If set to false, the `totalCount` is not
|
|
437
|
+
* returned as part of the cursor. This is useful for performance reason, when you don't care about the total number
|
|
438
|
+
* of pages.
|
|
439
|
+
*
|
|
436
440
|
* The `Cursor` object provides the following interface:
|
|
437
441
|
*
|
|
438
442
|
* ```ts
|
|
@@ -442,7 +446,7 @@ export class EntityManager {
|
|
|
442
446
|
* User { ... },
|
|
443
447
|
* User { ... },
|
|
444
448
|
* ],
|
|
445
|
-
* totalCount: 50,
|
|
449
|
+
* totalCount: 50, // not included if `includeCount: false`
|
|
446
450
|
* startCursor: 'WzRd',
|
|
447
451
|
* endCursor: 'WzZd',
|
|
448
452
|
* hasPrevPage: true,
|
|
@@ -457,7 +461,9 @@ export class EntityManager {
|
|
|
457
461
|
if (Utils.isEmpty(options.orderBy)) {
|
|
458
462
|
throw new Error('Explicit `orderBy` option required');
|
|
459
463
|
}
|
|
460
|
-
const [entities, count] =
|
|
464
|
+
const [entities, count] = options.includeCount !== false
|
|
465
|
+
? await em.findAndCount(entityName, where, options)
|
|
466
|
+
: [await em.find(entityName, where, options)];
|
|
461
467
|
return new Cursor(entities, count, options, this.metadata.get(entityName));
|
|
462
468
|
}
|
|
463
469
|
/**
|
|
@@ -149,7 +149,8 @@ export interface FindOptions<Entity, Hint extends string = never, Fields extends
|
|
|
149
149
|
/** @internal used to apply filters to the auto-joined relations */
|
|
150
150
|
em?: EntityManager;
|
|
151
151
|
}
|
|
152
|
-
export interface FindByCursorOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'offset'> {
|
|
152
|
+
export interface FindByCursorOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never, I extends boolean = true> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'offset'> {
|
|
153
|
+
includeCount?: I;
|
|
153
154
|
}
|
|
154
155
|
export interface FindOneOptions<T extends object, P extends string = never, F extends string = '*', E extends string = never> extends Omit<FindOptions<T, P, F, E>, 'limit' | 'lockMode'> {
|
|
155
156
|
lockMode?: LockMode;
|
package/entity/EntityFactory.js
CHANGED
|
@@ -45,6 +45,7 @@ export class EntityFactory {
|
|
|
45
45
|
let wrapped = exists && helper(exists);
|
|
46
46
|
if (wrapped && !options.refresh) {
|
|
47
47
|
wrapped.__processing = true;
|
|
48
|
+
Utils.dropUndefinedProperties(data);
|
|
48
49
|
this.mergeData(meta2, exists, data, options);
|
|
49
50
|
wrapped.__processing = false;
|
|
50
51
|
if (wrapped.isInitialized()) {
|
package/entity/EntityHelper.js
CHANGED
|
@@ -146,6 +146,9 @@ export class EntityHelper {
|
|
|
146
146
|
set(val) {
|
|
147
147
|
const entity = Reference.unwrapReference(val ?? wrapped.__data[prop.name]);
|
|
148
148
|
const old = Reference.unwrapReference(wrapped.__data[prop.name]);
|
|
149
|
+
if (old && old !== entity && prop.kind === ReferenceKind.MANY_TO_ONE && prop.inversedBy && old[prop.inversedBy]) {
|
|
150
|
+
old[prop.inversedBy].removeWithoutPropagation(this);
|
|
151
|
+
}
|
|
149
152
|
wrapped.__data[prop.name] = Reference.wrapReference(val, prop);
|
|
150
153
|
// when propagation from inside hydration, we set the FK to the entity data immediately
|
|
151
154
|
if (val && hydrator.isRunning() && wrapped.__originalEntityData && prop.owner) {
|
package/entity/EntityLoader.js
CHANGED
|
@@ -314,9 +314,16 @@ export class EntityLoader {
|
|
|
314
314
|
const innerOrderBy = Utils.asArray(options.orderBy)
|
|
315
315
|
.filter(orderBy => Utils.isObject(orderBy[prop.name]))
|
|
316
316
|
.map(orderBy => orderBy[prop.name]);
|
|
317
|
-
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging } = options;
|
|
317
|
+
const { refresh, filters, ignoreLazyScalarProperties, populateWhere, connectionType, logging, schema } = options;
|
|
318
318
|
const exclude = Array.isArray(options.exclude) ? Utils.extractChildElements(options.exclude, prop.name) : options.exclude;
|
|
319
|
-
const
|
|
319
|
+
const visited = options.visited;
|
|
320
|
+
for (const entity of entities) {
|
|
321
|
+
visited.delete(entity);
|
|
322
|
+
}
|
|
323
|
+
const filtered = Utils.unique(children.filter(e => !visited.has(e)));
|
|
324
|
+
for (const entity of entities) {
|
|
325
|
+
visited.add(entity);
|
|
326
|
+
}
|
|
320
327
|
await this.populate(prop.type, filtered, populate.children ?? populate.all, {
|
|
321
328
|
where: await this.extractChildCondition(options, prop, false),
|
|
322
329
|
orderBy: innerOrderBy,
|
|
@@ -329,6 +336,7 @@ export class EntityLoader {
|
|
|
329
336
|
populateWhere,
|
|
330
337
|
connectionType,
|
|
331
338
|
logging,
|
|
339
|
+
schema,
|
|
332
340
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
333
341
|
refresh: refresh && !filtered.every(item => options.visited.has(item)),
|
|
334
342
|
// @ts-ignore not a public option, will be propagated to the populate call
|
|
@@ -518,7 +526,7 @@ export class EntityLoader {
|
|
|
518
526
|
if (refresh) {
|
|
519
527
|
return entities;
|
|
520
528
|
}
|
|
521
|
-
return entities.filter(e => !e[field]?.__helper?.__initialized);
|
|
529
|
+
return entities.filter(e => e[field] !== null && !e[field]?.__helper?.__initialized);
|
|
522
530
|
}
|
|
523
531
|
lookupAllRelationships(entityName) {
|
|
524
532
|
const ret = [];
|
package/entity/Reference.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { inspect } from 'node:util';
|
|
2
|
-
import type { AddEager, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
2
|
+
import type { AddEager, AddOptional, Dictionary, EntityClass, EntityKey, EntityProperty, Loaded, LoadedReference, Primary, Ref } from '../typings.js';
|
|
3
3
|
import type { FindOneOptions, FindOneOrFailOptions } from '../drivers/IDatabaseDriver.js';
|
|
4
4
|
export declare class Reference<T extends object> {
|
|
5
5
|
private entity;
|
|
@@ -72,15 +72,11 @@ export interface LoadReferenceOrFailOptions<T extends object, P extends string =
|
|
|
72
72
|
/**
|
|
73
73
|
* shortcut for `wrap(entity).toReference()`
|
|
74
74
|
*/
|
|
75
|
-
export declare function ref<
|
|
75
|
+
export declare function ref<I extends unknown | Ref<unknown> | undefined | null, T extends I & {}>(entity: I): Ref<T> & LoadedReference<Loaded<T, AddEager<T>>> | AddOptional<typeof entity>;
|
|
76
76
|
/**
|
|
77
77
|
* shortcut for `Reference.createFromPK(entityType, pk)`
|
|
78
78
|
*/
|
|
79
|
-
export declare function ref<T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk
|
|
80
|
-
/**
|
|
81
|
-
* shortcut for `wrap(entity).toReference()`
|
|
82
|
-
*/
|
|
83
|
-
export declare function ref<T>(value: T | Ref<T>): Ref<T> & LoadedReference<Loaded<T, AddEager<T>>>;
|
|
79
|
+
export declare function ref<I extends unknown | undefined | null, T, PKV extends Primary<T> = Primary<T>>(entityType: EntityClass<T>, pk: I): Ref<T> | AddOptional<typeof pk>;
|
|
84
80
|
/**
|
|
85
81
|
* shortcut for `Reference.createNakedFromPK(entityType, pk)`
|
|
86
82
|
*/
|
|
@@ -256,6 +256,7 @@ export class ObjectHydrator extends Hydrator {
|
|
|
256
256
|
...prop2,
|
|
257
257
|
name: childProp.name,
|
|
258
258
|
embedded: childProp.embedded,
|
|
259
|
+
embeddedProps: childProp.embeddedProps,
|
|
259
260
|
};
|
|
260
261
|
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
261
262
|
ret.push(...hydrateProperty(prop3, childProp.object, [...path, childProp.embedded[1]], childDataKey).map(l => ' ' + l));
|
|
@@ -7,7 +7,7 @@ import { MetadataStorage } from './MetadataStorage.js';
|
|
|
7
7
|
import { EntitySchema } from './EntitySchema.js';
|
|
8
8
|
import { Cascade, ReferenceKind } from '../enums.js';
|
|
9
9
|
import { MetadataError } from '../errors.js';
|
|
10
|
-
import { ArrayType, BigIntType, BlobType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
|
|
10
|
+
import { ArrayType, BigIntType, BlobType, DateType, DecimalType, DoubleType, EnumArrayType, IntervalType, JsonType, t, Type, Uint8ArrayType, UnknownType, } from '../types/index.js';
|
|
11
11
|
import { colors } from '../logging/colors.js';
|
|
12
12
|
import { raw, RawQueryFragment } from '../utils/RawQueryFragment.js';
|
|
13
13
|
export class MetadataDiscovery {
|
|
@@ -765,6 +765,7 @@ export class MetadataDiscovery {
|
|
|
765
765
|
delete prop.default;
|
|
766
766
|
if (properties[prop.name] && properties[prop.name].type !== prop.type) {
|
|
767
767
|
properties[prop.name].type = `${properties[prop.name].type} | ${prop.type}`;
|
|
768
|
+
properties[prop.name].runtimeType = 'any';
|
|
768
769
|
return properties[prop.name];
|
|
769
770
|
}
|
|
770
771
|
return properties[prop.name] = prop;
|
|
@@ -924,7 +925,7 @@ export class MetadataDiscovery {
|
|
|
924
925
|
}
|
|
925
926
|
let i = 1;
|
|
926
927
|
Object.values(meta.properties).forEach(prop => {
|
|
927
|
-
const newProp =
|
|
928
|
+
const newProp = { ...prop };
|
|
928
929
|
if (meta.root.properties[prop.name] && meta.root.properties[prop.name].type !== prop.type) {
|
|
929
930
|
const name = newProp.name;
|
|
930
931
|
this.initFieldName(newProp, newProp.object);
|
|
@@ -1134,7 +1135,7 @@ export class MetadataDiscovery {
|
|
|
1134
1135
|
}
|
|
1135
1136
|
const mappedType = this.getMappedType(prop);
|
|
1136
1137
|
if (prop.fieldNames?.length === 1 && !prop.customType) {
|
|
1137
|
-
[BigIntType, DoubleType, DecimalType, IntervalType]
|
|
1138
|
+
[BigIntType, DoubleType, DecimalType, IntervalType, DateType]
|
|
1138
1139
|
.filter(type => mappedType instanceof type)
|
|
1139
1140
|
.forEach(type => prop.customType = new type());
|
|
1140
1141
|
}
|
|
@@ -100,14 +100,15 @@ export class MetadataValidator {
|
|
|
100
100
|
if (!prop.type) {
|
|
101
101
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
102
102
|
}
|
|
103
|
+
const targetMeta = metadata.find(prop.type);
|
|
103
104
|
// references do have type of known entity
|
|
104
|
-
if (!
|
|
105
|
+
if (!targetMeta) {
|
|
105
106
|
throw MetadataError.fromWrongTypeDefinition(meta, prop);
|
|
106
107
|
}
|
|
107
|
-
if (
|
|
108
|
+
if (targetMeta.abstract && !targetMeta.discriminatorColumn && !targetMeta.embeddable) {
|
|
108
109
|
throw MetadataError.targetIsAbstract(meta, prop);
|
|
109
110
|
}
|
|
110
|
-
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false &&
|
|
111
|
+
if ([ReferenceKind.MANY_TO_ONE, ReferenceKind.ONE_TO_ONE].includes(prop.kind) && prop.persist === false && targetMeta.compositePK && options.checkNonPersistentCompositeProps) {
|
|
111
112
|
throw MetadataError.nonPersistentCompositeProp(meta, prop);
|
|
112
113
|
}
|
|
113
114
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mikro-orm/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "7.0.0-dev.
|
|
4
|
+
"version": "7.0.0-dev.9",
|
|
5
5
|
"description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./package.json": "./package.json",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"dataloader": "2.2.3",
|
|
55
|
-
"dotenv": "16.
|
|
55
|
+
"dotenv": "16.5.0",
|
|
56
56
|
"esprima": "4.0.1",
|
|
57
57
|
"globby": "11.1.0",
|
|
58
|
-
"mikro-orm": "7.0.0-dev.
|
|
58
|
+
"mikro-orm": "7.0.0-dev.9",
|
|
59
59
|
"reflect-metadata": "0.2.2"
|
|
60
60
|
}
|
|
61
61
|
}
|
package/typings.d.ts
CHANGED
|
@@ -177,7 +177,7 @@ export interface IWrappedEntityInternal<Entity extends object> extends IWrappedE
|
|
|
177
177
|
__touched: boolean;
|
|
178
178
|
__originalEntityData?: EntityData<Entity>;
|
|
179
179
|
__loadedProperties: Set<string>;
|
|
180
|
-
__identifier?: EntityIdentifier;
|
|
180
|
+
__identifier?: EntityIdentifier | EntityIdentifier[];
|
|
181
181
|
__managed: boolean;
|
|
182
182
|
__processing: boolean;
|
|
183
183
|
__schema?: string;
|
|
@@ -206,7 +206,7 @@ export type EntityName<T> = string | EntityClass<T> | EntitySchema<T, any> | {
|
|
|
206
206
|
};
|
|
207
207
|
export type GetRepository<Entity extends {
|
|
208
208
|
[k: PropertyKey]: any;
|
|
209
|
-
}, Fallback> = Entity[typeof EntityRepositoryType] extends EntityRepository<
|
|
209
|
+
}, Fallback> = Entity[typeof EntityRepositoryType] extends EntityRepository<any> | undefined ? NonNullable<Entity[typeof EntityRepositoryType]> : Fallback;
|
|
210
210
|
export type EntityDataPropValue<T> = T | Primary<T>;
|
|
211
211
|
type ExpandEntityProp<T, C extends boolean = false> = T extends Record<string, any> ? {
|
|
212
212
|
[K in keyof T as CleanKeys<T, K>]?: EntityDataProp<ExpandProperty<T[K]>, C> | EntityDataPropValue<ExpandProperty<T[K]>> | null;
|
|
@@ -283,7 +283,7 @@ type PrimaryOrObject<T, U, C extends TypeConfig> = PreferExplicitConfig<C, Extra
|
|
|
283
283
|
} : Primary<U>;
|
|
284
284
|
export type EntityDTOProp<E, T, C extends TypeConfig = never> = T extends Scalar ? T : T extends {
|
|
285
285
|
__serialized?: infer U;
|
|
286
|
-
} ? 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;
|
|
286
|
+
} ? (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;
|
|
287
287
|
type DTOProbablyOptionalProps<T> = NonNullable<NullableKeys<T, undefined>>;
|
|
288
288
|
type DTOIsOptional<T, K extends keyof T> = T[K] extends LoadedCollection<any> ? false : K extends PrimaryProperty<T> ? false : K extends DTOProbablyOptionalProps<T> ? true : false;
|
|
289
289
|
type DTORequiredKeys<T, K extends keyof T> = DTOIsOptional<T, K> extends false ? ExcludeHidden<T, K> & CleanKeys<T, K> : never;
|
|
@@ -729,7 +729,7 @@ export type MergeSelected<T, U, F extends string> = T extends Loaded<infer TT, i
|
|
|
729
729
|
type MergeFields<F1 extends string, F2 extends string, P1, P2> = P1 | P2 extends '*' ? '*' : F1 | F2;
|
|
730
730
|
type MergeExcludes<F extends string, E extends string> = F extends E ? never : Exclude<E, F>;
|
|
731
731
|
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>;
|
|
732
|
-
type AddOptional<T> = undefined | null extends T ? null | undefined : null extends T ? null : undefined extends T ? undefined : never;
|
|
732
|
+
export type AddOptional<T> = undefined | null extends T ? null | undefined : null extends T ? null : undefined extends T ? undefined : never;
|
|
733
733
|
type LoadedProp<T, L extends string = never, F extends string = '*', E extends string = never> = LoadedLoadable<T, Loaded<ExtractType<T>, L, F, E>>;
|
|
734
734
|
export type AddEager<T> = ExtractEagerProps<T> & string;
|
|
735
735
|
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() {
|
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
|
/**
|
|
@@ -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}`;
|
package/utils/Utils.js
CHANGED
|
@@ -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
|
}
|